Skip to content

Commit 506a760

Browse files
mhaggergitster
authored andcommitted
refs: change how packed refs are deleted
Add a function remove_ref(), which removes a single entry from a reference cache. Use this function to reimplement repack_without_ref(). The old version iterated over all refs, packing all of them except for the one to be deleted, then discarded the entire packed reference cache. The new version deletes the doomed reference from the cache *before* iterating. This has two advantages: * the code for writing packed-refs becomes simpler, because it doesn't have to exclude one of the references. * it is no longer necessary to discard the packed refs cache after deleting a reference: symbolic refs cannot be packed, so packed references cannot depend on each other, so the rest of the packed refs cache remains valid after a reference is deleted. Signed-off-by: Michael Haggerty <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 9fc0a64 commit 506a760

File tree

1 file changed

+68
-16
lines changed

1 file changed

+68
-16
lines changed

refs.c

Lines changed: 68 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,57 @@ static struct ref_entry *find_ref(struct ref_dir *dir, const char *refname)
467467
return (entry->flag & REF_DIR) ? NULL : entry;
468468
}
469469

470+
/*
471+
* Remove the entry with the given name from dir, recursing into
472+
* subdirectories as necessary. If refname is the name of a directory
473+
* (i.e., ends with '/'), then remove the directory and its contents.
474+
* If the removal was successful, return the number of entries
475+
* remaining in the directory entry that contained the deleted entry.
476+
* If the name was not found, return -1. Please note that this
477+
* function only deletes the entry from the cache; it does not delete
478+
* it from the filesystem or ensure that other cache entries (which
479+
* might be symbolic references to the removed entry) are updated.
480+
* Nor does it remove any containing dir entries that might be made
481+
* empty by the removal. dir must represent the top-level directory
482+
* and must already be complete.
483+
*/
484+
static int remove_entry(struct ref_dir *dir, const char *refname)
485+
{
486+
int refname_len = strlen(refname);
487+
int entry_index;
488+
struct ref_entry *entry;
489+
int is_dir = refname[refname_len - 1] == '/';
490+
if (is_dir) {
491+
/*
492+
* refname represents a reference directory. Remove
493+
* the trailing slash; otherwise we will get the
494+
* directory *representing* refname rather than the
495+
* one *containing* it.
496+
*/
497+
char *dirname = xmemdupz(refname, refname_len - 1);
498+
dir = find_containing_dir(dir, dirname, 0);
499+
free(dirname);
500+
} else {
501+
dir = find_containing_dir(dir, refname, 0);
502+
}
503+
if (!dir)
504+
return -1;
505+
entry_index = search_ref_dir(dir, refname, refname_len);
506+
if (entry_index == -1)
507+
return -1;
508+
entry = dir->entries[entry_index];
509+
510+
memmove(&dir->entries[entry_index],
511+
&dir->entries[entry_index + 1],
512+
(dir->nr - entry_index - 1) * sizeof(*dir->entries)
513+
);
514+
dir->nr--;
515+
if (dir->sorted > entry_index)
516+
dir->sorted--;
517+
free_ref_entry(entry);
518+
return dir->nr;
519+
}
520+
470521
/*
471522
* Add a ref_entry to the ref_dir (unsorted), recursing into
472523
* subdirectories as necessary. dir must represent the top-level
@@ -1895,19 +1946,12 @@ struct ref_lock *lock_any_ref_for_update(const char *refname,
18951946
return lock_ref_sha1_basic(refname, old_sha1, flags, NULL);
18961947
}
18971948

1898-
struct repack_without_ref_sb {
1899-
const char *refname;
1900-
int fd;
1901-
};
1902-
1903-
static int repack_without_ref_fn(struct ref_entry *entry, void *cb_data)
1949+
static int repack_ref_fn(struct ref_entry *entry, void *cb_data)
19041950
{
1905-
struct repack_without_ref_sb *data = cb_data;
1951+
int *fd = cb_data;
19061952
char line[PATH_MAX + 100];
19071953
int len;
19081954

1909-
if (!strcmp(data->refname, entry->name))
1910-
return 0;
19111955
if (entry->flag & REF_ISBROKEN) {
19121956
/* This shouldn't happen to packed refs. */
19131957
error("%s is broken!", entry->name);
@@ -1948,30 +1992,38 @@ static int repack_without_ref_fn(struct ref_entry *entry, void *cb_data)
19481992
/* this should not happen but just being defensive */
19491993
if (len > sizeof(line))
19501994
die("too long a refname '%s'", entry->name);
1951-
write_or_die(data->fd, line, len);
1995+
write_or_die(*fd, line, len);
19521996
return 0;
19531997
}
19541998

19551999
static struct lock_file packlock;
19562000

19572001
static int repack_without_ref(const char *refname)
19582002
{
1959-
struct repack_without_ref_sb data;
2003+
int fd;
19602004
struct ref_cache *refs = get_ref_cache(NULL);
19612005
struct ref_dir *packed;
19622006

19632007
if (!get_packed_ref(refname))
19642008
return 0; /* refname does not exist in packed refs */
19652009

1966-
data.refname = refname;
1967-
data.fd = hold_lock_file_for_update(&packlock, git_path("packed-refs"), 0);
1968-
if (data.fd < 0) {
2010+
fd = hold_lock_file_for_update(&packlock, git_path("packed-refs"), 0);
2011+
if (fd < 0) {
19692012
unable_to_lock_error(git_path("packed-refs"), errno);
19702013
return error("cannot delete '%s' from packed refs", refname);
19712014
}
19722015
clear_packed_ref_cache(refs);
19732016
packed = get_packed_refs(refs);
1974-
do_for_each_entry_in_dir(packed, 0, repack_without_ref_fn, &data);
2017+
/* Remove refname from the cache. */
2018+
if (remove_entry(packed, refname) == -1) {
2019+
/*
2020+
* The packed entry disappeared while we were
2021+
* acquiring the lock.
2022+
*/
2023+
rollback_lock_file(&packlock);
2024+
return 0;
2025+
}
2026+
do_for_each_entry_in_dir(packed, 0, repack_ref_fn, &fd);
19752027
return commit_lock_file(&packlock);
19762028
}
19772029

@@ -2000,7 +2052,7 @@ int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
20002052
ret |= repack_without_ref(lock->ref_name);
20012053

20022054
unlink_or_warn(git_path("logs/%s", lock->ref_name));
2003-
invalidate_ref_cache(NULL);
2055+
clear_loose_ref_cache(get_ref_cache(NULL));
20042056
unlock_ref(lock);
20052057
return ret;
20062058
}

0 commit comments

Comments
 (0)