Merge branch 'jk/incore-lockfile-removal'
[gitweb.git] / refs / files-backend.c
index f3455609d658a55b20ca47026236cec17533ecca..a7cc65d0dee2dc5e8d3af6e447fc337e5958930f 100644 (file)
@@ -106,15 +106,6 @@ static void files_reflog_path(struct files_ref_store *refs,
                              struct strbuf *sb,
                              const char *refname)
 {
-       if (!refname) {
-               /*
-                * FIXME: of course this is wrong in multi worktree
-                * setting. To be fixed real soon.
-                */
-               strbuf_addf(sb, "%s/logs", refs->gitcommondir);
-               return;
-       }
-
        switch (ref_type(refname)) {
        case REF_TYPE_PER_WORKTREE:
        case REF_TYPE_PSEUDOREF:
@@ -2052,23 +2043,63 @@ static struct ref_iterator_vtable files_reflog_iterator_vtable = {
        files_reflog_iterator_abort
 };
 
-static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)
+static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store,
+                                                 const char *gitdir)
 {
-       struct files_ref_store *refs =
-               files_downcast(ref_store, REF_STORE_READ,
-                              "reflog_iterator_begin");
        struct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));
        struct ref_iterator *ref_iterator = &iter->base;
        struct strbuf sb = STRBUF_INIT;
 
        base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);
-       files_reflog_path(refs, &sb, NULL);
+       strbuf_addf(&sb, "%s/logs", gitdir);
        iter->dir_iterator = dir_iterator_begin(sb.buf);
        iter->ref_store = ref_store;
        strbuf_release(&sb);
+
        return ref_iterator;
 }
 
+static enum iterator_selection reflog_iterator_select(
+       struct ref_iterator *iter_worktree,
+       struct ref_iterator *iter_common,
+       void *cb_data)
+{
+       if (iter_worktree) {
+               /*
+                * We're a bit loose here. We probably should ignore
+                * common refs if they are accidentally added as
+                * per-worktree refs.
+                */
+               return ITER_SELECT_0;
+       } else if (iter_common) {
+               if (ref_type(iter_common->refname) == REF_TYPE_NORMAL)
+                       return ITER_SELECT_1;
+
+               /*
+                * The main ref store may contain main worktree's
+                * per-worktree refs, which should be ignored
+                */
+               return ITER_SKIP_1;
+       } else
+               return ITER_DONE;
+}
+
+static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)
+{
+       struct files_ref_store *refs =
+               files_downcast(ref_store, REF_STORE_READ,
+                              "reflog_iterator_begin");
+
+       if (!strcmp(refs->gitdir, refs->gitcommondir)) {
+               return reflog_iterator_begin(ref_store, refs->gitcommondir);
+       } else {
+               return merge_ref_iterator_begin(
+                       reflog_iterator_begin(ref_store, refs->gitdir),
+                       reflog_iterator_begin(ref_store, refs->gitcommondir),
+                       reflog_iterator_select, refs);
+       }
+}
+
 /*
  * If update is a direct update of head_ref (the reference pointed to
  * by HEAD), then add an extra REF_LOG_ONLY update for HEAD.
@@ -2092,11 +2123,10 @@ static int split_head_update(struct ref_update *update,
 
        /*
         * First make sure that HEAD is not already in the
-        * transaction. This insertion is O(N) in the transaction
+        * transaction. This check is O(lg N) in the transaction
         * size, but it happens at most once per transaction.
         */
-       item = string_list_insert(affected_refnames, "HEAD");
-       if (item->util) {
+       if (string_list_has_string(affected_refnames, "HEAD")) {
                /* An entry already existed */
                strbuf_addf(err,
                            "multiple updates for 'HEAD' (including one "
@@ -2111,6 +2141,14 @@ static int split_head_update(struct ref_update *update,
                        update->new_oid.hash, update->old_oid.hash,
                        update->msg);
 
+       /*
+        * Add "HEAD". This insertion is O(N) in the transaction
+        * size, but it happens at most once per transaction.
+        * Add new_update->refname instead of a literal "HEAD".
+        */
+       if (strcmp(new_update->refname, "HEAD"))
+               BUG("%s unexpectedly not 'HEAD'", new_update->refname);
+       item = string_list_insert(affected_refnames, new_update->refname);
        item->util = new_update;
 
        return 0;
@@ -2137,13 +2175,12 @@ static int split_symref_update(struct files_ref_store *refs,
 
        /*
         * First make sure that referent is not already in the
-        * transaction. This insertion is O(N) in the transaction
+        * transaction. This check is O(lg N) in the transaction
         * size, but it happens at most once per symref in a
         * transaction.
         */
-       item = string_list_insert(affected_refnames, referent);
-       if (item->util) {
-               /* An entry already existed */
+       if (string_list_has_string(affected_refnames, referent)) {
+               /* An entry already exists */
                strbuf_addf(err,
                            "multiple updates for '%s' (including one "
                            "via symref '%s') are not allowed",
@@ -2178,6 +2215,17 @@ static int split_symref_update(struct files_ref_store *refs,
        update->flags |= REF_LOG_ONLY | REF_NODEREF;
        update->flags &= ~REF_HAVE_OLD;
 
+       /*
+        * Add the referent. This insertion is O(N) in the transaction
+        * size, but it happens at most once per symref in a
+        * transaction. Make sure to add new_update->refname, which will
+        * be valid as long as affected_refnames is in use, and NOT
+        * referent, which might soon be freed by our caller.
+        */
+       item = string_list_insert(affected_refnames, new_update->refname);
+       if (item->util)
+               BUG("%s unexpectedly found in affected_refnames",
+                   new_update->refname);
        item->util = new_update;
 
        return 0;
@@ -2249,7 +2297,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
        struct strbuf referent = STRBUF_INIT;
        int mustexist = (update->flags & REF_HAVE_OLD) &&
                !is_null_oid(&update->old_oid);
-       int ret;
+       int ret = 0;
        struct ref_lock *lock;
 
        files_assert_main_repository(refs, "lock_ref_for_update");
@@ -2261,7 +2309,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
                ret = split_head_update(update, transaction, head_ref,
                                        affected_refnames, err);
                if (ret)
-                       return ret;
+                       goto out;
        }
 
        ret = lock_raw_ref(refs, update->refname, mustexist,
@@ -2275,7 +2323,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
                strbuf_addf(err, "cannot lock ref '%s': %s",
                            original_update_refname(update), reason);
                free(reason);
-               return ret;
+               goto out;
        }
 
        update->backend_data = lock;
@@ -2294,10 +2342,12 @@ static int lock_ref_for_update(struct files_ref_store *refs,
                                        strbuf_addf(err, "cannot lock ref '%s': "
                                                    "error reading reference",
                                                    original_update_refname(update));
-                                       return -1;
+                                       ret = TRANSACTION_GENERIC_ERROR;
+                                       goto out;
                                }
                        } else if (check_old_oid(update, &lock->old_oid, err)) {
-                               return TRANSACTION_GENERIC_ERROR;
+                               ret = TRANSACTION_GENERIC_ERROR;
+                               goto out;
                        }
                } else {
                        /*
@@ -2311,13 +2361,15 @@ static int lock_ref_for_update(struct files_ref_store *refs,
                                                  referent.buf, transaction,
                                                  affected_refnames, err);
                        if (ret)
-                               return ret;
+                               goto out;
                }
        } else {
                struct ref_update *parent_update;
 
-               if (check_old_oid(update, &lock->old_oid, err))
-                       return TRANSACTION_GENERIC_ERROR;
+               if (check_old_oid(update, &lock->old_oid, err)) {
+                       ret = TRANSACTION_GENERIC_ERROR;
+                       goto out;
+               }
 
                /*
                 * If this update is happening indirectly because of a
@@ -2354,7 +2406,8 @@ static int lock_ref_for_update(struct files_ref_store *refs,
                                    "cannot update ref '%s': %s",
                                    update->refname, write_err);
                        free(write_err);
-                       return TRANSACTION_GENERIC_ERROR;
+                       ret = TRANSACTION_GENERIC_ERROR;
+                       goto out;
                } else {
                        update->flags |= REF_NEEDS_COMMIT;
                }
@@ -2368,10 +2421,14 @@ static int lock_ref_for_update(struct files_ref_store *refs,
                if (close_ref_gently(lock)) {
                        strbuf_addf(err, "couldn't close '%s.lock'",
                                    update->refname);
-                       return TRANSACTION_GENERIC_ERROR;
+                       ret = TRANSACTION_GENERIC_ERROR;
+                       goto out;
                }
        }
-       return 0;
+
+out:
+       strbuf_release(&referent);
+       return ret;
 }
 
 /*