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 <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
main
Patrick Steinhardt 2026-04-27 07:53:51 +02:00 committed by Junio C Hamano
parent 94f057755b
commit 0cf4ad7cf5
2 changed files with 43 additions and 5 deletions

View File

@ -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;

View File

@ -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()`. */