23

Let's say I did a git merge and have a conflict in some file, say package.json or pom.xml, and perhaps some other files.

I would like to

  1. automatically resolve merge conflicts for certain files from command line, while
  2. letting myself resolve manually all the other conflicts, if any, and
  3. making sure I don't lose valuable changes from any branch.

Because of 2), I can't use git merge -Xours as this would resolve all conflicts. I want to resolve only conflicts in one particular file.

Because of 3), I can't use git checkout --ours package.json as this might lose some changes from the input branch.

The use case is for instance: auto-merging script that will resolve trivial well-known conflicts in a predefined way, and fail if there are some other conflicts in other files.

The typical case I often have is having two branches with version field in package.json diverged like below:

$ git diff
diff --cc package.json
index 75c469b,f709434..0000000
--- a/package.json
+++ b/package.json
@@@ -1,6 -1,6 +1,12 @@@
  {
    "name": "test",
++<<<<<<< HEAD
 +  "version": "2.0.1",
++||||||| merged common ancestors
++  "version": "1.0.0",
++=======
+   "version": "1.0.2",
++>>>>>>> master

Is it possible to resolve the conflict for just one file, using given strategy? Something like:

git-resolve-conflict --ours package.json
Pavlo
  • 43,301
  • 14
  • 77
  • 113
jakub.g
  • 38,512
  • 12
  • 92
  • 130

1 Answers1

22

This can be achieved using git-merge-file although its API is rather low-level.

In order to have an easy-to-use command, you can use the following bash script (to be imported to .bashrc, or you can also install it with npm install -g git-resolve-conflict):

git-resolve-conflict() {
  STRATEGY="$1"
  FILE_PATH="$2"

  if [ -z "$FILE_PATH" ] || [ -z "$STRATEGY" ]; then
    echo "Usage:   git-resolve-conflict <strategy> <file>"
    echo ""
    echo "Example: git-resolve-conflict --ours package.json"
    echo "Example: git-resolve-conflict --union package.json"
    echo "Example: git-resolve-conflict --theirs package.json"
    return
  fi

  git show :1:"$FILE_PATH" > ./tmp.common
  git show :2:"$FILE_PATH" > ./tmp.ours
  git show :3:"$FILE_PATH" > ./tmp.theirs

  git merge-file "$STRATEGY" -p ./tmp.ours ./tmp.common ./tmp.theirs > "$FILE_PATH"
  git add "$FILE_PATH"

  rm ./tmp.common
  rm ./tmp.ours
  rm ./tmp.theirs
}

(Note: I keep updating the script in my GitHub repo, check the repo for the latest improvements: https://github.com/jakub-g/git-resolve-conflict/blob/base/lib/git-resolve-conflict.sh)

In the particular example as described in the question, the usage would be as follows:

git-resolve-conflict --ours package.json

For a step by step study see:

https://github.com/jakub-g/git-resolve-conflict

Bonus:

If you have multiple package.json files in subfolders, and want to resolve the merge for all of them which are in conflicted state:

for ITEM in $(git diff --name-only --diff-filter=U | grep package.json$); do git-resolve-conflict --ours $ITEM; done
Community
  • 1
  • 1
jakub.g
  • 38,512
  • 12
  • 92
  • 130
  • Nice script. However, it fails if run from a directory other than the root of the repo. `git -x` only matches exact matches, but the filename is missing leading path. – Matt Nov 05 '20 at 00:42
  • 1
    @Matt you can add `cd "./"$(git rev-parse --show-cdup)` in the first line to move to the root of the repo, and then `cd -` at the end to return to previous folder. (It won't work though if you run the script from outside the repo, but this is probably a very niche edge case). – jakub.g Nov 05 '20 at 08:57