[PATCH 5/5] rebase [-m]: calculate patches in upstream correctly

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Plain 'git rebase' (without -m/-i/-p) applies the patches from

  git format-patch --ignore-if-in-upstream $upstream..$orig_head

, while 'git rebase -m' finds the commits using

  git rev-list $upstream..$orig_head

As Knut Franke reported in [1], the fact that there is no
--ignore-if-in-upstream or equivalent when using merge-based rebase
means that unnecessary conflicts can arise due to commits
cherry-picked between $orig_head and $upstream.

There is a second problem with the above method of calculating the
upstream commits. Copying the example history from [1]:

      .-c
     /
a---b---d---e---f
         \
          .-g---E

Commit E is here a cherry-pick of e. If we now run 'git rebase --onto
c f E', the revisions that will be applied onto 'c' are given by 'git
format-patch --ignore-if-in-upstream f..E'. In this case that would be
only 'g' and NOT 'E'.

To solve both of the above problems, we want to find the commits in
$upstream..$orig_head that are not cherry-picked in
$upstream..$onto. There is unfortunately no direct way of finding
these commits using 'git rev-list', so we will have to resort to using
'git cherry' and filter for lines starting with '+'.

To reduce the risk of 'git rebase' and 'git rebase -m' behaving
differently (with respect to the commits chosen) in the future,
perform the calculation already in git-rebase.sh.

As a side-effect, we also avoid the cost of formatting patches.

Test case updates for 'rebase -m' by Knut, the rest by Martin.

 [1] http://thread.gmane.org/gmane.comp.version-control.git/161917

Helped-by: Knut Franke <Knut.Franke@xxxxxx>
Signed-off-by: Martin von Zweigbergk <martin.von.zweigbergk@xxxxxxxxx>
---
 git-rebase--am.sh         |    6 ++----
 git-rebase--merge.sh      |    2 +-
 git-rebase.sh             |    7 +------
 t/t3401-rebase-partial.sh |   17 +++++++++++++++++
 t/t3406-rebase-message.sh |   14 +++++++-------
 5 files changed, 28 insertions(+), 18 deletions(-)

diff --git a/git-rebase--am.sh b/git-rebase--am.sh
index 392ebc9..89e0ab4 100644
--- a/git-rebase--am.sh
+++ b/git-rebase--am.sh
@@ -16,7 +16,6 @@ skip)
 	;;
 esac
 
-test -n "$rebase_root" && root_flag=--root
 
 if test -n "$keep_empty"
 then
@@ -26,9 +25,8 @@ then
 	# makes this easy
 	git cherry-pick --allow-empty "$revisions"
 else
-	git format-patch -k --stdout --full-index --ignore-if-in-upstream \
-		--src-prefix=a/ --dst-prefix=b/ \
-		--no-renames $root_flag "$revisions" |
+	echo "$revisions" |
+	sed -e 's/\([0-9a-f]\{40\}\)/From \1 Mon Sep 17 00:00:00 2001/' |
 	git am $git_am_opt --rebasing --resolvemsg="$resolvemsg"
 fi && move_to_original_branch
 
diff --git a/git-rebase--merge.sh b/git-rebase--merge.sh
index b10f2cf..7ea33e3 100644
--- a/git-rebase--merge.sh
+++ b/git-rebase--merge.sh
@@ -131,7 +131,7 @@ echo "$onto_name" > "$state_dir/onto_name"
 write_basic_state
 
 msgnum=0
-for cmt in `git rev-list --reverse --no-merges "$revisions"`
+for cmt in $revisions
 do
 	msgnum=$(($msgnum + 1))
 	echo "$cmt" > "$state_dir/cmt.$msgnum"
diff --git a/git-rebase.sh b/git-rebase.sh
index 6df06c4..47e75cb 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -522,11 +522,6 @@ then
 	exit 0
 fi
 
-if test -n "$rebase_root"
-then
-	revisions="$onto..$orig_head"
-else
-	revisions="$upstream..$orig_head"
-fi
+revisions="$(git cherry $onto $orig_head $upstream | sed -ne 's/^+ //p')"
 
 run_specific_rebase
diff --git a/t/t3401-rebase-partial.sh b/t/t3401-rebase-partial.sh
index 7ba1797..ce555fa 100755
--- a/t/t3401-rebase-partial.sh
+++ b/t/t3401-rebase-partial.sh
@@ -42,4 +42,21 @@ test_expect_success 'rebase --merge topic branch that was partially merged upstr
 	test_path_is_missing .git/rebase-merge
 '
 
+test_expect_success 'rebase --onto does not re-apply patches in $onto' '
+	git checkout C &&
+	test_commit C2 C.t &&
+	git checkout -B my-topic-branch master &&
+	test_commit D &&
+	git rebase --onto C2 A2 &&
+	test "$(git log --format=%s C2..)" = D
+'
+
+test_expect_success 'rebase --onto does not lose patches in $upstream' '
+	git rebase --onto A2 D &&
+	test "$(git log --format=%s A2..)" = "D
+C2
+C
+B"
+'
+
 test_done
diff --git a/t/t3406-rebase-message.sh b/t/t3406-rebase-message.sh
index 6898377..3eecc66 100755
--- a/t/t3406-rebase-message.sh
+++ b/t/t3406-rebase-message.sh
@@ -5,8 +5,10 @@ test_description='messages from rebase operation'
 . ./test-lib.sh
 
 quick_one () {
-	echo "$1" >"file$1" &&
-	git add "file$1" &&
+	fileno=$2
+	test -z "$fileno" && fileno=$1
+	echo "$1" >"file$fileno" &&
+	git add "file$fileno" &&
 	test_tick &&
 	git commit -m "$1"
 }
@@ -16,21 +18,19 @@ test_expect_success setup '
 	git branch topic &&
 	quick_one X &&
 	quick_one A &&
-	quick_one B &&
+	quick_one B A &&
 	quick_one Y &&
 
 	git checkout topic &&
 	quick_one A &&
-	quick_one B &&
+	quick_one B A &&
 	quick_one Z &&
 	git tag start
 
 '
 
 cat >expect <<\EOF
-Already applied: 0001 A
-Already applied: 0002 B
-Committed: 0003 Z
+Committed: 0001 Z
 EOF
 
 test_expect_success 'rebase -m' '
-- 
1.7.9.3.327.g2980b

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]