Suppose I have a commit history like this
master
|
|A| -> |B| -> |E|
|
|C| -> |D|
|
hotfix
Assume that commit E and D have conflicts. Now, according the docs, after a rebase
$> git checkout hotfix
$> git rebase master
# fix conflicts
$> git add -A
$> git rebase --continue
$> git push --force # rewrite history of branch hotfix on remote
I will endup with
master
|
|A| -> |B| -> |E|
|
|C| -> |D|
|
hotfix
However, I don't see where my merge/conflict results go ? If I do rebase again
$> git rebase master
I don't expect to get the same conflicts again, right ?
Final question, if commit D was before commit E, would the diagram I showed above (result after rebase) be still the same ?
However, I don't see where my merge/conflict results go ?
When you rebase git stores the interim objects in a temporary area under the .git
directory. (For instance, .git/rebase-merge
.) If there are conflicts, you will be asked to resolve them in the process of doing the rebase.
If I do rebase again ... I don't expect to get the same conflicts again, right ?
No you won't get the same conflicts again, because you've resolved them in your previous (first) rebase.
Final question, if commit D was before commit E, would the diagram I showed above (result after rebase) be still the same ?
Yes wherever C
and D
were the outcome it would still be the same, in the sense that they would be applied on the top of E
- the tip of master, but in both case it won't exactly be the diagram that you draw. The main difference is that the commits would be D'
and C'
, they will be new commits even if they are completely identical to D
and C
, in the case of no rebase conflicts.
When you do a rebase, what you're causing git to do "under the covers" is actually a series of cherry-picks. It's a lot more obvious if you label the commits differently: let's call the "new" versions of C
and D
, C'
and D'
instead. And, let's leave the old ones in there, but "abandoned" (lost and lonely, as it were). So we start with this (I've reversed the arrows since commits point to their parent commits, not their children: E
has B
as a parent, D
has C
, C
has B
, and B
has A
):
A <-- B <-- E <-- master
^
\
C <-- D <-- hotfix
To do the rebase, git cherry-picks commit C
, making new C'
with E
as its parent; then cherry-picks D
, intending to make new D'
with C'
as its parent. There's a conflict at this point (as you said) so the whole process stops and makes you resolve the conflict and continue. This finally makes D'
and then git rewrites the hotfix
label to point to D'
:
A <-- B <-- E <-- master
^ ^
| \
C - D \
\
C' <-- D' <-- hotfix
The old commits, C
and D
, are actually still in there (but with no branch label—though there's still a reference to them in the hotfix
branch reflog, and temporarily, rebase leaves a special label spelled ORIG_HEAD
pointing to D
). It's just that when you look to see what's "on branch hotfix
", you now see the copied commits.
The conflict resolution you applied is in the tree attached to commit D'
(since that's where the conflict occurred and you fixed it manually). If you dig up the commit-ID for D
, or use the reflog or ORIG_HEAD
, you can diff the trees for D
and D'
to see what you did:
$ git diff ORIG_HEAD hotfix
for instance. (But this also includes the changes in E
, so that's probably misleading too.)
(Each cherry-pick is done, loosely speaking, by diffing the to-be-picked commit's tree with its parent's tree, then applying that diff as a patch to the commit the "cherry" is being added on to. So to cherry-pick C
and add it to E
to get C'
, git diffs B
and C
. Then, to cherry-pick D
, git diffs C
and D
, applying the diff to C'
this time. Thus the tree for D
won't contain any lines from E
but the tree for D'
probably will.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With