1

When I am in the middle of a rebase conflict, I'd like to see the changes that would be instituted by --ours , --theirs and HEAD.

So, I check out all of them (literally):

git checkout --ours <new_file>

vim <new_file>

...I examine the file...

git checkout --theirs <new_file>

vim <new_file>

...I examine the file...

git checkout HEAD <new_file>

vim <new_file>

...I examine the file...

Then, I go back and checkout theirs and ours:

git checkout --theirs <new-file>

vim <new-file>

For some reason, --theirs and --ours have both conformed to the version when I checked out HEAD. I understand the git checkout is supposed to change the working directory and the index, but even when I checkout --theirs again it is still like the HEAD version.

How/why is this? And is there a way to get back my original --theirs and --ours versions? Thanks.

makansij
  • 9,303
  • 37
  • 105
  • 183

1 Answers1

2

Git uses the --ours and --theirs arguments to git checkout to select which "side" of a conflicted merge to extract. More precisely, when you have a conflicted merge, there are, for each conflicted ("unmerged") path, three1 entries in git's index/staging-area for that path. Git numbers these:

       :1:path    the merge-base version of the file
       :2:path    the "ours" version of the file
       :3:path    the "theirs" version of the file

Note that :0:path is "missing", here: slot zero is reserved for the resolved (merged) path.

When you did git checkout HEAD path, git thought you were resolving the conflict. It ditched the other three versions and filled in slot 0. In fact, if you replace HEAD with any branch or "tree-ish" specifier (e.g. git checkout branch1 path), it will also think you are resolving the conflict.

Fortunately, there's a way to tell git to (re)create the merge conflict, re-populating the other three slots (and ditching the version in slot 0):

       $ git checkout -m path

Git's merge attempt winds up in your work directory as usual.

One slight drawback is that the notations after the <<<< etc are lost (you can actually supply them, as they come out of special git environment variables, but it's rarely worth bothering, I think). [Edit to add note: I'm referring to the extra information added after the markers, e.g.:

<<<<<<< HEAD:more information here

It's the "more information here" that I have seen vanish.]


1More precisely, up to three entries: the obvious entries are left out if the file is deleted in some of the commits being merged.

torek
  • 448,244
  • 59
  • 642
  • 775
  • 1
    You say `It ditched the other three versions and filled in slot 0.` Does it fill in slot 0 with whatever I most recently checked out? – makansij Sep 23 '15 at 02:51
  • Also, what is a `path` in this context? I assume you mean a path to a conflicted file? Or is this a `git` term? – makansij Sep 23 '15 at 03:16
  • Last comment, I promise: IF it clears out the other "paths" 1, 2 and 3, then why is it that I'm still able to use `git checkout --theirs `? It seems to me that, rather than ditching them, it just fills in 1, 2, and 3 with the same contents of those in slot 0? – makansij Sep 23 '15 at 03:19
  • Yes, slot 0 gets the "resolved" version of the file (and slots 1-3 get cleared out). I italicized "path" to indicate that you can fill in any file path here, e.g., `:1:dir/sub/file.txt`. It seems like `git checkout` should complain that there is no `--ours` or `--theirs` version, at this point—using `:2:file.txt` gets an error, when there's no slot-2 entry—but perhaps the idea here is that you can apply it without error to a large collection of files (e.g., a directory) to resolve only the conflicted files in that directory. – torek Sep 23 '15 at 04:03
  • I don't understand the difference between `merge-base version` in `:1:` and `ours` in `:2:`? They seem the same on my machine? – makansij Sep 24 '15 at 15:06
  • 1
    It's possible that the merge-base version #1 and version #2 could be the same, but in general, for that case you would not get a conflict. (The files might differ slightly in white-space, which can result in conflicts that git can see, but people can't.) – torek Sep 24 '15 at 17:33
  • But, in a rebase conflict, it would seem to me that they would always be the same, right? Because the branch you are rebasing onto would always be `--ours` which would be the same as the `merge-base` version. – makansij Sep 29 '15 at 06:20
  • No, though it's hard to describe properly in a comment. Suppose you're rebasing commits `C-D`, which branch off from common point `o`, onto the tip of main-line development which goes `o-A-B`. Rebase copies commit `C` by diffing `C` vs `o` and then trying to apply that to `B` to make `C'`. If there's a conflict here, `--ours` is `B` and `--theirs` is `C` (due to rebase's viewpoint swap) while the base version is still `o`. Likewise when copying `D`, "ours" is now `C'`, "theirs" is `D`, base remains `o`. Makes more sense with graph drawn, but I can't do that in a comment. – torek Sep 29 '15 at 08:31