0

Scenario:

  1. attempt a merge from branch feature into master

  2. witness minor conflicts, e.g. a different version number, that should not be merged

How can you tell git to merge only non-conflicting parts and don't touch conflicting ones, just like when running git merge -s recursive -Xours feature?

My current solution is to run git merge --abort, followed by the above mentioned strategy-merge. Is there a way to do this without first aborting my merge? I do not want to fire up another tool or work through every conflict one-by-one.

Neither the git docs on merging nor this SO thread give any advice.

mattmilten
  • 6,242
  • 3
  • 35
  • 65

1 Answers1

0

You will still have to work through every file one by one, but you can use git merge-file to achieve what you want. You may wish to write a script to accomplish the steps below.

When Git stops with a merge conflict, Git leaves all three versions of the conflicted file in the index / staging-area. Let's say, for instance, that the conflicted file is named README.txt. Then because there was a conflict, you have a README.txt in your work-tree with conflict markers, but you also have:

  • :1:README.txt: the README.txt from the merge base (the common commit from which both your current commit, and your merge target commit, diverged).
  • :2:README.txt: the README.txt from the HEAD or --ours commit.
  • :3:README.txt: the README.txt from the --theirs commit.

You can extract each of these three files to a temporary file. The most straightforward way is to use git show :1:README.txt > README.txt.base, for instance. There is a fancier way that is more suitable for scripting—the git mergetool command uses this:

checkout_staged_file () {
        tmpfile=$(expr \
                "$(git checkout-index --temp --stage="$1" "$2" 2>/dev/null)" \
                : '\([^ ]*\)    ')

        if test $? -eq 0 && test -n "$tmpfile"
        then
                mv -- "$(git rev-parse --show-cdup)$tmpfile" "$3"
        else
                >"$3"
        fi
}

which it invokes as:

    checkout_staged_file 1 "$MERGED" "$BASE"
    checkout_staged_file 2 "$MERGED" "$LOCAL"
    checkout_staged_file 3 "$MERGED" "$REMOTE"

In any case, your job is to extract these three files, then run:

git merge-file --ours <stage-2-file> <stage-1-file> <stage-3-file>

which will leave the merged result, with conflicts resolved in favor of "ours", in <stage-2-file>.

Thus, given all the above, if the file is named README.txt and you wanted to re-do the merge of that one file with the equivalent of -X ours, these would work (though the git show method does not obey CRLF settings, hence you might want the fancier git checkout-index stuff):

git show :1:README.txt > README.txt.base
git checkout --ours README.txt
git show :3:README.txt > README.txt.other
git merge-file README.txt README.txt.base README.txt.other
rm README.txt.base README.txt.other
git add README.txt

The merge is now complete for README.txt. Repeat these six steps for all other files for which you wish to apply this "override conflicts from --ours" rule.

torek
  • 448,244
  • 59
  • 642
  • 775
  • Thank you for this extensive answer! Although, I was hoping for a more straight-forward solution to this seemingly straight-forward problem. – mattmilten Jul 03 '18 at 17:16
  • 1
    It would at least be nice for all of this to be wrapped up in a clean and neat script distributed with Git. You could then do `git retry-merge --ours `, and `git-retry-merge` would do all of the above (and perhaps even re-create conflicts with `git checkout -m` if needed). – torek Jul 03 '18 at 17:37