summaryrefslogtreecommitdiff
path: root/refs
diff options
context:
space:
mode:
Diffstat (limited to 'refs')
-rw-r--r--refs/files-backend.c97
-rw-r--r--refs/packed-backend.c23
-rw-r--r--refs/refs-internal.h2
3 files changed, 80 insertions, 42 deletions
diff --git a/refs/files-backend.c b/refs/files-backend.c
index dd8abe9185..561c33ac8a 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -215,6 +215,33 @@ static void files_ref_path(struct files_ref_store *refs,
}
/*
+ * Manually add refs/bisect, refs/rewritten and refs/worktree, which, being
+ * per-worktree, might not appear in the directory listing for
+ * refs/ in the main repo.
+ */
+static void add_per_worktree_entries_to_dir(struct ref_dir *dir, const char *dirname)
+{
+ const char *prefixes[] = { "refs/bisect/", "refs/worktree/", "refs/rewritten/" };
+ int ip;
+
+ if (strcmp(dirname, "refs/"))
+ return;
+
+ for (ip = 0; ip < ARRAY_SIZE(prefixes); ip++) {
+ const char *prefix = prefixes[ip];
+ int prefix_len = strlen(prefix);
+ struct ref_entry *child_entry;
+ int pos;
+
+ pos = search_ref_dir(dir, prefix, prefix_len);
+ if (pos >= 0)
+ continue;
+ child_entry = create_dir_entry(dir->cache, prefix, prefix_len, 1);
+ add_entry_to_dir(dir, child_entry);
+ }
+}
+
+/*
* Read the loose references from the namespace dirname into dir
* (without recursing). dirname must end with '/'. dir must be the
* directory entry corresponding to dirname.
@@ -297,28 +324,7 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
strbuf_release(&path);
closedir(d);
- /*
- * Manually add refs/bisect and refs/worktree, which, being
- * per-worktree, might not appear in the directory listing for
- * refs/ in the main repo.
- */
- if (!strcmp(dirname, "refs/")) {
- int pos = search_ref_dir(dir, "refs/bisect/", 12);
-
- if (pos < 0) {
- struct ref_entry *child_entry = create_dir_entry(
- dir->cache, "refs/bisect/", 12, 1);
- add_entry_to_dir(dir, child_entry);
- }
-
- pos = search_ref_dir(dir, "refs/worktree/", 11);
-
- if (pos < 0) {
- struct ref_entry *child_entry = create_dir_entry(
- dir->cache, "refs/worktree/", 11, 1);
- add_entry_to_dir(dir, child_entry);
- }
- }
+ add_per_worktree_entries_to_dir(dir, dirname);
}
static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs)
@@ -459,8 +465,7 @@ stat_ref:
close(fd);
strbuf_rtrim(&sb_contents);
buf = sb_contents.buf;
- if (starts_with(buf, "ref:")) {
- buf += 4;
+ if (skip_prefix(buf, "ref:", &buf)) {
while (isspace(*buf))
buf++;
@@ -1321,7 +1326,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,
{
struct files_ref_store *refs =
files_downcast(ref_store, REF_STORE_WRITE, "rename_ref");
- struct object_id oid, orig_oid;
+ struct object_id orig_oid;
int flag = 0, logmoved = 0;
struct ref_lock *lock;
struct stat loginfo;
@@ -1389,7 +1394,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,
*/
if (!copy && !refs_read_ref_full(&refs->base, newrefname,
RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
- &oid, NULL) &&
+ NULL, NULL) &&
refs_delete_ref(&refs->base, NULL, newrefname,
NULL, REF_NO_DEREF)) {
if (errno == EISDIR) {
@@ -2137,13 +2142,24 @@ static struct ref_iterator_vtable files_reflog_iterator_vtable = {
static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store,
const char *gitdir)
{
- struct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));
- struct ref_iterator *ref_iterator = &iter->base;
+ struct dir_iterator *diter;
+ struct files_reflog_iterator *iter;
+ struct ref_iterator *ref_iterator;
struct strbuf sb = STRBUF_INIT;
- base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable, 0);
strbuf_addf(&sb, "%s/logs", gitdir);
- iter->dir_iterator = dir_iterator_begin(sb.buf);
+
+ diter = dir_iterator_begin(sb.buf, 0);
+ if (!diter) {
+ strbuf_release(&sb);
+ return empty_ref_iterator_begin();
+ }
+
+ iter = xcalloc(1, sizeof(*iter));
+ ref_iterator = &iter->base;
+
+ base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable, 0);
+ iter->dir_iterator = diter;
iter->ref_store = ref_store;
strbuf_release(&sb);
@@ -2254,8 +2270,7 @@ static int split_head_update(struct ref_update *update,
* Note that the new update will itself be subject to splitting when
* the iteration gets to it.
*/
-static int split_symref_update(struct files_ref_store *refs,
- struct ref_update *update,
+static int split_symref_update(struct ref_update *update,
const char *referent,
struct ref_transaction *transaction,
struct string_list *affected_refnames,
@@ -2449,7 +2464,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
* of processing the split-off update, so we
* don't have to do it here.
*/
- ret = split_symref_update(refs, update,
+ ret = split_symref_update(update,
referent.buf, transaction,
affected_refnames, err);
if (ret)
@@ -2696,18 +2711,32 @@ static int files_transaction_prepare(struct ref_store *ref_store,
if (is_packed_transaction_needed(refs->packed_ref_store,
packed_transaction)) {
ret = ref_transaction_prepare(packed_transaction, err);
+ /*
+ * A failure during the prepare step will abort
+ * itself, but not free. Do that now, and disconnect
+ * from the files_transaction so it does not try to
+ * abort us when we hit the cleanup code below.
+ */
+ if (ret) {
+ ref_transaction_free(packed_transaction);
+ backend_data->packed_transaction = NULL;
+ }
} else {
/*
* We can skip rewriting the `packed-refs`
* file. But we do need to leave it locked, so
* that somebody else doesn't pack a reference
* that we are trying to delete.
+ *
+ * We need to disconnect our transaction from
+ * backend_data, since the abort (whether successful or
+ * not) will free it.
*/
+ backend_data->packed_transaction = NULL;
if (ref_transaction_abort(packed_transaction, err)) {
ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
}
- backend_data->packed_transaction = NULL;
}
}
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index c01c7f5901..4458a0f69c 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -1012,14 +1012,23 @@ int packed_refs_lock(struct ref_store *ref_store, int flags, struct strbuf *err)
}
/*
- * Now that we hold the `packed-refs` lock, make sure that our
- * snapshot matches the current version of the file. Normally
- * `get_snapshot()` does that for us, but that function
- * assumes that when the file is locked, any existing snapshot
- * is still valid. We've just locked the file, but it might
- * have changed the moment *before* we locked it.
+ * There is a stat-validity problem might cause `update-ref -d`
+ * lost the newly commit of a ref, because a new `packed-refs`
+ * file might has the same on-disk file attributes such as
+ * timestamp, file size and inode value, but has a changed
+ * ref value.
+ *
+ * This could happen with a very small chance when
+ * `update-ref -d` is called and at the same time another
+ * `pack-refs --all` process is running.
+ *
+ * Now that we hold the `packed-refs` lock, it is important
+ * to make sure we could read the latest version of
+ * `packed-refs` file no matter we have just mmap it or not.
+ * So what need to do is clear the snapshot if we hold it
+ * already.
*/
- validate_snapshot(refs);
+ clear_snapshot(refs);
/*
* Now make sure that the packed-refs file as it exists in the
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index f2d8c0123a..ff2436c0fb 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -262,7 +262,7 @@ int refs_rename_ref_available(struct ref_store *refs,
* after calling ref_iterator_advance() again or calling
* ref_iterator_abort(), you must make a copy. When the iteration has
* been exhausted, ref_iterator_advance() releases any resources
- * assocated with the iteration, frees the ref_iterator object, and
+ * associated with the iteration, frees the ref_iterator object, and
* returns ITER_DONE. If you want to abort the iteration early, call
* ref_iterator_abort(), which also frees the ref_iterator object and
* any associated resources. If there was an internal error advancing