25

In man git-merge doc, git merge -s recursive -X ours:

This should not be confused with the ours merge strategy, which does not even look at what the other tree contains at all. It discards everything the other tree did, declaring our history contains all that happened in it.

I have tested these two but can't found the difference.

Is there an example to tell what is the difference between these two?

My git version is git version 1.8.3.4

Tanky Woo
  • 4,906
  • 9
  • 44
  • 75

2 Answers2

36

As the man page says, -s ours ignores the contents of the other branch entirely. This should be obvious enough: no matter what's in the other branch, the tree attached to the merge commit is identical to the tree in the HEAD commit before the merge.

What -X ours does is more subtle: it uses "our" version of a change only when there's a conflict.

Here's a relatively simple example.

Suppose that you're on branch br1 and you ask to merge in branch br2. We'll make these really simple, with commit B (the merge base origin for both branches) having one single commit K on branch br1, and one single commit L on branch br2:

... - B - K   <-- HEAD=br1
        \
          L   <-- br2

Furthermore, the difference from B to K consists of only one item:

  • change (already existing) file f1: replace first line dog with cat

Meanwhile, the difference from B to L consists of:

  • change file f1: replace first line dog with poodle
  • change file f2: replace last line elephant with rhinoceros

When you merge these with no strategy or options, there will be a conflict in file f1 (different changes to the same lines), but not in f2 (the merge commit would take the change in commit L so that file f2 would change).

If you use:

git merge -s ours br2

the merge command will use "our" version of file f1 (dog becomes cat), and also our version of file f2 (elephant is not changed).

If you use:

git merge -s recursive -X ours br2

the merge command will find a conflict on file f1 and will resolve it in favor of our version—this is the same as before—but there is no conflict on file f2, so git will use their version of f2 (elephant becomes rhinoceros).

(This simple example does not show what happens if there are two different changes in different areas in f1 or f2. If f1 is long enough and there's a change in commit L further down than "the first line of the file", the merge can pick up that change since it won't conflict with the dog-to-cat change.)

You are encouraged to make this yourself for further insight, but here is a github repo recreating this if you would like to checkout any part of the process.

topher217
  • 1,188
  • 12
  • 35
torek
  • 448,244
  • 59
  • 642
  • 775
  • @topher217: `git merge`, without an argument telling Git which *commit* you want to merge, uses the "upstream" setting of the current branch (`git rev-parse --symbolic-full-name @{u}`; `git branch --set-upstream-to`). If there is no upstream set, you get the error you saw. – torek Aug 08 '22 at 01:26
  • After playing with the github repo a bit more and looking at diffs, I'm finding the phrasing of some of this confusing. Not sure how it could be better phrased, but just wanted to hopefully add some clarity here. For example, when you say (`dog` becomes `cat`) I understand what you are trying to say in that you are comparing what `br1` has committed compared to `br2`, but following the progression of your walkthrough and going from `br1` as `HEAD` and executing `git merge -s ours br2` results in f1 (and f2) being unchanged (i.e. `cat` remains `cat`). – topher217 Aug 08 '22 at 02:18
  • 1
    The trick is that we *must not compare the tip commits directly*. We must compare the *merge base*, twice: once to each tip commit. One says `dog -> cat`, one says `dog -> poodle`, and by default Git says "I don't know which change to take". – torek Aug 08 '22 at 02:33
  • That is definitely a helpful way of thinking about it, though I'm left wondering what is the point of even using `git merge -s ours br2` as the diff between the resulting commit and `br1` is empty. If anything looking at the log makes it appear something from `br2` has been merged, when in fact nothing has. Do you have any good reasoning for using the `ours` strategy in general? The man page claims `It is meant to be used to supersede old development history of side branches.`, but how would you pull in any information from this side branch if `ours` just ignores everything? – topher217 Aug 08 '22 at 02:44
  • You don't: as the documentation implies, the point of `-s ours` is to "kill off" the other branch entirely. You're declaring it to be no good for anything in the future, but of interest to software archaeologists. – torek Aug 08 '22 at 03:30
15

I don't know of any specific examples, but the difference is this:

git merge -s recursive -X ours

This does a "normal" merge (the recursive strategy is the default one), and when there are conflicts found, it attempts to resolve those conflicts by automatically using the code fragments from the branch being merged into ("ours"), rather than leaving conflict markers.

git merge -s ours

As the documentation you quoted says, this takes our version of the code, in it's entirety, and makes it the result of the merge - the other branch is not even looked at, no conflicts are detected, etc. It just simply says "make a new commit that looks like it merges the other branch in, but leave the resulting content exactly the way it looks right now in the current branch".

twalberg
  • 59,951
  • 11
  • 89
  • 84