Documentation: new upstream rebase recovery section in git-rebase
Document how to recover if the upstream that you pull from has rebased the branches you depend your work on. Hopefully this can also serve as a warning to potential rebasers. Signed-off-by: Thomas Rast <trast@student.ethz.ch> Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
parent
1293c95241
commit
90d1c08efc
|
@ -257,11 +257,10 @@ include::merge-strategies.txt[]
|
||||||
|
|
||||||
NOTES
|
NOTES
|
||||||
-----
|
-----
|
||||||
When you rebase a branch, you are changing its history in a way that
|
|
||||||
will cause problems for anyone who already has a copy of the branch
|
You should understand the implications of using 'git-rebase' on a
|
||||||
in their repository and tries to pull updates from you. You should
|
repository that you share. See also RECOVERING FROM UPSTREAM REBASE
|
||||||
understand the implications of using 'git-rebase' on a repository that
|
below.
|
||||||
you share.
|
|
||||||
|
|
||||||
When the git-rebase command is run, it will first execute a "pre-rebase"
|
When the git-rebase command is run, it will first execute a "pre-rebase"
|
||||||
hook if one exists. You can use this hook to do sanity checks and
|
hook if one exists. You can use this hook to do sanity checks and
|
||||||
|
@ -396,6 +395,127 @@ consistent (they compile, pass the testsuite, etc.) you should use
|
||||||
after each commit, test, and amend the commit if fixes are necessary.
|
after each commit, test, and amend the commit if fixes are necessary.
|
||||||
|
|
||||||
|
|
||||||
|
RECOVERING FROM UPSTREAM REBASE
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
Rebasing (or any other form of rewriting) a branch that others have
|
||||||
|
based work on is a bad idea: anyone downstream of it is forced to
|
||||||
|
manually fix their history. This section explains how to do the fix
|
||||||
|
from the downstream's point of view. The real fix, however, would be
|
||||||
|
to avoid rebasing the upstream in the first place.
|
||||||
|
|
||||||
|
To illustrate, suppose you are in a situation where someone develops a
|
||||||
|
'subsystem' branch, and you are working on a 'topic' that is dependent
|
||||||
|
on this 'subsystem'. You might end up with a history like the
|
||||||
|
following:
|
||||||
|
|
||||||
|
------------
|
||||||
|
o---o---o---o---o---o---o---o---o master
|
||||||
|
\
|
||||||
|
o---o---o---o---o subsystem
|
||||||
|
\
|
||||||
|
*---*---* topic
|
||||||
|
------------
|
||||||
|
|
||||||
|
If 'subsystem' is rebased against 'master', the following happens:
|
||||||
|
|
||||||
|
------------
|
||||||
|
o---o---o---o---o---o---o---o master
|
||||||
|
\ \
|
||||||
|
o---o---o---o---o o'--o'--o'--o'--o' subsystem
|
||||||
|
\
|
||||||
|
*---*---* topic
|
||||||
|
------------
|
||||||
|
|
||||||
|
If you now continue development as usual, and eventually merge 'topic'
|
||||||
|
to 'subsystem', the commits from 'subsystem' will remain duplicated forever:
|
||||||
|
|
||||||
|
------------
|
||||||
|
o---o---o---o---o---o---o---o master
|
||||||
|
\ \
|
||||||
|
o---o---o---o---o o'--o'--o'--o'--o'--M subsystem
|
||||||
|
\ /
|
||||||
|
*---*---*-..........-*--* topic
|
||||||
|
------------
|
||||||
|
|
||||||
|
Such duplicates are generally frowned upon because they clutter up
|
||||||
|
history, making it harder to follow. To clean things up, you need to
|
||||||
|
transplant the commits on 'topic' to the new 'subsystem' tip, i.e.,
|
||||||
|
rebase 'topic'. This becomes a ripple effect: anyone downstream from
|
||||||
|
'topic' is forced to rebase too, and so on!
|
||||||
|
|
||||||
|
There are two kinds of fixes, discussed in the following subsections:
|
||||||
|
|
||||||
|
Easy case: The changes are literally the same.::
|
||||||
|
|
||||||
|
This happens if the 'subsystem' rebase was a simple rebase and
|
||||||
|
had no conflicts.
|
||||||
|
|
||||||
|
Hard case: The changes are not the same.::
|
||||||
|
|
||||||
|
This happens if the 'subsystem' rebase had conflicts, or used
|
||||||
|
`\--interactive` to omit, edit, or squash commits; or if the
|
||||||
|
upstream used one of `commit \--amend`, `reset`, or
|
||||||
|
`filter-branch`.
|
||||||
|
|
||||||
|
|
||||||
|
The easy case
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Only works if the changes (patch IDs based on the diff contents) on
|
||||||
|
'subsystem' are literally the same before and after the rebase
|
||||||
|
'subsystem' did.
|
||||||
|
|
||||||
|
In that case, the fix is easy because 'git-rebase' knows to skip
|
||||||
|
changes that are already present in the new upstream. So if you say
|
||||||
|
(assuming you're on 'topic')
|
||||||
|
------------
|
||||||
|
$ git rebase subsystem
|
||||||
|
------------
|
||||||
|
you will end up with the fixed history
|
||||||
|
------------
|
||||||
|
o---o---o---o---o---o---o---o master
|
||||||
|
\
|
||||||
|
o'--o'--o'--o'--o' subsystem
|
||||||
|
\
|
||||||
|
*---*---* topic
|
||||||
|
------------
|
||||||
|
|
||||||
|
|
||||||
|
The hard case
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Things get more complicated if the 'subsystem' changes do not exactly
|
||||||
|
correspond to the ones before the rebase.
|
||||||
|
|
||||||
|
NOTE: While an "easy case recovery" sometimes appears to be successful
|
||||||
|
even in the hard case, it may have unintended consequences. For
|
||||||
|
example, a commit that was removed via `git rebase
|
||||||
|
\--interactive` will be **resurrected**!
|
||||||
|
|
||||||
|
The idea is to manually tell 'git-rebase' "where the old 'subsystem'
|
||||||
|
ended and your 'topic' began", that is, what the old merge-base
|
||||||
|
between them was. You will have to find a way to name the last commit
|
||||||
|
of the old 'subsystem', for example:
|
||||||
|
|
||||||
|
* With the 'subsystem' reflog: after 'git-fetch', the old tip of
|
||||||
|
'subsystem' is at `subsystem@\{1}`. Subsequent fetches will
|
||||||
|
increase the number. (See linkgit:git-reflog[1].)
|
||||||
|
|
||||||
|
* Relative to the tip of 'topic': knowing that your 'topic' has three
|
||||||
|
commits, the old tip of 'subsystem' must be `topic~3`.
|
||||||
|
|
||||||
|
You can then transplant the old `subsystem..topic` to the new tip by
|
||||||
|
saying (for the reflog case, and assuming you are on 'topic' already):
|
||||||
|
------------
|
||||||
|
$ git rebase --onto subsystem subsystem@{1}
|
||||||
|
------------
|
||||||
|
|
||||||
|
The ripple effect of a "hard case" recovery is especially bad:
|
||||||
|
'everyone' downstream from 'topic' will now have to perform a "hard
|
||||||
|
case" recovery too!
|
||||||
|
|
||||||
|
|
||||||
Authors
|
Authors
|
||||||
------
|
------
|
||||||
Written by Junio C Hamano <gitster@pobox.com> and
|
Written by Junio C Hamano <gitster@pobox.com> and
|
||||||
|
|
Loading…
Reference in New Issue