Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

(Git Merging) When to use 'ours' strategy, 'ours' option and 'theirs' option?

Definition of recursive merge strategy pulled from the git merge documentation.

This can only resolve two heads using a 3-way merge algorithm. When there is more than one common ancestor that can be used for 3-way merge, it creates a merged tree of the common ancestors and uses that as the reference tree for the 3-way merge. This has been reported to result in fewer merge conflicts without causing mismerges by tests done on actual merge commits taken from Linux 2.6 kernel development history. Additionally this can detect and handle merges involving renames. This is the default merge strategy when pulling or merging one branch.

The recursive strategy can take the following options:

As stated, the recursive strategy, which is the default strategy, makes use of the 3-way recursive merge algorithm (explained here and on Wikipedia).

My understanding is that conflicting hunks must be manually resolved and they are typically represented like this

<<<<<<<<<<<
developer 1's code here
============
developer 2's code here
>>>>>>>>>>>

The ours option of the recursive merge strategy is documented as follows:

This option forces conflicting hunks to be auto-resolved cleanly by favoring our version. Changes from the other tree that do not conflict with our side are reflected to the merge result. For a binary file, the entire contents are taken from our side.

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.

Now suppose I have the heads of two branches Y and M, with a common base ancestor B as follows

enter image description here

When merging Y and M using the default recursive strategy, line 30 will become Print("hello"); since at line 30, Y represents a change from the base ancestor and M does not. But if I were on branch M and run

git merge -s recursive -X ours Y

will line 30 become Print("bye"); in the merged output?

To those of you who say that this is obvious, note that the ours option states

This option forces conflicting hunks to be auto-resolved cleanly by favoring our version.

But (as I understand) there is no conflicting hunk at line 30.

For completeness, I will also give the documentation of the theirs option:

This is the opposite of ours.


The documentation for the ours strategy is as follows:

This resolves any number of heads, but the resulting tree of the merge is always that of the current branch head, effectively ignoring all changes from all other branches. It is meant to be used to supersede old development history of side branches. Note that this is different from the -Xours option to the recursive merge strategy.

So returning to the example above, if I ran

git merge -s ours Y

on branch M, it is clear that line 30 will be Print("bye"); in the merged output. In this case, why is there also not a theirs strategy? How can I achieve behaviour equal and opposite to the ours strategy?

I'm asking because I'm working on a project where I want to regularly and completely overwrite the master branch with changes from another development branch, whenever the code on the development branch builds successfully. This way I can ensure that my development branch never drifts too far off from the master branch and also that the code on the master branch will build successfully.

I have seen this question which recommends the solution

git checkout dev-branch
git merge -s ours master

But Git simply outputs Already up-to-date, despite the fact that the two branches contain different code (and dev-branch is actually a few commits ahead of master).

My current solution is to do

git merge -s recursive -X theirs dev-branch

I have also seen this question which recommends using the theirs option of the recursive strategy. But as the ours option of the recursive strategy is clearly different ours strategy, so too would the theirs option of the recursive strategy be different from the theirs strategy that I'm talking about.

like image 615
UnchartedWaters Avatar asked Jul 30 '17 17:07

UnchartedWaters


1 Answers

git merge -s recursive -X ours Y will line 30 become Print("bye"); in the merged output?

No, it will also output Print("hello");. This is because only the other side (Y branch) changed this file, file version on M branch is the same as their ancestor B, so recursive merge strategy keep the updated version from Y branch.

And you can have try: only the file’s version from M branch is different from their ancestor B (such as commit for changing 30 line as Print(“bye1”);), then the -X options can works. Now if you use git merge -s recursive -X ours Y, the output will be Print(“bye1”);.

And you can also find in Figure 4 of the article you linked, if one side of the file is same as their ancestor (as line 30 and 70), the file will keep the other side (changed) version as the merge results.

Reason for git merge -s ours Y output Print("bye");:

As the document said, This resolves any number of heads, but the resulting tree of the merge is always that of the current branch head, effectively ignoring all changes from all other branches.

That means it will ignore the version from Y branch and only keep the version as current branch is. So you got the output as the version of M branch Print("bye");.

Why there is no git merge -s theirs for -s option:

Git only defined the merge strategies with octupus, ours, recursive, resolve and subtree, so the -s theirs can’t be recognized and it’s a design issue. For the detail reason, only the git version control system developers may know.


For your situation (make sure development branch overwrite master branch), you can cherry-pick the latest commit from development branch to master branch with -X theirs option:

# On master branch
git cherry-pick development -X theirs

This will fully overwrite master branch.

Note: development in git cherry-pick command means the commit which development branch is point to (latest commit on development branch). You can also use commit sha-1 value instead.

like image 168
Marina Liu Avatar answered Sep 20 '22 12:09

Marina Liu