ChatGPT解决这个技术问题 Extra ChatGPT

Is there a "theirs" version of "git merge -s ours"?

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.

See SO answer git command for making one branch like another for all the current possible ways to simulate git merge -s their.
So you are not really looking for a 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...
@Torek - do the Git devs really find it that offensive to provide theirs in addition to ours??? This is a symptom of one of the high level engineering and design problems in Git: inconsistency.
@jww The problem here is not about "git merge -s ours" being offensive, but about it being counter-intuitive. You can see from the OP's question that if such a feature would be added he would use it by mistake when what he actually wants to do is a "git merge -s recursive -X theirs". It's common to want to merge another branch overriding conflicts with the version on the other branch, but completely overwriting the current branch with another branch completely discarding the current one's changes is really an exception case.
Everyone should check the OP's "update" carefully as the question has nothing to do with git merge -s ours.

u
uhbif19

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.


See other answer below for a better solution (Paul Pladijs) to the original question.
Worth to note that this is not the same as "merge strategy 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.
This is a terrible answer, because it looks correct, but is actually wrong. "git merge -X theirs" does not do what "git merge -s theirs" would do. It does not replace current branch with the content of the merged branch. It prefers "theirs" changes, but only in case of conflict. Therefore, the resulting commit may be quite different from "theirs" branch. This is not what the question poster had in mind.
@IvanKrivyakov it's only a terrible answer, because the question is so terrible, the OP wants the opposite of git merge -X ours as opposed to the opposite of git merge -s ours. This answer should reflect that the question is factually invalid.
@user3338098: yes, you're right. I re-read the question, and it is is also not so good. Unfortunately, the root cause of the confusion is not the answer and not even the question. It is the design choice of git authors to give the same name 'ours' to a merge strategy and to an option of merge strategy 'recursive'. The confusion in this case is practically inevitable.
P
Paul Pladijs

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.


Q: Why do you need branchTEMP? Couldn't you just git reset --soft branchA?
@cdunn2001: Somehow I thought the same thing, but no. Note that git reset --hard changes which commit branchA points to.
sorry to ask a stupid question but why is this method better the -xtheirs? what are the advantages/disadvantages
@chrispepper1989 -x theirs only affects conflicts, if a hunk of ours can be applied cleanly it will get through to the resulting merge. In this case, as shown by the last command, we are getting a merge that is perfectly identical to branchB, regardless of whether there would have been conflicts or not.
@UncleZeiv thank you for the explanation, so basically doing a "theirs" would keep un-conflicted changes and files resulting int code that is not identical to the pulled code.
J
Jeff Puckett

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.


I really don't understand Junio Hamano's explanation at all. How is 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?
@James McMahon: Junio C Hamano’s point is not 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.
It is a shame that the 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]
It's also missing the use case I'm stuck with right now, where I'm sharing a branch with someone else and we both happened to push up changes (on different files) at the same time. So I want to clobber all the old versions I have locally and use his newer ones instead. He wants to do the same for the files I updated. There's nothing to 'reset'. And the solution to checkout each individual file is not practical when it's ~20 files.
I really don't understand the philosophy. I just used 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.
s
siegi

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.


A cleaner version of option 3: git checkout branchA; git merge -s ours --no-commit branchB; git read-tree -um @ branchB; git commit
I think the last option should not use words "correct parent order" because logically this option has incorrect parent order even though it does match the order asked in the question. The reason this is logically incorrect is that merging is usually done into some branch and if you merge code that's ignored, it's logically merged into current branch and as such, -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.
Also, this should be the accepted answer because it correctly explains that the're multiple interpretations for that seemingly simple question.
@MikkoRantalainen, my thoughts: Merging 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?
And git commit-tree is the only way you can do this is if "theirs" is an older version of the same branch, assuming you want that an actual merge commit. See tne 2nd graph in devblogs.microsoft.com/oldnewthing/20200928-00/?p=104302
m
musicmatze

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)


would be great if the list of files could be retrieved. For example, git ls-files --modified | xargs git add I wanted to do this for added on both sides merge :/
Actually git returns the added on both sides files with git ls-files --modified so i guess this is also a viable solution.
Thanks for adding this too. More often then not it's needed on a file-by-file base. Also, git status shows "Unmerged Paths" at the very bottom. To get the list of unresolved paths, I use this alias: git config --global alias.unresolved '!git status --short|egrep "^([DAU])\1"'
what's the meaning of -X and what's the difference between -X and -s? cannot find any document.
C
Community

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

Thanks. This should be the accepted answer, by far the simplest and the most correct. If your branch is behind/conflicts with master, then its the branch job to merge and make everything work before merging back everything to master.
e
elmarco

I solved my problem using

git checkout -m old
git checkout -b new B
git merge -s ours old

a branch "B" from "old" branch
question about merging from B to old with solving conflicts using changes from branch b ove old, your answer is working telling opposite way.
r
rafalmag

If you are on branch A do:

git merge -s recursive -X theirs B

Tested on git version 1.7.8


this is logical, -s and -X together
V
VonC

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 effectively says 'mark commits made up to 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.


This is a useful answer, but it doesn't mention one key point that I've just understood from Junio C. Hamano's answer: 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.
@MikeBeaton Thank you. I have included your comment in the answer for more visibility.
E
Elijah Lynn

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.


What does the -R do here?
This way you lose the history of files "moved and changed". Am I right?
The -R is --reverse, I updated the answer with that to be more self-documenting.
This doesn't seem to work if the files you are trying to merge are deleted in the incoming branch.
j
jthill

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 the Raymond-Chen-favored method of getting Paul Pladijs' result. Not surprisingly, few people here get it.
M
Michael R

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>

B
Boaz Nahum

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}'

Same as jthill's answer, methinks, but with a bit more visual explantion.
b
briemers

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.


B
Brain2000

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

P
Pawan Maheshwari

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

I'd suggest adding 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.
T
Trann

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.


r
rubund

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.


this may be correct for the original question, however the OP updated the question in 2008 in such a way that this answer is incorrect.
@user3338098 Yeah, such a pity that they left the misleading title and just slapped a not so visible update at the end of the question body.... because this answers does answer correctly the title question.
I completely agree with @RomainValeri