Skip to content

Commit 8a2a121

Browse files
committed
replay: drop commits that become empty
If the changes in a commit being replayed are already in the branch that the commits are being replayed onto then "git replay" creates an empty commit. This is confusing because the commit message no longer matches the contents of the commit. Drop the commit instead. Commits that start off empty are not dropped. This matches the behavior of "git rebase --reapply-cherry-pick --empty=drop" and "git cherry-pick --empty-drop". If a branch points to a commit that is dropped it will be updated to point to the last commit that was not dropped. This can been seen in the new test where "topic1" is updated to point to the rebased "C" as "F" is dropped because it is already upstream. While this is a breaking change "git replay" is marked as experimental to allow improvements like this that change the behavior. Signed-off-by: Phillip Wood <[email protected]>
1 parent 4ac8283 commit 8a2a121

File tree

3 files changed

+35
-4
lines changed

3 files changed

+35
-4
lines changed

Documentation/git-replay.adoc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,9 @@ The default mode can be configured via the `replay.refAction` configuration vari
5959
be passed, but in `--advance <branch>` mode, they should have
6060
a single tip, so that it's clear where <branch> should point
6161
to. See "Specifying Ranges" in linkgit:git-rev-parse[1] and the
62-
"Commit Limiting" options below.
62+
"Commit Limiting" options below. Any commits in the range whose
63+
changes are already present in the branch the commits are being
64+
replayed onto will be dropped.
6365

6466
include::rev-list-options.adoc[]
6567

replay.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,12 @@ struct commit *replay_pick_regular_commit(struct repository *repo,
8888
struct merge_result *result)
8989
{
9090
struct commit *base, *replayed_base;
91-
struct tree *pickme_tree, *base_tree;
91+
struct tree *pickme_tree, *base_tree, *replayed_base_tree;
9292

9393
base = pickme->parents->item;
9494
replayed_base = mapped_commit(replayed_commits, base, onto);
9595

96-
result->tree = repo_get_commit_tree(repo, replayed_base);
96+
replayed_base_tree = repo_get_commit_tree(repo, replayed_base);
9797
pickme_tree = repo_get_commit_tree(repo, pickme);
9898
base_tree = repo_get_commit_tree(repo, base);
9999

@@ -103,13 +103,17 @@ struct commit *replay_pick_regular_commit(struct repository *repo,
103103

104104
merge_incore_nonrecursive(merge_opt,
105105
base_tree,
106-
result->tree,
106+
replayed_base_tree,
107107
pickme_tree,
108108
result);
109109

110110
free((char*)merge_opt->ancestor);
111111
merge_opt->ancestor = NULL;
112112
if (!result->clean)
113113
return NULL;
114+
/* Drop commits that become empty */
115+
if (oideq(&replayed_base_tree->object.oid, &result->tree->object.oid) &&
116+
!oideq(&pickme_tree->object.oid, &base_tree->object.oid))
117+
return replayed_base;
114118
return replay_create_commit(repo, result->tree, pickme, replayed_base);
115119
}

t/t3650-replay-basics.sh

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ test_expect_success 'setup' '
2525
git switch -c topic3 &&
2626
test_commit G &&
2727
test_commit H &&
28+
git switch -c empty &&
29+
git commit --allow-empty --only -m empty &&
2830
git switch -c topic4 main &&
2931
test_commit I &&
3032
test_commit J &&
@@ -106,6 +108,29 @@ test_expect_success 'using replay on bare repo to perform basic cherry-pick' '
106108
test_cmp expect result-bare
107109
'
108110

111+
test_expect_success 'commits that become empty are dropped' '
112+
git replay --ref-action=print --advance main topic1^! >result &&
113+
ONTO=$(cut -f 3 -d " " result) &&
114+
git replay --ref-action=print --onto $ONTO \
115+
--branches --ancestry-path=empty ^A >result &&
116+
# Write the new value of refs/heads/empty to "new-empty" and
117+
# generate a sed script that annotates the output of
118+
# `git log --format="%H %s"` with the updated branches
119+
SCRIPT="$(sed -e "
120+
/empty/{
121+
h
122+
s|^.*empty \([^ ]*\) .*|\1|wnew-empty
123+
g
124+
}
125+
s|^.*/\([^/ ]*\) \([^ ]*\).*|/^\2/s/\\\$/ (\1)/|
126+
\$s|\$|;s/^[^ ]* //|" result)" &&
127+
git log --format="%H %s" --stdin <new-empty >actual.raw &&
128+
sed -e "$SCRIPT" actual.raw >actual &&
129+
test_write_lines >expect \
130+
"empty (empty)" "H (topic3)" G "C (topic1)" F M L B A &&
131+
test_cmp expect actual
132+
'
133+
109134
test_expect_success 'replay on bare repo fails with both --advance and --onto' '
110135
test_must_fail git -C bare replay --advance main --onto main topic1..topic2 >result-bare
111136
'

0 commit comments

Comments
 (0)