Lithy
Lithy

Reputation: 877

`git merge --strategy-option theirs` for individual files

Using git checkout --theirs or --ours is handy for resolving conflicts, but they either take the full "theirs" file or the full "ours" file. It does not merge anything, even in areas where it is easy to merge (i.e. areas where the conflicted file does not show any >>>> and <<<<).

Is it possible to do a kind of checkout --theirs where there are conflicts but keep merged areas of the file that where correctly merged?

Said differently, I am looking for something that behaves like git merge --strategy-option theirs but at the scale of a single file (because for some other files I'd like to use other merging strategies).

Here is a minimal example to reproduce my issue:

  1. We first create a repo with a simple file a.txt:
$ git init
$ cat > a.txt
lorem
ipsum
dolor
sit
amet

consectatur
adipiscing
elit
some
area
to
change
$ git add a.txt
$ git commit -m 'A'
  1. In a branch, we edit the file at multiple location (top and bottom parts):
$ git checkout -b branch
$ cat > a.txt
lorem
ipsum
dolorB
sit
amet

consectatur
adipiscing
elit
B
some
areaB
change
$ git add a.txt
$ git commit -m 'B'
  1. In the master branch, we edit only one part of the file (top):
$ git checkout master
$ cat > a.txt
lorem
ipsumA
dolorA
sitA
amet

consectatur
adipiscing
elit
some
area
to
change
$ git add a.txt
$ git commit -m 'C'
  1. When trying to merge, the top part is in conflict but the bottom part could be merged successfully:
$ git merge branch
CONFLICT (content): Merge conflict in a.txt
$ cat a.txt
lorem
<<<<<<< HEAD
ipsumA
dolorA
sitA
=======
ipsum
dolorB
sit
>>>>>>> branch
amet

consectatur
adipiscing
elit
B
some
areaB
change

What I would like to have is a merged a.txt where each time there is a conflict the HEAD block is used (master) but still merge areas where there is no conflict.

Neither git checkout --theirs nor git checkout --ours works here:

$ git checkout --theirs a.txt
$ cat a.txt
lorem
ipsum
dolorB  <--- NOPE!
sit
amet

consectatur
adipiscing
elit
B
some
areaB
change
$ git checkout --ours a.txt
$ cat a.txt
lorem
ipsumA
dolorA
sitA
amet

consectatur
adipiscing
elit
some
area  <--- NOPE!
to
change

What I would like to have is:

lorem
ipsumA
dolorA
sitA
amet

consectatur
adipiscing
elit
B
some
areaB
change

Upvotes: 2

Views: 5612

Answers (1)

torek
torek

Reputation: 487883

Is it possible to do a kind of checkout --theirs where there are conflicts but keep merged areas of the file that where correctly merged?

No, but fortunately:

Said differently, I am looking for something that behaves like git merge --strategy-option theirs but at the scale of a single file (because for some other files I'd like to use other merging strategies).

Here the answer is "yes". It's just not git checkout at all.

The first step is to extract all three input files: merge base version, ours version, and theirs version. You can do this manually with:

git show :1:path/to/file > path/to/file.base
git show :2:path/to/file > path/to/file.ours
git show :3:path/to/file > path/to/file.theirs

(If you're in the appropriate subdirectory already, you can just use file three time with the three numbered index slots, and file.base etc with the names for the redirections.)

Side note: the git mergetool command knows how to do this on its own but might be impractical to use here, as it attempts to one particular command—your chosen merge tool—on all remaining conflicted files. That is, it does the equivalent of running git status and checking for unresolved files, and then does the three-checkouts thing for each such file, but then immediately wants to run the (single) tool on those three files. (Ideally this particular part of git mergetool, i.e., the "get three versions" part, ought to be provided as a separate Git command, but it isn't.)

Now that you have the three files, the command that lets you do git merge -X theirs on them is not actually git merge but rather git merge-file:

git merge-file --theirs file.ours file.base file.theirs

(note that the option is spelled slightly differently, but is still "theirs"). The order of the three named files matters: the first one must be the current or ours version and the last one must be the theirs version, leaving the base in the middle.

The program writes its output back to the first named file, but in this case if something goes wrong and you want to start over, you can just git show again. If you like the result, move it into place and git add it:

mv file.ours file; git add file

for instance (assuming no path/to/ part needed).

Upvotes: 2

Related Questions