Merge branch 'pw/replay-drop-empty' into seen

"git replay" is taught to drop commits that become empty (not the
ones that are empty in the original).

* pw/replay-drop-empty:
  replay: drop commits that become empty
This commit is contained in:
Junio C Hamano
2025-12-12 22:11:52 +09:00
3 changed files with 35 additions and 4 deletions

View File

@@ -61,7 +61,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.
include::rev-list-options.adoc[]

View File

@@ -88,12 +88,12 @@ struct commit *replay_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);
@@ -103,7 +103,7 @@ struct commit *replay_pick_regular_commit(struct repository *repo,
merge_incore_nonrecursive(merge_opt,
base_tree,
result->tree,
replayed_base_tree,
pickme_tree,
result);
@@ -111,5 +111,9 @@ struct commit *replay_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 replay_create_commit(repo, result->tree, pickme, replayed_base);
}

View File

@@ -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 --only -m empty &&
git switch -c topic4 main &&
test_commit I &&
test_commit J &&
@@ -106,6 +108,29 @@ 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' '
git replay --ref-action=print --advance main topic1^! >result &&
ONTO=$(cut -f 3 -d " " result) &&
git replay --ref-action=print --onto $ONTO \
--branches --ancestry-path=empty ^A >result &&
# Write the new value of refs/heads/empty to "new-empty" and
# generate a sed script that annotates the output of
# `git log --format="%H %s"` with the updated branches
SCRIPT="$(sed -e "
/empty/{
h
s|^.*empty \([^ ]*\) .*|\1|wnew-empty
g
}
s|^.*/\([^/ ]*\) \([^ ]*\).*|/^\2/s/\\\$/ (\1)/|
\$s|\$|;s/^[^ ]* //|" result)" &&
git log --format="%H %s" --stdin <new-empty >actual.raw &&
sed -e "$SCRIPT" actual.raw >actual &&
test_write_lines >expect \
"empty (empty)" "H (topic3)" G "C (topic1)" F M L B A &&
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
'