Skip to content

Commit c5c2374

Browse files
author
Junio C Hamano
committed
Merge branch 'ew/rebase'
* ew/rebase: rebase --merge: fix for rebasing more than 7 commits. rebase: error out for NO_PYTHON if they use recursive merge Add renaming-rebase test. rebase: Allow merge strategies to be used when rebasing
2 parents c3e1608 + 5887ac8 commit c5c2374

File tree

3 files changed

+320
-10
lines changed

3 files changed

+320
-10
lines changed

Documentation/git-rebase.txt

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ git-rebase - Rebase local commits to a new head
77

88
SYNOPSIS
99
--------
10-
'git-rebase' [--onto <newbase>] <upstream> [<branch>]
10+
'git-rebase' [--merge] [--onto <newbase>] <upstream> [<branch>]
1111

1212
'git-rebase' --continue | --skip | --abort
1313

@@ -106,6 +106,24 @@ OPTIONS
106106
--abort::
107107
Restore the original branch and abort the rebase operation.
108108

109+
--skip::
110+
Restart the rebasing process by skipping the current patch.
111+
This does not work with the --merge option.
112+
113+
--merge::
114+
Use merging strategies to rebase. When the recursive (default) merge
115+
strategy is used, this allows rebase to be aware of renames on the
116+
upstream side.
117+
118+
-s <strategy>, \--strategy=<strategy>::
119+
Use the given merge strategy; can be supplied more than
120+
once to specify them in the order they should be tried.
121+
If there is no `-s` option, a built-in list of strategies
122+
is used instead (`git-merge-recursive` when merging a single
123+
head, `git-merge-octopus` otherwise). This implies --merge.
124+
125+
include::merge-strategies.txt[]
126+
109127
NOTES
110128
-----
111129
When you rebase a branch, you are changing its history in a way that

git-rebase.sh

Lines changed: 189 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,93 @@ When you have resolved this problem run \"git rebase --continue\".
3434
If you would prefer to skip this patch, instead run \"git rebase --skip\".
3535
To restore the original branch and stop rebasing run \"git rebase --abort\".
3636
"
37+
38+
MRESOLVEMSG="
39+
When you have resolved this problem run \"git rebase --continue\".
40+
To restore the original branch and stop rebasing run \"git rebase --abort\".
41+
"
3742
unset newbase
43+
strategy=recursive
44+
do_merge=
45+
dotest=$GIT_DIR/.dotest-merge
46+
prec=4
47+
48+
continue_merge () {
49+
test -n "$prev_head" || die "prev_head must be defined"
50+
test -d "$dotest" || die "$dotest directory does not exist"
51+
52+
unmerged=$(git-ls-files -u)
53+
if test -n "$unmerged"
54+
then
55+
echo "You still have unmerged paths in your index"
56+
echo "did you forget update-index?"
57+
die "$MRESOLVEMSG"
58+
fi
59+
60+
if test -n "`git-diff-index HEAD`"
61+
then
62+
git-commit -C "`cat $dotest/current`"
63+
else
64+
echo "Previous merge succeeded automatically"
65+
fi
66+
67+
prev_head=`git-rev-parse HEAD^0`
68+
69+
# save the resulting commit so we can read-tree on it later
70+
echo "$prev_head" > "$dotest/cmt.$msgnum.result"
71+
echo "$prev_head" > "$dotest/prev_head"
72+
73+
# onto the next patch:
74+
msgnum=$(($msgnum + 1))
75+
echo "$msgnum" >"$dotest/msgnum"
76+
}
77+
78+
call_merge () {
79+
cmt="$(cat $dotest/cmt.$1)"
80+
echo "$cmt" > "$dotest/current"
81+
git-merge-$strategy "$cmt^" -- HEAD "$cmt"
82+
rv=$?
83+
case "$rv" in
84+
0)
85+
git-commit -C "$cmt" || die "commit failed: $MRESOLVEMSG"
86+
;;
87+
1)
88+
test -d "$GIT_DIR/rr-cache" && git-rerere
89+
die "$MRESOLVEMSG"
90+
;;
91+
2)
92+
echo "Strategy: $rv $strategy failed, try another" 1>&2
93+
die "$MRESOLVEMSG"
94+
;;
95+
*)
96+
die "Unknown exit code ($rv) from command:" \
97+
"git-merge-$strategy $cmt^ -- HEAD $cmt"
98+
;;
99+
esac
100+
}
101+
102+
finish_rb_merge () {
103+
set -e
104+
105+
msgnum=1
106+
echo "Finalizing rebased commits..."
107+
git-reset --hard "`cat $dotest/onto`"
108+
end="`cat $dotest/end`"
109+
while test "$msgnum" -le "$end"
110+
do
111+
git-read-tree `cat "$dotest/cmt.$msgnum.result"`
112+
git-checkout-index -q -f -u -a
113+
git-commit -C "`cat $dotest/cmt.$msgnum`"
114+
115+
printf "Committed %0${prec}d" $msgnum
116+
echo ' '`git-rev-list --pretty=oneline -1 HEAD | \
117+
sed 's/^[a-f0-9]\+ //'`
118+
msgnum=$(($msgnum + 1))
119+
done
120+
rm -r "$dotest"
121+
echo "All done."
122+
}
123+
38124
while case "$#" in 0) break ;; esac
39125
do
40126
case "$1" in
@@ -46,24 +132,67 @@ do
46132
exit 1
47133
;;
48134
esac
135+
if test -d "$dotest"
136+
then
137+
prev_head="`cat $dotest/prev_head`"
138+
end="`cat $dotest/end`"
139+
msgnum="`cat $dotest/msgnum`"
140+
onto="`cat $dotest/onto`"
141+
continue_merge
142+
while test "$msgnum" -le "$end"
143+
do
144+
call_merge "$msgnum"
145+
continue_merge
146+
done
147+
finish_rb_merge
148+
exit
149+
fi
49150
git am --resolved --3way --resolvemsg="$RESOLVEMSG"
50151
exit
51152
;;
52153
--skip)
154+
if test -d "$dotest"
155+
then
156+
die "--skip is not supported when using --merge"
157+
fi
53158
git am -3 --skip --resolvemsg="$RESOLVEMSG"
54159
exit
55160
;;
56161
--abort)
57-
[ -d .dotest ] || die "No rebase in progress?"
162+
if test -d "$dotest"
163+
then
164+
rm -r "$dotest"
165+
elif test -d .dotest
166+
then
167+
rm -r .dotest
168+
else
169+
die "No rebase in progress?"
170+
fi
58171
git reset --hard ORIG_HEAD
59-
rm -r .dotest
60172
exit
61173
;;
62174
--onto)
63175
test 2 -le "$#" || usage
64176
newbase="$2"
65177
shift
66178
;;
179+
-M|-m|--m|--me|--mer|--merg|--merge)
180+
do_merge=t
181+
;;
182+
-s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
183+
--strateg=*|--strategy=*|\
184+
-s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
185+
case "$#,$1" in
186+
*,*=*)
187+
strategy=`expr "$1" : '-[^=]*=\(.*\)'` ;;
188+
1,*)
189+
usage ;;
190+
*)
191+
strategy="$2"
192+
shift ;;
193+
esac
194+
do_merge=t
195+
;;
67196
-*)
68197
usage
69198
;;
@@ -75,16 +204,25 @@ do
75204
done
76205

77206
# Make sure we do not have .dotest
78-
if mkdir .dotest
207+
if test -z "$do_merge"
79208
then
80-
rmdir .dotest
81-
else
82-
echo >&2 '
209+
if mkdir .dotest
210+
then
211+
rmdir .dotest
212+
else
213+
echo >&2 '
83214
It seems that I cannot create a .dotest directory, and I wonder if you
84215
are in the middle of patch application or another rebase. If that is not
85216
the case, please rm -fr .dotest and run me again. I am stopping in case
86217
you still have something valuable there.'
87-
exit 1
218+
exit 1
219+
fi
220+
else
221+
if test -d "$dotest"
222+
then
223+
die "previous dotest directory $dotest still exists." \
224+
'try git-rebase < --continue | --abort >'
225+
fi
88226
fi
89227

90228
# The tree must be really really clean.
@@ -152,6 +290,48 @@ then
152290
exit 0
153291
fi
154292

155-
git-format-patch -k --stdout --full-index "$upstream"..ORIG_HEAD |
156-
git am --binary -3 -k --resolvemsg="$RESOLVEMSG"
293+
if test -z "$do_merge"
294+
then
295+
git-format-patch -k --stdout --full-index "$upstream"..ORIG_HEAD |
296+
git am --binary -3 -k --resolvemsg="$RESOLVEMSG"
297+
exit $?
298+
fi
299+
300+
if test "@@NO_PYTHON@@" && test "$strategy" = "recursive"
301+
then
302+
die 'The recursive merge strategy currently relies on Python,
303+
which this installation of git was not configured with. Please consider
304+
a different merge strategy (e.g. octopus, resolve, stupid, ours)
305+
or install Python and git with Python support.'
306+
307+
fi
308+
309+
# start doing a rebase with git-merge
310+
# this is rename-aware if the recursive (default) strategy is used
311+
312+
mkdir -p "$dotest"
313+
echo "$onto" > "$dotest/onto"
314+
prev_head=`git-rev-parse HEAD^0`
315+
echo "$prev_head" > "$dotest/prev_head"
316+
317+
msgnum=0
318+
for cmt in `git-rev-list --no-merges "$upstream"..ORIG_HEAD \
319+
| perl -e 'print reverse <>'`
320+
do
321+
msgnum=$(($msgnum + 1))
322+
echo "$cmt" > "$dotest/cmt.$msgnum"
323+
done
324+
325+
echo 1 >"$dotest/msgnum"
326+
echo $msgnum >"$dotest/end"
327+
328+
end=$msgnum
329+
msgnum=1
330+
331+
while test "$msgnum" -le "$end"
332+
do
333+
call_merge "$msgnum"
334+
continue_merge
335+
done
157336

337+
finish_rb_merge

t/t3402-rebase-merge.sh

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#!/bin/sh
2+
#
3+
# Copyright (c) 2006 Junio C Hamano
4+
#
5+
6+
test_description='git rebase --merge test'
7+
8+
. ./test-lib.sh
9+
10+
if test "$no_python"; then
11+
echo "Skipping: no python => no recursive merge"
12+
test_done
13+
exit 0
14+
fi
15+
16+
T="A quick brown fox
17+
jumps over the lazy dog."
18+
for i in 1 2 3 4 5 6 7 8 9 10
19+
do
20+
echo "$i $T"
21+
done >original
22+
23+
test_expect_success setup '
24+
git add original &&
25+
git commit -m"initial" &&
26+
git branch side &&
27+
echo "11 $T" >>original &&
28+
git commit -a -m"master updates a bit." &&
29+
30+
echo "12 $T" >>original &&
31+
git commit -a -m"master updates a bit more." &&
32+
33+
git checkout side &&
34+
(echo "0 $T" ; cat original) >renamed &&
35+
git add renamed &&
36+
git update-index --force-remove original &&
37+
git commit -a -m"side renames and edits." &&
38+
39+
tr "[a-z]" "[A-Z]" <original >newfile &&
40+
git add newfile &&
41+
git commit -a -m"side edits further." &&
42+
43+
tr "[a-m]" "[A-M]" <original >newfile &&
44+
rm -f original &&
45+
git commit -a -m"side edits once again." &&
46+
47+
git branch test-rebase side &&
48+
git branch test-rebase-pick side &&
49+
git branch test-reference-pick side &&
50+
git checkout -b test-merge side
51+
'
52+
53+
test_expect_success 'reference merge' '
54+
git merge -s recursive "reference merge" HEAD master
55+
'
56+
57+
test_expect_success rebase '
58+
git checkout test-rebase &&
59+
git rebase --merge master
60+
'
61+
62+
test_expect_success 'merge and rebase should match' '
63+
git diff-tree -r test-rebase test-merge >difference &&
64+
if test -s difference
65+
then
66+
cat difference
67+
(exit 1)
68+
else
69+
echo happy
70+
fi
71+
'
72+
73+
test_expect_success 'rebase the other way' '
74+
git reset --hard master &&
75+
git rebase --merge side
76+
'
77+
78+
test_expect_success 'merge and rebase should match' '
79+
git diff-tree -r test-rebase test-merge >difference &&
80+
if test -s difference
81+
then
82+
cat difference
83+
(exit 1)
84+
else
85+
echo happy
86+
fi
87+
'
88+
89+
test_expect_success 'picking rebase' '
90+
git reset --hard side &&
91+
git rebase --merge --onto master side^^ &&
92+
mb=$(git merge-base master HEAD) &&
93+
if test "$mb" = "$(git rev-parse master)"
94+
then
95+
echo happy
96+
else
97+
git show-branch
98+
(exit 1)
99+
fi &&
100+
f=$(git diff-tree --name-only HEAD^ HEAD) &&
101+
g=$(git diff-tree --name-only HEAD^^ HEAD^) &&
102+
case "$f,$g" in
103+
newfile,newfile)
104+
echo happy ;;
105+
*)
106+
echo "$f"
107+
echo "$g"
108+
(exit 1)
109+
esac
110+
'
111+
112+
test_done

0 commit comments

Comments
 (0)