aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/git-replay.adoc4
-rw-r--r--replay.c10
-rwxr-xr-xt/t3650-replay-basics.sh21
3 files changed, 31 insertions, 4 deletions
diff --git a/Documentation/git-replay.adoc b/Documentation/git-replay.adoc
index c3b214ec69..8d696ce3ab 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 <branch>` mode, the
range should have a single tip, so that it's clear to which tip the
- advanced <branch> should point.
+ advanced <branch> should point. Any commits in the range whose
+ changes are already present in the branch the commits are being
+ replayed onto will be dropped.
:git-replay: 1
include::rev-list-options.adoc[]
diff --git a/replay.c b/replay.c
index 94fb76384b..f97d652f33 100644
--- a/replay.c
+++ b/replay.c
@@ -217,12 +217,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);
@@ -232,7 +232,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);
@@ -240,6 +240,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 c862aa39f3..a03f8f9293 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 <original-branches &&
+ rm original-branches" &&
+ # Cherry-pick tip of topic1 ("F"), from the middle of A..empty, to main
+ git replay --advance main topic1^! &&
+
+ # Replay all of A..empty onto main (which includes topic1 & thus F
+ # in the middle)
+ git replay --onto main --branches --ancestry-path=empty ^A \
+ >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
'