diff options
Diffstat (limited to 'refs')
-rw-r--r-- | refs/files-backend.c | 4511 | ||||
-rw-r--r-- | refs/iterator.c | 435 | ||||
-rw-r--r-- | refs/packed-backend.c | 1656 | ||||
-rw-r--r-- | refs/packed-backend.h | 35 | ||||
-rw-r--r-- | refs/ref-cache.c | 551 | ||||
-rw-r--r-- | refs/ref-cache.h | 240 | ||||
-rw-r--r-- | refs/refs-internal.h | 636 |
7 files changed, 5534 insertions, 2530 deletions
diff --git a/refs/files-backend.c b/refs/files-backend.c index 81f68f846b..bec8e30e9e 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -1,929 +1,86 @@ #include "../cache.h" +#include "../config.h" #include "../refs.h" #include "refs-internal.h" +#include "ref-cache.h" +#include "packed-backend.h" +#include "../iterator.h" +#include "../dir-iterator.h" #include "../lockfile.h" #include "../object.h" #include "../dir.h" -struct ref_lock { - char *ref_name; - char *orig_ref_name; - struct lock_file *lk; - 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 ref_cache; - -/* - * 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 ref_cache that contains this ref_dir. */ - struct ref_cache *ref_cache; - - 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 ref_cache *ref_cache, - const char *dirname, size_t len, - int incomplete); -static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry); - -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_cache, - "refs/bisect/", - 12, 1); - add_entry_to_dir(dir, child_entry); - read_loose_refs("refs/bisect", - &child_entry->u.subdir); - } - } - 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 ref_cache *ref_cache, - const char *dirname, size_t len, - int incomplete) -{ - struct ref_entry *direntry; - FLEX_ALLOC_MEM(direntry, name, dirname, len); - direntry->u.subdir.ref_cache = ref_cache; - 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. + * This backend uses the following flags in `ref_update::flags` for + * internal bookkeeping purposes. Their numerical values must not + * conflict with REF_NO_DEREF, REF_FORCE_CREATE_REFLOG, REF_HAVE_NEW, + * REF_HAVE_OLD, or REF_IS_PRUNING, which are also stored in + * `ref_update::flags`. */ -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_cache, 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. + * Used as a flag in ref_update::flags when a loose ref is being + * pruned. This flag must only be used when REF_NO_DEREF is set. */ -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; -} +#define REF_IS_PRUNING (1 << 4) /* - * 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. + * Flag passed to lock_ref_sha1_basic() telling it to tolerate broken + * refs (i.e., because the reference is about to be deleted anyway). */ -static struct ref_entry *find_ref(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; -} +#define REF_DELETING (1 << 5) /* - * 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. + * Used as a flag in ref_update::flags when the lockfile needs to be + * committed. */ -static int remove_entry(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; -} +#define REF_NEEDS_COMMIT (1 << 6) /* - * 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. + * Used as a flag in ref_update::flags when we want to log a ref + * update but not actually perform it. This is used when a symbolic + * ref update is split up. */ -static int add_ref(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; -} +#define REF_LOG_ONLY (1 << 7) /* - * 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. + * Used as a flag in ref_update::flags when the ref_update was via an + * update to HEAD. */ -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; -} +#define REF_UPDATE_VIA_HEAD (1 << 8) /* - * Sort the entries in dir non-recursively (if they are not already - * sorted) and remove any duplicate entries. + * Used as a flag in ref_update::flags when the loose reference has + * been deleted. */ -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, sizeof(*dir->entries), 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; -} - -/* Include broken references in a do_for_each_ref*() iteration: */ -#define DO_FOR_EACH_INCLUDE_BROKEN 0x01 +#define REF_DELETED_LOOSE (1 << 9) -/* - * Return true iff the reference described by entry can be resolved to - * an object in the database. Emit a warning if the referred-to - * object does not exist. - */ -static int ref_resolves_to_object(struct ref_entry *entry) -{ - if (entry->flag & REF_ISBROKEN) - return 0; - if (!has_sha1_file(entry->u.value.oid.hash)) { - error("%s does not point to a valid object!", entry->name); - return 0; - } - return 1; -} - -/* - * current_ref is a performance hack: when iterating over references - * using the for_each_ref*() functions, current_ref is set to the - * current reference's entry before calling the callback function. If - * the callback function calls peel_ref(), then peel_ref() first - * checks whether the reference to be peeled is the current reference - * (it usually is) and if so, returns that reference's peeled version - * if it is available. This avoids a refname lookup in a common case. - */ -static struct ref_entry *current_ref; - -typedef int each_ref_entry_fn(struct ref_entry *entry, void *cb_data); - -struct ref_entry_cb { - const char *base; - int trim; - int flags; - each_ref_fn *fn; - void *cb_data; -}; - -/* - * Handle one reference in a do_for_each_ref*()-style iteration, - * calling an each_ref_fn for each entry. - */ -static int do_one_ref(struct ref_entry *entry, void *cb_data) -{ - struct ref_entry_cb *data = cb_data; - struct ref_entry *old_current_ref; - int retval; - - if (!starts_with(entry->name, data->base)) - return 0; - - if (!(data->flags & DO_FOR_EACH_INCLUDE_BROKEN) && - !ref_resolves_to_object(entry)) - return 0; - - /* Store the old value, in case this is a recursive call: */ - old_current_ref = current_ref; - current_ref = entry; - retval = data->fn(entry->name + data->trim, &entry->u.value.oid, - entry->flag, data->cb_data); - current_ref = old_current_ref; - return retval; -} - -/* - * 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; -} - -/* - * Call fn for each reference in the union of dir1 and dir2, in order - * by refname. Recurse into subdirectories. If a value entry appears - * in both dir1 and dir2, then only process the version that is in - * dir2. The input dirs must already be sorted, but subdirs will be - * sorted as needed. fn is called for all references, including - * broken ones. - */ -static int do_for_each_entry_in_dirs(struct ref_dir *dir1, - struct ref_dir *dir2, - each_ref_entry_fn fn, void *cb_data) -{ - int retval; - int i1 = 0, i2 = 0; - - assert(dir1->sorted == dir1->nr); - assert(dir2->sorted == dir2->nr); - while (1) { - struct ref_entry *e1, *e2; - int cmp; - if (i1 == dir1->nr) { - return do_for_each_entry_in_dir(dir2, i2, fn, cb_data); - } - if (i2 == dir2->nr) { - return do_for_each_entry_in_dir(dir1, i1, fn, cb_data); - } - e1 = dir1->entries[i1]; - e2 = dir2->entries[i2]; - cmp = strcmp(e1->name, e2->name); - if (cmp == 0) { - if ((e1->flag & REF_DIR) && (e2->flag & REF_DIR)) { - /* Both are directories; descend them in parallel. */ - struct ref_dir *subdir1 = get_ref_dir(e1); - struct ref_dir *subdir2 = get_ref_dir(e2); - sort_ref_dir(subdir1); - sort_ref_dir(subdir2); - retval = do_for_each_entry_in_dirs( - subdir1, subdir2, fn, cb_data); - i1++; - i2++; - } else if (!(e1->flag & REF_DIR) && !(e2->flag & REF_DIR)) { - /* Both are references; ignore the one from dir1. */ - retval = fn(e2, cb_data); - i1++; - i2++; - } else { - die("conflict between reference and directory: %s", - e1->name); - } - } else { - struct ref_entry *e; - if (cmp < 0) { - e = e1; - i1++; - } else { - e = e2; - i2++; - } - if (e->flag & REF_DIR) { - struct ref_dir *subdir = get_ref_dir(e); - sort_ref_dir(subdir); - retval = do_for_each_entry_in_dir( - subdir, 0, fn, cb_data); - } else { - retval = fn(e, cb_data); - } - } - if (retval) - return retval; - } -} - -/* - * 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)); - } -} - -struct nonmatching_ref_data { - const struct string_list *skip; - const char *conflicting_refname; -}; - -static int nonmatching_ref_fn(struct ref_entry *entry, void *vdata) -{ - struct nonmatching_ref_data *data = vdata; - - if (data->skip && string_list_has_string(data->skip, entry->name)) - return 0; - - data->conflicting_refname = entry->name; - return 1; -} - -/* - * Return 0 if a reference named refname could be created without - * conflicting with the name of an existing reference in dir. - * See verify_refname_available for more information. - */ -static int verify_refname_available_dir(const char *refname, - const struct string_list *extras, - const struct string_list *skip, - struct ref_dir *dir, - struct strbuf *err) -{ - const char *slash; - const char *extra_refname; - int pos; - struct strbuf dirname = STRBUF_INIT; - int ret = -1; - - /* - * For the sake of comments in this function, suppose that - * refname is "refs/foo/bar". - */ - - assert(err); - - strbuf_grow(&dirname, strlen(refname) + 1); - for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) { - /* Expand dirname to the new prefix, not including the trailing slash: */ - strbuf_add(&dirname, refname + dirname.len, slash - refname - dirname.len); - - /* - * We are still at a leading dir of the refname (e.g., - * "refs/foo"; if there is a reference with that name, - * it is a conflict, *unless* it is in skip. - */ - if (dir) { - pos = search_ref_dir(dir, dirname.buf, dirname.len); - if (pos >= 0 && - (!skip || !string_list_has_string(skip, dirname.buf))) { - /* - * We found a reference whose name is - * a proper prefix of refname; e.g., - * "refs/foo", and is not in skip. - */ - strbuf_addf(err, "'%s' exists; cannot create '%s'", - dirname.buf, refname); - goto cleanup; - } - } - - if (extras && string_list_has_string(extras, dirname.buf) && - (!skip || !string_list_has_string(skip, dirname.buf))) { - strbuf_addf(err, "cannot process '%s' and '%s' at the same time", - refname, dirname.buf); - goto cleanup; - } - - /* - * Otherwise, we can try to continue our search with - * the next component. So try to look up the - * directory, e.g., "refs/foo/". If we come up empty, - * we know there is nothing under this whole prefix, - * but even in that case we still have to continue the - * search for conflicts with extras. - */ - strbuf_addch(&dirname, '/'); - if (dir) { - pos = search_ref_dir(dir, dirname.buf, dirname.len); - if (pos < 0) { - /* - * There was no directory "refs/foo/", - * so there is nothing under this - * whole prefix. So there is no need - * to continue looking for conflicting - * references. But we need to continue - * looking for conflicting extras. - */ - dir = NULL; - } else { - dir = get_ref_dir(dir->entries[pos]); - } - } - } - - /* - * We are at the leaf of our refname (e.g., "refs/foo/bar"). - * There is no point in searching for a reference with that - * name, because a refname isn't considered to conflict with - * itself. But we still need to check for references whose - * names are in the "refs/foo/bar/" namespace, because they - * *do* conflict. - */ - strbuf_addstr(&dirname, refname + dirname.len); - strbuf_addch(&dirname, '/'); - - if (dir) { - pos = search_ref_dir(dir, dirname.buf, dirname.len); - - if (pos >= 0) { - /* - * We found a directory named "$refname/" - * (e.g., "refs/foo/bar/"). It is a problem - * iff it contains any ref that is not in - * "skip". - */ - struct nonmatching_ref_data data; - - data.skip = skip; - data.conflicting_refname = NULL; - dir = get_ref_dir(dir->entries[pos]); - sort_ref_dir(dir); - if (do_for_each_entry_in_dir(dir, 0, nonmatching_ref_fn, &data)) { - strbuf_addf(err, "'%s' exists; cannot create '%s'", - data.conflicting_refname, refname); - goto cleanup; - } - } - } - - extra_refname = find_descendant_ref(dirname.buf, extras, skip); - if (extra_refname) - strbuf_addf(err, "cannot process '%s' and '%s' at the same time", - refname, extra_refname); - else - ret = 0; - -cleanup: - strbuf_release(&dirname); - return ret; -} - -struct packed_ref_cache { - struct ref_entry *root; - - /* - * Count of references to the data structure in this instance, - * including the pointer from ref_cache::packed if any. The - * data will not be freed as long as the reference count is - * nonzero. - */ - unsigned int referrers; - - /* - * Iff the packed-refs file associated with this instance is - * currently locked for writing, this points at the associated - * lock (which is owned by somebody else). The referrer count - * is also incremented when the file is locked and decremented - * when it is unlocked. - */ - struct lock_file *lock; - - /* The metadata from when this packed-refs cache was read */ - struct stat_validity validity; +struct ref_lock { + char *ref_name; + struct lock_file lk; + struct object_id old_oid; }; /* * Future: need to be in "struct repository" * when doing a full libification. */ -static struct ref_cache { - struct ref_cache *next; - struct ref_entry *loose; - struct packed_ref_cache *packed; - /* - * The submodule name, or "" for the main repo. We allocate - * length 1 rather than FLEX_ARRAY so that the main ref_cache - * is initialized correctly. - */ - char name[1]; -} ref_cache, *submodule_ref_caches; +struct files_ref_store { + struct ref_store base; + unsigned int store_flags; -/* Lock used for the main packed-refs file: */ -static struct lock_file packlock; + char *gitdir; + char *gitcommondir; -/* - * Increment the reference count of *packed_refs. - */ -static void acquire_packed_ref_cache(struct packed_ref_cache *packed_refs) -{ - packed_refs->referrers++; -} - -/* - * Decrease the reference count of *packed_refs. If it goes to zero, - * free *packed_refs and return true; otherwise return false. - */ -static int release_packed_ref_cache(struct packed_ref_cache *packed_refs) -{ - if (!--packed_refs->referrers) { - free_ref_entry(packed_refs->root); - stat_validity_clear(&packed_refs->validity); - free(packed_refs); - return 1; - } else { - return 0; - } -} + struct ref_cache *loose; -static void clear_packed_ref_cache(struct ref_cache *refs) -{ - if (refs->packed) { - struct packed_ref_cache *packed_refs = refs->packed; - - if (packed_refs->lock) - die("internal error: packed-ref cache cleared while locked"); - refs->packed = NULL; - release_packed_ref_cache(packed_refs); - } -} + struct ref_store *packed_ref_store; +}; -static void clear_loose_ref_cache(struct ref_cache *refs) +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; } } @@ -932,231 +89,98 @@ static void clear_loose_ref_cache(struct ref_cache *refs) * Create a new submodule ref cache and add it to the internal * set of caches. */ -static struct ref_cache *create_ref_cache(const char *submodule) -{ - struct ref_cache *refs; - if (!submodule) - submodule = ""; - FLEX_ALLOC_STR(refs, name, submodule); - refs->next = submodule_ref_caches; - submodule_ref_caches = refs; - return refs; -} - -static struct ref_cache *lookup_ref_cache(const char *submodule) +static struct ref_store *files_ref_store_create(const char *gitdir, + unsigned int flags) { - struct ref_cache *refs; + struct files_ref_store *refs = xcalloc(1, sizeof(*refs)); + struct ref_store *ref_store = (struct ref_store *)refs; + struct strbuf sb = STRBUF_INIT; - if (!submodule || !*submodule) - return &ref_cache; + base_ref_store_init(ref_store, &refs_be_files); + refs->store_flags = flags; - for (refs = submodule_ref_caches; refs; refs = refs->next) - if (!strcmp(submodule, refs->name)) - return refs; - return NULL; -} + refs->gitdir = xstrdup(gitdir); + get_common_dir_noenv(&sb, gitdir); + refs->gitcommondir = strbuf_detach(&sb, NULL); + strbuf_addf(&sb, "%s/packed-refs", refs->gitcommondir); + refs->packed_ref_store = packed_ref_store_create(sb.buf, flags); + strbuf_release(&sb); -/* - * Return a pointer to a ref_cache for the specified submodule. For - * the main repository, use submodule==NULL. The returned structure - * will be allocated and initialized but not necessarily populated; it - * should not be freed. - */ -static struct ref_cache *get_ref_cache(const char *submodule) -{ - struct ref_cache *refs = lookup_ref_cache(submodule); - if (!refs) - refs = create_ref_cache(submodule); - return refs; + return ref_store; } -/* The length of a peeled reference line in packed-refs, including EOL: */ -#define PEELED_LINE_LENGTH 42 - /* - * The packed-refs header line that we write out. Perhaps other - * traits will be added later. The trailing space is required. + * Die if refs is not the main ref store. caller is used in any + * necessary error messages. */ -static const char PACKED_REFS_HEADER[] = - "# pack-refs with: peeled fully-peeled \n"; - -/* - * Parse one line from a packed-refs file. Write the SHA1 to sha1. - * Return a pointer to the refname within the line (null-terminated), - * or NULL if there was a problem. - */ -static const char *parse_ref_line(struct strbuf *line, unsigned char *sha1) +static void files_assert_main_repository(struct files_ref_store *refs, + const char *caller) { - const char *ref; - - /* - * 42: the answer to everything. - * - * In this case, it happens to be the answer to - * 40 (length of sha1 hex representation) - * +1 (space in between hex and name) - * +1 (newline at the end of the line) - */ - if (line->len <= 42) - return NULL; - - if (get_sha1_hex(line->buf, sha1) < 0) - return NULL; - if (!isspace(line->buf[40])) - return NULL; - - ref = line->buf + 41; - if (isspace(*ref)) - return NULL; - - if (line->buf[line->len - 1] != '\n') - return NULL; - line->buf[--line->len] = 0; + if (refs->store_flags & REF_STORE_MAIN) + return; - return ref; + die("BUG: operation %s only allowed for main ref store", caller); } /* - * Read f, which is a packed-refs file, into dir. - * - * A comment line of the form "# pack-refs with: " may contain zero or - * more traits. We interpret the traits as follows: - * - * No traits: - * - * Probably no references are peeled. But if the file contains a - * peeled value for a reference, we will use it. - * - * peeled: - * - * References under "refs/tags/", if they *can* be peeled, *are* - * peeled in this file. References outside of "refs/tags/" are - * probably not peeled even if they could have been, but if we find - * a peeled value for such a reference we will use it. - * - * fully-peeled: - * - * All references in the file that can be peeled are peeled. - * Inversely (and this is more important), any references in the - * file for which no peeled value is recorded is not peelable. This - * trait should typically be written alongside "peeled" for - * compatibility with older clients, but we do not require it - * (i.e., "peeled" is a no-op if "fully-peeled" is set). + * Downcast ref_store to files_ref_store. Die if ref_store is not a + * files_ref_store. required_flags is compared with ref_store's + * store_flags to ensure the ref_store has all required capabilities. + * "caller" is used in any necessary error messages. */ -static void read_packed_refs(FILE *f, struct ref_dir *dir) -{ - struct ref_entry *last = NULL; - struct strbuf line = STRBUF_INIT; - enum { PEELED_NONE, PEELED_TAGS, PEELED_FULLY } peeled = PEELED_NONE; - - while (strbuf_getwholeline(&line, f, '\n') != EOF) { - unsigned char sha1[20]; - const char *refname; - const char *traits; - - if (skip_prefix(line.buf, "# pack-refs with:", &traits)) { - if (strstr(traits, " fully-peeled ")) - peeled = PEELED_FULLY; - else if (strstr(traits, " peeled ")) - peeled = PEELED_TAGS; - /* perhaps other traits later as well */ - continue; - } - - refname = parse_ref_line(&line, sha1); - if (refname) { - int flag = REF_ISPACKED; - - if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) { - if (!refname_is_safe(refname)) - die("packed refname is dangerous: %s", refname); - hashclr(sha1); - flag |= REF_BAD_NAME | REF_ISBROKEN; - } - last = create_ref_entry(refname, sha1, flag, 0); - if (peeled == PEELED_FULLY || - (peeled == PEELED_TAGS && starts_with(refname, "refs/tags/"))) - last->flag |= REF_KNOWS_PEELED; - add_ref(dir, last); - continue; - } - if (last && - line.buf[0] == '^' && - line.len == PEELED_LINE_LENGTH && - line.buf[PEELED_LINE_LENGTH - 1] == '\n' && - !get_sha1_hex(line.buf + 1, sha1)) { - hashcpy(last->u.value.peeled.hash, sha1); - /* - * Regardless of what the file header said, - * we definitely know the value of *this* - * reference: - */ - last->flag |= REF_KNOWS_PEELED; - } - } +static struct files_ref_store *files_downcast(struct ref_store *ref_store, + unsigned int required_flags, + const char *caller) +{ + struct files_ref_store *refs; - strbuf_release(&line); -} + if (ref_store->be != &refs_be_files) + die("BUG: ref_store is type \"%s\" not \"files\" in %s", + ref_store->be->name, caller); -/* - * Get the packed_ref_cache for the specified ref_cache, creating it - * if necessary. - */ -static struct packed_ref_cache *get_packed_ref_cache(struct ref_cache *refs) -{ - char *packed_refs_file; + refs = (struct files_ref_store *)ref_store; - if (*refs->name) - packed_refs_file = git_pathdup_submodule(refs->name, "packed-refs"); - else - packed_refs_file = git_pathdup("packed-refs"); - - if (refs->packed && - !stat_validity_check(&refs->packed->validity, packed_refs_file)) - clear_packed_ref_cache(refs); - - if (!refs->packed) { - FILE *f; - - refs->packed = xcalloc(1, sizeof(*refs->packed)); - acquire_packed_ref_cache(refs->packed); - refs->packed->root = create_dir_entry(refs, "", 0, 0); - 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)); - fclose(f); - } - } - free(packed_refs_file); - return refs->packed; -} + if ((refs->store_flags & required_flags) != required_flags) + die("BUG: operation %s requires abilities 0x%x, but only have 0x%x", + caller, required_flags, refs->store_flags); -static struct ref_dir *get_packed_ref_dir(struct packed_ref_cache *packed_ref_cache) -{ - return get_ref_dir(packed_ref_cache->root); + return refs; } -static struct ref_dir *get_packed_refs(struct ref_cache *refs) +static void files_reflog_path(struct files_ref_store *refs, + struct strbuf *sb, + const char *refname) { - return get_packed_ref_dir(get_packed_ref_cache(refs)); + switch (ref_type(refname)) { + case REF_TYPE_PER_WORKTREE: + case REF_TYPE_PSEUDOREF: + strbuf_addf(sb, "%s/logs/%s", refs->gitdir, refname); + break; + case REF_TYPE_NORMAL: + strbuf_addf(sb, "%s/logs/%s", refs->gitcommondir, refname); + break; + default: + die("BUG: unknown ref type %d of ref %s", + ref_type(refname), refname); + } } -/* - * Add a reference to the in-memory packed reference cache. This may - * only be called while the packed-refs file is locked (see - * lock_packed_refs()). To actually write the packed-refs file, call - * commit_packed_refs(). - */ -static void add_packed_ref(const char *refname, const unsigned char *sha1) +static void files_ref_path(struct files_ref_store *refs, + struct strbuf *sb, + const char *refname) { - struct packed_ref_cache *packed_ref_cache = - get_packed_ref_cache(&ref_cache); - - if (!packed_ref_cache->lock) - die("internal error: packed refs not locked"); - add_ref(get_packed_ref_dir(packed_ref_cache), - create_ref_entry(refname, sha1, REF_ISPACKED, 1)); + switch (ref_type(refname)) { + case REF_TYPE_PER_WORKTREE: + case REF_TYPE_PSEUDOREF: + strbuf_addf(sb, "%s/%s", refs->gitdir, refname); + break; + case REF_TYPE_NORMAL: + strbuf_addf(sb, "%s/%s", refs->gitcommondir, refname); + break; + default: + die("BUG: unknown ref type %d of ref %s", + ref_type(refname), refname); + } } /* @@ -1164,9 +188,11 @@ static void add_packed_ref(const char *refname, const unsigned char *sha1) * (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 ref_cache *refs = dir->ref_cache; + struct files_ref_store *refs = + files_downcast(ref_store, REF_STORE_READ, "fill_ref_dir"); DIR *d; struct dirent *de; int dirnamelen = strlen(dirname); @@ -1174,10 +200,7 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir) struct strbuf path = STRBUF_INIT; size_t path_baselen; - if (*refs->name) - strbuf_git_path_submodule(&path, refs->name, "%s", dirname); - else - strbuf_git_path(&path, "%s", dirname); + files_ref_path(refs, &path, dirname); path_baselen = path.len; d = opendir(path.buf); @@ -1190,7 +213,7 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir) strbuf_add(&refname, dirname, dirnamelen); while ((de = readdir(d)) != NULL) { - unsigned char sha1[20]; + struct object_id oid; struct stat st; int flag; @@ -1205,29 +228,19 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir) } 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 { - int read_ok; - - if (*refs->name) { - hashclr(sha1); - flag = 0; - read_ok = !resolve_gitlink_ref(refs->name, - refname.buf, sha1); - } else { - read_ok = !read_ref_full(refname.buf, - RESOLVE_REF_READING, - sha1, &flag); - } - - if (!read_ok) { - hashclr(sha1); + if (!refs_resolve_ref_unsafe(&refs->base, + refname.buf, + RESOLVE_REF_READING, + &oid, &flag)) { + oidclr(&oid); flag |= REF_ISBROKEN; - } else if (is_null_sha1(sha1)) { + } else if (is_null_oid(&oid)) { /* * It is so astronomically unlikely - * that NULL_SHA1 is the SHA-1 of an + * that null_oid is the OID of an * actual object that we consider its * appearance in a loose reference * file to be repo corruption @@ -1240,11 +253,11 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir) REFNAME_ALLOW_ONELEVEL)) { if (!refname_is_safe(refname.buf)) die("loose refname is dangerous: %s", refname.buf); - hashclr(sha1); + oidclr(&oid); flag |= REF_BAD_NAME | REF_ISBROKEN; } add_entry_to_dir(dir, - create_ref_entry(refname.buf, sha1, flag, 0)); + create_ref_entry(refname.buf, &oid, flag)); } strbuf_setlen(&refname, dirnamelen); strbuf_setlen(&path, path_baselen); @@ -1252,9 +265,24 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir) 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 ref_cache *refs) +static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs) { if (!refs->loose) { /* @@ -1262,609 +290,560 @@ static struct ref_dir *get_loose_refs(struct ref_cache *refs) * 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); -} - -/* We allow "recursive" symbolic refs. Only within reason, though */ -#define MAXDEPTH 5 -#define MAXREFLEN (1024) - -/* - * Called by resolve_gitlink_ref_recursive() after it failed to read - * from the loose refs in ref_cache refs. Find <refname> in the - * packed-refs file for the submodule. - */ -static int resolve_gitlink_packed_ref(struct ref_cache *refs, - const char *refname, unsigned char *sha1) -{ - struct ref_entry *ref; - struct ref_dir *dir = get_packed_refs(refs); - - ref = find_ref(dir, refname); - if (ref == NULL) - return -1; - - hashcpy(sha1, ref->u.value.oid.hash); - return 0; -} - -static int resolve_gitlink_ref_recursive(struct ref_cache *refs, - const char *refname, unsigned char *sha1, - int recursion) -{ - int fd, len; - char buffer[128], *p; - char *path; - - if (recursion > MAXDEPTH || strlen(refname) > MAXREFLEN) - return -1; - path = *refs->name - ? git_pathdup_submodule(refs->name, "%s", refname) - : git_pathdup("%s", refname); - fd = open(path, O_RDONLY); - free(path); - if (fd < 0) - return resolve_gitlink_packed_ref(refs, refname, sha1); - - len = read(fd, buffer, sizeof(buffer)-1); - close(fd); - if (len < 0) - return -1; - while (len && isspace(buffer[len-1])) - len--; - buffer[len] = 0; - - /* Was it a detached head or an old-fashioned symlink? */ - if (!get_sha1_hex(buffer, sha1)) - return 0; - - /* Symref? */ - if (strncmp(buffer, "ref:", 4)) - return -1; - p = buffer + 4; - while (isspace(*p)) - p++; - - return resolve_gitlink_ref_recursive(refs, p, sha1, recursion+1); + return refs->loose; } -int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1) +static int files_read_raw_ref(struct ref_store *ref_store, + const char *refname, struct object_id *oid, + struct strbuf *referent, unsigned int *type) { - int len = strlen(path), retval; - struct strbuf submodule = STRBUF_INIT; - struct ref_cache *refs; - - while (len && path[len-1] == '/') - len--; - if (!len) - return -1; - - strbuf_add(&submodule, path, len); - refs = lookup_ref_cache(submodule.buf); - if (!refs) { - if (!is_nonbare_repository_dir(&submodule)) { - strbuf_release(&submodule); - return -1; - } - refs = create_ref_cache(submodule.buf); - } - strbuf_release(&submodule); + struct files_ref_store *refs = + files_downcast(ref_store, REF_STORE_READ, "read_raw_ref"); + struct strbuf sb_contents = STRBUF_INIT; + struct strbuf sb_path = STRBUF_INIT; + const char *path; + const char *buf; + const char *p; + struct stat st; + int fd; + int ret = -1; + int save_errno; + int remaining_retries = 3; - retval = resolve_gitlink_ref_recursive(refs, refname, sha1, 0); - return retval; -} + *type = 0; + strbuf_reset(&sb_path); -/* - * Return the ref_entry for the given refname from the packed - * references. If it does not exist, return NULL. - */ -static struct ref_entry *get_packed_ref(const char *refname) -{ - return find_ref(get_packed_refs(&ref_cache), refname); -} + files_ref_path(refs, &sb_path, refname); -/* - * A loose ref file doesn't exist; check for a packed ref. The - * options are forwarded from resolve_safe_unsafe(). - */ -static int resolve_missing_loose_ref(const char *refname, - int resolve_flags, - unsigned char *sha1, - int *flags) -{ - struct ref_entry *entry; + path = sb_path.buf; +stat_ref: /* - * The loose reference file does not exist; check for a packed - * reference. + * We might have to loop back here to avoid a race + * condition: first we lstat() the file, then we try + * to read it as a link or as a file. But if somebody + * changes the type of the file (file <-> directory + * <-> symlink) between the lstat() and reading, then + * we don't want to report that as an error but rather + * try again starting with the lstat(). + * + * We'll keep a count of the retries, though, just to avoid + * any confusing situation sending us into an infinite loop. */ - entry = get_packed_ref(refname); - if (entry) { - hashcpy(sha1, entry->u.value.oid.hash); - if (flags) - *flags |= REF_ISPACKED; - return 0; - } - /* The reference is not a packed reference, either. */ - if (resolve_flags & RESOLVE_REF_READING) { - errno = ENOENT; - return -1; - } else { - hashclr(sha1); - return 0; - } -} - -/* This function needs to return a meaningful errno on failure */ -static const char *resolve_ref_1(const char *refname, - int resolve_flags, - unsigned char *sha1, - int *flags, - struct strbuf *sb_refname, - struct strbuf *sb_path, - struct strbuf *sb_contents) -{ - int depth = MAXDEPTH; - int bad_name = 0; - - if (flags) - *flags = 0; - if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) { - if (flags) - *flags |= REF_BAD_NAME; + if (remaining_retries-- <= 0) + goto out; - if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) || - !refname_is_safe(refname)) { - errno = EINVAL; - return NULL; + if (lstat(path, &st) < 0) { + if (errno != ENOENT) + goto out; + if (refs_read_raw_ref(refs->packed_ref_store, refname, + oid, referent, type)) { + errno = ENOENT; + goto out; } - /* - * dwim_ref() uses REF_ISBROKEN to distinguish between - * missing refs and refs that were present but invalid, - * to complain about the latter to stderr. - * - * We don't know whether the ref exists, so don't set - * REF_ISBROKEN yet. - */ - bad_name = 1; + ret = 0; + goto out; } - for (;;) { - const char *path; - struct stat st; - char *buf; - int fd; - if (--depth < 0) { - errno = ELOOP; - return NULL; - } - - strbuf_reset(sb_path); - strbuf_git_path(sb_path, "%s", refname); - path = sb_path->buf; - - /* - * We might have to loop back here to avoid a race - * condition: first we lstat() the file, then we try - * to read it as a link or as a file. But if somebody - * changes the type of the file (file <-> directory - * <-> symlink) between the lstat() and reading, then - * we don't want to report that as an error but rather - * try again starting with the lstat(). - */ - stat_ref: - if (lstat(path, &st) < 0) { - if (errno != ENOENT) - return NULL; - if (resolve_missing_loose_ref(refname, resolve_flags, - sha1, flags)) - return NULL; - if (bad_name) { - hashclr(sha1); - if (flags) - *flags |= REF_ISBROKEN; - } - return refname; - } - - /* Follow "normalized" - ie "refs/.." symlinks by hand */ - if (S_ISLNK(st.st_mode)) { - strbuf_reset(sb_contents); - if (strbuf_readlink(sb_contents, path, 0) < 0) { - if (errno == ENOENT || errno == EINVAL) - /* inconsistent with lstat; retry */ - goto stat_ref; - else - return NULL; - } - if (starts_with(sb_contents->buf, "refs/") && - !check_refname_format(sb_contents->buf, 0)) { - strbuf_swap(sb_refname, sb_contents); - refname = sb_refname->buf; - if (flags) - *flags |= REF_ISSYMREF; - if (resolve_flags & RESOLVE_REF_NO_RECURSE) { - hashclr(sha1); - return refname; - } - continue; - } - } - - /* Is it a directory? */ - if (S_ISDIR(st.st_mode)) { - errno = EISDIR; - return NULL; - } - - /* - * Anything else, just open it and try to use it as - * a ref - */ - fd = open(path, O_RDONLY); - if (fd < 0) { - if (errno == ENOENT) + /* Follow "normalized" - ie "refs/.." symlinks by hand */ + if (S_ISLNK(st.st_mode)) { + strbuf_reset(&sb_contents); + if (strbuf_readlink(&sb_contents, path, 0) < 0) { + if (errno == ENOENT || errno == EINVAL) /* inconsistent with lstat; retry */ goto stat_ref; else - return NULL; + goto out; } - strbuf_reset(sb_contents); - if (strbuf_read(sb_contents, fd, 256) < 0) { - int save_errno = errno; - close(fd); - errno = save_errno; - return NULL; + if (starts_with(sb_contents.buf, "refs/") && + !check_refname_format(sb_contents.buf, 0)) { + strbuf_swap(&sb_contents, referent); + *type |= REF_ISSYMREF; + ret = 0; + goto out; } - close(fd); - strbuf_rtrim(sb_contents); + /* + * It doesn't look like a refname; fall through to just + * treating it like a non-symlink, and reading whatever it + * points to. + */ + } + /* Is it a directory? */ + if (S_ISDIR(st.st_mode)) { /* - * Is it a symbolic ref? + * Even though there is a directory where the loose + * ref is supposed to be, there could still be a + * packed ref: */ - if (!starts_with(sb_contents->buf, "ref:")) { - /* - * Please note that FETCH_HEAD has a second - * line containing other data. - */ - if (get_sha1_hex(sb_contents->buf, sha1) || - (sb_contents->buf[40] != '\0' && !isspace(sb_contents->buf[40]))) { - if (flags) - *flags |= REF_ISBROKEN; - errno = EINVAL; - return NULL; - } - if (bad_name) { - hashclr(sha1); - if (flags) - *flags |= REF_ISBROKEN; - } - return refname; + if (refs_read_raw_ref(refs->packed_ref_store, refname, + oid, referent, type)) { + errno = EISDIR; + goto out; } - if (flags) - *flags |= REF_ISSYMREF; - buf = sb_contents->buf + 4; + ret = 0; + goto out; + } + + /* + * Anything else, just open it and try to use it as + * a ref + */ + fd = open(path, O_RDONLY); + if (fd < 0) { + if (errno == ENOENT && !S_ISLNK(st.st_mode)) + /* inconsistent with lstat; retry */ + goto stat_ref; + else + goto out; + } + strbuf_reset(&sb_contents); + if (strbuf_read(&sb_contents, fd, 256) < 0) { + int save_errno = errno; + close(fd); + errno = save_errno; + goto out; + } + close(fd); + strbuf_rtrim(&sb_contents); + buf = sb_contents.buf; + if (starts_with(buf, "ref:")) { + buf += 4; while (isspace(*buf)) buf++; - strbuf_reset(sb_refname); - strbuf_addstr(sb_refname, buf); - refname = sb_refname->buf; - if (resolve_flags & RESOLVE_REF_NO_RECURSE) { - hashclr(sha1); - return refname; - } - if (check_refname_format(buf, REFNAME_ALLOW_ONELEVEL)) { - if (flags) - *flags |= REF_ISBROKEN; - - if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) || - !refname_is_safe(buf)) { - errno = EINVAL; - return NULL; - } - bad_name = 1; - } + + strbuf_reset(referent); + strbuf_addstr(referent, buf); + *type |= REF_ISSYMREF; + ret = 0; + goto out; } -} -const char *resolve_ref_unsafe(const char *refname, int resolve_flags, - unsigned char *sha1, int *flags) -{ - static struct strbuf sb_refname = STRBUF_INIT; - struct strbuf sb_contents = STRBUF_INIT; - struct strbuf sb_path = STRBUF_INIT; - const char *ret; + /* + * Please note that FETCH_HEAD has additional + * data after the sha. + */ + if (parse_oid_hex(buf, oid, &p) || + (*p != '\0' && !isspace(*p))) { + *type |= REF_ISBROKEN; + errno = EINVAL; + goto out; + } - ret = resolve_ref_1(refname, resolve_flags, sha1, flags, - &sb_refname, &sb_path, &sb_contents); + ret = 0; + +out: + save_errno = errno; strbuf_release(&sb_path); strbuf_release(&sb_contents); + errno = save_errno; return ret; } +static void unlock_ref(struct ref_lock *lock) +{ + rollback_lock_file(&lock->lk); + free(lock->ref_name); + free(lock); +} + /* - * 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. + * Lock refname, without following symrefs, and set *lock_p to point + * at a newly-allocated lock object. Fill in lock->old_oid, referent, + * and type similarly to read_raw_ref(). + * + * The caller must verify that refname is a "safe" reference name (in + * the sense of refname_is_safe()) before calling this function. + * + * If the reference doesn't already exist, verify that refname doesn't + * have a D/F conflict with any existing references. extras and skip + * are passed to refs_verify_refname_available() for this check. + * + * If mustexist is not set and the reference is not found or is + * broken, lock the reference anyway but clear old_oid. * - * 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. + * Return 0 on success. On failure, write an error message to err and + * return TRANSACTION_NAME_CONFLICT or TRANSACTION_GENERIC_ERROR. + * + * Implementation note: This function is basically + * + * lock reference + * read_raw_ref() + * + * but it includes a lot more code to + * - Deal with possible races with other processes + * - Avoid calling refs_verify_refname_available() when it can be + * avoided, namely if we were successfully able to read the ref + * - Generate informative error messages in the case of failure */ -static enum peel_status peel_entry(struct ref_entry *entry, int repeel) +static int lock_raw_ref(struct files_ref_store *refs, + const char *refname, int mustexist, + const struct string_list *extras, + const struct string_list *skip, + struct ref_lock **lock_p, + struct strbuf *referent, + unsigned int *type, + struct strbuf *err) { - enum peel_status status; + struct ref_lock *lock; + struct strbuf ref_file = STRBUF_INIT; + int attempts_remaining = 3; + int ret = TRANSACTION_GENERIC_ERROR; - 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; + assert(err); + files_assert_main_repository(refs, "lock_raw_ref"); - 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; -} + *type = 0; -int peel_ref(const char *refname, unsigned char *sha1) -{ - int flag; - unsigned char base[20]; + /* First lock the file so it can't change out from under us. */ - if (current_ref && (current_ref->name == refname - || !strcmp(current_ref->name, refname))) { - if (peel_entry(current_ref, 0)) - return -1; - hashcpy(sha1, current_ref->u.value.peeled.hash); - return 0; - } + *lock_p = lock = xcalloc(1, sizeof(*lock)); - if (read_ref_full(refname, RESOLVE_REF_READING, base, &flag)) - return -1; + lock->ref_name = xstrdup(refname); + files_ref_path(refs, &ref_file, refname); - /* - * If the reference is packed, read its ref_entry from the - * cache in the hope that we already know its peeled value. - * We only try this optimization on packed references because - * (a) forcing the filling of the loose reference cache could - * be expensive and (b) loose references anyway usually do not - * have REF_KNOWS_PEELED. - */ - if (flag & REF_ISPACKED) { - struct ref_entry *r = get_packed_ref(refname); - if (r) { - if (peel_entry(r, 0)) - return -1; - hashcpy(sha1, r->u.value.peeled.hash); - return 0; +retry: + switch (safe_create_leading_directories(ref_file.buf)) { + case SCLD_OK: + break; /* success */ + case SCLD_EXISTS: + /* + * Suppose refname is "refs/foo/bar". We just failed + * to create the containing directory, "refs/foo", + * because there was a non-directory in the way. This + * indicates a D/F conflict, probably because of + * another reference such as "refs/foo". There is no + * reason to expect this error to be transitory. + */ + if (refs_verify_refname_available(&refs->base, refname, + extras, skip, err)) { + if (mustexist) { + /* + * To the user the relevant error is + * that the "mustexist" reference is + * missing: + */ + strbuf_reset(err); + strbuf_addf(err, "unable to resolve reference '%s'", + refname); + } else { + /* + * The error message set by + * refs_verify_refname_available() is + * OK. + */ + ret = TRANSACTION_NAME_CONFLICT; + } + } else { + /* + * The file that is in the way isn't a loose + * reference. Report it as a low-level + * failure. + */ + strbuf_addf(err, "unable to create lock file %s.lock; " + "non-directory in the way", + ref_file.buf); } + goto error_return; + case SCLD_VANISHED: + /* Maybe another process was tidying up. Try again. */ + if (--attempts_remaining > 0) + goto retry; + /* fall through */ + default: + strbuf_addf(err, "unable to create directory for %s", + ref_file.buf); + goto error_return; } - return peel_object(base, sha1); -} - -/* - * Call fn for each reference in the specified ref_cache, omitting - * references not in the containing_dir of base. fn is called for all - * references, including broken ones. If fn ever returns a non-zero - * value, stop the iteration and return that value; otherwise, return - * 0. - */ -static int do_for_each_entry(struct ref_cache *refs, const char *base, - each_ref_entry_fn fn, void *cb_data) -{ - struct packed_ref_cache *packed_ref_cache; - struct ref_dir *loose_dir; - struct ref_dir *packed_dir; - int retval = 0; + if (hold_lock_file_for_update_timeout( + &lock->lk, ref_file.buf, LOCK_NO_DEREF, + get_files_ref_lock_timeout_ms()) < 0) { + if (errno == ENOENT && --attempts_remaining > 0) { + /* + * Maybe somebody just deleted one of the + * directories leading to ref_file. Try + * again: + */ + goto retry; + } else { + unable_to_lock_message(ref_file.buf, errno, err); + goto error_return; + } + } /* - * We must make sure that all loose refs are read before accessing the - * packed-refs file; this avoids a race condition in which loose refs - * are migrated to the packed-refs file by a simultaneous process, but - * our in-memory view is from before the migration. get_packed_ref_cache() - * takes care of making sure our view is up to date with what is on - * disk. + * Now we hold the lock and can read the reference without + * fear that its value will change. */ - loose_dir = get_loose_refs(refs); - if (base && *base) { - loose_dir = find_containing_dir(loose_dir, base, 0); - } - if (loose_dir) - prime_ref_dir(loose_dir); - packed_ref_cache = get_packed_ref_cache(refs); - acquire_packed_ref_cache(packed_ref_cache); - packed_dir = get_packed_ref_dir(packed_ref_cache); - if (base && *base) { - packed_dir = find_containing_dir(packed_dir, base, 0); - } + if (files_read_raw_ref(&refs->base, refname, + &lock->old_oid, referent, type)) { + if (errno == ENOENT) { + if (mustexist) { + /* Garden variety missing reference. */ + strbuf_addf(err, "unable to resolve reference '%s'", + refname); + goto error_return; + } else { + /* + * Reference is missing, but that's OK. We + * know that there is not a conflict with + * another loose reference because + * (supposing that we are trying to lock + * reference "refs/foo/bar"): + * + * - We were successfully able to create + * the lockfile refs/foo/bar.lock, so we + * know there cannot be a loose reference + * named "refs/foo". + * + * - We got ENOENT and not EISDIR, so we + * know that there cannot be a loose + * reference named "refs/foo/bar/baz". + */ + } + } else if (errno == EISDIR) { + /* + * There is a directory in the way. It might have + * contained references that have been deleted. If + * we don't require that the reference already + * exists, try to remove the directory so that it + * doesn't cause trouble when we want to rename the + * lockfile into place later. + */ + if (mustexist) { + /* Garden variety missing reference. */ + strbuf_addf(err, "unable to resolve reference '%s'", + refname); + goto error_return; + } else if (remove_dir_recursively(&ref_file, + REMOVE_DIR_EMPTY_ONLY)) { + if (refs_verify_refname_available( + &refs->base, refname, + extras, skip, err)) { + /* + * The error message set by + * verify_refname_available() is OK. + */ + ret = TRANSACTION_NAME_CONFLICT; + goto error_return; + } else { + /* + * We can't delete the directory, + * but we also don't know of any + * references that it should + * contain. + */ + strbuf_addf(err, "there is a non-empty directory '%s' " + "blocking reference '%s'", + ref_file.buf, refname); + goto error_return; + } + } + } else if (errno == EINVAL && (*type & REF_ISBROKEN)) { + strbuf_addf(err, "unable to resolve reference '%s': " + "reference broken", refname); + goto error_return; + } else { + strbuf_addf(err, "unable to resolve reference '%s': %s", + refname, strerror(errno)); + goto error_return; + } - if (packed_dir && loose_dir) { - sort_ref_dir(packed_dir); - sort_ref_dir(loose_dir); - retval = do_for_each_entry_in_dirs( - packed_dir, loose_dir, fn, cb_data); - } else if (packed_dir) { - sort_ref_dir(packed_dir); - retval = do_for_each_entry_in_dir( - packed_dir, 0, fn, cb_data); - } else if (loose_dir) { - sort_ref_dir(loose_dir); - retval = do_for_each_entry_in_dir( - loose_dir, 0, fn, cb_data); + /* + * If the ref did not exist and we are creating it, + * make sure there is no existing packed ref that + * conflicts with refname: + */ + if (refs_verify_refname_available( + refs->packed_ref_store, refname, + extras, skip, err)) + goto error_return; } - release_packed_ref_cache(packed_ref_cache); - return retval; -} - -/* - * Call fn for each reference in the specified ref_cache for which the - * refname begins with base. If trim is non-zero, then trim that many - * characters off the beginning of each refname before passing the - * refname to fn. flags can be DO_FOR_EACH_INCLUDE_BROKEN to include - * broken references in the iteration. If fn ever returns a non-zero - * value, stop the iteration and return that value; otherwise, return - * 0. - */ -static int do_for_each_ref(struct ref_cache *refs, const char *base, - each_ref_fn fn, int trim, int flags, void *cb_data) -{ - struct ref_entry_cb data; - data.base = base; - data.trim = trim; - data.flags = flags; - data.fn = fn; - data.cb_data = cb_data; + ret = 0; + goto out; - if (ref_paranoia < 0) - ref_paranoia = git_env_bool("GIT_REF_PARANOIA", 0); - if (ref_paranoia) - data.flags |= DO_FOR_EACH_INCLUDE_BROKEN; +error_return: + unlock_ref(lock); + *lock_p = NULL; - return do_for_each_entry(refs, base, do_one_ref, &data); +out: + strbuf_release(&ref_file); + return ret; } -static int do_head_ref(const char *submodule, each_ref_fn fn, void *cb_data) +struct files_ref_iterator { + struct ref_iterator base; + + struct ref_iterator *iter0; + unsigned int flags; +}; + +static int files_ref_iterator_advance(struct ref_iterator *ref_iterator) { - struct object_id oid; - int flag; + struct files_ref_iterator *iter = + (struct files_ref_iterator *)ref_iterator; + int ok; - if (submodule) { - if (resolve_gitlink_ref(submodule, "HEAD", oid.hash) == 0) - return fn("HEAD", &oid, 0, cb_data); + while ((ok = ref_iterator_advance(iter->iter0)) == ITER_OK) { + if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY && + ref_type(iter->iter0->refname) != REF_TYPE_PER_WORKTREE) + continue; - return 0; + if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) && + !ref_resolves_to_object(iter->iter0->refname, + iter->iter0->oid, + iter->iter0->flags)) + continue; + + iter->base.refname = iter->iter0->refname; + iter->base.oid = iter->iter0->oid; + iter->base.flags = iter->iter0->flags; + return ITER_OK; } - if (!read_ref_full("HEAD", RESOLVE_REF_READING, oid.hash, &flag)) - return fn("HEAD", &oid, flag, cb_data); + iter->iter0 = NULL; + if (ref_iterator_abort(ref_iterator) != ITER_DONE) + ok = ITER_ERROR; - return 0; + return ok; } -int head_ref(each_ref_fn fn, void *cb_data) +static int files_ref_iterator_peel(struct ref_iterator *ref_iterator, + struct object_id *peeled) { - return do_head_ref(NULL, fn, cb_data); -} + struct files_ref_iterator *iter = + (struct files_ref_iterator *)ref_iterator; -int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data) -{ - return do_head_ref(submodule, fn, cb_data); + return ref_iterator_peel(iter->iter0, peeled); } -int for_each_ref(each_ref_fn fn, void *cb_data) +static int files_ref_iterator_abort(struct ref_iterator *ref_iterator) { - return do_for_each_ref(&ref_cache, "", fn, 0, 0, cb_data); -} + struct files_ref_iterator *iter = + (struct files_ref_iterator *)ref_iterator; + int ok = ITER_DONE; -int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data) -{ - return do_for_each_ref(get_ref_cache(submodule), "", fn, 0, 0, cb_data); -} + if (iter->iter0) + ok = ref_iterator_abort(iter->iter0); -int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data) -{ - return do_for_each_ref(&ref_cache, prefix, fn, strlen(prefix), 0, cb_data); + base_ref_iterator_free(ref_iterator); + return ok; } -int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsigned int broken) +static struct ref_iterator_vtable files_ref_iterator_vtable = { + files_ref_iterator_advance, + files_ref_iterator_peel, + files_ref_iterator_abort +}; + +static struct ref_iterator *files_ref_iterator_begin( + struct ref_store *ref_store, + const char *prefix, unsigned int flags) { - unsigned int flag = 0; + struct files_ref_store *refs; + struct ref_iterator *loose_iter, *packed_iter, *overlay_iter; + struct files_ref_iterator *iter; + struct ref_iterator *ref_iterator; + unsigned int required_flags = REF_STORE_READ; - if (broken) - flag = DO_FOR_EACH_INCLUDE_BROKEN; - return do_for_each_ref(&ref_cache, prefix, fn, 0, flag, cb_data); -} + if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) + required_flags |= REF_STORE_ODB; -int for_each_ref_in_submodule(const char *submodule, const char *prefix, - each_ref_fn fn, void *cb_data) -{ - return do_for_each_ref(get_ref_cache(submodule), prefix, fn, strlen(prefix), 0, cb_data); -} + refs = files_downcast(ref_store, required_flags, "ref_iterator_begin"); -int for_each_replace_ref(each_ref_fn fn, void *cb_data) -{ - return do_for_each_ref(&ref_cache, git_replace_ref_base, fn, - strlen(git_replace_ref_base), 0, cb_data); -} + /* + * We must make sure that all loose refs are read before + * accessing the packed-refs file; this avoids a race + * 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 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 + * packed_ref_iterator_begin(), which internally checks + * whether the packed-ref cache is up to date with what is on + * disk, and re-reads it if not. + */ -int for_each_namespaced_ref(each_ref_fn fn, void *cb_data) -{ - struct strbuf buf = STRBUF_INIT; - int ret; - strbuf_addf(&buf, "%srefs/", get_git_namespace()); - ret = do_for_each_ref(&ref_cache, buf.buf, fn, 0, 0, cb_data); - strbuf_release(&buf); - return ret; -} + loose_iter = cache_ref_iterator_begin(get_loose_ref_cache(refs), + prefix, 1); -int for_each_rawref(each_ref_fn fn, void *cb_data) -{ - return do_for_each_ref(&ref_cache, "", fn, 0, - DO_FOR_EACH_INCLUDE_BROKEN, cb_data); -} + /* + * The packed-refs file might contain broken references, for + * example an old version of a reference that points at an + * object that has since been garbage-collected. This is OK as + * long as there is a corresponding loose reference that + * overrides it, and we don't want to emit an error message in + * this case. So ask the packed_ref_store for all of its + * references, and (if needed) do our own check for broken + * ones in files_ref_iterator_advance(), after we have merged + * the packed and loose references. + */ + packed_iter = refs_ref_iterator_begin( + refs->packed_ref_store, prefix, 0, + DO_FOR_EACH_INCLUDE_BROKEN); -static void unlock_ref(struct ref_lock *lock) -{ - /* Do not free lock->lk -- atexit() still looks at them */ - if (lock->lk) - rollback_lock_file(lock->lk); - free(lock->ref_name); - free(lock->orig_ref_name); - free(lock); + overlay_iter = overlay_ref_iterator_begin(loose_iter, packed_iter); + + iter = xcalloc(1, sizeof(*iter)); + ref_iterator = &iter->base; + base_ref_iterator_init(ref_iterator, &files_ref_iterator_vtable, + overlay_iter->ordered); + iter->iter0 = overlay_iter; + iter->flags = flags; + + return ref_iterator; } /* - * Verify that the reference locked by lock has the value old_sha1. - * Fail if the reference doesn't exist and mustexist is set. Return 0 - * on success. On error, write an error message to err, set errno, and - * return a negative value. + * Verify that the reference locked by lock has the value old_oid + * (unless it is NULL). Fail if the reference doesn't exist and + * mustexist is set. Return 0 on success. On error, write an error + * message to err, set errno, and return a negative value. */ -static int verify_lock(struct ref_lock *lock, - const unsigned char *old_sha1, int mustexist, +static int verify_lock(struct ref_store *ref_store, struct ref_lock *lock, + const struct object_id *old_oid, int mustexist, struct strbuf *err) { assert(err); - if (read_ref_full(lock->ref_name, - mustexist ? RESOLVE_REF_READING : 0, - lock->old_oid.hash, NULL)) { - if (old_sha1) { + if (refs_read_ref_full(ref_store, lock->ref_name, + mustexist ? RESOLVE_REF_READING : 0, + &lock->old_oid, NULL)) { + if (old_oid) { int save_errno = errno; - strbuf_addf(err, "can't verify ref %s", lock->ref_name); + strbuf_addf(err, "can't verify ref '%s'", lock->ref_name); errno = save_errno; return -1; } else { - hashclr(lock->old_oid.hash); + oidclr(&lock->old_oid); return 0; } } - if (old_sha1 && hashcmp(lock->old_oid.hash, old_sha1)) { - strbuf_addf(err, "ref %s is at %s but expected %s", + if (old_oid && oidcmp(&lock->old_oid, old_oid)) { + strbuf_addf(err, "ref '%s' is at %s but expected %s", lock->ref_name, - sha1_to_hex(lock->old_oid.hash), - sha1_to_hex(old_sha1)); + oid_to_hex(&lock->old_oid), + oid_to_hex(old_oid)); errno = EBUSY; return -1; } @@ -1881,28 +860,35 @@ static int remove_empty_directories(struct strbuf *path) return remove_dir_recursively(path, REMOVE_DIR_EMPTY_ONLY); } +static int create_reflock(const char *path, void *cb) +{ + struct lock_file *lk = cb; + + return hold_lock_file_for_update_timeout( + lk, path, LOCK_NO_DEREF, + get_files_ref_lock_timeout_ms()) < 0 ? -1 : 0; +} + /* * Locks a ref returning the lock on success and NULL on failure. * On failure errno is set to something meaningful. */ -static struct ref_lock *lock_ref_sha1_basic(const char *refname, - const unsigned char *old_sha1, - const struct string_list *extras, - const struct string_list *skip, - unsigned int flags, int *type_p, - struct strbuf *err) +static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs, + const char *refname, + const struct object_id *old_oid, + const struct string_list *extras, + const struct string_list *skip, + unsigned int flags, int *type, + struct strbuf *err) { struct strbuf ref_file = STRBUF_INIT; - struct strbuf orig_ref_file = STRBUF_INIT; - const char *orig_refname = refname; struct ref_lock *lock; int last_errno = 0; - int type; - int lflags = 0; - int mustexist = (old_sha1 && !is_null_sha1(old_sha1)); - int resolve_flags = 0; - int attempts_remaining = 3; + int mustexist = (old_oid && !is_null_oid(old_oid)); + int resolve_flags = RESOLVE_REF_NO_RECURSE; + int resolved; + files_assert_main_repository(refs, "lock_ref_oid_basic"); assert(err); lock = xcalloc(1, sizeof(struct ref_lock)); @@ -1911,48 +897,42 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, resolve_flags |= RESOLVE_REF_READING; if (flags & REF_DELETING) resolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME; - if (flags & REF_NODEREF) { - resolve_flags |= RESOLVE_REF_NO_RECURSE; - lflags |= LOCK_NO_DEREF; - } - refname = resolve_ref_unsafe(refname, resolve_flags, - lock->old_oid.hash, &type); - if (!refname && errno == EISDIR) { + files_ref_path(refs, &ref_file, refname); + resolved = !!refs_resolve_ref_unsafe(&refs->base, + refname, resolve_flags, + &lock->old_oid, type); + if (!resolved && errno == EISDIR) { /* * we are trying to lock foo but we used to * have foo/bar which now does not exist; * it is normal for the empty directory 'foo' * to remain. */ - strbuf_git_path(&orig_ref_file, "%s", orig_refname); - if (remove_empty_directories(&orig_ref_file)) { + if (remove_empty_directories(&ref_file)) { last_errno = errno; - if (!verify_refname_available_dir(orig_refname, extras, skip, - get_loose_refs(&ref_cache), err)) + if (!refs_verify_refname_available( + &refs->base, + refname, extras, skip, err)) strbuf_addf(err, "there are still refs under '%s'", - orig_refname); + refname); goto error_return; } - refname = resolve_ref_unsafe(orig_refname, resolve_flags, - lock->old_oid.hash, &type); + resolved = !!refs_resolve_ref_unsafe(&refs->base, + refname, resolve_flags, + &lock->old_oid, type); } - if (type_p) - *type_p = type; - if (!refname) { + if (!resolved) { last_errno = errno; if (last_errno != ENOTDIR || - !verify_refname_available_dir(orig_refname, extras, skip, - get_loose_refs(&ref_cache), err)) - strbuf_addf(err, "unable to resolve reference %s: %s", - orig_refname, strerror(last_errno)); + !refs_verify_refname_available(&refs->base, refname, + extras, skip, err)) + strbuf_addf(err, "unable to resolve reference '%s': %s", + refname, strerror(last_errno)); goto error_return; } - if (flags & REF_NODEREF) - refname = orig_refname; - /* * If the ref did not exist and we are creating it, make sure * there is no existing packed ref whose name begins with our @@ -1960,48 +940,21 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, * our refname. */ if (is_null_oid(&lock->old_oid) && - verify_refname_available_dir(refname, extras, skip, - get_packed_refs(&ref_cache), err)) { + refs_verify_refname_available(refs->packed_ref_store, refname, + extras, skip, err)) { last_errno = ENOTDIR; goto error_return; } - lock->lk = xcalloc(1, sizeof(struct lock_file)); - lock->ref_name = xstrdup(refname); - lock->orig_ref_name = xstrdup(orig_refname); - strbuf_git_path(&ref_file, "%s", refname); - retry: - switch (safe_create_leading_directories_const(ref_file.buf)) { - case SCLD_OK: - break; /* success */ - case SCLD_VANISHED: - if (--attempts_remaining > 0) - goto retry; - /* fall through */ - default: + if (raceproof_create_file(ref_file.buf, create_reflock, &lock->lk)) { last_errno = errno; - strbuf_addf(err, "unable to create directory for %s", - ref_file.buf); + unable_to_lock_message(ref_file.buf, errno, err); goto error_return; } - if (hold_lock_file_for_update(lock->lk, ref_file.buf, lflags) < 0) { - last_errno = errno; - if (errno == ENOENT && --attempts_remaining > 0) - /* - * Maybe somebody just deleted one of the - * directories leading to ref_file. Try - * again: - */ - goto retry; - else { - unable_to_lock_message(ref_file.buf, errno, err); - goto error_return; - } - } - if (verify_lock(lock, old_sha1, mustexist, err)) { + if (verify_lock(&refs->base, lock, old_oid, mustexist, err)) { last_errno = errno; goto error_return; } @@ -2013,200 +966,38 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, out: strbuf_release(&ref_file); - strbuf_release(&orig_ref_file); errno = last_errno; return lock; } -/* - * 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) -{ - 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 - * errno appropriately and return a nonzero value. - */ -static int lock_packed_refs(int flags) -{ - static int timeout_configured = 0; - static int timeout_value = 1000; - - struct packed_ref_cache *packed_ref_cache; - - if (!timeout_configured) { - git_config_get_int("core.packedrefstimeout", &timeout_value); - timeout_configured = 1; - } - - if (hold_lock_file_for_update_timeout( - &packlock, git_path("packed-refs"), - flags, timeout_value) < 0) - return -1; - /* - * Get the current packed-refs while holding the lock. If the - * packed-refs file has been modified since we last read it, - * this will automatically invalidate the cache and re-read - * the packed-refs file. - */ - packed_ref_cache = get_packed_ref_cache(&ref_cache); - packed_ref_cache->lock = &packlock; - /* Increment the reference count to prevent it from being freed: */ - acquire_packed_ref_cache(packed_ref_cache); - return 0; -} - -/* - * Write the current version of the packed refs cache from memory to - * disk. The packed-refs file must already be locked for writing (see - * lock_packed_refs()). Return zero on success. On errors, set errno - * and return a nonzero value - */ -static int commit_packed_refs(void) -{ - struct packed_ref_cache *packed_ref_cache = - get_packed_ref_cache(&ref_cache); - int error = 0; - int save_errno = 0; - FILE *out; - - if (!packed_ref_cache->lock) - die("internal error: packed-refs not locked"); - - out = fdopen_lock_file(packed_ref_cache->lock, "w"); - if (!out) - 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); - - if (commit_lock_file(packed_ref_cache->lock)) { - save_errno = errno; - error = -1; - } - packed_ref_cache->lock = NULL; - release_packed_ref_cache(packed_ref_cache); - errno = save_errno; - return error; -} - -/* - * Rollback the lockfile for the packed-refs file, and discard the - * in-memory packed reference cache. (The packed-refs file will be - * read anew if it is needed again after this function is called.) - */ -static void rollback_packed_refs(void) -{ - struct packed_ref_cache *packed_ref_cache = - get_packed_ref_cache(&ref_cache); - - if (!packed_ref_cache->lock) - die("internal error: packed-refs not locked"); - rollback_lock_file(packed_ref_cache->lock); - packed_ref_cache->lock = NULL; - release_packed_ref_cache(packed_ref_cache); - clear_packed_ref_cache(&ref_cache); -} - struct ref_to_prune { struct ref_to_prune *next; - unsigned char sha1[20]; + struct object_id oid; char name[FLEX_ARRAY]; }; -struct pack_refs_cb_data { - unsigned int flags; - struct ref_dir *packed_refs; - struct ref_to_prune *ref_to_prune; +enum { + REMOVE_EMPTY_PARENTS_REF = 0x01, + REMOVE_EMPTY_PARENTS_REFLOG = 0x02 }; /* - * 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) || !ref_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(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(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; -} - -/* - * Remove empty parents, but spare refs/ and immediate subdirs. - * Note: munges *name. + * Remove empty parent directories associated with the specified + * reference and/or its reflog, but spare [logs/]refs/ and immediate + * subdirs. flags is a combination of REMOVE_EMPTY_PARENTS_REF and/or + * REMOVE_EMPTY_PARENTS_REFLOG. */ -static void try_remove_empty_parents(char *name) +static void try_remove_empty_parents(struct files_ref_store *refs, + const char *refname, + unsigned int flags) { + struct strbuf buf = STRBUF_INIT; + struct strbuf sb = STRBUF_INIT; char *p, *q; int i; - p = name; + + strbuf_addstr(&buf, refname); + p = buf.buf; for (i = 0; i < 2; i++) { /* refs/{heads,tags,...}/ */ while (*p && *p != '/') p++; @@ -2214,181 +1005,208 @@ static void try_remove_empty_parents(char *name) while (*p == '/') p++; } - for (q = p; *q; q++) - ; - while (1) { + q = buf.buf + buf.len; + while (flags & (REMOVE_EMPTY_PARENTS_REF | REMOVE_EMPTY_PARENTS_REFLOG)) { while (q > p && *q != '/') q--; while (q > p && *(q-1) == '/') q--; if (q == p) break; - *q = '\0'; - if (rmdir(git_path("%s", name))) - break; + strbuf_setlen(&buf, q - buf.buf); + + strbuf_reset(&sb); + files_ref_path(refs, &sb, buf.buf); + if ((flags & REMOVE_EMPTY_PARENTS_REF) && rmdir(sb.buf)) + flags &= ~REMOVE_EMPTY_PARENTS_REF; + + strbuf_reset(&sb); + files_reflog_path(refs, &sb, buf.buf); + if ((flags & REMOVE_EMPTY_PARENTS_REFLOG) && rmdir(sb.buf)) + flags &= ~REMOVE_EMPTY_PARENTS_REFLOG; } + strbuf_release(&buf); + strbuf_release(&sb); } /* make sure nobody touched the ref, and unlink */ -static void prune_ref(struct ref_to_prune *r) +static void prune_ref(struct files_ref_store *refs, struct ref_to_prune *r) { struct ref_transaction *transaction; struct strbuf err = STRBUF_INIT; + int ret = -1; if (check_refname_format(r->name, 0)) return; - transaction = ref_transaction_begin(&err); - if (!transaction || - ref_transaction_delete(transaction, r->name, r->sha1, - REF_ISPRUNING, NULL, &err) || - ref_transaction_commit(transaction, &err)) { - ref_transaction_free(transaction); + transaction = ref_store_transaction_begin(&refs->base, &err); + if (!transaction) + goto cleanup; + ref_transaction_add_update( + transaction, r->name, + REF_NO_DEREF | REF_HAVE_NEW | REF_HAVE_OLD | REF_IS_PRUNING, + &null_oid, &r->oid, NULL); + if (ref_transaction_commit(transaction, &err)) + goto cleanup; + + ret = 0; + +cleanup: + if (ret) error("%s", err.buf); - strbuf_release(&err); - return; - } - ref_transaction_free(transaction); strbuf_release(&err); - try_remove_empty_parents(r->name); + ref_transaction_free(transaction); + return; } -static void prune_refs(struct ref_to_prune *r) +/* + * Prune the loose versions of the references in the linked list + * `*refs_to_prune`, freeing the entries in the list as we go. + */ +static void prune_refs(struct files_ref_store *refs, struct ref_to_prune **refs_to_prune) { - while (r) { - prune_ref(r); - r = r->next; + while (*refs_to_prune) { + struct ref_to_prune *r = *refs_to_prune; + *refs_to_prune = r->next; + prune_ref(refs, r); + free(r); } } -int pack_refs(unsigned int flags) +/* + * Return true if the specified reference should be packed. + */ +static int should_pack_ref(const char *refname, + const struct object_id *oid, unsigned int ref_flags, + unsigned int pack_flags) { - struct pack_refs_cb_data cbdata; - - memset(&cbdata, 0, sizeof(cbdata)); - cbdata.flags = flags; + /* Do not pack per-worktree refs: */ + if (ref_type(refname) != REF_TYPE_NORMAL) + return 0; - lock_packed_refs(LOCK_DIE_ON_ERROR); - cbdata.packed_refs = get_packed_refs(&ref_cache); + /* Do not pack non-tags unless PACK_REFS_ALL is set: */ + if (!(pack_flags & PACK_REFS_ALL) && !starts_with(refname, "refs/tags/")) + return 0; - do_for_each_entry_in_dir(get_loose_refs(&ref_cache), 0, - pack_if_possible_fn, &cbdata); + /* Do not pack symbolic refs: */ + if (ref_flags & REF_ISSYMREF) + return 0; - if (commit_packed_refs()) - die_errno("unable to overwrite old ref-pack file"); + /* Do not pack broken refs: */ + if (!ref_resolves_to_object(refname, oid, ref_flags)) + return 0; - prune_refs(cbdata.ref_to_prune); - return 0; + return 1; } -/* - * Rewrite the packed-refs file, omitting any refs listed in - * 'refnames'. On error, leave packed-refs unchanged, write an error - * message to 'err', and return a nonzero value. - * - * The refs in 'refnames' needn't be sorted. `err` must not be NULL. - */ -static int repack_without_refs(struct string_list *refnames, struct strbuf *err) +static int files_pack_refs(struct ref_store *ref_store, unsigned int flags) { - struct ref_dir *packed; - struct string_list_item *refname; - int ret, needs_repacking = 0, removed = 0; - - assert(err); + struct files_ref_store *refs = + files_downcast(ref_store, REF_STORE_WRITE | REF_STORE_ODB, + "pack_refs"); + struct ref_iterator *iter; + int ok; + struct ref_to_prune *refs_to_prune = NULL; + struct strbuf err = STRBUF_INIT; + struct ref_transaction *transaction; - /* Look for a packed ref */ - for_each_string_list_item(refname, refnames) { - if (get_packed_ref(refname->string)) { - needs_repacking = 1; - break; - } - } + transaction = ref_store_transaction_begin(refs->packed_ref_store, &err); + if (!transaction) + return -1; - /* Avoid locking if we have nothing to do */ - if (!needs_repacking) - return 0; /* no refname exists in packed refs */ + packed_refs_lock(refs->packed_ref_store, LOCK_DIE_ON_ERROR, &err); - if (lock_packed_refs(0)) { - unable_to_lock_message(git_path("packed-refs"), errno, err); - return -1; - } - packed = get_packed_refs(&ref_cache); + 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. + */ + if (!should_pack_ref(iter->refname, iter->oid, iter->flags, + flags)) + continue; - /* Remove refnames from the cache */ - for_each_string_list_item(refname, refnames) - if (remove_entry(packed, refname->string) != -1) - removed = 1; - if (!removed) { /* - * All packed entries disappeared while we were - * acquiring the lock. + * Add a reference creation for this reference to the + * packed-refs transaction: */ - rollback_packed_refs(); - return 0; + if (ref_transaction_update(transaction, iter->refname, + iter->oid, NULL, + REF_NO_DEREF, NULL, &err)) + die("failure preparing to create packed reference %s: %s", + iter->refname, err.buf); + + /* 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); + oidcpy(&n->oid, iter->oid); + n->next = refs_to_prune; + refs_to_prune = n; + } } + if (ok != ITER_DONE) + die("error while iterating over references"); - /* Write what remains */ - ret = commit_packed_refs(); - if (ret) - strbuf_addf(err, "unable to overwrite old ref-pack file: %s", - strerror(errno)); - return ret; -} + if (ref_transaction_commit(transaction, &err)) + die("unable to write new packed-refs: %s", err.buf); -static int delete_ref_loose(struct ref_lock *lock, int flag, struct strbuf *err) -{ - assert(err); + ref_transaction_free(transaction); - if (!(flag & REF_ISPACKED) || flag & REF_ISSYMREF) { - /* - * loose. The loose file name is the same as the - * lockfile name, minus ".lock": - */ - char *loose_filename = get_locked_file_path(lock->lk); - int res = unlink_or_msg(loose_filename, err); - free(loose_filename); - if (res) - return 1; - } + packed_refs_unlock(refs->packed_ref_store); + + prune_refs(refs, &refs_to_prune); + strbuf_release(&err); return 0; } -int delete_refs(struct string_list *refnames) +static int files_delete_refs(struct ref_store *ref_store, const char *msg, + struct string_list *refnames, unsigned int flags) { + struct files_ref_store *refs = + files_downcast(ref_store, REF_STORE_WRITE, "delete_refs"); struct strbuf err = STRBUF_INIT; int i, result = 0; if (!refnames->nr) return 0; - result = repack_without_refs(refnames, &err); - if (result) { - /* - * If we failed to rewrite the packed-refs file, then - * it is unsafe to try to remove loose refs, because - * doing so might expose an obsolete packed value for - * a reference that might even point at an object that - * has been garbage collected. - */ - if (refnames->nr == 1) - error(_("could not delete reference %s: %s"), - refnames->items[0].string, err.buf); - else - error(_("could not delete references: %s"), err.buf); + if (packed_refs_lock(refs->packed_ref_store, 0, &err)) + goto error; - goto out; + if (refs_delete_refs(refs->packed_ref_store, msg, refnames, flags)) { + packed_refs_unlock(refs->packed_ref_store); + goto error; } + packed_refs_unlock(refs->packed_ref_store); + for (i = 0; i < refnames->nr; i++) { const char *refname = refnames->items[i].string; - if (delete_ref(refname, NULL, 0)) + if (refs_delete_ref(&refs->base, msg, refname, NULL, flags)) result |= error(_("could not remove reference %s"), refname); } -out: strbuf_release(&err); return result; + +error: + /* + * If we failed to rewrite the packed-refs file, then it is + * unsafe to try to remove loose refs, because doing so might + * expose an obsolete packed value for a reference that might + * even point at an object that has been garbage collected. + */ + if (refnames->nr == 1) + error(_("could not delete reference %s: %s"), + refnames->items[0].string, err.buf); + else + error(_("could not delete references: %s"), err.buf); + + strbuf_release(&err); + return -1; } /* @@ -2398,123 +1216,148 @@ out: * IOW, to avoid cross device rename errors, the temporary renamed log must * live into logs/refs. */ -#define TMP_RENAMED_LOG "logs/refs/.tmp-renamed-log" +#define TMP_RENAMED_LOG "refs/.tmp-renamed-log" -static int rename_tmp_log(const char *newrefname) -{ - int attempts_remaining = 4; - struct strbuf path = STRBUF_INIT; - int ret = -1; +struct rename_cb { + const char *tmp_renamed_log; + int true_errno; +}; - retry: - strbuf_reset(&path); - strbuf_git_path(&path, "logs/%s", newrefname); - switch (safe_create_leading_directories_const(path.buf)) { - case SCLD_OK: - break; /* success */ - case SCLD_VANISHED: - if (--attempts_remaining > 0) - goto retry; - /* fall through */ - default: - error("unable to create directory for %s", newrefname); - goto out; - } +static int rename_tmp_log_callback(const char *path, void *cb_data) +{ + struct rename_cb *cb = cb_data; - if (rename(git_path(TMP_RENAMED_LOG), path.buf)) { - if ((errno==EISDIR || errno==ENOTDIR) && --attempts_remaining > 0) { - /* - * rename(a, b) when b is an existing - * directory ought to result in ISDIR, but - * Solaris 5.8 gives ENOTDIR. Sheesh. - */ - if (remove_empty_directories(&path)) { - error("Directory not empty: logs/%s", newrefname); - goto out; - } - goto retry; - } else if (errno == ENOENT && --attempts_remaining > 0) { - /* - * Maybe another process just deleted one of - * the directories in the path to newrefname. - * Try again from the beginning. - */ - goto retry; - } else { - error("unable to move logfile "TMP_RENAMED_LOG" to logs/%s: %s", - newrefname, strerror(errno)); - goto out; - } + if (rename(cb->tmp_renamed_log, path)) { + /* + * rename(a, b) when b is an existing directory ought + * to result in ISDIR, but Solaris 5.8 gives ENOTDIR. + * Sheesh. Record the true errno for error reporting, + * but report EISDIR to raceproof_create_file() so + * that it knows to retry. + */ + cb->true_errno = errno; + if (errno == ENOTDIR) + errno = EISDIR; + return -1; + } else { + return 0; } - ret = 0; -out: - strbuf_release(&path); - return ret; } -int verify_refname_available(const char *newname, - struct string_list *extras, - struct string_list *skip, - struct strbuf *err) +static int rename_tmp_log(struct files_ref_store *refs, const char *newrefname) { - struct ref_dir *packed_refs = get_packed_refs(&ref_cache); - struct ref_dir *loose_refs = get_loose_refs(&ref_cache); + struct strbuf path = STRBUF_INIT; + struct strbuf tmp = STRBUF_INIT; + struct rename_cb cb; + int ret; - if (verify_refname_available_dir(newname, extras, skip, - packed_refs, err) || - verify_refname_available_dir(newname, extras, skip, - loose_refs, err)) - return -1; + files_reflog_path(refs, &path, newrefname); + files_reflog_path(refs, &tmp, TMP_RENAMED_LOG); + cb.tmp_renamed_log = tmp.buf; + ret = raceproof_create_file(path.buf, rename_tmp_log_callback, &cb); + if (ret) { + if (errno == EISDIR) + error("directory not empty: %s", path.buf); + else + error("unable to move logfile %s to %s: %s", + tmp.buf, path.buf, + strerror(cb.true_errno)); + } - return 0; + strbuf_release(&path); + strbuf_release(&tmp); + return ret; } static int write_ref_to_lockfile(struct ref_lock *lock, - const unsigned char *sha1, struct strbuf *err); -static int commit_ref_update(struct ref_lock *lock, - const unsigned char *sha1, const char *logmsg, - int flags, struct strbuf *err); - -int rename_ref(const char *oldrefname, const char *newrefname, const char *logmsg) -{ - unsigned char sha1[20], orig_sha1[20]; + const struct object_id *oid, struct strbuf *err); +static int commit_ref_update(struct files_ref_store *refs, + struct ref_lock *lock, + const struct object_id *oid, const char *logmsg, + struct strbuf *err); + +static int files_copy_or_rename_ref(struct ref_store *ref_store, + const char *oldrefname, const char *newrefname, + const char *logmsg, int copy) +{ + struct files_ref_store *refs = + files_downcast(ref_store, REF_STORE_WRITE, "rename_ref"); + struct object_id oid, orig_oid; int flag = 0, logmoved = 0; struct ref_lock *lock; struct stat loginfo; - int log = !lstat(git_path("logs/%s", oldrefname), &loginfo); - const char *symref = NULL; + struct strbuf sb_oldref = STRBUF_INIT; + struct strbuf sb_newref = STRBUF_INIT; + struct strbuf tmp_renamed_log = STRBUF_INIT; + int log, ret; struct strbuf err = STRBUF_INIT; - if (log && S_ISLNK(loginfo.st_mode)) - return error("reflog for %s is a symlink", oldrefname); + files_reflog_path(refs, &sb_oldref, oldrefname); + files_reflog_path(refs, &sb_newref, newrefname); + files_reflog_path(refs, &tmp_renamed_log, TMP_RENAMED_LOG); - symref = resolve_ref_unsafe(oldrefname, RESOLVE_REF_READING, - orig_sha1, &flag); - if (flag & REF_ISSYMREF) - return error("refname %s is a symbolic ref, renaming it is not supported", - oldrefname); - if (!symref) - return error("refname %s not found", oldrefname); + log = !lstat(sb_oldref.buf, &loginfo); + if (log && S_ISLNK(loginfo.st_mode)) { + ret = error("reflog for %s is a symlink", oldrefname); + goto out; + } - if (!rename_ref_available(oldrefname, newrefname)) - return 1; + if (!refs_resolve_ref_unsafe(&refs->base, oldrefname, + RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, + &orig_oid, &flag)) { + ret = error("refname %s not found", oldrefname); + goto out; + } - if (log && rename(git_path("logs/%s", oldrefname), git_path(TMP_RENAMED_LOG))) - return error("unable to move logfile logs/%s to "TMP_RENAMED_LOG": %s", - oldrefname, strerror(errno)); + if (flag & REF_ISSYMREF) { + if (copy) + ret = error("refname %s is a symbolic ref, copying it is not supported", + oldrefname); + else + ret = error("refname %s is a symbolic ref, renaming it is not supported", + oldrefname); + goto out; + } + if (!refs_rename_ref_available(&refs->base, oldrefname, newrefname)) { + ret = 1; + goto out; + } + + if (!copy && log && rename(sb_oldref.buf, tmp_renamed_log.buf)) { + ret = error("unable to move logfile logs/%s to logs/"TMP_RENAMED_LOG": %s", + oldrefname, strerror(errno)); + goto out; + } - if (delete_ref(oldrefname, orig_sha1, REF_NODEREF)) { + if (copy && log && copy_file(tmp_renamed_log.buf, sb_oldref.buf, 0644)) { + ret = error("unable to copy logfile logs/%s to logs/"TMP_RENAMED_LOG": %s", + oldrefname, strerror(errno)); + goto out; + } + + if (!copy && refs_delete_ref(&refs->base, logmsg, oldrefname, + &orig_oid, REF_NO_DEREF)) { error("unable to delete old %s", oldrefname); goto rollback; } - if (!read_ref_full(newrefname, RESOLVE_REF_READING, sha1, NULL) && - delete_ref(newrefname, sha1, REF_NODEREF)) { - if (errno==EISDIR) { + /* + * Since we are doing a shallow lookup, oid is not the + * correct value to pass to delete_ref as old_oid. But that + * doesn't matter, because an old_oid check wouldn't add to + * the safety anyway; we want to delete the reference whatever + * its current value. + */ + if (!copy && !refs_read_ref_full(&refs->base, newrefname, + RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, + &oid, NULL) && + refs_delete_ref(&refs->base, NULL, newrefname, + NULL, REF_NO_DEREF)) { + if (errno == EISDIR) { struct strbuf path = STRBUF_INIT; int result; - strbuf_git_path(&path, "%s", newrefname); + files_ref_path(refs, &path, newrefname); result = remove_empty_directories(&path); strbuf_release(&path); @@ -2528,30 +1371,36 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms } } - if (log && rename_tmp_log(newrefname)) + if (log && rename_tmp_log(refs, newrefname)) goto rollback; logmoved = log; - lock = lock_ref_sha1_basic(newrefname, NULL, NULL, NULL, 0, NULL, &err); + lock = lock_ref_oid_basic(refs, newrefname, NULL, NULL, NULL, + REF_NO_DEREF, NULL, &err); if (!lock) { - error("unable to rename '%s' to '%s': %s", oldrefname, newrefname, err.buf); + if (copy) + error("unable to copy '%s' to '%s': %s", oldrefname, newrefname, err.buf); + else + error("unable to rename '%s' to '%s': %s", oldrefname, newrefname, err.buf); strbuf_release(&err); goto rollback; } - hashcpy(lock->old_oid.hash, orig_sha1); + oidcpy(&lock->old_oid, &orig_oid); - if (write_ref_to_lockfile(lock, orig_sha1, &err) || - commit_ref_update(lock, orig_sha1, logmsg, 0, &err)) { + if (write_ref_to_lockfile(lock, &orig_oid, &err) || + commit_ref_update(refs, lock, &orig_oid, logmsg, &err)) { error("unable to write current sha1 into %s: %s", newrefname, err.buf); strbuf_release(&err); goto rollback; } - return 0; + ret = 0; + goto out; rollback: - lock = lock_ref_sha1_basic(oldrefname, NULL, NULL, NULL, 0, NULL, &err); + lock = lock_ref_oid_basic(refs, oldrefname, NULL, NULL, NULL, + REF_NO_DEREF, NULL, &err); if (!lock) { error("unable to lock %s for rollback: %s", oldrefname, err.buf); strbuf_release(&err); @@ -2559,99 +1408,176 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms } flag = log_all_ref_updates; - log_all_ref_updates = 0; - if (write_ref_to_lockfile(lock, orig_sha1, &err) || - commit_ref_update(lock, orig_sha1, NULL, 0, &err)) { + log_all_ref_updates = LOG_REFS_NONE; + if (write_ref_to_lockfile(lock, &orig_oid, &err) || + commit_ref_update(refs, lock, &orig_oid, NULL, &err)) { error("unable to write current sha1 into %s: %s", oldrefname, err.buf); strbuf_release(&err); } log_all_ref_updates = flag; rollbacklog: - if (logmoved && rename(git_path("logs/%s", newrefname), git_path("logs/%s", oldrefname))) + if (logmoved && rename(sb_newref.buf, sb_oldref.buf)) error("unable to restore logfile %s from %s: %s", oldrefname, newrefname, strerror(errno)); if (!logmoved && log && - rename(git_path(TMP_RENAMED_LOG), git_path("logs/%s", oldrefname))) - error("unable to restore logfile %s from "TMP_RENAMED_LOG": %s", + rename(tmp_renamed_log.buf, sb_oldref.buf)) + error("unable to restore logfile %s from logs/"TMP_RENAMED_LOG": %s", oldrefname, strerror(errno)); + ret = 1; + out: + strbuf_release(&sb_newref); + strbuf_release(&sb_oldref); + strbuf_release(&tmp_renamed_log); - return 1; + return ret; } -static int close_ref(struct ref_lock *lock) +static int files_rename_ref(struct ref_store *ref_store, + const char *oldrefname, const char *newrefname, + const char *logmsg) { - if (close_lock_file(lock->lk)) + return files_copy_or_rename_ref(ref_store, oldrefname, + newrefname, logmsg, 0); +} + +static int files_copy_ref(struct ref_store *ref_store, + const char *oldrefname, const char *newrefname, + const char *logmsg) +{ + return files_copy_or_rename_ref(ref_store, oldrefname, + newrefname, logmsg, 1); +} + +static int close_ref_gently(struct ref_lock *lock) +{ + if (close_lock_file_gently(&lock->lk)) return -1; return 0; } static int commit_ref(struct ref_lock *lock) { - if (commit_lock_file(lock->lk)) + char *path = get_locked_file_path(&lock->lk); + struct stat st; + + if (!lstat(path, &st) && S_ISDIR(st.st_mode)) { + /* + * There is a directory at the path we want to rename + * the lockfile to. Hopefully it is empty; try to + * delete it. + */ + size_t len = strlen(path); + struct strbuf sb_path = STRBUF_INIT; + + strbuf_attach(&sb_path, path, len, len); + + /* + * If this fails, commit_lock_file() will also fail + * and will report the problem. + */ + remove_empty_directories(&sb_path); + strbuf_release(&sb_path); + } else { + free(path); + } + + if (commit_lock_file(&lock->lk)) return -1; return 0; } +static int open_or_create_logfile(const char *path, void *cb) +{ + int *fd = cb; + + *fd = open(path, O_APPEND | O_WRONLY | O_CREAT, 0666); + return (*fd < 0) ? -1 : 0; +} + /* - * Create a reflog for a ref. If force_create = 0, the reflog will - * only be created for certain refs (those for which - * should_autocreate_reflog returns non-zero. Otherwise, create it - * regardless of the ref name. Fill in *err and return -1 on failure. + * Create a reflog for a ref. If force_create = 0, only create the + * reflog for certain refs (those for which should_autocreate_reflog + * returns non-zero). Otherwise, create it regardless of the reference + * name. If the logfile already existed or was created, return 0 and + * set *logfd to the file descriptor opened for appending to the file. + * If no logfile exists and we decided not to create one, return 0 and + * set *logfd to -1. On failure, fill in *err, set *logfd to -1, and + * return -1. */ -static int log_ref_setup(const char *refname, struct strbuf *logfile, struct strbuf *err, int force_create) +static int log_ref_setup(struct files_ref_store *refs, + const char *refname, int force_create, + int *logfd, struct strbuf *err) { - int logfd, oflags = O_APPEND | O_WRONLY; + struct strbuf logfile_sb = STRBUF_INIT; + char *logfile; - strbuf_git_path(logfile, "logs/%s", refname); - if (force_create || should_autocreate_reflog(refname)) { - if (safe_create_leading_directories(logfile->buf) < 0) { - strbuf_addf(err, "unable to create directory for %s: " - "%s", logfile->buf, strerror(errno)); - return -1; - } - oflags |= O_CREAT; - } + files_reflog_path(refs, &logfile_sb, refname); + logfile = strbuf_detach(&logfile_sb, NULL); - logfd = open(logfile->buf, oflags, 0666); - if (logfd < 0) { - if (!(oflags & O_CREAT) && (errno == ENOENT || errno == EISDIR)) - return 0; + if (force_create || should_autocreate_reflog(refname)) { + if (raceproof_create_file(logfile, open_or_create_logfile, logfd)) { + if (errno == ENOENT) + strbuf_addf(err, "unable to create directory for '%s': " + "%s", logfile, strerror(errno)); + else if (errno == EISDIR) + strbuf_addf(err, "there are still logs under '%s'", + logfile); + else + strbuf_addf(err, "unable to append to '%s': %s", + logfile, strerror(errno)); - if (errno == EISDIR) { - if (remove_empty_directories(logfile)) { - strbuf_addf(err, "There are still logs under " - "'%s'", logfile->buf); - return -1; - } - logfd = open(logfile->buf, oflags, 0666); + goto error; } - - if (logfd < 0) { - strbuf_addf(err, "unable to append to %s: %s", - logfile->buf, strerror(errno)); - return -1; + } else { + *logfd = open(logfile, O_APPEND | O_WRONLY, 0666); + if (*logfd < 0) { + if (errno == ENOENT || errno == EISDIR) { + /* + * The logfile doesn't already exist, + * but that is not an error; it only + * means that we won't write log + * entries to it. + */ + ; + } else { + strbuf_addf(err, "unable to append to '%s': %s", + logfile, strerror(errno)); + goto error; + } } } - adjust_shared_perm(logfile->buf); - close(logfd); + if (*logfd >= 0) + adjust_shared_perm(logfile); + + free(logfile); return 0; -} +error: + free(logfile); + return -1; +} -int safe_create_reflog(const char *refname, int force_create, struct strbuf *err) +static int files_create_reflog(struct ref_store *ref_store, + const char *refname, int force_create, + struct strbuf *err) { - int ret; - struct strbuf sb = STRBUF_INIT; + struct files_ref_store *refs = + files_downcast(ref_store, REF_STORE_WRITE, "create_reflog"); + int fd; - ret = log_ref_setup(refname, &sb, err, force_create); - strbuf_release(&sb); - return ret; + if (log_ref_setup(refs, refname, force_create, &fd, err)) + return -1; + + if (fd >= 0) + close(fd); + + return 0; } -static int log_ref_write_fd(int fd, const unsigned char *old_sha1, - const unsigned char *new_sha1, +static int log_ref_write_fd(int fd, const struct object_id *old_oid, + const struct object_id *new_oid, const char *committer, const char *msg) { int msglen, written; @@ -2662,106 +1588,97 @@ static int log_ref_write_fd(int fd, const unsigned char *old_sha1, maxlen = strlen(committer) + msglen + 100; logrec = xmalloc(maxlen); len = xsnprintf(logrec, maxlen, "%s %s %s\n", - sha1_to_hex(old_sha1), - sha1_to_hex(new_sha1), + oid_to_hex(old_oid), + oid_to_hex(new_oid), committer); if (msglen) len += copy_reflog_msg(logrec + len - 1, msg) - 1; written = len <= maxlen ? write_in_full(fd, logrec, len) : -1; free(logrec); - if (written != len) + if (written < 0) return -1; return 0; } -static int log_ref_write_1(const char *refname, const unsigned char *old_sha1, - const unsigned char *new_sha1, const char *msg, - struct strbuf *logfile, int flags, - struct strbuf *err) +static int files_log_ref_write(struct files_ref_store *refs, + const char *refname, const struct object_id *old_oid, + const struct object_id *new_oid, const char *msg, + int flags, struct strbuf *err) { - int logfd, result, oflags = O_APPEND | O_WRONLY; + int logfd, result; - if (log_all_ref_updates < 0) - log_all_ref_updates = !is_bare_repository(); + if (log_all_ref_updates == LOG_REFS_UNSET) + log_all_ref_updates = is_bare_repository() ? LOG_REFS_NONE : LOG_REFS_NORMAL; - result = log_ref_setup(refname, logfile, err, flags & REF_FORCE_CREATE_REFLOG); + result = log_ref_setup(refs, refname, + flags & REF_FORCE_CREATE_REFLOG, + &logfd, err); if (result) return result; - logfd = open(logfile->buf, oflags); if (logfd < 0) return 0; - result = log_ref_write_fd(logfd, old_sha1, new_sha1, + result = log_ref_write_fd(logfd, old_oid, new_oid, git_committer_info(0), msg); if (result) { - strbuf_addf(err, "unable to append to %s: %s", logfile->buf, - strerror(errno)); + struct strbuf sb = STRBUF_INIT; + int save_errno = errno; + + files_reflog_path(refs, &sb, refname); + strbuf_addf(err, "unable to append to '%s': %s", + sb.buf, strerror(save_errno)); + strbuf_release(&sb); close(logfd); return -1; } if (close(logfd)) { - strbuf_addf(err, "unable to append to %s: %s", logfile->buf, - strerror(errno)); + struct strbuf sb = STRBUF_INIT; + int save_errno = errno; + + files_reflog_path(refs, &sb, refname); + strbuf_addf(err, "unable to append to '%s': %s", + sb.buf, strerror(save_errno)); + strbuf_release(&sb); return -1; } return 0; } -static int log_ref_write(const char *refname, const unsigned char *old_sha1, - const unsigned char *new_sha1, const char *msg, - int flags, struct strbuf *err) -{ - return files_log_ref_write(refname, old_sha1, new_sha1, msg, flags, - err); -} - -int files_log_ref_write(const char *refname, const unsigned char *old_sha1, - const unsigned char *new_sha1, const char *msg, - int flags, struct strbuf *err) -{ - struct strbuf sb = STRBUF_INIT; - int ret = log_ref_write_1(refname, old_sha1, new_sha1, msg, &sb, flags, - err); - strbuf_release(&sb); - return ret; -} - /* - * Write sha1 into the open lockfile, then close the lockfile. On - * errors, rollback the lockfile, fill in *err and - * return -1. + * Write oid into the open lockfile, then close the lockfile. On + * errors, rollback the lockfile, fill in *err and return -1. */ static int write_ref_to_lockfile(struct ref_lock *lock, - const unsigned char *sha1, struct strbuf *err) + const struct object_id *oid, struct strbuf *err) { static char term = '\n'; struct object *o; int fd; - o = parse_object(sha1); + o = parse_object(oid); if (!o) { strbuf_addf(err, - "Trying to write ref %s with nonexistent object %s", - lock->ref_name, sha1_to_hex(sha1)); + "trying to write ref '%s' with nonexistent object %s", + lock->ref_name, oid_to_hex(oid)); unlock_ref(lock); return -1; } if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) { strbuf_addf(err, - "Trying to write non-commit object %s to branch %s", - sha1_to_hex(sha1), lock->ref_name); + "trying to write non-commit object %s to branch '%s'", + oid_to_hex(oid), lock->ref_name); unlock_ref(lock); return -1; } - fd = get_lock_file_fd(lock->lk); - if (write_in_full(fd, sha1_to_hex(sha1), 40) != 40 || - write_in_full(fd, &term, 1) != 1 || - close_ref(lock) < 0) { + fd = get_lock_file_fd(&lock->lk); + if (write_in_full(fd, oid_to_hex(oid), GIT_SHA1_HEXSZ) < 0 || + write_in_full(fd, &term, 1) < 0 || + close_ref_gently(lock) < 0) { strbuf_addf(err, - "Couldn't write %s", get_lock_file_path(lock->lk)); + "couldn't write '%s'", get_lock_file_path(&lock->lk)); unlock_ref(lock); return -1; } @@ -2773,22 +1690,26 @@ static int write_ref_to_lockfile(struct ref_lock *lock, * to the loose reference lockfile. Also update the reflogs if * necessary, using the specified lockmsg (which can be NULL). */ -static int commit_ref_update(struct ref_lock *lock, - const unsigned char *sha1, const char *logmsg, - int flags, struct strbuf *err) -{ - clear_loose_ref_cache(&ref_cache); - if (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg, flags, err) < 0 || - (strcmp(lock->ref_name, lock->orig_ref_name) && - log_ref_write(lock->orig_ref_name, lock->old_oid.hash, sha1, logmsg, flags, err) < 0)) { +static int commit_ref_update(struct files_ref_store *refs, + struct ref_lock *lock, + const struct object_id *oid, const char *logmsg, + struct strbuf *err) +{ + files_assert_main_repository(refs, "commit_ref_update"); + + clear_loose_ref_cache(refs); + if (files_log_ref_write(refs, lock->ref_name, + &lock->old_oid, oid, + logmsg, 0, err)) { char *old_msg = strbuf_detach(err, NULL); - strbuf_addf(err, "Cannot update the ref '%s': %s", + strbuf_addf(err, "cannot update the ref '%s': %s", lock->ref_name, old_msg); free(old_msg); unlock_ref(lock); return -1; } - if (strcmp(lock->orig_ref_name, "HEAD") != 0) { + + if (strcmp(lock->ref_name, "HEAD") != 0) { /* * Special hack: If a branch is updated directly and HEAD * points to it (may happen on the remote side of a push @@ -2801,23 +1722,26 @@ static int commit_ref_update(struct ref_lock *lock, * check with HEAD only which should cover 99% of all usage * scenarios (even 100% of the default ones). */ - unsigned char head_sha1[20]; int head_flag; const char *head_ref; - head_ref = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, - head_sha1, &head_flag); + + head_ref = refs_resolve_ref_unsafe(&refs->base, "HEAD", + RESOLVE_REF_READING, + NULL, &head_flag); if (head_ref && (head_flag & REF_ISSYMREF) && !strcmp(head_ref, lock->ref_name)) { struct strbuf log_err = STRBUF_INIT; - if (log_ref_write("HEAD", lock->old_oid.hash, sha1, - logmsg, 0, &log_err)) { + if (files_log_ref_write(refs, "HEAD", + &lock->old_oid, oid, + logmsg, 0, &log_err)) { error("%s", log_err.buf); strbuf_release(&log_err); } } } + if (commit_ref(lock)) { - error("Couldn't set %s", lock->ref_name); + strbuf_addf(err, "couldn't set '%s'", lock->ref_name); unlock_ref(lock); return -1; } @@ -2830,7 +1754,7 @@ static int create_ref_symlink(struct ref_lock *lock, const char *target) { int ret = -1; #ifndef NO_SYMLINK_HEAD - char *ref_path = get_locked_file_path(lock->lk); + char *ref_path = get_locked_file_path(&lock->lk); unlink(ref_path); ret = symlink(target, ref_path); free(ref_path); @@ -2841,86 +1765,113 @@ static int create_ref_symlink(struct ref_lock *lock, const char *target) return ret; } -static void update_symref_reflog(struct ref_lock *lock, const char *refname, +static void update_symref_reflog(struct files_ref_store *refs, + struct ref_lock *lock, const char *refname, const char *target, const char *logmsg) { struct strbuf err = STRBUF_INIT; - unsigned char new_sha1[20]; - if (logmsg && !read_ref(target, new_sha1) && - log_ref_write(refname, lock->old_oid.hash, new_sha1, logmsg, 0, &err)) { + struct object_id new_oid; + if (logmsg && + !refs_read_ref_full(&refs->base, target, + RESOLVE_REF_READING, &new_oid, NULL) && + files_log_ref_write(refs, refname, &lock->old_oid, + &new_oid, logmsg, 0, &err)) { error("%s", err.buf); strbuf_release(&err); } } -static int create_symref_locked(struct ref_lock *lock, const char *refname, +static int create_symref_locked(struct files_ref_store *refs, + struct ref_lock *lock, const char *refname, const char *target, const char *logmsg) { if (prefer_symlink_refs && !create_ref_symlink(lock, target)) { - update_symref_reflog(lock, refname, target, logmsg); + update_symref_reflog(refs, lock, refname, target, logmsg); return 0; } - if (!fdopen_lock_file(lock->lk, "w")) + if (!fdopen_lock_file(&lock->lk, "w")) return error("unable to fdopen %s: %s", - lock->lk->tempfile.filename.buf, strerror(errno)); + lock->lk.tempfile->filename.buf, strerror(errno)); - update_symref_reflog(lock, refname, target, logmsg); + update_symref_reflog(refs, lock, refname, target, logmsg); /* no error check; commit_ref will check ferror */ - fprintf(lock->lk->tempfile.fp, "ref: %s\n", target); + fprintf(lock->lk.tempfile->fp, "ref: %s\n", target); if (commit_ref(lock) < 0) return error("unable to write symref for %s: %s", refname, strerror(errno)); return 0; } -int create_symref(const char *refname, const char *target, const char *logmsg) +static int files_create_symref(struct ref_store *ref_store, + const char *refname, const char *target, + const char *logmsg) { + struct files_ref_store *refs = + files_downcast(ref_store, REF_STORE_WRITE, "create_symref"); struct strbuf err = STRBUF_INIT; struct ref_lock *lock; int ret; - lock = lock_ref_sha1_basic(refname, NULL, NULL, NULL, REF_NODEREF, NULL, - &err); + lock = lock_ref_oid_basic(refs, refname, NULL, + NULL, NULL, REF_NO_DEREF, NULL, + &err); if (!lock) { error("%s", err.buf); strbuf_release(&err); return -1; } - ret = create_symref_locked(lock, refname, target, logmsg); + ret = create_symref_locked(refs, lock, refname, target, logmsg); unlock_ref(lock); return ret; } -int reflog_exists(const char *refname) +static int files_reflog_exists(struct ref_store *ref_store, + const char *refname) { + struct files_ref_store *refs = + files_downcast(ref_store, REF_STORE_READ, "reflog_exists"); + struct strbuf sb = STRBUF_INIT; struct stat st; + int ret; - return !lstat(git_path("logs/%s", refname), &st) && - S_ISREG(st.st_mode); + files_reflog_path(refs, &sb, refname); + ret = !lstat(sb.buf, &st) && S_ISREG(st.st_mode); + strbuf_release(&sb); + return ret; } -int delete_reflog(const char *refname) +static int files_delete_reflog(struct ref_store *ref_store, + const char *refname) { - return remove_path(git_path("logs/%s", refname)); + struct files_ref_store *refs = + files_downcast(ref_store, REF_STORE_WRITE, "delete_reflog"); + struct strbuf sb = STRBUF_INIT; + int ret; + + files_reflog_path(refs, &sb, refname); + ret = remove_path(sb.buf); + strbuf_release(&sb); + return ret; } static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *cb_data) { - unsigned char osha1[20], nsha1[20]; + struct object_id ooid, noid; char *email_end, *message; - unsigned long timestamp; + timestamp_t timestamp; int tz; + const char *p = sb->buf; /* old SP new SP name <email> SP time TAB msg LF */ - if (sb->len < 83 || sb->buf[sb->len - 1] != '\n' || - get_sha1_hex(sb->buf, osha1) || sb->buf[40] != ' ' || - get_sha1_hex(sb->buf + 41, nsha1) || sb->buf[81] != ' ' || - !(email_end = strchr(sb->buf + 82, '>')) || + if (!sb->len || sb->buf[sb->len - 1] != '\n' || + parse_oid_hex(p, &ooid, &p) || *p++ != ' ' || + parse_oid_hex(p, &noid, &p) || *p++ != ' ' || + !(email_end = strchr(p, '>')) || email_end[1] != ' ' || - !(timestamp = strtoul(email_end + 2, &message, 10)) || + !(timestamp = parse_timestamp(email_end + 2, &message, 10)) || !message || message[0] != ' ' || (message[1] != '+' && message[1] != '-') || !isdigit(message[2]) || !isdigit(message[3]) || @@ -2932,7 +1883,7 @@ static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *c message += 6; else message += 7; - return fn(osha1, nsha1, sb->buf + 82, timestamp, tz, message, cb_data); + return fn(&ooid, &noid, p, timestamp, tz, message, cb_data); } static char *find_beginning_of_line(char *bob, char *scan) @@ -2946,21 +1897,29 @@ static char *find_beginning_of_line(char *bob, char *scan) return scan; } -int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void *cb_data) +static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store, + const char *refname, + each_reflog_ent_fn fn, + void *cb_data) { + struct files_ref_store *refs = + files_downcast(ref_store, REF_STORE_READ, + "for_each_reflog_ent_reverse"); struct strbuf sb = STRBUF_INIT; FILE *logfp; long pos; int ret = 0, at_tail = 1; - logfp = fopen(git_path("logs/%s", refname), "r"); + files_reflog_path(refs, &sb, refname); + logfp = fopen(sb.buf, "r"); + strbuf_release(&sb); if (!logfp) return -1; /* 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; @@ -2970,13 +1929,17 @@ int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void /* 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; @@ -3048,13 +2011,20 @@ int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void return ret; } -int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_data) +static int files_for_each_reflog_ent(struct ref_store *ref_store, + const char *refname, + each_reflog_ent_fn fn, void *cb_data) { + struct files_ref_store *refs = + files_downcast(ref_store, REF_STORE_READ, + "for_each_reflog_ent"); FILE *logfp; struct strbuf sb = STRBUF_INIT; int ret = 0; - logfp = fopen(git_path("logs/%s", refname), "r"); + files_reflog_path(refs, &sb, refname); + logfp = fopen(sb.buf, "r"); + strbuf_release(&sb); if (!logfp) return -1; @@ -3064,102 +2034,553 @@ int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_dat strbuf_release(&sb); return ret; } -/* - * Call fn for each reflog in the namespace indicated by name. name - * must be empty or end with '/'. Name will be used as a scratch - * space, but its contents will be restored before return. - */ -static int do_for_each_reflog(struct strbuf *name, each_ref_fn fn, void *cb_data) -{ - DIR *d = opendir(git_path("logs/%s", name->buf)); - int retval = 0; - struct dirent *de; - int oldlen = name->len; - if (!d) - return name->len ? errno : 0; +struct files_reflog_iterator { + struct ref_iterator base; - while ((de = readdir(d)) != NULL) { - struct stat st; + struct ref_store *ref_store; + struct dir_iterator *dir_iterator; + struct object_id oid; +}; - if (de->d_name[0] == '.') +static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator) +{ + struct files_reflog_iterator *iter = + (struct files_reflog_iterator *)ref_iterator; + struct dir_iterator *diter = iter->dir_iterator; + int ok; + + while ((ok = dir_iterator_advance(diter)) == ITER_OK) { + int flags; + + if (!S_ISREG(diter->st.st_mode)) continue; - if (ends_with(de->d_name, ".lock")) + if (diter->basename[0] == '.') + continue; + if (ends_with(diter->basename, ".lock")) continue; - strbuf_addstr(name, de->d_name); - if (stat(git_path("logs/%s", name->buf), &st) < 0) { - ; /* silently ignore */ - } else { - if (S_ISDIR(st.st_mode)) { - strbuf_addch(name, '/'); - retval = do_for_each_reflog(name, fn, cb_data); - } else { - struct object_id oid; - if (read_ref_full(name->buf, 0, oid.hash, NULL)) - retval = error("bad ref for %s", name->buf); - else - retval = fn(name->buf, &oid, 0, cb_data); - } - if (retval) - break; + if (refs_read_ref_full(iter->ref_store, + diter->relative_path, 0, + &iter->oid, &flags)) { + error("bad ref for %s", diter->path.buf); + continue; } - strbuf_setlen(name, oldlen); + + iter->base.refname = diter->relative_path; + iter->base.oid = &iter->oid; + iter->base.flags = flags; + return ITER_OK; } - closedir(d); - return retval; + + iter->dir_iterator = NULL; + if (ref_iterator_abort(ref_iterator) == ITER_ERROR) + ok = ITER_ERROR; + return ok; } -int for_each_reflog(each_ref_fn fn, void *cb_data) +static int files_reflog_iterator_peel(struct ref_iterator *ref_iterator, + struct object_id *peeled) { - int retval; - struct strbuf name; - strbuf_init(&name, PATH_MAX); - retval = do_for_each_reflog(&name, fn, cb_data); - strbuf_release(&name); - return retval; + die("BUG: ref_iterator_peel() called for reflog_iterator"); } -static int ref_update_reject_duplicates(struct string_list *refnames, - struct strbuf *err) +static int files_reflog_iterator_abort(struct ref_iterator *ref_iterator) { - int i, n = refnames->nr; + struct files_reflog_iterator *iter = + (struct files_reflog_iterator *)ref_iterator; + int ok = ITER_DONE; - assert(err); + if (iter->dir_iterator) + ok = dir_iterator_abort(iter->dir_iterator); + + base_ref_iterator_free(ref_iterator); + return ok; +} + +static struct ref_iterator_vtable files_reflog_iterator_vtable = { + files_reflog_iterator_advance, + files_reflog_iterator_peel, + files_reflog_iterator_abort +}; + +static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store, + const char *gitdir) +{ + struct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter)); + struct ref_iterator *ref_iterator = &iter->base; + struct strbuf sb = STRBUF_INIT; + + base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable, 0); + strbuf_addf(&sb, "%s/logs", gitdir); + iter->dir_iterator = dir_iterator_begin(sb.buf); + iter->ref_store = ref_store; + strbuf_release(&sb); + + return ref_iterator; +} - for (i = 1; i < n; i++) - if (!strcmp(refnames->items[i - 1].string, refnames->items[i].string)) { +static enum iterator_selection reflog_iterator_select( + struct ref_iterator *iter_worktree, + struct ref_iterator *iter_common, + void *cb_data) +{ + if (iter_worktree) { + /* + * We're a bit loose here. We probably should ignore + * common refs if they are accidentally added as + * per-worktree refs. + */ + return ITER_SELECT_0; + } else if (iter_common) { + if (ref_type(iter_common->refname) == REF_TYPE_NORMAL) + return ITER_SELECT_1; + + /* + * The main ref store may contain main worktree's + * per-worktree refs, which should be ignored + */ + return ITER_SKIP_1; + } else + return ITER_DONE; +} + +static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store) +{ + struct files_ref_store *refs = + files_downcast(ref_store, REF_STORE_READ, + "reflog_iterator_begin"); + + if (!strcmp(refs->gitdir, refs->gitcommondir)) { + return reflog_iterator_begin(ref_store, refs->gitcommondir); + } else { + return merge_ref_iterator_begin( + 0, + reflog_iterator_begin(ref_store, refs->gitdir), + reflog_iterator_begin(ref_store, refs->gitcommondir), + reflog_iterator_select, refs); + } +} + +/* + * If update is a direct update of head_ref (the reference pointed to + * by HEAD), then add an extra REF_LOG_ONLY update for HEAD. + */ +static int split_head_update(struct ref_update *update, + struct ref_transaction *transaction, + const char *head_ref, + struct string_list *affected_refnames, + struct strbuf *err) +{ + struct string_list_item *item; + struct ref_update *new_update; + + if ((update->flags & REF_LOG_ONLY) || + (update->flags & REF_IS_PRUNING) || + (update->flags & REF_UPDATE_VIA_HEAD)) + return 0; + + if (strcmp(update->refname, head_ref)) + return 0; + + /* + * First make sure that HEAD is not already in the + * transaction. This check is O(lg N) in the transaction + * size, but it happens at most once per transaction. + */ + if (string_list_has_string(affected_refnames, "HEAD")) { + /* An entry already existed */ + strbuf_addf(err, + "multiple updates for 'HEAD' (including one " + "via its referent '%s') are not allowed", + update->refname); + return TRANSACTION_NAME_CONFLICT; + } + + new_update = ref_transaction_add_update( + transaction, "HEAD", + update->flags | REF_LOG_ONLY | REF_NO_DEREF, + &update->new_oid, &update->old_oid, + update->msg); + + /* + * Add "HEAD". This insertion is O(N) in the transaction + * size, but it happens at most once per transaction. + * Add new_update->refname instead of a literal "HEAD". + */ + if (strcmp(new_update->refname, "HEAD")) + BUG("%s unexpectedly not 'HEAD'", new_update->refname); + item = string_list_insert(affected_refnames, new_update->refname); + item->util = new_update; + + return 0; +} + +/* + * update is for a symref that points at referent and doesn't have + * REF_NO_DEREF set. Split it into two updates: + * - The original update, but with REF_LOG_ONLY and REF_NO_DEREF set + * - A new, separate update for the referent reference + * Note that the new update will itself be subject to splitting when + * the iteration gets to it. + */ +static int split_symref_update(struct files_ref_store *refs, + struct ref_update *update, + const char *referent, + struct ref_transaction *transaction, + struct string_list *affected_refnames, + struct strbuf *err) +{ + struct string_list_item *item; + struct ref_update *new_update; + unsigned int new_flags; + + /* + * First make sure that referent is not already in the + * transaction. This check is O(lg N) in the transaction + * size, but it happens at most once per symref in a + * transaction. + */ + if (string_list_has_string(affected_refnames, referent)) { + /* An entry already exists */ + strbuf_addf(err, + "multiple updates for '%s' (including one " + "via symref '%s') are not allowed", + referent, update->refname); + return TRANSACTION_NAME_CONFLICT; + } + + new_flags = update->flags; + if (!strcmp(update->refname, "HEAD")) { + /* + * Record that the new update came via HEAD, so that + * when we process it, split_head_update() doesn't try + * to add another reflog update for HEAD. Note that + * this bit will be propagated if the new_update + * itself needs to be split. + */ + new_flags |= REF_UPDATE_VIA_HEAD; + } + + new_update = ref_transaction_add_update( + transaction, referent, new_flags, + &update->new_oid, &update->old_oid, + update->msg); + + new_update->parent_update = update; + + /* + * Change the symbolic ref update to log only. Also, it + * doesn't need to check its old OID value, as that will be + * done when new_update is processed. + */ + update->flags |= REF_LOG_ONLY | REF_NO_DEREF; + update->flags &= ~REF_HAVE_OLD; + + /* + * Add the referent. This insertion is O(N) in the transaction + * size, but it happens at most once per symref in a + * transaction. Make sure to add new_update->refname, which will + * be valid as long as affected_refnames is in use, and NOT + * referent, which might soon be freed by our caller. + */ + item = string_list_insert(affected_refnames, new_update->refname); + if (item->util) + BUG("%s unexpectedly found in affected_refnames", + new_update->refname); + item->util = new_update; + + return 0; +} + +/* + * Return the refname under which update was originally requested. + */ +static const char *original_update_refname(struct ref_update *update) +{ + while (update->parent_update) + update = update->parent_update; + + return update->refname; +} + +/* + * Check whether the REF_HAVE_OLD and old_oid values stored in update + * are consistent with oid, which is the reference's current value. If + * everything is OK, return 0; otherwise, write an error message to + * err and return -1. + */ +static int check_old_oid(struct ref_update *update, struct object_id *oid, + struct strbuf *err) +{ + if (!(update->flags & REF_HAVE_OLD) || + !oidcmp(oid, &update->old_oid)) + return 0; + + if (is_null_oid(&update->old_oid)) + strbuf_addf(err, "cannot lock ref '%s': " + "reference already exists", + original_update_refname(update)); + else if (is_null_oid(oid)) + strbuf_addf(err, "cannot lock ref '%s': " + "reference is missing but expected %s", + original_update_refname(update), + oid_to_hex(&update->old_oid)); + else + strbuf_addf(err, "cannot lock ref '%s': " + "is at %s but expected %s", + original_update_refname(update), + oid_to_hex(oid), + oid_to_hex(&update->old_oid)); + + return -1; +} + +/* + * Prepare for carrying out update: + * - Lock the reference referred to by update. + * - Read the reference under lock. + * - Check that its old OID value (if specified) is correct, and in + * any case record it in update->lock->old_oid for later use when + * writing the reflog. + * - If it is a symref update without REF_NO_DEREF, split it up into a + * REF_LOG_ONLY update of the symref and add a separate update for + * the referent to transaction. + * - If it is an update of head_ref, add a corresponding REF_LOG_ONLY + * update of HEAD. + */ +static int lock_ref_for_update(struct files_ref_store *refs, + struct ref_update *update, + struct ref_transaction *transaction, + const char *head_ref, + struct string_list *affected_refnames, + struct strbuf *err) +{ + struct strbuf referent = STRBUF_INIT; + int mustexist = (update->flags & REF_HAVE_OLD) && + !is_null_oid(&update->old_oid); + int ret = 0; + struct ref_lock *lock; + + files_assert_main_repository(refs, "lock_ref_for_update"); + + if ((update->flags & REF_HAVE_NEW) && is_null_oid(&update->new_oid)) + update->flags |= REF_DELETING; + + if (head_ref) { + ret = split_head_update(update, transaction, head_ref, + affected_refnames, err); + if (ret) + goto out; + } + + ret = lock_raw_ref(refs, update->refname, mustexist, + affected_refnames, NULL, + &lock, &referent, + &update->type, err); + if (ret) { + char *reason; + + reason = strbuf_detach(err, NULL); + strbuf_addf(err, "cannot lock ref '%s': %s", + original_update_refname(update), reason); + free(reason); + goto out; + } + + update->backend_data = lock; + + if (update->type & REF_ISSYMREF) { + if (update->flags & REF_NO_DEREF) { + /* + * We won't be reading the referent as part of + * the transaction, so we have to read it here + * to record and possibly check old_oid: + */ + if (refs_read_ref_full(&refs->base, + referent.buf, 0, + &lock->old_oid, NULL)) { + if (update->flags & REF_HAVE_OLD) { + strbuf_addf(err, "cannot lock ref '%s': " + "error reading reference", + original_update_refname(update)); + ret = TRANSACTION_GENERIC_ERROR; + goto out; + } + } else if (check_old_oid(update, &lock->old_oid, err)) { + ret = TRANSACTION_GENERIC_ERROR; + goto out; + } + } else { + /* + * Create a new update for the reference this + * symref is pointing at. Also, we will record + * and verify old_oid for this update as part + * of processing the split-off update, so we + * don't have to do it here. + */ + ret = split_symref_update(refs, update, + referent.buf, transaction, + affected_refnames, err); + if (ret) + goto out; + } + } else { + struct ref_update *parent_update; + + if (check_old_oid(update, &lock->old_oid, err)) { + ret = TRANSACTION_GENERIC_ERROR; + goto out; + } + + /* + * If this update is happening indirectly because of a + * symref update, record the old OID in the parent + * update: + */ + for (parent_update = update->parent_update; + parent_update; + parent_update = parent_update->parent_update) { + struct ref_lock *parent_lock = parent_update->backend_data; + oidcpy(&parent_lock->old_oid, &lock->old_oid); + } + } + + if ((update->flags & REF_HAVE_NEW) && + !(update->flags & REF_DELETING) && + !(update->flags & REF_LOG_ONLY)) { + if (!(update->type & REF_ISSYMREF) && + !oidcmp(&lock->old_oid, &update->new_oid)) { + /* + * The reference already has the desired + * value, so we don't need to write it. + */ + } else if (write_ref_to_lockfile(lock, &update->new_oid, + err)) { + char *write_err = strbuf_detach(err, NULL); + + /* + * The lock was freed upon failure of + * write_ref_to_lockfile(): + */ + update->backend_data = NULL; strbuf_addf(err, - "Multiple updates for ref '%s' not allowed.", - refnames->items[i].string); - return 1; + "cannot update ref '%s': %s", + update->refname, write_err); + free(write_err); + ret = TRANSACTION_GENERIC_ERROR; + goto out; + } else { + update->flags |= REF_NEEDS_COMMIT; } - return 0; + } + if (!(update->flags & REF_NEEDS_COMMIT)) { + /* + * We didn't call write_ref_to_lockfile(), so + * the lockfile is still open. Close it to + * free up the file descriptor: + */ + if (close_ref_gently(lock)) { + strbuf_addf(err, "couldn't close '%s.lock'", + update->refname); + ret = TRANSACTION_GENERIC_ERROR; + goto out; + } + } + +out: + strbuf_release(&referent); + return ret; +} + +struct files_transaction_backend_data { + struct ref_transaction *packed_transaction; + int packed_refs_locked; +}; + +/* + * Unlock any references in `transaction` that are still locked, and + * mark the transaction closed. + */ +static void files_transaction_cleanup(struct files_ref_store *refs, + struct ref_transaction *transaction) +{ + size_t i; + struct files_transaction_backend_data *backend_data = + transaction->backend_data; + struct strbuf err = STRBUF_INIT; + + for (i = 0; i < transaction->nr; i++) { + struct ref_update *update = transaction->updates[i]; + struct ref_lock *lock = update->backend_data; + + if (lock) { + unlock_ref(lock); + update->backend_data = NULL; + } + } + + if (backend_data->packed_transaction && + ref_transaction_abort(backend_data->packed_transaction, &err)) { + error("error aborting transaction: %s", err.buf); + strbuf_release(&err); + } + + if (backend_data->packed_refs_locked) + packed_refs_unlock(refs->packed_ref_store); + + free(backend_data); + + transaction->state = REF_TRANSACTION_CLOSED; } -int ref_transaction_commit(struct ref_transaction *transaction, - struct strbuf *err) +static int files_transaction_prepare(struct ref_store *ref_store, + struct ref_transaction *transaction, + struct strbuf *err) { - int ret = 0, i; - int n = transaction->nr; - struct ref_update **updates = transaction->updates; - struct string_list refs_to_delete = STRING_LIST_INIT_NODUP; - struct string_list_item *ref_to_delete; + struct files_ref_store *refs = + files_downcast(ref_store, REF_STORE_WRITE, + "ref_transaction_prepare"); + size_t i; + int ret = 0; struct string_list affected_refnames = STRING_LIST_INIT_NODUP; + char *head_ref = NULL; + int head_type; + struct files_transaction_backend_data *backend_data; + struct ref_transaction *packed_transaction = NULL; assert(err); - if (transaction->state != REF_TRANSACTION_OPEN) - die("BUG: commit called for transaction that is not open"); + if (!transaction->nr) + goto cleanup; - if (!n) { - transaction->state = REF_TRANSACTION_CLOSED; - return 0; - } + backend_data = xcalloc(1, sizeof(*backend_data)); + transaction->backend_data = backend_data; - /* Fail if a refname appears more than once in the transaction: */ - for (i = 0; i < n; i++) - string_list_append(&affected_refnames, updates[i]->refname); + /* + * Fail if a refname appears more than once in the + * transaction. (If we end up splitting up any updates using + * split_symref_update() or split_head_update(), those + * functions will check that the new updates don't have the + * same refname as any existing ones.) Also fail if any of the + * updates use REF_IS_PRUNING without REF_NO_DEREF. + */ + for (i = 0; i < transaction->nr; i++) { + struct ref_update *update = transaction->updates[i]; + struct string_list_item *item = + string_list_append(&affected_refnames, update->refname); + + if ((update->flags & REF_IS_PRUNING) && + !(update->flags & REF_NO_DEREF)) + BUG("REF_IS_PRUNING set without REF_NO_DEREF"); + + /* + * We store a pointer to update in item->util, but at + * the moment we never use the value of this field + * except to check whether it is non-NULL. + */ + item->util = update; + } string_list_sort(&affected_refnames); if (ref_update_reject_duplicates(&affected_refnames, err)) { ret = TRANSACTION_GENERIC_ERROR; @@ -3167,135 +2588,262 @@ int ref_transaction_commit(struct ref_transaction *transaction, } /* + * Special hack: If a branch is updated directly and HEAD + * points to it (may happen on the remote side of a push + * for example) then logically the HEAD reflog should be + * updated too. + * + * A generic solution would require reverse symref lookups, + * but finding all symrefs pointing to a given branch would be + * rather costly for this rare event (the direct update of a + * branch) to be worth it. So let's cheat and check with HEAD + * only, which should cover 99% of all usage scenarios (even + * 100% of the default ones). + * + * So if HEAD is a symbolic reference, then record the name of + * the reference that it points to. If we see an update of + * head_ref within the transaction, then split_head_update() + * arranges for the reflog of HEAD to be updated, too. + */ + head_ref = refs_resolve_refdup(ref_store, "HEAD", + RESOLVE_REF_NO_RECURSE, + NULL, &head_type); + + if (head_ref && !(head_type & REF_ISSYMREF)) { + FREE_AND_NULL(head_ref); + } + + /* * Acquire all locks, verify old values if provided, check * that new values are valid, and write new values to the * lockfiles, ready to be activated. Only keep one lockfile * open at a time to avoid running out of file descriptors. + * Note that lock_ref_for_update() might append more updates + * to the transaction. */ - for (i = 0; i < n; i++) { - struct ref_update *update = updates[i]; - - if ((update->flags & REF_HAVE_NEW) && - is_null_sha1(update->new_sha1)) - update->flags |= REF_DELETING; - update->lock = lock_ref_sha1_basic( - update->refname, - ((update->flags & REF_HAVE_OLD) ? - update->old_sha1 : NULL), - &affected_refnames, NULL, - update->flags, - &update->type, - err); - if (!update->lock) { - char *reason; - - ret = (errno == ENOTDIR) - ? TRANSACTION_NAME_CONFLICT - : TRANSACTION_GENERIC_ERROR; - reason = strbuf_detach(err, NULL); - strbuf_addf(err, "cannot lock ref '%s': %s", - update->refname, reason); - free(reason); + for (i = 0; i < transaction->nr; i++) { + struct ref_update *update = transaction->updates[i]; + + ret = lock_ref_for_update(refs, update, transaction, + head_ref, &affected_refnames, err); + if (ret) goto cleanup; - } - if ((update->flags & REF_HAVE_NEW) && - !(update->flags & REF_DELETING)) { - int overwriting_symref = ((update->type & REF_ISSYMREF) && - (update->flags & REF_NODEREF)); - if (!overwriting_symref && - !hashcmp(update->lock->old_oid.hash, update->new_sha1)) { - /* - * The reference already has the desired - * value, so we don't need to write it. - */ - } else if (write_ref_to_lockfile(update->lock, - update->new_sha1, - err)) { - char *write_err = strbuf_detach(err, NULL); + if (update->flags & REF_DELETING && + !(update->flags & REF_LOG_ONLY) && + !(update->flags & REF_IS_PRUNING)) { + /* + * This reference has to be deleted from + * packed-refs if it exists there. + */ + if (!packed_transaction) { + packed_transaction = ref_store_transaction_begin( + refs->packed_ref_store, err); + if (!packed_transaction) { + ret = TRANSACTION_GENERIC_ERROR; + goto cleanup; + } - /* - * The lock was freed upon failure of - * write_ref_to_lockfile(): - */ - update->lock = NULL; - strbuf_addf(err, - "cannot update the ref '%s': %s", - update->refname, write_err); - free(write_err); - ret = TRANSACTION_GENERIC_ERROR; - goto cleanup; - } else { - update->flags |= REF_NEEDS_COMMIT; + backend_data->packed_transaction = + packed_transaction; } + + ref_transaction_add_update( + packed_transaction, update->refname, + REF_HAVE_NEW | REF_NO_DEREF, + &update->new_oid, NULL, + NULL); + } + } + + if (packed_transaction) { + if (packed_refs_lock(refs->packed_ref_store, 0, err)) { + ret = TRANSACTION_GENERIC_ERROR; + goto cleanup; } - if (!(update->flags & REF_NEEDS_COMMIT)) { + backend_data->packed_refs_locked = 1; + + if (is_packed_transaction_needed(refs->packed_ref_store, + packed_transaction)) { + ret = ref_transaction_prepare(packed_transaction, err); + } else { /* - * We didn't have to write anything to the lockfile. - * Close it to free up the file descriptor: + * We can skip rewriting the `packed-refs` + * file. But we do need to leave it locked, so + * that somebody else doesn't pack a reference + * that we are trying to delete. */ - if (close_ref(update->lock)) { - strbuf_addf(err, "Couldn't close %s.lock", - update->refname); + if (ref_transaction_abort(packed_transaction, err)) { + ret = TRANSACTION_GENERIC_ERROR; goto cleanup; } + backend_data->packed_transaction = NULL; } } - /* Perform updates first so live commits remain referenced */ - for (i = 0; i < n; i++) { - struct ref_update *update = updates[i]; +cleanup: + free(head_ref); + string_list_clear(&affected_refnames, 0); - if (update->flags & REF_NEEDS_COMMIT) { - if (commit_ref_update(update->lock, - update->new_sha1, update->msg, - update->flags, err)) { - /* freed by commit_ref_update(): */ - update->lock = NULL; + if (ret) + files_transaction_cleanup(refs, transaction); + else + transaction->state = REF_TRANSACTION_PREPARED; + + return ret; +} + +static int files_transaction_finish(struct ref_store *ref_store, + struct ref_transaction *transaction, + struct strbuf *err) +{ + struct files_ref_store *refs = + files_downcast(ref_store, 0, "ref_transaction_finish"); + size_t i; + int ret = 0; + struct strbuf sb = STRBUF_INIT; + struct files_transaction_backend_data *backend_data; + struct ref_transaction *packed_transaction; + + + assert(err); + + if (!transaction->nr) { + transaction->state = REF_TRANSACTION_CLOSED; + return 0; + } + + backend_data = transaction->backend_data; + packed_transaction = backend_data->packed_transaction; + + /* Perform updates first so live commits remain referenced */ + for (i = 0; i < transaction->nr; i++) { + struct ref_update *update = transaction->updates[i]; + struct ref_lock *lock = update->backend_data; + + if (update->flags & REF_NEEDS_COMMIT || + update->flags & REF_LOG_ONLY) { + if (files_log_ref_write(refs, + lock->ref_name, + &lock->old_oid, + &update->new_oid, + update->msg, update->flags, + err)) { + char *old_msg = strbuf_detach(err, NULL); + + strbuf_addf(err, "cannot update the ref '%s': %s", + lock->ref_name, old_msg); + free(old_msg); + unlock_ref(lock); + update->backend_data = NULL; ret = TRANSACTION_GENERIC_ERROR; goto cleanup; - } else { - /* freed by commit_ref_update(): */ - update->lock = NULL; } } - } - - /* Perform deletes now that updates are safely completed */ - for (i = 0; i < n; i++) { - struct ref_update *update = updates[i]; - - if (update->flags & REF_DELETING) { - if (delete_ref_loose(update->lock, update->type, err)) { + if (update->flags & REF_NEEDS_COMMIT) { + clear_loose_ref_cache(refs); + if (commit_ref(lock)) { + strbuf_addf(err, "couldn't set '%s'", lock->ref_name); + unlock_ref(lock); + update->backend_data = NULL; ret = TRANSACTION_GENERIC_ERROR; goto cleanup; } + } + } - if (!(update->flags & REF_ISPRUNING)) - string_list_append(&refs_to_delete, - update->lock->ref_name); + /* + * Now that updates are safely completed, we can perform + * deletes. First delete the reflogs of any references that + * will be deleted, since (in the unexpected event of an + * error) leaving a reference without a reflog is less bad + * than leaving a reflog without a reference (the latter is a + * mildly invalid repository state): + */ + for (i = 0; i < transaction->nr; i++) { + struct ref_update *update = transaction->updates[i]; + if (update->flags & REF_DELETING && + !(update->flags & REF_LOG_ONLY) && + !(update->flags & REF_IS_PRUNING)) { + strbuf_reset(&sb); + files_reflog_path(refs, &sb, update->refname); + if (!unlink_or_warn(sb.buf)) + try_remove_empty_parents(refs, update->refname, + REMOVE_EMPTY_PARENTS_REFLOG); } } - if (repack_without_refs(&refs_to_delete, err)) { - ret = TRANSACTION_GENERIC_ERROR; - goto cleanup; + /* + * Perform deletes now that updates are safely completed. + * + * First delete any packed versions of the references, while + * retaining the packed-refs lock: + */ + if (packed_transaction) { + ret = ref_transaction_commit(packed_transaction, err); + ref_transaction_free(packed_transaction); + packed_transaction = NULL; + backend_data->packed_transaction = NULL; + if (ret) + goto cleanup; + } + + /* Now delete the loose versions of the references: */ + for (i = 0; i < transaction->nr; i++) { + struct ref_update *update = transaction->updates[i]; + struct ref_lock *lock = update->backend_data; + + if (update->flags & REF_DELETING && + !(update->flags & REF_LOG_ONLY)) { + if (!(update->type & REF_ISPACKED) || + update->type & REF_ISSYMREF) { + /* It is a loose reference. */ + strbuf_reset(&sb); + files_ref_path(refs, &sb, lock->ref_name); + if (unlink_or_msg(sb.buf, err)) { + ret = TRANSACTION_GENERIC_ERROR; + goto cleanup; + } + update->flags |= REF_DELETED_LOOSE; + } + } } - for_each_string_list_item(ref_to_delete, &refs_to_delete) - unlink_or_warn(git_path("logs/%s", ref_to_delete->string)); - clear_loose_ref_cache(&ref_cache); + + clear_loose_ref_cache(refs); cleanup: - transaction->state = REF_TRANSACTION_CLOSED; + files_transaction_cleanup(refs, transaction); - for (i = 0; i < n; i++) - if (updates[i]->lock) - unlock_ref(updates[i]->lock); - string_list_clear(&refs_to_delete, 0); - string_list_clear(&affected_refnames, 0); + for (i = 0; i < transaction->nr; i++) { + struct ref_update *update = transaction->updates[i]; + + if (update->flags & REF_DELETED_LOOSE) { + /* + * The loose reference was deleted. Delete any + * empty parent directories. (Note that this + * can only work because we have already + * removed the lockfile.) + */ + try_remove_empty_parents(refs, update->refname, + REMOVE_EMPTY_PARENTS_REF); + } + } + + strbuf_release(&sb); return ret; } +static int files_transaction_abort(struct ref_store *ref_store, + struct ref_transaction *transaction, + struct strbuf *err) +{ + struct files_ref_store *refs = + files_downcast(ref_store, 0, "ref_transaction_abort"); + + files_transaction_cleanup(refs, transaction); + return 0; +} + static int ref_present(const char *refname, const struct object_id *oid, int flags, void *cb_data) { @@ -3304,13 +2852,17 @@ static int ref_present(const char *refname, return string_list_has_string(affected_refnames, refname); } -int initial_ref_transaction_commit(struct ref_transaction *transaction, - struct strbuf *err) +static int files_initial_transaction_commit(struct ref_store *ref_store, + struct ref_transaction *transaction, + struct strbuf *err) { - int ret = 0, i; - int n = transaction->nr; - struct ref_update **updates = transaction->updates; + struct files_ref_store *refs = + files_downcast(ref_store, REF_STORE_WRITE, + "initial_ref_transaction_commit"); + size_t i; + int ret = 0; struct string_list affected_refnames = STRING_LIST_INIT_NODUP; + struct ref_transaction *packed_transaction = NULL; assert(err); @@ -3318,8 +2870,9 @@ int initial_ref_transaction_commit(struct ref_transaction *transaction, die("BUG: commit called for transaction that is not open"); /* Fail if a refname appears more than once in the transaction: */ - for (i = 0; i < n; i++) - string_list_append(&affected_refnames, updates[i]->refname); + for (i = 0; i < transaction->nr; i++) + string_list_append(&affected_refnames, + transaction->updates[i]->refname); string_list_sort(&affected_refnames); if (ref_update_reject_duplicates(&affected_refnames, err)) { ret = TRANSACTION_GENERIC_ERROR; @@ -3338,46 +2891,52 @@ int initial_ref_transaction_commit(struct ref_transaction *transaction, * so here we really only check that none of the references * that we are creating already exists. */ - if (for_each_rawref(ref_present, &affected_refnames)) + if (refs_for_each_rawref(&refs->base, ref_present, + &affected_refnames)) die("BUG: initial ref transaction called with existing refs"); - for (i = 0; i < n; i++) { - struct ref_update *update = updates[i]; + packed_transaction = ref_store_transaction_begin(refs->packed_ref_store, err); + if (!packed_transaction) { + ret = TRANSACTION_GENERIC_ERROR; + goto cleanup; + } + + for (i = 0; i < transaction->nr; i++) { + struct ref_update *update = transaction->updates[i]; if ((update->flags & REF_HAVE_OLD) && - !is_null_sha1(update->old_sha1)) + !is_null_oid(&update->old_oid)) die("BUG: initial ref transaction with old_sha1 set"); - if (verify_refname_available(update->refname, - &affected_refnames, NULL, - err)) { + if (refs_verify_refname_available(&refs->base, update->refname, + &affected_refnames, NULL, + err)) { ret = TRANSACTION_NAME_CONFLICT; goto cleanup; } + + /* + * Add a reference creation for this reference to the + * packed-refs transaction: + */ + ref_transaction_add_update(packed_transaction, update->refname, + update->flags & ~REF_HAVE_OLD, + &update->new_oid, &update->old_oid, + NULL); } - if (lock_packed_refs(0)) { - strbuf_addf(err, "unable to lock packed-refs file: %s", - strerror(errno)); + if (packed_refs_lock(refs->packed_ref_store, 0, err)) { ret = TRANSACTION_GENERIC_ERROR; goto cleanup; } - for (i = 0; i < n; i++) { - struct ref_update *update = updates[i]; - - if ((update->flags & REF_HAVE_NEW) && - !is_null_sha1(update->new_sha1)) - add_packed_ref(update->refname, update->new_sha1); - } - - if (commit_packed_refs()) { - strbuf_addf(err, "unable to commit packed-refs file: %s", - strerror(errno)); + if (initial_ref_transaction_commit(packed_transaction, err)) { ret = TRANSACTION_GENERIC_ERROR; - goto cleanup; } + packed_refs_unlock(refs->packed_ref_store); cleanup: + if (packed_transaction) + ref_transaction_free(packed_transaction); transaction->state = REF_TRANSACTION_CLOSED; string_list_clear(&affected_refnames, 0); return ret; @@ -3388,20 +2947,20 @@ struct expire_reflog_cb { reflog_expiry_should_prune_fn *should_prune_fn; void *policy_cb; FILE *newlog; - unsigned char last_kept_sha1[20]; + struct object_id last_kept_oid; }; -static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1, - const char *email, unsigned long timestamp, int tz, +static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid, + const char *email, timestamp_t timestamp, int tz, const char *message, void *cb_data) { struct expire_reflog_cb *cb = cb_data; struct expire_reflog_policy_cb *policy_cb = cb->policy_cb; if (cb->flags & EXPIRE_REFLOGS_REWRITE) - osha1 = cb->last_kept_sha1; + ooid = &cb->last_kept_oid; - if ((*cb->should_prune_fn)(osha1, nsha1, email, timestamp, tz, + if ((*cb->should_prune_fn)(ooid, noid, email, timestamp, tz, message, policy_cb)) { if (!cb->newlog) printf("would prune %s", message); @@ -3409,10 +2968,10 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1, printf("prune %s", message); } else { if (cb->newlog) { - fprintf(cb->newlog, "%s %s %s %lu %+05d\t%s", - sha1_to_hex(osha1), sha1_to_hex(nsha1), + fprintf(cb->newlog, "%s %s %s %"PRItime" %+05d\t%s", + oid_to_hex(ooid), oid_to_hex(noid), email, timestamp, tz, message); - hashcpy(cb->last_kept_sha1, nsha1); + oidcpy(&cb->last_kept_oid, noid); } if (cb->flags & EXPIRE_REFLOGS_VERBOSE) printf("keep %s", message); @@ -3420,16 +2979,20 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1, return 0; } -int reflog_expire(const char *refname, const unsigned char *sha1, - unsigned int flags, - reflog_expiry_prepare_fn prepare_fn, - reflog_expiry_should_prune_fn should_prune_fn, - reflog_expiry_cleanup_fn cleanup_fn, - void *policy_cb_data) +static int files_reflog_expire(struct ref_store *ref_store, + const char *refname, const struct object_id *oid, + unsigned int flags, + reflog_expiry_prepare_fn prepare_fn, + reflog_expiry_should_prune_fn should_prune_fn, + reflog_expiry_cleanup_fn cleanup_fn, + void *policy_cb_data) { + struct files_ref_store *refs = + files_downcast(ref_store, REF_STORE_WRITE, "reflog_expire"); static struct lock_file reflog_lock; struct expire_reflog_cb cb; struct ref_lock *lock; + struct strbuf log_file_sb = STRBUF_INIT; char *log_file; int status = 0; int type; @@ -3445,18 +3008,21 @@ int reflog_expire(const char *refname, const unsigned char *sha1, * reference itself, plus we might need to update the * reference if --updateref was specified: */ - lock = lock_ref_sha1_basic(refname, sha1, NULL, NULL, 0, &type, &err); + lock = lock_ref_oid_basic(refs, refname, oid, + NULL, NULL, REF_NO_DEREF, + &type, &err); if (!lock) { error("cannot lock ref '%s': %s", refname, err.buf); strbuf_release(&err); return -1; } - if (!reflog_exists(refname)) { + if (!refs_reflog_exists(ref_store, refname)) { unlock_ref(lock); return 0; } - log_file = git_pathdup("logs/%s", refname); + files_reflog_path(refs, &log_file_sb, refname); + log_file = strbuf_detach(&log_file_sb, NULL); if (!(flags & EXPIRE_REFLOGS_DRY_RUN)) { /* * Even though holding $GIT_DIR/logs/$reflog.lock has @@ -3480,8 +3046,8 @@ int reflog_expire(const char *refname, const unsigned char *sha1, } } - (*prepare_fn)(refname, sha1, cb.policy_cb); - for_each_reflog_ent(refname, expire_reflog_ent, &cb); + (*prepare_fn)(refname, oid, cb.policy_cb); + refs_for_each_reflog_ent(ref_store, refname, expire_reflog_ent, &cb); (*cleanup_fn)(cb.policy_cb); if (!(flags & EXPIRE_REFLOGS_DRY_RUN)) { @@ -3494,18 +3060,19 @@ int reflog_expire(const char *refname, const unsigned char *sha1, */ int update = (flags & EXPIRE_REFLOGS_UPDATE_REF) && !(type & REF_ISSYMREF) && - !is_null_sha1(cb.last_kept_sha1); + !is_null_oid(&cb.last_kept_oid); - if (close_lock_file(&reflog_lock)) { + if (close_lock_file_gently(&reflog_lock)) { status |= error("couldn't write %s: %s", log_file, strerror(errno)); + rollback_lock_file(&reflog_lock); } else if (update && - (write_in_full(get_lock_file_fd(lock->lk), - sha1_to_hex(cb.last_kept_sha1), 40) != 40 || - write_str_in_full(get_lock_file_fd(lock->lk), "\n") != 1 || - close_ref(lock) < 0)) { + (write_in_full(get_lock_file_fd(&lock->lk), + oid_to_hex(&cb.last_kept_oid), GIT_SHA1_HEXSZ) < 0 || + write_str_in_full(get_lock_file_fd(&lock->lk), "\n") < 0 || + close_ref_gently(lock) < 0)) { status |= error("couldn't write %s", - get_lock_file_path(lock->lk)); + get_lock_file_path(&lock->lk)); rollback_lock_file(&reflog_lock); } else if (commit_lock_file(&reflog_lock)) { status |= error("unable to write reflog '%s' (%s)", @@ -3524,3 +3091,51 @@ int reflog_expire(const char *refname, const unsigned char *sha1, unlock_ref(lock); return -1; } + +static int files_init_db(struct ref_store *ref_store, struct strbuf *err) +{ + struct files_ref_store *refs = + files_downcast(ref_store, REF_STORE_WRITE, "init_db"); + struct strbuf sb = STRBUF_INIT; + + /* + * Create .git/refs/{heads,tags} + */ + files_ref_path(refs, &sb, "refs/heads"); + safe_create_dir(sb.buf, 1); + + strbuf_reset(&sb); + files_ref_path(refs, &sb, "refs/tags"); + safe_create_dir(sb.buf, 1); + + strbuf_release(&sb); + return 0; +} + +struct ref_storage_be refs_be_files = { + NULL, + "files", + files_ref_store_create, + files_init_db, + files_transaction_prepare, + files_transaction_finish, + files_transaction_abort, + files_initial_transaction_commit, + + files_pack_refs, + files_create_symref, + files_delete_refs, + files_rename_ref, + files_copy_ref, + + files_ref_iterator_begin, + files_read_raw_ref, + + files_reflog_iterator_begin, + files_for_each_reflog_ent, + files_for_each_reflog_ent_reverse, + files_reflog_exists, + files_create_reflog, + files_delete_reflog, + files_reflog_expire +}; diff --git a/refs/iterator.c b/refs/iterator.c new file mode 100644 index 0000000000..bd35da4e62 --- /dev/null +++ b/refs/iterator.c @@ -0,0 +1,435 @@ +/* + * Generic reference iterator infrastructure. See refs-internal.h for + * documentation about the design and use of reference iterators. + */ + +#include "cache.h" +#include "refs.h" +#include "refs/refs-internal.h" +#include "iterator.h" + +int ref_iterator_advance(struct ref_iterator *ref_iterator) +{ + return ref_iterator->vtable->advance(ref_iterator); +} + +int ref_iterator_peel(struct ref_iterator *ref_iterator, + struct object_id *peeled) +{ + return ref_iterator->vtable->peel(ref_iterator, peeled); +} + +int ref_iterator_abort(struct ref_iterator *ref_iterator) +{ + return ref_iterator->vtable->abort(ref_iterator); +} + +void base_ref_iterator_init(struct ref_iterator *iter, + struct ref_iterator_vtable *vtable, + int ordered) +{ + iter->vtable = vtable; + iter->ordered = !!ordered; + iter->refname = NULL; + iter->oid = NULL; + iter->flags = 0; +} + +void base_ref_iterator_free(struct ref_iterator *iter) +{ + /* Help make use-after-free bugs fail quickly: */ + iter->vtable = NULL; + free(iter); +} + +struct empty_ref_iterator { + struct ref_iterator base; +}; + +static int empty_ref_iterator_advance(struct ref_iterator *ref_iterator) +{ + return ref_iterator_abort(ref_iterator); +} + +static int empty_ref_iterator_peel(struct ref_iterator *ref_iterator, + struct object_id *peeled) +{ + die("BUG: peel called for empty iterator"); +} + +static int empty_ref_iterator_abort(struct ref_iterator *ref_iterator) +{ + base_ref_iterator_free(ref_iterator); + return ITER_DONE; +} + +static struct ref_iterator_vtable empty_ref_iterator_vtable = { + empty_ref_iterator_advance, + empty_ref_iterator_peel, + empty_ref_iterator_abort +}; + +struct ref_iterator *empty_ref_iterator_begin(void) +{ + struct empty_ref_iterator *iter = xcalloc(1, sizeof(*iter)); + struct ref_iterator *ref_iterator = &iter->base; + + base_ref_iterator_init(ref_iterator, &empty_ref_iterator_vtable, 1); + return ref_iterator; +} + +int is_empty_ref_iterator(struct ref_iterator *ref_iterator) +{ + return ref_iterator->vtable == &empty_ref_iterator_vtable; +} + +struct merge_ref_iterator { + struct ref_iterator base; + + struct ref_iterator *iter0, *iter1; + + ref_iterator_select_fn *select; + void *cb_data; + + /* + * A pointer to iter0 or iter1 (whichever is supplying the + * current value), or NULL if advance has not yet been called. + */ + struct ref_iterator **current; +}; + +static int merge_ref_iterator_advance(struct ref_iterator *ref_iterator) +{ + struct merge_ref_iterator *iter = + (struct merge_ref_iterator *)ref_iterator; + int ok; + + if (!iter->current) { + /* Initialize: advance both iterators to their first entries */ + if ((ok = ref_iterator_advance(iter->iter0)) != ITER_OK) { + iter->iter0 = NULL; + if (ok == ITER_ERROR) + goto error; + } + if ((ok = ref_iterator_advance(iter->iter1)) != ITER_OK) { + iter->iter1 = NULL; + if (ok == ITER_ERROR) + goto error; + } + } else { + /* + * Advance the current iterator past the just-used + * entry: + */ + if ((ok = ref_iterator_advance(*iter->current)) != ITER_OK) { + *iter->current = NULL; + if (ok == ITER_ERROR) + goto error; + } + } + + /* Loop until we find an entry that we can yield. */ + while (1) { + struct ref_iterator **secondary; + enum iterator_selection selection = + iter->select(iter->iter0, iter->iter1, iter->cb_data); + + if (selection == ITER_SELECT_DONE) { + return ref_iterator_abort(ref_iterator); + } else if (selection == ITER_SELECT_ERROR) { + ref_iterator_abort(ref_iterator); + return ITER_ERROR; + } + + if ((selection & ITER_CURRENT_SELECTION_MASK) == 0) { + iter->current = &iter->iter0; + secondary = &iter->iter1; + } else { + iter->current = &iter->iter1; + secondary = &iter->iter0; + } + + if (selection & ITER_SKIP_SECONDARY) { + if ((ok = ref_iterator_advance(*secondary)) != ITER_OK) { + *secondary = NULL; + if (ok == ITER_ERROR) + goto error; + } + } + + if (selection & ITER_YIELD_CURRENT) { + iter->base.refname = (*iter->current)->refname; + iter->base.oid = (*iter->current)->oid; + iter->base.flags = (*iter->current)->flags; + return ITER_OK; + } + } + +error: + ref_iterator_abort(ref_iterator); + return ITER_ERROR; +} + +static int merge_ref_iterator_peel(struct ref_iterator *ref_iterator, + struct object_id *peeled) +{ + struct merge_ref_iterator *iter = + (struct merge_ref_iterator *)ref_iterator; + + if (!iter->current) { + die("BUG: peel called before advance for merge iterator"); + } + return ref_iterator_peel(*iter->current, peeled); +} + +static int merge_ref_iterator_abort(struct ref_iterator *ref_iterator) +{ + struct merge_ref_iterator *iter = + (struct merge_ref_iterator *)ref_iterator; + int ok = ITER_DONE; + + if (iter->iter0) { + if (ref_iterator_abort(iter->iter0) != ITER_DONE) + ok = ITER_ERROR; + } + if (iter->iter1) { + if (ref_iterator_abort(iter->iter1) != ITER_DONE) + ok = ITER_ERROR; + } + base_ref_iterator_free(ref_iterator); + return ok; +} + +static struct ref_iterator_vtable merge_ref_iterator_vtable = { + merge_ref_iterator_advance, + merge_ref_iterator_peel, + merge_ref_iterator_abort +}; + +struct ref_iterator *merge_ref_iterator_begin( + int ordered, + struct ref_iterator *iter0, struct ref_iterator *iter1, + ref_iterator_select_fn *select, void *cb_data) +{ + struct merge_ref_iterator *iter = xcalloc(1, sizeof(*iter)); + struct ref_iterator *ref_iterator = &iter->base; + + /* + * We can't do the same kind of is_empty_ref_iterator()-style + * optimization here as overlay_ref_iterator_begin() does, + * because we don't know the semantics of the select function. + * It might, for example, implement "intersect" by passing + * references through only if they exist in both iterators. + */ + + base_ref_iterator_init(ref_iterator, &merge_ref_iterator_vtable, ordered); + iter->iter0 = iter0; + iter->iter1 = iter1; + iter->select = select; + iter->cb_data = cb_data; + iter->current = NULL; + return ref_iterator; +} + +/* + * A ref_iterator_select_fn that overlays the items from front on top + * of those from back (like loose refs over packed refs). See + * overlay_ref_iterator_begin(). + */ +static enum iterator_selection overlay_iterator_select( + struct ref_iterator *front, struct ref_iterator *back, + void *cb_data) +{ + int cmp; + + if (!back) + return front ? ITER_SELECT_0 : ITER_SELECT_DONE; + else if (!front) + return ITER_SELECT_1; + + cmp = strcmp(front->refname, back->refname); + + if (cmp < 0) + return ITER_SELECT_0; + else if (cmp > 0) + return ITER_SELECT_1; + else + return ITER_SELECT_0_SKIP_1; +} + +struct ref_iterator *overlay_ref_iterator_begin( + struct ref_iterator *front, struct ref_iterator *back) +{ + /* + * Optimization: if one of the iterators is empty, return the + * other one rather than incurring the overhead of wrapping + * them. + */ + if (is_empty_ref_iterator(front)) { + ref_iterator_abort(front); + return back; + } else if (is_empty_ref_iterator(back)) { + ref_iterator_abort(back); + return front; + } else if (!front->ordered || !back->ordered) { + BUG("overlay_ref_iterator requires ordered inputs"); + } + + return merge_ref_iterator_begin(1, front, back, + overlay_iterator_select, NULL); +} + +struct prefix_ref_iterator { + struct ref_iterator base; + + struct ref_iterator *iter0; + char *prefix; + int trim; +}; + +/* Return -1, 0, 1 if refname is before, inside, or after the prefix. */ +static int compare_prefix(const char *refname, const char *prefix) +{ + while (*prefix) { + if (*refname != *prefix) + return ((unsigned char)*refname < (unsigned char)*prefix) ? -1 : +1; + + refname++; + prefix++; + } + + return 0; +} + +static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator) +{ + struct prefix_ref_iterator *iter = + (struct prefix_ref_iterator *)ref_iterator; + int ok; + + while ((ok = ref_iterator_advance(iter->iter0)) == ITER_OK) { + int cmp = compare_prefix(iter->iter0->refname, iter->prefix); + + if (cmp < 0) + continue; + + if (cmp > 0) { + /* + * If the source iterator is ordered, then we + * can stop the iteration as soon as we see a + * refname that comes after the prefix: + */ + if (iter->iter0->ordered) { + ok = ref_iterator_abort(iter->iter0); + break; + } else { + continue; + } + } + + if (iter->trim) { + /* + * It is nonsense to trim off characters that + * you haven't already checked for via a + * prefix check, whether via this + * `prefix_ref_iterator` or upstream in + * `iter0`). So if there wouldn't be at least + * one character left in the refname after + * trimming, report it as a bug: + */ + if (strlen(iter->iter0->refname) <= iter->trim) + die("BUG: attempt to trim too many characters"); + iter->base.refname = iter->iter0->refname + iter->trim; + } else { + iter->base.refname = iter->iter0->refname; + } + + iter->base.oid = iter->iter0->oid; + iter->base.flags = iter->iter0->flags; + return ITER_OK; + } + + iter->iter0 = NULL; + if (ref_iterator_abort(ref_iterator) != ITER_DONE) + return ITER_ERROR; + return ok; +} + +static int prefix_ref_iterator_peel(struct ref_iterator *ref_iterator, + struct object_id *peeled) +{ + struct prefix_ref_iterator *iter = + (struct prefix_ref_iterator *)ref_iterator; + + return ref_iterator_peel(iter->iter0, peeled); +} + +static int prefix_ref_iterator_abort(struct ref_iterator *ref_iterator) +{ + struct prefix_ref_iterator *iter = + (struct prefix_ref_iterator *)ref_iterator; + int ok = ITER_DONE; + + if (iter->iter0) + ok = ref_iterator_abort(iter->iter0); + free(iter->prefix); + base_ref_iterator_free(ref_iterator); + return ok; +} + +static struct ref_iterator_vtable prefix_ref_iterator_vtable = { + prefix_ref_iterator_advance, + prefix_ref_iterator_peel, + prefix_ref_iterator_abort +}; + +struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0, + const char *prefix, + int trim) +{ + struct prefix_ref_iterator *iter; + struct ref_iterator *ref_iterator; + + if (!*prefix && !trim) + return iter0; /* optimization: no need to wrap iterator */ + + iter = xcalloc(1, sizeof(*iter)); + ref_iterator = &iter->base; + + base_ref_iterator_init(ref_iterator, &prefix_ref_iterator_vtable, iter0->ordered); + + iter->iter0 = iter0; + iter->prefix = xstrdup(prefix); + iter->trim = trim; + + return ref_iterator; +} + +struct ref_iterator *current_ref_iter = NULL; + +int do_for_each_ref_iterator(struct ref_iterator *iter, + each_ref_fn fn, void *cb_data) +{ + int retval = 0, ok; + struct ref_iterator *old_ref_iter = current_ref_iter; + + current_ref_iter = iter; + while ((ok = ref_iterator_advance(iter)) == ITER_OK) { + retval = fn(iter->refname, iter->oid, iter->flags, cb_data); + if (retval) { + /* + * If ref_iterator_abort() returns ITER_ERROR, + * we ignore that error in deference to the + * callback function's return value. + */ + ref_iterator_abort(iter); + goto out; + } + } + +out: + current_ref_iter = old_ref_iter; + if (ok == ITER_ERROR) + return -1; + return retval; +} diff --git a/refs/packed-backend.c b/refs/packed-backend.c new file mode 100644 index 0000000000..65288c6472 --- /dev/null +++ b/refs/packed-backend.c @@ -0,0 +1,1656 @@ +#include "../cache.h" +#include "../config.h" +#include "../refs.h" +#include "refs-internal.h" +#include "packed-backend.h" +#include "../iterator.h" +#include "../lockfile.h" + +enum mmap_strategy { + /* + * Don't use mmap() at all for reading `packed-refs`. + */ + MMAP_NONE, + + /* + * Can use mmap() for reading `packed-refs`, but the file must + * not remain mmapped. This is the usual option on Windows, + * where you cannot rename a new version of a file onto a file + * that is currently mmapped. + */ + MMAP_TEMPORARY, + + /* + * It is OK to leave the `packed-refs` file mmapped while + * arbitrary other code is running. + */ + MMAP_OK +}; + +#if defined(NO_MMAP) +static enum mmap_strategy mmap_strategy = MMAP_NONE; +#elif defined(MMAP_PREVENTS_DELETE) +static enum mmap_strategy mmap_strategy = MMAP_TEMPORARY; +#else +static enum mmap_strategy mmap_strategy = MMAP_OK; +#endif + +struct packed_ref_store; + +/* + * A `snapshot` represents one snapshot of a `packed-refs` file. + * + * Normally, this will be a mmapped view of the contents of the + * `packed-refs` file at the time the snapshot was created. However, + * if the `packed-refs` file was not sorted, this might point at heap + * memory holding the contents of the `packed-refs` file with its + * records sorted by refname. + * + * `snapshot` instances are reference counted (via + * `acquire_snapshot()` and `release_snapshot()`). This is to prevent + * an instance from disappearing while an iterator is still iterating + * over it. Instances are garbage collected when their `referrers` + * count goes to zero. + * + * The most recent `snapshot`, if available, is referenced by the + * `packed_ref_store`. Its freshness is checked whenever + * `get_snapshot()` is called; if the existing snapshot is obsolete, a + * new snapshot is taken. + */ +struct snapshot { + /* + * A back-pointer to the packed_ref_store with which this + * snapshot is associated: + */ + struct packed_ref_store *refs; + + /* Is the `packed-refs` file currently mmapped? */ + int mmapped; + + /* + * The contents of the `packed-refs` file: + * + * - buf -- a pointer to the start of the memory + * - start -- a pointer to the first byte of actual references + * (i.e., after the header line, if one is present) + * - eof -- a pointer just past the end of the reference + * contents + * + * If the `packed-refs` file was already sorted, `buf` points + * at the mmapped contents of the file. If not, it points at + * heap-allocated memory containing the contents, sorted. If + * there were no contents (e.g., because the file didn't + * exist), `buf`, `start`, and `eof` are all NULL. + */ + char *buf, *start, *eof; + + /* + * What is the peeled state of the `packed-refs` file that + * this snapshot represents? (This is usually determined from + * the file's header.) + */ + enum { PEELED_NONE, PEELED_TAGS, PEELED_FULLY } peeled; + + /* + * Count of references to this instance, including the pointer + * from `packed_ref_store::snapshot`, if any. The instance + * will not be freed as long as the reference count is + * nonzero. + */ + unsigned int referrers; + + /* + * The metadata of the `packed-refs` file from which this + * snapshot was created, used to tell if the file has been + * replaced since we read it. + */ + struct stat_validity validity; +}; + +/* + * A `ref_store` representing references stored in a `packed-refs` + * file. It implements the `ref_store` interface, though it has some + * limitations: + * + * - It cannot store symbolic references. + * + * - It cannot store reflogs. + * + * - It does not support reference renaming (though it could). + * + * On the other hand, it can be locked outside of a reference + * transaction. In that case, it remains locked even after the + * transaction is done and the new `packed-refs` file is activated. + */ +struct packed_ref_store { + struct ref_store base; + + unsigned int store_flags; + + /* The path of the "packed-refs" file: */ + char *path; + + /* + * A snapshot of the values read from the `packed-refs` file, + * if it might still be current; otherwise, NULL. + */ + struct snapshot *snapshot; + + /* + * Lock used for the "packed-refs" file. Note that this (and + * thus the enclosing `packed_ref_store`) must not be freed. + */ + struct lock_file lock; + + /* + * Temporary file used when rewriting new contents to the + * "packed-refs" file. Note that this (and thus the enclosing + * `packed_ref_store`) must not be freed. + */ + struct tempfile *tempfile; +}; + +/* + * Increment the reference count of `*snapshot`. + */ +static void acquire_snapshot(struct snapshot *snapshot) +{ + snapshot->referrers++; +} + +/* + * If the buffer in `snapshot` is active, then either munmap the + * memory and close the file, or free the memory. Then set the buffer + * pointers to NULL. + */ +static void clear_snapshot_buffer(struct snapshot *snapshot) +{ + if (snapshot->mmapped) { + if (munmap(snapshot->buf, snapshot->eof - snapshot->buf)) + die_errno("error ummapping packed-refs file %s", + snapshot->refs->path); + snapshot->mmapped = 0; + } else { + free(snapshot->buf); + } + snapshot->buf = snapshot->start = snapshot->eof = NULL; +} + +/* + * Decrease the reference count of `*snapshot`. If it goes to zero, + * free `*snapshot` and return true; otherwise return false. + */ +static int release_snapshot(struct snapshot *snapshot) +{ + if (!--snapshot->referrers) { + stat_validity_clear(&snapshot->validity); + clear_snapshot_buffer(snapshot); + free(snapshot); + return 1; + } else { + return 0; + } +} + +struct ref_store *packed_ref_store_create(const char *path, + unsigned int store_flags) +{ + struct packed_ref_store *refs = xcalloc(1, sizeof(*refs)); + struct ref_store *ref_store = (struct ref_store *)refs; + + base_ref_store_init(ref_store, &refs_be_packed); + refs->store_flags = store_flags; + + refs->path = xstrdup(path); + return ref_store; +} + +/* + * Downcast `ref_store` to `packed_ref_store`. Die if `ref_store` is + * not a `packed_ref_store`. Also die if `packed_ref_store` doesn't + * support at least the flags specified in `required_flags`. `caller` + * is used in any necessary error messages. + */ +static struct packed_ref_store *packed_downcast(struct ref_store *ref_store, + unsigned int required_flags, + const char *caller) +{ + struct packed_ref_store *refs; + + if (ref_store->be != &refs_be_packed) + die("BUG: ref_store is type \"%s\" not \"packed\" in %s", + ref_store->be->name, caller); + + refs = (struct packed_ref_store *)ref_store; + + if ((refs->store_flags & required_flags) != required_flags) + die("BUG: unallowed operation (%s), requires %x, has %x\n", + caller, required_flags, refs->store_flags); + + return refs; +} + +static void clear_snapshot(struct packed_ref_store *refs) +{ + if (refs->snapshot) { + struct snapshot *snapshot = refs->snapshot; + + refs->snapshot = NULL; + release_snapshot(snapshot); + } +} + +static NORETURN void die_unterminated_line(const char *path, + const char *p, size_t len) +{ + if (len < 80) + die("unterminated line in %s: %.*s", path, (int)len, p); + else + die("unterminated line in %s: %.75s...", path, p); +} + +static NORETURN void die_invalid_line(const char *path, + const char *p, size_t len) +{ + const char *eol = memchr(p, '\n', len); + + if (!eol) + die_unterminated_line(path, p, len); + else if (eol - p < 80) + die("unexpected line in %s: %.*s", path, (int)(eol - p), p); + else + die("unexpected line in %s: %.75s...", path, p); + +} + +struct snapshot_record { + const char *start; + size_t len; +}; + +static int cmp_packed_ref_records(const void *v1, const void *v2) +{ + const struct snapshot_record *e1 = v1, *e2 = v2; + const char *r1 = e1->start + GIT_SHA1_HEXSZ + 1; + const char *r2 = e2->start + GIT_SHA1_HEXSZ + 1; + + while (1) { + if (*r1 == '\n') + return *r2 == '\n' ? 0 : -1; + if (*r1 != *r2) { + if (*r2 == '\n') + return 1; + else + return (unsigned char)*r1 < (unsigned char)*r2 ? -1 : +1; + } + r1++; + r2++; + } +} + +/* + * Compare a snapshot record at `rec` to the specified NUL-terminated + * refname. + */ +static int cmp_record_to_refname(const char *rec, const char *refname) +{ + const char *r1 = rec + GIT_SHA1_HEXSZ + 1; + const char *r2 = refname; + + while (1) { + if (*r1 == '\n') + return *r2 ? -1 : 0; + if (!*r2) + return 1; + if (*r1 != *r2) + return (unsigned char)*r1 < (unsigned char)*r2 ? -1 : +1; + r1++; + r2++; + } +} + +/* + * `snapshot->buf` is not known to be sorted. Check whether it is, and + * if not, sort it into new memory and munmap/free the old storage. + */ +static void sort_snapshot(struct snapshot *snapshot) +{ + struct snapshot_record *records = NULL; + size_t alloc = 0, nr = 0; + int sorted = 1; + const char *pos, *eof, *eol; + size_t len, i; + char *new_buffer, *dst; + + pos = snapshot->start; + eof = snapshot->eof; + + if (pos == eof) + return; + + len = eof - pos; + + /* + * Initialize records based on a crude estimate of the number + * of references in the file (we'll grow it below if needed): + */ + ALLOC_GROW(records, len / 80 + 20, alloc); + + while (pos < eof) { + eol = memchr(pos, '\n', eof - pos); + if (!eol) + /* The safety check should prevent this. */ + BUG("unterminated line found in packed-refs"); + if (eol - pos < GIT_SHA1_HEXSZ + 2) + die_invalid_line(snapshot->refs->path, + pos, eof - pos); + eol++; + if (eol < eof && *eol == '^') { + /* + * Keep any peeled line together with its + * reference: + */ + const char *peeled_start = eol; + + eol = memchr(peeled_start, '\n', eof - peeled_start); + if (!eol) + /* The safety check should prevent this. */ + BUG("unterminated peeled line found in packed-refs"); + eol++; + } + + ALLOC_GROW(records, nr + 1, alloc); + records[nr].start = pos; + records[nr].len = eol - pos; + nr++; + + if (sorted && + nr > 1 && + cmp_packed_ref_records(&records[nr - 2], + &records[nr - 1]) >= 0) + sorted = 0; + + pos = eol; + } + + if (sorted) + goto cleanup; + + /* We need to sort the memory. First we sort the records array: */ + QSORT(records, nr, cmp_packed_ref_records); + + /* + * Allocate a new chunk of memory, and copy the old memory to + * the new in the order indicated by `records` (not bothering + * with the header line): + */ + new_buffer = xmalloc(len); + for (dst = new_buffer, i = 0; i < nr; i++) { + memcpy(dst, records[i].start, records[i].len); + dst += records[i].len; + } + + /* + * Now munmap the old buffer and use the sorted buffer in its + * place: + */ + clear_snapshot_buffer(snapshot); + snapshot->buf = snapshot->start = new_buffer; + snapshot->eof = new_buffer + len; + +cleanup: + free(records); +} + +/* + * Return a pointer to the start of the record that contains the + * character `*p` (which must be within the buffer). If no other + * record start is found, return `buf`. + */ +static const char *find_start_of_record(const char *buf, const char *p) +{ + while (p > buf && (p[-1] != '\n' || p[0] == '^')) + p--; + return p; +} + +/* + * Return a pointer to the start of the record following the record + * that contains `*p`. If none is found before `end`, return `end`. + */ +static const char *find_end_of_record(const char *p, const char *end) +{ + while (++p < end && (p[-1] != '\n' || p[0] == '^')) + ; + return p; +} + +/* + * We want to be able to compare mmapped reference records quickly, + * without totally parsing them. We can do so because the records are + * LF-terminated, and the refname should start exactly (GIT_SHA1_HEXSZ + * + 1) bytes past the beginning of the record. + * + * But what if the `packed-refs` file contains garbage? We're willing + * to tolerate not detecting the problem, as long as we don't produce + * totally garbled output (we can't afford to check the integrity of + * the whole file during every Git invocation). But we do want to be + * sure that we never read past the end of the buffer in memory and + * perform an illegal memory access. + * + * Guarantee that minimum level of safety by verifying that the last + * record in the file is LF-terminated, and that it has at least + * (GIT_SHA1_HEXSZ + 1) characters before the LF. Die if either of + * these checks fails. + */ +static void verify_buffer_safe(struct snapshot *snapshot) +{ + const char *start = snapshot->start; + const char *eof = snapshot->eof; + const char *last_line; + + if (start == eof) + return; + + last_line = find_start_of_record(start, eof - 1); + if (*(eof - 1) != '\n' || eof - last_line < GIT_SHA1_HEXSZ + 2) + die_invalid_line(snapshot->refs->path, + last_line, eof - last_line); +} + +#define SMALL_FILE_SIZE (32*1024) + +/* + * Depending on `mmap_strategy`, either mmap or read the contents of + * the `packed-refs` file into the snapshot. Return 1 if the file + * existed and was read, or 0 if the file was absent or empty. Die on + * errors. + */ +static int load_contents(struct snapshot *snapshot) +{ + int fd; + struct stat st; + size_t size; + ssize_t bytes_read; + + fd = open(snapshot->refs->path, O_RDONLY); + if (fd < 0) { + if (errno == ENOENT) { + /* + * This is OK; it just means that no + * "packed-refs" file has been written yet, + * which is equivalent to it being empty, + * which is its state when initialized with + * zeros. + */ + return 0; + } else { + die_errno("couldn't read %s", snapshot->refs->path); + } + } + + stat_validity_update(&snapshot->validity, fd); + + if (fstat(fd, &st) < 0) + die_errno("couldn't stat %s", snapshot->refs->path); + size = xsize_t(st.st_size); + + if (!size) { + return 0; + } else if (mmap_strategy == MMAP_NONE || size <= SMALL_FILE_SIZE) { + snapshot->buf = xmalloc(size); + bytes_read = read_in_full(fd, snapshot->buf, size); + if (bytes_read < 0 || bytes_read != size) + die_errno("couldn't read %s", snapshot->refs->path); + snapshot->mmapped = 0; + } else { + snapshot->buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + snapshot->mmapped = 1; + } + close(fd); + + snapshot->start = snapshot->buf; + snapshot->eof = snapshot->buf + size; + + return 1; +} + +/* + * Find the place in `snapshot->buf` where the start of the record for + * `refname` starts. If `mustexist` is true and the reference doesn't + * exist, then return NULL. If `mustexist` is false and the reference + * doesn't exist, then return the point where that reference would be + * inserted, or `snapshot->eof` (which might be NULL) if it would be + * inserted at the end of the file. In the latter mode, `refname` + * doesn't have to be a proper reference name; for example, one could + * search for "refs/replace/" to find the start of any replace + * references. + * + * The record is sought using a binary search, so `snapshot->buf` must + * be sorted. + */ +static const char *find_reference_location(struct snapshot *snapshot, + const char *refname, int mustexist) +{ + /* + * This is not *quite* a garden-variety binary search, because + * the data we're searching is made up of records, and we + * always need to find the beginning of a record to do a + * comparison. A "record" here is one line for the reference + * itself and zero or one peel lines that start with '^'. Our + * loop invariant is described in the next two comments. + */ + + /* + * A pointer to the character at the start of a record whose + * preceding records all have reference names that come + * *before* `refname`. + */ + const char *lo = snapshot->start; + + /* + * A pointer to a the first character of a record whose + * reference name comes *after* `refname`. + */ + const char *hi = snapshot->eof; + + while (lo != hi) { + const char *mid, *rec; + int cmp; + + mid = lo + (hi - lo) / 2; + rec = find_start_of_record(lo, mid); + cmp = cmp_record_to_refname(rec, refname); + if (cmp < 0) { + lo = find_end_of_record(mid, hi); + } else if (cmp > 0) { + hi = rec; + } else { + return rec; + } + } + + if (mustexist) + return NULL; + else + return lo; +} + +/* + * Create a newly-allocated `snapshot` of the `packed-refs` file in + * its current state and return it. The return value will already have + * its reference count incremented. + * + * A comment line of the form "# pack-refs with: " may contain zero or + * more traits. We interpret the traits as follows: + * + * Neither `peeled` nor `fully-peeled`: + * + * Probably no references are peeled. But if the file contains a + * peeled value for a reference, we will use it. + * + * `peeled`: + * + * References under "refs/tags/", if they *can* be peeled, *are* + * peeled in this file. References outside of "refs/tags/" are + * probably not peeled even if they could have been, but if we find + * a peeled value for such a reference we will use it. + * + * `fully-peeled`: + * + * All references in the file that can be peeled are peeled. + * Inversely (and this is more important), any references in the + * file for which no peeled value is recorded is not peelable. This + * trait should typically be written alongside "peeled" for + * compatibility with older clients, but we do not require it + * (i.e., "peeled" is a no-op if "fully-peeled" is set). + * + * `sorted`: + * + * The references in this file are known to be sorted by refname. + */ +static struct snapshot *create_snapshot(struct packed_ref_store *refs) +{ + struct snapshot *snapshot = xcalloc(1, sizeof(*snapshot)); + int sorted = 0; + + snapshot->refs = refs; + acquire_snapshot(snapshot); + snapshot->peeled = PEELED_NONE; + + if (!load_contents(snapshot)) + return snapshot; + + /* If the file has a header line, process it: */ + if (snapshot->buf < snapshot->eof && *snapshot->buf == '#') { + char *tmp, *p, *eol; + struct string_list traits = STRING_LIST_INIT_NODUP; + + eol = memchr(snapshot->buf, '\n', + snapshot->eof - snapshot->buf); + if (!eol) + die_unterminated_line(refs->path, + snapshot->buf, + snapshot->eof - snapshot->buf); + + tmp = xmemdupz(snapshot->buf, eol - snapshot->buf); + + if (!skip_prefix(tmp, "# pack-refs with:", (const char **)&p)) + die_invalid_line(refs->path, + snapshot->buf, + snapshot->eof - snapshot->buf); + + string_list_split_in_place(&traits, p, ' ', -1); + + if (unsorted_string_list_has_string(&traits, "fully-peeled")) + snapshot->peeled = PEELED_FULLY; + else if (unsorted_string_list_has_string(&traits, "peeled")) + snapshot->peeled = PEELED_TAGS; + + sorted = unsorted_string_list_has_string(&traits, "sorted"); + + /* perhaps other traits later as well */ + + /* The "+ 1" is for the LF character. */ + snapshot->start = eol + 1; + + string_list_clear(&traits, 0); + free(tmp); + } + + verify_buffer_safe(snapshot); + + if (!sorted) { + sort_snapshot(snapshot); + + /* + * Reordering the records might have moved a short one + * to the end of the buffer, so verify the buffer's + * safety again: + */ + verify_buffer_safe(snapshot); + } + + if (mmap_strategy != MMAP_OK && snapshot->mmapped) { + /* + * We don't want to leave the file mmapped, so we are + * forced to make a copy now: + */ + size_t size = snapshot->eof - snapshot->start; + char *buf_copy = xmalloc(size); + + memcpy(buf_copy, snapshot->start, size); + clear_snapshot_buffer(snapshot); + snapshot->buf = snapshot->start = buf_copy; + snapshot->eof = buf_copy + size; + } + + return snapshot; +} + +/* + * Check that `refs->snapshot` (if present) still reflects the + * contents of the `packed-refs` file. If not, clear the snapshot. + */ +static void validate_snapshot(struct packed_ref_store *refs) +{ + if (refs->snapshot && + !stat_validity_check(&refs->snapshot->validity, refs->path)) + clear_snapshot(refs); +} + +/* + * Get the `snapshot` for the specified packed_ref_store, creating and + * populating it if it hasn't been read before or if the file has been + * changed (according to its `validity` field) since it was last read. + * On the other hand, if we hold the lock, then assume that the file + * hasn't been changed out from under us, so skip the extra `stat()` + * call in `stat_validity_check()`. This function does *not* increase + * the snapshot's reference count on behalf of the caller. + */ +static struct snapshot *get_snapshot(struct packed_ref_store *refs) +{ + if (!is_lock_file_locked(&refs->lock)) + validate_snapshot(refs); + + if (!refs->snapshot) + refs->snapshot = create_snapshot(refs); + + return refs->snapshot; +} + +static int packed_read_raw_ref(struct ref_store *ref_store, + const char *refname, struct object_id *oid, + struct strbuf *referent, unsigned int *type) +{ + struct packed_ref_store *refs = + packed_downcast(ref_store, REF_STORE_READ, "read_raw_ref"); + struct snapshot *snapshot = get_snapshot(refs); + const char *rec; + + *type = 0; + + rec = find_reference_location(snapshot, refname, 1); + + if (!rec) { + /* refname is not a packed reference. */ + errno = ENOENT; + return -1; + } + + if (get_oid_hex(rec, oid)) + die_invalid_line(refs->path, rec, snapshot->eof - rec); + + *type = REF_ISPACKED; + return 0; +} + +/* + * This value is set in `base.flags` if the peeled value of the + * current reference is known. In that case, `peeled` contains the + * correct peeled value for the reference, which might be `null_oid` + * if the reference is not a tag or if it is broken. + */ +#define REF_KNOWS_PEELED 0x40 + +/* + * An iterator over a snapshot of a `packed-refs` file. + */ +struct packed_ref_iterator { + struct ref_iterator base; + + struct snapshot *snapshot; + + /* The current position in the snapshot's buffer: */ + const char *pos; + + /* The end of the part of the buffer that will be iterated over: */ + const char *eof; + + /* Scratch space for current values: */ + struct object_id oid, peeled; + struct strbuf refname_buf; + + unsigned int flags; +}; + +/* + * Move the iterator to the next record in the snapshot, without + * respect for whether the record is actually required by the current + * iteration. Adjust the fields in `iter` and return `ITER_OK` or + * `ITER_DONE`. This function does not free the iterator in the case + * of `ITER_DONE`. + */ +static int next_record(struct packed_ref_iterator *iter) +{ + const char *p = iter->pos, *eol; + + strbuf_reset(&iter->refname_buf); + + if (iter->pos == iter->eof) + return ITER_DONE; + + iter->base.flags = REF_ISPACKED; + + if (iter->eof - p < GIT_SHA1_HEXSZ + 2 || + parse_oid_hex(p, &iter->oid, &p) || + !isspace(*p++)) + die_invalid_line(iter->snapshot->refs->path, + iter->pos, iter->eof - iter->pos); + + eol = memchr(p, '\n', iter->eof - p); + if (!eol) + die_unterminated_line(iter->snapshot->refs->path, + iter->pos, iter->eof - iter->pos); + + strbuf_add(&iter->refname_buf, p, eol - p); + iter->base.refname = iter->refname_buf.buf; + + if (check_refname_format(iter->base.refname, REFNAME_ALLOW_ONELEVEL)) { + if (!refname_is_safe(iter->base.refname)) + die("packed refname is dangerous: %s", + iter->base.refname); + oidclr(&iter->oid); + iter->base.flags |= REF_BAD_NAME | REF_ISBROKEN; + } + if (iter->snapshot->peeled == PEELED_FULLY || + (iter->snapshot->peeled == PEELED_TAGS && + starts_with(iter->base.refname, "refs/tags/"))) + iter->base.flags |= REF_KNOWS_PEELED; + + iter->pos = eol + 1; + + if (iter->pos < iter->eof && *iter->pos == '^') { + p = iter->pos + 1; + if (iter->eof - p < GIT_SHA1_HEXSZ + 1 || + parse_oid_hex(p, &iter->peeled, &p) || + *p++ != '\n') + die_invalid_line(iter->snapshot->refs->path, + iter->pos, iter->eof - iter->pos); + iter->pos = p; + + /* + * Regardless of what the file header said, we + * definitely know the value of *this* reference. But + * we suppress it if the reference is broken: + */ + if ((iter->base.flags & REF_ISBROKEN)) { + oidclr(&iter->peeled); + iter->base.flags &= ~REF_KNOWS_PEELED; + } else { + iter->base.flags |= REF_KNOWS_PEELED; + } + } else { + oidclr(&iter->peeled); + } + + return ITER_OK; +} + +static int packed_ref_iterator_advance(struct ref_iterator *ref_iterator) +{ + struct packed_ref_iterator *iter = + (struct packed_ref_iterator *)ref_iterator; + int ok; + + while ((ok = next_record(iter)) == ITER_OK) { + if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY && + ref_type(iter->base.refname) != REF_TYPE_PER_WORKTREE) + continue; + + if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) && + !ref_resolves_to_object(iter->base.refname, &iter->oid, + iter->flags)) + continue; + + return ITER_OK; + } + + if (ref_iterator_abort(ref_iterator) != ITER_DONE) + ok = ITER_ERROR; + + return ok; +} + +static int packed_ref_iterator_peel(struct ref_iterator *ref_iterator, + struct object_id *peeled) +{ + struct packed_ref_iterator *iter = + (struct packed_ref_iterator *)ref_iterator; + + if ((iter->base.flags & REF_KNOWS_PEELED)) { + oidcpy(peeled, &iter->peeled); + return is_null_oid(&iter->peeled) ? -1 : 0; + } else if ((iter->base.flags & (REF_ISBROKEN | REF_ISSYMREF))) { + return -1; + } else { + return !!peel_object(&iter->oid, peeled); + } +} + +static int packed_ref_iterator_abort(struct ref_iterator *ref_iterator) +{ + struct packed_ref_iterator *iter = + (struct packed_ref_iterator *)ref_iterator; + int ok = ITER_DONE; + + strbuf_release(&iter->refname_buf); + release_snapshot(iter->snapshot); + base_ref_iterator_free(ref_iterator); + return ok; +} + +static struct ref_iterator_vtable packed_ref_iterator_vtable = { + packed_ref_iterator_advance, + packed_ref_iterator_peel, + packed_ref_iterator_abort +}; + +static struct ref_iterator *packed_ref_iterator_begin( + struct ref_store *ref_store, + const char *prefix, unsigned int flags) +{ + struct packed_ref_store *refs; + struct snapshot *snapshot; + const char *start; + struct packed_ref_iterator *iter; + struct ref_iterator *ref_iterator; + unsigned int required_flags = REF_STORE_READ; + + if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) + required_flags |= REF_STORE_ODB; + refs = packed_downcast(ref_store, required_flags, "ref_iterator_begin"); + + /* + * Note that `get_snapshot()` internally checks whether the + * snapshot is up to date with what is on disk, and re-reads + * it if not. + */ + snapshot = get_snapshot(refs); + + if (prefix && *prefix) + start = find_reference_location(snapshot, prefix, 0); + else + start = snapshot->start; + + if (start == snapshot->eof) + return empty_ref_iterator_begin(); + + iter = xcalloc(1, sizeof(*iter)); + ref_iterator = &iter->base; + base_ref_iterator_init(ref_iterator, &packed_ref_iterator_vtable, 1); + + iter->snapshot = snapshot; + acquire_snapshot(snapshot); + + iter->pos = start; + iter->eof = snapshot->eof; + strbuf_init(&iter->refname_buf, 0); + + iter->base.oid = &iter->oid; + + iter->flags = flags; + + if (prefix && *prefix) + /* Stop iteration after we've gone *past* prefix: */ + ref_iterator = prefix_ref_iterator_begin(ref_iterator, prefix, 0); + + return ref_iterator; +} + +/* + * 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. On + * error, return a nonzero value and leave errno set at the value left + * by the failing call to `fprintf()`. + */ +static int write_packed_entry(FILE *fh, const char *refname, + const struct object_id *oid, + const struct object_id *peeled) +{ + if (fprintf(fh, "%s %s\n", oid_to_hex(oid), refname) < 0 || + (peeled && fprintf(fh, "^%s\n", oid_to_hex(peeled)) < 0)) + return -1; + + return 0; +} + +int packed_refs_lock(struct ref_store *ref_store, int flags, struct strbuf *err) +{ + struct packed_ref_store *refs = + packed_downcast(ref_store, REF_STORE_WRITE | REF_STORE_MAIN, + "packed_refs_lock"); + static int timeout_configured = 0; + static int timeout_value = 1000; + + if (!timeout_configured) { + git_config_get_int("core.packedrefstimeout", &timeout_value); + timeout_configured = 1; + } + + /* + * Note that we close the lockfile immediately because we + * don't write new content to it, but rather to a separate + * tempfile. + */ + if (hold_lock_file_for_update_timeout( + &refs->lock, + refs->path, + flags, timeout_value) < 0) { + unable_to_lock_message(refs->path, errno, err); + return -1; + } + + if (close_lock_file_gently(&refs->lock)) { + strbuf_addf(err, "unable to close %s: %s", refs->path, strerror(errno)); + rollback_lock_file(&refs->lock); + return -1; + } + + /* + * Now that we hold the `packed-refs` lock, make sure that our + * snapshot matches the current version of the file. Normally + * `get_snapshot()` does that for us, but that function + * assumes that when the file is locked, any existing snapshot + * is still valid. We've just locked the file, but it might + * have changed the moment *before* we locked it. + */ + validate_snapshot(refs); + + /* + * Now make sure that the packed-refs file as it exists in the + * locked state is loaded into the snapshot: + */ + get_snapshot(refs); + return 0; +} + +void packed_refs_unlock(struct ref_store *ref_store) +{ + struct packed_ref_store *refs = packed_downcast( + ref_store, + REF_STORE_READ | REF_STORE_WRITE, + "packed_refs_unlock"); + + if (!is_lock_file_locked(&refs->lock)) + die("BUG: packed_refs_unlock() called when not locked"); + rollback_lock_file(&refs->lock); +} + +int packed_refs_is_locked(struct ref_store *ref_store) +{ + struct packed_ref_store *refs = packed_downcast( + ref_store, + REF_STORE_READ | REF_STORE_WRITE, + "packed_refs_is_locked"); + + return is_lock_file_locked(&refs->lock); +} + +/* + * The packed-refs header line that we write out. Perhaps other traits + * will be added later. + * + * Note that earlier versions of Git used to parse these traits by + * looking for " trait " in the line. For this reason, the space after + * the colon and the trailing space are required. + */ +static const char PACKED_REFS_HEADER[] = + "# pack-refs with: peeled fully-peeled sorted \n"; + +static int packed_init_db(struct ref_store *ref_store, struct strbuf *err) +{ + /* Nothing to do. */ + return 0; +} + +/* + * Write the packed refs from the current snapshot to the packed-refs + * tempfile, incorporating any changes from `updates`. `updates` must + * be a sorted string list whose keys are the refnames and whose util + * values are `struct ref_update *`. On error, rollback the tempfile, + * write an error message to `err`, and return a nonzero value. + * + * The packfile must be locked before calling this function and will + * remain locked when it is done. + */ +static int write_with_updates(struct packed_ref_store *refs, + struct string_list *updates, + struct strbuf *err) +{ + struct ref_iterator *iter = NULL; + size_t i; + int ok; + FILE *out; + struct strbuf sb = STRBUF_INIT; + char *packed_refs_path; + + if (!is_lock_file_locked(&refs->lock)) + die("BUG: write_with_updates() called while unlocked"); + + /* + * If packed-refs is a symlink, we want to overwrite the + * symlinked-to file, not the symlink itself. Also, put the + * staging file next to it: + */ + packed_refs_path = get_locked_file_path(&refs->lock); + strbuf_addf(&sb, "%s.new", packed_refs_path); + free(packed_refs_path); + refs->tempfile = create_tempfile(sb.buf); + if (!refs->tempfile) { + strbuf_addf(err, "unable to create file %s: %s", + sb.buf, strerror(errno)); + strbuf_release(&sb); + return -1; + } + strbuf_release(&sb); + + out = fdopen_tempfile(refs->tempfile, "w"); + if (!out) { + strbuf_addf(err, "unable to fdopen packed-refs tempfile: %s", + strerror(errno)); + goto error; + } + + if (fprintf(out, "%s", PACKED_REFS_HEADER) < 0) + goto write_error; + + /* + * We iterate in parallel through the current list of refs and + * the list of updates, processing an entry from at least one + * of the lists each time through the loop. When the current + * list of refs is exhausted, set iter to NULL. When the list + * of updates is exhausted, leave i set to updates->nr. + */ + iter = packed_ref_iterator_begin(&refs->base, "", + DO_FOR_EACH_INCLUDE_BROKEN); + if ((ok = ref_iterator_advance(iter)) != ITER_OK) + iter = NULL; + + i = 0; + + while (iter || i < updates->nr) { + struct ref_update *update = NULL; + int cmp; + + if (i >= updates->nr) { + cmp = -1; + } else { + update = updates->items[i].util; + + if (!iter) + cmp = +1; + else + cmp = strcmp(iter->refname, update->refname); + } + + if (!cmp) { + /* + * There is both an old value and an update + * for this reference. Check the old value if + * necessary: + */ + if ((update->flags & REF_HAVE_OLD)) { + if (is_null_oid(&update->old_oid)) { + strbuf_addf(err, "cannot update ref '%s': " + "reference already exists", + update->refname); + goto error; + } else if (oidcmp(&update->old_oid, iter->oid)) { + strbuf_addf(err, "cannot update ref '%s': " + "is at %s but expected %s", + update->refname, + oid_to_hex(iter->oid), + oid_to_hex(&update->old_oid)); + goto error; + } + } + + /* Now figure out what to use for the new value: */ + if ((update->flags & REF_HAVE_NEW)) { + /* + * The update takes precedence. Skip + * the iterator over the unneeded + * value. + */ + if ((ok = ref_iterator_advance(iter)) != ITER_OK) + iter = NULL; + cmp = +1; + } else { + /* + * The update doesn't actually want to + * change anything. We're done with it. + */ + i++; + cmp = -1; + } + } else if (cmp > 0) { + /* + * There is no old value but there is an + * update for this reference. Make sure that + * the update didn't expect an existing value: + */ + if ((update->flags & REF_HAVE_OLD) && + !is_null_oid(&update->old_oid)) { + strbuf_addf(err, "cannot update ref '%s': " + "reference is missing but expected %s", + update->refname, + oid_to_hex(&update->old_oid)); + goto error; + } + } + + if (cmp < 0) { + /* Pass the old reference through. */ + + struct object_id peeled; + int peel_error = ref_iterator_peel(iter, &peeled); + + if (write_packed_entry(out, iter->refname, + iter->oid, + peel_error ? NULL : &peeled)) + goto write_error; + + if ((ok = ref_iterator_advance(iter)) != ITER_OK) + iter = NULL; + } else if (is_null_oid(&update->new_oid)) { + /* + * The update wants to delete the reference, + * and the reference either didn't exist or we + * have already skipped it. So we're done with + * the update (and don't have to write + * anything). + */ + i++; + } else { + struct object_id peeled; + int peel_error = peel_object(&update->new_oid, + &peeled); + + if (write_packed_entry(out, update->refname, + &update->new_oid, + peel_error ? NULL : &peeled)) + goto write_error; + + i++; + } + } + + if (ok != ITER_DONE) { + strbuf_addstr(err, "unable to write packed-refs file: " + "error iterating over old contents"); + goto error; + } + + if (close_tempfile_gently(refs->tempfile)) { + strbuf_addf(err, "error closing file %s: %s", + get_tempfile_path(refs->tempfile), + strerror(errno)); + strbuf_release(&sb); + delete_tempfile(&refs->tempfile); + return -1; + } + + return 0; + +write_error: + strbuf_addf(err, "error writing to %s: %s", + get_tempfile_path(refs->tempfile), strerror(errno)); + +error: + if (iter) + ref_iterator_abort(iter); + + delete_tempfile(&refs->tempfile); + return -1; +} + +int is_packed_transaction_needed(struct ref_store *ref_store, + struct ref_transaction *transaction) +{ + struct packed_ref_store *refs = packed_downcast( + ref_store, + REF_STORE_READ, + "is_packed_transaction_needed"); + struct strbuf referent = STRBUF_INIT; + size_t i; + int ret; + + if (!is_lock_file_locked(&refs->lock)) + BUG("is_packed_transaction_needed() called while unlocked"); + + /* + * We're only going to bother returning false for the common, + * trivial case that references are only being deleted, their + * old values are not being checked, and the old `packed-refs` + * file doesn't contain any of those reference(s). This gives + * false positives for some other cases that could + * theoretically be optimized away: + * + * 1. It could be that the old value is being verified without + * setting a new value. In this case, we could verify the + * old value here and skip the update if it agrees. If it + * disagrees, we could either let the update go through + * (the actual commit would re-detect and report the + * problem), or come up with a way of reporting such an + * error to *our* caller. + * + * 2. It could be that a new value is being set, but that it + * is identical to the current packed value of the + * reference. + * + * Neither of these cases will come up in the current code, + * because the only caller of this function passes to it a + * transaction that only includes `delete` updates with no + * `old_id`. Even if that ever changes, false positives only + * cause an optimization to be missed; they do not affect + * correctness. + */ + + /* + * Start with the cheap checks that don't require old + * reference values to be read: + */ + for (i = 0; i < transaction->nr; i++) { + struct ref_update *update = transaction->updates[i]; + + if (update->flags & REF_HAVE_OLD) + /* Have to check the old value -> needed. */ + return 1; + + if ((update->flags & REF_HAVE_NEW) && !is_null_oid(&update->new_oid)) + /* Have to set a new value -> needed. */ + return 1; + } + + /* + * The transaction isn't checking any old values nor is it + * setting any nonzero new values, so it still might be able + * to be skipped. Now do the more expensive check: the update + * is needed if any of the updates is a delete, and the old + * `packed-refs` file contains a value for that reference. + */ + ret = 0; + for (i = 0; i < transaction->nr; i++) { + struct ref_update *update = transaction->updates[i]; + unsigned int type; + struct object_id oid; + + if (!(update->flags & REF_HAVE_NEW)) + /* + * This reference isn't being deleted -> not + * needed. + */ + continue; + + if (!refs_read_raw_ref(ref_store, update->refname, + &oid, &referent, &type) || + errno != ENOENT) { + /* + * We have to actually delete that reference + * -> this transaction is needed. + */ + ret = 1; + break; + } + } + + strbuf_release(&referent); + return ret; +} + +struct packed_transaction_backend_data { + /* True iff the transaction owns the packed-refs lock. */ + int own_lock; + + struct string_list updates; +}; + +static void packed_transaction_cleanup(struct packed_ref_store *refs, + struct ref_transaction *transaction) +{ + struct packed_transaction_backend_data *data = transaction->backend_data; + + if (data) { + string_list_clear(&data->updates, 0); + + if (is_tempfile_active(refs->tempfile)) + delete_tempfile(&refs->tempfile); + + if (data->own_lock && is_lock_file_locked(&refs->lock)) { + packed_refs_unlock(&refs->base); + data->own_lock = 0; + } + + free(data); + transaction->backend_data = NULL; + } + + transaction->state = REF_TRANSACTION_CLOSED; +} + +static int packed_transaction_prepare(struct ref_store *ref_store, + struct ref_transaction *transaction, + struct strbuf *err) +{ + struct packed_ref_store *refs = packed_downcast( + ref_store, + REF_STORE_READ | REF_STORE_WRITE | REF_STORE_ODB, + "ref_transaction_prepare"); + struct packed_transaction_backend_data *data; + size_t i; + int ret = TRANSACTION_GENERIC_ERROR; + + /* + * Note that we *don't* skip transactions with zero updates, + * because such a transaction might be executed for the side + * effect of ensuring that all of the references are peeled or + * ensuring that the `packed-refs` file is sorted. If the + * caller wants to optimize away empty transactions, it should + * do so itself. + */ + + data = xcalloc(1, sizeof(*data)); + string_list_init(&data->updates, 0); + + transaction->backend_data = data; + + /* + * Stick the updates in a string list by refname so that we + * can sort them: + */ + for (i = 0; i < transaction->nr; i++) { + struct ref_update *update = transaction->updates[i]; + struct string_list_item *item = + string_list_append(&data->updates, update->refname); + + /* Store a pointer to update in item->util: */ + item->util = update; + } + string_list_sort(&data->updates); + + if (ref_update_reject_duplicates(&data->updates, err)) + goto failure; + + if (!is_lock_file_locked(&refs->lock)) { + if (packed_refs_lock(ref_store, 0, err)) + goto failure; + data->own_lock = 1; + } + + if (write_with_updates(refs, &data->updates, err)) + goto failure; + + transaction->state = REF_TRANSACTION_PREPARED; + return 0; + +failure: + packed_transaction_cleanup(refs, transaction); + return ret; +} + +static int packed_transaction_abort(struct ref_store *ref_store, + struct ref_transaction *transaction, + struct strbuf *err) +{ + struct packed_ref_store *refs = packed_downcast( + ref_store, + REF_STORE_READ | REF_STORE_WRITE | REF_STORE_ODB, + "ref_transaction_abort"); + + packed_transaction_cleanup(refs, transaction); + return 0; +} + +static int packed_transaction_finish(struct ref_store *ref_store, + struct ref_transaction *transaction, + struct strbuf *err) +{ + struct packed_ref_store *refs = packed_downcast( + ref_store, + REF_STORE_READ | REF_STORE_WRITE | REF_STORE_ODB, + "ref_transaction_finish"); + int ret = TRANSACTION_GENERIC_ERROR; + char *packed_refs_path; + + clear_snapshot(refs); + + packed_refs_path = get_locked_file_path(&refs->lock); + if (rename_tempfile(&refs->tempfile, packed_refs_path)) { + strbuf_addf(err, "error replacing %s: %s", + refs->path, strerror(errno)); + goto cleanup; + } + + ret = 0; + +cleanup: + free(packed_refs_path); + packed_transaction_cleanup(refs, transaction); + return ret; +} + +static int packed_initial_transaction_commit(struct ref_store *ref_store, + struct ref_transaction *transaction, + struct strbuf *err) +{ + return ref_transaction_commit(transaction, err); +} + +static int packed_delete_refs(struct ref_store *ref_store, const char *msg, + struct string_list *refnames, unsigned int flags) +{ + struct packed_ref_store *refs = + packed_downcast(ref_store, REF_STORE_WRITE, "delete_refs"); + struct strbuf err = STRBUF_INIT; + struct ref_transaction *transaction; + struct string_list_item *item; + int ret; + + (void)refs; /* We need the check above, but don't use the variable */ + + if (!refnames->nr) + return 0; + + /* + * Since we don't check the references' old_oids, the + * individual updates can't fail, so we can pack all of the + * updates into a single transaction. + */ + + transaction = ref_store_transaction_begin(ref_store, &err); + if (!transaction) + return -1; + + for_each_string_list_item(item, refnames) { + if (ref_transaction_delete(transaction, item->string, NULL, + flags, msg, &err)) { + warning(_("could not delete reference %s: %s"), + item->string, err.buf); + strbuf_reset(&err); + } + } + + ret = ref_transaction_commit(transaction, &err); + + if (ret) { + if (refnames->nr == 1) + error(_("could not delete reference %s: %s"), + refnames->items[0].string, err.buf); + else + error(_("could not delete references: %s"), err.buf); + } + + ref_transaction_free(transaction); + strbuf_release(&err); + return ret; +} + +static int packed_pack_refs(struct ref_store *ref_store, unsigned int flags) +{ + /* + * Packed refs are already packed. It might be that loose refs + * are packed *into* a packed refs store, but that is done by + * updating the packed references via a transaction. + */ + return 0; +} + +static int packed_create_symref(struct ref_store *ref_store, + const char *refname, const char *target, + const char *logmsg) +{ + die("BUG: packed reference store does not support symrefs"); +} + +static int packed_rename_ref(struct ref_store *ref_store, + const char *oldrefname, const char *newrefname, + const char *logmsg) +{ + die("BUG: packed reference store does not support renaming references"); +} + +static int packed_copy_ref(struct ref_store *ref_store, + const char *oldrefname, const char *newrefname, + const char *logmsg) +{ + die("BUG: packed reference store does not support copying references"); +} + +static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_store) +{ + return empty_ref_iterator_begin(); +} + +static int packed_for_each_reflog_ent(struct ref_store *ref_store, + const char *refname, + each_reflog_ent_fn fn, void *cb_data) +{ + return 0; +} + +static int packed_for_each_reflog_ent_reverse(struct ref_store *ref_store, + const char *refname, + each_reflog_ent_fn fn, + void *cb_data) +{ + return 0; +} + +static int packed_reflog_exists(struct ref_store *ref_store, + const char *refname) +{ + return 0; +} + +static int packed_create_reflog(struct ref_store *ref_store, + const char *refname, int force_create, + struct strbuf *err) +{ + die("BUG: packed reference store does not support reflogs"); +} + +static int packed_delete_reflog(struct ref_store *ref_store, + const char *refname) +{ + return 0; +} + +static int packed_reflog_expire(struct ref_store *ref_store, + const char *refname, const struct object_id *oid, + unsigned int flags, + reflog_expiry_prepare_fn prepare_fn, + reflog_expiry_should_prune_fn should_prune_fn, + reflog_expiry_cleanup_fn cleanup_fn, + void *policy_cb_data) +{ + return 0; +} + +struct ref_storage_be refs_be_packed = { + NULL, + "packed", + packed_ref_store_create, + packed_init_db, + packed_transaction_prepare, + packed_transaction_finish, + packed_transaction_abort, + packed_initial_transaction_commit, + + packed_pack_refs, + packed_create_symref, + packed_delete_refs, + packed_rename_ref, + packed_copy_ref, + + packed_ref_iterator_begin, + packed_read_raw_ref, + + packed_reflog_iterator_begin, + packed_for_each_reflog_ent, + packed_for_each_reflog_ent_reverse, + packed_reflog_exists, + packed_create_reflog, + packed_delete_reflog, + packed_reflog_expire +}; diff --git a/refs/packed-backend.h b/refs/packed-backend.h new file mode 100644 index 0000000000..640245d3b9 --- /dev/null +++ b/refs/packed-backend.h @@ -0,0 +1,35 @@ +#ifndef REFS_PACKED_BACKEND_H +#define REFS_PACKED_BACKEND_H + +/* + * Support for storing references in a `packed-refs` file. + * + * Note that this backend doesn't check for D/F conflicts, because it + * doesn't care about them. But usually it should be wrapped in a + * `files_ref_store` that prevents D/F conflicts from being created, + * even among packed refs. + */ + +struct ref_store *packed_ref_store_create(const char *path, + unsigned int store_flags); + +/* + * Lock the packed-refs file for writing. Flags is passed to + * hold_lock_file_for_update(). Return 0 on success. On errors, write + * an error message to `err` and return a nonzero value. + */ +int packed_refs_lock(struct ref_store *ref_store, int flags, struct strbuf *err); + +void packed_refs_unlock(struct ref_store *ref_store); +int packed_refs_is_locked(struct ref_store *ref_store); + +/* + * Return true if `transaction` really needs to be carried out against + * the specified packed_ref_store, or false if it can be skipped + * (i.e., because it is an obvious NOOP). `ref_store` must be locked + * before calling this function. + */ +int is_packed_transaction_needed(struct ref_store *ref_store, + struct ref_transaction *transaction); + +#endif /* REFS_PACKED_BACKEND_H */ diff --git a/refs/ref-cache.c b/refs/ref-cache.c new file mode 100644 index 0000000000..e90bd3e727 --- /dev/null +++ b/refs/ref-cache.c @@ -0,0 +1,551 @@ +#include "../cache.h" +#include "../refs.h" +#include "refs-internal.h" +#include "ref-cache.h" +#include "../iterator.h" + +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; +} + +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) { + if (!dir->cache->fill_ref_dir) + die("BUG: incomplete ref_store without fill_ref_dir function"); + + dir->cache->fill_ref_dir(dir->cache->ref_store, dir, entry->name); + entry->flag &= ~REF_INCOMPLETE; + } + return dir; +} + +struct ref_entry *create_ref_entry(const char *refname, + const struct object_id *oid, int flag) +{ + struct ref_entry *ref; + + FLEX_ALLOC_STR(ref, name, refname); + oidcpy(&ref->u.value.oid, oid); + ref->flag = flag; + return ref; +} + +struct ref_cache *create_ref_cache(struct ref_store *refs, + fill_ref_dir_fn *fill_ref_dir) +{ + struct ref_cache *ret = xcalloc(1, sizeof(*ret)); + + ret->ref_store = refs; + ret->fill_ref_dir = fill_ref_dir; + ret->root = create_dir_entry(ret, "", 0, 1); + return ret; +} + +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); +} + +void free_ref_cache(struct ref_cache *cache) +{ + free_ref_entry(cache->root); + free(cache); +} + +/* + * 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_AND_NULL(dir->entries); + dir->sorted = dir->nr = dir->alloc = 0; +} + +struct ref_entry *create_dir_entry(struct ref_cache *cache, + const char *dirname, size_t len, + int incomplete) +{ + struct ref_entry *direntry; + + FLEX_ALLOC_MEM(direntry, name, dirname, len); + direntry->u.subdir.cache = cache; + 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]; +} + +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->cache, 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., it 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; +} + +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; +} + +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]; + + MOVE_ARRAY(&dir->entries[entry_index], + &dir->entries[entry_index + 1], dir->nr - entry_index - 1); + dir->nr--; + if (dir->sorted > entry_index) + dir->sorted--; + free_ref_entry(entry); + return dir->nr; +} + +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 oid. Die if they have the same name but different + * oids. + */ +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; +} + +enum prefix_state { + /* All refs within the directory would match prefix: */ + PREFIX_CONTAINS_DIR, + + /* Some, but not all, refs within the directory might match prefix: */ + PREFIX_WITHIN_DIR, + + /* No refs within the directory could possibly match prefix: */ + PREFIX_EXCLUDES_DIR +}; + +/* + * Return a `prefix_state` constant describing the relationship + * between the directory with the specified `dirname` and `prefix`. + */ +static enum prefix_state overlaps_prefix(const char *dirname, + const char *prefix) +{ + while (*prefix && *dirname == *prefix) { + dirname++; + prefix++; + } + if (!*prefix) + return PREFIX_CONTAINS_DIR; + else if (!*dirname) + return PREFIX_WITHIN_DIR; + else + return PREFIX_EXCLUDES_DIR; +} + +/* + * Load all of the refs from `dir` (recursively) that could possibly + * contain references matching `prefix` into our in-memory cache. If + * `prefix` is NULL, prime unconditionally. + */ +static void prime_ref_dir(struct ref_dir *dir, const char *prefix) +{ + /* + * 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. + */ + int i; + for (i = 0; i < dir->nr; i++) { + struct ref_entry *entry = dir->entries[i]; + if (!(entry->flag & REF_DIR)) { + /* Not a directory; no need to recurse. */ + } else if (!prefix) { + /* Recurse in any case: */ + prime_ref_dir(get_ref_dir(entry), NULL); + } else { + switch (overlaps_prefix(entry->name, prefix)) { + case PREFIX_CONTAINS_DIR: + /* + * Recurse, and from here down we + * don't have to check the prefix + * anymore: + */ + prime_ref_dir(get_ref_dir(entry), NULL); + break; + case PREFIX_WITHIN_DIR: + prime_ref_dir(get_ref_dir(entry), prefix); + break; + case PREFIX_EXCLUDES_DIR: + /* No need to prime this directory. */ + break; + } + } + } +} + +/* + * 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; + + enum prefix_state prefix_state; + + /* + * 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; + + /* + * Only include references with this prefix in the iteration. + * The prefix is matched textually, without regard for path + * component boundaries. + */ + const char *prefix; + + /* + * 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; + enum prefix_state entry_prefix_state; + + 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 (level->prefix_state == PREFIX_WITHIN_DIR) { + entry_prefix_state = overlaps_prefix(entry->name, iter->prefix); + if (entry_prefix_state == PREFIX_EXCLUDES_DIR) + continue; + } else { + entry_prefix_state = level->prefix_state; + } + + 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->prefix_state = entry_prefix_state; + 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 int cache_ref_iterator_peel(struct ref_iterator *ref_iterator, + struct object_id *peeled) +{ + return peel_object(ref_iterator->oid, peeled); +} + +static int cache_ref_iterator_abort(struct ref_iterator *ref_iterator) +{ + struct cache_ref_iterator *iter = + (struct cache_ref_iterator *)ref_iterator; + + free((char *)iter->prefix); + 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 +}; + +struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache, + const char *prefix, + int prime_dir) +{ + struct ref_dir *dir; + struct cache_ref_iterator *iter; + struct ref_iterator *ref_iterator; + struct cache_ref_iterator_level *level; + + dir = get_ref_dir(cache->root); + if (prefix && *prefix) + dir = find_containing_dir(dir, prefix, 0); + if (!dir) + /* There's nothing to iterate over. */ + return empty_ref_iterator_begin(); + + if (prime_dir) + prime_ref_dir(dir, prefix); + + iter = xcalloc(1, sizeof(*iter)); + ref_iterator = &iter->base; + base_ref_iterator_init(ref_iterator, &cache_ref_iterator_vtable, 1); + ALLOC_GROW(iter->levels, 10, iter->levels_alloc); + + iter->levels_nr = 1; + level = &iter->levels[0]; + level->index = -1; + level->dir = dir; + + if (prefix && *prefix) { + iter->prefix = xstrdup(prefix); + level->prefix_state = PREFIX_WITHIN_DIR; + } else { + level->prefix_state = PREFIX_CONTAINS_DIR; + } + + return ref_iterator; +} diff --git a/refs/ref-cache.h b/refs/ref-cache.h new file mode 100644 index 0000000000..eda65e73ed --- /dev/null +++ b/refs/ref-cache.h @@ -0,0 +1,240 @@ +#ifndef REFS_REF_CACHE_H +#define REFS_REF_CACHE_H + +struct ref_dir; + +/* + * If this ref_cache is filled lazily, this function is used to load + * information into the specified ref_dir (shallow or deep, at the + * option of the ref_store). dirname includes a trailing slash. + */ +typedef void fill_ref_dir_fn(struct ref_store *ref_store, + struct ref_dir *dir, const char *dirname); + +struct ref_cache { + struct ref_entry *root; + + /* A pointer to the ref_store whose cache this is: */ + struct ref_store *ref_store; + + /* + * Function used (if necessary) to lazily-fill cache. May be + * NULL. + */ + fill_ref_dir_fn *fill_ref_dir; +}; + +/* + * 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; +}; + +/* + * 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; + + /* The ref_cache containing this entry: */ + struct ref_cache *cache; + + 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. + */ + +/* ref_entry represents a directory of references */ +#define REF_DIR 0x10 + +/* + * Entry has not yet been read from disk (used only for REF_DIR + * entries representing loose references) + */ +#define REF_INCOMPLETE 0x20 + +/* + * 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 refs_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]; +}; + +/* + * 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. + */ +int search_ref_dir(struct ref_dir *dir, const char *refname, size_t len); + +struct ref_dir *get_ref_dir(struct ref_entry *entry); + +/* + * 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. + */ +struct ref_entry *create_dir_entry(struct ref_cache *cache, + const char *dirname, size_t len, + int incomplete); + +struct ref_entry *create_ref_entry(const char *refname, + const struct object_id *oid, int flag); + +/* + * Return a pointer to a new `ref_cache`. Its top-level starts out + * marked incomplete. If `fill_ref_dir` is non-NULL, it is the + * function called to fill in incomplete directories in the + * `ref_cache` when they are accessed. If it is NULL, then the whole + * `ref_cache` must be filled (including clearing its directories' + * `REF_INCOMPLETE` bits) before it is used, and `refs` can be NULL, + * too. + */ +struct ref_cache *create_ref_cache(struct ref_store *refs, + fill_ref_dir_fn *fill_ref_dir); + +/* + * Free the `ref_cache` and all of its associated data. + */ +void free_ref_cache(struct ref_cache *cache); + +/* + * Add a ref_entry to the end of dir (unsorted). Entry is always + * stored directly in dir; no recursion into subdirectories is + * done. + */ +void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *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. + */ +int remove_entry_from_dir(struct ref_dir *dir, const char *refname); + +/* + * 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. + */ +int add_ref_entry(struct ref_dir *dir, struct ref_entry *ref); + +/* + * 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. + */ +struct ref_entry *find_ref_entry(struct ref_dir *dir, const char *refname); + +/* + * Start iterating over references in `cache`. If `prefix` is + * specified, only include references whose names start with that + * prefix. If `prime_dir` is true, then fill any incomplete + * directories before beginning the iteration. The output is ordered + * by refname. + */ +struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache, + const char *prefix, + int prime_dir); + +#endif /* REFS_REF_CACHE_H */ diff --git a/refs/refs-internal.h b/refs/refs-internal.h index c7dded35f4..dd834314bd 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -8,39 +8,28 @@ */ /* - * Flag passed to lock_ref_sha1_basic() telling it to tolerate broken - * refs (i.e., because the reference is about to be deleted anyway). + * The following flags can appear in `ref_update::flags`. Their + * numerical values must not conflict with those of REF_NO_DEREF and + * REF_FORCE_CREATE_REFLOG, which are also stored in + * `ref_update::flags`. */ -#define REF_DELETING 0x02 /* - * Used as a flag in ref_update::flags when a loose ref is being - * pruned. + * The reference should be updated to new_oid. */ -#define REF_ISPRUNING 0x04 +#define REF_HAVE_NEW (1 << 2) /* - * Used as a flag in ref_update::flags when the reference should be - * updated to new_sha1. + * The current reference's value should be checked to make sure that + * it agrees with old_oid. */ -#define REF_HAVE_NEW 0x08 +#define REF_HAVE_OLD (1 << 3) /* - * Used as a flag in ref_update::flags when old_sha1 should be - * checked. - */ -#define REF_HAVE_OLD 0x10 - -/* - * Used as a flag in ref_update::flags when the lockfile needs to be - * committed. - */ -#define REF_NEEDS_COMMIT 0x20 - -/* - * 0x40 is REF_FORCE_CREATE_REFLOG, so skip it if you're adding a - * value to ref_update::flags + * Return the length of time to retry acquiring a loose reference lock + * before giving up, in milliseconds: */ +long get_files_ref_lock_timeout_ms(void); /* * Return true iff refname is minimally safe. "Safe" here means that @@ -49,14 +38,24 @@ * This function does not check that the reference name is legal; for * that, use check_refname_format(). * - * We consider a refname that starts with "refs/" to be safe as long - * as any ".." components that it might contain do not escape "refs/". - * Names that do not start with "refs/" are considered safe iff they - * consist entirely of upper case characters and '_' (like "HEAD" and - * "MERGE_HEAD" but not "config" or "FOO/BAR"). + * A refname that starts with "refs/" is considered safe iff it + * doesn't contain any "." or ".." components or consecutive '/' + * characters, end with '/', or (on Windows) contain any '\' + * characters. Names that do not start with "refs/" are considered + * safe iff they consist entirely of upper case characters and '_' + * (like "HEAD" and "MERGE_HEAD" but not "config" or "FOO/BAR"). */ int refname_is_safe(const char *refname); +/* + * Helper function: 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 does not exist, emit a warning and return false. + */ +int ref_resolves_to_object(const char *refname, + const struct object_id *oid, + unsigned int flags); + enum peel_status { /* object was peeled successfully: */ PEEL_PEELED = 0, @@ -85,33 +84,11 @@ enum peel_status { /* * Peel the named object; i.e., if the object is a tag, resolve the * tag recursively until a non-tag is found. If successful, store the - * result to sha1 and return PEEL_PEELED. If the object is not a tag + * result to oid and return PEEL_PEELED. If the object is not a tag * or is not valid, return PEEL_NON_TAG or PEEL_INVALID, respectively, - * and leave sha1 unchanged. - */ -enum peel_status peel_object(const unsigned char *name, unsigned char *sha1); - -/* - * Return 0 if a reference named refname could be created without - * conflicting with the name of an existing reference. Otherwise, - * return a negative value and write an explanation to err. If extras - * is non-NULL, it is a list of additional refnames with which refname - * is not allowed to conflict. If skip is non-NULL, ignore potential - * conflicts with refs in skip (e.g., because they are scheduled for - * deletion in the same operation). Behavior is undefined if the same - * name is listed in both extras and skip. - * - * Two reference names conflict if one of them exactly matches the - * leading components of the other; e.g., "foo/bar" conflicts with - * both "foo" and with "foo/bar/baz" but not with "foo/bar" or - * "foo/barbados". - * - * extras and skip must be sorted. + * and leave oid unchanged. */ -int verify_refname_available(const char *newname, - struct string_list *extras, - struct string_list *skip, - struct strbuf *err); +enum peel_status peel_object(const struct object_id *name, struct object_id *oid); /* * Copy the reflog message msg to buf, which has been allocated sufficiently @@ -120,49 +97,101 @@ int verify_refname_available(const char *newname, */ int copy_reflog_msg(char *buf, const char *msg); -int should_autocreate_reflog(const char *refname); - /** - * Information needed for a single ref update. Set new_sha1 to the new - * value or to null_sha1 to delete the ref. To check the old value - * while the ref is locked, set (flags & REF_HAVE_OLD) and set - * old_sha1 to the old value, or to null_sha1 to ensure the ref does - * not exist before update. + * Information needed for a single ref update. Set new_oid to the new + * value or to null_oid to delete the ref. To check the old value + * while the ref is locked, set (flags & REF_HAVE_OLD) and set old_oid + * to the old value, or to null_oid to ensure the ref does not exist + * before update. */ struct ref_update { /* - * If (flags & REF_HAVE_NEW), set the reference to this value: + * If (flags & REF_HAVE_NEW), set the reference to this value + * (or delete it, if `new_oid` is `null_oid`). */ - unsigned char new_sha1[20]; + struct object_id new_oid; + /* * If (flags & REF_HAVE_OLD), check that the reference - * previously had this value: + * previously had this value (or didn't previously exist, if + * `old_oid` is `null_oid`). */ - unsigned char old_sha1[20]; + struct object_id old_oid; + /* - * One or more of REF_HAVE_NEW, REF_HAVE_OLD, REF_NODEREF, - * REF_DELETING, and REF_ISPRUNING: + * One or more of REF_NO_DEREF, REF_FORCE_CREATE_REFLOG, + * REF_HAVE_NEW, REF_HAVE_OLD, or backend-specific flags. */ unsigned int flags; - struct ref_lock *lock; - int type; + + void *backend_data; + unsigned int type; char *msg; + + /* + * If this ref_update was split off of a symref update via + * split_symref_update(), then this member points at that + * update. This is used for two purposes: + * 1. When reporting errors, we report the refname under which + * the update was originally requested. + * 2. When we read the old value of this reference, we + * propagate it back to its parent update for recording in + * the latter's reflog. + */ + struct ref_update *parent_update; + const char refname[FLEX_ARRAY]; }; +int refs_read_raw_ref(struct ref_store *ref_store, + const char *refname, struct object_id *oid, + struct strbuf *referent, unsigned int *type); + +/* + * Write an error to `err` and return a nonzero value iff the same + * refname appears multiple times in `refnames`. `refnames` must be + * sorted on entry to this function. + */ +int ref_update_reject_duplicates(struct string_list *refnames, + struct strbuf *err); + +/* + * Add a ref_update with the specified properties to transaction, and + * return a pointer to the new object. This function does not verify + * that refname is well-formed. new_oid and old_oid are only + * dereferenced if the REF_HAVE_NEW and REF_HAVE_OLD bits, + * respectively, are set in flags. + */ +struct ref_update *ref_transaction_add_update( + struct ref_transaction *transaction, + const char *refname, unsigned int flags, + const struct object_id *new_oid, + const struct object_id *old_oid, + const char *msg); + /* * Transaction states. - * OPEN: The transaction is in a valid state and can accept new updates. - * An OPEN transaction can be committed. - * CLOSED: A closed transaction is no longer active and no other operations - * than free can be used on it in this state. - * A transaction can either become closed by successfully committing - * an active transaction or if there is a failure while building - * the transaction thus rendering it failed/inactive. + * + * OPEN: The transaction is initialized and new updates can still be + * added to it. An OPEN transaction can be prepared, + * committed, freed, or aborted (freeing and aborting an open + * transaction are equivalent). + * + * PREPARED: ref_transaction_prepare(), which locks all of the + * references involved in the update and checks that the + * update has no errors, has been called successfully for the + * transaction. A PREPARED transaction can be committed or + * aborted. + * + * CLOSED: The transaction is no longer active. A transaction becomes + * CLOSED if there is a failure while building the transaction + * or if a transaction is committed or aborted. A CLOSED + * transaction can only be freed. */ enum ref_transaction_state { - REF_TRANSACTION_OPEN = 0, - REF_TRANSACTION_CLOSED = 1 + REF_TRANSACTION_OPEN = 0, + REF_TRANSACTION_PREPARED = 1, + REF_TRANSACTION_CLOSED = 2 }; /* @@ -171,16 +200,14 @@ enum ref_transaction_state { * as atomically as possible. This structure is opaque to callers. */ struct ref_transaction { + struct ref_store *ref_store; struct ref_update **updates; size_t alloc; size_t nr; enum ref_transaction_state state; + void *backend_data; }; -int files_log_ref_write(const char *refname, const unsigned char *old_sha1, - const unsigned char *new_sha1, const char *msg, - int flags, struct strbuf *err); - /* * Check for entries in extras that are within the specified * directory, where dirname is a reference directory name including @@ -195,6 +222,451 @@ const char *find_descendant_ref(const char *dirname, const struct string_list *extras, const struct string_list *skip); -int rename_ref_available(const char *oldname, const char *newname); +/* + * Check whether an attempt to rename old_refname to new_refname would + * cause a D/F conflict with any existing reference (other than + * possibly old_refname). If there would be a conflict, emit an error + * message and return false; otherwise, return true. + * + * Note that this function is not safe against all races with other + * processes (though rename_ref() catches some races that might get by + * this check). + */ +int refs_rename_ref_available(struct ref_store *refs, + const char *old_refname, + const char *new_refname); + +/* We allow "recursive" symbolic refs. Only within reason, though */ +#define SYMREF_MAXDEPTH 5 + +/* Include broken references in a do_for_each_ref*() iteration: */ +#define DO_FOR_EACH_INCLUDE_BROKEN 0x01 + +/* + * Reference iterators + * + * A reference iterator encapsulates the state of an in-progress + * iteration over references. Create an instance of `struct + * ref_iterator` via one of the functions in this module. + * + * A freshly-created ref_iterator doesn't yet point at a reference. To + * advance the iterator, call ref_iterator_advance(). If successful, + * this sets the iterator's refname, oid, and flags fields to describe + * the next reference and returns ITER_OK. The data pointed at by + * refname and oid belong to the iterator; if you want to retain them + * after calling ref_iterator_advance() again or calling + * ref_iterator_abort(), you must make a copy. When the iteration has + * been exhausted, ref_iterator_advance() releases any resources + * assocated with the iteration, frees the ref_iterator object, and + * returns ITER_DONE. If you want to abort the iteration early, call + * ref_iterator_abort(), which also frees the ref_iterator object and + * any associated resources. If there was an internal error advancing + * to the next entry, ref_iterator_advance() aborts the iteration, + * frees the ref_iterator, and returns ITER_ERROR. + * + * The reference currently being looked at can be peeled by calling + * ref_iterator_peel(). This function is often faster than peel_ref(), + * so it should be preferred when iterating over references. + * + * Putting it all together, a typical iteration looks like this: + * + * int ok; + * struct ref_iterator *iter = ...; + * + * while ((ok = ref_iterator_advance(iter)) == ITER_OK) { + * if (want_to_stop_iteration()) { + * ok = ref_iterator_abort(iter); + * break; + * } + * + * // Access information about the current reference: + * if (!(iter->flags & REF_ISSYMREF)) + * printf("%s is %s\n", iter->refname, oid_to_hex(&iter->oid)); + * + * // If you need to peel the reference: + * ref_iterator_peel(iter, &oid); + * } + * + * if (ok != ITER_DONE) + * handle_error(); + */ +struct ref_iterator { + struct ref_iterator_vtable *vtable; + + /* + * Does this `ref_iterator` iterate over references in order + * by refname? + */ + unsigned int ordered : 1; + + const char *refname; + const struct object_id *oid; + unsigned int flags; +}; + +/* + * Advance the iterator to the first or next item and return ITER_OK. + * If the iteration is exhausted, free the resources associated with + * the ref_iterator and return ITER_DONE. On errors, free the iterator + * resources and return ITER_ERROR. It is a bug to use ref_iterator or + * call this function again after it has returned ITER_DONE or + * ITER_ERROR. + */ +int ref_iterator_advance(struct ref_iterator *ref_iterator); + +/* + * If possible, peel the reference currently being viewed by the + * iterator. Return 0 on success. + */ +int ref_iterator_peel(struct ref_iterator *ref_iterator, + struct object_id *peeled); + +/* + * End the iteration before it has been exhausted, freeing the + * reference iterator and any associated resources and returning + * ITER_DONE. If the abort itself failed, return ITER_ERROR. + */ +int ref_iterator_abort(struct ref_iterator *ref_iterator); + +/* + * An iterator over nothing (its first ref_iterator_advance() call + * returns ITER_DONE). + */ +struct ref_iterator *empty_ref_iterator_begin(void); + +/* + * Return true iff ref_iterator is an empty_ref_iterator. + */ +int is_empty_ref_iterator(struct ref_iterator *ref_iterator); + +/* + * Return an iterator that goes over each reference in `refs` for + * which the refname begins with prefix. If trim is non-zero, then + * trim that many characters off the beginning of each refname. flags + * can be DO_FOR_EACH_INCLUDE_BROKEN to include broken references in + * the iteration. The output is ordered by refname. + */ +struct ref_iterator *refs_ref_iterator_begin( + struct ref_store *refs, + const char *prefix, int trim, int flags); + +/* + * A callback function used to instruct merge_ref_iterator how to + * interleave the entries from iter0 and iter1. The function should + * return one of the constants defined in enum iterator_selection. It + * must not advance either of the iterators itself. + * + * The function must be prepared to handle the case that iter0 and/or + * iter1 is NULL, which indicates that the corresponding sub-iterator + * has been exhausted. Its return value must be consistent with the + * current states of the iterators; e.g., it must not return + * ITER_SKIP_1 if iter1 has already been exhausted. + */ +typedef enum iterator_selection ref_iterator_select_fn( + struct ref_iterator *iter0, struct ref_iterator *iter1, + void *cb_data); + +/* + * Iterate over the entries from iter0 and iter1, with the values + * interleaved as directed by the select function. The iterator takes + * ownership of iter0 and iter1 and frees them when the iteration is + * over. A derived class should set `ordered` to 1 or 0 based on + * whether it generates its output in order by reference name. + */ +struct ref_iterator *merge_ref_iterator_begin( + int ordered, + struct ref_iterator *iter0, struct ref_iterator *iter1, + ref_iterator_select_fn *select, void *cb_data); + +/* + * An iterator consisting of the union of the entries from front and + * back. If there are entries common to the two sub-iterators, use the + * one from front. Each iterator must iterate over its entries in + * strcmp() order by refname for this to work. + * + * The new iterator takes ownership of its arguments and frees them + * when the iteration is over. As a convenience to callers, if front + * or back is an empty_ref_iterator, then abort that one immediately + * and return the other iterator directly, without wrapping it. + */ +struct ref_iterator *overlay_ref_iterator_begin( + struct ref_iterator *front, struct ref_iterator *back); + +/* + * Wrap iter0, only letting through the references whose names start + * with prefix. If trim is set, set iter->refname to the name of the + * reference with that many characters trimmed off the front; + * otherwise set it to the full refname. The new iterator takes over + * ownership of iter0 and frees it when iteration is over. It makes + * its own copy of prefix. + * + * As an convenience to callers, if prefix is the empty string and + * trim is zero, this function returns iter0 directly, without + * wrapping it. + * + * The resulting ref_iterator is ordered if iter0 is. + */ +struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0, + const char *prefix, + int trim); + +/* Internal implementation of reference iteration: */ + +/* + * Base class constructor for ref_iterators. Initialize the + * ref_iterator part of iter, setting its vtable pointer as specified. + * `ordered` should be set to 1 if the iterator will iterate over + * references in order by refname; otherwise it should be set to 0. + * This is meant to be called only by the initializers of derived + * classes. + */ +void base_ref_iterator_init(struct ref_iterator *iter, + struct ref_iterator_vtable *vtable, + int ordered); + +/* + * Base class destructor for ref_iterators. Destroy the ref_iterator + * part of iter and shallow-free the object. This is meant to be + * called only by the destructors of derived classes. + */ +void base_ref_iterator_free(struct ref_iterator *iter); + +/* Virtual function declarations for ref_iterators: */ + +typedef int ref_iterator_advance_fn(struct ref_iterator *ref_iterator); + +typedef int ref_iterator_peel_fn(struct ref_iterator *ref_iterator, + struct object_id *peeled); + +/* + * Implementations of this function should free any resources specific + * to the derived class, then call base_ref_iterator_free() to clean + * up and free the ref_iterator object. + */ +typedef int ref_iterator_abort_fn(struct ref_iterator *ref_iterator); + +struct ref_iterator_vtable { + ref_iterator_advance_fn *advance; + ref_iterator_peel_fn *peel; + ref_iterator_abort_fn *abort; +}; + +/* + * current_ref_iter is a performance hack: when iterating over + * references using the for_each_ref*() functions, current_ref_iter is + * set to the reference iterator before calling the callback function. + * If the callback function calls peel_ref(), then peel_ref() first + * checks whether the reference to be peeled is the one referred to by + * the iterator (it usually is) and if so, asks the iterator for the + * peeled version of the reference if it is available. This avoids a + * refname lookup in a common case. current_ref_iter is set to NULL + * when the iteration is over. + */ +extern struct ref_iterator *current_ref_iter; + +/* + * The common backend for the for_each_*ref* functions. Call fn for + * each reference in iter. If the iterator itself ever returns + * ITER_ERROR, return -1. If fn ever returns a non-zero value, stop + * the iteration and return that value. Otherwise, return 0. In any + * case, free the iterator when done. This function is basically an + * adapter between the callback style of reference iteration and the + * iterator style. + */ +int do_for_each_ref_iterator(struct ref_iterator *iter, + each_ref_fn fn, void *cb_data); + +/* + * Only include per-worktree refs in a do_for_each_ref*() iteration. + * Normally this will be used with a files ref_store, since that's + * where all reference backends will presumably store their + * per-worktree refs. + */ +#define DO_FOR_EACH_PER_WORKTREE_ONLY 0x02 + +struct ref_store; + +/* refs backends */ + +/* ref_store_init flags */ +#define REF_STORE_READ (1 << 0) +#define REF_STORE_WRITE (1 << 1) /* can perform update operations */ +#define REF_STORE_ODB (1 << 2) /* has access to object database */ +#define REF_STORE_MAIN (1 << 3) +#define REF_STORE_ALL_CAPS (REF_STORE_READ | \ + REF_STORE_WRITE | \ + REF_STORE_ODB | \ + REF_STORE_MAIN) + +/* + * Initialize the ref_store for the specified gitdir. These functions + * should call base_ref_store_init() to initialize the shared part of + * the ref_store and to record the ref_store for later lookup. + */ +typedef struct ref_store *ref_store_init_fn(const char *gitdir, + unsigned int flags); + +typedef int ref_init_db_fn(struct ref_store *refs, struct strbuf *err); + +typedef int ref_transaction_prepare_fn(struct ref_store *refs, + struct ref_transaction *transaction, + struct strbuf *err); + +typedef int ref_transaction_finish_fn(struct ref_store *refs, + struct ref_transaction *transaction, + struct strbuf *err); + +typedef int ref_transaction_abort_fn(struct ref_store *refs, + struct ref_transaction *transaction, + struct strbuf *err); + +typedef int ref_transaction_commit_fn(struct ref_store *refs, + struct ref_transaction *transaction, + struct strbuf *err); + +typedef int pack_refs_fn(struct ref_store *ref_store, unsigned int flags); +typedef int create_symref_fn(struct ref_store *ref_store, + const char *ref_target, + const char *refs_heads_master, + const char *logmsg); +typedef int delete_refs_fn(struct ref_store *ref_store, const char *msg, + struct string_list *refnames, unsigned int flags); +typedef int rename_ref_fn(struct ref_store *ref_store, + const char *oldref, const char *newref, + const char *logmsg); +typedef int copy_ref_fn(struct ref_store *ref_store, + const char *oldref, const char *newref, + const char *logmsg); + +/* + * Iterate over the references in `ref_store` whose names start with + * `prefix`. `prefix` is matched as a literal string, without regard + * for path separators. If prefix is NULL or the empty string, iterate + * over all references in `ref_store`. The output is ordered by + * refname. + */ +typedef struct ref_iterator *ref_iterator_begin_fn( + struct ref_store *ref_store, + const char *prefix, unsigned int flags); + +/* reflog functions */ + +/* + * Iterate over the references in the specified ref_store that have a + * reflog. The refs are iterated over in arbitrary order. + */ +typedef struct ref_iterator *reflog_iterator_begin_fn( + struct ref_store *ref_store); + +typedef int for_each_reflog_ent_fn(struct ref_store *ref_store, + const char *refname, + each_reflog_ent_fn fn, + void *cb_data); +typedef int for_each_reflog_ent_reverse_fn(struct ref_store *ref_store, + const char *refname, + each_reflog_ent_fn fn, + void *cb_data); +typedef int reflog_exists_fn(struct ref_store *ref_store, const char *refname); +typedef int create_reflog_fn(struct ref_store *ref_store, const char *refname, + int force_create, struct strbuf *err); +typedef int delete_reflog_fn(struct ref_store *ref_store, const char *refname); +typedef int reflog_expire_fn(struct ref_store *ref_store, + const char *refname, const struct object_id *oid, + unsigned int flags, + reflog_expiry_prepare_fn prepare_fn, + reflog_expiry_should_prune_fn should_prune_fn, + reflog_expiry_cleanup_fn cleanup_fn, + void *policy_cb_data); + +/* + * Read a reference from the specified reference store, non-recursively. + * Set type to describe the reference, and: + * + * - If refname is the name of a normal reference, fill in oid + * (leaving referent unchanged). + * + * - If refname is the name of a symbolic reference, write the full + * name of the reference to which it refers (e.g. + * "refs/heads/master") to referent and set the REF_ISSYMREF bit in + * type (leaving oid unchanged). The caller is responsible for + * validating that referent is a valid reference name. + * + * WARNING: refname might be used as part of a filename, so it is + * important from a security standpoint that it be safe in the sense + * of refname_is_safe(). Moreover, for symrefs this function sets + * referent to whatever the repository says, which might not be a + * properly-formatted or even safe reference name. NEITHER INPUT NOR + * OUTPUT REFERENCE NAMES ARE VALIDATED WITHIN THIS FUNCTION. + * + * Return 0 on success. If the ref doesn't exist, set errno to ENOENT + * and return -1. If the ref exists but is neither a symbolic ref nor + * an object ID, it is broken; set REF_ISBROKEN in type, set errno to + * EINVAL, and return -1. If there is another error reading the ref, + * set errno appropriately and return -1. + * + * Backend-specific flags might be set in type as well, regardless of + * outcome. + * + * It is OK for refname to point into referent. If so: + * + * - if the function succeeds with REF_ISSYMREF, referent will be + * overwritten and the memory formerly pointed to by it might be + * changed or even freed. + * + * - in all other cases, referent will be untouched, and therefore + * refname will still be valid and unchanged. + */ +typedef int read_raw_ref_fn(struct ref_store *ref_store, + const char *refname, struct object_id *oid, + struct strbuf *referent, unsigned int *type); + +struct ref_storage_be { + struct ref_storage_be *next; + const char *name; + ref_store_init_fn *init; + ref_init_db_fn *init_db; + + ref_transaction_prepare_fn *transaction_prepare; + ref_transaction_finish_fn *transaction_finish; + ref_transaction_abort_fn *transaction_abort; + ref_transaction_commit_fn *initial_transaction_commit; + + pack_refs_fn *pack_refs; + create_symref_fn *create_symref; + delete_refs_fn *delete_refs; + rename_ref_fn *rename_ref; + copy_ref_fn *copy_ref; + + ref_iterator_begin_fn *iterator_begin; + read_raw_ref_fn *read_raw_ref; + + reflog_iterator_begin_fn *reflog_iterator_begin; + for_each_reflog_ent_fn *for_each_reflog_ent; + for_each_reflog_ent_reverse_fn *for_each_reflog_ent_reverse; + reflog_exists_fn *reflog_exists; + create_reflog_fn *create_reflog; + delete_reflog_fn *delete_reflog; + reflog_expire_fn *reflog_expire; +}; + +extern struct ref_storage_be refs_be_files; +extern struct ref_storage_be refs_be_packed; + +/* + * A representation of the reference store for the main repository or + * a submodule. The ref_store instances for submodules are kept in a + * linked list. + */ +struct ref_store { + /* The backend describing this ref_store's storage scheme: */ + const struct ref_storage_be *be; +}; + +/* + * Fill in the generic part of refs and add it to our collection of + * reference stores. + */ +void base_ref_store_init(struct ref_store *refs, + const struct ref_storage_be *be); #endif /* REFS_REFS_INTERNAL_H */ |