August Feng

Automating merge conflicts

About

Using the default ort merge strategy, what do the strategy options ours and theirs do?

Let's assume this git history for context.

gitGraph commit id: "foo" branch foobar commit id: "foobar" checkout main branch foobaz commit id: "foobaz"

Each commit mutates a file helloworld.txt:

  git init -q

  echo 'foo' > helloworld.txt
  git add helloworld.txt
  git commit -q -m "create helloworld.txt"

  git switch -q -c foobar master
  echo 'bar' >> helloworld.txt
  git add helloworld.txt
  git commit -q -m "foobar"

  git switch -q -c foobaz master
  echo 'baz' >> helloworld.txt
  git add helloworld.txt
  git commit -q -m "foobaz"

git merge

Let's merge foobar and foobaz respectively into main:

  git switch main
  git merge foobar
  git merge --strategy-option ours foobaz

When we specify ours, we are telling git to prefer the HEAD's hunk. This would have been the merge conflict if we did not specify the strategy option:

  foo
  <<<<<<< HEAD
  bar
  =======
  baz
  >>>>>>> foobaz

The final content of helloworld.txt will be:

  foo
  bar

The final git history will be:

gitGraph commit id: "foo" branch foobaz commit id: "foobaz" checkout main commit id: "foobar" checkout main merge foobaz id: "Merge branch 'foobaz'"

git rebase

Let's try to achieve a similar git history with rebase, where the history will be as follows:

gitGraph commit id: "foo" branch foobar commit id: "foobar" checkout main branch "foobaz" commit id: "foobar'" commit id: "foobaz"

The ours strategy behaves slightly in a unexpected way if we don't understand what happens during a rebase operation.

If we use the ours strategy option when using the rebase command on the foobar branch, we'll end up with foobaz's change:

  git switch foobar
  git rebase --strategy-option ours foobaz 2>&1 | tr '\r' '\n'
  cat helloworld.txt
  # foo
  # baz

The reason is that a rebase command will stash away the current branch's commits and switch into foobaz to subsequently apply them one by one.

The corollary of this is that we'll be on the foobaz branch as we merge the stashed commits, so "our" changes are actually foobaz's.