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