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
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.
git merge -s recursive -X ours Y
will line 30 becomePrint("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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With