diff options
Diffstat (limited to 'refs')
-rw-r--r-- | refs/debug.c | 33 | ||||
-rw-r--r-- | refs/files-backend.c | 503 | ||||
-rw-r--r-- | refs/packed-backend.c | 45 | ||||
-rw-r--r-- | refs/packed-backend.h | 4 | ||||
-rw-r--r-- | refs/ref-cache.c | 97 | ||||
-rw-r--r-- | refs/ref-cache.h | 27 | ||||
-rw-r--r-- | refs/refs-internal.h | 119 |
7 files changed, 437 insertions, 391 deletions
diff --git a/refs/debug.c b/refs/debug.c index 1a7a9e11cf..2b0771ca53 100644 --- a/refs/debug.c +++ b/refs/debug.c @@ -26,7 +26,8 @@ struct ref_store *maybe_debug_wrap_ref_store(const char *gitdir, struct ref_stor be_copy->name = store->be->name; trace_printf_key(&trace_refs, "ref_store for %s\n", gitdir); res->refs = store; - base_ref_store_init((struct ref_store *)res, be_copy); + base_ref_store_init((struct ref_store *)res, store->repo, gitdir, + be_copy); return (struct ref_store *)res; } @@ -47,7 +48,8 @@ static int debug_transaction_prepare(struct ref_store *refs, transaction->ref_store = drefs->refs; res = drefs->refs->be->transaction_prepare(drefs->refs, transaction, err); - trace_printf_key(&trace_refs, "transaction_prepare: %d\n", res); + trace_printf_key(&trace_refs, "transaction_prepare: %d \"%s\"\n", res, + err->buf); return res; } @@ -239,15 +241,14 @@ debug_ref_iterator_begin(struct ref_store *ref_store, const char *prefix, static int debug_read_raw_ref(struct ref_store *ref_store, const char *refname, struct object_id *oid, struct strbuf *referent, - unsigned int *type) + unsigned int *type, int *failure_errno) { struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store; int res = 0; oidcpy(oid, null_oid()); - errno = 0; res = drefs->refs->be->read_raw_ref(drefs->refs, refname, oid, referent, - type); + type, failure_errno); if (res == 0) { trace_printf_key(&trace_refs, "read_raw_ref: %s: %s (=> %s) type %x: %d\n", @@ -255,7 +256,7 @@ static int debug_read_raw_ref(struct ref_store *ref_store, const char *refname, } else { trace_printf_key(&trace_refs, "read_raw_ref: %s: %d (errno %d)\n", refname, - res, errno); + res, *failure_errno); } return res; } @@ -285,6 +286,7 @@ static int debug_print_reflog_ent(struct object_id *old_oid, int ret; char o[GIT_MAX_HEXSZ + 1] = "null"; char n[GIT_MAX_HEXSZ + 1] = "null"; + char *msgend = strchrnul(msg, '\n'); if (old_oid) oid_to_hex_r(o, old_oid); if (new_oid) @@ -292,8 +294,10 @@ static int debug_print_reflog_ent(struct object_id *old_oid, ret = dbg->fn(old_oid, new_oid, committer, timestamp, tz, msg, dbg->cb_data); - trace_printf_key(&trace_refs, "reflog_ent %s (ret %d): %s -> %s, %s %ld \"%s\"\n", - dbg->refname, ret, o, n, committer, (long int)timestamp, msg); + trace_printf_key(&trace_refs, + "reflog_ent %s (ret %d): %s -> %s, %s %ld \"%.*s\"\n", + dbg->refname, ret, o, n, committer, + (long int)timestamp, (int)(msgend - msg), msg); return ret; } @@ -340,11 +344,10 @@ static int debug_reflog_exists(struct ref_store *ref_store, const char *refname) } static int debug_create_reflog(struct ref_store *ref_store, const char *refname, - int force_create, struct strbuf *err) + struct strbuf *err) { struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store; - int res = drefs->refs->be->create_reflog(drefs->refs, refname, - force_create, err); + int res = drefs->refs->be->create_reflog(drefs->refs, refname, err); trace_printf_key(&trace_refs, "create_reflog: %s: %d\n", refname, res); return res; } @@ -365,8 +368,8 @@ struct debug_reflog_expiry_should_prune { }; static void debug_reflog_expiry_prepare(const char *refname, - const struct object_id *oid, - void *cb_data) + const struct object_id *oid, + void *cb_data) { struct debug_reflog_expiry_should_prune *prune = cb_data; trace_printf_key(&trace_refs, "reflog_expire_prepare: %s\n", refname); @@ -392,7 +395,7 @@ static void debug_reflog_expiry_cleanup(void *cb_data) } static int debug_reflog_expire(struct ref_store *ref_store, const char *refname, - const struct object_id *oid, unsigned int flags, + unsigned int flags, reflog_expiry_prepare_fn prepare_fn, reflog_expiry_should_prune_fn should_prune_fn, reflog_expiry_cleanup_fn cleanup_fn, @@ -405,7 +408,7 @@ static int debug_reflog_expire(struct ref_store *ref_store, const char *refname, .should_prune = should_prune_fn, .cb_data = policy_cb_data, }; - int res = drefs->refs->be->reflog_expire(drefs->refs, refname, oid, + int res = drefs->refs->be->reflog_expire(drefs->refs, refname, flags, &debug_reflog_expiry_prepare, &debug_reflog_expiry_should_prune_fn, &debug_reflog_expiry_cleanup, diff --git a/refs/files-backend.c b/refs/files-backend.c index 677b7e4cdd..a40267b3ae 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -16,8 +16,7 @@ * 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`. */ /* @@ -79,22 +78,20 @@ 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; - ref_store->gitdir = xstrdup(gitdir); - base_ref_store_init(ref_store, &refs_be_files); + base_ref_store_init(ref_store, repo, gitdir, &refs_be_files); refs->store_flags = flags; - 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->base.gitdir); chdir_notify_reparent("files-backend $GIT_COMMONDIR", @@ -227,7 +224,7 @@ 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); } } @@ -278,7 +275,7 @@ static void loose_fill_ref_dir(struct ref_store *ref_store, strbuf_addch(&refname, '/'); add_entry_to_dir(dir, create_dir_entry(dir->cache, refname.buf, - refname.len, 1)); + refname.len)); } else { if (!refs_resolve_ref_unsafe(&refs->base, refname.buf, @@ -336,14 +333,14 @@ static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs) * lazily): */ add_entry_to_dir(get_ref_dir(refs->loose->root), - create_dir_entry(refs->loose, "refs/", 5, 1)); + 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 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) { struct files_ref_store *refs = files_downcast(ref_store, REF_STORE_READ, "read_raw_ref"); @@ -354,8 +351,8 @@ static int files_read_raw_ref(struct ref_store *ref_store, struct stat st; int fd; int ret = -1; - int save_errno; int remaining_retries = 3; + int myerr = 0; *type = 0; strbuf_reset(&sb_path); @@ -382,11 +379,13 @@ stat_ref: goto out; if (lstat(path, &st) < 0) { - if (errno != ENOENT) + int ignore_errno; + myerr = errno; + if (myerr != ENOENT) 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; @@ -397,7 +396,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 @@ -419,14 +419,15 @@ 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 (refs_read_raw_ref(refs->packed_ref_store, refname, oid, + referent, type, &ignore_errno)) { + myerr = EISDIR; goto out; } ret = 0; @@ -439,7 +440,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 @@ -447,27 +449,30 @@ 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); + ret = parse_loose_ref_contents(buf, oid, referent, type, &myerr); out: - save_errno = errno; + 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 = save_errno; + errno = 0; return ret; } int parse_loose_ref_contents(const char *buf, struct object_id *oid, - struct strbuf *referent, unsigned int *type) + struct strbuf *referent, unsigned int *type, + int *failure_errno) { const char *p; if (skip_prefix(buf, "ref:", &buf)) { @@ -486,7 +491,7 @@ int parse_loose_ref_contents(const char *buf, struct object_id *oid, if (parse_oid_hex(buf, oid, &p) || (*p != '\0' && !isspace(*p))) { *type |= REF_ISBROKEN; - errno = EINVAL; + *failure_errno = EINVAL; return -1; } return 0; @@ -531,7 +536,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, @@ -541,6 +545,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"); @@ -568,7 +573,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 @@ -611,7 +616,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 @@ -619,7 +626,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; } } @@ -629,9 +636,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'", @@ -655,7 +662,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 @@ -673,7 +680,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. @@ -693,13 +700,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; } @@ -710,7 +717,7 @@ retry: */ if (refs_verify_refname_available( refs->packed_ref_store, refname, - extras, skip, err)) + extras, NULL, err)) goto error_return; } @@ -730,6 +737,7 @@ struct files_ref_iterator { struct ref_iterator base; struct ref_iterator *iter0; + struct repository *repo; unsigned int flags; }; @@ -744,8 +752,14 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator) ref_type(iter->iter0->refname) != REF_TYPE_PER_WORKTREE) 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; @@ -824,7 +838,7 @@ static struct ref_iterator *files_ref_iterator_begin( */ loose_iter = cache_ref_iterator_begin(get_loose_ref_cache(refs), - prefix, 1); + prefix, ref_store->repo, 1); /* * The packed-refs file might contain broken references, for @@ -848,45 +862,119 @@ static struct ref_iterator *files_ref_iterator_begin( base_ref_iterator_init(ref_iterator, &files_ref_iterator_vtable, overlay_iter->ordered); 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. + */ +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 verify_lock(struct ref_store *ref_store, struct ref_lock *lock, - const struct object_id *old_oid, int mustexist, - struct strbuf *err) +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) @@ -910,67 +998,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); CALLOC_ARRAY(lock, 1); - if (mustexist) - resolve_flags |= RESOLVE_REF_READING; - if (flags & REF_DELETING) - resolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME; - 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 @@ -980,23 +1021,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: @@ -1005,7 +1042,6 @@ static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs, out: strbuf_release(&ref_file); - errno = last_errno; return lock; } @@ -1132,7 +1168,7 @@ 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; @@ -1155,7 +1191,8 @@ 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), NULL, + the_repository, 0); while ((ok = ref_iterator_advance(iter)) == ITER_OK) { /* * If the loose reference can be packed, add an entry @@ -1309,12 +1346,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) @@ -1343,7 +1410,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; } @@ -1387,9 +1454,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) { @@ -1415,8 +1482,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); @@ -1427,7 +1493,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); @@ -1438,8 +1504,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); @@ -1448,7 +1513,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); @@ -1569,7 +1634,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) { /* @@ -1598,15 +1663,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) @@ -1684,26 +1748,31 @@ 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 || @@ -1803,9 +1872,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); @@ -1846,9 +1916,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); @@ -2092,9 +2160,9 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator) if (ends_with(diter->basename, ".lock")) continue; - if (refs_read_ref_full(iter->ref_store, - diter->relative_path, 0, - &iter->oid, &flags)) { + if (!refs_resolve_ref_unsafe(iter->ref_store, + diter->relative_path, 0, + &iter->oid, &flags)) { error("bad ref for %s", diter->path.buf); continue; } @@ -2112,7 +2180,7 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator) } static int files_reflog_iterator_peel(struct ref_iterator *ref_iterator, - struct object_id *peeled) + struct object_id *peeled) { BUG("ref_iterator_peel() called for reflog_iterator"); } @@ -2416,7 +2484,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) { @@ -2438,9 +2506,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", @@ -2496,8 +2564,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); /* @@ -3000,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, @@ -3012,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, @@ -3052,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; @@ -3065,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; @@ -3080,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 @@ -3107,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 @@ -3115,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, diff --git a/refs/packed-backend.c b/refs/packed-backend.c index f8aa97d799..d91a2018f6 100644 --- a/refs/packed-backend.c +++ b/refs/packed-backend.c @@ -193,19 +193,20 @@ static int release_snapshot(struct snapshot *snapshot) } } -struct ref_store *packed_ref_store_create(const char *path, +struct ref_store *packed_ref_store_create(struct repository *repo, + const char *gitdir, unsigned int store_flags) { struct packed_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_packed); - ref_store->gitdir = xstrdup(path); + base_ref_store_init(ref_store, repo, gitdir, &refs_be_packed); refs->store_flags = store_flags; - refs->path = xstrdup(path); + strbuf_addf(&sb, "%s/packed-refs", gitdir); + refs->path = strbuf_detach(&sb, NULL); chdir_notify_reparent("packed-refs", &refs->path); - return ref_store; } @@ -724,9 +725,9 @@ static struct snapshot *get_snapshot(struct packed_ref_store *refs) return refs->snapshot; } -static int packed_read_raw_ref(struct ref_store *ref_store, - const char *refname, struct object_id *oid, - struct strbuf *referent, unsigned int *type) +static int packed_read_raw_ref(struct ref_store *ref_store, const char *refname, + struct object_id *oid, struct strbuf *referent, + unsigned int *type, int *failure_errno) { struct packed_ref_store *refs = packed_downcast(ref_store, REF_STORE_READ, "read_raw_ref"); @@ -739,7 +740,7 @@ static int packed_read_raw_ref(struct ref_store *ref_store, if (!rec) { /* refname is not a packed reference. */ - errno = ENOENT; + *failure_errno = ENOENT; return -1; } @@ -776,6 +777,7 @@ struct packed_ref_iterator { struct object_id oid, peeled; struct strbuf refname_buf; + struct repository *repo; unsigned int flags; }; @@ -864,8 +866,8 @@ static int packed_ref_iterator_advance(struct ref_iterator *ref_iterator) continue; if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) && - !ref_resolves_to_object(iter->base.refname, &iter->oid, - iter->flags)) + !ref_resolves_to_object(iter->base.refname, iter->repo, + &iter->oid, iter->flags)) continue; return ITER_OK; @@ -883,6 +885,9 @@ static int packed_ref_iterator_peel(struct ref_iterator *ref_iterator, struct packed_ref_iterator *iter = (struct packed_ref_iterator *)ref_iterator; + if (iter->repo != the_repository) + BUG("peeling for non-the_repository is not supported"); + if ((iter->base.flags & REF_KNOWS_PEELED)) { oidcpy(peeled, &iter->peeled); return is_null_oid(&iter->peeled) ? -1 : 0; @@ -954,6 +959,7 @@ static struct ref_iterator *packed_ref_iterator_begin( iter->base.oid = &iter->oid; + iter->repo = ref_store->repo; iter->flags = flags; if (prefix && *prefix) @@ -1347,6 +1353,7 @@ int is_packed_transaction_needed(struct ref_store *ref_store, ret = 0; for (i = 0; i < transaction->nr; i++) { struct ref_update *update = transaction->updates[i]; + int failure_errno; unsigned int type; struct object_id oid; @@ -1357,9 +1364,9 @@ int is_packed_transaction_needed(struct ref_store *ref_store, */ continue; - if (!refs_read_raw_ref(ref_store, update->refname, - &oid, &referent, &type) || - errno != ENOENT) { + if (!refs_read_raw_ref(ref_store, update->refname, &oid, + &referent, &type, &failure_errno) || + failure_errno != ENOENT) { /* * We have to actually delete that reference * -> this transaction is needed. @@ -1600,6 +1607,7 @@ static int packed_for_each_reflog_ent(struct ref_store *ref_store, const char *refname, each_reflog_ent_fn fn, void *cb_data) { + BUG("packed reference store does not support reflogs"); return 0; } @@ -1608,18 +1616,19 @@ static int packed_for_each_reflog_ent_reverse(struct ref_store *ref_store, each_reflog_ent_fn fn, void *cb_data) { + BUG("packed reference store does not support reflogs"); return 0; } static int packed_reflog_exists(struct ref_store *ref_store, const char *refname) { + BUG("packed reference store does not support reflogs"); return 0; } static int packed_create_reflog(struct ref_store *ref_store, - const char *refname, int force_create, - struct strbuf *err) + const char *refname, struct strbuf *err) { BUG("packed reference store does not support reflogs"); } @@ -1627,17 +1636,19 @@ static int packed_create_reflog(struct ref_store *ref_store, static int packed_delete_reflog(struct ref_store *ref_store, const char *refname) { + BUG("packed reference store does not support reflogs"); return 0; } static int packed_reflog_expire(struct ref_store *ref_store, - const char *refname, const struct object_id *oid, + const char *refname, 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) { + BUG("packed reference store does not support reflogs"); return 0; } diff --git a/refs/packed-backend.h b/refs/packed-backend.h index a01a0aff9c..9dd8a344c3 100644 --- a/refs/packed-backend.h +++ b/refs/packed-backend.h @@ -1,6 +1,7 @@ #ifndef REFS_PACKED_BACKEND_H #define REFS_PACKED_BACKEND_H +struct repository; struct ref_transaction; /* @@ -12,7 +13,8 @@ struct ref_transaction; * even among packed refs. */ -struct ref_store *packed_ref_store_create(const char *path, +struct ref_store *packed_ref_store_create(struct repository *repo, + const char *gitdir, unsigned int store_flags); /* diff --git a/refs/ref-cache.c b/refs/ref-cache.c index 49d732f6db..be4aa5e098 100644 --- a/refs/ref-cache.c +++ b/refs/ref-cache.c @@ -49,7 +49,7 @@ struct ref_cache *create_ref_cache(struct ref_store *refs, ret->ref_store = refs; ret->fill_ref_dir = fill_ref_dir; - ret->root = create_dir_entry(ret, "", 0, 1); + ret->root = create_dir_entry(ret, "", 0); return ret; } @@ -86,14 +86,13 @@ static void clear_ref_dir(struct ref_dir *dir) } struct ref_entry *create_dir_entry(struct ref_cache *cache, - const char *dirname, size_t len, - int incomplete) + const char *dirname, size_t len) { struct ref_entry *direntry; FLEX_ALLOC_MEM(direntry, name, dirname, len); direntry->u.subdir.cache = cache; - direntry->flag = REF_DIR | (incomplete ? REF_INCOMPLETE : 0); + direntry->flag = REF_DIR | REF_INCOMPLETE; return direntry; } @@ -144,30 +143,19 @@ int search_ref_dir(struct ref_dir *dir, const char *refname, size_t len) /* * 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 + * name (i.e., end in '/'). Returns 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) + const char *subdirname, size_t len) { int entry_index = search_ref_dir(dir, subdirname, len); struct ref_entry *entry; - if (entry_index == -1) { - if (!mkdir) - return NULL; - /* - * Since dir is complete, the absence of a subdir - * means that the subdir really doesn't exist; - * therefore, create an empty record for it but mark - * the record complete. - */ - entry = create_dir_entry(dir->cache, subdirname, len, 0); - add_entry_to_dir(dir, entry); - } else { - entry = dir->entries[entry_index]; - } + + if (entry_index == -1) + return NULL; + + entry = dir->entries[entry_index]; return get_ref_dir(entry); } @@ -176,18 +164,17 @@ static struct ref_dir *search_for_subdir(struct ref_dir *dir, * tree that should hold refname. If refname is a directory name * (i.e., it ends in '/'), then return that ref_dir itself. dir must * represent the top-level directory and must already be complete. - * Sort ref_dirs and recurse into subdirectories as necessary. If - * mkdir is set, then create any missing directories; otherwise, + * Sort ref_dirs and recurse into subdirectories as necessary. Will * 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 *refname) { 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); + subdir = search_for_subdir(dir, refname, dirnamelen); if (!subdir) { dir = NULL; break; @@ -202,7 +189,7 @@ struct ref_entry *find_ref_entry(struct ref_dir *dir, const char *refname) { int entry_index; struct ref_entry *entry; - dir = find_containing_dir(dir, refname, 0); + dir = find_containing_dir(dir, refname); if (!dir) return NULL; entry_index = search_ref_dir(dir, refname, strlen(refname)); @@ -212,50 +199,6 @@ struct ref_entry *find_ref_entry(struct ref_dir *dir, const char *refname) return (entry->flag & REF_DIR) ? NULL : entry; } -int remove_entry_from_dir(struct ref_dir *dir, const char *refname) -{ - int refname_len = strlen(refname); - int entry_index; - struct ref_entry *entry; - int is_dir = refname[refname_len - 1] == '/'; - if (is_dir) { - /* - * refname represents a reference directory. Remove - * the trailing slash; otherwise we will get the - * directory *representing* refname rather than the - * one *containing* it. - */ - char *dirname = xmemdupz(refname, refname_len - 1); - dir = find_containing_dir(dir, dirname, 0); - free(dirname); - } else { - dir = find_containing_dir(dir, refname, 0); - } - if (!dir) - return -1; - entry_index = search_ref_dir(dir, refname, refname_len); - if (entry_index == -1) - return -1; - entry = dir->entries[entry_index]; - - MOVE_ARRAY(&dir->entries[entry_index], - &dir->entries[entry_index + 1], dir->nr - entry_index - 1); - dir->nr--; - if (dir->sorted > entry_index) - dir->sorted--; - free_ref_entry(entry); - return dir->nr; -} - -int add_ref_entry(struct ref_dir *dir, struct ref_entry *ref) -{ - dir = find_containing_dir(dir, ref->name, 1); - if (!dir) - return -1; - add_entry_to_dir(dir, ref); - return 0; -} - /* * Emit a warning and return true iff ref1 and ref2 have the same name * and the same oid. Die if they have the same name but different @@ -435,6 +378,8 @@ struct cache_ref_iterator { * on from there.) */ struct cache_ref_iterator_level *levels; + + struct repository *repo; }; static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator) @@ -491,6 +436,11 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator) 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; + + if (iter->repo != the_repository) + BUG("peeling for non-the_repository is not supported"); return peel_object(ref_iterator->oid, peeled) ? -1 : 0; } @@ -513,6 +463,7 @@ static struct ref_iterator_vtable cache_ref_iterator_vtable = { struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache, const char *prefix, + struct repository *repo, int prime_dir) { struct ref_dir *dir; @@ -522,7 +473,7 @@ struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache, dir = get_ref_dir(cache->root); if (prefix && *prefix) - dir = find_containing_dir(dir, prefix, 0); + dir = find_containing_dir(dir, prefix); if (!dir) /* There's nothing to iterate over. */ return empty_ref_iterator_begin(); @@ -547,5 +498,7 @@ struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache, level->prefix_state = PREFIX_CONTAINS_DIR; } + iter->repo = repo; + return ref_iterator; } diff --git a/refs/ref-cache.h b/refs/ref-cache.h index 3bfb89d2b3..850d9d3744 100644 --- a/refs/ref-cache.h +++ b/refs/ref-cache.h @@ -169,8 +169,7 @@ struct ref_dir *get_ref_dir(struct ref_entry *entry); * "refs/heads/") or "" for the top-level directory. */ struct ref_entry *create_dir_entry(struct ref_cache *cache, - const char *dirname, size_t len, - int incomplete); + const char *dirname, size_t len); struct ref_entry *create_ref_entry(const char *refname, const struct object_id *oid, int flag); @@ -200,29 +199,6 @@ void free_ref_cache(struct ref_cache *cache); void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry); /* - * Remove the entry with the given name from dir, recursing into - * subdirectories as necessary. If refname is the name of a directory - * (i.e., ends with '/'), then remove the directory and its contents. - * If the removal was successful, return the number of entries - * remaining in the directory entry that contained the deleted entry. - * If the name was not found, return -1. Please note that this - * function only deletes the entry from the cache; it does not delete - * it from the filesystem or ensure that other cache entries (which - * might be symbolic references to the removed entry) are updated. - * Nor does it remove any containing dir entries that might be made - * empty by the removal. dir must represent the top-level directory - * and must already be complete. - */ -int remove_entry_from_dir(struct ref_dir *dir, const char *refname); - -/* - * Add a ref_entry to the ref_dir (unsorted), recursing into - * subdirectories as necessary. dir must represent the top-level - * directory. Return 0 on success. - */ -int add_ref_entry(struct ref_dir *dir, struct ref_entry *ref); - -/* * Find the value entry with the given name in dir, sorting ref_dirs * and recursing into subdirectories as necessary. If the name is not * found or it corresponds to a directory entry, return NULL. @@ -238,6 +214,7 @@ struct ref_entry *find_ref_entry(struct ref_dir *dir, const char *refname); */ struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache, const char *prefix, + struct repository *repo, int prime_dir); #endif /* REFS_REF_CACHE_H */ diff --git a/refs/refs-internal.h b/refs/refs-internal.h index 3155708345..7ff6fba4f0 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -66,6 +66,7 @@ int refname_is_safe(const char *refname); * referred-to object does not exist, emit a warning and return false. */ int ref_resolves_to_object(const char *refname, + struct repository *repo, const struct object_id *oid, unsigned int flags); @@ -149,9 +150,9 @@ struct ref_update { const char refname[FLEX_ARRAY]; }; -int refs_read_raw_ref(struct ref_store *ref_store, - const char *refname, struct object_id *oid, - struct strbuf *referent, unsigned int *type); +int refs_read_raw_ref(struct ref_store *ref_store, const char *refname, + struct object_id *oid, struct strbuf *referent, + unsigned int *type, int *failure_errno); /* * Write an error to `err` and return a nonzero value iff the same @@ -228,25 +229,39 @@ const char *find_descendant_ref(const char *dirname, const struct string_list *extras, const struct string_list *skip); +/* We allow "recursive" symbolic refs. Only within reason, though */ +#define SYMREF_MAXDEPTH 5 + /* - * Check whether an attempt to rename old_refname to new_refname would - * cause a D/F conflict with any existing reference (other than - * possibly old_refname). If there would be a conflict, emit an error - * message and return false; otherwise, return true. - * - * Note that this function is not safe against all races with other - * processes (though rename_ref() catches some races that might get by - * this check). + * These flags are passed to refs_ref_iterator_begin() (and do_for_each_ref(), + * which feeds it). */ -int refs_rename_ref_available(struct ref_store *refs, - const char *old_refname, - const char *new_refname); +enum do_for_each_ref_flags { + /* + * Include broken references in a do_for_each_ref*() iteration, which + * would normally be omitted. This includes both refs that point to + * missing objects (a true repository corruption), ones with illegal + * names (which we prefer not to expose to callers), as well as + * dangling symbolic refs (i.e., those that point to a non-existent + * ref; this is not a corruption, but as they have no valid oid, we + * omit them from normal iteration results). + */ + DO_FOR_EACH_INCLUDE_BROKEN = (1 << 0), -/* We allow "recursive" symbolic refs. Only within reason, though */ -#define SYMREF_MAXDEPTH 5 + /* + * Only include per-worktree refs in a do_for_each_ref*() iteration. + * Normally this will be used with a files ref_store, since that's + * where all reference backends will presumably store their + * per-worktree refs. + */ + DO_FOR_EACH_PER_WORKTREE_ONLY = (1 << 1), -/* Include broken references in a do_for_each_ref*() iteration: */ -#define DO_FOR_EACH_INCLUDE_BROKEN 0x01 + /* + * Omit dangling symrefs from output; this only has an effect with + * INCLUDE_BROKEN, since they are otherwise not included at all. + */ + DO_FOR_EACH_OMIT_DANGLING_SYMREFS = (1 << 2), +}; /* * Reference iterators @@ -349,16 +364,12 @@ int is_empty_ref_iterator(struct ref_iterator *ref_iterator); * Return an iterator that goes over each reference in `refs` for * which the refname begins with prefix. If trim is non-zero, then * trim that many characters off the beginning of each refname. - * The output is ordered by refname. The following flags are supported: - * - * DO_FOR_EACH_INCLUDE_BROKEN: include broken references in - * the iteration. - * - * DO_FOR_EACH_PER_WORKTREE_ONLY: only produce REF_TYPE_PER_WORKTREE refs. + * The output is ordered by refname. */ struct ref_iterator *refs_ref_iterator_begin( struct ref_store *refs, - const char *prefix, int trim, int flags); + const char *prefix, int trim, + enum do_for_each_ref_flags flags); /* * A callback function used to instruct merge_ref_iterator how to @@ -446,10 +457,8 @@ void base_ref_iterator_free(struct ref_iterator *iter); /* * backend-specific implementation of ref_iterator_advance. For symrefs, the * function should set REF_ISSYMREF, and it should also dereference the symref - * to provide the OID referent. If DO_FOR_EACH_INCLUDE_BROKEN is set, symrefs - * with non-existent referents and refs pointing to non-existent object names - * should also be returned. If DO_FOR_EACH_PER_WORKTREE_ONLY, only - * REF_TYPE_PER_WORKTREE refs should be returned. + * to provide the OID referent. It should respect do_for_each_ref_flags + * that were passed to refs_ref_iterator_begin(). */ typedef int ref_iterator_advance_fn(struct ref_iterator *ref_iterator); @@ -498,14 +507,6 @@ int do_for_each_repo_ref_iterator(struct repository *r, struct ref_iterator *iter, each_repo_ref_fn fn, void *cb_data); -/* - * Only include per-worktree refs in a do_for_each_ref*() iteration. - * Normally this will be used with a files ref_store, since that's - * where all reference backends will presumably store their - * per-worktree refs. - */ -#define DO_FOR_EACH_PER_WORKTREE_ONLY 0x02 - struct ref_store; /* refs backends */ @@ -525,7 +526,8 @@ struct ref_store; * should call base_ref_store_init() to initialize the shared part of * the ref_store and to record the ref_store for later lookup. */ -typedef struct ref_store *ref_store_init_fn(const char *gitdir, +typedef struct ref_store *ref_store_init_fn(struct repository *repo, + const char *gitdir, unsigned int flags); typedef int ref_init_db_fn(struct ref_store *refs, struct strbuf *err); @@ -590,10 +592,10 @@ typedef int for_each_reflog_ent_reverse_fn(struct ref_store *ref_store, void *cb_data); typedef int reflog_exists_fn(struct ref_store *ref_store, const char *refname); typedef int create_reflog_fn(struct ref_store *ref_store, const char *refname, - int force_create, struct strbuf *err); + struct strbuf *err); typedef int delete_reflog_fn(struct ref_store *ref_store, const char *refname); typedef int reflog_expire_fn(struct ref_store *ref_store, - const char *refname, const struct object_id *oid, + const char *refname, unsigned int flags, reflog_expiry_prepare_fn prepare_fn, reflog_expiry_should_prune_fn should_prune_fn, @@ -620,11 +622,15 @@ typedef int reflog_expire_fn(struct ref_store *ref_store, * properly-formatted or even safe reference name. NEITHER INPUT NOR * OUTPUT REFERENCE NAMES ARE VALIDATED WITHIN THIS FUNCTION. * - * Return 0 on success. If the ref doesn't exist, set errno to ENOENT - * and return -1. If the ref exists but is neither a symbolic ref nor - * an object ID, it is broken; set REF_ISBROKEN in type, set errno to - * EINVAL, and return -1. If there is another error reading the ref, - * set errno appropriately and return -1. + * Return 0 on success, or -1 on failure. If the ref exists but is neither a + * symbolic ref nor an object ID, it is broken. In this case set REF_ISBROKEN in + * type, and return -1 (failure_errno should not be ENOENT) + * + * failure_errno provides errno codes that are interpreted beyond error + * reporting. The following error codes have special meaning: + * * ENOENT: the ref doesn't exist + * * EISDIR: ref name is a directory + * * ENOTDIR: ref prefix is not a directory * * Backend-specific flags might be set in type as well, regardless of * outcome. @@ -638,9 +644,9 @@ typedef int reflog_expire_fn(struct ref_store *ref_store, * - in all other cases, referent will be untouched, and therefore * refname will still be valid and unchanged. */ -typedef int read_raw_ref_fn(struct ref_store *ref_store, - const char *refname, struct object_id *oid, - struct strbuf *referent, unsigned int *type); +typedef int read_raw_ref_fn(struct ref_store *ref_store, const char *refname, + struct object_id *oid, struct strbuf *referent, + unsigned int *type, int *failure_errno); struct ref_storage_be { struct ref_storage_be *next; @@ -683,22 +689,29 @@ struct ref_store { /* The backend describing this ref_store's storage scheme: */ const struct ref_storage_be *be; - /* The gitdir that this ref_store applies to: */ + struct repository *repo; + + /* + * The gitdir that this ref_store applies to. Note that this is not + * necessarily repo->gitdir if the repo has multiple worktrees. + */ char *gitdir; }; /* - * Parse contents of a loose ref file. + * Parse contents of a loose ref file. *failure_errno maybe be set to EINVAL for + * invalid contents. */ int parse_loose_ref_contents(const char *buf, struct object_id *oid, - struct strbuf *referent, unsigned int *type); + struct strbuf *referent, unsigned int *type, + int *failure_errno); /* * Fill in the generic part of refs and add it to our collection of * reference stores. */ -void base_ref_store_init(struct ref_store *refs, - const struct ref_storage_be *be); +void base_ref_store_init(struct ref_store *refs, struct repository *repo, + const char *path, const struct ref_storage_be *be); /* * Support GIT_TRACE_REFS by optionally wrapping the given ref_store instance. |