Search code examples
gitgit-merge-conflict

What's the default version of a binary marked as both modified in Git?


If the conflict happen on a binary or any non-text files that can't be resolved by using text editor, I should use git checkout --theirs or git checkout --ours instead, then add them into the staging area.

But, if I just do nothing, add those unmerged binaries into staging area directly, without resolving the conflict, without choosing a proper version, which version will be added into staging area ? Ours or theirs ?

Unmerged paths:
  (use "git add/rm <file>..." as appropriate to mark resolution)

      both modified: test.bin
      both modified: test.a 

Solution

  • If the conflict happen on a binary or any non-text files that can't be resolved by using text editor, I should use git checkout --theirs or git checkout --ours instead ...

    This is not necessarily true. Just as with plain text files, where sometimes the correct resolution is in fact a combined change, the correct resolution of some binary files may be a combined change. It's just that Git cannot do this for you. The only way to do it correctly may be to find a source file or files, and some sort of source-to-binary translator. You may need to combine the source changes, compile / translate the result to a new, third binary, and use that.

    Nonetheless, this question has an answer. It just may be pointless to study and memorize it.

    But, if I just do nothing, add those unmerged binaries into staging area directly ...

    What git add does is to copy whatever is in the work-tree into the index / staging-area at slot zero, removing any higher-stage entries. What you have during the conflict is three different binaries, in the index / staging area, but using slots 1 (merge base), 2 (ours), and 3 (theirs).

    Hence, this just means Copy the work-tree file into slot zero and remove the entries in slots 1, 2, and 3. So the question is the same as:

    What does Git leave in the work-tree in the case of a conflict in a binary file?

    The answer to that is in both the git merge documentation and the gitattributes documentation. This makes it hard to read.

    The first one says, regarding the recursive and resolve strategies, that there are additional arguments that affect the result. These additional arguments are the -X ours and -X theirs arguments, which mean:

    ours
    This option forces conflicting hunks to be auto-resolved cleanly by favoring our version. Changes from the other tree that do not conflict with our side are reflected to the merge result. For a binary file, the entire contents are taken from our side.
    ...
    theirs
    This is the opposite of ours; note that, unlike ours, there is no theirs merge strategy to confuse this merge option with.

    The gitattributes documentation adds this:

    Performing a three-way merge
    ...
    The attribute merge affects how three versions of a file are merged when a file-level merge is necessary ...

    and then goes on to say that most of them "[k]eep the version from your branch in the work tree". So in general the work-tree matches index-slot-2, which you can test by doing git checkout-index --temp --stage=2 test.bin and comparing the resulting file to test.bin.

    It's not clear whether such attributes override -X theirs; you will have to test this, if you set your own merge attributes and use -X theirs, by comparing what's in the work-tree against what's in index slots 2 and 3.

    In general, though, most approaches mostly leave the "ours" version in the work-tree. If that's appropriate, you can use git add to copy the work-tree version back to slot zero and erase slots 1-3, marking the file as resolved. Just be sure that if you have used -X theirs and/or some sort of interesting merge setting, that you know what you are doing and have double checked everything.

    (Your best bet, of course, is to avoid merging binary files this way.)