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

Posted

技术标签:

【中文标题】是否有“git merge -s ours”的“他们的”版本?【英文标题】:Is there a "theirs" version of "git merge -s ours"? 【发布时间】:2010-09-15 11:49:55 【问题描述】:

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

我知道git merge -s ours。但我想要的是git merge -s theirs

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

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

【问题讨论】:

查看 SO 回答 git command for making one branch like another 了解所有当前模拟git merge -s their的可能方法。 所以你并不是真的在寻找git merge -s theirs(可以通过git merge -s ours 和一个临时分支轻松实现),因为-s ours 完全忽略了merge-from 分支的变化。 .. @Torek - 让 Git 开发人员真的觉得除了 @ 之外还提供 theirs 冒犯987654331@???这是 Git 中高级工程和设计问题之一的症状:不一致。 @jww 这里的问题不是“git merge -s ours”令人反感,而是它违反直觉。您可以从 OP 的问题中看到,如果要添加这样的功能,当他真正想要做的是“git merge -s recursive -X theirs”时,他会错误地使用它。想要合并另一个分支覆盖与另一个分支上的版本冲突是很常见的,但是用另一个完全丢弃当前更改的分支完全覆盖当前分支确实是一个例外情况。 每个人都应该仔细检查 OP 的“更新”,因为这个问题与git merge -s ours 无关。 【参考方案1】:

尚不完全清楚您想要的结果是什么,因此在答案及其 cmets 中对“正确”的执行方式存在一些混淆。我尝试给出一个概述,并查看以下三个选项:

尝试合并并使用 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's answer 会。

仅使用 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。这就是例如Gandalf458's answer 会。

仅使用 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

这就是 Paul Pladijs's answer 所做的(不需要临时分支)。

特殊情况

如果branchB 的提交是branchA 的祖先,git merge 不起作用(它只是退出并显示“已经是最新的。”这样的消息)。 p>

在这种或其他类似/高级情况下,可以使用低级命令git commit-tree

【讨论】:

选项 3 的更简洁版本:git checkout branchA; git merge -s ours --no-commit branchB; git read-tree -um @ branchB; git commit 我认为最后一个选项不应使用“正确的父顺序”一词,因为从逻辑上讲,该选项的父顺序不正确,即使它确实与问题中提出的顺序相匹配。这是逻辑上不正确的原因是合并通常是在某个分支中完成的,如果您合并被忽略的代码,它会在逻辑上合并到当前分支中,因此-s ours应始终改为使用。 -s ours 旨在将某些分支保存为已弃用,并且该分支的所有“用户”都应快速前进到另一个分支。将父母置于“正确”(原文如此)的顺序将是相反的。 另外,这应该是公认的答案,因为它正确地解释了这个看似简单的问题有多种解释。 @MikkoRantalainen,我的想法:将branchB 合并到branchA 应该始终导致branchA 成为第一个父项,branchB 成为第二个父项。选择的策略应该对此没有影响。所以模仿(不存在的)策略-s theirsbranchB 合并到branchA 应该导致branchA 也是第一个父级。我同意它可能经常没有用(这就是它不存在的原因),但如果你真的想做-s theirs,正确的父顺序是内容等效-s ours 的逆定义,在我的观点。为什么还需要它? 而 git commit-tree 是你可以做到这一点的唯一方法是如果“他们的”是同一分支的旧版本,假设你想要一个实际的合并提交。请参阅devblogs.microsoft.com/oldnewthing/20200928-00/?p=104302中的第二张图【参考方案2】:

类似的替代方案是--strategy-option(短格式-X)选项,它接受theirs。例如:

git checkout branchA
git 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 命令进行实际删除将首先防止冲突发生。

【讨论】:

查看下面的其他答案以获得对原始问题的更好解决方案(Paul Pladijs)。 值得注意的是,这与“合并策略theirs”不同。 -Xtheirs 是应用于递归策略的策略选项。这意味着递归策略仍然会合并任何它可以合并的东西,并且只会在发生冲突时回退到“他们的”逻辑。虽然这是在大多数情况下需要的,就像上面一样,但这与“按原样从分支 B 获取所有内容”不同。无论如何,它会进行真正的合并。 这是一个糟糕的答案,因为它看起来正确,但实际上是错误的。 “git merge -X theirs”做“git merge -s theirs”会做的事情。它不会用合并分支的内容替换当前分支。它更喜欢“他们的”更改,但仅在发生冲突的情况下。因此,生成的提交可能与“他们的”分支完全不同。这不是问题发布者的想法。 @IvanKrivyakov 这只是一个糟糕的答案,因为这个问题太糟糕了,OP 想要 git merge -X ours 的反面,而不是 git merge -s ours 的反面。这个答案应该反映 question 实际上是invalid @user3338098:是的,你是对的。我重新阅读了这个问题,它也不是那么好。不幸的是,混淆的根本原因不是答案,甚至不是问题。 git 作者的设计选择是为合并策略和合并策略“递归”选项赋予相同的名称“我们的”。这种情况下的混淆实际上是不可避免的。【参考方案3】:

为什么不存在?

虽然我在“git command for making one branch like another”中提到了如何模拟git merge -s theirs,但请注意 Git 2.15(2017 年第四季度)现在更加清晰:

用于合并的“-X<option>”文档具有误导性 写来暗示“-s theirs”存在,事实并非如此。

参见Junio C Hamano (gitster) 的commit c25d98b(2017 年 9 月 25 日)。(由 Junio C Hamano -- gitster -- 合并于 commit 4da3e23,2017 年 9 月 28 日)

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

-Xours合并选项的描述有括号 这告诉读者它与-s ours 非常不同, 这是正确的,但它后面的-Xtheirs 的描述 漫不经心地说“这与ours相反”,给了一个假 印象是还需要警告读者,这是非常 不同于-s theirs,实际上根本不存在。

-Xtheirs 是一个应用于递归策略的策略选项。这意味着递归策略仍然会合并任何它可以合并的东西,并且只会在发生冲突时回退到“theirs”逻辑。

关于 theirs 合并策略是否相关的争论最近又回到了 in this Sept. 2017 thread。 它承认older (2008) threads

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

它提到了别名:

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

Yaroslav Halchenko 试图再次倡导该策略,but Junio C. Hamano adds:

我们的和他们的之所以不对称,是因为你是你而不是他们——我们的历史和他们的历史的控制权和所有权是不对称的。

一旦您决定他们的历史是主线,您宁愿将您的开发线视为一个分支并朝该方向进行合并,即结果合并的第一个父项是对他们历史的提交第二个父母是你历史上最后一个坏人。所以你最终会使用“checkout their-history && merge -s ours your-history”来 保持第一父母的理智。

到那时,使用“-s ours”不再是缺少“-s theirs”的解决方法。它是所需语义的适当部分,即从点从幸存的规范历史线的角度来看,您想保留它所做的,而取消另一条历史线的所作所为

正如Mike Beaton 评论的那样,Junio 补充道:

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

【讨论】:

这是一个有用的答案,但它没有提到我刚刚从 Junio C. Hamano 的回答中理解的一个关键点:git merge -s ours <their-ref> 有效地表示'标记提交到 在他们的分支上作为提交被永久忽略';这很重要,因为如果您随后从其分支的后续状态合并,则将引入他们以后的更改,而不会引入忽略的更改。 @MikeBeaton 谢谢。我已将您的评论包含在答案中以提高知名度。【参考方案4】:

此答案由 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 theirs A”:

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

【讨论】:

【参考方案5】:

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

git pull --strategy=theirs remote_branch

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

git fetch origin
git reset --hard origin

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

【讨论】:

我真的完全不明白滨野朱尼奥的解释。 git reset --hard origin 如何解决他们的风格合并?如果我想将 BranchB 合并到 BranchA 中(如 Alan W. Smith 的回答),我将如何使用 reset 方法? @James McMahon:Junio C Hamano 的观点不是 git reset --hard 进行“他们的”式合并。当然,git reset --hard 不会创建任何合并提交或任何提交。他的观点是我们不应该使用合并来用其他东西替换 HEAD 中的任何内容。不过,我不一定同意。 很遗憾 theirs 被删除,因为理由不完整。它没有考虑到那些代码很好的情况,只是上游是在不同的哲学基础上维护的,所以从这个意义上说是“坏的”,所以一个人确实希望与上游保持同步,但是同时保留好的代码“修复”[git 和 msysgit 由于目标平台的理念不同而存在一些“冲突”] 它也缺少我现在坚持的用例,我与其他人共享一个分支,我们都碰巧同时推送更改(在不同的文件上)。所以我想破坏我在本地拥有的所有旧版本,并改用他的新版本。他想对我更新的文件做同样的事情。没有什么可以“重置”的。签出每个单独的文件的解决方案在大约 20 个文件时是不实用的。 我真的不懂哲学。我刚刚使用了theirs,因为我不小心更改了两个单独分支中的一些文件,并且想放弃一个更改,而不必为每个文件手动执行。【参考方案6】:

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

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

紧随其后

git merge -s ours branchB

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

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

这种方法还可以从之后的修订日志中清楚地看出做了什么 - 以及打算做什么。

【讨论】:

这对于原始问题可能是正确的,但是 OP 在 2008 年更新了这个问题,使得这个答案不正确。 @user3338098 是的,很遗憾他们留下了误导性的标题,只是在问题主体的 end 处添加了一个不那么明显的更新......因为这个答案确实正确回答了标题问题。 我完全同意@RomainValeri【参考方案7】:

'git merge -s theirs 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'

【讨论】:

与 jthill 的回答相同,我认为,但有更多的视觉解释。【参考方案8】:

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

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

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

git commit --amend

在我所知道的任何情况下都不会发生冲突,您不必进行额外的分支,并且它就像一个正常的合并提交。

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

【讨论】:

-R 在这里做什么? 这样您就丢失了“移动和更改”文件的历史记录。我说的对吗? -R 是--reverse,我更新了答案,使其更具自我记录性。 如果您尝试合并的文件在传入分支中被删除,这似乎不起作用。【参考方案9】:

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

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 中发出合并请求以使其成为新的主节点,要么将其保留为分支。

【讨论】:

【参考方案10】:

我想你真正想要的是:

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

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

【讨论】:

【参考方案11】:

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

我知道 git merge -s ours。但我想要的是 git merge >-s their.

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

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

git checkout Branch
git merge master -s ours

然后,checkout master 并合并你的分支(现在可以顺利进行):

git checkout master
git merge Branch

【讨论】:

谢谢。这应该是公认的答案,是迄今为止最简单、最正确的答案。如果您的分支落后/与 master 冲突,那么在将所有内容合并回 master 之前,它的分支作业是合并并使一切正常工作。【参考方案12】:

这个使用 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>

【讨论】:

【参考方案13】:

请参阅Junio Hamano's widely cited answer:如果您要丢弃已提交的内容,只需丢弃已提交的内容,或者无论如何将其保留在主要历史记录之外。以后为什么还要打扰每个人从没有提供任何东西的提交中阅读提交消息?

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

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

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

【讨论】:

这是 Raymond-Chen 最喜欢的获取 Paul Pladijs 结果的方法。毫不奇怪,这里很少有人能得到它。【参考方案14】:

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

# 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:不知怎的,我也有同样的想法,但没有。请注意,git reset --hard 更改提交 branchA 指向。 很抱歉问了一个愚蠢的问题,但为什么这种方法比-xtheirs 更好?有什么优点/缺点 @chrispepper1989 -x theirs 只会影响 conflicts,如果我们的一大块可以干净地应用,它将通过结果合并。在这种情况下,如最后一条命令所示,我们将得到一个与分支B完全相同的合并,无论是否存在冲突。 @UncleZeiv 谢谢你的解释,所以基本上做一个“他们的”会保持不冲突的更改和文件,导致 int 代码与提取的代码不同。【参考方案15】:

这会将您的 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 的合并提交并假设操作已完成。【参考方案16】:

从现在开始,我就使用了 Paul Pladijs 的答案。我发现,你可以做一个“正常”的合并,发生冲突,所以你这样做

git checkout --theirs <file>

通过使用来自其他分支的修订来解决冲突。如果对每个文件都执行此操作,您将获得与

所期望的相同的行为
git merge <branch> -s theirs

无论如何,努力比合并策略要多! (这是用 git 版本 1.8.0 测试的)

【讨论】:

如果可以检索文件列表会很棒。例如,git ls-files --modified | xargs git add 我想这样做是为了在两侧添加合并:/ 其实git用git ls-files --modified返回两边文件的添加,所以我想这也是一个可行的解决方案。 感谢您也添加此内容。更多时候,它不需要在逐个文件的基础上。此外, git status 在最底部显示“未合并的路径”。要获取未解析路径的列表,我使用此别名:git config --global alias.unresolved '!git status --short|egrep "^([DAU])\1"' -X是什么意思,-X-s有什么区别?找不到任何文件。【参考方案17】:

如果你在分支 A 做:

git merge -s recursive -X theirs B

在 git 版本 1.7.8 上测试

【讨论】:

【参考方案18】:

我用

解决了我的问题
git checkout -m old
git checkout -b new B
git merge -s ours old

【讨论】:

来自“旧”分支的分支“B” 关于从 B 合并到旧分支并使用旧分支的更改解决冲突的问题,您的答案正相反。

以上是关于是否有“git merge -s ours”的“他们的”版本?的主要内容,如果未能解决你的问题,请参考以下文章

是否可以在他自己的方法中将一个对象取消引用到另一个对象?

是否有可以立即显示更改的 HTML/CSS/JS 编辑器? [关闭]

需求:有个文件"pp.txt",判断里面是否包含"李四"的键,如果有把他改成"100"

Jquery validate remote方式是否有问题

UPC ——问题 C: 请教

判断是否撒谎