有“他们的”版本的“git合并-s our”吗?

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

我知道git merge -s ours。但我想要的是像git merge -s theirs这样的东西。

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

丢弃分支A(合并提交点到树的B版本)中的任何内容的“解决方案”不是我想要的。

625620 次浏览

我解决了我的问题

git checkout -m oldgit checkout -b new Bgit merge -s ours old

旧版本的git允许您使用“他们的”合并策略:

git pull --strategy=theirs remote_branch

但正如JunioHamano(Git维护者)在这条消息中解释的那样,这已被删除。如链接中所述,相反,您可以这样做:

git fetch origingit reset --hard origin

不过,请注意,这与实际的合并不同。您的解决方案可能是您真正寻找的选项。

类似的选择是--strategy-option(简写形式-X)选项,它接受theirs。例如:

git checkout branchAgit merge -X theirs branchB

然而,这更等同于-X ours而不是-s ours。关键的区别在于-X执行常规递归合并,使用选择的一方解决任何冲突,而-s ours将合并更改为完全忽略另一方。

在某些情况下,使用-X theirs而不是假设的-s theirs的主要问题是删除的文件。在这种情况下,只需使用已删除文件的名称运行git rm

git rm {DELETED-FILE-NAME}

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

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

将分支B合并到我们的签出分支A的可能且经过测试的解决方案:

# in case branchA is not our current branchgit checkout branchA
# make merge commit but without conflicts!!# the contents of 'ours' will be discarded latergit merge -s ours branchB
# make temporary branch to merged commitgit branch branchTEMP
# get contents of working tree and index to the one of branchBgit reset --hard branchB
# reset to our merged commit but# keep contents of working tree and indexgit reset --soft branchTEMP
# change the contents of the merged commit# with the contents of branchBgit commit --amend
# get rid off our temporary branchgit branch -D branchTEMP
# verify that the merge commit contains only contents of branchBgit diff HEAD branchB

要使其自动化,您可以将其包装到一个脚本中,使用分支A和分支B作为参数。

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

如果您在分支A上,请执行:

git merge -s recursive -X theirs B

在git版本1.7.8上测试

从那时起,我用了保罗·普拉迪斯的答案。我发现,你可以做一个“正常”的合并,冲突发生了,所以你做了

git checkout --theirs <file>

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

git merge <branch> -s theirs

无论如何,这比使用合并策略要多得多!(这在git版本1.8.0中进行了测试)

这将合并你的new分支在现有的base分支

git checkout <baseBranch> // this will checkout baseBranchgit merge -s ours <newBranch> // this will simple merge newBranch in baseBranchgit 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

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

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

(编辑:哇,我以前是不是弄错了。这个有用。)

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

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

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

我假设你从master创建了一个分支,现在想合并回master,覆盖master中的任何旧东西。这正是我看到这篇文章时想做的。

做你想做的事,除了先把一个分支合并到另一个分支。我刚刚做了这个,效果很好。

git checkout Branchgit merge master -s ours

然后,签出master并将您的分支合并到其中(现在会顺利进行):

git checkout mastergit merge Branch

要真正正确地进行合并,从您正在合并的分支中获取只有输入,您可以这样做

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

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

git commit --amend

在我所知道的任何情况下都不会有冲突,你不必创建额外的分支,它的行为就像一个正常的合并提交。

然而,这对子模块来说并不好。

你想要的结果是什么并不完全清楚,所以在答案和他们的评论中,对“正确”的方式存在一些混淆。我试图给出一个概述,并看到以下三个选项:

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

这是没有,“他们的git merge -s ours版本”,但“他们的git merge -X ours版本”(这是git merge -s recursive -X ours的缩写):

git checkout branchA# also uses -s recursive implicitlygit 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

请注意,合并提交的第一个父级现在来自branchB,只有第二个来自branchA。这就是例如第458章答案所做的。

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

这是真正的“他们对git merge -s ours的版本”。它具有与之前选项中相同的内容(即仅来自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

这就是保罗·普拉迪斯的回答所做的(不需要临时分支)。

特殊情况

如果branchB的提交是branchA的祖先,则git merge不起作用(它只是以“已经更新了。”之类的消息退出)。

在这个或其他类似/高级的情况下,可以使用低级命令#0

这个使用git管道命令读取树,但使整体工作流程更短。

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>

我认为你真正想要的是:

git checkout -B mergeBranch branchBgit merge -s ours branchAgit checkout branchAgit merge mergeBranchgit branch -D mergeBranch

这看起来很笨拙,但它应该可以工作。我唯一不喜欢这个解决方案的是git历史记录会令人困惑…但至少历史记录将被完全保留,你不需要为删除的文件做一些特别的事情。

我最近需要为两个共享共同历史的独立存储库执行此操作。我从:

  • Org/repository1 master
  • Org/repository2 master

我希望将repository2 master的所有更改应用于repository1 master,接受repository2将进行的所有更改。用git的术语来说,这个应该是一个名为-s theirs的策略,但它不存在。要小心,因为-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中发出合并请求以使其成为新的主服务器,或者仅将其保留为分支。

为什么它不存在?

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

为合并留档-X<option>具有误导性写入暗示“-s theirs”存在,事实并非如此。

提交c25d98b(2017年9月25日)byJunio C Hamano(#0)
(合并于提交4da3e23中的Junio C Hamano----#0----,2017年9月28日)

合并策略:避免暗示“0”存在

-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补充道,正如mikebeaton所评论的:

git merge -s ours <their-ref>有效地表示“将分支上最多<their-ref>的提交标记为永久忽略的提交”;
这很重要,因为如果您随后从其分支的后续状态合并,则将引入它们的后续更改,而不会引入被忽略的更改。

等价(保持父顺序)到'git合并-s他们的分支B'

合并前:输入图片描述

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

执行合并:

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

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

合并后:输入图片描述

更确切地说:

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

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

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

其次是

git merge -s ours branchB

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

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

这种方法还可以从之后的修订日志中清楚地看出完成了什么-以及意图是什么。

这个答案是由保罗·普拉迪斯给出的。我只是接受了他的命令,并为方便起见做了一个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合并-s他们的A”:

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

重新审视这个老问题,因为我刚刚找到了一个解决方案简短而且-因为它只使用瓷器命令-易于理解。为了清楚起见,我想回答标题中提出的问题问题(实现git merge -s theirs),而不是问题主体。在换句话说,我想创建一个合并提交,其树与它的第二个父树:

# Start from the branch that is going to receive the merge.git switch our_branch
# Create the merge commit, albeit with the wrong tree.git merge -s ours their_branch
# Replace our working tree and our index with their tree.git restore --source=their_branch --worktree --staged :/
# Put their tree in the merge commit.git commit --amend

警告git restore是一个相当新的命令,在git中引入2.23.git help restore警告

这个命令是实验性的。行为可能会改变。

我确实使用多个版本的git(2.25.1、2.30.2、2.31.1,2.34.1和2.35.1),它按预期工作。

这并不一定回答了原始海报的问题,但我到达这里时的情况是,我已经尝试合并但最终遇到了冲突。通常我在IDE中管理冲突,但是当我没有访问权限时,可以使用以下REGEX来查找并替换与“他们的”内容的差异:

回复<<<<<<< HEAD\n[^•>]+\n=======\n([^•>]+)>>>>>>> .+\n\1

(以防其他人因为和我一样的原因登陆这个页面)。