diff --git a/Documentation/git-replay.adoc b/Documentation/git-replay.adoc index 4c61f3aa1f..dc966486ca 100644 --- a/Documentation/git-replay.adoc +++ b/Documentation/git-replay.adoc @@ -62,7 +62,9 @@ The default mode can be configured via the `replay.refAction` configuration vari Range of commits to replay; see "Specifying Ranges" in linkgit:git-rev-parse[1]. In `--advance ` mode, the range should have a single tip, so that it's clear to which tip the - advanced should point. + advanced should point. Any commits in the range whose + changes are already present in the branch the commits are being + replayed onto will be dropped. include::rev-list-options.adoc[] diff --git a/replay.c b/replay.c index 74e45ed27a..1266c70630 100644 --- a/replay.c +++ b/replay.c @@ -211,12 +211,12 @@ static struct commit *pick_regular_commit(struct repository *repo, struct merge_result *result) { struct commit *base, *replayed_base; - struct tree *pickme_tree, *base_tree; + struct tree *pickme_tree, *base_tree, *replayed_base_tree; base = pickme->parents->item; replayed_base = mapped_commit(replayed_commits, base, onto); - result->tree = repo_get_commit_tree(repo, replayed_base); + replayed_base_tree = repo_get_commit_tree(repo, replayed_base); pickme_tree = repo_get_commit_tree(repo, pickme); base_tree = repo_get_commit_tree(repo, base); @@ -226,7 +226,7 @@ static struct commit *pick_regular_commit(struct repository *repo, merge_incore_nonrecursive(merge_opt, base_tree, - result->tree, + replayed_base_tree, pickme_tree, result); @@ -234,6 +234,10 @@ static struct commit *pick_regular_commit(struct repository *repo, merge_opt->ancestor = NULL; if (!result->clean) return NULL; + /* Drop 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; return create_commit(repo, result->tree, pickme, replayed_base); } diff --git a/t/t3650-replay-basics.sh b/t/t3650-replay-basics.sh index 307101eeb9..3d965fb994 100755 --- a/t/t3650-replay-basics.sh +++ b/t/t3650-replay-basics.sh @@ -25,6 +25,8 @@ test_expect_success 'setup' ' git switch -c topic3 && test_commit G && test_commit H && + git switch -c empty && + git commit --allow-empty -m empty && git switch -c topic4 main && test_commit I && test_commit J && @@ -160,6 +162,25 @@ test_expect_success 'using replay on bare repo to perform basic cherry-pick' ' test_cmp expect result-bare ' +test_expect_success 'commits that become empty are dropped' ' + # Save original branches + git for-each-ref --format="update %(refname) %(objectname)" \ + refs/heads/ >original-branches && + test_when_finished "git update-ref --stdin result && + git log --format="%s%d" L..empty >actual && + test_write_lines >expect \ + "empty (empty)" "H (topic3)" G "C (topic1)" "F (main)" "M (tag: M)" && + test_cmp expect actual +' + test_expect_success 'replay on bare repo fails with both --advance and --onto' ' test_must_fail git -C bare replay --advance main --onto main topic1..topic2 >result-bare '