Git:查看自上次合并冲突合并以来其分支上的更改

时间:2016-11-08 23:23:40

标签: git git-merge

我认为我的情景应该相当普遍,并且必须有一种更简单的方式来做我做的事情。假设有两个分支currentnext,用于两行开发。此阶段的next会收到current分支的所有更改。作为next分支的维护者,我正在同步这些更改(如alpha版本所需):

$ git co origin/next -b next
$ git merge origin/current

某些文件更改了next上的格式,自上次同步合并以来对这些文件所做的每次更改都会导致冲突。要解决此冲突,我需要查看自上次合并以来current分支上的更改。通常很多文件都已更改,但只有我提到的1或2个文件存在冲突。

实施例

假设baz.dat分支上的文件current包含方括号中的单词,例如

[real]
[programmers]
[use]
[pascal]

next分支上,语法更改要求用冒号包围单词

:real:
:programmers:
:use:
:pascal:

current分支上的更改为文件添加了一行:

[real]
[programmers]
[don't]
[use]
[pascal]

每次合并冲突都会导致以下差异合并标记:

<<<<<<< ours
:real:
:programmers:
:use:
:pascal:
=======
[real]
[programmers]
[don't]
[use]
[pascal]
>>>>>>>> theirs

删除并替换整个文件内容,因为从某种意义上说,它是。

同样,只有git diff显示所有传入,&#34;他们的&#34;删除的行和&#34;我们的&#34;添加的行:

$ git diff
<usual unified diff preamble>
+  :real:
... more lines with "+ " (our new) or " -" (their deleted) mark, 
 - [pascal]

我在寻找什么

查看自上次合并以来current(&#34;他们的&#34;)分支上发生了哪些更改的方法,因此我可以手动将更改合并到next(&#34) ;我们的&#34;)分支

$ git diff --magically-from-merge-point --theirs
<usual unified diff preamble>
+ [don't]

换句话说,我想在这个例子中看到只添加了一行,所以我也可以用新格式插入它。

(我的真实案例是特定于域的语言的变化,但它基本上与这个简单的例子非常相似)。

当前解决方案

我要做的是一系列相当笨拙的命令:

$ git status 
. . . .
      both modified:   foo/bar/baz.dat <== copy/paste every conflict filename
$ git diff `git merge-base HEAD origin/current`..origin/current -- foo/bar/baz.dat

这显示了我想要的东西,但相当复杂,我每次都构造并输入它。在bash中编写脚本很容易,但在我这样做之前,我想问一下,是否有更简单的方法可以看到来自merge base的冲突变化? I. e。

(next)$ git merge origin/current
. . . 
CONFLICT (content): foo/bar/baz.dat
(next|MERGING)$ git diff --magic-switch

和diff仅显示冲突文件的变化,作为合并基础和合并点之间的差异(在理想情况下,我会进一步限制,例如--magic-switch --theirs

这样的神奇开关是否存在?看起来我错过了一些明显的东西!

2 个答案:

答案 0 :(得分:1)

git mergetool将在您选择的差异编辑器(meld,vimdiff,kdiff3,winmerge ......)中逐个打开冲突文件,作为3个版本之间的3路合并:

  • local:当前分支中的版本(在您的情况下:next的版本)
  • base:合并基础提交中的版本
  • remote:合并分支中的版本(在您的情况下:origin/current的版本)

如果编辑+保存中心文件,git会将此冲突标记为已解决,这是您在索引中保存的阶段。

如果您的合并因冲突而停止,git会在.git/MERGE_HEAD中存储对合并提交的引用。这意味着您可以在git命令中使用字符串"MERGE_HEAD"作为有效引用:

git log -1 MERGE_HEAD            # view last commit on the merged branch
git merge-base MERGE_HEAD HEAD   # no need for the name of the branch

然后,您可以构建一个更简单的别名:

theirs = 'git diff $(git merge-base MERGE_HEAD HEAD) MERGE_HEAD'
# usage :
git theirs                  # complete diff between 'base' and 'theirs'
git theirs -w -- this/file  # you can add any option you would pass to 'git diff'

答案 1 :(得分:1)

啊,通过这个例子,我们可以到达某个地方。

这不是内置于Git中的,因为它一般来说是一个非常难的问题。但是如果我们稍微限制它,我们可以编写一个脚本或工具或程序来处理它(而 部分,事实证明,内置到Git中!好吧,有点儿)。让我们看看我们是否可以描述约束:

  • 这是标准的三向合并,标准修改冲突,因此基本版本(第1阶段),本地/ HEAD / --ours版本(第2阶段)和远程/其他/ --theirs版本(第3阶段)。

  • 合并的一个“侧面”触及每一行,但是采用重复且可识别的模式,我们可以暂时退出。 (让我们给这个改变一个名字:让我们把它称为“系统增量”或简称SD,+ SD意味着添加或保持这个增量,而-SD意味着撤消/删除它.SD可能是一个不可逆转的变化,即, -SD可能无法完美地撤消它;如果是这样,我们可能仍然能够自动处理它。)它可能会或可能不会有一些额外的更改,相对于基础

  • 合并的另一个“侧”只触及几行,甚至没有行,缺少重复和可识别的模式更改,我们可以添加它,也许是暂时的。

我们还有一些其他问题和决定需要考虑,可能是根据具体情况或系统地进行。它们将影响我们编写脚本,工具或过程的方式。这些是:

  • SD实际上是可逆的吗?也就是说,一旦我们发现SD,base + SD - SD会重现原始基础吗?
  • 我们想在结果中使用SD吗?
  • 我们可以检测到SD的存在吗? (我们可能不需要,但如果可以的话,我们会发现它很方便。)

如果SD 可逆,我们处于良好状态,因为我们可以得到任何我们喜欢的结果。如果没有,我们将不得不将SD应用到缺少它的那个方面:当且仅当我们想要结果中的SD时,这是可以的。

让我们假设我们想要结果中的+ SD。在这种情况下,我们处于良好状态:让我们调用应用SD“标准化”文件的过程。或者,如果我们在结果中想要-SD,那么 SD 是可逆的,让我们调用-SD“规范化”。此外,如果我们可以判断文件是否已应用SD,我们只需编写一个“规范化过滤器”并通过该过滤器运行所有内容。

这是Git内置的内容:它具有“干净”和“污迹”过滤器,如the gitattributes documentation中所述。此外,我们可以指示git merge运行两个过滤器(两个过滤器 - 但我们可以在进行合并之前使它们都“正常化”,或者在此处为每个文件保留一个未设置的)。由于“规范化”为我们提供了我们想要的最终形式,因此它使得Git能够完成我们想要的合并。

如果SD不可逆,我们需要更聪明,但只要我们想要+ SD结果,我们仍然可以。我们可以编写自己的合并驱动程序,而不是使用the merge.renormalize setting described in gitattributes。这也在同一手册中记录(进一步说明)。我不会在这里详细说明,因为清洁/涂抹过滤器方法更容易并且可能适用于您的情况,但实质是我们提取三个阶段,将+ SD应用于任何需要它的人(选择“需要的文件”)它“通过测试或先验知识”,然后使用git-merge-file在三个(现在所有+ SD)输入上实现所需的合并。

请注意,您运行.gitattributes时工作树中存在的git merge文件,以及您在.gitconfig-c中选择的所有配置项} git的命令行选项,在您运行git merge时控制过滤和重新规范化。这个.gitattributes文件甚至无需在任何地方签到。这意味着您可以在不同时间生成不同的 .gitattributes文件,方法是将其作为普通的跟踪文件并切换提交和/或分支(以便Git为您更新),或者只是根据需要手动更新。