Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

git: where do the conflict commits go when rebasing

Tags:

git

rebase

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 ?

like image 806
Jeanluca Scaljeri Avatar asked Sep 06 '25 19:09

Jeanluca Scaljeri


2 Answers

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.

like image 175
mockinterface Avatar answered Sep 08 '25 23:09

mockinterface


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.)

like image 27
torek Avatar answered Sep 08 '25 23:09

torek