]> git.ipfire.org Git - thirdparty/git.git/commitdiff
replay: allow callers to control what happens with empty commits
authorPatrick Steinhardt <ps@pks.im>
Mon, 27 Apr 2026 05:53:51 +0000 (07:53 +0200)
committerJunio C Hamano <gitster@pobox.com>
Mon, 27 Apr 2026 13:20:56 +0000 (22:20 +0900)
When replaying commits it may happen that some of the commits become
empty relative to their parent. Such commits are for now automatically
dropped by the replay subsystem without much control from the user.

Introduce a new enum that allows the caller to drop, keep or abort in
this case.

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

index f96f1f6551ae631d850dc1e928826e708342a193..4ef8abb607770a69f99600330a0c4a7713f69b0b 100644 (file)
--- a/replay.c
+++ b/replay.c
@@ -269,7 +269,8 @@ static struct commit *pick_regular_commit(struct repository *repo,
                                          struct commit *onto,
                                          struct merge_options *merge_opt,
                                          struct merge_result *result,
-                                         enum replay_mode mode)
+                                         enum replay_mode mode,
+                                         enum replay_empty_commit_action empty)
 {
        struct commit *base, *replayed_base;
        struct tree *pickme_tree, *base_tree, *replayed_base_tree;
@@ -321,12 +322,25 @@ static struct commit *pick_regular_commit(struct repository *repo,
        }
        merge_opt->ancestor = NULL;
        merge_opt->branch2 = NULL;
+
        if (!result->clean)
                return NULL;
-       /* Drop commits that become empty */
+
+       /* Handle commits that become empty */
        if (oideq(&replayed_base_tree->object.oid, &result->tree->object.oid) &&
-           !oideq(&pickme_tree->object.oid, &base_tree->object.oid))
-               return replayed_base;
+           !oideq(&pickme_tree->object.oid, &base_tree->object.oid)) {
+               switch (empty) {
+               case REPLAY_EMPTY_COMMIT_DROP:
+                       return replayed_base;
+               case REPLAY_EMPTY_COMMIT_KEEP:
+                       break;
+               case REPLAY_EMPTY_COMMIT_ABORT:
+                       result->clean = error(_("commit %s became empty after replay"),
+                                             oid_to_hex(&pickme->object.oid));
+                       return NULL;
+               }
+       }
+
        return create_commit(repo, result->tree, pickme, replayed_base, mode);
 }
 
@@ -417,7 +431,7 @@ int replay_revisions(struct rev_info *revs,
 
                last_commit = pick_regular_commit(revs->repo, commit, replayed_commits,
                                                  mode == REPLAY_MODE_REVERT ? last_commit : onto,
-                                                 &merge_opt, &result, mode);
+                                                 &merge_opt, &result, mode, opts->empty);
                if (!last_commit)
                        break;
 
@@ -458,6 +472,11 @@ int replay_revisions(struct rev_info *revs,
                }
        }
 
+       if (result.clean < 0) {
+               ret = -1;
+               goto out;
+       }
+
        if (!result.clean) {
                ret = 1;
                goto out;
index 0ab74b9805af1664a9464fbd12d727b200db2f48..1851a07705ab030a61ea651680320237772db02d 100644 (file)
--- a/replay.h
+++ b/replay.h
@@ -6,6 +6,19 @@
 struct repository;
 struct rev_info;
 
+/*
+ * Controls what happens when a replayed commit becomes empty (i.e. its tree
+ * is identical to its parent's tree after the replay).
+ */
+enum replay_empty_commit_action {
+       /* Silently discard the empty commit. */
+       REPLAY_EMPTY_COMMIT_DROP,
+       /* Keep the empty commit as-is. */
+       REPLAY_EMPTY_COMMIT_KEEP,
+       /* Abort with an error. */
+       REPLAY_EMPTY_COMMIT_ABORT,
+};
+
 /*
  * A set of options that can be passed to `replay_revisions()`.
  */
@@ -43,6 +56,12 @@ struct replay_revisions_options {
         * Requires `onto` to be set.
         */
        int contained;
+
+       /*
+        * Controls what to do when a replayed commit becomes empty.
+        * Defaults to REPLAY_EMPTY_COMMIT_DROP.
+        */
+       enum replay_empty_commit_action empty;
 };
 
 /* This struct is used as an out-parameter by `replay_revisions()`. */