* [PATCH] rebase: Allow merge strategies to be used when rebasing
@ 2006-06-18 3:02 Eric Wong
2006-06-18 9:08 ` Junio C Hamano
2006-06-18 11:48 ` [PATCH] Add renaming-rebase test Junio C Hamano
0 siblings, 2 replies; 11+ messages in thread
From: Eric Wong @ 2006-06-18 3:02 UTC (permalink / raw)
To: git, Junio C Hamano; +Cc: Eric Wong
This solves the problem of rebasing local commits against an
upstream that has renamed files.
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
Documentation/git-rebase.txt | 20 +++++
git-rebase.sh | 176 ++++++++++++++++++++++++++++++++++++++++--
2 files changed, 186 insertions(+), 10 deletions(-)
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 08ee4aa..c339c45 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -7,7 +7,7 @@ git-rebase - Rebase local commits to a n
SYNOPSIS
--------
-'git-rebase' [--onto <newbase>] <upstream> [<branch>]
+'git-rebase' [--merge] [--onto <newbase>] <upstream> [<branch>]
'git-rebase' --continue | --skip | --abort
@@ -106,6 +106,24 @@ OPTIONS
--abort::
Restore the original branch and abort the rebase operation.
+--skip::
+ Restart the rebasing process by skipping the current patch.
+ This does not work with the --merge option.
+
+--merge::
+ Use merging strategies to rebase. When the recursive (default) merge
+ strategy is used, this allows rebase to be aware of renames on the
+ upstream side.
+
+-s <strategy>, \--strategy=<strategy>::
+ Use the given merge strategy; can be supplied more than
+ once to specify them in the order they should be tried.
+ If there is no `-s` option, a built-in list of strategies
+ is used instead (`git-merge-recursive` when merging a single
+ head, `git-merge-octopus` otherwise). This implies --merge.
+
+include::merge-strategies.txt[]
+
NOTES
-----
When you rebase a branch, you are changing its history in a way that
diff --git a/git-rebase.sh b/git-rebase.sh
index e6b57b8..68bddfb 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -34,7 +34,81 @@ When you have resolved this problem run
If you would prefer to skip this patch, instead run \"git rebase --skip\".
To restore the original branch and stop rebasing run \"git rebase --abort\".
"
+
+MRESOLVEMSG="
+When you have resolved this problem run \"git rebase --continue\".
+To restore the original branch and stop rebasing run \"git rebase --abort\".
+"
unset newbase
+strategy_args=
+do_merge=
+dotest=$GIT_DIR/.dotest-merge
+prec=4
+
+continue_merge () {
+ test -n "$prev_head" || die "prev_head must be defined"
+ test -d "$dotest" || die "$dotest directory does not exist"
+
+ unmerged=$(git-ls-files -u)
+ if test -n "$unmerged"
+ then
+ echo "You still have unmerged paths in your index"
+ echo "did you forget update-index?"
+ die "$MRESOLVEMSG"
+ fi
+
+ mh="$GIT_DIR/MERGE_HEAD"
+ mm="$GIT_DIR/MERGE_MSG"
+ if test -f "$mh" && test -f "$mm"
+ then
+ git-commit -F "$mm" || die "commit failed: $MRESOLVEMSG"
+ else
+ echo "Previous merge succeeded automatically"
+ fi
+
+ prev_head=`git-rev-parse HEAD^0`
+
+ # save the resulting commit so we can read-tree on it later
+ echo "$prev_head" > "$dotest/`printf %0${prec}d $msgnum`.result"
+ echo "$prev_head" > "$dotest/prev_head"
+
+ # onto the next patch:
+ msgnum=$(($msgnum + 1))
+ printf "%0${prec}d" "$msgnum" > "$dotest/msgnum"
+}
+
+call_merge () {
+ cmt="$(cat $dotest/`printf %0${prec}d $1`)"
+ echo "$cmt" > "$dotest/current"
+ git-merge $strategy_args "rebase-merge: $cmt" HEAD "$cmt" \
+ || die "$MRESOLVEMSG"
+}
+
+finish_rb_merge () {
+ set -e
+
+ msgnum=1
+ echo "Finalizing rebased commits..."
+ git-reset --hard "`cat $dotest/upstream`"
+ end="`cat $dotest/end`"
+ while test "$msgnum" -le "$end"
+ do
+ msgnum=`printf "%0${prec}d" "$msgnum"`
+ printf "%0${prec}d" "$msgnum" > "$dotest/msgnum"
+
+ git-read-tree `cat "$dotest/$msgnum.result"`
+ git-checkout-index -q -f -u -a
+ git-commit -C "`cat $dotest/$msgnum`"
+
+ echo "Committed $msgnum"
+ echo ' '`git-rev-list --pretty=oneline -1 HEAD | \
+ sed 's/^[a-f0-9]\+ //'`
+ msgnum=$(($msgnum + 1))
+ done
+ rm -r "$dotest"
+ echo "All done."
+}
+
while case "$#" in 0) break ;; esac
do
case "$1" in
@@ -46,17 +120,42 @@ do
exit 1
;;
esac
+ if test -d "$dotest"
+ then
+ prev_head="`cat $dotest/prev_head`"
+ end="`cat $dotest/end`"
+ msgnum="`cat $dotest/msgnum`"
+ continue_merge
+ while test "$msgnum" -le "$end"
+ do
+ call_merge "$msgnum"
+ continue_merge
+ done
+ finish_rb_merge
+ exit
+ fi
git am --resolved --3way --resolvemsg="$RESOLVEMSG"
exit
;;
--skip)
+ if test -d "$dotest"
+ then
+ die "--skip is not supported when using --merge"
+ fi
git am -3 --skip --resolvemsg="$RESOLVEMSG"
exit
;;
--abort)
- [ -d .dotest ] || die "No rebase in progress?"
+ if test -d "$dotest"
+ then
+ rm -r "$dotest"
+ elif test -d .dotest
+ then
+ rm -r .dotest
+ else
+ die "No rebase in progress?"
+ fi
git reset --hard ORIG_HEAD
- rm -r .dotest
exit
;;
--onto)
@@ -64,6 +163,24 @@ do
newbase="$2"
shift
;;
+ -M|-m|--m|--me|--mer|--merg|--merge)
+ do_merge=t
+ ;;
+ -s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
+ --strateg=*|--strategy=*|\
+ -s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
+ case "$#,$1" in
+ *,*=*)
+ strategy=`expr "$1" : '-[^=]*=\(.*\)'` ;;
+ 1,*)
+ usage ;;
+ *)
+ strategy="$2"
+ shift ;;
+ esac
+ do_merge=t
+ strategy_args="${strategy_args}-s $strategy "
+ ;;
-*)
usage
;;
@@ -75,16 +192,25 @@ do
done
# Make sure we do not have .dotest
-if mkdir .dotest
+if test -z "$do_merge"
then
- rmdir .dotest
-else
- echo >&2 '
+ if mkdir .dotest
+ then
+ rmdir .dotest
+ else
+ echo >&2 '
It seems that I cannot create a .dotest directory, and I wonder if you
are in the middle of patch application or another rebase. If that is not
the case, please rm -fr .dotest and run me again. I am stopping in case
you still have something valuable there.'
- exit 1
+ exit 1
+ fi
+else
+ if test -d "$dotest"
+ then
+ die "previous dotest directory $dotest still exists." \
+ 'try git-rebase < --continue | --abort >'
+ fi
fi
# The tree must be really really clean.
@@ -152,6 +278,38 @@ then
exit 0
fi
-git-format-patch -k --stdout --full-index "$upstream"..ORIG_HEAD |
-git am --binary -3 -k --resolvemsg="$RESOLVEMSG"
+if test -z "$do_merge"
+then
+ git-format-patch -k --stdout --full-index "$upstream"..ORIG_HEAD |
+ git am --binary -3 -k --resolvemsg="$RESOLVEMSG"
+ exit 0
+fi
+
+# start doing a rebase with git-merge
+# this is rename-aware if the recursive (default) strategy is used
+
+mkdir -p "$dotest"
+echo "$upstream" > "$dotest/upstream"
+prev_head=`git-rev-parse HEAD^0`
+echo "$prev_head" > "$dotest/prev_head"
+
+msgnum=0
+for cmt in `git-rev-list "$upstream"..ORIG_HEAD | tac`
+do
+ msgnum=$(($msgnum + 1))
+ echo "$cmt" > "$dotest/`printf "%0${prec}d" $msgnum`"
+done
+
+printf "%0${prec}d" 1 > "$dotest/msgnum"
+printf "%0${prec}d" "$msgnum" > "$dotest/end"
+
+end=$msgnum
+msgnum=1
+
+while test "$msgnum" -le "$end"
+do
+ call_merge "$msgnum"
+ continue_merge
+done
+finish_rb_merge
--
1.4.0.ge24c
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH] rebase: Allow merge strategies to be used when rebasing
2006-06-18 3:02 [PATCH] rebase: Allow merge strategies to be used when rebasing Eric Wong
@ 2006-06-18 9:08 ` Junio C Hamano
2006-06-19 21:39 ` Eric Wong
2006-06-18 11:48 ` [PATCH] Add renaming-rebase test Junio C Hamano
1 sibling, 1 reply; 11+ messages in thread
From: Junio C Hamano @ 2006-06-18 9:08 UTC (permalink / raw)
To: Eric Wong; +Cc: git
Eric Wong <normalperson@yhbt.net> writes:
> This solves the problem of rebasing local commits against an
> upstream that has renamed files.
I think leveraging the merge strategy to perform rebase is
sound, but the selection of merge base for this purpose is quite
different from the regular merge, and I think unfortunately this
patch is probably wrong in letting git-merge choose the merge
base.
But let's mention other things as well.
- You kept the original "format-patch piped to am" workflow
optionally working.
- You check if merge or patch was used for failed rebase and
follow the appropriate codepath while resuming, which is
good.
- The list of commits you generate with tac seem to include
merge commit -- you may want to give --no-merges to
rev-list.
- I do not think we use "tac" elsewhere -- is it portable
enough?
- Exiting with success unconditionally after "git am" feels
wrong. I would do "exit $?" instead of "exit 0" there.
Suppose you have this commit ancestry graph:
----------------------------------------------------------------
Example: git-rebase --onto master A topic
A---B---C topic B'--C' topic
/ --> /
D---E---F---G master D---E---F---G master
----------------------------------------------------------------
This is slightly different from the one at the beginning of the
script. The idea is A turned out to be not so cool, and we
would want to drop it.
> +call_merge () {
> + cmt="$(cat $dotest/`printf %0${prec}d $1`)"
> + echo "$cmt" > "$dotest/current"
> + git-merge $strategy_args "rebase-merge: $cmt" HEAD "$cmt" \
> + || die "$MRESOLVEMSG"
> +}
call_merge is first called with B in cmt, and HEAD is pointing
at G. But the merge in this function makes a merge between B
and G, taking the effect of E->A.
I think the three-way merge you would want here is not between B
and G using E as the pivot, but between B and G using A as the
pivot. That's how cherry-pick and revert works. I would
leverage the interface that is one level lower for this -- the
strategy modules themselves.
git-merge-$strategy $cmt^ -- HEAD $cmt
The strategy modules take merge base(s), double-dash as the
separator, our head and the other head. They do not make commit
themselves (instead they leave working tree and index in
committable state) and signal the results with their exit
status:
0 -- success
1 -- conflicts
2 -- did not handle the merge at all
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH] Add renaming-rebase test.
2006-06-18 3:02 [PATCH] rebase: Allow merge strategies to be used when rebasing Eric Wong
2006-06-18 9:08 ` Junio C Hamano
@ 2006-06-18 11:48 ` Junio C Hamano
1 sibling, 0 replies; 11+ messages in thread
From: Junio C Hamano @ 2006-06-18 11:48 UTC (permalink / raw)
To: Eric Wong; +Cc: git
This tests Eric's "renaming rebase" patch. It tests only very
basic cases and most of the tests except the last one passes.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
t/t3402-rebase-merge.sh | 96 +++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 96 insertions(+), 0 deletions(-)
diff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh
new file mode 100755
index 0000000..8c7a519
--- /dev/null
+++ b/t/t3402-rebase-merge.sh
@@ -0,0 +1,96 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Junio C Hamano
+#
+
+test_description='git rebase --merge test'
+
+. ./test-lib.sh
+
+T="A quick brown fox
+jumps over the lazy dog."
+for i in 1 2 3 4 5 6 7 8 9 10
+do
+ echo "$i $T"
+done >original
+
+test_expect_success setup '
+ git add original &&
+ git commit -m"initial" &&
+ git branch side &&
+ echo "11 $T" >>original &&
+ git commit -a -m"master updates a bit." &&
+
+ echo "12 $T" >>original &&
+ git commit -a -m"master updates a bit more." &&
+
+ git checkout side &&
+ (echo "0 $T" ; cat original) >renamed &&
+ git add renamed &&
+ git update-index --force-remove original &&
+ git commit -a -m"side renames and edits." &&
+
+ tr "[a-z]" "[A-Z]" <original >newfile &&
+ git add newfile &&
+ git commit -a -m"side edits further." &&
+
+ tr "[a-m]" "[A-M]" <original >newfile &&
+ rm -f original &&
+ git commit -a -m"side edits once again." &&
+
+ git branch test-rebase side &&
+ git branch test-rebase-pick side &&
+ git branch test-reference-pick side &&
+ git checkout -b test-merge side
+'
+
+test_expect_success 'reference merge' '
+ git merge -s recursive "reference merge" HEAD master
+'
+
+test_expect_success rebase '
+ git checkout test-rebase &&
+ git rebase --merge master
+'
+
+test_expect_success 'merge and rebase should match' '
+ git diff-tree -r test-rebase test-merge >difference &&
+ if test -s difference
+ then
+ cat difference
+ (exit 1)
+ else
+ echo happy
+ fi
+'
+
+test_expect_success 'rebase the other way' '
+ git reset --hard master &&
+ git rebase --merge side
+'
+
+test_expect_success 'merge and rebase should match' '
+ git diff-tree -r test-rebase test-merge >difference &&
+ if test -s difference
+ then
+ cat difference
+ (exit 1)
+ else
+ echo happy
+ fi
+'
+
+test_expect_success 'picking rebase' '
+ git reset --hard side &&
+ git rebase --merge --onto master side^^ &&
+ mb=$(git merge-base master HEAD) &&
+ if test "$mb" = "$(git rev-parse master)"
+ then
+ echo happy
+ else
+ git show-branch
+ (exit 1)
+ fi
+'
+
+test_done
--
1.4.0.g1910f
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH] rebase: Allow merge strategies to be used when rebasing
2006-06-18 9:08 ` Junio C Hamano
@ 2006-06-19 21:39 ` Eric Wong
2006-06-19 21:55 ` Junio C Hamano
0 siblings, 1 reply; 11+ messages in thread
From: Eric Wong @ 2006-06-19 21:39 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
Junio C Hamano <junkio@cox.net> wrote:
> Eric Wong <normalperson@yhbt.net> writes:
>
> > This solves the problem of rebasing local commits against an
> > upstream that has renamed files.
>
> I think leveraging the merge strategy to perform rebase is
> sound, but the selection of merge base for this purpose is quite
> different from the regular merge, and I think unfortunately this
> patch is probably wrong in letting git-merge choose the merge
> base.
>
> But let's mention other things as well.
>
> - You kept the original "format-patch piped to am" workflow
> optionally working.
I left it as the default, too. I figured that it's best not
to change the default (and most likely faster) behavior of
something people rely on.
> - You check if merge or patch was used for failed rebase and
> follow the appropriate codepath while resuming, which is
> good.
>
> - The list of commits you generate with tac seem to include
> merge commit -- you may want to give --no-merges to
> rev-list.
Good point, I'll change it.
> - I do not think we use "tac" elsewhere -- is it portable
> enough?
Nope. perl -e 'print reverse <>' is equivalent and we already use
plenty of perl.
> - Exiting with success unconditionally after "git am" feels
> wrong. I would do "exit $?" instead of "exit 0" there.
Oops, I'll change that, too.
> Suppose you have this commit ancestry graph:
>
> ----------------------------------------------------------------
> Example: git-rebase --onto master A topic
>
> A---B---C topic B'--C' topic
> / --> /
> D---E---F---G master D---E---F---G master
> ----------------------------------------------------------------
>
> This is slightly different from the one at the beginning of the
> script. The idea is A turned out to be not so cool, and we
> would want to drop it.
>
> > +call_merge () {
> > + cmt="$(cat $dotest/`printf %0${prec}d $1`)"
> > + echo "$cmt" > "$dotest/current"
> > + git-merge $strategy_args "rebase-merge: $cmt" HEAD "$cmt" \
> > + || die "$MRESOLVEMSG"
> > +}
>
> call_merge is first called with B in cmt, and HEAD is pointing
> at G. But the merge in this function makes a merge between B
> and G, taking the effect of E->A.
>
> I think the three-way merge you would want here is not between B
> and G using E as the pivot, but between B and G using A as the
> pivot. That's how cherry-pick and revert works. I would
> leverage the interface that is one level lower for this -- the
> strategy modules themselves.
>
> git-merge-$strategy $cmt^ -- HEAD $cmt
Changing the 'git-merge $strategy_args "rebase-merge: $cmt" HEAD "$cmt"'
line in call_merge() to this seems to have broken more tests.
I'm not an expert at merging strategies by any measure, I've just
trusted merge-recursive to Do The Right Thing(TM) more often than not,
and use rerere to avoid repeating work.
I'm not entirely sure I want (or fully understand how) to support
cherry-picking/revert with this, as I've already dropped --skip when
--merge was in use.
I'll think about this some more when I'm less distracted.
> The strategy modules take merge base(s), double-dash as the
> separator, our head and the other head. They do not make commit
> themselves (instead they leave working tree and index in
> committable state) and signal the results with their exit
> status:
>
> 0 -- success
> 1 -- conflicts
> 2 -- did not handle the merge at all
Cool, that's good.
--
Eric Wong
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] rebase: Allow merge strategies to be used when rebasing
2006-06-19 21:39 ` Eric Wong
@ 2006-06-19 21:55 ` Junio C Hamano
2006-06-21 10:01 ` Eric Wong
0 siblings, 1 reply; 11+ messages in thread
From: Junio C Hamano @ 2006-06-19 21:55 UTC (permalink / raw)
To: Eric Wong; +Cc: git
Eric Wong <normalperson@yhbt.net> writes:
> Junio C Hamano <junkio@cox.net> wrote:
>
>> - You kept the original "format-patch piped to am" workflow
>> optionally working.
>
> I left it as the default, too. I figured that it's best not
> to change the default (and most likely faster) behavior of
> something people rely on.
I should have said: "You kept ... working, which is good".
>> I think the three-way merge you would want here is not between B
>> and G using E as the pivot, but between B and G using A as the
>> pivot. That's how cherry-pick and revert works. I would
>> leverage the interface that is one level lower for this -- the
>> strategy modules themselves.
>>
>> git-merge-$strategy $cmt^ -- HEAD $cmt
>
> Changing the 'git-merge $strategy_args "rebase-merge: $cmt" HEAD "$cmt"'
> line in call_merge() to this seems to have broken more tests.
Oh, that is to be expected if you changed git-merge -s recursive
with git-merge-recursive without other changes. The former
makes a commit (which your original patch later used to create a
separate commit chain and discarded); the latter does not make a
commit but expects the caller to create a commit out of the
resulting index file.
> I'm not an expert at merging strategies by any measure, I've just
> trusted merge-recursive to Do The Right Thing(TM) more often than not,
> and use rerere to avoid repeating work.
I was originally hoping that rebasing would just be a matter of
listing sequence of commits to be ported onto a new base and
running "git-cherry-pick" on each of them in sequence. Now
cherry-pick does not use merge machinery (hence does not use
git-merge-recursive), but if we change that then updating rebase
would be pretty much straightforward. It just needs a UI layer
to guide the user through recovery process when the merge does
not resolve cleanly in the middle, no?
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] rebase: Allow merge strategies to be used when rebasing
2006-06-19 21:55 ` Junio C Hamano
@ 2006-06-21 10:01 ` Eric Wong
2006-06-21 10:04 ` [PATCH (fixed)] " Eric Wong
2006-06-21 11:01 ` [PATCH] " Junio C Hamano
0 siblings, 2 replies; 11+ messages in thread
From: Eric Wong @ 2006-06-21 10:01 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
Junio C Hamano <junkio@cox.net> wrote:
> Eric Wong <normalperson@yhbt.net> writes:
>
> > Junio C Hamano <junkio@cox.net> wrote:
> >
> >> - You kept the original "format-patch piped to am" workflow
> >> optionally working.
> >
> > I left it as the default, too. I figured that it's best not
> > to change the default (and most likely faster) behavior of
> > something people rely on.
>
> I should have said: "You kept ... working, which is good".
>
> >> I think the three-way merge you would want here is not between B
> >> and G using E as the pivot, but between B and G using A as the
> >> pivot. That's how cherry-pick and revert works. I would
> >> leverage the interface that is one level lower for this -- the
> >> strategy modules themselves.
> >>
> >> git-merge-$strategy $cmt^ -- HEAD $cmt
> >
> > Changing the 'git-merge $strategy_args "rebase-merge: $cmt" HEAD "$cmt"'
> > line in call_merge() to this seems to have broken more tests.
>
> Oh, that is to be expected if you changed git-merge -s recursive
> with git-merge-recursive without other changes. The former
> makes a commit (which your original patch later used to create a
> separate commit chain and discarded); the latter does not make a
> commit but expects the caller to create a commit out of the
> resulting index file.
Oops, *smacks head*
> > I'm not an expert at merging strategies by any measure, I've just
> > trusted merge-recursive to Do The Right Thing(TM) more often than not,
> > and use rerere to avoid repeating work.
>
> I was originally hoping that rebasing would just be a matter of
> listing sequence of commits to be ported onto a new base and
> running "git-cherry-pick" on each of them in sequence. Now
> cherry-pick does not use merge machinery (hence does not use
> git-merge-recursive), but if we change that then updating rebase
> would be pretty much straightforward. It just needs a UI layer
> to guide the user through recovery process when the merge does
> not resolve cleanly in the middle, no?
Sounds workable right to me. But then again, a cherry-pick is also a
case of rebase on a single commit, so we could be using rebase (and its
recovery code) in cherry-pick, too, right?
--
Eric Wong
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH (fixed)] rebase: Allow merge strategies to be used when rebasing
2006-06-21 10:01 ` Eric Wong
@ 2006-06-21 10:04 ` Eric Wong
2006-06-21 10:04 ` [PATCH 1/3] " Eric Wong
2006-06-21 11:01 ` [PATCH (fixed)] rebase: Allow merge strategies to be used when rebasing Junio C Hamano
2006-06-21 11:01 ` [PATCH] " Junio C Hamano
1 sibling, 2 replies; 11+ messages in thread
From: Eric Wong @ 2006-06-21 10:04 UTC (permalink / raw)
To: Junio C Hamano, git; +Cc: Eric Wong
1 - is fixed for --onto usage.
2 - in the series is the test from Junio, so I'm not resending it:
Subject: [PATCH] Add renaming-rebase test.
3 - keeps the NO_PYTHON people happy
--
Eric Wong
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 1/3] rebase: Allow merge strategies to be used when rebasing
2006-06-21 10:04 ` [PATCH (fixed)] " Eric Wong
@ 2006-06-21 10:04 ` Eric Wong
2006-06-21 10:04 ` [PATCH 3/3] rebase: error out for NO_PYTHON if they use recursive merge Eric Wong
2006-06-21 11:01 ` [PATCH (fixed)] rebase: Allow merge strategies to be used when rebasing Junio C Hamano
1 sibling, 1 reply; 11+ messages in thread
From: Eric Wong @ 2006-06-21 10:04 UTC (permalink / raw)
To: Junio C Hamano, git; +Cc: Eric Wong
This solves the problem of rebasing local commits against an
upstream that has renamed files.
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
Documentation/git-rebase.txt | 20 ++++
git-rebase.sh | 192 ++++++++++++++++++++++++++++++++++++++++--
2 files changed, 202 insertions(+), 10 deletions(-)
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 08ee4aa..c339c45 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -7,7 +7,7 @@ git-rebase - Rebase local commits to a n
SYNOPSIS
--------
-'git-rebase' [--onto <newbase>] <upstream> [<branch>]
+'git-rebase' [--merge] [--onto <newbase>] <upstream> [<branch>]
'git-rebase' --continue | --skip | --abort
@@ -106,6 +106,24 @@ OPTIONS
--abort::
Restore the original branch and abort the rebase operation.
+--skip::
+ Restart the rebasing process by skipping the current patch.
+ This does not work with the --merge option.
+
+--merge::
+ Use merging strategies to rebase. When the recursive (default) merge
+ strategy is used, this allows rebase to be aware of renames on the
+ upstream side.
+
+-s <strategy>, \--strategy=<strategy>::
+ Use the given merge strategy; can be supplied more than
+ once to specify them in the order they should be tried.
+ If there is no `-s` option, a built-in list of strategies
+ is used instead (`git-merge-recursive` when merging a single
+ head, `git-merge-octopus` otherwise). This implies --merge.
+
+include::merge-strategies.txt[]
+
NOTES
-----
When you rebase a branch, you are changing its history in a way that
diff --git a/git-rebase.sh b/git-rebase.sh
index e6b57b8..bce7bf8 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -34,7 +34,96 @@ When you have resolved this problem run
If you would prefer to skip this patch, instead run \"git rebase --skip\".
To restore the original branch and stop rebasing run \"git rebase --abort\".
"
+
+MRESOLVEMSG="
+When you have resolved this problem run \"git rebase --continue\".
+To restore the original branch and stop rebasing run \"git rebase --abort\".
+"
unset newbase
+strategy=recursive
+do_merge=
+dotest=$GIT_DIR/.dotest-merge
+prec=4
+
+continue_merge () {
+ test -n "$prev_head" || die "prev_head must be defined"
+ test -d "$dotest" || die "$dotest directory does not exist"
+
+ unmerged=$(git-ls-files -u)
+ if test -n "$unmerged"
+ then
+ echo "You still have unmerged paths in your index"
+ echo "did you forget update-index?"
+ die "$MRESOLVEMSG"
+ fi
+
+ if test -n "`git-diff-index HEAD`"
+ then
+ git-commit -C "`cat $dotest/current`"
+ else
+ echo "Previous merge succeeded automatically"
+ fi
+
+ prev_head=`git-rev-parse HEAD^0`
+
+ # save the resulting commit so we can read-tree on it later
+ echo "$prev_head" > "$dotest/`printf %0${prec}d $msgnum`.result"
+ echo "$prev_head" > "$dotest/prev_head"
+
+ # onto the next patch:
+ msgnum=$(($msgnum + 1))
+ printf "%0${prec}d" "$msgnum" > "$dotest/msgnum"
+}
+
+call_merge () {
+ cmt="$(cat $dotest/`printf %0${prec}d $1`)"
+ echo "$cmt" > "$dotest/current"
+ git-merge-$strategy "$cmt^" -- HEAD "$cmt"
+ rv=$?
+ case "$rv" in
+ 0)
+ git-commit -C "$cmt" || die "commit failed: $MRESOLVEMSG"
+ ;;
+ 1)
+ test -d "$GIT_DIR/rr-cache" && git-rerere
+ die "$MRESOLVEMSG"
+ ;;
+ 2)
+ echo "Strategy: $rv $strategy failed, try another" 1>&2
+ die "$MRESOLVEMSG"
+ ;;
+ *)
+ die "Unknown exit code ($rv) from command:" \
+ "git-merge-$strategy $cmt^ -- HEAD $cmt"
+ ;;
+ esac
+}
+
+finish_rb_merge () {
+ set -e
+
+ msgnum=1
+ echo "Finalizing rebased commits..."
+ git-reset --hard "`cat $dotest/onto`"
+ end="`cat $dotest/end`"
+ while test "$msgnum" -le "$end"
+ do
+ msgnum=`printf "%0${prec}d" "$msgnum"`
+ printf "%0${prec}d" "$msgnum" > "$dotest/msgnum"
+
+ git-read-tree `cat "$dotest/$msgnum.result"`
+ git-checkout-index -q -f -u -a
+ git-commit -C "`cat $dotest/$msgnum`"
+
+ echo "Committed $msgnum"
+ echo ' '`git-rev-list --pretty=oneline -1 HEAD | \
+ sed 's/^[a-f0-9]\+ //'`
+ msgnum=$(($msgnum + 1))
+ done
+ rm -r "$dotest"
+ echo "All done."
+}
+
while case "$#" in 0) break ;; esac
do
case "$1" in
@@ -46,17 +135,43 @@ do
exit 1
;;
esac
+ if test -d "$dotest"
+ then
+ prev_head="`cat $dotest/prev_head`"
+ end="`cat $dotest/end`"
+ msgnum="`cat $dotest/msgnum`"
+ onto="`cat $dotest/onto`"
+ continue_merge
+ while test "$msgnum" -le "$end"
+ do
+ call_merge "$msgnum"
+ continue_merge
+ done
+ finish_rb_merge
+ exit
+ fi
git am --resolved --3way --resolvemsg="$RESOLVEMSG"
exit
;;
--skip)
+ if test -d "$dotest"
+ then
+ die "--skip is not supported when using --merge"
+ fi
git am -3 --skip --resolvemsg="$RESOLVEMSG"
exit
;;
--abort)
- [ -d .dotest ] || die "No rebase in progress?"
+ if test -d "$dotest"
+ then
+ rm -r "$dotest"
+ elif test -d .dotest
+ then
+ rm -r .dotest
+ else
+ die "No rebase in progress?"
+ fi
git reset --hard ORIG_HEAD
- rm -r .dotest
exit
;;
--onto)
@@ -64,6 +179,23 @@ do
newbase="$2"
shift
;;
+ -M|-m|--m|--me|--mer|--merg|--merge)
+ do_merge=t
+ ;;
+ -s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
+ --strateg=*|--strategy=*|\
+ -s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
+ case "$#,$1" in
+ *,*=*)
+ strategy=`expr "$1" : '-[^=]*=\(.*\)'` ;;
+ 1,*)
+ usage ;;
+ *)
+ strategy="$2"
+ shift ;;
+ esac
+ do_merge=t
+ ;;
-*)
usage
;;
@@ -75,16 +207,25 @@ do
done
# Make sure we do not have .dotest
-if mkdir .dotest
+if test -z "$do_merge"
then
- rmdir .dotest
-else
- echo >&2 '
+ if mkdir .dotest
+ then
+ rmdir .dotest
+ else
+ echo >&2 '
It seems that I cannot create a .dotest directory, and I wonder if you
are in the middle of patch application or another rebase. If that is not
the case, please rm -fr .dotest and run me again. I am stopping in case
you still have something valuable there.'
- exit 1
+ exit 1
+ fi
+else
+ if test -d "$dotest"
+ then
+ die "previous dotest directory $dotest still exists." \
+ 'try git-rebase < --continue | --abort >'
+ fi
fi
# The tree must be really really clean.
@@ -152,6 +293,39 @@ then
exit 0
fi
-git-format-patch -k --stdout --full-index "$upstream"..ORIG_HEAD |
-git am --binary -3 -k --resolvemsg="$RESOLVEMSG"
+if test -z "$do_merge"
+then
+ git-format-patch -k --stdout --full-index "$upstream"..ORIG_HEAD |
+ git am --binary -3 -k --resolvemsg="$RESOLVEMSG"
+ exit $?
+fi
+
+# start doing a rebase with git-merge
+# this is rename-aware if the recursive (default) strategy is used
+
+mkdir -p "$dotest"
+echo "$onto" > "$dotest/onto"
+prev_head=`git-rev-parse HEAD^0`
+echo "$prev_head" > "$dotest/prev_head"
+
+msgnum=0
+for cmt in `git-rev-list --no-merges "$upstream"..ORIG_HEAD \
+ | perl -e 'print reverse <>'`
+do
+ msgnum=$(($msgnum + 1))
+ echo "$cmt" > "$dotest/`printf "%0${prec}d" $msgnum`"
+done
+
+printf "%0${prec}d" 1 > "$dotest/msgnum"
+printf "%0${prec}d" "$msgnum" > "$dotest/end"
+
+end=$msgnum
+msgnum=1
+
+while test "$msgnum" -le "$end"
+do
+ call_merge "$msgnum"
+ continue_merge
+done
+finish_rb_merge
--
1.4.0.g65f3
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 3/3] rebase: error out for NO_PYTHON if they use recursive merge
2006-06-21 10:04 ` [PATCH 1/3] " Eric Wong
@ 2006-06-21 10:04 ` Eric Wong
0 siblings, 0 replies; 11+ messages in thread
From: Eric Wong @ 2006-06-21 10:04 UTC (permalink / raw)
To: Junio C Hamano, git; +Cc: Eric Wong
recursive merge relies on Python, and we can't perform
rename-aware merges without the recursive merge. So bail out
before trying it.
The test won't work w/o recursive merge, either, so skip that,
too.
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
git-rebase.sh | 9 +++++++++
t/t3402-rebase-merge.sh | 6 ++++++
2 files changed, 15 insertions(+), 0 deletions(-)
diff --git a/git-rebase.sh b/git-rebase.sh
index bce7bf8..b9ce112 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -300,6 +300,15 @@ then
exit $?
fi
+if test "@@NO_PYTHON@@" && test "$strategy" = "recursive"
+then
+ die 'The recursive merge strategy currently relies on Python,
+which this installation of git was not configured with. Please consider
+a different merge strategy (e.g. octopus, resolve, stupid, ours)
+or install Python and git with Python support.'
+
+fi
+
# start doing a rebase with git-merge
# this is rename-aware if the recursive (default) strategy is used
diff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh
index 8c7a519..f1c1f35 100755
--- a/t/t3402-rebase-merge.sh
+++ b/t/t3402-rebase-merge.sh
@@ -7,6 +7,12 @@ test_description='git rebase --merge tes
. ./test-lib.sh
+if test "$no_python"; then
+ echo "Skipping: no python => no recursive merge"
+ test_done
+ exit 0
+fi
+
T="A quick brown fox
jumps over the lazy dog."
for i in 1 2 3 4 5 6 7 8 9 10
--
1.4.0.g65f3
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH] rebase: Allow merge strategies to be used when rebasing
2006-06-21 10:01 ` Eric Wong
2006-06-21 10:04 ` [PATCH (fixed)] " Eric Wong
@ 2006-06-21 11:01 ` Junio C Hamano
1 sibling, 0 replies; 11+ messages in thread
From: Junio C Hamano @ 2006-06-21 11:01 UTC (permalink / raw)
To: Eric Wong; +Cc: git
Eric Wong <normalperson@yhbt.net> writes:
> Junio C Hamano <junkio@cox.net> wrote:
>>
>> >> git-merge-$strategy $cmt^ -- HEAD $cmt
>> >
>> > Changing the 'git-merge $strategy_args "rebase-merge: $cmt" HEAD "$cmt"'
>> > line in call_merge() to this seems to have broken more tests.
>>
>> Oh, that is to be expected if you changed git-merge -s recursive
>> with git-merge-recursive without other changes. The former
>> makes a commit (which your original patch later used to create a
>> separate commit chain and discarded); the latter does not make a
>> commit but expects the caller to create a commit out of the
>> resulting index file.
>
> Oops, *smacks head*
Well, but you used it to do the right thing after all ;-).
The patch looks quite good.
>> I was originally hoping that rebasing would just be a matter of
>> listing sequence of commits to be ported onto a new base and
>> running "git-cherry-pick" on each of them in sequence. Now
>> cherry-pick does not use merge machinery (hence does not use
>> git-merge-recursive), but if we change that then updating rebase
>> would be pretty much straightforward. It just needs a UI layer
>> to guide the user through recovery process when the merge does
>> not resolve cleanly in the middle, no?
>
> Sounds workable right to me. But then again, a cherry-pick is also a
> case of rebase on a single commit, so we could be using rebase (and its
> recovery code) in cherry-pick, too, right?
Revert and cherry-pick are quite similar operation (the only
difference is that you swap his and pivot when doing revert), so
when you implement cherry-pick as an atomic operation you can
have revert almost for free. If you have a rebase like you did,
it would be a bit more involved to make it do revert as well.
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH (fixed)] rebase: Allow merge strategies to be used when rebasing
2006-06-21 10:04 ` [PATCH (fixed)] " Eric Wong
2006-06-21 10:04 ` [PATCH 1/3] " Eric Wong
@ 2006-06-21 11:01 ` Junio C Hamano
1 sibling, 0 replies; 11+ messages in thread
From: Junio C Hamano @ 2006-06-21 11:01 UTC (permalink / raw)
To: Eric Wong; +Cc: git
Eric Wong <normalperson@yhbt.net> writes:
> 1 - is fixed for --onto usage.
More importantly, it used to rebase "side edits further" commit
incorrectly in the test sequence -- the commit should only touch
"newfile", but it touched "renamed" as well. Your updated patch
fixes this problem.
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2006-06-21 11:01 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-06-18 3:02 [PATCH] rebase: Allow merge strategies to be used when rebasing Eric Wong
2006-06-18 9:08 ` Junio C Hamano
2006-06-19 21:39 ` Eric Wong
2006-06-19 21:55 ` Junio C Hamano
2006-06-21 10:01 ` Eric Wong
2006-06-21 10:04 ` [PATCH (fixed)] " Eric Wong
2006-06-21 10:04 ` [PATCH 1/3] " Eric Wong
2006-06-21 10:04 ` [PATCH 3/3] rebase: error out for NO_PYTHON if they use recursive merge Eric Wong
2006-06-21 11:01 ` [PATCH (fixed)] rebase: Allow merge strategies to be used when rebasing Junio C Hamano
2006-06-21 11:01 ` [PATCH] " Junio C Hamano
2006-06-18 11:48 ` [PATCH] Add renaming-rebase test Junio C Hamano
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox