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 <mhagger@alum.mit.edu> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Michael Haggerty committed Apr 22, 2013 at 21:52 UTC 506a760db8c3fd510d7ebe70c189672f185ee108
1 file changed +68 -16
refs.c
+68 -16
@@ -467,6 +467,57 @@ static struct ref_entry *find_ref(struct ref_dir *dir, const char *refname)
467 return (entry->flag & REF_DIR) ? NULL : entry;
468 }
469
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 +
521 /*
522 * Add a ref_entry to the ref_dir (unsorted), recursing into
523 * subdirectories as necessary. dir must represent the top-level
@@ -1895,19 +1946,12 @@ struct ref_lock *lock_any_ref_for_update(const char *refname,
1946 return lock_ref_sha1_basic(refname, old_sha1, flags, NULL);
1947 }
1948
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)
1950 {
1905 - struct repack_without_ref_sb *data = cb_data;
1951 + int *fd = cb_data;
1952 char line[PATH_MAX + 100];
1953 int len;
1954
1909 - if (!strcmp(data->refname, entry->name))
1910 - return 0;
1955 if (entry->flag & REF_ISBROKEN) {
1956 /* This shouldn't happen to packed refs. */
1957 error("%s is broken!", entry->name);
@@ -1948,7 +1992,7 @@ static int repack_without_ref_fn(struct ref_entry *entry, void *cb_data)
1992 /* this should not happen but just being defensive */
1993 if (len > sizeof(line))
1994 die("too long a refname '%s'", entry->name);
1951 - write_or_die(data->fd, line, len);
1995 + write_or_die(*fd, line, len);
1996 return 0;
1997 }
1998
@@ -1956,22 +2000,30 @@ static struct lock_file packlock;
2000
2001 static int repack_without_ref(const char *refname)
2002 {
1959 - struct repack_without_ref_sb data;
2003 + int fd;
2004 struct ref_cache *refs = get_ref_cache(NULL);
2005 struct ref_dir *packed;
2006
2007 if (!get_packed_ref(refname))
2008 return 0; /* refname does not exist in packed refs */
2009
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) {
2012 unable_to_lock_error(git_path("packed-refs"), errno);
2013 return error("cannot delete '%s' from packed refs", refname);
2014 }
2015 clear_packed_ref_cache(refs);
2016 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);
2027 return commit_lock_file(&packlock);
2028 }
2029
@@ -2000,7 +2052,7 @@ int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
2052 ret |= repack_without_ref(lock->ref_name);
2053
2054 unlink_or_warn(git_path("logs/%s", lock->ref_name));
2003 - invalidate_ref_cache(NULL);
2055 + clear_loose_ref_cache(get_ref_cache(NULL));
2056 unlock_ref(lock);
2057 return ret;
2058 }