是否有“他们的”版本的“ git merge -s ours”?


876

使用将主题分支“ B”合并到“ A”时git merge,出现了一些冲突。我知道可以使用“ B”中的版本解决所有冲突。

我知道git merge -s ours。但是我想要的是类似的东西git merge -s theirs

为什么不存在?与现有git命令发生冲突合并后,如何获得相同的结果?(git checkout来自B的每个未合并文件)

更新:只是从分支A中丢弃任何东西(合并提交点到树的B版本)的“解决方案”不是我想要的。


1
也看到这个答案:stackoverflow.com/questions/928646/... -这是微不足道的例子改为使用版本B的,而不是A.
罗宾Basak

8
请参阅SO Answer git命令,以使一个分支像另一分支一样,可以模拟所有当前可能的方式。git merge -s their
VonC

4
所以,你是不是真的找一个git merge -s theirs(可以很容易地实现git merge -s ours和临时党支部),因为我们-s完全忽略合并,从分支的变化...
尼科洛尼

21
@Torek-Git开发人员真的发现它除了提供了进攻性之外还具有攻击性吗?这是Git中的高级工程和设计问题之一:不一致。theirsours
jww

3
@jww这里的问题不在于“ git merge -s ours”令人反感,而在于它违反直觉。您可以从OP的问题中看到,如果要添加这样的功能,当他实际想要做的是“ git merge -s递归-X他们的”时,他会错误地使用它。通常要合并另一个分支,使其与另一个分支上的版本冲突,但是用另一个分支完全覆盖当前分支,而完全放弃当前更改是一个例外情况。
ThomazMoura '17

Answers:


1018

-X选项添加到theirs。例如:

git checkout branchA
git merge -X theirs branchB

一切都会以所需的方式合并。

我所见的唯一引起问题的原因是文件是否从branchB中删除。如果除git以外的其他操作删除,它们将显示为冲突。

修复很容易。只需git rm使用已删除的任何文件的名称运行:

git rm {DELETED-FILE-NAME}

之后,-X theirs应该可以按预期工作。

当然,使用该git rm命令进行实际删除将首先避免发生冲突。


注意:还存在一个较长的表格选项。

要使用它,请替换:

-X theirs

与:

--strategy-option=theirs

6
请参阅下面的其他答案,以找到对原始问题的更好解决方案(Paul Pladijs)。
马尔科姆

204
值得注意的是,这与“合并策略theirs”不同。-Xtheirs是应用于递归策略的策略选项。这意味着递归策略仍将合并所有可能的内容,并且仅在发生冲突时才退回到“其”逻辑中。尽管这是在大多数情况下(如上所述)所需要的,但这与“仅从分支B中获取所有内容”不同。无论如何,它执行真正的合并。
帖木儿(Timur)

2
它也可以与其他命令一起使用。我只是做了一个'git cherry-pick sha1 -X他们的'。谢谢!
Max Hohenegger 2014年

48
这是一个糟糕的答案,因为它看起来正确,但实际上是错误的。“混帐合并-X他们的” 该做什么“混帐合并-s他们”会做。它不会用合并分支的内容替换当前分支。它更喜欢“他们的”更改,但仅在发生冲突的情况下。因此,产生的提交可能与“他们的”分支完全不同。这不是问题发布者所想到的。
伊万·克里瓦雅科夫

7
@ user3338098:是的,您是对的。我重新阅读了这个问题,它也不是很好。不幸的是,造成混乱的根本原因不是答案,甚至不是问题。git作者的设计选择是为合并策略和合并策略“递归”选择相同的名称“我们的”。实际上,这种情况下的混乱是不可避免的。
伊万·克里瓦雅科夫

218

将BranchB合并到我们已签出的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

要使其自动化,您可以使用branchA和branchB作为参数将其包装到脚本中。

就像您期望的那样,此解决方案保留了合并提交的第一和第二父级git merge -s theirs branchB


问:为什么需要branchTEMP?你不能git reset --soft branchA吗?
cdunn2001 2012年

4
@ cdunn2001:我以某种方式想了同样的事情,但没有。请注意,git reset --hard提交branchA指向的更改。
伊藤刚(Tsuyoshi Ito)

5
很抱歉提出一个愚蠢的问题,但是为什么-xtheirs这种方法更好?优点/缺点是什么
chrispepper1989

9
@ chrispepper1989 -x它们的冲突只会影响冲突,如果可以干净地应用我们的一大块冲突,它将进入最终的合并。在这种情况下,如最后一条命令所示,无论是否存在冲突,我们都将获得 branchB 完全相同的合并。
UncleZeiv

2
@UncleZeiv感谢您的解释,因此基本上执行“其”操作将保留无冲突的更改和文件,从而导致生成的int代码与拉出的代码不同。
chrispepper1989

89

较早版本的git允许您使用“其”合并策略:

git pull --strategy=theirs remote_branch

但这已被删除,正如Junio Hamano(Git维护者)在此消息中所解释的。如链接中所述,您可以执行以下操作:

git fetch origin
git reset --hard origin

但是请注意,这与实际合并不同。您的解决方案可能是您真正想要的选择。


7
我真的完全不理解滨野润男的解释。git reset --hard origin他们的风格合并的解决方案如何?如果我想将BranchB合并到BranchA中(就像在Alan W. Smith的答案中一样),我将如何使用该reset方法呢?
詹姆斯·麦克马洪

3
@詹姆士·麦克马洪(James McMahon):Junio C Hamano的观点不是git reset --hard“他们的”式合并。当然,git reset --hard不会为此创建任何合并提交或任何提交。他的观点是,我们不应使用合并将HEAD中的任何内容替换为其他内容。不过,我不一定同意。
伊藤刚(Tsuyoshi Ito)

11
可惜theirs因为理由不完整而被删除。它没有考虑到代码良好的情况,只是上游是在不同的哲学基础上维护的,因此从某种意义上说是“不好的”,因此人们都希望两者都与上游保持最新,但是同时保留一个好的代码“修复程序” [git&msysgit由于目标平台的理念不同而有一些“冲突”]
Philip Oakley

1
它还缺少我现在坚持使用的用例,在这里我与其他人共享一个分支,而我们俩都碰巧同时推送更改(在不同文件上)。所以我想破坏我在本地拥有的所有旧版本,而改用他的较新版本。他想对我更新的文件执行相同的操作。没有什么可以“重置”的。当每个文件约为20个文件时,检出每个文件的解决方案不切实际。
szeitlin

2
我真的不懂这种哲学。我theirs之所以使用,是因为我不小心在两个独立的分支中更改了一些文件,并且想要放弃对一个文件的更改而不必手动对每个文件进行更改。
sudo

65

目前尚不清楚您期望的结果是什么,因此在答案和评论中对“正确”的实现方式有些困惑。我尝试给出一个概述,并查看以下三个选项:

尝试合并并使用B解决冲突

不是 “他们的版本git merge -s ours”,而是“他们的版本git merge -X ours”(是的缩写git merge -s recursive -X ours):

git checkout branchA
# also uses -s recursive implicitly
git merge -X theirs branchB

这就是艾伦·史密斯(Alan W. Smith)的答案

仅使用B中的内容

这会为两个分支创建一个合并提交,但会丢弃的所有更改,branchA而只会保留的内容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

请注意,合并现在提交的第一个父对象是from branchB,只有第二个提交是from branchA。例如,Gandalf458的答案就是这样做的。

仅使用B的内容并保持正确的父顺序

这是真正的“他们的版本git merge -s ours”。它的内容与之前的选项相同(即仅来自from branchB),但是父级的顺序是正确的,即,第一个父级来自,branchA第二个父级来自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

这就是Paul Pladijs的回答(不需要临时分支)。


选项3的较干净版本:git checkout branchA; git merge -s ours --no-commit branchB; git read-tree -um @ branchB; git commit
jthill '16

@jthill:虽然您的版本更简洁,但它也使用了低级(“管道”)命令read-tree,该命令可能不习惯一般git用户(至少我不是;-))。答案中的解决方案仅使用大多数git用户应该知道的高级(“瓷器”)命令。我更喜欢它们,但是仍然感谢您的版本!
siegi '16

您能解释倒数第二个步骤(结帐branchA)吗?似乎它不应该在那里。
帕特里克

@Patrick,这是必需的步骤。如果您跳过它,将获得相同的提交,但没有分支指向该提交。因此,一旦切换到另一个提交/分支,您对最后一个命令(…commit --amend…)所做的提交就会“丢失”。我打算在有时间的情况下尽快在此答案中添加一些图形解释…;-)
siegi

62

从现在开始,我一直使用Paul Pladijs的答案。我发现,您可以进行“常规”合并,发生冲突,因此您可以

git checkout --theirs <file>

通过使用另一个分支的修订来解决冲突。如果对每个文件执行此操作,则将具有与预期相同的行为

git merge <branch> -s theirs

无论如何,这比合并策略要付出更多的努力!(这已在git版本1.8.0中进行了测试)


1
如果可以检索文件列表,那就太好了。例如,git ls-files --modified | xargs git add我想这样做是为了在两边添加合并:/
andho 2012年

实际上git会返回双方添加的文件,git ls-files --modified因此我猜这也是可行的解决方案。
andho 2012年

1
也感谢您添加此内容。通常,在逐个文件的基础上不需要它。同样,git status在最底部显示“ Unmerged Paths”。要获取未解析路径的列表,我使用以下别名:git config --global alias.unresolved '!git status --short|egrep "^([DAU])\1"'
Melvyn 2015年

是什么意思-X,什么是之间的区别-X-s?找不到任何文档。
杨磊


13

当使用git merge在“ A”中合并主题分支“ B”时,出现一些冲突。我知道所有冲突都可以使用“ B”中的版本解决。

我知道git merge -s我们的。但是我想要的是git merge> -s他们的东西。

我假设您创建了master分支,现在想合并回master,覆盖master中的所有旧内容。这正是我遇到这篇文章时想要做的。

确切地执行您要执行的操作,除了首先将一个分支合并到另一个分支中。我就是这样做的,效果很好。

git checkout Branch
git merge master -s ours

然后,结帐master并合并其中的分支(现在可以顺利进行):

git checkout master
git merge Branch

1
谢谢。到目前为止,这应该是最简单,最正确的答案。如果您的分支落后于master或与master发生冲突,则将其分支工作合并并使一切正常,然后再将所有内容合并回master。
StackHola

这很棒,谢谢。
Kodie Grantham

12

如果您在分支机构A上,请执行以下操作:

git merge -s recursive -X theirs B

在git版本1.7.8上测试


8

要真正做到正确的合并这需要从分支要合并,你可以做输入

git merge --strategy=ours ref-to-be-merged

git diff --binary ref-to-be-merged | git apply --reverse --index

git commit --amend

据我所知,在任何情况下都不会发生冲突,您不必进行其他分支,并且它的行为就像普通的合并提交一样。

但是,这在子模块中效果不佳。


-R在这里做什么?
P. Myer Nore

这样,您将丢失文件“移动和更改”的历史记录。我对吗?
elysch '16

1
-R是--reverse,我将答案更新为更多自我说明。
伊利亚·林恩

如果您要合并的文件在传入分支中被删除,这似乎不起作用。
2017年

7

为什么不存在?

虽然我在“ git命令使一个分支像另一个分支 ”中提到了如何模拟git merge -s theirs,但请注意,Git 2.15(2017年第四季度)现在更加清晰:

-X<option>误写了“ ”用于合并的文档以表明-s theirs存在“ ”,事实并非如此。

参见Junio C Hamano()提交的commit c25d98b(25 Sep 2017 (通过合并JUNIOÇ滨野- -提交4da3e23,2017年9月28日)gitster
gitster

合并策略:避免暗示“ -s theirs”存在

-Xours合并选项的描述带有括号,告诉读者它与完全不同-s ours,这是正确的,但-Xtheirs随后的描述却粗心地说“这与ours” 相反,给人以错误的印象,认为读者也需要请注意,它与完全不同-s theirs,实际上甚至不存在。

-Xtheirs是应用于递归策略的策略选项。这意味着递归策略仍将合并所有可能的内容,并且仅theirs在发生冲突时才退回到逻辑上。

有关theirs合并策略是否相关的争论最近在2017年9月的这个话题中引起了人们的注意。
它承认较旧的(2008)线程

简而言之,前面的讨论可以总结为“我们不希望' -s theirs',因为它会鼓励错误的工作流程”。

它提到了别名:

mtheirs = !sh -c 'git merge -s ours --no-commit $1 && git read-tree -m -u $1' -

雅罗斯拉夫·哈尔琴科(Yaroslav Halchenko)尝试再次倡导该战略,但Junio C. Hamano补充说

我们和他们之所以不对称的原因是因为你是你而不是他们-我们历史及其历史的控制和所有权不是对称的。

一旦确定他们的历史是主线,您就希望将您的开发线视为分支,并在该方向上进行合并,即,产生的合并的第一个父项是对他们的历史的提交,第二个父母是您历史上的最后一个坏人。因此,您最终将使用“ checkout their-history && merge -s ours your-history”来保持初婚年龄。

在这一点上,使用“ -s ours”不再是缺少“ -s theirs” 的解决方法。
它是所需语义的适当部分,即,从尚存的规范历史路线的角度来看,您想要保留它所做的事情,而使其他历史路线所做的事情无效

Junio补充说,正如Mike Beaton所说:

git merge -s ours <their-ref>有效地说:“标记提交<their-ref>在其分支上构成为永久忽略的提交”;
这很重要,因为如果您随后从其分支的较后状态进行合并,则将引入其较晚的更改,而不会引入任何被忽略的更改


1
这是一个有用的答案,但它没有提到我刚刚从Junio C. Hamano的答案中了解的一个关键点:git merge -s ours <their-ref>有效地说“标记提交由其分支上的<their-ref>组成,因为这些提交将被永久忽略” '; 这很重要,因为如果您随后从其分支的较后状态进行合并,则将引入其较晚的更改,而不会引入任何被忽略的更改
。– MikeBeaton

1
@MikeBeaton谢谢。我已将您的评论包含在答案中,以提高知名度。
VonC

5

请参见Junio Hamano广为引用的答案:如果您要丢弃已提交的内容,请丢弃已提交的内容,或者至少将其排除在主要历史记录之外。为什么将来要打扰每个人都从没有提供内容的提交中读取提交消息?

但是有时会有管理要求,或者可能还有其他原因。对于那些您确实必须记录毫无贡献的提交的情况,您需要:

(编辑:哇,我以前设法弄错了吗。这个有效。)

git update-ref HEAD $(
        git commit-tree -m 'completely superseding with branchB content' \
                        -p HEAD -p branchB    branchB:
)
git reset --hard

3

这使用了一个git plumbing命令read-tree,但是使整个工作流程缩短了。

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>

0

这会将您的newBranch合并到现有的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在示例末尾添加,否则用户可能会看到合并提交git log并假定操作已完成。
Michael R

0

我认为您真正想要的是:

git checkout -B mergeBranch branchB
git merge -s ours branchA
git checkout branchA
git merge mergeBranch
git branch -D mergeBranch

这看起来很笨拙,但应该可以。我真正不喜欢此解决方案的唯一想法是git历史记录会令人困惑……但是至少历史记录将被完全保留,并且您不需要为已删除的文件做一些特殊的事情。


0

等同于(保持父顺序)'git merge -s他们的branchB'

合并之前:在此处输入图片说明

!!! 确保您处于清洁状态!

进行合并:

git commit-tree -m "take theirs" -p HEAD -p branchB 'branchB^{tree}'
git reset --hard 36daf519952 # is the output of the prev command

我们做了什么 ?我们创建了一个新的提交,其中有我们的两个父母和他们的父母,该提交的内容是branchB-他们的父母

合并后:在此处输入图片说明

更确切地说:

git commit-tree -m "take theirs" -p HEAD -p 'SOURCE^{commit}' 'SOURCE^{tree}'

0

保罗·普拉迪斯(Paul Pladijs)给出了这个答案。为了方便起见,我只是接受了他的命令并制作了一个git别名。

编辑您的.gitconfig并添加以下内容:

[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"

然后,您可以通过运行以下命令“ git merge -s thems A”:

git checkout B (optional, just making sure we're on branch B)
git mergetheirs A

-1

我最近刚刚需要对两个共享相同历史记录的独立存储库执行此操作。我开始于:

  • Org/repository1 master
  • Org/repository2 master

我希望将所有更改repository2 master都应用于repository1 master,接受repository2所做的所有更改。用git的术语来说,这应该是一种称为-s theirsBUT 的策略,但它并不存在。请小心,因为-X theirs它的名称就像您想要的一样,但是名称并不相同(甚至在手册页中也是如此)。

我解决这个问题的方法是去建立repository2一个新的分支repo1-merge。在那个分支中,我运行了git pull git@gitlab.com:Org/repository1 -s ours,并且合并得很好,没有任何问题。然后,我将其推到遥控器上。

然后我回到repository1并建立一个新分支repo2-merge。在该分支中,我git pull git@gitlab.com:Org/repository2 repo1-merge将运行所有问题。

最后,您可能需要发出合并请求repository1以使其成为新的主服务器,或者只是将其保留为分支。


-2

一种简单直观的方法(在我看来)是两步

git checkout branchB .
git commit -m "Picked up the content from branchB"

其次是

git merge -s ours branchB

(将两个分支标记为合并)

唯一的缺点是它不会从当前分支中删除在branchB中已删除的文件。之后,两个分支之间的简单差异将显示是否存在任何此类文件。

此方法还可以从修订日志中清楚地知道随后进行的操作和预期的操作。


这对于原始问题可能是正确的,但是OP在2008年以一种错误的方式更新了该问题。
user3338098

1
@ user3338098是的,太可惜了,他们留下了令人误解的标题,只是在问题正文末尾拍了一个不太明显的更新……。因为此答案确实正确回答了标题问题。
RomainValeri

我完全同意@RomainValeri
AppleCiderGuy,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.