]> git.ipfire.org Git - thirdparty/git.git/commitdiff
builtin/history: split handling of ref updates into two phases
authorPatrick Steinhardt <ps@pks.im>
Wed, 3 Jun 2026 16:14:07 +0000 (18:14 +0200)
committerJunio C Hamano <gitster@pobox.com>
Thu, 4 Jun 2026 00:04:26 +0000 (09:04 +0900)
The function `handle_reference_updates()` is used by git-history(1) to
update all references that refer to commits that have been rewritten. As
such, it performs two steps:

  - It gathers the references that need to be updated in the first
    place.

  - It prepares and commits the reference transaction.

In a subsequent commit we'll want to handle those two steps separately.
Prepare for this by splitting up the function into two.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin/history.c

index 0fc06fb2045814a5467862ecefcd436528a1b817..4fadf38c32bb2e5670f7109673101d3947f687a4 100644 (file)
@@ -333,21 +333,17 @@ static int handle_ref_update(struct ref_transaction *transaction,
                                      NULL, NULL, 0, reflog_msg, err);
 }
 
-static int handle_reference_updates(struct rev_info *revs,
-                                   enum ref_action action,
-                                   struct commit *original,
-                                   struct commit *rewritten,
-                                   const char *reflog_msg,
-                                   int dry_run,
-                                   enum replay_empty_commit_action empty)
+static int compute_pending_ref_updates(struct rev_info *revs,
+                                      enum ref_action action,
+                                      struct commit *original,
+                                      struct commit *rewritten,
+                                      enum replay_empty_commit_action empty,
+                                      struct replay_result *result)
 {
        const struct name_decoration *decoration;
        struct replay_revisions_options opts = {
                .empty = empty,
        };
-       struct replay_result result = { 0 };
-       struct ref_transaction *transaction = NULL;
-       struct strbuf err = STRBUF_INIT;
        char hex[GIT_MAX_HEXSZ + 1];
        bool detached_head;
        int head_flags = 0;
@@ -359,34 +355,13 @@ static int handle_reference_updates(struct rev_info *revs,
 
        opts.onto = oid_to_hex_r(hex, &rewritten->object.oid);
 
-       ret = replay_revisions(revs, &opts, &result);
+       ret = replay_revisions(revs, &opts, result);
        if (ret)
-               goto out;
+               return ret;
 
        if (action != REF_ACTION_BRANCHES && action != REF_ACTION_HEAD)
                BUG("unsupported ref action %d", action);
 
-       if (!dry_run) {
-               transaction = ref_store_transaction_begin(get_main_ref_store(revs->repo), 0, &err);
-               if (!transaction) {
-                       ret = error(_("failed to begin ref transaction: %s"), err.buf);
-                       goto out;
-               }
-       }
-
-       for (size_t i = 0; i < result.updates_nr; i++) {
-               ret = handle_ref_update(transaction,
-                                       result.updates[i].refname,
-                                       &result.updates[i].new_oid,
-                                       &result.updates[i].old_oid,
-                                       reflog_msg, &err);
-               if (ret) {
-                       ret = error(_("failed to update ref '%s': %s"),
-                                   result.updates[i].refname, err.buf);
-                       goto out;
-               }
-       }
-
        /*
         * `replay_revisions()` only updates references that are
         * ancestors of `rewritten`, so we need to manually
@@ -414,14 +389,43 @@ static int handle_reference_updates(struct rev_info *revs,
                    !detached_head)
                        continue;
 
+               ALLOC_GROW(result->updates, result->updates_nr + 1, result->updates_alloc);
+               result->updates[result->updates_nr].refname = xstrdup(decoration->name);
+               result->updates[result->updates_nr].old_oid = original->object.oid;
+               result->updates[result->updates_nr].new_oid = rewritten->object.oid;
+               result->updates_nr++;
+       }
+
+       return 0;
+}
+
+static int apply_pending_ref_updates(struct repository *repo,
+                                    const struct replay_result *result,
+                                    const char *reflog_msg,
+                                    int dry_run)
+{
+       struct ref_transaction *transaction = NULL;
+       struct strbuf err = STRBUF_INIT;
+       int ret;
+
+       if (!dry_run) {
+               transaction = ref_store_transaction_begin(get_main_ref_store(repo),
+                                                         0, &err);
+               if (!transaction) {
+                       ret = error(_("failed to begin ref transaction: %s"), err.buf);
+                       goto out;
+               }
+       }
+
+       for (size_t i = 0; i < result->updates_nr; i++) {
                ret = handle_ref_update(transaction,
-                                       decoration->name,
-                                       &rewritten->object.oid,
-                                       &original->object.oid,
+                                       result->updates[i].refname,
+                                       &result->updates[i].new_oid,
+                                       &result->updates[i].old_oid,
                                        reflog_msg, &err);
                if (ret) {
                        ret = error(_("failed to update ref '%s': %s"),
-                                   decoration->name, err.buf);
+                                   result->updates[i].refname, err.buf);
                        goto out;
                }
        }
@@ -435,11 +439,33 @@ static int handle_reference_updates(struct rev_info *revs,
 
 out:
        ref_transaction_free(transaction);
-       replay_result_release(&result);
        strbuf_release(&err);
        return ret;
 }
 
+static int handle_reference_updates(struct rev_info *revs,
+                                   enum ref_action action,
+                                   struct commit *original,
+                                   struct commit *rewritten,
+                                   const char *reflog_msg,
+                                   int dry_run,
+                                   enum replay_empty_commit_action empty)
+{
+       struct replay_result result = { 0 };
+       int ret;
+
+       ret = compute_pending_ref_updates(revs, action, original, rewritten,
+                                         empty, &result);
+       if (ret)
+               goto out;
+
+       ret = apply_pending_ref_updates(revs->repo, &result, reflog_msg, dry_run);
+
+out:
+       replay_result_release(&result);
+       return ret;
+}
+
 static int commit_became_empty(struct repository *repo,
                               struct commit *original,
                               struct tree *result)