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

Re: [PATCH] Fix rebase -p --onto

From
Greg Price <price@ksplice.com>
Date
Jul 17, 2009, 16:33 UTC
Message-ID
<20090717163350.GK7878@vinegar-pot.mit.edu>
In-Reply-To
<7vk52767el.fsf@alter.siamese.dyndns.org>
On Fri, Jul 17, 2009 at 01:27:46AM -0700, Junio C Hamano wrote:
Show 18 quoted lines
> Hmm, if the original history were
> 
>  .---X---o---o---o---M
>   \                 /
>    x---x---x---x---x
> 
>      Y
> 
> and the rebase is about moving history leading to M since X on top of Y,
> I would actually have agreed that this
> 
>  .---X---o---o---o---M
>   \                 /
>    x---x---x---x---x
>                     \
>      Y---o'--o'--o'--M'
> 
> would be the right thing to do (IOW, I would agree with you).
Yes, I agree.
> Can the current code distinguish the two cases?

Yes. Stephen's commit acc8559 already limits $TODO to the descendants of the merge-bases of HEAD and UPSTREAM, which captures the distinction here. When we rebase from X, the merge-base is X, so only that branch moves. When we rebase from the base of both branches, both branches should move.

I've added a test demonstrating this case to a revised version of the patch, below.

Greg
>From dcab1103e14394364c15136823269416f8c56e97 Mon Sep 17 00:00:00 2001
From: Greg Price <price@ksplice.com>
Date: Thu, 16 Jul 2009 12:48:40 -0400
Subject: [PATCH] Fix rebase -p --onto

In a rebase with --onto, the correct test for whether we can skip rewriting a commit is if it is already on top of $ONTO, not $UPSTREAM. Without --onto, this distinction does not exist and the behavior does not change.

In the situation
 X---o---o---o---M
  \             /
   x---x---x---x
 Y

if we try to move the branches merged at M from their base on X to be based on Y, so as to get

 X
 Y---o'--o'--o'--M'
  \             /
   x'--x'--x'--x'

then we fail. The command `git rebase -p --onto Y X M` moves only the first-parent chain, like so:

 X
  \
   x---x---x---x
                \
 Y---o'--o'--o'--M'

because it mistakenly drops the other branch(es) x---x---x---x from the TODO file. This tests and fixes this behavior.

Signed-off-by: Greg Price <price@ksplice.com>
---
 git-rebase--interactive.sh      |    2 +-
 t/t3414-rebase-preserve-onto.sh |   80 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 81 insertions(+), 1 deletions(-)
 create mode 100755 t/t3414-rebase-preserve-onto.sh
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index f96d887..23ded48 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -703,7 +703,7 @@ first and then run 'git rebase --continue' again."
 					preserve=t
 					for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-)
 					do
-						if test -f "$REWRITTEN"/$p -a \( $p != $UPSTREAM -o $sha1 = $first_after_upstream \)
+						if test -f "$REWRITTEN"/$p -a \( $p != $ONTO -o $sha1 = $first_after_upstream \)
 						then
 							preserve=f
 						fi
diff --git a/t/t3414-rebase-preserve-onto.sh b/t/t3414-rebase-preserve-onto.sh
new file mode 100755
index 0000000..80019ee
--- /dev/null
+++ b/t/t3414-rebase-preserve-onto.sh
@@ -0,0 +1,80 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Greg Price
+#
+
+test_description='git rebase -p should respect --onto
+
+In a rebase with --onto, we should rewrite all the commits that
+aren'"'"'t on top of $ONTO, even if they are on top of $UPSTREAM.
+'
+. ./test-lib.sh
+
+. ../lib-rebase.sh
+
+# Set up branches like this:
+# A1---B1---E1---F1---G1
+#  \    \             /
+#   \    \--C1---D1--/
+#    H1
+
+test_expect_success 'setup' '
+	test_commit A1 &&
+	test_commit B1 &&
+	test_commit C1 &&
+	test_commit D1 &&
+	git reset --hard B1 &&
+	test_commit E1 &&
+	test_commit F1 &&
+	test_merge G1 D1 &&
+	git reset --hard A1 &&
+	test_commit H1
+'
+
+# Now rebase merge G1 from both branches' base B1, both should move:
+# A1---B1---E1---F1---G1
+#  \    \             /
+#   \    \--C1---D1--/
+#    \
+#     H1---E2---F2---G2
+#      \             /
+#       \--C2---D2--/
+
+test_expect_success 'rebase from B1 onto H1' '
+	git checkout G1 &&
+	git rebase -p --onto H1 B1 &&
+	test "$(git rev-parse HEAD^1^1^1)" = "$(git rev-parse H1)" &&
+	test "$(git rev-parse HEAD^2^1^1)" = "$(git rev-parse H1)"
+'
+
+# On the other hand if rebase from E1 which is within one branch,
+# then the other branch stays:
+# A1---B1---E1---F1---G1
+#  \    \             /
+#   \    \--C1---D1--/
+#    \             \
+#     H1-----F3-----G3
+
+test_expect_success 'rebase from E1 onto H1' '
+	git checkout G1 &&
+	git rebase -p --onto H1 E1 &&
+	test "$(git rev-parse HEAD^1^1)" = "$(git rev-parse H1)" &&
+	test "$(git rev-parse HEAD^2)" = "$(git rev-parse D1)"
+'
+
+# And the same if we rebase from a commit in the second-parent branch.
+# A1---B1---E1---F1----G1
+#  \    \          \   /
+#   \    \--C1---D1-\-/
+#    \               \
+#     H1------D3------G4
+
+test_expect_success 'rebase from C1 onto H1' '
+	git checkout G1 &&
+	git rev-list --first-parent --pretty=oneline C1..G1 &&
+	git rebase -p --onto H1 C1 &&
+	test "$(git rev-parse HEAD^2^1)" = "$(git rev-parse H1)" &&
+	test "$(git rev-parse HEAD^1)" = "$(git rev-parse F1)"
+'
+
+test_done
-- 
1.6.3.1.499.ge7b8da
Previous: Greg Price
Message 9 of 9 in “Fix rebase -p --onto”
  1. Fix rebase -p --ontoGreg Price, Jul 16, 2009
  2. Johannes SixtJul 17, 2009
  3. Junio C HamanoJul 17, 2009
  4. Johannes SixtJul 17, 2009
  5. Johannes SixtJul 17, 2009
  6. Greg PriceJul 17, 2009
  7. Johannes SchindelinJul 17, 2009
  8. Greg PriceJul 17, 2009
  9. Greg PriceJul 17, 2009

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.