#include "../cache.h"
#include "../refs.h"
#include "refs-internal.h"
+#include "ref-cache.h"
#include "../iterator.h"
#include "../dir-iterator.h"
#include "../lockfile.h"
struct object_id old_oid;
};
-struct ref_entry;
-
-/*
- * Information used (along with the information in ref_entry) to
- * describe a single cached reference. This data structure only
- * occurs embedded in a union in struct ref_entry, and only when
- * (ref_entry->flag & REF_DIR) is zero.
- */
-struct ref_value {
- /*
- * The name of the object to which this reference resolves
- * (which may be a tag object). If REF_ISBROKEN, this is
- * null. If REF_ISSYMREF, then this is the name of the object
- * referred to by the last reference in the symlink chain.
- */
- struct object_id oid;
-
- /*
- * If REF_KNOWS_PEELED, then this field holds the peeled value
- * of this reference, or null if the reference is known not to
- * be peelable. See the documentation for peel_ref() for an
- * exact definition of "peelable".
- */
- struct object_id peeled;
-};
-
-struct files_ref_store;
-
-/*
- * Information used (along with the information in ref_entry) to
- * describe a level in the hierarchy of references. This data
- * structure only occurs embedded in a union in struct ref_entry, and
- * only when (ref_entry.flag & REF_DIR) is set. In that case,
- * (ref_entry.flag & REF_INCOMPLETE) determines whether the references
- * in the directory have already been read:
- *
- * (ref_entry.flag & REF_INCOMPLETE) unset -- a directory of loose
- * or packed references, already read.
- *
- * (ref_entry.flag & REF_INCOMPLETE) set -- a directory of loose
- * references that hasn't been read yet (nor has any of its
- * subdirectories).
- *
- * Entries within a directory are stored within a growable array of
- * pointers to ref_entries (entries, nr, alloc). Entries 0 <= i <
- * sorted are sorted by their component name in strcmp() order and the
- * remaining entries are unsorted.
- *
- * Loose references are read lazily, one directory at a time. When a
- * directory of loose references is read, then all of the references
- * in that directory are stored, and REF_INCOMPLETE stubs are created
- * for any subdirectories, but the subdirectories themselves are not
- * read. The reading is triggered by get_ref_dir().
- */
-struct ref_dir {
- int nr, alloc;
-
- /*
- * Entries with index 0 <= i < sorted are sorted by name. New
- * entries are appended to the list unsorted, and are sorted
- * only when required; thus we avoid the need to sort the list
- * after the addition of every reference.
- */
- int sorted;
-
- /* A pointer to the files_ref_store that contains this ref_dir. */
- struct files_ref_store *ref_store;
-
- struct ref_entry **entries;
-};
-
-/*
- * Bit values for ref_entry::flag. REF_ISSYMREF=0x01,
- * REF_ISPACKED=0x02, REF_ISBROKEN=0x04 and REF_BAD_NAME=0x08 are
- * public values; see refs.h.
- */
-
-/*
- * The field ref_entry->u.value.peeled of this value entry contains
- * the correct peeled value for the reference, which might be
- * null_sha1 if the reference is not a tag or if it is broken.
- */
-#define REF_KNOWS_PEELED 0x10
-
-/* ref_entry represents a directory of references */
-#define REF_DIR 0x20
-
-/*
- * Entry has not yet been read from disk (used only for REF_DIR
- * entries representing loose references)
- */
-#define REF_INCOMPLETE 0x40
-
-/*
- * A ref_entry represents either a reference or a "subdirectory" of
- * references.
- *
- * Each directory in the reference namespace is represented by a
- * ref_entry with (flags & REF_DIR) set and containing a subdir member
- * that holds the entries in that directory that have been read so
- * far. If (flags & REF_INCOMPLETE) is set, then the directory and
- * its subdirectories haven't been read yet. REF_INCOMPLETE is only
- * used for loose reference directories.
- *
- * References are represented by a ref_entry with (flags & REF_DIR)
- * unset and a value member that describes the reference's value. The
- * flag member is at the ref_entry level, but it is also needed to
- * interpret the contents of the value field (in other words, a
- * ref_value object is not very much use without the enclosing
- * ref_entry).
- *
- * Reference names cannot end with slash and directories' names are
- * always stored with a trailing slash (except for the top-level
- * directory, which is always denoted by ""). This has two nice
- * consequences: (1) when the entries in each subdir are sorted
- * lexicographically by name (as they usually are), the references in
- * a whole tree can be generated in lexicographic order by traversing
- * the tree in left-to-right, depth-first order; (2) the names of
- * references and subdirectories cannot conflict, and therefore the
- * presence of an empty subdirectory does not block the creation of a
- * similarly-named reference. (The fact that reference names with the
- * same leading components can conflict *with each other* is a
- * separate issue that is regulated by verify_refname_available().)
- *
- * Please note that the name field contains the fully-qualified
- * reference (or subdirectory) name. Space could be saved by only
- * storing the relative names. But that would require the full names
- * to be generated on the fly when iterating in do_for_each_ref(), and
- * would break callback functions, who have always been able to assume
- * that the name strings that they are passed will not be freed during
- * the iteration.
- */
-struct ref_entry {
- unsigned char flag; /* ISSYMREF? ISPACKED? */
- union {
- struct ref_value value; /* if not (flags&REF_DIR) */
- struct ref_dir subdir; /* if (flags&REF_DIR) */
- } u;
- /*
- * The full name of the reference (e.g., "refs/heads/master")
- * or the full name of the directory with a trailing slash
- * (e.g., "refs/heads/"):
- */
- char name[FLEX_ARRAY];
-};
-
-static void read_loose_refs(const char *dirname, struct ref_dir *dir);
-static int search_ref_dir(struct ref_dir *dir, const char *refname, size_t len);
-static struct ref_entry *create_dir_entry(struct files_ref_store *ref_store,
- const char *dirname, size_t len,
- int incomplete);
-static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry);
-static int files_log_ref_write(struct files_ref_store *refs,
- const char *refname, const unsigned char *old_sha1,
- const unsigned char *new_sha1, const char *msg,
- int flags, struct strbuf *err);
-
-static struct ref_dir *get_ref_dir(struct ref_entry *entry)
-{
- struct ref_dir *dir;
- assert(entry->flag & REF_DIR);
- dir = &entry->u.subdir;
- if (entry->flag & REF_INCOMPLETE) {
- read_loose_refs(entry->name, dir);
-
- /*
- * Manually add refs/bisect, which, being
- * per-worktree, might not appear in the directory
- * listing for refs/ in the main repo.
- */
- if (!strcmp(entry->name, "refs/")) {
- int pos = search_ref_dir(dir, "refs/bisect/", 12);
- if (pos < 0) {
- struct ref_entry *child_entry;
- child_entry = create_dir_entry(dir->ref_store,
- "refs/bisect/",
- 12, 1);
- add_entry_to_dir(dir, child_entry);
- }
- }
- entry->flag &= ~REF_INCOMPLETE;
- }
- return dir;
-}
-
-static struct ref_entry *create_ref_entry(const char *refname,
- const unsigned char *sha1, int flag,
- int check_name)
-{
- struct ref_entry *ref;
-
- if (check_name &&
- check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
- die("Reference has invalid format: '%s'", refname);
- FLEX_ALLOC_STR(ref, name, refname);
- hashcpy(ref->u.value.oid.hash, sha1);
- oidclr(&ref->u.value.peeled);
- ref->flag = flag;
- return ref;
-}
-
-static void clear_ref_dir(struct ref_dir *dir);
-
-static void free_ref_entry(struct ref_entry *entry)
-{
- if (entry->flag & REF_DIR) {
- /*
- * Do not use get_ref_dir() here, as that might
- * trigger the reading of loose refs.
- */
- clear_ref_dir(&entry->u.subdir);
- }
- free(entry);
-}
-
-/*
- * Add a ref_entry to the end of dir (unsorted). Entry is always
- * stored directly in dir; no recursion into subdirectories is
- * done.
- */
-static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry)
-{
- ALLOC_GROW(dir->entries, dir->nr + 1, dir->alloc);
- dir->entries[dir->nr++] = entry;
- /* optimize for the case that entries are added in order */
- if (dir->nr == 1 ||
- (dir->nr == dir->sorted + 1 &&
- strcmp(dir->entries[dir->nr - 2]->name,
- dir->entries[dir->nr - 1]->name) < 0))
- dir->sorted = dir->nr;
-}
-
-/*
- * Clear and free all entries in dir, recursively.
- */
-static void clear_ref_dir(struct ref_dir *dir)
-{
- int i;
- for (i = 0; i < dir->nr; i++)
- free_ref_entry(dir->entries[i]);
- free(dir->entries);
- dir->sorted = dir->nr = dir->alloc = 0;
- dir->entries = NULL;
-}
-
-/*
- * Create a struct ref_entry object for the specified dirname.
- * dirname is the name of the directory with a trailing slash (e.g.,
- * "refs/heads/") or "" for the top-level directory.
- */
-static struct ref_entry *create_dir_entry(struct files_ref_store *ref_store,
- const char *dirname, size_t len,
- int incomplete)
-{
- struct ref_entry *direntry;
- FLEX_ALLOC_MEM(direntry, name, dirname, len);
- direntry->u.subdir.ref_store = ref_store;
- direntry->flag = REF_DIR | (incomplete ? REF_INCOMPLETE : 0);
- return direntry;
-}
-
-static int ref_entry_cmp(const void *a, const void *b)
-{
- struct ref_entry *one = *(struct ref_entry **)a;
- struct ref_entry *two = *(struct ref_entry **)b;
- return strcmp(one->name, two->name);
-}
-
-static void sort_ref_dir(struct ref_dir *dir);
-
-struct string_slice {
- size_t len;
- const char *str;
-};
-
-static int ref_entry_cmp_sslice(const void *key_, const void *ent_)
-{
- const struct string_slice *key = key_;
- const struct ref_entry *ent = *(const struct ref_entry * const *)ent_;
- int cmp = strncmp(key->str, ent->name, key->len);
- if (cmp)
- return cmp;
- return '\0' - (unsigned char)ent->name[key->len];
-}
-
-/*
- * Return the index of the entry with the given refname from the
- * ref_dir (non-recursively), sorting dir if necessary. Return -1 if
- * no such entry is found. dir must already be complete.
- */
-static int search_ref_dir(struct ref_dir *dir, const char *refname, size_t len)
-{
- struct ref_entry **r;
- struct string_slice key;
-
- if (refname == NULL || !dir->nr)
- return -1;
-
- sort_ref_dir(dir);
- key.len = len;
- key.str = refname;
- r = bsearch(&key, dir->entries, dir->nr, sizeof(*dir->entries),
- ref_entry_cmp_sslice);
-
- if (r == NULL)
- return -1;
-
- return r - dir->entries;
-}
-
-/*
- * Search for a directory entry directly within dir (without
- * recursing). Sort dir if necessary. subdirname must be a directory
- * name (i.e., end in '/'). If mkdir is set, then create the
- * directory if it is missing; otherwise, return NULL if the desired
- * directory cannot be found. dir must already be complete.
- */
-static struct ref_dir *search_for_subdir(struct ref_dir *dir,
- const char *subdirname, size_t len,
- int mkdir)
-{
- int entry_index = search_ref_dir(dir, subdirname, len);
- struct ref_entry *entry;
- if (entry_index == -1) {
- if (!mkdir)
- return NULL;
- /*
- * Since dir is complete, the absence of a subdir
- * means that the subdir really doesn't exist;
- * therefore, create an empty record for it but mark
- * the record complete.
- */
- entry = create_dir_entry(dir->ref_store, subdirname, len, 0);
- add_entry_to_dir(dir, entry);
- } else {
- entry = dir->entries[entry_index];
- }
- return get_ref_dir(entry);
-}
-
-/*
- * If refname is a reference name, find the ref_dir within the dir
- * tree that should hold refname. If refname is a directory name
- * (i.e., ends in '/'), then return that ref_dir itself. dir must
- * represent the top-level directory and must already be complete.
- * Sort ref_dirs and recurse into subdirectories as necessary. If
- * mkdir is set, then create any missing directories; otherwise,
- * return NULL if the desired directory cannot be found.
- */
-static struct ref_dir *find_containing_dir(struct ref_dir *dir,
- const char *refname, int mkdir)
-{
- const char *slash;
- for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) {
- size_t dirnamelen = slash - refname + 1;
- struct ref_dir *subdir;
- subdir = search_for_subdir(dir, refname, dirnamelen, mkdir);
- if (!subdir) {
- dir = NULL;
- break;
- }
- dir = subdir;
- }
-
- return dir;
-}
-
-/*
- * Find the value entry with the given name in dir, sorting ref_dirs
- * and recursing into subdirectories as necessary. If the name is not
- * found or it corresponds to a directory entry, return NULL.
- */
-static struct ref_entry *find_ref_entry(struct ref_dir *dir, const char *refname)
-{
- int entry_index;
- struct ref_entry *entry;
- dir = find_containing_dir(dir, refname, 0);
- if (!dir)
- return NULL;
- entry_index = search_ref_dir(dir, refname, strlen(refname));
- if (entry_index == -1)
- return NULL;
- entry = dir->entries[entry_index];
- return (entry->flag & REF_DIR) ? NULL : entry;
-}
-
-/*
- * Remove the entry with the given name from dir, recursing into
- * subdirectories as necessary. If refname is the name of a directory
- * (i.e., ends with '/'), then remove the directory and its contents.
- * If the removal was successful, return the number of entries
- * remaining in the directory entry that contained the deleted entry.
- * If the name was not found, return -1. Please note that this
- * function only deletes the entry from the cache; it does not delete
- * it from the filesystem or ensure that other cache entries (which
- * might be symbolic references to the removed entry) are updated.
- * Nor does it remove any containing dir entries that might be made
- * empty by the removal. dir must represent the top-level directory
- * and must already be complete.
- */
-static int remove_entry_from_dir(struct ref_dir *dir, const char *refname)
-{
- int refname_len = strlen(refname);
- int entry_index;
- struct ref_entry *entry;
- int is_dir = refname[refname_len - 1] == '/';
- if (is_dir) {
- /*
- * refname represents a reference directory. Remove
- * the trailing slash; otherwise we will get the
- * directory *representing* refname rather than the
- * one *containing* it.
- */
- char *dirname = xmemdupz(refname, refname_len - 1);
- dir = find_containing_dir(dir, dirname, 0);
- free(dirname);
- } else {
- dir = find_containing_dir(dir, refname, 0);
- }
- if (!dir)
- return -1;
- entry_index = search_ref_dir(dir, refname, refname_len);
- if (entry_index == -1)
- return -1;
- entry = dir->entries[entry_index];
-
- memmove(&dir->entries[entry_index],
- &dir->entries[entry_index + 1],
- (dir->nr - entry_index - 1) * sizeof(*dir->entries)
- );
- dir->nr--;
- if (dir->sorted > entry_index)
- dir->sorted--;
- free_ref_entry(entry);
- return dir->nr;
-}
-
-/*
- * Add a ref_entry to the ref_dir (unsorted), recursing into
- * subdirectories as necessary. dir must represent the top-level
- * directory. Return 0 on success.
- */
-static int add_ref_entry(struct ref_dir *dir, struct ref_entry *ref)
-{
- dir = find_containing_dir(dir, ref->name, 1);
- if (!dir)
- return -1;
- add_entry_to_dir(dir, ref);
- return 0;
-}
-
-/*
- * Emit a warning and return true iff ref1 and ref2 have the same name
- * and the same sha1. Die if they have the same name but different
- * sha1s.
- */
-static int is_dup_ref(const struct ref_entry *ref1, const struct ref_entry *ref2)
-{
- if (strcmp(ref1->name, ref2->name))
- return 0;
-
- /* Duplicate name; make sure that they don't conflict: */
-
- if ((ref1->flag & REF_DIR) || (ref2->flag & REF_DIR))
- /* This is impossible by construction */
- die("Reference directory conflict: %s", ref1->name);
-
- if (oidcmp(&ref1->u.value.oid, &ref2->u.value.oid))
- die("Duplicated ref, and SHA1s don't match: %s", ref1->name);
-
- warning("Duplicated ref: %s", ref1->name);
- return 1;
-}
-
-/*
- * Sort the entries in dir non-recursively (if they are not already
- * sorted) and remove any duplicate entries.
- */
-static void sort_ref_dir(struct ref_dir *dir)
-{
- int i, j;
- struct ref_entry *last = NULL;
-
- /*
- * This check also prevents passing a zero-length array to qsort(),
- * which is a problem on some platforms.
- */
- if (dir->sorted == dir->nr)
- return;
-
- QSORT(dir->entries, dir->nr, ref_entry_cmp);
-
- /* Remove any duplicates: */
- for (i = 0, j = 0; j < dir->nr; j++) {
- struct ref_entry *entry = dir->entries[j];
- if (last && is_dup_ref(last, entry))
- free_ref_entry(entry);
- else
- last = dir->entries[i++] = entry;
- }
- dir->sorted = dir->nr = i;
-}
-
/*
* Return true if refname, which has the specified oid and flags, can
* be resolved to an object in the database. If the referred-to object
return 1;
}
-/*
- * Return true if the reference described by entry can be resolved to
- * an object in the database; otherwise, emit a warning and return
- * false.
- */
-static int entry_resolves_to_object(struct ref_entry *entry)
-{
- return ref_resolves_to_object(entry->name,
- &entry->u.value.oid, entry->flag);
-}
-
-typedef int each_ref_entry_fn(struct ref_entry *entry, void *cb_data);
-
-/*
- * Call fn for each reference in dir that has index in the range
- * offset <= index < dir->nr. Recurse into subdirectories that are in
- * that index range, sorting them before iterating. This function
- * does not sort dir itself; it should be sorted beforehand. fn is
- * called for all references, including broken ones.
- */
-static int do_for_each_entry_in_dir(struct ref_dir *dir, int offset,
- each_ref_entry_fn fn, void *cb_data)
-{
- int i;
- assert(dir->sorted == dir->nr);
- for (i = offset; i < dir->nr; i++) {
- struct ref_entry *entry = dir->entries[i];
- int retval;
- if (entry->flag & REF_DIR) {
- struct ref_dir *subdir = get_ref_dir(entry);
- sort_ref_dir(subdir);
- retval = do_for_each_entry_in_dir(subdir, 0, fn, cb_data);
- } else {
- retval = fn(entry, cb_data);
- }
- if (retval)
- return retval;
- }
- return 0;
-}
-
-/*
- * Load all of the refs from the dir into our in-memory cache. The hard work
- * of loading loose refs is done by get_ref_dir(), so we just need to recurse
- * through all of the sub-directories. We do not even need to care about
- * sorting, as traversal order does not matter to us.
- */
-static void prime_ref_dir(struct ref_dir *dir)
-{
- int i;
- for (i = 0; i < dir->nr; i++) {
- struct ref_entry *entry = dir->entries[i];
- if (entry->flag & REF_DIR)
- prime_ref_dir(get_ref_dir(entry));
- }
-}
-
-/*
- * A level in the reference hierarchy that is currently being iterated
- * through.
- */
-struct cache_ref_iterator_level {
- /*
- * The ref_dir being iterated over at this level. The ref_dir
- * is sorted before being stored here.
- */
- struct ref_dir *dir;
-
- /*
- * The index of the current entry within dir (which might
- * itself be a directory). If index == -1, then the iteration
- * hasn't yet begun. If index == dir->nr, then the iteration
- * through this level is over.
- */
- int index;
-};
-
-/*
- * Represent an iteration through a ref_dir in the memory cache. The
- * iteration recurses through subdirectories.
- */
-struct cache_ref_iterator {
- struct ref_iterator base;
-
- /*
- * The number of levels currently on the stack. This is always
- * at least 1, because when it becomes zero the iteration is
- * ended and this struct is freed.
- */
- size_t levels_nr;
-
- /* The number of levels that have been allocated on the stack */
- size_t levels_alloc;
-
- /*
- * A stack of levels. levels[0] is the uppermost level that is
- * being iterated over in this iteration. (This is not
- * necessary the top level in the references hierarchy. If we
- * are iterating through a subtree, then levels[0] will hold
- * the ref_dir for that subtree, and subsequent levels will go
- * on from there.)
- */
- struct cache_ref_iterator_level *levels;
-};
-
-static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
-{
- struct cache_ref_iterator *iter =
- (struct cache_ref_iterator *)ref_iterator;
-
- while (1) {
- struct cache_ref_iterator_level *level =
- &iter->levels[iter->levels_nr - 1];
- struct ref_dir *dir = level->dir;
- struct ref_entry *entry;
-
- if (level->index == -1)
- sort_ref_dir(dir);
-
- if (++level->index == level->dir->nr) {
- /* This level is exhausted; pop up a level */
- if (--iter->levels_nr == 0)
- return ref_iterator_abort(ref_iterator);
-
- continue;
- }
-
- entry = dir->entries[level->index];
-
- if (entry->flag & REF_DIR) {
- /* push down a level */
- ALLOC_GROW(iter->levels, iter->levels_nr + 1,
- iter->levels_alloc);
-
- level = &iter->levels[iter->levels_nr++];
- level->dir = get_ref_dir(entry);
- level->index = -1;
- } else {
- iter->base.refname = entry->name;
- iter->base.oid = &entry->u.value.oid;
- iter->base.flags = entry->flag;
- return ITER_OK;
- }
- }
-}
-
-static enum peel_status peel_entry(struct ref_entry *entry, int repeel);
-
-static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
-{
- struct cache_ref_iterator *iter =
- (struct cache_ref_iterator *)ref_iterator;
- struct cache_ref_iterator_level *level;
- struct ref_entry *entry;
-
- level = &iter->levels[iter->levels_nr - 1];
-
- if (level->index == -1)
- die("BUG: peel called before advance for cache iterator");
-
- entry = level->dir->entries[level->index];
-
- if (peel_entry(entry, 0))
- return -1;
- oidcpy(peeled, &entry->u.value.peeled);
- return 0;
-}
-
-static int cache_ref_iterator_abort(struct ref_iterator *ref_iterator)
-{
- struct cache_ref_iterator *iter =
- (struct cache_ref_iterator *)ref_iterator;
-
- free(iter->levels);
- base_ref_iterator_free(ref_iterator);
- return ITER_DONE;
-}
-
-static struct ref_iterator_vtable cache_ref_iterator_vtable = {
- cache_ref_iterator_advance,
- cache_ref_iterator_peel,
- cache_ref_iterator_abort
-};
-
-static struct ref_iterator *cache_ref_iterator_begin(struct ref_dir *dir)
-{
- struct cache_ref_iterator *iter;
- struct ref_iterator *ref_iterator;
- struct cache_ref_iterator_level *level;
-
- iter = xcalloc(1, sizeof(*iter));
- ref_iterator = &iter->base;
- base_ref_iterator_init(ref_iterator, &cache_ref_iterator_vtable);
- ALLOC_GROW(iter->levels, 10, iter->levels_alloc);
-
- iter->levels_nr = 1;
- level = &iter->levels[0];
- level->index = -1;
- level->dir = dir;
-
- return ref_iterator;
-}
-
struct packed_ref_cache {
- struct ref_entry *root;
+ struct ref_cache *cache;
/*
* Count of references to the data structure in this instance,
char *gitcommondir;
char *packed_refs_path;
- struct ref_entry *loose;
+ struct ref_cache *loose;
struct packed_ref_cache *packed;
};
static int release_packed_ref_cache(struct packed_ref_cache *packed_refs)
{
if (!--packed_refs->referrers) {
- free_ref_entry(packed_refs->root);
+ free_ref_cache(packed_refs->cache);
stat_validity_clear(&packed_refs->validity);
free(packed_refs);
return 1;
static void clear_loose_ref_cache(struct files_ref_store *refs)
{
if (refs->loose) {
- free_ref_entry(refs->loose);
+ free_ref_cache(refs->loose);
refs->loose = NULL;
}
}
refs->packed = xcalloc(1, sizeof(*refs->packed));
acquire_packed_ref_cache(refs->packed);
- refs->packed->root = create_dir_entry(refs, "", 0, 0);
+ refs->packed->cache = create_ref_cache(&refs->base, NULL);
+ refs->packed->cache->root->flag &= ~REF_INCOMPLETE;
f = fopen(packed_refs_file, "r");
if (f) {
stat_validity_update(&refs->packed->validity, fileno(f));
- read_packed_refs(f, get_ref_dir(refs->packed->root));
+ read_packed_refs(f, get_ref_dir(refs->packed->cache->root));
fclose(f);
}
}
static struct ref_dir *get_packed_ref_dir(struct packed_ref_cache *packed_ref_cache)
{
- return get_ref_dir(packed_ref_cache->root);
+ return get_ref_dir(packed_ref_cache->cache->root);
}
static struct ref_dir *get_packed_refs(struct files_ref_store *refs)
if (!packed_ref_cache->lock)
die("internal error: packed refs not locked");
add_ref_entry(get_packed_ref_dir(packed_ref_cache),
- create_ref_entry(refname, sha1, REF_ISPACKED, 1));
+ create_ref_entry(refname, sha1, REF_ISPACKED, 1));
}
/*
* (without recursing). dirname must end with '/'. dir must be the
* directory entry corresponding to dirname.
*/
-static void read_loose_refs(const char *dirname, struct ref_dir *dir)
+static void loose_fill_ref_dir(struct ref_store *ref_store,
+ struct ref_dir *dir, const char *dirname)
{
- struct files_ref_store *refs = dir->ref_store;
+ struct files_ref_store *refs =
+ files_downcast(ref_store, REF_STORE_READ, "fill_ref_dir");
DIR *d;
struct dirent *de;
int dirnamelen = strlen(dirname);
} else if (S_ISDIR(st.st_mode)) {
strbuf_addch(&refname, '/');
add_entry_to_dir(dir,
- create_dir_entry(refs, refname.buf,
+ create_dir_entry(dir->cache, refname.buf,
refname.len, 1));
} else {
if (!refs_resolve_ref_unsafe(&refs->base,
strbuf_release(&refname);
strbuf_release(&path);
closedir(d);
+
+ /*
+ * Manually add refs/bisect, which, being per-worktree, might
+ * not appear in the directory listing for refs/ in the main
+ * repo.
+ */
+ if (!strcmp(dirname, "refs/")) {
+ int pos = search_ref_dir(dir, "refs/bisect/", 12);
+
+ if (pos < 0) {
+ struct ref_entry *child_entry = create_dir_entry(
+ dir->cache, "refs/bisect/", 12, 1);
+ add_entry_to_dir(dir, child_entry);
+ }
+ }
}
-static struct ref_dir *get_loose_refs(struct files_ref_store *refs)
+static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs)
{
if (!refs->loose) {
/*
* are about to read the only subdirectory that can
* hold references:
*/
- refs->loose = create_dir_entry(refs, "", 0, 0);
+ refs->loose = create_ref_cache(&refs->base, loose_fill_ref_dir);
+
+ /* We're going to fill the top level ourselves: */
+ refs->loose->root->flag &= ~REF_INCOMPLETE;
+
/*
- * Create an incomplete entry for "refs/":
+ * Add an incomplete entry for "refs/" (to be filled
+ * lazily):
*/
- add_entry_to_dir(get_ref_dir(refs->loose),
- create_dir_entry(refs, "refs/", 5, 1));
+ add_entry_to_dir(get_ref_dir(refs->loose->root),
+ create_dir_entry(refs->loose, "refs/", 5, 1));
}
- return get_ref_dir(refs->loose);
+ return refs->loose;
}
/*
return ret;
}
-/*
- * Peel the entry (if possible) and return its new peel_status. If
- * repeel is true, re-peel the entry even if there is an old peeled
- * value that is already stored in it.
- *
- * It is OK to call this function with a packed reference entry that
- * might be stale and might even refer to an object that has since
- * been garbage-collected. In such a case, if the entry has
- * REF_KNOWS_PEELED then leave the status unchanged and return
- * PEEL_PEELED or PEEL_NON_TAG; otherwise, return PEEL_INVALID.
- */
-static enum peel_status peel_entry(struct ref_entry *entry, int repeel)
-{
- enum peel_status status;
-
- if (entry->flag & REF_KNOWS_PEELED) {
- if (repeel) {
- entry->flag &= ~REF_KNOWS_PEELED;
- oidclr(&entry->u.value.peeled);
- } else {
- return is_null_oid(&entry->u.value.peeled) ?
- PEEL_NON_TAG : PEEL_PEELED;
- }
- }
- if (entry->flag & REF_ISBROKEN)
- return PEEL_BROKEN;
- if (entry->flag & REF_ISSYMREF)
- return PEEL_IS_SYMREF;
-
- status = peel_object(entry->u.value.oid.hash, entry->u.value.peeled.hash);
- if (status == PEEL_PEELED || status == PEEL_NON_TAG)
- entry->flag |= REF_KNOWS_PEELED;
- return status;
-}
-
static int files_peel_ref(struct ref_store *ref_store,
const char *refname, unsigned char *sha1)
{
const char *prefix, unsigned int flags)
{
struct files_ref_store *refs;
- struct ref_dir *loose_dir, *packed_dir;
struct ref_iterator *loose_iter, *packed_iter;
struct files_ref_iterator *iter;
struct ref_iterator *ref_iterator;
* condition if loose refs are migrated to the packed-refs
* file by a simultaneous process, but our in-memory view is
* from before the migration. We ensure this as follows:
- * First, we call prime_ref_dir(), which pre-reads the loose
- * references for the subtree into the cache. (If they've
- * already been read, that's OK; we only need to guarantee
- * that they're read before the packed refs, not *how much*
- * before.) After that, we call get_packed_ref_cache(), which
- * internally checks whether the packed-ref cache is up to
- * date with what is on disk, and re-reads it if not.
+ * First, we call start the loose refs iteration with its
+ * `prime_ref` argument set to true. This causes the loose
+ * references in the subtree to be pre-read into the cache.
+ * (If they've already been read, that's OK; we only need to
+ * guarantee that they're read before the packed refs, not
+ * *how much* before.) After that, we call
+ * get_packed_ref_cache(), which internally checks whether the
+ * packed-ref cache is up to date with what is on disk, and
+ * re-reads it if not.
*/
- loose_dir = get_loose_refs(refs);
-
- if (prefix && *prefix)
- loose_dir = find_containing_dir(loose_dir, prefix, 0);
-
- if (loose_dir) {
- prime_ref_dir(loose_dir);
- loose_iter = cache_ref_iterator_begin(loose_dir);
- } else {
- /* There's nothing to iterate over. */
- loose_iter = empty_ref_iterator_begin();
- }
+ loose_iter = cache_ref_iterator_begin(get_loose_ref_cache(refs),
+ prefix, 1);
iter->packed_ref_cache = get_packed_ref_cache(refs);
acquire_packed_ref_cache(iter->packed_ref_cache);
- packed_dir = get_packed_ref_dir(iter->packed_ref_cache);
-
- if (prefix && *prefix)
- packed_dir = find_containing_dir(packed_dir, prefix, 0);
-
- if (packed_dir) {
- packed_iter = cache_ref_iterator_begin(packed_dir);
- } else {
- /* There's nothing to iterate over. */
- packed_iter = empty_ref_iterator_begin();
- }
+ packed_iter = cache_ref_iterator_begin(iter->packed_ref_cache->cache,
+ prefix, 0);
iter->iter0 = overlay_ref_iterator_begin(loose_iter, packed_iter);
iter->flags = flags;
* Write an entry to the packed-refs file for the specified refname.
* If peeled is non-NULL, write it as the entry's peeled value.
*/
-static void write_packed_entry(FILE *fh, char *refname, unsigned char *sha1,
- unsigned char *peeled)
+static void write_packed_entry(FILE *fh, const char *refname,
+ const unsigned char *sha1,
+ const unsigned char *peeled)
{
fprintf_or_die(fh, "%s %s\n", sha1_to_hex(sha1), refname);
if (peeled)
fprintf_or_die(fh, "^%s\n", sha1_to_hex(peeled));
}
-/*
- * An each_ref_entry_fn that writes the entry to a packed-refs file.
- */
-static int write_packed_entry_fn(struct ref_entry *entry, void *cb_data)
-{
- enum peel_status peel_status = peel_entry(entry, 0);
-
- if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG)
- error("internal error: %s is not a valid packed reference!",
- entry->name);
- write_packed_entry(cb_data, entry->name, entry->u.value.oid.hash,
- peel_status == PEEL_PEELED ?
- entry->u.value.peeled.hash : NULL);
- return 0;
-}
-
/*
* Lock the packed-refs file for writing. Flags is passed to
* hold_lock_file_for_update(). Return 0 on success. On errors, set
{
struct packed_ref_cache *packed_ref_cache =
get_packed_ref_cache(refs);
- int error = 0;
+ int ok, error = 0;
int save_errno = 0;
FILE *out;
+ struct ref_iterator *iter;
files_assert_main_repository(refs, "commit_packed_refs");
die_errno("unable to fdopen packed-refs descriptor");
fprintf_or_die(out, "%s", PACKED_REFS_HEADER);
- do_for_each_entry_in_dir(get_packed_ref_dir(packed_ref_cache),
- 0, write_packed_entry_fn, out);
+
+ iter = cache_ref_iterator_begin(packed_ref_cache->cache, NULL, 0);
+ while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
+ struct object_id peeled;
+ int peel_error = ref_iterator_peel(iter, &peeled);
+
+ write_packed_entry(out, iter->refname, iter->oid->hash,
+ peel_error ? NULL : peeled.hash);
+ }
+
+ if (ok != ITER_DONE)
+ die("error while iterating over references");
if (commit_lock_file(packed_ref_cache->lock)) {
save_errno = errno;
char name[FLEX_ARRAY];
};
-struct pack_refs_cb_data {
- unsigned int flags;
- struct ref_dir *packed_refs;
- struct ref_to_prune *ref_to_prune;
-};
-
-/*
- * An each_ref_entry_fn that is run over loose references only. If
- * the loose reference can be packed, add an entry in the packed ref
- * cache. If the reference should be pruned, also add it to
- * ref_to_prune in the pack_refs_cb_data.
- */
-static int pack_if_possible_fn(struct ref_entry *entry, void *cb_data)
-{
- struct pack_refs_cb_data *cb = cb_data;
- enum peel_status peel_status;
- struct ref_entry *packed_entry;
- int is_tag_ref = starts_with(entry->name, "refs/tags/");
-
- /* Do not pack per-worktree refs: */
- if (ref_type(entry->name) != REF_TYPE_NORMAL)
- return 0;
-
- /* ALWAYS pack tags */
- if (!(cb->flags & PACK_REFS_ALL) && !is_tag_ref)
- return 0;
-
- /* Do not pack symbolic or broken refs: */
- if ((entry->flag & REF_ISSYMREF) || !entry_resolves_to_object(entry))
- return 0;
-
- /* Add a packed ref cache entry equivalent to the loose entry. */
- peel_status = peel_entry(entry, 1);
- if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG)
- die("internal error peeling reference %s (%s)",
- entry->name, oid_to_hex(&entry->u.value.oid));
- packed_entry = find_ref_entry(cb->packed_refs, entry->name);
- if (packed_entry) {
- /* Overwrite existing packed entry with info from loose entry */
- packed_entry->flag = REF_ISPACKED | REF_KNOWS_PEELED;
- oidcpy(&packed_entry->u.value.oid, &entry->u.value.oid);
- } else {
- packed_entry = create_ref_entry(entry->name, entry->u.value.oid.hash,
- REF_ISPACKED | REF_KNOWS_PEELED, 0);
- add_ref_entry(cb->packed_refs, packed_entry);
- }
- oidcpy(&packed_entry->u.value.peeled, &entry->u.value.peeled);
-
- /* Schedule the loose reference for pruning if requested. */
- if ((cb->flags & PACK_REFS_PRUNE)) {
- struct ref_to_prune *n;
- FLEX_ALLOC_STR(n, name, entry->name);
- hashcpy(n->sha1, entry->u.value.oid.hash);
- n->next = cb->ref_to_prune;
- cb->ref_to_prune = n;
- }
- return 0;
-}
-
enum {
REMOVE_EMPTY_PARENTS_REF = 0x01,
REMOVE_EMPTY_PARENTS_REFLOG = 0x02
struct files_ref_store *refs =
files_downcast(ref_store, REF_STORE_WRITE | REF_STORE_ODB,
"pack_refs");
- struct pack_refs_cb_data cbdata;
-
- memset(&cbdata, 0, sizeof(cbdata));
- cbdata.flags = flags;
+ struct ref_iterator *iter;
+ struct ref_dir *packed_refs;
+ int ok;
+ struct ref_to_prune *refs_to_prune = NULL;
lock_packed_refs(refs, LOCK_DIE_ON_ERROR);
- cbdata.packed_refs = get_packed_refs(refs);
+ packed_refs = get_packed_refs(refs);
- do_for_each_entry_in_dir(get_loose_refs(refs), 0,
- pack_if_possible_fn, &cbdata);
+ iter = cache_ref_iterator_begin(get_loose_ref_cache(refs), NULL, 0);
+ while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
+ /*
+ * If the loose reference can be packed, add an entry
+ * in the packed ref cache. If the reference should be
+ * pruned, also add it to refs_to_prune.
+ */
+ struct ref_entry *packed_entry;
+ int is_tag_ref = starts_with(iter->refname, "refs/tags/");
+
+ /* Do not pack per-worktree refs: */
+ if (ref_type(iter->refname) != REF_TYPE_NORMAL)
+ continue;
+
+ /* ALWAYS pack tags */
+ if (!(flags & PACK_REFS_ALL) && !is_tag_ref)
+ continue;
+
+ /* Do not pack symbolic or broken refs: */
+ if (iter->flags & REF_ISSYMREF)
+ continue;
+
+ if (!ref_resolves_to_object(iter->refname, iter->oid, iter->flags))
+ continue;
+
+ /*
+ * Create an entry in the packed-refs cache equivalent
+ * to the one from the loose ref cache, except that
+ * we don't copy the peeled status, because we want it
+ * to be re-peeled.
+ */
+ packed_entry = find_ref_entry(packed_refs, iter->refname);
+ if (packed_entry) {
+ /* Overwrite existing packed entry with info from loose entry */
+ packed_entry->flag = REF_ISPACKED;
+ oidcpy(&packed_entry->u.value.oid, iter->oid);
+ } else {
+ packed_entry = create_ref_entry(iter->refname, iter->oid->hash,
+ REF_ISPACKED, 0);
+ add_ref_entry(packed_refs, packed_entry);
+ }
+ oidclr(&packed_entry->u.value.peeled);
+
+ /* Schedule the loose reference for pruning if requested. */
+ if ((flags & PACK_REFS_PRUNE)) {
+ struct ref_to_prune *n;
+ FLEX_ALLOC_STR(n, name, iter->refname);
+ hashcpy(n->sha1, iter->oid->hash);
+ n->next = refs_to_prune;
+ refs_to_prune = n;
+ }
+ }
+ if (ok != ITER_DONE)
+ die("error while iterating over references");
if (commit_packed_refs(refs))
die_errno("unable to overwrite old ref-pack file");
- prune_refs(refs, cbdata.ref_to_prune);
+ prune_refs(refs, refs_to_prune);
return 0;
}
/* Jump to the end */
if (fseek(logfp, 0, SEEK_END) < 0)
- return error("cannot seek back reflog for %s: %s",
- refname, strerror(errno));
+ ret = error("cannot seek back reflog for %s: %s",
+ refname, strerror(errno));
pos = ftell(logfp);
while (!ret && 0 < pos) {
int cnt;
/* Fill next block from the end */
cnt = (sizeof(buf) < pos) ? sizeof(buf) : pos;
- if (fseek(logfp, pos - cnt, SEEK_SET))
- return error("cannot seek back reflog for %s: %s",
- refname, strerror(errno));
+ if (fseek(logfp, pos - cnt, SEEK_SET)) {
+ ret = error("cannot seek back reflog for %s: %s",
+ refname, strerror(errno));
+ break;
+ }
nread = fread(buf, cnt, 1, logfp);
- if (nread != 1)
- return error("cannot read %d bytes from reflog for %s: %s",
- cnt, refname, strerror(errno));
+ if (nread != 1) {
+ ret = error("cannot read %d bytes from reflog for %s: %s",
+ cnt, refname, strerror(errno));
+ break;
+ }
pos -= cnt;
scanp = endp = buf + cnt;