Skip to content

Commit 58634db

Browse files
Eric WongJunio C Hamano
authored andcommitted
rebase: Allow merge strategies to be used when rebasing
This solves the problem of rebasing local commits against an upstream that has renamed files. Signed-off-by: Eric Wong <normalperson@yhbt.net> Signed-off-by: Junio C Hamano <junkio@cox.net>
1 parent 86f660b commit 58634db

File tree

2 files changed

+202
-10
lines changed

2 files changed

+202
-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: 183 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,96 @@ 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/`printf %0${prec}d $msgnum`.result"
71+
echo "$prev_head" > "$dotest/prev_head"
72+
73+
# onto the next patch:
74+
msgnum=$(($msgnum + 1))
75+
printf "%0${prec}d" "$msgnum" > "$dotest/msgnum"
76+
}
77+
78+
call_merge () {
79+
cmt="$(cat $dotest/`printf %0${prec}d $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+
msgnum=`printf "%0${prec}d" "$msgnum"`
112+
printf "%0${prec}d" "$msgnum" > "$dotest/msgnum"
113+
114+
git-read-tree `cat "$dotest/$msgnum.result"`
115+
git-checkout-index -q -f -u -a
116+
git-commit -C "`cat $dotest/$msgnum`"
117+
118+
echo "Committed $msgnum"
119+
echo ' '`git-rev-list --pretty=oneline -1 HEAD | \
120+
sed 's/^[a-f0-9]\+ //'`
121+
msgnum=$(($msgnum + 1))
122+
done
123+
rm -r "$dotest"
124+
echo "All done."
125+
}
126+
38127
while case "$#" in 0) break ;; esac
39128
do
40129
case "$1" in
@@ -46,24 +135,67 @@ do
46135
exit 1
47136
;;
48137
esac
138+
if test -d "$dotest"
139+
then
140+
prev_head="`cat $dotest/prev_head`"
141+
end="`cat $dotest/end`"
142+
msgnum="`cat $dotest/msgnum`"
143+
onto="`cat $dotest/onto`"
144+
continue_merge
145+
while test "$msgnum" -le "$end"
146+
do
147+
call_merge "$msgnum"
148+
continue_merge
149+
done
150+
finish_rb_merge
151+
exit
152+
fi
49153
git am --resolved --3way --resolvemsg="$RESOLVEMSG"
50154
exit
51155
;;
52156
--skip)
157+
if test -d "$dotest"
158+
then
159+
die "--skip is not supported when using --merge"
160+
fi
53161
git am -3 --skip --resolvemsg="$RESOLVEMSG"
54162
exit
55163
;;
56164
--abort)
57-
[ -d .dotest ] || die "No rebase in progress?"
165+
if test -d "$dotest"
166+
then
167+
rm -r "$dotest"
168+
elif test -d .dotest
169+
then
170+
rm -r .dotest
171+
else
172+
die "No rebase in progress?"
173+
fi
58174
git reset --hard ORIG_HEAD
59-
rm -r .dotest
60175
exit
61176
;;
62177
--onto)
63178
test 2 -le "$#" || usage
64179
newbase="$2"
65180
shift
66181
;;
182+
-M|-m|--m|--me|--mer|--merg|--merge)
183+
do_merge=t
184+
;;
185+
-s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
186+
--strateg=*|--strategy=*|\
187+
-s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
188+
case "$#,$1" in
189+
*,*=*)
190+
strategy=`expr "$1" : '-[^=]*=\(.*\)'` ;;
191+
1,*)
192+
usage ;;
193+
*)
194+
strategy="$2"
195+
shift ;;
196+
esac
197+
do_merge=t
198+
;;
67199
-*)
68200
usage
69201
;;
@@ -75,16 +207,25 @@ do
75207
done
76208

77209
# Make sure we do not have .dotest
78-
if mkdir .dotest
210+
if test -z "$do_merge"
79211
then
80-
rmdir .dotest
81-
else
82-
echo >&2 '
212+
if mkdir .dotest
213+
then
214+
rmdir .dotest
215+
else
216+
echo >&2 '
83217
It seems that I cannot create a .dotest directory, and I wonder if you
84218
are in the middle of patch application or another rebase. If that is not
85219
the case, please rm -fr .dotest and run me again. I am stopping in case
86220
you still have something valuable there.'
87-
exit 1
221+
exit 1
222+
fi
223+
else
224+
if test -d "$dotest"
225+
then
226+
die "previous dotest directory $dotest still exists." \
227+
'try git-rebase < --continue | --abort >'
228+
fi
88229
fi
89230

90231
# The tree must be really really clean.
@@ -152,6 +293,39 @@ then
152293
exit 0
153294
fi
154295

155-
git-format-patch -k --stdout --full-index "$upstream"..ORIG_HEAD |
156-
git am --binary -3 -k --resolvemsg="$RESOLVEMSG"
296+
if test -z "$do_merge"
297+
then
298+
git-format-patch -k --stdout --full-index "$upstream"..ORIG_HEAD |
299+
git am --binary -3 -k --resolvemsg="$RESOLVEMSG"
300+
exit $?
301+
fi
302+
303+
# start doing a rebase with git-merge
304+
# this is rename-aware if the recursive (default) strategy is used
305+
306+
mkdir -p "$dotest"
307+
echo "$onto" > "$dotest/onto"
308+
prev_head=`git-rev-parse HEAD^0`
309+
echo "$prev_head" > "$dotest/prev_head"
310+
311+
msgnum=0
312+
for cmt in `git-rev-list --no-merges "$upstream"..ORIG_HEAD \
313+
| perl -e 'print reverse <>'`
314+
do
315+
msgnum=$(($msgnum + 1))
316+
echo "$cmt" > "$dotest/`printf "%0${prec}d" $msgnum`"
317+
done
318+
319+
printf "%0${prec}d" 1 > "$dotest/msgnum"
320+
printf "%0${prec}d" "$msgnum" > "$dotest/end"
321+
322+
end=$msgnum
323+
msgnum=1
324+
325+
while test "$msgnum" -le "$end"
326+
do
327+
call_merge "$msgnum"
328+
continue_merge
329+
done
157330

331+
finish_rb_merge

0 commit comments

Comments
 (0)