summaryrefslogtreecommitdiff
path: root/refs
diff options
context:
space:
mode:
Diffstat (limited to 'refs')
-rw-r--r--refs/debug.c15
-rw-r--r--refs/files-backend.c276
-rw-r--r--refs/packed-backend.c15
-rw-r--r--refs/ref-cache.c87
-rw-r--r--refs/ref-cache.h26
-rw-r--r--refs/refs-internal.h78
6 files changed, 242 insertions, 255 deletions
diff --git a/refs/debug.c b/refs/debug.c
index 1a7a9e11cf..8667c64023 100644
--- a/refs/debug.c
+++ b/refs/debug.c
@@ -239,15 +239,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 +254,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;
}
@@ -365,8 +364,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 +391,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 +404,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 74c0385873..6a6ead0b99 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -227,7 +227,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 +278,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 +336,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,7 +354,6 @@ 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;
*type = 0;
@@ -459,10 +458,9 @@ stat_ref:
ret = parse_loose_ref_contents(buf, oid, referent, type);
out:
- save_errno = errno;
+ *failure_errno = errno;
strbuf_release(&sb_path);
strbuf_release(&sb_contents);
- errno = save_errno;
return ret;
}
@@ -531,7 +529,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 +538,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 +566,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 +609,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 +619,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 +629,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 +655,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 +673,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 +693,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 +710,7 @@ retry:
*/
if (refs_verify_refname_available(
refs->packed_ref_store, refname,
- extras, skip, err))
+ extras, NULL, err))
goto error_return;
}
@@ -744,6 +744,11 @@ 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->iter0->oid,
@@ -854,39 +859,112 @@ static struct ref_iterator *files_ref_iterator_begin(
}
/*
- * 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,64 +988,27 @@ 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,
+ const char *refname, 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))
+ if (!refs_resolve_ref_unsafe(&refs->base, refname,
+ RESOLVE_REF_NO_RECURSE,
+ &lock->old_oid, type)) {
+ if (!refs_verify_refname_available(&refs->base, refname,
+ NULL, NULL, err))
strbuf_addf(err, "unable to resolve reference '%s': %s",
- refname, strerror(last_errno));
+ refname, strerror(errno));
goto error_return;
}
@@ -980,23 +1021,20 @@ 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_read_ref_full(&refs->base, lock->ref_name,
+ 0,
+ &lock->old_oid, NULL))
+ oidclr(&lock->old_oid);
goto out;
error_return:
@@ -1005,7 +1043,6 @@ static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs,
out:
strbuf_release(&ref_file);
- errno = last_errno;
return lock;
}
@@ -1415,8 +1452,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, NULL, &err);
if (!lock) {
if (copy)
error("unable to copy '%s' to '%s': %s", oldrefname, newrefname, err.buf);
@@ -1438,8 +1474,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, NULL, &err);
if (!lock) {
error("unable to lock %s for rollback: %s", oldrefname, err.buf);
strbuf_release(&err);
@@ -1846,9 +1881,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, NULL, &err);
if (!lock) {
error("%s", err.buf);
strbuf_release(&err);
@@ -2416,7 +2449,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) {
@@ -3037,7 +3070,7 @@ static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
}
static int files_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,
@@ -3054,6 +3087,7 @@ static int files_reflog_expire(struct ref_store *ref_store,
int status = 0;
int type;
struct strbuf err = STRBUF_INIT;
+ const struct object_id *oid;
memset(&cb, 0, sizeof(cb));
cb.flags = flags;
@@ -3065,14 +3099,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, &type, &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;
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index f8aa97d799..47247a1491 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -724,9 +724,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 +739,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;
}
@@ -1600,6 +1600,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,12 +1609,14 @@ 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;
}
@@ -1627,17 +1630,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/ref-cache.c b/refs/ref-cache.c
index 49d732f6db..a5ad8a39fb 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
@@ -522,7 +465,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();
diff --git a/refs/ref-cache.h b/refs/ref-cache.h
index 3bfb89d2b3..5c042ae718 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.
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 3155708345..72746407fc 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -245,8 +245,36 @@ int refs_rename_ref_available(struct ref_store *refs,
/* We allow "recursive" symbolic refs. Only within reason, though */
#define SYMREF_MAXDEPTH 5
-/* Include broken references in a do_for_each_ref*() iteration: */
-#define DO_FOR_EACH_INCLUDE_BROKEN 0x01
+/*
+ * These flags are passed to refs_ref_iterator_begin() (and do_for_each_ref(),
+ * which feeds it).
+ */
+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),
+
+ /*
+ * 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),
+
+ /*
+ * 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 +377,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 +470,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 +520,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 */
@@ -593,7 +607,7 @@ typedef int create_reflog_fn(struct ref_store *ref_store, const char *refname,
int force_create, struct strbuf *err);
typedef int delete_reflog_fn(struct ref_store *ref_store, const char *refname);
typedef int reflog_expire_fn(struct ref_store *ref_store,
- const char *refname, const struct object_id *oid,
+ const char *refname,
unsigned int flags,
reflog_expiry_prepare_fn prepare_fn,
reflog_expiry_should_prune_fn should_prune_fn,
@@ -620,11 +634,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 +656,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;