diff options
Diffstat (limited to 'refs/files-backend.c')
-rw-r--r-- | refs/files-backend.c | 3531 |
1 files changed, 1291 insertions, 2240 deletions
diff --git a/refs/files-backend.c b/refs/files-backend.c index 2455564352..f75d960e19 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -1,970 +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; - 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(). + * 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`. */ -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. + * 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. */ +#define REF_IS_PRUNING (1 << 4) /* - * 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. + * Flag passed to lock_ref_sha1_basic() telling it to tolerate broken + * refs (i.e., because the reference is about to be deleted anyway). */ -#define REF_KNOWS_PEELED 0x10 - -/* ref_entry represents a directory of references */ -#define REF_DIR 0x20 +#define REF_DELETING (1 << 5) /* - * Entry has not yet been read from disk (used only for REF_DIR - * entries representing loose references) + * Used as a flag in ref_update::flags when the lockfile needs to be + * committed. */ -#define REF_INCOMPLETE 0x40 +#define REF_NEEDS_COMMIT (1 << 6) /* - * 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. + * 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. */ -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); -} +#define REF_LOG_ONLY (1 << 7) /* - * Add a ref_entry to the end of dir (unsorted). Entry is always - * stored directly in dir; no recursion into subdirectories is - * done. + * Used as a flag in ref_update::flags when the ref_update was via an + * update to HEAD. */ -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; -} +#define REF_UPDATE_VIA_HEAD (1 << 8) /* - * Clear and free all entries in dir, recursively. + * Used as a flag in ref_update::flags when the loose reference has + * been deleted. */ -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. - */ -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. - */ -static struct ref_dir *find_containing_dir(struct ref_dir *dir, - const char *refname, int mkdir) -{ - const char *slash; - for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) { - size_t dirnamelen = slash - refname + 1; - struct ref_dir *subdir; - subdir = search_for_subdir(dir, refname, dirnamelen, mkdir); - if (!subdir) { - dir = NULL; - break; - } - dir = subdir; - } - - return dir; -} - -/* - * Find the value entry with the given name in dir, sorting ref_dirs - * and recursing into subdirectories as necessary. If the name is not - * found or it corresponds to a directory entry, return NULL. - */ -static struct ref_entry *find_ref(struct ref_dir *dir, const char *refname) -{ - int entry_index; - struct ref_entry *entry; - dir = find_containing_dir(dir, refname, 0); - if (!dir) - return NULL; - entry_index = search_ref_dir(dir, refname, strlen(refname)); - if (entry_index == -1) - return NULL; - entry = dir->entries[entry_index]; - return (entry->flag & REF_DIR) ? NULL : entry; -} - -/* - * Remove the entry with the given name from dir, recursing into - * subdirectories as necessary. If refname is the name of a directory - * (i.e., ends with '/'), then remove the directory and its contents. - * If the removal was successful, return the number of entries - * remaining in the directory entry that contained the deleted entry. - * If the name was not found, return -1. Please note that this - * function only deletes the entry from the cache; it does not delete - * it from the filesystem or ensure that other cache entries (which - * might be symbolic references to the removed entry) are updated. - * Nor does it remove any containing dir entries that might be made - * empty by the removal. dir must represent the top-level directory - * and must already be complete. - */ -static int remove_entry(struct ref_dir *dir, const char *refname) -{ - int refname_len = strlen(refname); - int entry_index; - struct ref_entry *entry; - int is_dir = refname[refname_len - 1] == '/'; - if (is_dir) { - /* - * refname represents a reference directory. Remove - * the trailing slash; otherwise we will get the - * directory *representing* refname rather than the - * one *containing* it. - */ - char *dirname = xmemdupz(refname, refname_len - 1); - dir = find_containing_dir(dir, dirname, 0); - free(dirname); - } else { - dir = find_containing_dir(dir, refname, 0); - } - if (!dir) - return -1; - entry_index = search_ref_dir(dir, refname, refname_len); - if (entry_index == -1) - return -1; - entry = dir->entries[entry_index]; - - memmove(&dir->entries[entry_index], - &dir->entries[entry_index + 1], - (dir->nr - entry_index - 1) * sizeof(*dir->entries) - ); - dir->nr--; - if (dir->sorted > entry_index) - dir->sorted--; - free_ref_entry(entry); - return dir->nr; -} - -/* - * Add a ref_entry to the ref_dir (unsorted), recursing into - * subdirectories as necessary. dir must represent the top-level - * directory. Return 0 on success. - */ -static int add_ref(struct ref_dir *dir, struct ref_entry *ref) -{ - dir = find_containing_dir(dir, ref->name, 1); - if (!dir) - return -1; - add_entry_to_dir(dir, ref); - return 0; -} - -/* - * Emit a warning and return true iff ref1 and ref2 have the same name - * and the same sha1. Die if they have the same name but different - * sha1s. - */ -static int is_dup_ref(const struct ref_entry *ref1, const struct ref_entry *ref2) -{ - if (strcmp(ref1->name, ref2->name)) - return 0; - - /* Duplicate name; make sure that they don't conflict: */ - - if ((ref1->flag & REF_DIR) || (ref2->flag & REF_DIR)) - /* This is impossible by construction */ - die("Reference directory conflict: %s", ref1->name); - - if (oidcmp(&ref1->u.value.oid, &ref2->u.value.oid)) - die("Duplicated ref, and SHA1s don't match: %s", ref1->name); - - warning("Duplicated ref: %s", ref1->name); - return 1; -} - -/* - * Sort the entries in dir non-recursively (if they are not already - * sorted) and remove any duplicate entries. - */ -static void sort_ref_dir(struct ref_dir *dir) -{ - int i, j; - struct ref_entry *last = NULL; - - /* - * This check also prevents passing a zero-length array to qsort(), - * which is a problem on some platforms. - */ - if (dir->sorted == dir->nr) - return; - - qsort(dir->entries, dir->nr, 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; -} - -/* - * 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. - */ -static int ref_resolves_to_object(const char *refname, - const struct object_id *oid, - unsigned int flags) -{ - if (flags & REF_ISBROKEN) - return 0; - if (!has_sha1_file(oid->hash)) { - error("%s does not point to a valid object!", refname); - return 0; - } - return 1; -} - -/* - * Return true if the reference described by entry can be resolved to - * an object in the database; otherwise, emit a warning and return - * false. - */ -static int entry_resolves_to_object(struct ref_entry *entry) -{ - return ref_resolves_to_object(entry->name, - &entry->u.value.oid, entry->flag); -} - -typedef int each_ref_entry_fn(struct ref_entry *entry, void *cb_data); - -/* - * Call fn for each reference in dir that has index in the range - * offset <= index < dir->nr. Recurse into subdirectories that are in - * that index range, sorting them before iterating. This function - * does not sort dir itself; it should be sorted beforehand. fn is - * called for all references, including broken ones. - */ -static int do_for_each_entry_in_dir(struct ref_dir *dir, int offset, - each_ref_entry_fn fn, void *cb_data) -{ - int i; - assert(dir->sorted == dir->nr); - for (i = offset; i < dir->nr; i++) { - struct ref_entry *entry = dir->entries[i]; - int retval; - if (entry->flag & REF_DIR) { - struct ref_dir *subdir = get_ref_dir(entry); - sort_ref_dir(subdir); - retval = do_for_each_entry_in_dir(subdir, 0, fn, cb_data); - } else { - retval = fn(entry, cb_data); - } - if (retval) - return retval; - } - return 0; -} - -/* - * Load all of the refs from the dir into our in-memory cache. The hard work - * of loading loose refs is done by get_ref_dir(), so we just need to recurse - * through all of the sub-directories. We do not even need to care about - * sorting, as traversal order does not matter to us. - */ -static void prime_ref_dir(struct ref_dir *dir) -{ - int i; - for (i = 0; i < dir->nr; i++) { - struct ref_entry *entry = dir->entries[i]; - if (entry->flag & REF_DIR) - prime_ref_dir(get_ref_dir(entry)); - } -} - -/* - * A level in the reference hierarchy that is currently being iterated - * through. - */ -struct cache_ref_iterator_level { - /* - * The ref_dir being iterated over at this level. The ref_dir - * is sorted before being stored here. - */ - struct ref_dir *dir; - - /* - * The index of the current entry within dir (which might - * itself be a directory). If index == -1, then the iteration - * hasn't yet begun. If index == dir->nr, then the iteration - * through this level is over. - */ - int index; -}; - -/* - * Represent an iteration through a ref_dir in the memory cache. The - * iteration recurses through subdirectories. - */ -struct cache_ref_iterator { - struct ref_iterator base; - - /* - * The number of levels currently on the stack. This is always - * at least 1, because when it becomes zero the iteration is - * ended and this struct is freed. - */ - size_t levels_nr; - - /* The number of levels that have been allocated on the stack */ - size_t levels_alloc; - - /* - * A stack of levels. levels[0] is the uppermost level that is - * being iterated over in this iteration. (This is not - * necessary the top level in the references hierarchy. If we - * are iterating through a subtree, then levels[0] will hold - * the ref_dir for that subtree, and subsequent levels will go - * on from there.) - */ - struct cache_ref_iterator_level *levels; -}; - -static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator) -{ - struct cache_ref_iterator *iter = - (struct cache_ref_iterator *)ref_iterator; - - while (1) { - struct cache_ref_iterator_level *level = - &iter->levels[iter->levels_nr - 1]; - struct ref_dir *dir = level->dir; - struct ref_entry *entry; - - if (level->index == -1) - sort_ref_dir(dir); - - if (++level->index == level->dir->nr) { - /* This level is exhausted; pop up a level */ - if (--iter->levels_nr == 0) - return ref_iterator_abort(ref_iterator); - - continue; - } - - entry = dir->entries[level->index]; - - if (entry->flag & REF_DIR) { - /* push down a level */ - ALLOC_GROW(iter->levels, iter->levels_nr + 1, - iter->levels_alloc); - - level = &iter->levels[iter->levels_nr++]; - level->dir = get_ref_dir(entry); - level->index = -1; - } else { - iter->base.refname = entry->name; - iter->base.oid = &entry->u.value.oid; - iter->base.flags = entry->flag; - return ITER_OK; - } - } -} - -static enum peel_status peel_entry(struct ref_entry *entry, int repeel); - -static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator, - struct object_id *peeled) -{ - struct cache_ref_iterator *iter = - (struct cache_ref_iterator *)ref_iterator; - struct cache_ref_iterator_level *level; - struct ref_entry *entry; - - level = &iter->levels[iter->levels_nr - 1]; - - if (level->index == -1) - die("BUG: peel called before advance for cache iterator"); - - entry = level->dir->entries[level->index]; - - if (peel_entry(entry, 0)) - return -1; - hashcpy(peeled->hash, entry->u.value.peeled.hash); - return 0; -} - -static int cache_ref_iterator_abort(struct ref_iterator *ref_iterator) -{ - struct cache_ref_iterator *iter = - (struct cache_ref_iterator *)ref_iterator; - - free(iter->levels); - base_ref_iterator_free(ref_iterator); - return ITER_DONE; -} - -static struct ref_iterator_vtable cache_ref_iterator_vtable = { - cache_ref_iterator_advance, - cache_ref_iterator_peel, - cache_ref_iterator_abort -}; - -static struct ref_iterator *cache_ref_iterator_begin(struct ref_dir *dir) -{ - struct cache_ref_iterator *iter; - struct ref_iterator *ref_iterator; - struct cache_ref_iterator_level *level; - - iter = xcalloc(1, sizeof(*iter)); - ref_iterator = &iter->base; - base_ref_iterator_init(ref_iterator, &cache_ref_iterator_vtable); - ALLOC_GROW(iter->levels, 10, iter->levels_alloc); - - iter->levels_nr = 1; - level = &iter->levels[0]; - level->index = -1; - level->dir = dir; - - return ref_iterator; -} - -struct 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; +#define REF_DELETED_LOOSE (1 << 9) - 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; - -/* Lock used for the main packed-refs file: */ -static struct lock_file packlock; - -/* - * Increment the reference count of *packed_refs. - */ -static void acquire_packed_ref_cache(struct packed_ref_cache *packed_refs) -{ - packed_refs->referrers++; -} +struct files_ref_store { + struct ref_store base; + unsigned int store_flags; -/* - * 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; - } -} + char *gitdir; + char *gitcommondir; -static void clear_packed_ref_cache(struct ref_cache *refs) -{ - if (refs->packed) { - struct packed_ref_cache *packed_refs = refs->packed; + struct ref_cache *loose; - 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; } } @@ -973,242 +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; + + base_ref_store_init(ref_store, &refs_be_files); + refs->store_flags = flags; - if (!submodule || !*submodule) - return &ref_cache; + 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); - for (refs = submodule_ref_caches; refs; refs = refs->next) - if (!strcmp(submodule, refs->name)) - return refs; - return NULL; + return ref_store; } /* - * Return a pointer to a ref_cache for the specified submodule. For - * the main repository, use submodule==NULL; such a call cannot fail. - * For a submodule, the submodule must exist and be a nonbare - * repository, otherwise return NULL. - * - * The returned structure will be allocated and initialized but not - * necessarily populated; it should not be freed. + * Die if refs is not the main ref store. caller is used in any + * necessary error messages. */ -static struct ref_cache *get_ref_cache(const char *submodule) +static void files_assert_main_repository(struct files_ref_store *refs, + const char *caller) { - struct ref_cache *refs = lookup_ref_cache(submodule); - - if (!refs) { - struct strbuf submodule_sb = STRBUF_INIT; - - strbuf_addstr(&submodule_sb, submodule); - if (is_nonbare_repository_dir(&submodule_sb)) - refs = create_ref_cache(submodule); - strbuf_release(&submodule_sb); - } + if (refs->store_flags & REF_STORE_MAIN) + return; - return refs; + die("BUG: operation %s only allowed for main ref store", caller); } -/* 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. + * 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 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 struct files_ref_store *files_downcast(struct ref_store *ref_store, + unsigned int required_flags, + 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; + struct files_ref_store *refs; - ref = line->buf + 41; - if (isspace(*ref)) - return NULL; + if (ref_store->be != &refs_be_files) + die("BUG: ref_store is type \"%s\" not \"files\" in %s", + ref_store->be->name, caller); - if (line->buf[line->len - 1] != '\n') - return NULL; - line->buf[--line->len] = 0; + refs = (struct files_ref_store *)ref_store; - return ref; -} + 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); -/* - * 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). - */ -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; - } - } - - strbuf_release(&line); + return refs; } -/* - * 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) +static void files_reflog_path(struct files_ref_store *refs, + struct strbuf *sb, + const char *refname) { - char *packed_refs_file; - - 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); - } + 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); } - free(packed_refs_file); - return refs->packed; -} - -static struct ref_dir *get_packed_ref_dir(struct packed_ref_cache *packed_ref_cache) -{ - return get_ref_dir(packed_ref_cache->root); } -static struct ref_dir *get_packed_refs(struct ref_cache *refs) +static void files_ref_path(struct files_ref_store *refs, + struct strbuf *sb, + const char *refname) { - return get_packed_ref_dir(get_packed_ref_cache(refs)); -} - -/* - * 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) -{ - 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); + } } /* @@ -1216,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); @@ -1226,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); @@ -1242,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; @@ -1257,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 @@ -1292,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); @@ -1304,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) { /* @@ -1314,139 +290,32 @@ 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); - /* - * Create an incomplete entry for "refs/": - */ - add_entry_to_dir(get_ref_dir(refs->loose), - create_dir_entry(refs, "refs/", 5, 1)); - } - return get_ref_dir(refs->loose); -} - -#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 > SYMREF_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); -} - -int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1) -{ - 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 = get_ref_cache(submodule.buf); - if (!refs) { - strbuf_release(&submodule); - return -1; - } - strbuf_release(&submodule); - - retval = resolve_gitlink_ref_recursive(refs, refname, sha1, 0); - return retval; -} - -/* - * 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); -} + refs->loose = create_ref_cache(&refs->base, loose_fill_ref_dir); -/* - * A loose ref file doesn't exist; check for a packed ref. - */ -static int resolve_missing_loose_ref(const char *refname, - unsigned char *sha1, - unsigned int *flags) -{ - struct ref_entry *entry; + /* We're going to fill the top level ourselves: */ + refs->loose->root->flag &= ~REF_INCOMPLETE; - /* - * The loose reference file does not exist; check for a packed - * reference. - */ - entry = get_packed_ref(refname); - if (entry) { - hashcpy(sha1, entry->u.value.oid.hash); - *flags |= REF_ISPACKED; - return 0; + /* + * Add an incomplete entry for "refs/" (to be filled + * lazily): + */ + add_entry_to_dir(get_ref_dir(refs->loose->root), + create_dir_entry(refs->loose, "refs/", 5, 1)); } - /* refname is not a packed reference. */ - return -1; + return refs->loose; } -int read_raw_ref(const char *refname, unsigned char *sha1, - struct strbuf *referent, unsigned int *type) +static int files_read_raw_ref(struct ref_store *ref_store, + const char *refname, struct object_id *oid, + struct strbuf *referent, unsigned int *type) { + 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; @@ -1455,7 +324,9 @@ int read_raw_ref(const char *refname, unsigned char *sha1, *type = 0; strbuf_reset(&sb_path); - strbuf_git_path(&sb_path, "%s", refname); + + files_ref_path(refs, &sb_path, refname); + path = sb_path.buf; stat_ref: @@ -1478,7 +349,8 @@ stat_ref: if (lstat(path, &st) < 0) { if (errno != ENOENT) goto out; - if (resolve_missing_loose_ref(refname, sha1, type)) { + if (refs_read_raw_ref(refs->packed_ref_store, refname, + oid, referent, type)) { errno = ENOENT; goto out; } @@ -1517,7 +389,8 @@ stat_ref: * ref is supposed to be, there could still be a * packed ref: */ - if (resolve_missing_loose_ref(refname, sha1, type)) { + if (refs_read_raw_ref(refs->packed_ref_store, refname, + oid, referent, type)) { errno = EISDIR; goto out; } @@ -1563,8 +436,8 @@ stat_ref: * Please note that FETCH_HEAD has additional * data after the sha. */ - if (get_sha1_hex(buf, sha1) || - (buf[40] != '\0' && !isspace(buf[40]))) { + if (parse_oid_hex(buf, oid, &p) || + (*p != '\0' && !isspace(*p))) { *type |= REF_ISBROKEN; errno = EINVAL; goto out; @@ -1582,9 +455,7 @@ out: 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); + rollback_lock_file(&lock->lk); free(lock->ref_name); free(lock); } @@ -1599,10 +470,10 @@ static void unlock_ref(struct ref_lock *lock) * * 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 verify_refname_available_dir() for this check. + * 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 sha1. + * broken, lock the reference anyway but clear old_oid. * * Return 0 on success. On failure, write an error message to err and * return TRANSACTION_NAME_CONFLICT or TRANSACTION_GENERIC_ERROR. @@ -1614,11 +485,12 @@ static void unlock_ref(struct ref_lock *lock) * * but it includes a lot more code to * - Deal with possible races with other processes - * - Avoid calling verify_refname_available_dir() when it can be + * - 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 int lock_raw_ref(const char *refname, int mustexist, +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, @@ -1632,6 +504,8 @@ static int lock_raw_ref(const char *refname, int mustexist, int ret = TRANSACTION_GENERIC_ERROR; assert(err); + files_assert_main_repository(refs, "lock_raw_ref"); + *type = 0; /* First lock the file so it can't change out from under us. */ @@ -1639,7 +513,7 @@ static int lock_raw_ref(const char *refname, int mustexist, *lock_p = lock = xcalloc(1, sizeof(*lock)); lock->ref_name = xstrdup(refname); - strbuf_git_path(&ref_file, "%s", refname); + files_ref_path(refs, &ref_file, refname); retry: switch (safe_create_leading_directories(ref_file.buf)) { @@ -1654,7 +528,8 @@ retry: * another reference such as "refs/foo". There is no * reason to expect this error to be transitory. */ - if (verify_refname_available(refname, extras, skip, err)) { + if (refs_verify_refname_available(&refs->base, refname, + extras, skip, err)) { if (mustexist) { /* * To the user the relevant error is @@ -1667,7 +542,8 @@ retry: } else { /* * The error message set by - * verify_refname_available_dir() is OK. + * refs_verify_refname_available() is + * OK. */ ret = TRANSACTION_NAME_CONFLICT; } @@ -1693,10 +569,9 @@ retry: goto error_return; } - if (!lock->lk) - lock->lk = xcalloc(1, sizeof(struct lock_file)); - - if (hold_lock_file_for_update(lock->lk, ref_file.buf, LOCK_NO_DEREF) < 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 @@ -1715,7 +590,8 @@ retry: * fear that its value will change. */ - if (read_raw_ref(refname, lock->old_oid.hash, referent, type)) { + if (files_read_raw_ref(&refs->base, refname, + &lock->old_oid, referent, type)) { if (errno == ENOENT) { if (mustexist) { /* Garden variety missing reference. */ @@ -1756,10 +632,9 @@ retry: goto error_return; } else if (remove_dir_recursively(&ref_file, REMOVE_DIR_EMPTY_ONLY)) { - if (verify_refname_available_dir( - refname, extras, skip, - get_loose_refs(&ref_cache), - err)) { + if (refs_verify_refname_available( + &refs->base, refname, + extras, skip, err)) { /* * The error message set by * verify_refname_available() is OK. @@ -1791,16 +666,13 @@ retry: /* * If the ref did not exist and we are creating it, - * make sure there is no existing packed ref whose - * name begins with our refname, nor a packed ref - * whose name is a proper prefix of our refname. + * make sure there is no existing packed ref that + * conflicts with refname: */ - if (verify_refname_available_dir( - refname, extras, skip, - get_packed_refs(&ref_cache), - err)) { + if (refs_verify_refname_available( + refs->packed_ref_store, refname, + extras, skip, err)) goto error_return; - } } ret = 0; @@ -1815,83 +687,9 @@ out: return ret; } -/* - * Peel the entry (if possible) and return its new peel_status. If - * repeel is true, re-peel the entry even if there is an old peeled - * value that is already stored in it. - * - * It is OK to call this function with a packed reference entry that - * might be stale and might even refer to an object that has since - * been garbage-collected. In such a case, if the entry has - * REF_KNOWS_PEELED then leave the status unchanged and return - * PEEL_PEELED or PEEL_NON_TAG; otherwise, return PEEL_INVALID. - */ -static enum peel_status peel_entry(struct ref_entry *entry, int repeel) -{ - enum peel_status status; - - if (entry->flag & REF_KNOWS_PEELED) { - if (repeel) { - entry->flag &= ~REF_KNOWS_PEELED; - oidclr(&entry->u.value.peeled); - } else { - return is_null_oid(&entry->u.value.peeled) ? - PEEL_NON_TAG : PEEL_PEELED; - } - } - if (entry->flag & REF_ISBROKEN) - return PEEL_BROKEN; - if (entry->flag & REF_ISSYMREF) - return PEEL_IS_SYMREF; - - status = peel_object(entry->u.value.oid.hash, entry->u.value.peeled.hash); - if (status == PEEL_PEELED || status == PEEL_NON_TAG) - entry->flag |= REF_KNOWS_PEELED; - return status; -} - -int peel_ref(const char *refname, unsigned char *sha1) -{ - int flag; - unsigned char base[20]; - - if (current_ref_iter && current_ref_iter->refname == refname) { - struct object_id peeled; - - if (ref_iterator_peel(current_ref_iter, &peeled)) - return -1; - hashcpy(sha1, peeled.hash); - return 0; - } - - if (read_ref_full(refname, RESOLVE_REF_READING, base, &flag)) - return -1; - - /* - * 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; - } - } - - return peel_object(base, sha1); -} - struct files_ref_iterator { struct ref_iterator base; - struct packed_ref_cache *packed_ref_cache; struct ref_iterator *iter0; unsigned int flags; }; @@ -1903,6 +701,10 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator) int ok; 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; + if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) && !ref_resolves_to_object(iter->iter0->refname, iter->iter0->oid, @@ -1940,7 +742,6 @@ static int files_ref_iterator_abort(struct ref_iterator *ref_iterator) if (iter->iter0) ok = ref_iterator_abort(iter->iter0); - release_packed_ref_cache(iter->packed_ref_cache); base_ref_iterator_free(ref_iterator); return ok; } @@ -1951,27 +752,20 @@ static struct ref_iterator_vtable files_ref_iterator_vtable = { files_ref_iterator_abort }; -struct ref_iterator *files_ref_iterator_begin( - const char *submodule, +static struct ref_iterator *files_ref_iterator_begin( + struct ref_store *ref_store, const char *prefix, unsigned int flags) { - struct ref_cache *refs = get_ref_cache(submodule); - struct ref_dir *loose_dir, *packed_dir; - struct ref_iterator *loose_iter, *packed_iter; + 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 (!refs) - return empty_ref_iterator_begin(); + if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) + required_flags |= REF_STORE_ODB; - if (ref_paranoia < 0) - ref_paranoia = git_env_bool("GIT_REF_PARANOIA", 0); - if (ref_paranoia) - flags |= DO_FOR_EACH_INCLUDE_BROKEN; - - iter = xcalloc(1, sizeof(*iter)); - ref_iterator = &iter->base; - base_ref_iterator_init(ref_iterator, &files_ref_iterator_vtable); + refs = files_downcast(ref_store, required_flags, "ref_iterator_begin"); /* * We must make sure that all loose refs are read before @@ -1979,64 +773,63 @@ struct ref_iterator *files_ref_iterator_begin( * condition if loose refs are migrated to the packed-refs * file by a simultaneous process, but our in-memory view is * from before the migration. We ensure this as follows: - * First, we call prime_ref_dir(), which pre-reads the loose - * references for the subtree into the cache. (If they've - * already been read, that's OK; we only need to guarantee - * that they're read before the packed refs, not *how much* - * before.) After that, we call get_packed_ref_cache(), which - * internally checks whether the packed-ref cache is up to - * date with what is on disk, and re-reads it if not. + * First, we call start the loose refs iteration with its + * `prime_ref` argument set to true. This causes the loose + * references in the subtree to be pre-read into the cache. + * (If they've already been read, that's OK; we only need to + * guarantee that they're read before the packed refs, not + * *how much* before.) After that, we call + * 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. */ - loose_dir = get_loose_refs(refs); - - if (prefix && *prefix) - loose_dir = find_containing_dir(loose_dir, prefix, 0); + loose_iter = cache_ref_iterator_begin(get_loose_ref_cache(refs), + prefix, 1); - if (loose_dir) { - prime_ref_dir(loose_dir); - loose_iter = cache_ref_iterator_begin(loose_dir); - } else { - /* There's nothing to iterate over. */ - loose_iter = empty_ref_iterator_begin(); - } - - iter->packed_ref_cache = get_packed_ref_cache(refs); - acquire_packed_ref_cache(iter->packed_ref_cache); - packed_dir = get_packed_ref_dir(iter->packed_ref_cache); + /* + * 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); - if (prefix && *prefix) - packed_dir = find_containing_dir(packed_dir, prefix, 0); + overlay_iter = overlay_ref_iterator_begin(loose_iter, packed_iter); - if (packed_dir) { - packed_iter = cache_ref_iterator_begin(packed_dir); - } else { - /* There's nothing to iterate over. */ - packed_iter = empty_ref_iterator_begin(); - } - - iter->iter0 = 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); errno = save_errno; @@ -2046,11 +839,11 @@ static int verify_lock(struct ref_lock *lock, return 0; } } - if (old_sha1 && hashcmp(lock->old_oid.hash, old_sha1)) { + if (old_oid && oidcmp(&lock->old_oid, old_oid)) { strbuf_addf(err, "ref '%s' is at %s but expected %s", lock->ref_name, oid_to_hex(&lock->old_oid), - sha1_to_hex(old_sha1)); + oid_to_hex(old_oid)); errno = EBUSY; return -1; } @@ -2067,26 +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, - 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 ref_lock *lock; int last_errno = 0; - int lflags = LOCK_NO_DEREF; - int mustexist = (old_sha1 && !is_null_sha1(old_sha1)); + int mustexist = (old_oid && !is_null_oid(old_oid)); int resolve_flags = RESOLVE_REF_NO_RECURSE; - int attempts_remaining = 3; int resolved; + files_assert_main_repository(refs, "lock_ref_oid_basic"); assert(err); lock = xcalloc(1, sizeof(struct ref_lock)); @@ -2096,9 +898,10 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, if (flags & REF_DELETING) resolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME; - strbuf_git_path(&ref_file, "%s", refname); - resolved = !!resolve_ref_unsafe(refname, resolve_flags, - lock->old_oid.hash, type); + 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 @@ -2108,20 +911,22 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, */ if (remove_empty_directories(&ref_file)) { last_errno = errno; - if (!verify_refname_available_dir(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'", refname); goto error_return; } - resolved = !!resolve_ref_unsafe(refname, resolve_flags, - lock->old_oid.hash, type); + resolved = !!refs_resolve_ref_unsafe(&refs->base, + refname, resolve_flags, + &lock->old_oid, type); } if (!resolved) { last_errno = errno; if (last_errno != ENOTDIR || - !verify_refname_available_dir(refname, extras, skip, - get_loose_refs(&ref_cache), err)) + !refs_verify_refname_available(&refs->base, refname, + extras, skip, err)) strbuf_addf(err, "unable to resolve reference '%s': %s", refname, strerror(last_errno)); @@ -2135,46 +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); - 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; } @@ -2190,195 +970,34 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, 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) || !entry_resolves_to_object(entry)) - return 0; - - /* Add a packed ref cache entry equivalent to the loose entry. */ - peel_status = peel_entry(entry, 1); - if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG) - die("internal error peeling reference %s (%s)", - entry->name, oid_to_hex(&entry->u.value.oid)); - packed_entry = find_ref(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++; @@ -2386,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 | REF_NODEREF, 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, unsigned int flags) +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, flags)) + 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; } /* @@ -2570,129 +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, - const struct string_list *extras, - const 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, + 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); -int rename_ref(const char *oldrefname, const char *newrefname, const char *logmsg) +static int files_copy_or_rename_ref(struct ref_store *ref_store, + const char *oldrefname, const char *newrefname, + const char *logmsg, int copy) { - unsigned char sha1[20], orig_sha1[20]; + 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); + 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); - if (!resolve_ref_unsafe(oldrefname, RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, - orig_sha1, &flag)) - 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 (flag & REF_ISSYMREF) - return error("refname %s is a symbolic ref, renaming it is not supported", - oldrefname); - 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; } /* - * Since we are doing a shallow lookup, sha1 is not the - * correct value to pass to delete_ref as old_sha1. But that - * doesn't matter, because an old_sha1 check wouldn't add to + * 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 (!read_ref_full(newrefname, RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, - sha1, NULL) && - delete_ref(newrefname, NULL, REF_NODEREF)) { - if (errno==EISDIR) { + 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); @@ -2706,32 +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, REF_NODEREF, - 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, &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, REF_NODEREF, - 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); @@ -2739,36 +1408,57 @@ 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, &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) { - char *path = get_locked_file_path(lock->lk); + char *path = get_locked_file_path(&lock->lk); struct stat st; if (!lstat(path, &st) && S_ISDIR(st.st_mode)) { @@ -2792,70 +1482,102 @@ static int commit_ref(struct ref_lock *lock) free(path); } - if (commit_lock_file(lock->lk)) + 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; @@ -2866,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)); + 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); + 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; } @@ -2977,12 +1690,17 @@ 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, +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) { - clear_loose_ref_cache(&ref_cache); - if (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg, 0, 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", lock->ref_name, old_msg); @@ -3004,17 +1722,18 @@ 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); } @@ -3035,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); @@ -3046,121 +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 set_worktree_head_symref(const char *gitdir, const char *target) +static int files_reflog_exists(struct ref_store *ref_store, + const char *refname) { - static struct lock_file head_lock; - struct ref_lock *lock; - struct strbuf head_path = STRBUF_INIT; - const char *head_rel; + struct files_ref_store *refs = + files_downcast(ref_store, REF_STORE_READ, "reflog_exists"); + struct strbuf sb = STRBUF_INIT; + struct stat st; int ret; - strbuf_addf(&head_path, "%s/HEAD", absolute_path(gitdir)); - if (hold_lock_file_for_update(&head_lock, head_path.buf, - LOCK_NO_DEREF) < 0) { - struct strbuf err = STRBUF_INIT; - unable_to_lock_message(head_path.buf, errno, &err); - error("%s", err.buf); - strbuf_release(&err); - strbuf_release(&head_path); - return -1; - } - - /* head_rel will be "HEAD" for the main tree, "worktrees/wt/HEAD" for - linked trees */ - head_rel = remove_leading_path(head_path.buf, - absolute_path(get_git_common_dir())); - /* to make use of create_symref_locked(), initialize ref_lock */ - lock = xcalloc(1, sizeof(struct ref_lock)); - lock->lk = &head_lock; - lock->ref_name = xstrdup(head_rel); - - ret = create_symref_locked(lock, head_rel, target, NULL); - - unlock_ref(lock); /* will free lock */ - strbuf_release(&head_path); + files_reflog_path(refs, &sb, refname); + ret = !lstat(sb.buf, &st) && S_ISREG(st.st_mode); + strbuf_release(&sb); return ret; } -int reflog_exists(const char *refname) +static int files_delete_reflog(struct ref_store *ref_store, + const char *refname) { - struct stat st; - - return !lstat(git_path("logs/%s", refname), &st) && - S_ISREG(st.st_mode); -} + struct files_ref_store *refs = + files_downcast(ref_store, REF_STORE_WRITE, "delete_reflog"); + struct strbuf sb = STRBUF_INIT; + int ret; -int delete_reflog(const char *refname) -{ - return remove_path(git_path("logs/%s", refname)); + 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]) || @@ -3172,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) @@ -3186,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; @@ -3210,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; @@ -3288,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; @@ -3308,6 +2038,7 @@ int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_dat struct files_reflog_iterator { struct ref_iterator base; + struct ref_store *ref_store; struct dir_iterator *dir_iterator; struct object_id oid; }; @@ -3329,8 +2060,9 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator) if (ends_with(diter->basename, ".lock")) continue; - if (read_ref_full(diter->relative_path, 0, - iter->oid.hash, &flags)) { + if (refs_read_ref_full(iter->ref_store, + diter->relative_path, 0, + &iter->oid, &flags)) { error("bad ref for %s", diter->path.buf); continue; } @@ -3372,37 +2104,62 @@ static struct ref_iterator_vtable files_reflog_iterator_vtable = { files_reflog_iterator_abort }; -struct ref_iterator *files_reflog_iterator_begin(void) +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); - base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable); - iter->dir_iterator = dir_iterator_begin(git_path("logs")); return ref_iterator; } -int for_each_reflog(each_ref_fn fn, void *cb_data) +static enum iterator_selection reflog_iterator_select( + struct ref_iterator *iter_worktree, + struct ref_iterator *iter_common, + void *cb_data) { - return do_for_each_ref_iterator(files_reflog_iterator_begin(), - fn, 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 int ref_update_reject_duplicates(struct string_list *refnames, - struct strbuf *err) +static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store) { - int i, n = refnames->nr; - - assert(err); + struct files_ref_store *refs = + files_downcast(ref_store, REF_STORE_READ, + "reflog_iterator_begin"); - for (i = 1; i < n; i++) - if (!strcmp(refnames->items[i - 1].string, refnames->items[i].string)) { - strbuf_addf(err, - "multiple updates for ref '%s' not allowed.", - refnames->items[i].string); - return 1; - } - return 0; + 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); + } } /* @@ -3419,7 +2176,7 @@ static int split_head_update(struct ref_update *update, struct ref_update *new_update; if ((update->flags & REF_LOG_ONLY) || - (update->flags & REF_ISPRUNING) || + (update->flags & REF_IS_PRUNING) || (update->flags & REF_UPDATE_VIA_HEAD)) return 0; @@ -3428,11 +2185,10 @@ static int split_head_update(struct ref_update *update, /* * First make sure that HEAD is not already in the - * transaction. This insertion is O(N) in the transaction + * transaction. This check is O(lg N) in the transaction * size, but it happens at most once per transaction. */ - item = string_list_insert(affected_refnames, "HEAD"); - if (item->util) { + if (string_list_has_string(affected_refnames, "HEAD")) { /* An entry already existed */ strbuf_addf(err, "multiple updates for 'HEAD' (including one " @@ -3443,10 +2199,18 @@ static int split_head_update(struct ref_update *update, new_update = ref_transaction_add_update( transaction, "HEAD", - update->flags | REF_LOG_ONLY | REF_NODEREF, - update->new_sha1, update->old_sha1, + 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; @@ -3454,13 +2218,14 @@ static int split_head_update(struct ref_update *update, /* * update is for a symref that points at referent and doesn't have - * REF_NODEREF set. Split it into two updates: - * - The original update, but with REF_LOG_ONLY and REF_NODEREF set + * 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 ref_update *update, +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, @@ -3472,13 +2237,12 @@ static int split_symref_update(struct ref_update *update, /* * First make sure that referent is not already in the - * transaction. This insertion is O(N) in the transaction + * transaction. This check is O(lg N) in the transaction * size, but it happens at most once per symref in a * transaction. */ - item = string_list_insert(affected_refnames, referent); - if (item->util) { - /* An entry already existed */ + 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", @@ -3500,19 +2264,30 @@ static int split_symref_update(struct ref_update *update, new_update = ref_transaction_add_update( transaction, referent, new_flags, - update->new_sha1, update->old_sha1, + &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 SHA-1 value, as that will be + * 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_NODEREF; + 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; @@ -3539,10 +2314,10 @@ static int check_old_oid(struct ref_update *update, struct object_id *oid, struct strbuf *err) { if (!(update->flags & REF_HAVE_OLD) || - !hashcmp(oid->hash, update->old_sha1)) + !oidcmp(oid, &update->old_oid)) return 0; - if (is_null_sha1(update->old_sha1)) + if (is_null_oid(&update->old_oid)) strbuf_addf(err, "cannot lock ref '%s': " "reference already exists", original_update_refname(update)); @@ -3550,13 +2325,13 @@ static int check_old_oid(struct ref_update *update, struct object_id *oid, strbuf_addf(err, "cannot lock ref '%s': " "reference is missing but expected %s", original_update_refname(update), - sha1_to_hex(update->old_sha1)); + 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), - sha1_to_hex(update->old_sha1)); + oid_to_hex(&update->old_oid)); return -1; } @@ -3565,16 +2340,17 @@ static int check_old_oid(struct ref_update *update, struct object_id *oid, * Prepare for carrying out update: * - Lock the reference referred to by update. * - Read the reference under lock. - * - Check that its old SHA-1 value (if specified) is correct, and in + * - 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_NODEREF, split it up into a + * - 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 ref_update *update, +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, @@ -3582,25 +2358,26 @@ static int lock_ref_for_update(struct ref_update *update, { struct strbuf referent = STRBUF_INIT; int mustexist = (update->flags & REF_HAVE_OLD) && - !is_null_sha1(update->old_sha1); - int ret; + !is_null_oid(&update->old_oid); + int ret = 0; struct ref_lock *lock; - if ((update->flags & REF_HAVE_NEW) && is_null_sha1(update->new_sha1)) + 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) - return ret; + goto out; } - ret = lock_raw_ref(update->refname, mustexist, + ret = lock_raw_ref(refs, update->refname, mustexist, affected_refnames, NULL, - &update->lock, &referent, + &lock, &referent, &update->type, err); - if (ret) { char *reason; @@ -3608,57 +2385,64 @@ static int lock_ref_for_update(struct ref_update *update, strbuf_addf(err, "cannot lock ref '%s': %s", original_update_refname(update), reason); free(reason); - return ret; + goto out; } - lock = update->lock; + update->backend_data = lock; if (update->type & REF_ISSYMREF) { - if (update->flags & REF_NODEREF) { + 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_sha1: + * to record and possibly check old_oid: */ - if (read_ref_full(referent.buf, 0, - lock->old_oid.hash, NULL)) { + 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)); - return -1; + ret = TRANSACTION_GENERIC_ERROR; + goto out; } } else if (check_old_oid(update, &lock->old_oid, err)) { - return TRANSACTION_GENERIC_ERROR; + 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_sha1 for this update as part + * 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(update, referent.buf, transaction, + ret = split_symref_update(refs, update, + referent.buf, transaction, affected_refnames, err); if (ret) - return ret; + goto out; } } else { struct ref_update *parent_update; - if (check_old_oid(update, &lock->old_oid, err)) - return TRANSACTION_GENERIC_ERROR; + 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 SHA-1 in the parent + * symref update, record the old OID in the parent * update: */ for (parent_update = update->parent_update; parent_update; parent_update = parent_update->parent_update) { - oidcpy(&parent_update->lock->old_oid, &lock->old_oid); + struct ref_lock *parent_lock = parent_update->backend_data; + oidcpy(&parent_lock->old_oid, &lock->old_oid); } } @@ -3666,12 +2450,12 @@ static int lock_ref_for_update(struct ref_update *update, !(update->flags & REF_DELETING) && !(update->flags & REF_LOG_ONLY)) { if (!(update->type & REF_ISSYMREF) && - !hashcmp(lock->old_oid.hash, update->new_sha1)) { + !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_sha1, + } else if (write_ref_to_lockfile(lock, &update->new_oid, err)) { char *write_err = strbuf_detach(err, NULL); @@ -3679,12 +2463,13 @@ static int lock_ref_for_update(struct ref_update *update, * The lock was freed upon failure of * write_ref_to_lockfile(): */ - update->lock = NULL; + update->backend_data = NULL; strbuf_addf(err, "cannot update ref '%s': %s", update->refname, write_err); free(write_err); - return TRANSACTION_GENERIC_ERROR; + ret = TRANSACTION_GENERIC_ERROR; + goto out; } else { update->flags |= REF_NEEDS_COMMIT; } @@ -3695,48 +2480,100 @@ static int lock_ref_for_update(struct ref_update *update, * the lockfile is still open. Close it to * free up the file descriptor: */ - if (close_ref(lock)) { + if (close_ref_gently(lock)) { strbuf_addf(err, "couldn't close '%s.lock'", update->refname); - return TRANSACTION_GENERIC_ERROR; + ret = TRANSACTION_GENERIC_ERROR; + goto out; } } - return 0; + +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; - 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 object_id head_oid; + 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 (!transaction->nr) { - 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. (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.) + * 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 @@ -3768,12 +2605,12 @@ int ref_transaction_commit(struct ref_transaction *transaction, * head_ref within the transaction, then split_head_update() * arranges for the reflog of HEAD to be updated, too. */ - head_ref = resolve_refdup("HEAD", RESOLVE_REF_NO_RECURSE, - head_oid.hash, &head_type); + head_ref = refs_resolve_refdup(ref_store, "HEAD", + RESOLVE_REF_NO_RECURSE, + NULL, &head_type); if (head_ref && !(head_type & REF_ISSYMREF)) { - free(head_ref); - head_ref = NULL; + FREE_AND_NULL(head_ref); } /* @@ -3781,86 +2618,232 @@ int ref_transaction_commit(struct ref_transaction *transaction, * 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 < transaction->nr; i++) { struct ref_update *update = transaction->updates[i]; - ret = lock_ref_for_update(update, transaction, head_ref, - &affected_refnames, err); + ret = lock_ref_for_update(refs, update, transaction, + head_ref, &affected_refnames, err); if (ret) goto cleanup; + + 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; + } + + 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; + } + 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 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 (ref_transaction_abort(packed_transaction, err)) { + ret = TRANSACTION_GENERIC_ERROR; + goto cleanup; + } + backend_data->packed_transaction = NULL; + } } +cleanup: + free(head_ref); + string_list_clear(&affected_refnames, 0); + + 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->lock; + struct ref_lock *lock = update->backend_data; if (update->flags & REF_NEEDS_COMMIT || update->flags & REF_LOG_ONLY) { - if (log_ref_write(lock->ref_name, lock->old_oid.hash, - update->new_sha1, - update->msg, update->flags, err)) { + 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->lock = NULL; + update->backend_data = NULL; ret = TRANSACTION_GENERIC_ERROR; goto cleanup; } } if (update->flags & REF_NEEDS_COMMIT) { - clear_loose_ref_cache(&ref_cache); + clear_loose_ref_cache(refs); if (commit_ref(lock)) { strbuf_addf(err, "couldn't set '%s'", lock->ref_name); unlock_ref(lock); - update->lock = NULL; + update->backend_data = NULL; ret = TRANSACTION_GENERIC_ERROR; goto cleanup; } } } - /* Perform deletes now that updates are safely completed */ + + /* + * 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); + } + } + + /* + * 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 (delete_ref_loose(update->lock, update->type, err)) { - ret = TRANSACTION_GENERIC_ERROR; - goto cleanup; + 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; } - - if (!(update->flags & REF_ISPRUNING)) - string_list_append(&refs_to_delete, - update->lock->ref_name); } } - if (repack_without_refs(&refs_to_delete, err)) { - ret = TRANSACTION_GENERIC_ERROR; - goto cleanup; - } - 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 < transaction->nr; i++) - if (transaction->updates[i]->lock) - unlock_ref(transaction->updates[i]->lock); - string_list_clear(&refs_to_delete, 0); - free(head_ref); - 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) { @@ -3869,11 +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; + 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); @@ -3902,46 +2891,53 @@ 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"); + 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 < transaction->nr; i++) { - struct ref_update *update = transaction->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; } cleanup: + if (packed_transaction) + ref_transaction_free(packed_transaction); + packed_refs_unlock(refs->packed_ref_store); transaction->state = REF_TRANSACTION_CLOSED; string_list_clear(&affected_refnames, 0); return ret; @@ -3952,20 +2948,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); @@ -3973,10 +2969,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); @@ -3984,16 +2980,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; @@ -4009,19 +3009,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, REF_NODEREF, - &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 @@ -4045,8 +3047,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)) { @@ -4059,18 +3061,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)", @@ -4089,3 +3092,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 +}; |