diff options
Diffstat (limited to 'refs')
-rw-r--r-- | refs/files-backend.c | 326 | ||||
-rw-r--r-- | refs/iterator.c | 18 | ||||
-rw-r--r-- | refs/ref-cache.c | 104 | ||||
-rw-r--r-- | refs/ref-cache.h | 6 | ||||
-rw-r--r-- | refs/refs-internal.h | 62 |
5 files changed, 357 insertions, 159 deletions
diff --git a/refs/files-backend.c b/refs/files-backend.c index cb1f528cbe..0404f2c233 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -1,4 +1,5 @@ #include "../cache.h" +#include "../config.h" #include "../refs.h" #include "refs-internal.h" #include "ref-cache.h" @@ -43,15 +44,6 @@ struct packed_ref_cache { */ 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; }; @@ -70,10 +62,13 @@ struct files_ref_store { struct ref_cache *loose; struct packed_ref_cache *packed; -}; -/* Lock used for the main packed-refs file: */ -static struct lock_file packlock; + /* + * Lock used for the "packed-refs" file. Note that this (and + * thus the enclosing `files_ref_store`) must not be freed. + */ + struct lock_file packed_refs_lock; +}; /* * Increment the reference count of *packed_refs. @@ -104,8 +99,8 @@ static void clear_packed_ref_cache(struct files_ref_store *refs) if (refs->packed) { struct packed_ref_cache *packed_refs = refs->packed; - if (packed_refs->lock) - die("internal error: packed-ref cache cleared while locked"); + if (is_lock_file_locked(&refs->packed_refs_lock)) + die("BUG: packed-ref cache cleared while locked"); refs->packed = NULL; release_packed_ref_cache(packed_refs); } @@ -215,7 +210,9 @@ static const char *parse_ref_line(struct strbuf *line, struct object_id *oid) } /* - * Read f, which is a packed-refs file, into dir. + * Read from `packed_refs_file` into a newly-allocated + * `packed_ref_cache` and return it. The return value will already + * have its reference count incremented. * * A comment line of the form "# pack-refs with: " may contain zero or * more traits. We interpret the traits as follows: @@ -241,12 +238,36 @@ static const char *parse_ref_line(struct strbuf *line, struct object_id *oid) * 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) +static struct packed_ref_cache *read_packed_refs(const char *packed_refs_file) { + FILE *f; + struct packed_ref_cache *packed_refs = xcalloc(1, sizeof(*packed_refs)); struct ref_entry *last = NULL; struct strbuf line = STRBUF_INIT; enum { PEELED_NONE, PEELED_TAGS, PEELED_FULLY } peeled = PEELED_NONE; + struct ref_dir *dir; + acquire_packed_ref_cache(packed_refs); + packed_refs->cache = create_ref_cache(NULL, NULL); + packed_refs->cache->root->flag &= ~REF_INCOMPLETE; + + f = fopen(packed_refs_file, "r"); + if (!f) { + if (errno == ENOENT) { + /* + * This is OK; it just means that no + * "packed-refs" file has been written yet, + * which is equivalent to it being empty. + */ + return packed_refs; + } else { + die_errno("couldn't read %s", packed_refs_file); + } + } + + stat_validity_update(&packed_refs->validity, fileno(f)); + + dir = get_ref_dir(packed_refs->cache->root); while (strbuf_getwholeline(&line, f, '\n') != EOF) { struct object_id oid; const char *refname; @@ -271,7 +292,7 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir) oidclr(&oid); flag |= REF_BAD_NAME | REF_ISBROKEN; } - last = create_ref_entry(refname, &oid, flag, 0); + last = create_ref_entry(refname, &oid, flag); if (peeled == PEELED_FULLY || (peeled == PEELED_TAGS && starts_with(refname, "refs/tags/"))) last->flag |= REF_KNOWS_PEELED; @@ -293,7 +314,10 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir) } } + fclose(f); strbuf_release(&line); + + return packed_refs; } static const char *files_packed_refs_path(struct files_ref_store *refs) @@ -347,31 +371,35 @@ static void files_ref_path(struct files_ref_store *refs, } /* + * Check that the packed refs cache (if any) still reflects the + * contents of the file. If not, clear the cache. + */ +static void validate_packed_ref_cache(struct files_ref_store *refs) +{ + if (refs->packed && + !stat_validity_check(&refs->packed->validity, + files_packed_refs_path(refs))) + clear_packed_ref_cache(refs); +} + +/* * Get the packed_ref_cache for the specified files_ref_store, - * creating it if necessary. + * creating and populating it if it hasn't been read before or if the + * file has been changed (according to its `validity` field) since it + * was last read. On the other hand, if we hold the lock, then assume + * that the file hasn't been changed out from under us, so skip the + * extra `stat()` call in `stat_validity_check()`. */ static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *refs) { const char *packed_refs_file = files_packed_refs_path(refs); - if (refs->packed && - !stat_validity_check(&refs->packed->validity, packed_refs_file)) - clear_packed_ref_cache(refs); + if (!is_lock_file_locked(&refs->packed_refs_lock)) + validate_packed_ref_cache(refs); + + if (!refs->packed) + refs->packed = read_packed_refs(packed_refs_file); - if (!refs->packed) { - FILE *f; - - refs->packed = xcalloc(1, sizeof(*refs->packed)); - acquire_packed_ref_cache(refs->packed); - refs->packed->cache = create_ref_cache(&refs->base, NULL); - refs->packed->cache->root->flag &= ~REF_INCOMPLETE; - f = fopen(packed_refs_file, "r"); - if (f) { - stat_validity_update(&refs->packed->validity, fileno(f)); - read_packed_refs(f, get_ref_dir(refs->packed->cache->root)); - fclose(f); - } - } return refs->packed; } @@ -396,10 +424,14 @@ static void add_packed_ref(struct files_ref_store *refs, { struct packed_ref_cache *packed_ref_cache = get_packed_ref_cache(refs); - if (!packed_ref_cache->lock) - die("internal error: packed refs not locked"); + if (!is_lock_file_locked(&refs->packed_refs_lock)) + die("BUG: packed refs not locked"); + + if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) + die("Reference has invalid format: '%s'", refname); + add_ref_entry(get_packed_ref_dir(packed_ref_cache), - create_ref_entry(refname, oid, REF_ISPACKED, 1)); + create_ref_entry(refname, oid, REF_ISPACKED)); } /* @@ -476,7 +508,7 @@ static void loose_fill_ref_dir(struct ref_store *ref_store, flag |= REF_BAD_NAME | REF_ISBROKEN; } add_entry_to_dir(dir, - create_ref_entry(refname.buf, &oid, flag, 0)); + create_ref_entry(refname.buf, &oid, flag)); } strbuf_setlen(&refname, dirnamelen); strbuf_setlen(&path, path_baselen); @@ -1057,15 +1089,12 @@ static struct ref_iterator *files_ref_iterator_begin( struct ref_iterator *loose_iter, *packed_iter; struct files_ref_iterator *iter; struct ref_iterator *ref_iterator; + unsigned int required_flags = REF_STORE_READ; - if (ref_paranoia < 0) - ref_paranoia = git_env_bool("GIT_REF_PARANOIA", 0); - if (ref_paranoia) - flags |= DO_FOR_EACH_INCLUDE_BROKEN; + if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) + required_flags |= REF_STORE_ODB; - refs = files_downcast(ref_store, - REF_STORE_READ | (ref_paranoia ? 0 : REF_STORE_ODB), - "ref_iterator_begin"); + refs = files_downcast(ref_store, required_flags, "ref_iterator_begin"); iter = xcalloc(1, sizeof(*iter)); ref_iterator = &iter->base; @@ -1290,17 +1319,21 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags) } if (hold_lock_file_for_update_timeout( - &packlock, files_packed_refs_path(refs), + &refs->packed_refs_lock, files_packed_refs_path(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. + * Now that we hold the `packed-refs` lock, make sure that our + * cache matches the current version of the file. Normally + * `get_packed_ref_cache()` does that for us, but that + * function assumes that when the file is locked, any existing + * cache is still valid. We've just locked the file, but it + * might have changed the moment *before* we locked it. */ + validate_packed_ref_cache(refs); + packed_ref_cache = get_packed_ref_cache(refs); - packed_ref_cache->lock = &packlock; /* Increment the reference count to prevent it from being freed: */ acquire_packed_ref_cache(packed_ref_cache); return 0; @@ -1323,10 +1356,10 @@ static int commit_packed_refs(struct files_ref_store *refs) files_assert_main_repository(refs, "commit_packed_refs"); - if (!packed_ref_cache->lock) - die("internal error: packed-refs not locked"); + if (!is_lock_file_locked(&refs->packed_refs_lock)) + die("BUG: packed-refs not locked"); - out = fdopen_lock_file(packed_ref_cache->lock, "w"); + out = fdopen_lock_file(&refs->packed_refs_lock, "w"); if (!out) die_errno("unable to fdopen packed-refs descriptor"); @@ -1344,11 +1377,10 @@ static int commit_packed_refs(struct files_ref_store *refs) if (ok != ITER_DONE) die("error while iterating over references"); - if (commit_lock_file(packed_ref_cache->lock)) { + if (commit_lock_file(&refs->packed_refs_lock)) { save_errno = errno; error = -1; } - packed_ref_cache->lock = NULL; release_packed_ref_cache(packed_ref_cache); errno = save_errno; return error; @@ -1366,10 +1398,9 @@ static void rollback_packed_refs(struct files_ref_store *refs) files_assert_main_repository(refs, "rollback_packed_refs"); - if (!packed_ref_cache->lock) - die("internal error: packed-refs not locked"); - rollback_lock_file(packed_ref_cache->lock); - packed_ref_cache->lock = NULL; + if (!is_lock_file_locked(&refs->packed_refs_lock)) + die("BUG: packed-refs not locked"); + rollback_lock_file(&refs->packed_refs_lock); release_packed_ref_cache(packed_ref_cache); clear_packed_ref_cache(refs); } @@ -1464,6 +1495,32 @@ static void prune_refs(struct files_ref_store *refs, struct ref_to_prune *r) } } +/* + * 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) +{ + /* Do not pack per-worktree refs: */ + if (ref_type(refname) != REF_TYPE_NORMAL) + return 0; + + /* 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 not pack symbolic refs: */ + if (ref_flags & REF_ISSYMREF) + return 0; + + /* Do not pack broken refs: */ + if (!ref_resolves_to_object(refname, oid, ref_flags)) + return 0; + + return 1; +} + static int files_pack_refs(struct ref_store *ref_store, unsigned int flags) { struct files_ref_store *refs = @@ -1485,21 +1542,9 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags) * pruned, also add it to refs_to_prune. */ struct ref_entry *packed_entry; - int is_tag_ref = starts_with(iter->refname, "refs/tags/"); - - /* Do not pack per-worktree refs: */ - if (ref_type(iter->refname) != REF_TYPE_NORMAL) - continue; - - /* ALWAYS pack tags */ - if (!(flags & PACK_REFS_ALL) && !is_tag_ref) - continue; - /* Do not pack symbolic or broken refs: */ - if (iter->flags & REF_ISSYMREF) - continue; - - if (!ref_resolves_to_object(iter->refname, iter->oid, iter->flags)) + if (!should_pack_ref(iter->refname, iter->oid, iter->flags, + flags)) continue; /* @@ -1515,7 +1560,7 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags) oidcpy(&packed_entry->u.value.oid, iter->oid); } else { packed_entry = create_ref_entry(iter->refname, iter->oid, - REF_ISPACKED, 0); + REF_ISPACKED); add_ref_entry(packed_refs, packed_entry); } oidclr(&packed_entry->u.value.peeled); @@ -1595,7 +1640,7 @@ static int repack_without_refs(struct files_ref_store *refs, return ret; } -static int files_delete_refs(struct ref_store *ref_store, +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 = @@ -1627,7 +1672,7 @@ static int files_delete_refs(struct ref_store *ref_store, for (i = 0; i < refnames->nr; i++) { const char *refname = refnames->items[i].string; - if (refs_delete_ref(&refs->base, NULL, refname, NULL, flags)) + if (refs_delete_ref(&refs->base, msg, refname, NULL, flags)) result |= error(_("could not remove reference %s"), refname); } @@ -2521,23 +2566,6 @@ static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_st return ref_iterator; } -static int ref_update_reject_duplicates(struct string_list *refnames, - struct strbuf *err) -{ - int i, n = refnames->nr; - - assert(err); - - 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 update is a direct update of head_ref (the reference pointed to * by HEAD), then add an extra REF_LOG_ONLY update for HEAD. @@ -2843,31 +2871,45 @@ static int lock_ref_for_update(struct files_ref_store *refs, return 0; } -static int files_transaction_commit(struct ref_store *ref_store, - struct ref_transaction *transaction, - struct strbuf *err) +/* + * Unlock any references in `transaction` that are still locked, and + * mark the transaction closed. + */ +static void files_transaction_cleanup(struct ref_transaction *transaction) +{ + size_t i; + + 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; + } + } + + transaction->state = REF_TRANSACTION_CLOSED; +} + +static int files_transaction_prepare(struct ref_store *ref_store, + struct ref_transaction *transaction, + struct strbuf *err) { struct files_ref_store *refs = files_downcast(ref_store, REF_STORE_WRITE, - "ref_transaction_commit"); - int ret = 0, i; - struct string_list refs_to_delete = STRING_LIST_INIT_NODUP; - struct string_list_item *ref_to_delete; + "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 strbuf sb = STRBUF_INIT; assert(err); - if (transaction->state != REF_TRANSACTION_OPEN) - die("BUG: commit called for transaction that is not open"); - - if (!transaction->nr) { - transaction->state = REF_TRANSACTION_CLOSED; - return 0; - } + if (!transaction->nr) + goto cleanup; /* * Fail if a refname appears more than once in the @@ -2917,8 +2959,7 @@ static int files_transaction_commit(struct ref_store *ref_store, head_oid.hash, &head_type); if (head_ref && !(head_type & REF_ISSYMREF)) { - free(head_ref); - head_ref = NULL; + FREE_AND_NULL(head_ref); } /* @@ -2926,6 +2967,8 @@ static int files_transaction_commit(struct ref_store *ref_store, * 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]; @@ -2933,7 +2976,38 @@ static int files_transaction_commit(struct ref_store *ref_store, ret = lock_ref_for_update(refs, update, transaction, head_ref, &affected_refnames, err); if (ret) - goto cleanup; + break; + } + +cleanup: + free(head_ref); + string_list_clear(&affected_refnames, 0); + + if (ret) + files_transaction_cleanup(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 string_list refs_to_delete = STRING_LIST_INIT_NODUP; + struct string_list_item *ref_to_delete; + struct strbuf sb = STRBUF_INIT; + + assert(err); + + if (!transaction->nr) { + transaction->state = REF_TRANSACTION_CLOSED; + return 0; } /* Perform updates first so live commits remain referenced */ @@ -3013,15 +3087,10 @@ static int files_transaction_commit(struct ref_store *ref_store, clear_loose_ref_cache(refs); cleanup: - strbuf_release(&sb); - transaction->state = REF_TRANSACTION_CLOSED; + files_transaction_cleanup(transaction); 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); if (update->flags & REF_DELETED_LOOSE) { /* @@ -3035,13 +3104,19 @@ cleanup: } } + strbuf_release(&sb); string_list_clear(&refs_to_delete, 0); - free(head_ref); - string_list_clear(&affected_refnames, 0); - return ret; } +static int files_transaction_abort(struct ref_store *ref_store, + struct ref_transaction *transaction, + struct strbuf *err) +{ + files_transaction_cleanup(transaction); + return 0; +} + static int ref_present(const char *refname, const struct object_id *oid, int flags, void *cb_data) { @@ -3057,7 +3132,8 @@ static int files_initial_transaction_commit(struct ref_store *ref_store, struct files_ref_store *refs = files_downcast(ref_store, REF_STORE_WRITE, "initial_ref_transaction_commit"); - int ret = 0, i; + size_t i; + int ret = 0; struct string_list affected_refnames = STRING_LIST_INIT_NODUP; assert(err); @@ -3311,7 +3387,9 @@ struct ref_storage_be refs_be_files = { "files", files_ref_store_create, files_init_db, - files_transaction_commit, + files_transaction_prepare, + files_transaction_finish, + files_transaction_abort, files_initial_transaction_commit, files_pack_refs, diff --git a/refs/iterator.c b/refs/iterator.c index bce1f192f7..4cf449ef66 100644 --- a/refs/iterator.c +++ b/refs/iterator.c @@ -292,7 +292,23 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator) if (!starts_with(iter->iter0->refname, iter->prefix)) continue; - iter->base.refname = iter->iter0->refname + iter->trim; + if (iter->trim) { + /* + * It is nonsense to trim off characters that + * you haven't already checked for via a + * prefix check, whether via this + * `prefix_ref_iterator` or upstream in + * `iter0`). So if there wouldn't be at least + * one character left in the refname after + * trimming, report it as a bug: + */ + if (strlen(iter->iter0->refname) <= iter->trim) + die("BUG: attempt to trim too many characters"); + iter->base.refname = iter->iter0->refname + iter->trim; + } else { + iter->base.refname = iter->iter0->refname; + } + iter->base.oid = iter->iter0->oid; iter->base.flags = iter->iter0->flags; return ITER_OK; diff --git a/refs/ref-cache.c b/refs/ref-cache.c index 6b11d9cd12..76bb723c86 100644 --- a/refs/ref-cache.c +++ b/refs/ref-cache.c @@ -32,14 +32,10 @@ struct ref_dir *get_ref_dir(struct ref_entry *entry) } struct ref_entry *create_ref_entry(const char *refname, - const struct object_id *oid, int flag, - int check_name) + const struct object_id *oid, int flag) { 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); oidcpy(&ref->u.value.oid, oid); oidclr(&ref->u.value.peeled); @@ -86,9 +82,8 @@ 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); + FREE_AND_NULL(dir->entries); dir->sorted = dir->nr = dir->alloc = 0; - dir->entries = NULL; } struct ref_entry *create_dir_entry(struct ref_cache *cache, @@ -316,11 +311,42 @@ static void sort_ref_dir(struct ref_dir *dir) dir->sorted = dir->nr = i; } +enum prefix_state { + /* All refs within the directory would match prefix: */ + PREFIX_CONTAINS_DIR, + + /* Some, but not all, refs within the directory might match prefix: */ + PREFIX_WITHIN_DIR, + + /* No refs within the directory could possibly match prefix: */ + PREFIX_EXCLUDES_DIR +}; + /* - * Load all of the refs from `dir` (recursively) into our in-memory - * cache. + * Return a `prefix_state` constant describing the relationship + * between the directory with the specified `dirname` and `prefix`. */ -static void prime_ref_dir(struct ref_dir *dir) +static enum prefix_state overlaps_prefix(const char *dirname, + const char *prefix) +{ + while (*prefix && *dirname == *prefix) { + dirname++; + prefix++; + } + if (!*prefix) + return PREFIX_CONTAINS_DIR; + else if (!*dirname) + return PREFIX_WITHIN_DIR; + else + return PREFIX_EXCLUDES_DIR; +} + +/* + * Load all of the refs from `dir` (recursively) that could possibly + * contain references matching `prefix` into our in-memory cache. If + * `prefix` is NULL, prime unconditionally. + */ +static void prime_ref_dir(struct ref_dir *dir, const char *prefix) { /* * The hard work of loading loose refs is done by get_ref_dir(), so we @@ -331,8 +357,29 @@ 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)); + if (!(entry->flag & REF_DIR)) { + /* Not a directory; no need to recurse. */ + } else if (!prefix) { + /* Recurse in any case: */ + prime_ref_dir(get_ref_dir(entry), NULL); + } else { + switch (overlaps_prefix(entry->name, prefix)) { + case PREFIX_CONTAINS_DIR: + /* + * Recurse, and from here down we + * don't have to check the prefix + * anymore: + */ + prime_ref_dir(get_ref_dir(entry), NULL); + break; + case PREFIX_WITHIN_DIR: + prime_ref_dir(get_ref_dir(entry), prefix); + break; + case PREFIX_EXCLUDES_DIR: + /* No need to prime this directory. */ + break; + } + } } } @@ -347,6 +394,8 @@ struct cache_ref_iterator_level { */ struct ref_dir *dir; + enum prefix_state prefix_state; + /* * The index of the current entry within dir (which might * itself be a directory). If index == -1, then the iteration @@ -374,6 +423,13 @@ struct cache_ref_iterator { size_t levels_alloc; /* + * Only include references with this prefix in the iteration. + * The prefix is matched textually, without regard for path + * component boundaries. + */ + const char *prefix; + + /* * A stack of levels. levels[0] is the uppermost level that is * being iterated over in this iteration. (This is not * necessary the top level in the references hierarchy. If we @@ -394,6 +450,7 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator) &iter->levels[iter->levels_nr - 1]; struct ref_dir *dir = level->dir; struct ref_entry *entry; + enum prefix_state entry_prefix_state; if (level->index == -1) sort_ref_dir(dir); @@ -408,6 +465,14 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator) entry = dir->entries[level->index]; + if (level->prefix_state == PREFIX_WITHIN_DIR) { + entry_prefix_state = overlaps_prefix(entry->name, iter->prefix); + if (entry_prefix_state == PREFIX_EXCLUDES_DIR) + continue; + } else { + entry_prefix_state = level->prefix_state; + } + if (entry->flag & REF_DIR) { /* push down a level */ ALLOC_GROW(iter->levels, iter->levels_nr + 1, @@ -415,6 +480,7 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator) level = &iter->levels[iter->levels_nr++]; level->dir = get_ref_dir(entry); + level->prefix_state = entry_prefix_state; level->index = -1; } else { iter->base.refname = entry->name; @@ -475,6 +541,7 @@ static int cache_ref_iterator_abort(struct ref_iterator *ref_iterator) struct cache_ref_iterator *iter = (struct cache_ref_iterator *)ref_iterator; + free((char *)iter->prefix); free(iter->levels); base_ref_iterator_free(ref_iterator); return ITER_DONE; @@ -500,10 +567,10 @@ struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache, dir = find_containing_dir(dir, prefix, 0); if (!dir) /* There's nothing to iterate over. */ - return empty_ref_iterator_begin(); + return empty_ref_iterator_begin(); if (prime_dir) - prime_ref_dir(dir); + prime_ref_dir(dir, prefix); iter = xcalloc(1, sizeof(*iter)); ref_iterator = &iter->base; @@ -515,9 +582,12 @@ struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache, level->index = -1; level->dir = dir; - if (prefix && *prefix) - ref_iterator = prefix_ref_iterator_begin(ref_iterator, - prefix, 0); + if (prefix && *prefix) { + iter->prefix = xstrdup(prefix); + level->prefix_state = PREFIX_WITHIN_DIR; + } else { + level->prefix_state = PREFIX_CONTAINS_DIR; + } return ref_iterator; } diff --git a/refs/ref-cache.h b/refs/ref-cache.h index 1f65e2f9ed..794f000fd3 100644 --- a/refs/ref-cache.h +++ b/refs/ref-cache.h @@ -185,8 +185,7 @@ struct ref_entry *create_dir_entry(struct ref_cache *cache, int incomplete); struct ref_entry *create_ref_entry(const char *refname, - const struct object_id *oid, int flag, - int check_name); + const struct object_id *oid, int flag); /* * Return a pointer to a new `ref_cache`. Its top-level starts out @@ -194,7 +193,8 @@ struct ref_entry *create_ref_entry(const char *refname, * function called to fill in incomplete directories in the * `ref_cache` when they are accessed. If it is NULL, then the whole * `ref_cache` must be filled (including clearing its directories' - * `REF_INCOMPLETE` bits) before it is used. + * `REF_INCOMPLETE` bits) before it is used, and `refs` can be NULL, + * too. */ struct ref_cache *create_ref_cache(struct ref_store *refs, fill_ref_dir_fn *fill_ref_dir); diff --git a/refs/refs-internal.h b/refs/refs-internal.h index b6b291cf00..192f9f85c9 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -170,6 +170,14 @@ int refs_read_raw_ref(struct ref_store *ref_store, struct strbuf *referent, unsigned int *type); /* + * Write an error to `err` and return a nonzero value iff the same + * refname appears multiple times in `refnames`. `refnames` must be + * sorted on entry to this function. + */ +int ref_update_reject_duplicates(struct string_list *refnames, + struct strbuf *err); + +/* * Add a ref_update with the specified properties to transaction, and * return a pointer to the new object. This function does not verify * that refname is well-formed. new_sha1 and old_sha1 are only @@ -185,17 +193,27 @@ struct ref_update *ref_transaction_add_update( /* * Transaction states. - * OPEN: The transaction is in a valid state and can accept new updates. - * An OPEN transaction can be committed. - * CLOSED: A closed transaction is no longer active and no other operations - * than free can be used on it in this state. - * A transaction can either become closed by successfully committing - * an active transaction or if there is a failure while building - * the transaction thus rendering it failed/inactive. + * + * OPEN: The transaction is initialized and new updates can still be + * added to it. An OPEN transaction can be prepared, + * committed, freed, or aborted (freeing and aborting an open + * transaction are equivalent). + * + * PREPARED: ref_transaction_prepare(), which locks all of the + * references involved in the update and checks that the + * update has no errors, has been called successfully for the + * transaction. A PREPARED transaction can be committed or + * aborted. + * + * CLOSED: The transaction is no longer active. A transaction becomes + * CLOSED if there is a failure while building the transaction + * or if a transaction is committed or aborted. A CLOSED + * transaction can only be freed. */ enum ref_transaction_state { - REF_TRANSACTION_OPEN = 0, - REF_TRANSACTION_CLOSED = 1 + REF_TRANSACTION_OPEN = 0, + REF_TRANSACTION_PREPARED = 1, + REF_TRANSACTION_CLOSED = 2 }; /* @@ -497,6 +515,18 @@ typedef struct ref_store *ref_store_init_fn(const char *gitdir, typedef int ref_init_db_fn(struct ref_store *refs, struct strbuf *err); +typedef int ref_transaction_prepare_fn(struct ref_store *refs, + struct ref_transaction *transaction, + struct strbuf *err); + +typedef int ref_transaction_finish_fn(struct ref_store *refs, + struct ref_transaction *transaction, + struct strbuf *err); + +typedef int ref_transaction_abort_fn(struct ref_store *refs, + struct ref_transaction *transaction, + struct strbuf *err); + typedef int ref_transaction_commit_fn(struct ref_store *refs, struct ref_transaction *transaction, struct strbuf *err); @@ -508,16 +538,17 @@ typedef int create_symref_fn(struct ref_store *ref_store, const char *ref_target, const char *refs_heads_master, const char *logmsg); -typedef int delete_refs_fn(struct ref_store *ref_store, +typedef int delete_refs_fn(struct ref_store *ref_store, const char *msg, struct string_list *refnames, unsigned int flags); typedef int rename_ref_fn(struct ref_store *ref_store, const char *oldref, const char *newref, const char *logmsg); /* - * Iterate over the references in the specified ref_store that are - * within find_containing_dir(prefix). If prefix is NULL or the empty - * string, iterate over all references in the submodule. + * Iterate over the references in `ref_store` whose names start with + * `prefix`. `prefix` is matched as a literal string, without regard + * for path separators. If prefix is NULL or the empty string, iterate + * over all references in `ref_store`. */ typedef struct ref_iterator *ref_iterator_begin_fn( struct ref_store *ref_store, @@ -599,7 +630,10 @@ struct ref_storage_be { const char *name; ref_store_init_fn *init; ref_init_db_fn *init_db; - ref_transaction_commit_fn *transaction_commit; + + ref_transaction_prepare_fn *transaction_prepare; + ref_transaction_finish_fn *transaction_finish; + ref_transaction_abort_fn *transaction_abort; ref_transaction_commit_fn *initial_transaction_commit; pack_refs_fn *pack_refs; |