git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 4/4] git-rebase -i: New option to support rebase with merges

From
JSJörg Sommer <joerg@alea.gnuu.de>
Date
Mar 23, 2008, 21:42 UTC
Message-ID
<1206308562-31489-4-git-send-email-joerg@alea.gnuu.de>
In-Reply-To
<1206308562-31489-3-git-send-email-joerg@alea.gnuu.de>

The option --preserve-merges does not allow to change the order of commits or squash them. The new option --linear-history does support this, but doing so it can only look at the commits reachable with through the first parent of each merge.

Joining merge commits with other commits leads to problems, because git merge fails with a dirty index (the case “COMMIT squash MERGE”) and squashing a merge leads to the lost of the parents (case “MERGE squash COMMIT”). Therefore, I've prohibited these cases.

Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
 Documentation/git-rebase.txt  |    8 ++++
 git-rebase--interactive.sh    |   27 +++++++++++++++-
 t/t3404-rebase-interactive.sh |   72 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 106 insertions(+), 1 deletions(-)

I had no better idea for a name of this new option. Propositions are welcome.

diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index e0412e0..354b6f0 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -11,6 +11,7 @@ SYNOPSIS
 'git-rebase' [-i | --interactive] [-v | --verbose] [-m | --merge]
 	[-s <strategy> | --strategy=<strategy>]
 	[-C<n>] [ --whitespace=<option>] [-p | --preserve-merges]
+	[-l | --linear-history]
 	[--onto <newbase>] <upstream> [<branch>]
 'git-rebase' --continue | --skip | --abort
 
@@ -247,6 +248,13 @@ OPTIONS
 	Instead of ignoring merges, try to recreate them.  This option
 	only works in interactive mode.
 
+-l, \--linear-history::
+	Use only commits of the branch they are not merged in, i.e.
+	follow only the first parent of a merge. Merges are part of this
+	list and they will be redone. It's possible to move merges in the
+	history forward and backward, but they can't take part on a join
+	(squash). This option only works in interactive mode.
+
 include::merge-strategies.txt[]
 
 NOTES
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 94c6827..a2a61f8 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -26,9 +26,11 @@ REWRITTEN="$DOTEST"/rewritten
 PRESERVE_MERGES=
 STRATEGY=
 VERBOSE=
+LINEAR_HISTORY=
 test -d "$REWRITTEN" && PRESERVE_MERGES=t
 test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)"
 test -f "$DOTEST"/verbose && VERBOSE=t
+test -f "$DOTEST"/linear_history && LINEAR_HISTORY=t
 
 GIT_CHERRY_PICK_HELP="  After resolving the conflicts,
 mark the corrected paths with 'git add <paths>', and
@@ -150,7 +152,18 @@ pick_one () {
 		sha1=$(git rev-parse --short $sha1)
 		output warn Fast forward to $sha1
 	else
-		output git cherry-pick "$@"
+		if test t = "$LINEAR_HISTORY" &&
+			other_parents="$(parents_of_commit $sha1 | cut -s -d' ' -f2-)" &&
+			test -n "$other_parents"
+		then
+			if test a"$1" = a-n
+			then
+				merge_opt=--no-commit
+			fi
+			redo_merge $sha1 $no_commit $other_parents
+		else
+			output git cherry-pick "$@"
+		fi
 	fi
 }
 
@@ -288,6 +301,11 @@ do_next () {
 		has_action "$DONE" ||
 			die "Cannot 'squash' without a previous commit"
 
+		test t = "$LINEAR_HISTORY" &&
+			( test "$(parents_of_commit HEAD |wc -w)" -gt 1 ||
+				 test "$(parents_of_commit $sha1 |wc -w)" -gt 1) &&
+			die "Joining a merge with a commit is not supported"
+
 		mark_action_done
 		make_squash_message $sha1 > "$MSG"
 		case "$(peek_next_command)" in
@@ -459,6 +477,9 @@ do
 	-i|--interactive)
 		# yeah, we know
 		;;
+	-l|--linear-history)
+		LINEAR_HISTORY=t
+		;;
 	''|-h)
 		usage
 		;;
@@ -522,6 +543,10 @@ do
 					die "Could not init rewritten commits"
 			done
 			MERGES_OPTION=
+		elif test t = "$LINEAR_HISTORY"
+		then
+			: > "$DOTEST"/linear_history
+			MERGES_OPTION=--first-parent
 		else
 			MERGES_OPTION=--no-merges
 		fi
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 9cf873f..0476f6a 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -361,4 +361,76 @@ test_expect_success 'rebase with a file named HEAD in worktree' '
 
 '
 
+test_expect_success 'rebase linear history preserves merges' '
+	git tag linear-history-base to-be-preserved~2
+	git checkout -b linear-history linear-history-base &&
+	for i in 1 2 3
+	do
+		test $? -eq 0 &&
+		echo linear history test > lin-h-$i &&
+		git add lin-h-$i &&
+		test_tick &&
+	       	git commit -m "rebase linear history commit $i"
+	done
+	test_tick &&
+	git merge to-be-preserved &&
+	old_head=$(git rev-parse HEAD) &&
+	test_tick &&
+	EXPECT_COUNT=4 FAKE_LINES="2 4 edit 1 3" \
+		git rebase -v -i -l linear-history-base &&
+	EXPECT_COUNT=invalid git rebase --continue &&
+	test "$(git rev-list --parents -1 HEAD~2 | cut -d" " -f3-)" = \
+		"$(git rev-parse to-be-preserved)" &&
+	test "$(git show HEAD~2: | grep ^lin-h- | cut -c7- | tr -d \\012)" = 2 &&
+	test "$(git show HEAD~1: | grep ^lin-h- | cut -c7- | tr -d \\012)" = 12 &&
+	test "$(git cat-file commit HEAD | sed -n "/^tree/{p;q;}")" = \
+		"$(git cat-file commit $old_head | sed -n "/^tree/{p;q;}")"
+'
+
+test_expect_success 'rebase linear history is noop, if base = base' '
+	old_head=$(git rev-parse HEAD) &&
+	test_tick &&
+	EXPECT_COUNT=4 git rebase -v -i -l linear-history-base &&
+	test "$(git rev-parse HEAD)" = $old_head
+'
+
+test_expect_success 'ensure rebase linear history persits across edits' '
+	old_head=$(git rev-parse HEAD) &&
+	test_tick &&
+	EXPECT_COUNT=4 FAKE_LINES="edit 1 2 3 4" \
+		git rebase -v -i -l linear-history-base &&
+	EXPECT_COUNT=invalid git rebase --continue &&
+	test "$(git rev-parse HEAD)" = $old_head
+'
+
+test_str='test_tick &&
+	(
+		outp=$(test_must_fail git rebase -v -i -l HEAD~3 2>&1)
+		rc=$?
+		echo "$outp"
+        	echo "$outp" | grep "^Joining .* not supported\$" >/dev/null &&
+		return $rc
+	) &&
+	git rebase --abort'
+
+test_expect_success 'rebase linear with squashing a merge fails' "
+	export EXPECT_COUNT=3 FAKE_LINES='1 squash 2 3' &&
+	$test_str &&
+	FAKE_LINES='2 squash 1 3' &&
+	$test_str
+"
+
+test_expect_success 'rebase linear history does a fast forward' '
+	old_head=$(git rev-parse HEAD) &&
+	test_tick &&
+	EXPECT_COUNT=4 FAKE_LINES="2 3 4 1" \
+		git rebase -v -i -l linear-history-base &&
+	test "$(git rev-parse HEAD~3)" = "$(git rev-parse to-be-preserved)" &&
+	test "$(git show HEAD~2: | grep ^lin-h- | cut -c7- | tr -d \\012)" = 1 &&
+	test "$(git show HEAD~1: | grep ^lin-h- | cut -c7- | tr -d \\012)" = 13 &&
+	test -z "$(git rev-list --parents -1 HEAD~3 | cut -d" " -f3-)" &&
+	test "$(git cat-file commit HEAD | sed -n "/^tree/{p;q;}")" = \
+		"$(git cat-file commit $old_head | sed -n "/^tree/{p;q;}")"
+'
+
 test_done
-- 
1.5.4.4
Previous: Jörg SommerNext: Johannes Schindelin
Message 4 of 104 in “Move redo merge code in a function”
  1. 1/4 Move redo merge code in a functionJörg Sommer, Mar 23, 2008
  2. 2/4 Rework redo_mergeJörg Sommer, Mar 23, 2008
  3. 3/4 Add a function for get the parents of a commitJörg Sommer, Mar 23, 2008
  4. 4/4 git-rebase -i: New option to support rebase with mergesJörg Sommer, Mar 23, 2008
  5. Johannes SchindelinMar 23, 2008
  6. Jörg SommerMar 24, 2008
  7. Johannes SchindelinMar 24, 2008
  8. Jörg SommerMar 24, 2008
  9. Junio C HamanoMar 24, 2008
  10. Junio C HamanoMar 24, 2008
  11. Jörg SommerMar 25, 2008
  12. Junio C HamanoMar 26, 2008
  13. Teach rebase interactive more commands to do better preserve mergesJörg Sommer, Apr 9, 2008
  14. 01/10 Teach rebase interactive the mark commandJörg Sommer, Apr 9, 2008
  15. 02/10 Teach rebase interactive the reset commandJörg Sommer, Apr 9, 2008
  16. 03/10 Teach rebase interactive the merge commandJörg Sommer, Apr 9, 2008
  17. 04/10 Move redo merge code in a functionJörg Sommer, Apr 9, 2008
  18. 05/10 Rework redo_mergeJörg Sommer, Apr 9, 2008
  19. 06/10 Unify the lenght of $SHORT* and the commits in the TODO listJörg Sommer, Apr 9, 2008
  20. 07/10 fake-editor: output TODO list if unchangedJörg Sommer, Apr 9, 2008
  21. 08/10 Don't append default merge message to -m messageJörg Sommer, Apr 9, 2008
  22. 09/10 Select all lines with fake-editorJörg Sommer, Apr 9, 2008
  23. 10/10 Do rebase with preserve merges with advanced TODO listJörg Sommer, Apr 9, 2008
  24. Junio C HamanoApr 12, 2008
  25. Jörg SommerApr 12, 2008
  26. Junio C HamanoApr 13, 2008
  27. Jörg SommerApr 13, 2008
  28. TarmiganApr 14, 2008
  29. Junio C HamanoApr 11, 2008
  30. Jörg SommerApr 12, 2008
  31. Mike RalphsonApr 10, 2008
  32. Jörg SommerApr 12, 2008
  33. Junio C HamanoApr 11, 2008
  34. Jörg SommerApr 12, 2008
  35. Shawn O. PearceApr 13, 2008
  36. Jörg SommerApr 13, 2008
  37. Shawn O. PearceApr 14, 2008
  38. Junio C HamanoApr 14, 2008
  39. Jörg SommerApr 14, 2008
  40. 01/13 fake-editor: output TODO list if unchangedJörg Sommer, Apr 14, 2008
  41. 02/13 Don't append default merge message to -m messageJörg Sommer, Apr 14, 2008
  42. 03/13 Move cleanup code into it's own functionJörg Sommer, Apr 14, 2008
  43. 04/13 Teach rebase interactive the mark commandJörg Sommer, Apr 14, 2008
  44. 05/13 Teach rebase interactive the reset commandJörg Sommer, Apr 14, 2008
  45. 06/13 Move redo merge code in a functionJörg Sommer, Apr 14, 2008
  46. 07/13 Teach rebase interactive the merge commandJörg Sommer, Apr 14, 2008
  47. 08/13 Unify the lenght of $SHORT* and the commits in the TODO listJörg Sommer, Apr 14, 2008
  48. 09/13 Select all lines with fake-editorJörg Sommer, Apr 14, 2008
  49. 10/13 Do rebase with preserve merges with advanced TODO listJörg Sommer, Apr 14, 2008
  50. 11/13 Add option --first-parentJörg Sommer, Apr 14, 2008
  51. 12/13 Teach rebase interactive the tag commandJörg Sommer, Apr 14, 2008
  52. 13/13 Add option --preserve-tagsJörg Sommer, Apr 14, 2008
  53. Junio C HamanoApr 22, 2008
  54. Junio C HamanoApr 22, 2008
  55. Johannes SchindelinApr 22, 2008
  56. Jörg SommerApr 22, 2008
  57. Johannes SchindelinApr 22, 2008
  58. Junio C HamanoApr 22, 2008
  59. Johannes SchindelinApr 22, 2008
  60. Junio C HamanoApr 29, 2008
  61. Johannes SchindelinApr 29, 2008
  62. Junio C HamanoApr 29, 2008
  63. Johannes SixtApr 29, 2008
  64. Johannes SchindelinApr 29, 2008
  65. Junio C HamanoApr 29, 2008
  66. Johannes SchindelinApr 29, 2008
  67. Junio C HamanoApr 29, 2008
  68. Johannes SchindelinApr 29, 2008
  69. Junio C HamanoApr 29, 2008
  70. Johannes SchindelinApr 29, 2008
  71. Junio C HamanoApr 30, 2008
  72. Johannes SixtApr 30, 2008
  73. Junio C HamanoApr 30, 2008
  74. Johannes SchindelinApr 30, 2008
  75. Junio C HamanoApr 30, 2008
  76. Johannes SixtApr 30, 2008
  77. Johannes SchindelinApr 30, 2008
  78. Junio C HamanoMay 1, 2008
  79. Johannes SchindelinMay 3, 2008
  80. Junio C HamanoMay 3, 2008
  81. Johannes SchindelinMay 4, 2008
  82. Jörg SommerMay 4, 2008
  83. Dmitry PotapovApr 30, 2008
  84. Johannes SchindelinMay 1, 2008
  85. Junio C HamanoApr 22, 2008
  86. Jörg SommerApr 25, 2008
  87. Teach rebase interactive the mark commandJörg Sommer, Apr 25, 2008
  88. Junio C HamanoApr 27, 2008
  89. Jörg SommerApr 27, 2008
  90. Teach rebase interactive the mark commandJörg Sommer, Apr 14, 2008
  91. Shawn O. PearceApr 14, 2008
  92. mark parsing in fast-importJörg Sommer, Apr 20, 2008
  93. Shawn O. PearceApr 21, 2008
  94. Jörg SommerApr 21, 2008
  95. Shawn O. PearceApr 21, 2008
  96. Jörg SommerApr 22, 2008
  97. Shawn O. PearceApr 22, 2008
  98. Make mark parsing much more restrictiveJörg Sommer, Apr 25, 2008
  99. Junio C HamanoApr 20, 2008
  100. Jörg SommerApr 21, 2008
  101. Junio C HamanoApr 22, 2008
  102. Johannes SchindelinMar 23, 2008
  103. Johannes SchindelinMar 23, 2008
  104. Johannes SchindelinMar 23, 2008

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.