From: Patrick Steinhardt Date: Mon, 27 Apr 2026 05:53:51 +0000 (+0200) Subject: replay: allow callers to control what happens with empty commits X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=0cf4ad7cf555ac2ed6556a3169d30b2eabb68d41;p=thirdparty%2Fgit.git replay: allow callers to control what happens with empty commits 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 Signed-off-by: Junio C Hamano --- diff --git a/replay.c b/replay.c index f96f1f6551..4ef8abb607 100644 --- 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; diff --git a/replay.h b/replay.h index 0ab74b9805..1851a07705 100644 --- 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()`. */