git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] replay: drop commits that become empty
@ 2025-11-27 16:15 Phillip Wood
  2025-11-28  7:29 ` Junio C Hamano
                   ` (4 more replies)
  0 siblings, 5 replies; 16+ messages in thread
From: Phillip Wood @ 2025-11-27 16:15 UTC (permalink / raw)
  To: git; +Cc: Elijah Newren, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

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 <phillip.wood@dunelm.org.uk>
---
Elijah - I'm not really clear why we were setting result->tree before
calling merge_incore_nonrecursive(), was it just for convenience to
avoid declaring a local variable or have I missed something?

This patch is based on ps/history

I think dropping commits that become empty is the sensible default,
if it turns out that some users are relying on the current behavior
we can add an option to retain the empty commits.

Base-Commit: 4ac8283def34401e50908903b89fa22498bb23a2
Published-As: https://github.com/phillipwood/git/releases/tag/pw%2Freplay-drop-commits-that-become-empty%2Fv1
View-Changes-At: https://github.com/phillipwood/git/compare/4ac8283de...8a2a12153
Fetch-It-Via: git fetch https://github.com/phillipwood/git pw/replay-drop-commits-that-become-empty/v1

 Documentation/git-replay.adoc |  4 +++-
 replay.c                      | 10 +++++++---
 t/t3650-replay-basics.sh      | 25 +++++++++++++++++++++++++
 3 files changed, 35 insertions(+), 4 deletions(-)

diff --git a/Documentation/git-replay.adoc b/Documentation/git-replay.adoc
index dcb26e8a8e8..96a3a557bf3 100644
--- a/Documentation/git-replay.adoc
+++ b/Documentation/git-replay.adoc
@@ -59,7 +59,9 @@ The default mode can be configured via the `replay.refAction` configuration vari
 	be passed, but in `--advance <branch>` mode, they should have
 	a single tip, so that it's clear where <branch> should point
 	to. See "Specifying Ranges" in linkgit:git-rev-parse[1] and the
-	"Commit Limiting" options below.
+	"Commit Limiting" options below. 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 58fdc20140b..7cd7206eee5 100644
--- a/replay.c
+++ b/replay.c
@@ -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,13 +103,17 @@ struct commit *replay_pick_regular_commit(struct repository *repo,
 
 	merge_incore_nonrecursive(merge_opt,
 				  base_tree,
-				  result->tree,
+				  replayed_base_tree,
 				  pickme_tree,
 				  result);
 
 	free((char*)merge_opt->ancestor);
 	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);
 }
diff --git a/t/t3650-replay-basics.sh b/t/t3650-replay-basics.sh
index cf3aacf3551..d73ab16908a 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 --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
 '
-- 
2.52.0.362.g884e03848a9


^ permalink raw reply related	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2025-12-19  4:44 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-27 16:15 [PATCH] replay: drop commits that become empty Phillip Wood
2025-11-28  7:29 ` Junio C Hamano
2025-12-04 14:08   ` Phillip Wood
2025-11-28  8:06 ` Elijah Newren
2025-12-04 14:06   ` Phillip Wood
2025-12-15 10:07 ` [PATCH v2] " Phillip Wood
2025-12-15 23:50   ` Junio C Hamano
2025-12-16 14:19     ` Phillip Wood
2025-12-17 14:45     ` Phillip Wood
2025-12-17 23:49       ` Junio C Hamano
2025-12-16  0:21   ` Elijah Newren
2025-12-16 14:19 ` [PATCH v3] " Phillip Wood
2025-12-16 16:36   ` Elijah Newren
2025-12-17 14:47     ` Phillip Wood
2025-12-18 16:50 ` [PATCH v4] " Phillip Wood
2025-12-19  4:44   ` Junio C Hamano

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).