(Git 合并)何时使用“我们的”策略、“我们的”选项和“他们的”选项?

Posted

技术标签:

【中文标题】(Git 合并)何时使用“我们的”策略、“我们的”选项和“他们的”选项?【英文标题】:(Git Merging) When to use 'ours' strategy, 'ours' option and 'theirs' option? 【发布时间】:2018-01-06 05:59:52 【问题描述】:

从git merge documentation 中提取的递归合并策略的定义。

这只能使用 3 路合并算法解决两个头。当有多个共同祖先可用于 3 路合并时,它会创建共同祖先的合并树并将其用作 3 路合并的参考树。据报道,通过对取自 Linux 2.6 内核开发历史的实际合并提交进行的测试,这会导致更少的合并冲突,而不会导致错误合并。此外,这可以检测和处理涉及重命名的合并。这是拉取或合并一个分支时的默认合并策略。

递归策略可以采用以下选项:

如上所述,作为默认策略的递归策略使用 3 路递归合并算法(解释为 here 和 Wikipedia)。

我的理解是,必须手动解决冲突的大块,它们通常是这样表示的

<<<<<<<<<<<
developer 1's code here
============
developer 2's code here
>>>>>>>>>>>

递归合并策略的ours选项记录如下:

此选项通过支持 我们的 版本来强制自动解决冲突的块。与我们这边不冲突的另一棵树的更改会反映到合并结果中。对于二进制文件,全部内容都取自我们这边。

这不应与 ours 合并策略混淆,后者甚至根本不查看其他树包含的内容。它丢弃了其他树所做的一切,并声明我们的历史记录包含其中发生的一切。

现在假设我有两个分支 Y 和 M 的头,它们有一个共同的基础祖先 B,如下所示

当使用默认递归策略合并 Y 和 M 时,第 30 行将变为Print("hello");,因为在第 30 行,Y 表示从基本祖先的变化,而 M 没有。但是如果我在分支 M 上运行

git merge -s recursive -X ours Y

第 30 行会在合并输出中变为 Print("bye"); 吗?

对于那些说这很明显的人,请注意我们的选项声明

此选项强制冲突的帅哥通过支持我们的版本来干净利落地自动解决。

但是(据我所知)第 30 行没有冲突的大块。

为了完整起见,我还将提供他们的选项的文档:

这与我们的相反。


我们的策略的文档如下:

这解决了任意数量的头,但合并的结果树始终是当前分支头的树,有效地忽略了所有其他分支的所有更改。它旨在用于取代分支的旧开发历史。请注意,这与 recursive 合并策略的 -Xours 选项不同。

所以回到上面的例子,如果我跑了

git merge -s ours Y

在分支 M 上,很明显第 30 行将是合并输出中的Print("bye");。在这种情况下,为什么也没有他们的策略?我怎样才能实现与我们的策略相同和相反的行为?

我问这个问题是因为我正在处理一个项目,只要开发分支上的代码成功构建,我想定期完全用另一个开发分支的更改覆盖主分支。这样我可以确保我的开发分支永远不会偏离主分支太远,并且主分支上的代码将成功构建。

我见过this question推荐的解决方案

git checkout dev-branch
git merge -s ours master

但 Git 只是输出 Already up-to-date,尽管这两个分支包含不同的代码(而 dev-branch 实际上是 master 之前的几个提交)。

我目前的解决方案是这样做

git merge -s recursive -X theirs dev-branch

我还看到了this question,它建议使用 recursive 策略的 theirs 选项。但是,由于 recursive 策略的 ours 选项显然不同于 ours 策略,因此 theirs 选项也是如此递归策略与我所说的他们的策略不同。

【问题讨论】:

这个问题重复了很多其他问题的很多部分。考虑一下我对***.com/q/42099431/1256452***.com/q/44212642/1256452 和***.com/q/44858155/1256452 的回答(也许从最后一个开始?)。 【参考方案1】:

git merge -s recursive -X ours Y 将第 30 行变为 Print("bye"); in 合并后的输出?

不,它也会输出Print("hello");。这是因为只有对方(Y 分支)改变了这个文件,M 分支上的文件版本与他们的祖先B 相同,所以递归合并策略保持更新版本来自Y 分支。

你可以试试:只有来自M分支的文件版本与他们的祖先B不同(例如将30行更改为Print(“bye1”);),然后-X选项可以工作。现在如果你使用git merge -s recursive -X ours Y,输出将是Print(“bye1”);

您还可以在article you linked的图4中找到,如果文件的一侧与其祖先相同(如第30和70行),则文件将保留另一侧(更改)版本作为合并结果。

git merge -s ours Y 输出原因 Print("bye");:

正如文件所说, 这解决了任意数量的头,但合并的结果树始终是当前分支头的树,有效地忽略所有其他分支的所有更改

这意味着它将忽略来自Y 分支的版本,并且只保留当前分支的版本。所以你得到的输出是M 分支Print("bye"); 的版本。

为什么 -s 选项没有git merge -s theirs

Git 只定义了 octupusoursrecursiveresolvesubtree 的合并策略,因此无法识别-s theirs,这是设计问题。具体原因,只有git版本控制系统的开发者可能知道。


针对您的情况(确保development 分支覆盖master 分支),您可以cherry-pick 使用-X theirs 从开发分支到主分支的最新提交 strong> 选项:

# On master branch
git cherry-pick development -X theirs

这将完全覆盖master 分支。

注意: git cherry-pick 命令中的development 表示development 分支指向的提交(development 分支上的最新提交)。您也可以改用提交 sha-1 值。

【讨论】:

既然有我们的策略,为什么没有他们的策略? Git 只定义了 octupus、ours、recursive、resolve 和 subtree 合并策略。这是一个设计问题,只有git版本控制系统开发人员可能知道详细原因。 您建议的挑选策略似乎不会导致干净的合并。我仍然收到“development 是 X 提前提交,Y 提交落后 master”形式的消息。不过还是谢谢你的帮助。 如果你想开发和master同步,你可以在cherry-pick之后使用git merge development

以上是关于(Git 合并)何时使用“我们的”策略、“我们的”选项和“他们的”选项?的主要内容,如果未能解决你的问题,请参考以下文章

Git:在 gitattribute 中为整个文件夹指定一个 git 合并策略“我们的”(或者只是禁用特定文件夹上的合并错误?)

TFS PR 搞砸了 Git 历史

git:清理 git 历史记录并仅在 master 中保留合并的提交

为什么会使用“git merge -s ours”?

你啥时候会使用不同的 git 合并策略?

Git 分支管理 分支管理策略 不使用Fast forward模式进行合并