diff options
Diffstat (limited to 'refs/files-backend.c')
-rw-r--r-- | refs/files-backend.c | 1067 |
1 files changed, 587 insertions, 480 deletions
diff --git a/refs/files-backend.c b/refs/files-backend.c index 6516c7b..a098d14 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -1,23 +1,33 @@ -#include "../cache.h" -#include "../config.h" +#include "../git-compat-util.h" +#include "../copy.h" +#include "../environment.h" +#include "../gettext.h" +#include "../hash.h" +#include "../hex.h" #include "../refs.h" #include "refs-internal.h" #include "ref-cache.h" #include "packed-backend.h" +#include "../ident.h" #include "../iterator.h" #include "../dir-iterator.h" #include "../lockfile.h" #include "../object.h" +#include "../object-file.h" +#include "../path.h" #include "../dir.h" #include "../chdir-notify.h" -#include "worktree.h" +#include "../setup.h" +#include "../wrapper.h" +#include "../write-or-die.h" +#include "../revision.h" +#include <wildmatch.h> /* * 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`. + * or REF_HAVE_OLD, which are also stored in `ref_update::flags`. */ /* @@ -39,23 +49,16 @@ #define REF_NEEDS_COMMIT (1 << 6) /* - * 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. - */ -#define REF_LOG_ONLY (1 << 7) - -/* * Used as a flag in ref_update::flags when the ref_update was via an * update to HEAD. */ #define REF_UPDATE_VIA_HEAD (1 << 8) /* - * Used as a flag in ref_update::flags when the loose reference has - * been deleted. + * Used as a flag in ref_update::flags when a reference has been + * deleted and the ref's parent directories may need cleanup. */ -#define REF_DELETED_LOOSE (1 << 9) +#define REF_DELETED_RMDIR (1 << 9) struct ref_lock { char *ref_name; @@ -67,7 +70,6 @@ struct files_ref_store { struct ref_store base; unsigned int store_flags; - char *gitdir; char *gitcommondir; struct ref_cache *loose; @@ -87,25 +89,22 @@ static void clear_loose_ref_cache(struct files_ref_store *refs) * Create a new submodule ref cache and add it to the internal * set of caches. */ -static struct ref_store *files_ref_store_create(const char *gitdir, +static struct ref_store *files_ref_store_create(struct repository *repo, + const char *gitdir, unsigned int flags) { 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); + base_ref_store_init(ref_store, repo, gitdir, &refs_be_files); refs->store_flags = flags; - - 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); + refs->packed_ref_store = + packed_ref_store_create(repo, refs->gitcommondir, flags); - chdir_notify_reparent("files-backend $GIT_DIR", - &refs->gitdir); + chdir_notify_reparent("files-backend $GIT_DIR", &refs->base.gitdir); chdir_notify_reparent("files-backend $GIT_COMMONDIR", &refs->gitcommondir); @@ -150,44 +149,30 @@ static struct files_ref_store *files_downcast(struct ref_store *ref_store, return refs; } -static void files_reflog_path_other_worktrees(struct files_ref_store *refs, - struct strbuf *sb, - const char *refname) -{ - const char *real_ref; - const char *worktree_name; - int length; - - if (parse_worktree_ref(refname, &worktree_name, &length, &real_ref)) - BUG("refname %s is not a other-worktree ref", refname); - - if (worktree_name) - strbuf_addf(sb, "%s/worktrees/%.*s/logs/%s", refs->gitcommondir, - length, worktree_name, real_ref); - else - strbuf_addf(sb, "%s/logs/%s", refs->gitcommondir, - real_ref); -} - static void files_reflog_path(struct files_ref_store *refs, struct strbuf *sb, const char *refname) { - switch (ref_type(refname)) { - case REF_TYPE_PER_WORKTREE: - case REF_TYPE_PSEUDOREF: - strbuf_addf(sb, "%s/logs/%s", refs->gitdir, refname); + const char *bare_refname; + const char *wtname; + int wtname_len; + enum ref_worktree_type wt_type = parse_worktree_ref( + refname, &wtname, &wtname_len, &bare_refname); + + switch (wt_type) { + case REF_WORKTREE_CURRENT: + strbuf_addf(sb, "%s/logs/%s", refs->base.gitdir, refname); break; - case REF_TYPE_OTHER_PSEUDOREF: - case REF_TYPE_MAIN_PSEUDOREF: - files_reflog_path_other_worktrees(refs, sb, refname); + case REF_WORKTREE_SHARED: + case REF_WORKTREE_MAIN: + strbuf_addf(sb, "%s/logs/%s", refs->gitcommondir, bare_refname); break; - case REF_TYPE_NORMAL: - strbuf_addf(sb, "%s/logs/%s", refs->gitcommondir, refname); + case REF_WORKTREE_OTHER: + strbuf_addf(sb, "%s/worktrees/%.*s/logs/%s", refs->gitcommondir, + wtname_len, wtname, bare_refname); break; default: - BUG("unknown ref type %d of ref %s", - ref_type(refname), refname); + BUG("unknown ref type %d of ref %s", wt_type, refname); } } @@ -195,22 +180,25 @@ static void files_ref_path(struct files_ref_store *refs, struct strbuf *sb, const char *refname) { - switch (ref_type(refname)) { - case REF_TYPE_PER_WORKTREE: - case REF_TYPE_PSEUDOREF: - strbuf_addf(sb, "%s/%s", refs->gitdir, refname); + const char *bare_refname; + const char *wtname; + int wtname_len; + enum ref_worktree_type wt_type = parse_worktree_ref( + refname, &wtname, &wtname_len, &bare_refname); + switch (wt_type) { + case REF_WORKTREE_CURRENT: + strbuf_addf(sb, "%s/%s", refs->base.gitdir, refname); + break; + case REF_WORKTREE_OTHER: + strbuf_addf(sb, "%s/worktrees/%.*s/%s", refs->gitcommondir, + wtname_len, wtname, bare_refname); break; - case REF_TYPE_MAIN_PSEUDOREF: - if (!skip_prefix(refname, "main-worktree/", &refname)) - BUG("ref %s is not a main pseudoref", refname); - /* fallthrough */ - case REF_TYPE_OTHER_PSEUDOREF: - case REF_TYPE_NORMAL: - strbuf_addf(sb, "%s/%s", refs->gitcommondir, refname); + case REF_WORKTREE_SHARED: + case REF_WORKTREE_MAIN: + strbuf_addf(sb, "%s/%s", refs->gitcommondir, bare_refname); break; default: - BUG("unknown ref type %d of ref %s", - ref_type(refname), refname); + BUG("unknown ref type %d of ref %s", wt_type, refname); } } @@ -236,11 +224,43 @@ static void add_per_worktree_entries_to_dir(struct ref_dir *dir, const char *dir pos = search_ref_dir(dir, prefix, prefix_len); if (pos >= 0) continue; - child_entry = create_dir_entry(dir->cache, prefix, prefix_len, 1); + child_entry = create_dir_entry(dir->cache, prefix, prefix_len); add_entry_to_dir(dir, child_entry); } } +static void loose_fill_ref_dir_regular_file(struct files_ref_store *refs, + const char *refname, + struct ref_dir *dir) +{ + struct object_id oid; + int flag; + + if (!refs_resolve_ref_unsafe(&refs->base, refname, RESOLVE_REF_READING, + &oid, &flag)) { + oidclr(&oid); + flag |= REF_ISBROKEN; + } else if (is_null_oid(&oid)) { + /* + * It is so astronomically unlikely + * that null_oid is the OID of an + * actual object that we consider its + * appearance in a loose reference + * file to be repo corruption + * (probably due to a software bug). + */ + flag |= REF_ISBROKEN; + } + + if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) { + if (!refname_is_safe(refname)) + die("loose refname is dangerous: %s", refname); + oidclr(&oid); + flag |= REF_BAD_NAME | REF_ISBROKEN; + } + add_entry_to_dir(dir, create_ref_entry(refname, &oid, flag)); +} + /* * Read the loose references from the namespace dirname into dir * (without recursing). dirname must end with '/'. dir must be the @@ -256,10 +276,8 @@ static void loose_fill_ref_dir(struct ref_store *ref_store, int dirnamelen = strlen(dirname); struct strbuf refname; struct strbuf path = STRBUF_INIT; - size_t path_baselen; files_ref_path(refs, &path, dirname); - path_baselen = path.len; d = opendir(path.buf); if (!d) { @@ -271,54 +289,24 @@ static void loose_fill_ref_dir(struct ref_store *ref_store, strbuf_add(&refname, dirname, dirnamelen); while ((de = readdir(d)) != NULL) { - struct object_id oid; - struct stat st; - int flag; + unsigned char dtype; if (de->d_name[0] == '.') continue; if (ends_with(de->d_name, ".lock")) continue; strbuf_addstr(&refname, de->d_name); - strbuf_addstr(&path, de->d_name); - if (stat(path.buf, &st) < 0) { - ; /* silently ignore */ - } else if (S_ISDIR(st.st_mode)) { + + dtype = get_dtype(de, &path, 1); + if (dtype == DT_DIR) { strbuf_addch(&refname, '/'); add_entry_to_dir(dir, create_dir_entry(dir->cache, refname.buf, - refname.len, 1)); - } else { - if (!refs_resolve_ref_unsafe(&refs->base, - refname.buf, - RESOLVE_REF_READING, - &oid, &flag)) { - oidclr(&oid); - flag |= REF_ISBROKEN; - } else if (is_null_oid(&oid)) { - /* - * It is so astronomically unlikely - * that null_oid is the OID of an - * actual object that we consider its - * appearance in a loose reference - * file to be repo corruption - * (probably due to a software bug). - */ - flag |= REF_ISBROKEN; - } - - if (check_refname_format(refname.buf, - REFNAME_ALLOW_ONELEVEL)) { - if (!refname_is_safe(refname.buf)) - die("loose refname is dangerous: %s", refname.buf); - oidclr(&oid); - flag |= REF_BAD_NAME | REF_ISBROKEN; - } - add_entry_to_dir(dir, - create_ref_entry(refname.buf, &oid, flag)); + refname.len)); + } else if (dtype == DT_REG) { + loose_fill_ref_dir_regular_file(refs, refname.buf, dir); } strbuf_setlen(&refname, dirnamelen); - strbuf_setlen(&path, path_baselen); } strbuf_release(&refname); strbuf_release(&path); @@ -327,9 +315,59 @@ static void loose_fill_ref_dir(struct ref_store *ref_store, add_per_worktree_entries_to_dir(dir, dirname); } -static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs) +/* + * Add pseudorefs to the ref dir by parsing the directory for any files + * which follow the pseudoref syntax. + */ +static void add_pseudoref_and_head_entries(struct ref_store *ref_store, + struct ref_dir *dir, + const char *dirname) +{ + struct files_ref_store *refs = + files_downcast(ref_store, REF_STORE_READ, "fill_ref_dir"); + struct strbuf path = STRBUF_INIT, refname = STRBUF_INIT; + struct dirent *de; + size_t dirnamelen; + DIR *d; + + files_ref_path(refs, &path, dirname); + + d = opendir(path.buf); + if (!d) { + strbuf_release(&path); + return; + } + + strbuf_addstr(&refname, dirname); + dirnamelen = refname.len; + + while ((de = readdir(d)) != NULL) { + unsigned char dtype; + + if (de->d_name[0] == '.') + continue; + if (ends_with(de->d_name, ".lock")) + continue; + strbuf_addstr(&refname, de->d_name); + + dtype = get_dtype(de, &path, 1); + if (dtype == DT_REG && (is_pseudoref(ref_store, de->d_name) || + is_headref(ref_store, de->d_name))) + loose_fill_ref_dir_regular_file(refs, refname.buf, dir); + + strbuf_setlen(&refname, dirnamelen); + } + strbuf_release(&refname); + strbuf_release(&path); + closedir(d); +} + +static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs, + unsigned int flags) { if (!refs->loose) { + struct ref_dir *dir; + /* * Mark the top-level directory complete because we * are about to read the only subdirectory that can @@ -340,19 +378,24 @@ static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs) /* We're going to fill the top level ourselves: */ refs->loose->root->flag &= ~REF_INCOMPLETE; + dir = get_ref_dir(refs->loose->root); + + if (flags & DO_FOR_EACH_INCLUDE_ROOT_REFS) + add_pseudoref_and_head_entries(dir->cache->ref_store, dir, + refs->loose->root->name); + /* * 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)); + add_entry_to_dir(dir, create_dir_entry(refs->loose, "refs/", 5)); } return refs->loose; } -static int files_read_raw_ref(struct ref_store *ref_store, - const char *refname, struct object_id *oid, - struct strbuf *referent, unsigned int *type) +static int read_ref_internal(struct ref_store *ref_store, const char *refname, + struct object_id *oid, struct strbuf *referent, + unsigned int *type, int *failure_errno, int skip_packed_refs) { struct files_ref_store *refs = files_downcast(ref_store, REF_STORE_READ, "read_raw_ref"); @@ -360,12 +403,11 @@ static int files_read_raw_ref(struct ref_store *ref_store, struct strbuf sb_path = STRBUF_INIT; const char *path; const char *buf; - const char *p; struct stat st; int fd; int ret = -1; - int save_errno; int remaining_retries = 3; + int myerr = 0; *type = 0; strbuf_reset(&sb_path); @@ -392,11 +434,13 @@ stat_ref: goto out; if (lstat(path, &st) < 0) { - if (errno != ENOENT) + int ignore_errno; + myerr = errno; + if (myerr != ENOENT || skip_packed_refs) goto out; - if (refs_read_raw_ref(refs->packed_ref_store, refname, - oid, referent, type)) { - errno = ENOENT; + if (refs_read_raw_ref(refs->packed_ref_store, refname, oid, + referent, type, &ignore_errno)) { + myerr = ENOENT; goto out; } ret = 0; @@ -407,7 +451,8 @@ stat_ref: if (S_ISLNK(st.st_mode)) { strbuf_reset(&sb_contents); if (strbuf_readlink(&sb_contents, path, st.st_size) < 0) { - if (errno == ENOENT || errno == EINVAL) + myerr = errno; + if (myerr == ENOENT || myerr == EINVAL) /* inconsistent with lstat; retry */ goto stat_ref; else @@ -429,14 +474,16 @@ stat_ref: /* Is it a directory? */ if (S_ISDIR(st.st_mode)) { + int ignore_errno; /* * Even though there is a directory where the loose * ref is supposed to be, there could still be a * packed ref: */ - if (refs_read_raw_ref(refs->packed_ref_store, refname, - oid, referent, type)) { - errno = EISDIR; + if (skip_packed_refs || + refs_read_raw_ref(refs->packed_ref_store, refname, oid, + referent, type, &ignore_errno)) { + myerr = EISDIR; goto out; } ret = 0; @@ -449,7 +496,8 @@ stat_ref: */ fd = open(path, O_RDONLY); if (fd < 0) { - if (errno == ENOENT && !S_ISLNK(st.st_mode)) + myerr = errno; + if (myerr == ENOENT && !S_ISLNK(st.st_mode)) /* inconsistent with lstat; retry */ goto stat_ref; else @@ -457,14 +505,53 @@ stat_ref: } strbuf_reset(&sb_contents); if (strbuf_read(&sb_contents, fd, 256) < 0) { - int save_errno = errno; + myerr = errno; close(fd); - errno = save_errno; goto out; } close(fd); strbuf_rtrim(&sb_contents); buf = sb_contents.buf; + + ret = parse_loose_ref_contents(buf, oid, referent, type, &myerr); + +out: + if (ret && !myerr) + BUG("returning non-zero %d, should have set myerr!", ret); + *failure_errno = myerr; + + strbuf_release(&sb_path); + strbuf_release(&sb_contents); + errno = 0; + return ret; +} + +static int files_read_raw_ref(struct ref_store *ref_store, const char *refname, + struct object_id *oid, struct strbuf *referent, + unsigned int *type, int *failure_errno) +{ + return read_ref_internal(ref_store, refname, oid, referent, type, failure_errno, 0); +} + +static int files_read_symbolic_ref(struct ref_store *ref_store, const char *refname, + struct strbuf *referent) +{ + struct object_id oid; + int failure_errno, ret; + unsigned int type; + + ret = read_ref_internal(ref_store, refname, &oid, referent, &type, &failure_errno, 1); + if (ret) + return ret; + + return !(type & REF_ISSYMREF); +} + +int parse_loose_ref_contents(const char *buf, struct object_id *oid, + struct strbuf *referent, unsigned int *type, + int *failure_errno) +{ + const char *p; if (skip_prefix(buf, "ref:", &buf)) { while (isspace(*buf)) buf++; @@ -472,29 +559,19 @@ stat_ref: strbuf_reset(referent); strbuf_addstr(referent, buf); *type |= REF_ISSYMREF; - ret = 0; - goto out; + return 0; } /* - * Please note that FETCH_HEAD has additional - * data after the sha. + * FETCH_HEAD has additional data after the sha. */ if (parse_oid_hex(buf, oid, &p) || (*p != '\0' && !isspace(*p))) { *type |= REF_ISBROKEN; - errno = EINVAL; - goto out; + *failure_errno = EINVAL; + return -1; } - - ret = 0; - -out: - save_errno = errno; - strbuf_release(&sb_path); - strbuf_release(&sb_contents); - errno = save_errno; - return ret; + return 0; } static void unlock_ref(struct ref_lock *lock) @@ -536,7 +613,6 @@ static void unlock_ref(struct ref_lock *lock) static int lock_raw_ref(struct files_ref_store *refs, const char *refname, int mustexist, const struct string_list *extras, - const struct string_list *skip, struct ref_lock **lock_p, struct strbuf *referent, unsigned int *type, @@ -546,6 +622,7 @@ static int lock_raw_ref(struct files_ref_store *refs, struct strbuf ref_file = STRBUF_INIT; int attempts_remaining = 3; int ret = TRANSACTION_GENERIC_ERROR; + int failure_errno; assert(err); files_assert_main_repository(refs, "lock_raw_ref"); @@ -554,7 +631,7 @@ static int lock_raw_ref(struct files_ref_store *refs, /* First lock the file so it can't change out from under us. */ - *lock_p = lock = xcalloc(1, sizeof(*lock)); + *lock_p = CALLOC_ARRAY(lock, 1); lock->ref_name = xstrdup(refname); files_ref_path(refs, &ref_file, refname); @@ -573,7 +650,7 @@ retry: * reason to expect this error to be transitory. */ if (refs_verify_refname_available(&refs->base, refname, - extras, skip, err)) { + extras, NULL, err)) { if (mustexist) { /* * To the user the relevant error is @@ -616,7 +693,9 @@ retry: 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) { + int myerr = errno; + errno = 0; + if (myerr == ENOENT && --attempts_remaining > 0) { /* * Maybe somebody just deleted one of the * directories leading to ref_file. Try @@ -624,7 +703,7 @@ retry: */ goto retry; } else { - unable_to_lock_message(ref_file.buf, errno, err); + unable_to_lock_message(ref_file.buf, myerr, err); goto error_return; } } @@ -634,9 +713,9 @@ retry: * fear that its value will change. */ - if (files_read_raw_ref(&refs->base, refname, - &lock->old_oid, referent, type)) { - if (errno == ENOENT) { + if (files_read_raw_ref(&refs->base, refname, &lock->old_oid, referent, + type, &failure_errno)) { + if (failure_errno == ENOENT) { if (mustexist) { /* Garden variety missing reference. */ strbuf_addf(err, "unable to resolve reference '%s'", @@ -660,7 +739,7 @@ retry: * reference named "refs/foo/bar/baz". */ } - } else if (errno == EISDIR) { + } else if (failure_errno == EISDIR) { /* * There is a directory in the way. It might have * contained references that have been deleted. If @@ -678,7 +757,7 @@ retry: REMOVE_DIR_EMPTY_ONLY)) { if (refs_verify_refname_available( &refs->base, refname, - extras, skip, err)) { + extras, NULL, err)) { /* * The error message set by * verify_refname_available() is OK. @@ -698,13 +777,13 @@ retry: goto error_return; } } - } else if (errno == EINVAL && (*type & REF_ISBROKEN)) { + } else if (failure_errno == EINVAL && (*type & REF_ISBROKEN)) { strbuf_addf(err, "unable to resolve reference '%s': " "reference broken", refname); goto error_return; } else { strbuf_addf(err, "unable to resolve reference '%s': %s", - refname, strerror(errno)); + refname, strerror(failure_errno)); goto error_return; } @@ -715,7 +794,7 @@ retry: */ if (refs_verify_refname_available( refs->packed_ref_store, refname, - extras, skip, err)) + extras, NULL, err)) goto error_return; } @@ -735,6 +814,7 @@ struct files_ref_iterator { struct ref_iterator base; struct ref_iterator *iter0; + struct repository *repo; unsigned int flags; }; @@ -746,11 +826,18 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator) 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) + parse_worktree_ref(iter->iter0->refname, NULL, NULL, + NULL) != REF_WORKTREE_CURRENT) + continue; + + if ((iter->flags & DO_FOR_EACH_OMIT_DANGLING_SYMREFS) && + (iter->iter0->flags & REF_ISSYMREF) && + (iter->iter0->flags & REF_ISBROKEN)) continue; if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) && !ref_resolves_to_object(iter->iter0->refname, + iter->repo, iter->iter0->oid, iter->iter0->flags)) continue; @@ -791,14 +878,15 @@ static int files_ref_iterator_abort(struct ref_iterator *ref_iterator) } static struct ref_iterator_vtable files_ref_iterator_vtable = { - files_ref_iterator_advance, - files_ref_iterator_peel, - files_ref_iterator_abort + .advance = files_ref_iterator_advance, + .peel = files_ref_iterator_peel, + .abort = files_ref_iterator_abort, }; static struct ref_iterator *files_ref_iterator_begin( struct ref_store *ref_store, - const char *prefix, unsigned int flags) + const char *prefix, const char **exclude_patterns, + unsigned int flags) { struct files_ref_store *refs; struct ref_iterator *loose_iter, *packed_iter, *overlay_iter; @@ -828,8 +916,8 @@ static struct ref_iterator *files_ref_iterator_begin( * disk, and re-reads it if not. */ - loose_iter = cache_ref_iterator_begin(get_loose_ref_cache(refs), - prefix, 1); + loose_iter = cache_ref_iterator_begin(get_loose_ref_cache(refs, flags), + prefix, ref_store->repo, 1); /* * The packed-refs file might contain broken references, for @@ -843,55 +931,128 @@ static struct ref_iterator *files_ref_iterator_begin( * the packed and loose references. */ packed_iter = refs_ref_iterator_begin( - refs->packed_ref_store, prefix, 0, + refs->packed_ref_store, prefix, exclude_patterns, 0, DO_FOR_EACH_INCLUDE_BROKEN); overlay_iter = overlay_ref_iterator_begin(loose_iter, packed_iter); - iter = xcalloc(1, sizeof(*iter)); + CALLOC_ARRAY(iter, 1); ref_iterator = &iter->base; - base_ref_iterator_init(ref_iterator, &files_ref_iterator_vtable, - overlay_iter->ordered); + base_ref_iterator_init(ref_iterator, &files_ref_iterator_vtable); iter->iter0 = overlay_iter; + iter->repo = ref_store->repo; iter->flags = flags; return ref_iterator; } /* - * 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. + * Callback function for raceproof_create_file(). This function is + * expected to do something that makes dirname(path) permanent despite + * the fact that other processes might be cleaning up empty + * directories at the same time. Usually it will create a file named + * path, but alternatively it could create another file in that + * directory, or even chdir() into that directory. The function should + * return 0 if the action was completed successfully. On error, it + * should return a nonzero result and set errno. + * raceproof_create_file() treats two errno values specially: + * + * - ENOENT -- dirname(path) does not exist. In this case, + * raceproof_create_file() tries creating dirname(path) + * (and any parent directories, if necessary) and calls + * the function again. + * + * - EISDIR -- the file already exists and is a directory. In this + * case, raceproof_create_file() removes the directory if + * it is empty (and recursively any empty directories that + * it contains) and calls the function again. + * + * Any other errno causes raceproof_create_file() to fail with the + * callback's return value and errno. + * + * Obviously, this function should be OK with being called again if it + * fails with ENOENT or EISDIR. In other scenarios it will not be + * called again. */ -static int verify_lock(struct ref_store *ref_store, struct ref_lock *lock, - const struct object_id *old_oid, int mustexist, - struct strbuf *err) +typedef int create_file_fn(const char *path, void *cb); + +/* + * Create a file in dirname(path) by calling fn, creating leading + * directories if necessary. Retry a few times in case we are racing + * with another process that is trying to clean up the directory that + * contains path. See the documentation for create_file_fn for more + * details. + * + * Return the value and set the errno that resulted from the most + * recent call of fn. fn is always called at least once, and will be + * called more than once if it returns ENOENT or EISDIR. + */ +static int raceproof_create_file(const char *path, create_file_fn fn, void *cb) { - assert(err); + /* + * The number of times we will try to remove empty directories + * in the way of path. This is only 1 because if another + * process is racily creating directories that conflict with + * us, we don't want to fight against them. + */ + int remove_directories_remaining = 1; - 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; - return -1; - } else { - oidclr(&lock->old_oid); - return 0; - } - } - if (old_oid && !oideq(&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), - oid_to_hex(old_oid)); - errno = EBUSY; - return -1; + /* + * The number of times that we will try to create the + * directories containing path. We are willing to attempt this + * more than once, because another process could be trying to + * clean up empty directories at the same time as we are + * trying to create them. + */ + int create_directories_remaining = 3; + + /* A scratch copy of path, filled lazily if we need it: */ + struct strbuf path_copy = STRBUF_INIT; + + int ret, save_errno; + + /* Sanity check: */ + assert(*path); + +retry_fn: + ret = fn(path, cb); + save_errno = errno; + if (!ret) + goto out; + + if (errno == EISDIR && remove_directories_remaining-- > 0) { + /* + * A directory is in the way. Maybe it is empty; try + * to remove it: + */ + if (!path_copy.len) + strbuf_addstr(&path_copy, path); + + if (!remove_dir_recursively(&path_copy, REMOVE_DIR_EMPTY_ONLY)) + goto retry_fn; + } else if (errno == ENOENT && create_directories_remaining-- > 0) { + /* + * Maybe the containing directory didn't exist, or + * maybe it was just deleted by a process that is + * racing with us to clean up empty directories. Try + * to create it: + */ + enum scld_error scld_result; + + if (!path_copy.len) + strbuf_addstr(&path_copy, path); + + do { + scld_result = safe_create_leading_directories(path_copy.buf); + if (scld_result == SCLD_OK) + goto retry_fn; + } while (scld_result == SCLD_VANISHED && create_directories_remaining-- > 0); } - return 0; + +out: + strbuf_release(&path_copy); + errno = save_errno; + return ret; } static int remove_empty_directories(struct strbuf *path) @@ -915,67 +1076,20 @@ static int create_reflock(const char *path, void *cb) /* * 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_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 mustexist = (old_oid && !is_null_oid(old_oid)); - int resolve_flags = RESOLVE_REF_NO_RECURSE; - int resolved; files_assert_main_repository(refs, "lock_ref_oid_basic"); assert(err); - lock = xcalloc(1, sizeof(struct ref_lock)); - - if (mustexist) - resolve_flags |= RESOLVE_REF_READING; - if (flags & REF_DELETING) - resolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME; + CALLOC_ARRAY(lock, 1); files_ref_path(refs, &ref_file, refname); - resolved = !!refs_resolve_ref_unsafe(&refs->base, - refname, resolve_flags, - &lock->old_oid, type); - if (!resolved && errno == EISDIR) { - /* - * we are trying to lock foo but we used to - * have foo/bar which now does not exist; - * it is normal for the empty directory 'foo' - * to remain. - */ - if (remove_empty_directories(&ref_file)) { - last_errno = errno; - 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 = !!refs_resolve_ref_unsafe(&refs->base, - refname, resolve_flags, - &lock->old_oid, type); - } - if (!resolved) { - last_errno = errno; - if (last_errno != ENOTDIR || - !refs_verify_refname_available(&refs->base, refname, - extras, skip, err)) - strbuf_addf(err, "unable to resolve reference '%s': %s", - refname, strerror(last_errno)); - - goto error_return; - } /* * If the ref did not exist and we are creating it, make sure @@ -985,23 +1099,19 @@ static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs, */ if (is_null_oid(&lock->old_oid) && refs_verify_refname_available(refs->packed_ref_store, refname, - extras, skip, err)) { - last_errno = ENOTDIR; + NULL, NULL, err)) goto error_return; - } lock->ref_name = xstrdup(refname); if (raceproof_create_file(ref_file.buf, create_reflock, &lock->lk)) { - last_errno = errno; unable_to_lock_message(ref_file.buf, errno, err); goto error_return; } - if (verify_lock(&refs->base, lock, old_oid, mustexist, err)) { - last_errno = errno; - goto error_return; - } + if (!refs_resolve_ref_unsafe(&refs->base, lock->ref_name, 0, + &lock->old_oid, NULL)) + oidclr(&lock->old_oid); goto out; error_return: @@ -1010,7 +1120,6 @@ static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs, out: strbuf_release(&ref_file); - errno = last_errno; return lock; } @@ -1089,7 +1198,7 @@ static void prune_ref(struct files_ref_store *refs, struct ref_to_prune *r) ref_transaction_add_update( transaction, r->name, REF_NO_DEREF | REF_HAVE_NEW | REF_HAVE_OLD | REF_IS_PRUNING, - &null_oid, &r->oid, NULL); + null_oid(), &r->oid, NULL); if (ref_transaction_commit(transaction, &err)) goto cleanup; @@ -1122,14 +1231,13 @@ static void prune_refs(struct files_ref_store *refs, struct ref_to_prune **refs_ */ static int should_pack_ref(const char *refname, const struct object_id *oid, unsigned int ref_flags, - unsigned int pack_flags) + struct pack_refs_opts *opts) { - /* Do not pack per-worktree refs: */ - if (ref_type(refname) != REF_TYPE_NORMAL) - return 0; + struct string_list_item *item; - /* Do not pack non-tags unless PACK_REFS_ALL is set: */ - if (!(pack_flags & PACK_REFS_ALL) && !starts_with(refname, "refs/tags/")) + /* Do not pack per-worktree refs: */ + if (parse_worktree_ref(refname, NULL, NULL, NULL) != + REF_WORKTREE_SHARED) return 0; /* Do not pack symbolic refs: */ @@ -1137,13 +1245,21 @@ static int should_pack_ref(const char *refname, return 0; /* Do not pack broken refs: */ - if (!ref_resolves_to_object(refname, oid, ref_flags)) + if (!ref_resolves_to_object(refname, the_repository, oid, ref_flags)) return 0; - return 1; + if (ref_excluded(opts->exclusions, refname)) + return 0; + + for_each_string_list_item(item, opts->includes) + if (!wildmatch(item->string, refname, 0)) + return 1; + + return 0; } -static int files_pack_refs(struct ref_store *ref_store, unsigned int flags) +static int files_pack_refs(struct ref_store *ref_store, + struct pack_refs_opts *opts) { struct files_ref_store *refs = files_downcast(ref_store, REF_STORE_WRITE | REF_STORE_ODB, @@ -1160,15 +1276,15 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags) packed_refs_lock(refs->packed_ref_store, LOCK_DIE_ON_ERROR, &err); - iter = cache_ref_iterator_begin(get_loose_ref_cache(refs), NULL, 0); + iter = cache_ref_iterator_begin(get_loose_ref_cache(refs, 0), NULL, + the_repository, 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)) + if (!should_pack_ref(iter->refname, iter->oid, iter->flags, opts)) continue; /* @@ -1182,7 +1298,7 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags) iter->refname, err.buf); /* Schedule the loose reference for pruning if requested. */ - if ((flags & PACK_REFS_PRUNE)) { + if ((opts->flags & PACK_REFS_PRUNE)) { struct ref_to_prune *n; FLEX_ALLOC_STR(n, name, iter->refname); oidcpy(&n->oid, iter->oid); @@ -1205,54 +1321,6 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags) return 0; } -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; - - if (packed_refs_lock(refs->packed_ref_store, 0, &err)) - goto error; - - 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 (refs_delete_ref(&refs->base, msg, refname, NULL, flags)) - result |= error(_("could not remove reference %s"), refname); - } - - 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; -} - /* * People using contrib's git-new-workdir have .git/logs/refs -> * /some/other/path/.git/logs/refs, and that may live on another device. @@ -1314,12 +1382,42 @@ static int rename_tmp_log(struct files_ref_store *refs, const char *newrefname) } static int write_ref_to_lockfile(struct ref_lock *lock, - const struct object_id *oid, struct strbuf *err); + const struct object_id *oid, + int skip_oid_verification, 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); +/* + * Emit a better error message than lockfile.c's + * unable_to_lock_message() would in case there is a D/F conflict with + * another existing reference. If there would be a conflict, emit an error + * message and return false; otherwise, return true. + * + * Note that this function is not safe against all races with other + * processes, and that's not its job. We'll emit a more verbose error on D/f + * conflicts if we get past it into lock_ref_oid_basic(). + */ +static int refs_rename_ref_available(struct ref_store *refs, + const char *old_refname, + const char *new_refname) +{ + struct string_list skip = STRING_LIST_INIT_NODUP; + struct strbuf err = STRBUF_INIT; + int ok; + + string_list_insert(&skip, old_refname); + ok = !refs_verify_refname_available(refs, new_refname, + NULL, &skip, &err); + if (!ok) + error("%s", err.buf); + + string_list_clear(&skip, 0); + strbuf_release(&err); + return ok; +} + static int files_copy_or_rename_ref(struct ref_store *ref_store, const char *oldrefname, const char *newrefname, const char *logmsg, int copy) @@ -1348,7 +1446,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store, if (!refs_resolve_ref_unsafe(&refs->base, oldrefname, RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, - &orig_oid, &flag)) { + &orig_oid, &flag)) { ret = error("refname %s not found", oldrefname); goto out; } @@ -1392,9 +1490,9 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store, * the safety anyway; we want to delete the reference whatever * its current value. */ - if (!copy && !refs_read_ref_full(&refs->base, newrefname, - RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, - NULL, NULL) && + if (!copy && refs_resolve_ref_unsafe(&refs->base, newrefname, + RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, + NULL, NULL) && refs_delete_ref(&refs->base, NULL, newrefname, NULL, REF_NO_DEREF)) { if (errno == EISDIR) { @@ -1420,8 +1518,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store, logmoved = log; - lock = lock_ref_oid_basic(refs, newrefname, NULL, NULL, NULL, - REF_NO_DEREF, NULL, &err); + lock = lock_ref_oid_basic(refs, newrefname, &err); if (!lock) { if (copy) error("unable to copy '%s' to '%s': %s", oldrefname, newrefname, err.buf); @@ -1432,7 +1529,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store, } oidcpy(&lock->old_oid, &orig_oid); - if (write_ref_to_lockfile(lock, &orig_oid, &err) || + if (write_ref_to_lockfile(lock, &orig_oid, 0, &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); @@ -1443,8 +1540,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store, goto out; rollback: - lock = lock_ref_oid_basic(refs, oldrefname, NULL, NULL, NULL, - REF_NO_DEREF, NULL, &err); + lock = lock_ref_oid_basic(refs, oldrefname, &err); if (!lock) { error("unable to lock %s for rollback: %s", oldrefname, err.buf); strbuf_release(&err); @@ -1453,7 +1549,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store, flag = log_all_ref_updates; log_all_ref_updates = LOG_REFS_NONE; - if (write_ref_to_lockfile(lock, &orig_oid, &err) || + if (write_ref_to_lockfile(lock, &orig_oid, 0, &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); @@ -1574,7 +1670,7 @@ static int log_ref_setup(struct files_ref_store *refs, goto error; } } else { - *logfd = open(logfile, O_APPEND | O_WRONLY, 0666); + *logfd = open(logfile, O_APPEND | O_WRONLY); if (*logfd < 0) { if (errno == ENOENT || errno == EISDIR) { /* @@ -1603,15 +1699,14 @@ error: return -1; } -static int files_create_reflog(struct ref_store *ref_store, - const char *refname, int force_create, +static int files_create_reflog(struct ref_store *ref_store, const char *refname, struct strbuf *err) { struct files_ref_store *refs = files_downcast(ref_store, REF_STORE_WRITE, "create_reflog"); int fd; - if (log_ref_setup(refs, refname, force_create, &fd, err)) + if (log_ref_setup(refs, refname, 1, &fd, err)) return -1; if (fd >= 0) @@ -1628,8 +1723,10 @@ static int log_ref_write_fd(int fd, const struct object_id *old_oid, int ret = 0; strbuf_addf(&sb, "%s %s %s", oid_to_hex(old_oid), oid_to_hex(new_oid), committer); - if (msg && *msg) - copy_reflog_msg(&sb, msg); + if (msg && *msg) { + strbuf_addch(&sb, '\t'); + strbuf_addstr(&sb, msg); + } strbuf_addch(&sb, '\n'); if (write_in_full(fd, sb.buf, sb.len) < 0) ret = -1; @@ -1687,30 +1784,36 @@ static int files_log_ref_write(struct files_ref_store *refs, * errors, rollback the lockfile, fill in *err and return -1. */ static int write_ref_to_lockfile(struct ref_lock *lock, - const struct object_id *oid, struct strbuf *err) + const struct object_id *oid, + int skip_oid_verification, struct strbuf *err) { static char term = '\n'; struct object *o; int fd; - o = parse_object(the_repository, oid); - if (!o) { - strbuf_addf(err, - "trying to write ref '%s' with nonexistent object %s", - lock->ref_name, oid_to_hex(oid)); - unlock_ref(lock); - return -1; - } - if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) { - strbuf_addf(err, - "trying to write non-commit object %s to branch '%s'", - oid_to_hex(oid), lock->ref_name); - unlock_ref(lock); - return -1; + if (!skip_oid_verification) { + o = parse_object(the_repository, oid); + if (!o) { + strbuf_addf( + err, + "trying to write ref '%s' with nonexistent object %s", + lock->ref_name, oid_to_hex(oid)); + unlock_ref(lock); + return -1; + } + if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) { + strbuf_addf( + err, + "trying to write non-commit object %s to branch '%s'", + oid_to_hex(oid), lock->ref_name); + unlock_ref(lock); + return -1; + } } fd = get_lock_file_fd(&lock->lk); if (write_in_full(fd, oid_to_hex(oid), the_hash_algo->hexsz) < 0 || write_in_full(fd, &term, 1) < 0 || + fsync_component(FSYNC_COMPONENT_REFERENCE, get_lock_file_fd(&lock->lk)) < 0 || close_ref_gently(lock) < 0) { strbuf_addf(err, "couldn't write '%s'", get_lock_file_path(&lock->lk)); @@ -1806,9 +1909,10 @@ static void update_symref_reflog(struct files_ref_store *refs, { struct strbuf err = STRBUF_INIT; struct object_id new_oid; + if (logmsg && - !refs_read_ref_full(&refs->base, target, - RESOLVE_REF_READING, &new_oid, NULL) && + refs_resolve_ref_unsafe(&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); @@ -1827,12 +1931,12 @@ static int create_symref_locked(struct files_ref_store *refs, if (!fdopen_lock_file(&lock->lk, "w")) return error("unable to fdopen %s: %s", - lock->lk.tempfile->filename.buf, strerror(errno)); + get_lock_file_path(&lock->lk), strerror(errno)); 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(get_lock_file_fp(&lock->lk), "ref: %s\n", target); if (commit_ref(lock) < 0) return error("unable to write symref for %s: %s", refname, strerror(errno)); @@ -1849,9 +1953,7 @@ static int files_create_symref(struct ref_store *ref_store, struct ref_lock *lock; int ret; - lock = lock_ref_oid_basic(refs, refname, NULL, - NULL, NULL, REF_NO_DEREF, NULL, - &err); + lock = lock_ref_oid_basic(refs, refname, &err); if (!lock) { error("%s", err.buf); strbuf_release(&err); @@ -2072,10 +2174,8 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store, struct files_reflog_iterator { struct ref_iterator base; - struct ref_store *ref_store; struct dir_iterator *dir_iterator; - struct object_id oid; }; static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator) @@ -2086,25 +2186,13 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator) int ok; while ((ok = dir_iterator_advance(diter)) == ITER_OK) { - int flags; - if (!S_ISREG(diter->st.st_mode)) continue; - if (diter->basename[0] == '.') - continue; - if (ends_with(diter->basename, ".lock")) - continue; - - if (refs_read_ref_full(iter->ref_store, - diter->relative_path, 0, - &iter->oid, &flags)) { - error("bad ref for %s", diter->path.buf); + if (check_refname_format(diter->basename, + REFNAME_ALLOW_ONELEVEL)) continue; - } iter->base.refname = diter->relative_path; - iter->base.oid = &iter->oid; - iter->base.flags = flags; return ITER_OK; } @@ -2114,8 +2202,8 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator) return ok; } -static int files_reflog_iterator_peel(struct ref_iterator *ref_iterator, - struct object_id *peeled) +static int files_reflog_iterator_peel(struct ref_iterator *ref_iterator UNUSED, + struct object_id *peeled UNUSED) { BUG("ref_iterator_peel() called for reflog_iterator"); } @@ -2134,9 +2222,9 @@ static int files_reflog_iterator_abort(struct ref_iterator *ref_iterator) } static struct ref_iterator_vtable files_reflog_iterator_vtable = { - files_reflog_iterator_advance, - files_reflog_iterator_peel, - files_reflog_iterator_abort + .advance = files_reflog_iterator_advance, + .peel = files_reflog_iterator_peel, + .abort = files_reflog_iterator_abort, }; static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store, @@ -2149,16 +2237,16 @@ static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store, strbuf_addf(&sb, "%s/logs", gitdir); - diter = dir_iterator_begin(sb.buf, 0); + diter = dir_iterator_begin(sb.buf, DIR_ITERATOR_SORTED); if (!diter) { strbuf_release(&sb); return empty_ref_iterator_begin(); } - iter = xcalloc(1, sizeof(*iter)); + CALLOC_ARRAY(iter, 1); ref_iterator = &iter->base; - base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable, 0); + base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable); iter->dir_iterator = diter; iter->ref_store = ref_store; strbuf_release(&sb); @@ -2166,45 +2254,19 @@ static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store, return ref_iterator; } -static enum iterator_selection reflog_iterator_select( - struct ref_iterator *iter_worktree, - struct ref_iterator *iter_common, - void *cb_data) -{ - if (iter_worktree) { - /* - * We're a bit loose here. We probably should ignore - * common refs if they are accidentally added as - * per-worktree refs. - */ - return ITER_SELECT_0; - } else if (iter_common) { - if (ref_type(iter_common->refname) == REF_TYPE_NORMAL) - return ITER_SELECT_1; - - /* - * The main ref store may contain main worktree's - * per-worktree refs, which should be ignored - */ - return ITER_SKIP_1; - } else - return ITER_DONE; -} - static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store) { struct files_ref_store *refs = files_downcast(ref_store, REF_STORE_READ, "reflog_iterator_begin"); - if (!strcmp(refs->gitdir, refs->gitcommondir)) { + if (!strcmp(refs->base.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->base.gitdir), reflog_iterator_begin(ref_store, refs->gitcommondir), - reflog_iterator_select, refs); + ref_iterator_select, refs); } } @@ -2420,7 +2482,7 @@ static int lock_ref_for_update(struct files_ref_store *refs, } ret = lock_raw_ref(refs, update->refname, mustexist, - affected_refnames, NULL, + affected_refnames, &lock, &referent, &update->type, err); if (ret) { @@ -2442,9 +2504,9 @@ static int lock_ref_for_update(struct files_ref_store *refs, * the transaction, so we have to read it here * to record and possibly check old_oid: */ - if (refs_read_ref_full(&refs->base, - referent.buf, 0, - &lock->old_oid, NULL)) { + if (!refs_resolve_ref_unsafe(&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", @@ -2500,8 +2562,10 @@ static int lock_ref_for_update(struct files_ref_store *refs, * The reference already has the desired * value, so we don't need to write it. */ - } else if (write_ref_to_lockfile(lock, &update->new_oid, - err)) { + } else if (write_ref_to_lockfile( + lock, &update->new_oid, + update->flags & REF_SKIP_OID_VERIFICATION, + err)) { char *write_err = strbuf_detach(err, NULL); /* @@ -2601,7 +2665,7 @@ static int files_transaction_prepare(struct ref_store *ref_store, if (!transaction->nr) goto cleanup; - backend_data = xcalloc(1, sizeof(*backend_data)); + CALLOC_ARRAY(backend_data, 1); transaction->backend_data = backend_data; /* @@ -2856,6 +2920,7 @@ static int files_transaction_finish(struct ref_store *ref_store, if (update->flags & REF_DELETING && !(update->flags & REF_LOG_ONLY)) { + update->flags |= REF_DELETED_RMDIR; if (!(update->type & REF_ISPACKED) || update->type & REF_ISSYMREF) { /* It is a loose reference. */ @@ -2865,7 +2930,6 @@ static int files_transaction_finish(struct ref_store *ref_store, ret = TRANSACTION_GENERIC_ERROR; goto cleanup; } - update->flags |= REF_DELETED_LOOSE; } } } @@ -2878,9 +2942,9 @@ cleanup: for (i = 0; i < transaction->nr; i++) { struct ref_update *update = transaction->updates[i]; - if (update->flags & REF_DELETED_LOOSE) { + if (update->flags & REF_DELETED_RMDIR) { /* - * The loose reference was deleted. Delete any + * The reference was deleted. Delete any * empty parent directories. (Note that this * can only work because we have already * removed the lockfile.) @@ -2896,7 +2960,7 @@ cleanup: static int files_transaction_abort(struct ref_store *ref_store, struct ref_transaction *transaction, - struct strbuf *err) + struct strbuf *err UNUSED) { struct files_ref_store *refs = files_downcast(ref_store, 0, "ref_transaction_abort"); @@ -2906,7 +2970,9 @@ static int files_transaction_abort(struct ref_store *ref_store, } static int ref_present(const char *refname, - const struct object_id *oid, int flags, void *cb_data) + const struct object_id *oid UNUSED, + int flags UNUSED, + void *cb_data) { struct string_list *affected_refnames = cb_data; @@ -3004,11 +3070,12 @@ cleanup: } struct expire_reflog_cb { - unsigned int flags; reflog_expiry_should_prune_fn *should_prune_fn; void *policy_cb; FILE *newlog; struct object_id last_kept_oid; + unsigned int rewrite:1, + dry_run:1; }; static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid, @@ -3016,33 +3083,27 @@ static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid, const char *message, void *cb_data) { struct expire_reflog_cb *cb = cb_data; - struct expire_reflog_policy_cb *policy_cb = cb->policy_cb; + reflog_expiry_should_prune_fn *fn = cb->should_prune_fn; - if (cb->flags & EXPIRE_REFLOGS_REWRITE) + if (cb->rewrite) ooid = &cb->last_kept_oid; - if ((*cb->should_prune_fn)(ooid, noid, email, timestamp, tz, - message, policy_cb)) { - if (!cb->newlog) - printf("would prune %s", message); - else if (cb->flags & EXPIRE_REFLOGS_VERBOSE) - printf("prune %s", message); - } else { - if (cb->newlog) { - fprintf(cb->newlog, "%s %s %s %"PRItime" %+05d\t%s", - oid_to_hex(ooid), oid_to_hex(noid), - email, timestamp, tz, message); - oidcpy(&cb->last_kept_oid, noid); - } - if (cb->flags & EXPIRE_REFLOGS_VERBOSE) - printf("keep %s", message); - } + if (fn(ooid, noid, email, timestamp, tz, message, cb->policy_cb)) + return 0; + + if (cb->dry_run) + return 0; /* --dry-run */ + + fprintf(cb->newlog, "%s %s %s %"PRItime" %+05d\t%s", oid_to_hex(ooid), + oid_to_hex(noid), email, timestamp, tz, message); + oidcpy(&cb->last_kept_oid, noid); + return 0; } static int files_reflog_expire(struct ref_store *ref_store, - const char *refname, const struct object_id *oid, - unsigned int flags, + const char *refname, + unsigned int expire_flags, reflog_expiry_prepare_fn prepare_fn, reflog_expiry_should_prune_fn should_prune_fn, reflog_expiry_cleanup_fn cleanup_fn, @@ -3056,11 +3117,12 @@ static int files_reflog_expire(struct ref_store *ref_store, struct strbuf log_file_sb = STRBUF_INIT; char *log_file; int status = 0; - int type; struct strbuf err = STRBUF_INIT; + const struct object_id *oid; memset(&cb, 0, sizeof(cb)); - cb.flags = flags; + cb.rewrite = !!(expire_flags & EXPIRE_REFLOGS_REWRITE); + cb.dry_run = !!(expire_flags & EXPIRE_REFLOGS_DRY_RUN); cb.policy_cb = policy_cb_data; cb.should_prune_fn = should_prune_fn; @@ -3069,14 +3131,26 @@ static int files_reflog_expire(struct ref_store *ref_store, * reference itself, plus we might need to update the * reference if --updateref was specified: */ - lock = lock_ref_oid_basic(refs, refname, oid, - NULL, NULL, REF_NO_DEREF, - &type, &err); + lock = lock_ref_oid_basic(refs, refname, &err); if (!lock) { error("cannot lock ref '%s': %s", refname, err.buf); strbuf_release(&err); return -1; } + oid = &lock->old_oid; + + /* + * When refs are deleted, their reflog is deleted before the + * ref itself is deleted. This is because there is no separate + * lock for reflog; instead we take a lock on the ref with + * lock_ref_oid_basic(). + * + * If a race happens and the reflog doesn't exist after we've + * acquired the lock that's OK. We've got nothing more to do; + * We were asked to delete the reflog, but someone else + * deleted it! The caller doesn't care that we deleted it, + * just that it is deleted. So we can return successfully. + */ if (!refs_reflog_exists(ref_store, refname)) { unlock_ref(lock); return 0; @@ -3084,7 +3158,7 @@ static int files_reflog_expire(struct ref_store *ref_store, files_reflog_path(refs, &log_file_sb, refname); log_file = strbuf_detach(&log_file_sb, NULL); - if (!(flags & EXPIRE_REFLOGS_DRY_RUN)) { + if (!cb.dry_run) { /* * Even though holding $GIT_DIR/logs/$reflog.lock has * no locking implications, we use the lock_file @@ -3111,7 +3185,7 @@ static int files_reflog_expire(struct ref_store *ref_store, refs_for_each_reflog_ent(ref_store, refname, expire_reflog_ent, &cb); (*cleanup_fn)(cb.policy_cb); - if (!(flags & EXPIRE_REFLOGS_DRY_RUN)) { + if (!cb.dry_run) { /* * It doesn't make sense to adjust a reference pointed * to by a symbolic ref based on expiring entries in @@ -3119,9 +3193,18 @@ static int files_reflog_expire(struct ref_store *ref_store, * a reference if there are no remaining reflog * entries. */ - int update = (flags & EXPIRE_REFLOGS_UPDATE_REF) && - !(type & REF_ISSYMREF) && - !is_null_oid(&cb.last_kept_oid); + int update = 0; + + if ((expire_flags & EXPIRE_REFLOGS_UPDATE_REF) && + !is_null_oid(&cb.last_kept_oid)) { + int type; + const char *ref; + + ref = refs_resolve_ref_unsafe(&refs->base, refname, + RESOLVE_REF_NO_RECURSE, + NULL, &type); + update = !!(ref && !(type & REF_ISSYMREF)); + } if (close_lock_file_gently(&reflog_lock)) { status |= error("couldn't write %s: %s", log_file, @@ -3153,50 +3236,74 @@ static int files_reflog_expire(struct ref_store *ref_store, return -1; } -static int files_init_db(struct ref_store *ref_store, struct strbuf *err) +static int files_init_db(struct ref_store *ref_store, + int flags, + struct strbuf *err UNUSED) { struct files_ref_store *refs = files_downcast(ref_store, REF_STORE_WRITE, "init_db"); struct strbuf sb = STRBUF_INIT; /* - * Create .git/refs/{heads,tags} + * We need to create a "refs" dir in any case so that older versions of + * Git can tell that this is a repository. This serves two main purposes: + * + * - Clients will know to stop walking the parent-directory chain when + * detecting the Git repository. Otherwise they may end up detecting + * a Git repository in a parent directory instead. + * + * - Instead of failing to detect a repository with unknown reference + * format altogether, old clients will print an error saying that + * they do not understand the reference format extension. */ - files_ref_path(refs, &sb, "refs/heads"); + strbuf_addf(&sb, "%s/refs", ref_store->gitdir); safe_create_dir(sb.buf, 1); + adjust_shared_perm(sb.buf); - strbuf_reset(&sb); - files_ref_path(refs, &sb, "refs/tags"); - safe_create_dir(sb.buf, 1); + /* + * There is no need to create directories for common refs when creating + * a worktree ref store. + */ + if (!(flags & REFS_INIT_DB_IS_WORKTREE)) { + /* + * Create .git/refs/{heads,tags} + */ + strbuf_reset(&sb); + 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 + .name = "files", + .init = files_ref_store_create, + .init_db = files_init_db, + .transaction_prepare = files_transaction_prepare, + .transaction_finish = files_transaction_finish, + .transaction_abort = files_transaction_abort, + .initial_transaction_commit = files_initial_transaction_commit, + + .pack_refs = files_pack_refs, + .create_symref = files_create_symref, + .rename_ref = files_rename_ref, + .copy_ref = files_copy_ref, + + .iterator_begin = files_ref_iterator_begin, + .read_raw_ref = files_read_raw_ref, + .read_symbolic_ref = files_read_symbolic_ref, + + .reflog_iterator_begin = files_reflog_iterator_begin, + .for_each_reflog_ent = files_for_each_reflog_ent, + .for_each_reflog_ent_reverse = files_for_each_reflog_ent_reverse, + .reflog_exists = files_reflog_exists, + .create_reflog = files_create_reflog, + .delete_reflog = files_delete_reflog, + .reflog_expire = files_reflog_expire }; |