When merging topic branch "B" into "A" using git merge
, I get some conflicts. I know all the conflicts can be solved using the version in "B".
I am aware of git merge -s ours
. But what I want is something like git merge -s theirs
.
Why doesn't it exist? How can I achieve the same result after the conflicting merge with existing git
commands? (git checkout
every unmerged file from B)
The "solution" of just discarding anything from branch A (the merge commit point to B version of the tree) is not what I am looking for.
git merge -s their
.
git merge -s theirs
(that can be achieved easily with git merge -s ours
and a temporary branch), since -s ours completely ignores the changes of the merge-from branch...
theirs
in addition to ours
??? This is a symptom of one of the high level engineering and design problems in Git: inconsistency.
git merge -s ours
.
A similar alternative is the --strategy-option
(short form -X
) option, which accepts theirs
. For example:
git checkout branchA
git merge -X theirs branchB
However, this is more equivalent to -X ours
than -s ours
. The key difference being that -X
performs a regular recursive merge, resolving any conflicts using the chosen side, whereas -s ours
changes the merge to just completely ignore the other side.
In some cases, the main problem using -X theirs
instead of the hypothetical -s theirs
is deleted files. In this case, just run git rm
with the name of any files that were deleted:
git rm {DELETED-FILE-NAME}
After that, the -X theirs
may work as expected.
Of course, doing the actual removal with the git rm
command will prevent the conflict from happening in the first place.
A possible and tested solution for merging branchB into our checked-out branchA:
# in case branchA is not our current branch
git checkout branchA
# make merge commit but without conflicts!!
# the contents of 'ours' will be discarded later
git merge -s ours branchB
# make temporary branch to merged commit
git branch branchTEMP
# get contents of working tree and index to the one of branchB
git reset --hard branchB
# reset to our merged commit but
# keep contents of working tree and index
git reset --soft branchTEMP
# change the contents of the merged commit
# with the contents of branchB
git commit --amend
# get rid off our temporary branch
git branch -D branchTEMP
# verify that the merge commit contains only contents of branchB
git diff HEAD branchB
To automate it you can wrap it into a script using branchA and branchB as arguments.
This solution preserves the first and second parent of the merge commit, just as you would expect of git merge -s theirs branchB
.
git reset --soft branchA
?
git reset --hard
changes which commit branchA
points to.
Older versions of git allowed you to use the "theirs" merge strategy:
git pull --strategy=theirs remote_branch
But this has since been removed, as explained in this message by Junio Hamano (the Git maintainer). As noted in the link, instead you would do this:
git fetch origin
git reset --hard origin
Beware, though, that this is different than an actual merge. Your solution is probably the option you're really looking for.
git reset --hard origin
a solution for a theirs style merge? If I wanted to merge BranchB into BranchA (like in Alan W. Smith's answer), how would I do it using the reset
method?
git reset --hard
does a “theirs”-style merge. Of course, git reset --hard
does not create any merge commit, or any commit for that matter. His point is that we should not use a merge to replace whatever in HEAD by something else. I do not necessarily agree, though.
theirs
was removed because the rationale was incomplete. It failed to allow for those instances where the code is good, its just that the upstream is maintained on a different philosophical basis, so in that sense is 'bad', so one does want to both keep up to date with the upstream, but at the same time retain ones good code 'fixes' [git & msysgit have this some of this 'conflict' because of their different target platform's philosophies]
theirs
because I had accidentally changed some files in two separate branches and wanted to discard the changes of one without having to manually do it for every file.
It is not entirely clear what your desired outcome is, so there is some confusion about the "correct" way of doing it in the answers and their comments. I try to give an overview and see the following three options:
Try merge and use B for conflicts
This is not the "theirs version for git merge -s ours
" but the "theirs version for git merge -X ours
" (which is short for git merge -s recursive -X ours
):
git checkout branchA
# also uses -s recursive implicitly
git merge -X theirs branchB
This is what e.g. Alan W. Smith's answer does.
Use content from B only
This creates a merge commit for both branches but discards all changes from branchA
and only keeps the contents from branchB
.
# Get the content you want to keep.
# If you want to keep branchB at the current commit, you can add --detached,
# else it will be advanced to the merge commit in the next step.
git checkout branchB
# Do the merge an keep current (our) content from branchB we just checked out.
git merge -s ours branchA
# Set branchA to current commit and check it out.
git checkout -B branchA
Note that the merge commits first parent now is that from branchB
and only the second is from branchA
. This is what e.g. Gandalf458's answer does.
Use content from B only and keep correct parent order
This is the real "theirs version for git merge -s ours
". It has the same content as in the option before (i.e. only that from branchB
) but the order of parents is correct, i.e. the first parent comes from branchA
and the second from branchB
.
git checkout branchA
# Do a merge commit. The content of this commit does not matter,
# so use a strategy that never fails.
# Note: This advances branchA.
git merge -s ours branchB
# Change working tree and index to desired content.
# --detach ensures branchB will not move when doing the reset in the next step.
git checkout --detach branchB
# Move HEAD to branchA without changing contents of working tree and index.
git reset --soft branchA
# 'attach' HEAD to branchA.
# This ensures branchA will move when doing 'commit --amend'.
git checkout branchA
# Change content of merge commit to current index (i.e. content of branchB).
git commit --amend -C HEAD
This is what Paul Pladijs's answer does (without requiring a temporary branch).
Special cases
If the commit of branchB
is an ancestor of branchA
, git merge
does not work (it just exits with a message like "Already up to date.").
In this or other similar/advanced cases the low-level command git commit-tree
can be used.
git checkout branchA; git merge -s ours --no-commit branchB; git read-tree -um @ branchB; git commit
-s ours
should always be used instead. The -s ours
is intended to save some branch as deprecated and all "users" of that branch should fast forward to another branch. Putting parents in "correct" (sic) order would be opposite.
branchB
into branchA
should always result in branchA
being the first parent and branchB
the second. The chosen strategy should have no influence on this. So imitating (the non existing) strategy -s theirs
to merge branchB
into branchA
should result in branchA
being the first parent too. I agree it might not be useful that often (thats the reason it does not exist), but if you really want to do -s theirs
, the correct parent order is the reverse of a content-equivalent -s ours
by definition, in my opinion. Why else would you need it?
I used the answer from Paul Pladijs since now. I found out, you can do a "normal" merge, conflicts occur, so you do
git checkout --theirs <file>
to resolve the conflict by using the revision from the other branch. If you do this for each file, you have the same behaviour as you would expect from
git merge <branch> -s theirs
Anyway, the effort is more than it would be with the merge-strategy! (This was tested with git version 1.8.0)
git ls-files --modified | xargs git add
I wanted to do this for added on both sides merge :/
git ls-files --modified
so i guess this is also a viable solution.
git config --global alias.unresolved '!git status --short|egrep "^([DAU])\1"'
-X
and what's the difference between -X
and -s
? cannot find any document.
When merging topic branch "B" in "A" using git merge, I get some conflicts. I >know all the conflicts can be solved using the version in "B". I am aware of git merge -s ours. But what I want is something like git merge >-s their.
I'm assuming that you created a branch off of master and now want to merge back into master, overriding any of the old stuff in master. That's exactly what I wanted to do when I came across this post.
Do exactly what it is you want to do, Except merge the one branch into the other first. I just did this, and it worked great.
git checkout Branch
git merge master -s ours
Then, checkout master and merge your branch in it (it will go smoothly now):
git checkout master
git merge Branch
I solved my problem using
git checkout -m old
git checkout -b new B
git merge -s ours old
If you are on branch A do:
git merge -s recursive -X theirs B
Tested on git version 1.7.8
Why doesn't it exist?
While I mention in "git command for making one branch like another" how to simulate git merge -s theirs
, note that Git 2.15 (Q4 2017) is now clearer:
The documentation for '-X
See commit c25d98b (25 Sep 2017) by Junio C Hamano (gitster
).
(Merged by Junio C Hamano -- gitster
-- in commit 4da3e23, 28 Sep 2017)
merge-strategies: avoid implying that "-s theirs" exists The description of -Xours merge option has a parenthetical note that tells the readers that it is very different from -s ours, which is correct, but the description of -Xtheirs that follows it carelessly says "this is the opposite of ours", giving a false impression that the readers also need to be warned that it is very different from -s theirs, which in reality does not even exist.
-Xtheirs
is a strategy option applied to recursive strategy. This means that recursive strategy will still merge anything it can, and will only fall back to "theirs
" logic in case of conflicts.
That debate for the pertinence or not of a theirs
merge strategy was brought back recently in this Sept. 2017 thread.
It acknowledges older (2008) threads
In short, the previous discussion can be summarized to "we don't want '-s theirs' as it encourages the wrong workflow".
It mentions the alias:
mtheirs = !sh -c 'git merge -s ours --no-commit $1 && git read-tree -m -u $1' -
Yaroslav Halchenko tries to advocate once more for that strategy, but Junio C. Hamano adds:
The reason why ours and theirs are not symmetric is because you are you and not them---the control and ownership of our history and their history is not symmetric. Once you decide that their history is the mainline, you'd rather want to treat your line of development as a side branch and make a merge in that direction, i.e. the first parent of the resulting merge is a commit on their history and the second parent is the last bad one of your history. So you would end up using "checkout their-history && merge -s ours your-history" to keep the first-parenthood sensible. And at that point, use of "-s ours" is no longer a workaround for lack of "-s theirs". It is a proper part of the desired semantics, i.e. from the point of view of the surviving canonical history line, you want to preserve what it did, nullifying what the other line of history did.
Junio adds, as commented by Mike Beaton:
git merge -s ours
git merge -s ours <their-ref>
effectively says 'mark commits made up to <their-ref> on their branch as commits to be permanently ignored'; and this matters because, if you subsequently merge from later states of their branch, their later changes will be brought in without the ignored changes ever being brought in.
To really properly do a merge which takes only input from the branch you are merging you can do
git merge --strategy=ours ref-to-be-merged
git diff --binary ref-to-be-merged | git apply --reverse --index
git commit --amend
There will be no conflicts in any scenario I know of, you don't have to make additional branches, and it acts like a normal merge commit.
This doesn't play nice with submodules however.
See Junio Hamano's widely cited answer: if you're going to discard committed content, just discard the commits, or at any rate keep it out of the main history. Why bother everyone in the future reading commit messages from commits that have nothing to offer?
But sometimes there are administrative requirements, or perhaps some other reason. For those situations where you really have to record commits that contribute nothing, you want:
(edit: wow, did I manage to get this wrong before. This one works.)
git update-ref HEAD $(
git commit-tree -m 'completely superseding with branchB content' \
-p HEAD -p branchB branchB:
)
git reset --hard
This one uses a git plumbing command read-tree, but makes for a shorter overall workflow.
git checkout <base-branch>
git merge --no-commit -s ours <their-branch>
git read-tree -u --reset <their-branch>
git commit
# Check your work!
git diff <their-branch>
The equivalent(which keep parent order) to 'git merge -s theirs branchB'
https://i.stack.imgur.com/6AFQ5.png
!!! Make sure you are in clean state !!!
Do the merge:
git commit-tree -m "take theirs" -p HEAD -p branchB 'branchB^{tree}'
git reset --hard 36daf519952 # is the output of the prev command
What we did ? We created a new commit which two parents ours and theirs and the contnet of the commit is branchB - theirs
https://i.stack.imgur.com/XlMV5.png
More precisely:
git commit-tree -m "take theirs" -p HEAD -p 'SOURCE^{commit}' 'SOURCE^{tree}'
I think what you actually want is:
git checkout -B mergeBranch branchB
git merge -s ours branchA
git checkout branchA
git merge mergeBranch
git branch -D mergeBranch
This seems clumsy, but it should work. The only think I really dislike about this solution is the git history will be confusing... But at least the history will be completely preserved and you won't need to do something special for deleted files.
This answer was given by Paul Pladijs. I just took his commands and made a git alias for convenience.
Edit your .gitconfig and add the following:
[alias]
mergetheirs = "!git merge -s ours \"$1\" && git branch temp_THEIRS && git reset --hard \"$1\" && git reset --soft temp_THEIRS && git commit --amend && git branch -D temp_THEIRS"
Then you can "git merge -s theirs A" by running:
git checkout B (optional, just making sure we're on branch B)
git mergetheirs A
This will merge your newBranch in existing baseBranch
git checkout <baseBranch> // this will checkout baseBranch
git merge -s ours <newBranch> // this will simple merge newBranch in baseBranch
git rm -rf . // this will remove all non references files from baseBranch (deleted in newBranch)
git checkout newBranch -- . //this will replace all conflicted files in baseBranch
git commit --amend
to the end of your example, or users may see the merge commit with git log
and assume the operation is complete.
I just recently needed to do this for two separate repositories that share a common history. I started with:
Org/repository1 master
Org/repository2 master
I wanted all the changes from repository2 master
to be applied to repository1 master
, accepting all changes that repository2 would make. In git's terms, this should be a strategy called -s theirs
BUT it does not exist. Be careful because -X theirs
is named like it would be what you want, but it is NOT the same (it even says so in the man page).
The way I solved this was to go to repository2
and make a new branch repo1-merge
. In that branch, I ran git pull git@gitlab.com:Org/repository1 -s ours
and it merges fine with no issues. I then push it to the remote.
Then I go back to repository1
and make a new branch repo2-merge
. In that branch, I run git pull git@gitlab.com:Org/repository2 repo1-merge
which will complete with issues.
Finally, you would either need to issue a merge request in repository1
to make it the new master, or just keep it as a branch.
A simple and intuitive (in my opinion) two-step way of doing it is
git checkout branchB .
git commit -m "Picked up the content from branchB"
followed by
git merge -s ours branchB
(which marks the two branches as merged)
The only disadvantage is that it does not remove files which have been deleted in branchB from your current branch. A simple diff between the two branches afterwards will show if there are any such files.
This approach also makes it clear from the revision log afterwards what was done - and what was intended.
Success story sharing
theirs
". -Xtheirs is the strategy option applied to recursive strategy. This means that recursive strategy will still merge anything it can, and will only fall back to "theirs" logic in case of conflicts. While this is what one needs in most cases like above, this is not the same as "just take everything from branch B as is". It does the real merge instead anyway.git merge -X ours
as opposed to the opposite ofgit merge -s ours
. This answer should reflect that the question is factually invalid.