Git:查看自上次合并冲突以来其分支上的更改
Git: see changes on their branch since last merge on merge conflict
我认为我的场景应该是相当普遍的,并且必须有更简单的方法来做我所做的事情。假设有两个分支,current
和next
,用于两条开发线。 next
在此阶段接收来自 current
分支的所有更改。作为 next
分支的维护者,我正在像这样同步更改(对于 alpha 版本是必要的):
$ git co origin/next -b next
$ git merge origin/current
一些文件在 next
上更改了它们的格式,自上次同步合并以来对这几个文件所做的每次更改都会导致冲突。要解决此冲突,我需要查看自上次合并以来 current
分支上的更改。通常很多文件都发生了变化,但只有 1 或 2 个像我提到的文件有冲突。
例子
假设 current
分支上的文件 baz.dat
包含方括号中的单词,如
[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
显示所有传入的行,"their" 行已删除,"our" 行已添加:
$ git diff
<usual unified diff preamble>
+ :real:
... more lines with "+ " (our new) or " -" (their deleted) mark,
- [pascal]
我在找什么
一种查看自上次合并后 current
("their") 分支发生了什么变化的方法,因此我可以手动将更改合并到 next
("our") 分支
$ 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 中编写脚本很容易,但在我这样做之前,我想问一下是否有更简单的方法来查看合并基础中的冲突更改?即
(next)$ git merge origin/current
. . .
CONFLICT (content): foo/bar/baz.dat
(next|MERGING)$ git diff --magic-switch
和 diff 只会将冲突文件的更改显示为合并基础和合并点之间的增量(在理想情况下,我会进一步限制,例如 --magic-switch --theirs
)
有这样的神奇开关吗?看来我遗漏了一些明显的东西!
git mergetool
将在您选择的 diff 编辑器(meld、vimdiff、kdiff3、winmerge ...)中一个接一个地打开冲突文件,作为 3 个版本之间的 3 路合并:
local
:当前分支中的版本(在您的情况下:next
的版本)
base
: 合并基础提交中的版本
remote
:合并分支中的版本(在您的情况下:origin/current
的版本)
如果您编辑并保存中央文件,git 会将此冲突标记为已解决,这是您在索引中保存的阶段。
如果您的合并因冲突而停止,git
会在 .git/MERGE_HEAD
中存储对合并提交的引用。这意味着您可以使用字符串 "MERGE_HEAD"
作为 git 命令中的有效引用:
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'
啊,通过这个例子我们可以有所作为。
Git 这不是内置的,因为它通常是一个非常难的问题。但是如果我们稍微限制它,我们可以编写一个脚本或工具或程序来处理它(那个部分,事实证明,是内置于 Git!......好吧,有点)。让我们看看是否可以描述约束条件:
这是一个标准的三向合并,有一个标准的修改冲突,所以是一个基础版本(第一阶段),一个local/HEAD/--ours
版本(第 2 阶段)和 remote/other/--theirs
版本(第 3 阶段)。
其中一个 "side" 合并触及每一行,但以重复和可识别的模式,我们可以退出,也许是暂时的。 (让我们给这个改变起个名字:让我们称它为 "systematic delta" 或简称 SD,+SD 表示添加或保留此增量,而 -SD 表示撤消/删除它。SD 可能是不可逆的更改,即, -SD 可能无法完美地撤消它;如果是这样,我们 可能 仍然能够自动处理它。)它可能有也可能没有一些 额外的 相对于基数的变化。
合并的另一个"side"只有几行,甚至没有行,和缺乏重复和可识别的模式更改,我们可以将其添加到其中,也许是暂时的。
我们这里还有一些额外的问题和决定需要考虑,可能是根据具体情况或可能是系统地考虑。它们将影响我们编写脚本、工具或过程的方式。它们是:
- SD真的是可逆的吗?也就是说,一旦我们发现了SD,base + SD - SD 是否复制了原来的base?
- 我们想要结果中的 SD 吗?
- 我们可以检测到 SD 的存在吗? (我们可能不需要,但如果可以的话,我们会看到它很方便。)
如果 SD 是可逆的,我们就处于良好状态,因为我们可以得到任何我们喜欢的结果。如果没有,我们将不得不将 SD 应用到缺少它的一侧:当且仅当我们希望结果中有 SD 时,这才可以。
让我们暂时假设我们想要 +SD 在结果中。在这种情况下,我们的状态很好:我们只调用应用 SD "normalizing" 文件的过程。或者,如果我们想要结果中的 -SD, 和 SD 是 可逆的,让我们调用 -SD "normalizing"。此外,如果我们可以判断一个文件是否应用了 SD,我们可以通过该过滤器写入 "normalizer filter" 和 运行 所有内容。
这是 Git 的内置功能:它具有 "clean" 和 "smudge" 过滤器,如 the gitattributes
documentation 中所述。此外,我们可以指示 git merge
到 运行 这两个过滤器(它们都是 - 但我们可以让它们都成为 "normalize",或者为了我们这里的特定目的保留一个不设置)合并之前的文件。由于 "normalizing" 为我们提供了我们想要的最终形式,它使得 Git 进行我们想要完成的合并。
如果SD是不可逆的,我们就需要更聪明一些,但是只要我们想要结果+SD,还是可以的。我们可以编写自己的合并驱动程序,而不是使用 the merge.renormalize
setting described in gitattributes
。这也在同一手册中记录(进一步向下)。我不会在这里详细介绍,因为 clean/smudge 过滤方法更简单并且可能适用于您的情况,但本质是我们提取三个阶段,将 +SD 应用于需要它的任何阶段(selecting "files that need it" 通过测试或先验知识),然后使用 git-merge-file
在三个(现在所有 +SD)输入上实现所需的合并。
请注意,.gitattributes
文件在您 运行 git merge
时存在于工作树中,加上您 select 在 .gitconfig
或使用 -c
命令行选项 git
,在 运行 git merge
时控制过滤和重新规范化。这个 .gitattributes
文件甚至不需要在任何地方签入。这意味着你可以让 不同的 .gitattributes
文件在不同的时间生效,要么将其设为普通的跟踪文件并切换提交 and/or 分支(以便 Git 为您更新),或者根据需要手动更新。
我认为我的场景应该是相当普遍的,并且必须有更简单的方法来做我所做的事情。假设有两个分支,current
和next
,用于两条开发线。 next
在此阶段接收来自 current
分支的所有更改。作为 next
分支的维护者,我正在像这样同步更改(对于 alpha 版本是必要的):
$ git co origin/next -b next
$ git merge origin/current
一些文件在 next
上更改了它们的格式,自上次同步合并以来对这几个文件所做的每次更改都会导致冲突。要解决此冲突,我需要查看自上次合并以来 current
分支上的更改。通常很多文件都发生了变化,但只有 1 或 2 个像我提到的文件有冲突。
例子
假设 current
分支上的文件 baz.dat
包含方括号中的单词,如
[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
显示所有传入的行,"their" 行已删除,"our" 行已添加:
$ git diff
<usual unified diff preamble>
+ :real:
... more lines with "+ " (our new) or " -" (their deleted) mark,
- [pascal]
我在找什么
一种查看自上次合并后 current
("their") 分支发生了什么变化的方法,因此我可以手动将更改合并到 next
("our") 分支
$ 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 中编写脚本很容易,但在我这样做之前,我想问一下是否有更简单的方法来查看合并基础中的冲突更改?即
(next)$ git merge origin/current
. . .
CONFLICT (content): foo/bar/baz.dat
(next|MERGING)$ git diff --magic-switch
和 diff 只会将冲突文件的更改显示为合并基础和合并点之间的增量(在理想情况下,我会进一步限制,例如 --magic-switch --theirs
)
有这样的神奇开关吗?看来我遗漏了一些明显的东西!
git mergetool
将在您选择的 diff 编辑器(meld、vimdiff、kdiff3、winmerge ...)中一个接一个地打开冲突文件,作为 3 个版本之间的 3 路合并:
local
:当前分支中的版本(在您的情况下:next
的版本)base
: 合并基础提交中的版本remote
:合并分支中的版本(在您的情况下:origin/current
的版本)
如果您编辑并保存中央文件,git 会将此冲突标记为已解决,这是您在索引中保存的阶段。
如果您的合并因冲突而停止,git
会在 .git/MERGE_HEAD
中存储对合并提交的引用。这意味着您可以使用字符串 "MERGE_HEAD"
作为 git 命令中的有效引用:
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'
啊,通过这个例子我们可以有所作为。
Git 这不是内置的,因为它通常是一个非常难的问题。但是如果我们稍微限制它,我们可以编写一个脚本或工具或程序来处理它(那个部分,事实证明,是内置于 Git!......好吧,有点)。让我们看看是否可以描述约束条件:
这是一个标准的三向合并,有一个标准的修改冲突,所以是一个基础版本(第一阶段),一个local/HEAD/
--ours
版本(第 2 阶段)和 remote/other/--theirs
版本(第 3 阶段)。其中一个 "side" 合并触及每一行,但以重复和可识别的模式,我们可以退出,也许是暂时的。 (让我们给这个改变起个名字:让我们称它为 "systematic delta" 或简称 SD,+SD 表示添加或保留此增量,而 -SD 表示撤消/删除它。SD 可能是不可逆的更改,即, -SD 可能无法完美地撤消它;如果是这样,我们 可能 仍然能够自动处理它。)它可能有也可能没有一些 额外的 相对于基数的变化。
合并的另一个"side"只有几行,甚至没有行,和缺乏重复和可识别的模式更改,我们可以将其添加到其中,也许是暂时的。
我们这里还有一些额外的问题和决定需要考虑,可能是根据具体情况或可能是系统地考虑。它们将影响我们编写脚本、工具或过程的方式。它们是:
- SD真的是可逆的吗?也就是说,一旦我们发现了SD,base + SD - SD 是否复制了原来的base?
- 我们想要结果中的 SD 吗?
- 我们可以检测到 SD 的存在吗? (我们可能不需要,但如果可以的话,我们会看到它很方便。)
如果 SD 是可逆的,我们就处于良好状态,因为我们可以得到任何我们喜欢的结果。如果没有,我们将不得不将 SD 应用到缺少它的一侧:当且仅当我们希望结果中有 SD 时,这才可以。
让我们暂时假设我们想要 +SD 在结果中。在这种情况下,我们的状态很好:我们只调用应用 SD "normalizing" 文件的过程。或者,如果我们想要结果中的 -SD, 和 SD 是 可逆的,让我们调用 -SD "normalizing"。此外,如果我们可以判断一个文件是否应用了 SD,我们可以通过该过滤器写入 "normalizer filter" 和 运行 所有内容。
这是 Git 的内置功能:它具有 "clean" 和 "smudge" 过滤器,如 the gitattributes
documentation 中所述。此外,我们可以指示 git merge
到 运行 这两个过滤器(它们都是 - 但我们可以让它们都成为 "normalize",或者为了我们这里的特定目的保留一个不设置)合并之前的文件。由于 "normalizing" 为我们提供了我们想要的最终形式,它使得 Git 进行我们想要完成的合并。
如果SD是不可逆的,我们就需要更聪明一些,但是只要我们想要结果+SD,还是可以的。我们可以编写自己的合并驱动程序,而不是使用 the merge.renormalize
setting described in gitattributes
。这也在同一手册中记录(进一步向下)。我不会在这里详细介绍,因为 clean/smudge 过滤方法更简单并且可能适用于您的情况,但本质是我们提取三个阶段,将 +SD 应用于需要它的任何阶段(selecting "files that need it" 通过测试或先验知识),然后使用 git-merge-file
在三个(现在所有 +SD)输入上实现所需的合并。
请注意,.gitattributes
文件在您 运行 git merge
时存在于工作树中,加上您 select 在 .gitconfig
或使用 -c
命令行选项 git
,在 运行 git merge
时控制过滤和重新规范化。这个 .gitattributes
文件甚至不需要在任何地方签入。这意味着你可以让 不同的 .gitattributes
文件在不同的时间生效,要么将其设为普通的跟踪文件并切换提交 and/or 分支(以便 Git 为您更新),或者根据需要手动更新。