哪个版本的git文件最终会被使用:LOCAL、BASE还是REMOTE?

190

当使用git merge时发生冲突,我会打开一个名为Meld的合并工具。它会打开三个文件:LOCAL、BASE和REMOTE。根据我所了解的,LOCAL是我的本地分支,BASE是共同祖先,REMOTE是要合并的分支。

现在来回答我的问题:最终使用哪个文件版本?是REMOTE吗?如果是,我可以自由编辑它,而不受BASE分支中内容的影响吗?

8个回答

150

这是中间的那个:BASE

实际上,BASE 不是普通的共同祖先,而是半成品合并,其中冲突标记为 >>>><<<<

您可以在 meld 编辑窗口顶部看到文件名。

点击此处查看屏幕截图

meld base

您可以使用 meld 命令对 BASE 文件进行编辑,也可以不使用 meld 命令直接使用您喜欢的文本编辑器编辑文件。

  • 位于 <<<< HEAD===== 标记之间的代码是合并前您本地文件的代码。
  • 位于 ====>>>> <branch name> 标记之间的代码是远程文件的代码。

4
如果将merge.conflictstyle配置选项设置为diff3而不是默认的merge,有些人能更好地理解在文件中冲突块的自动合并失败。 - kostix
3
实际上,我看不到 <<<、=== 和 HEAD 标记。在你提供的情况下,中间窗口应该是空的。但这只是给其他人的一个提示,感谢你的回答。 - tsusanka
10
当我使用Meld进行合并时,在中间面板(即基本版本)中,我看不到任何<<<<<<======>>>>>>标记;有时,中间面板会是空的,就像aGr报告的那样。也许这种差异是由于不同的设置造成的。当我启动Meld工具时,假设仓库中的文件名为 X.java,以下文件将存在:X.javaX.java.origX.java.BACKUP.#X.java.BASE.#X.java.LOCAL.#X.java.REMOTE.#,其中 # 是某个数字。把合并结果称为“BASE”版本是令人困惑的;更好的命名应该是“MERGED”。 - Teemu Leisti
5
BASE实际上是共同的祖先,MERGED是包含部分合并信息的文件的名称。请参考我的问题和答案设置和使用Meld作为您的git difftool和mergetool,其中详细解释了它的工作原理。希望对你有所帮助。 - mattst
1
你看不到 HEAD<<<<==== 的原因是默认配置设置中间窗口为 $BASE(祖先)。如果你将配置切换到 cmd = meld "$LOCAL" "$MERGED" "$REMOTE" --output "$MERGED",那么你的中间窗口就是 $MERGED,你就会看到 <<<=== 等。请参见此线程 - Brandon Loudermilk
显示剩余3条评论

112

Meld有一个隐藏的三方合并功能,通过传入第四个参数来激活:

meld $LOCAL $BASE $REMOTE $MERGED

左右两个窗格以只读模式打开,这样您就不会意外地错误合并。中间窗格显示合并的结果。对于冲突,它显示基础版本,以便您可以看到所有重要部分:中间的原始文本和两侧的冲突修改。最后,当您按下“保存”按钮时,$MERGED文件将被写入 - 正如git所期望的那样。

我使用的~/.gitconfig文件包含以下设置:

[merge]
tool = mymeld
conflictstyle = diff3
[mergetool "mymeld"]
cmd = meld --diff $BASE $LOCAL --diff $BASE $REMOTE --diff $LOCAL $BASE $REMOTE $MERGED

这将打开Meld窗口,包括3个标签页,第1和第2个标签页显示我正在尝试合并的简单差异,而默认打开的第3个标签页则显示三方合并视图。

现在,该功能被隐藏的原因是它还不够完善。现在它已经非常有用,但是Meld作者Kai Willadsen指出了一些需要解决的问题。例如,没有GUI启动三方合并模式,命令行语法有点晦涩难懂等。如果你会Python并且手头有时间,你知道该怎么做。

编辑:在更新的Meld版本中,语法略有变化。这个信息在评论中,但应该出现在答案中。

Meld命令现在使用--output选项,因此上面代码段的最后一行应该改为:

--output 合并后的文件名

cmd = meld --diff $BASE $LOCAL --diff $BASE $REMOTE --diff $LOCAL $BASE $REMOTE --output $MERGED

7
@Jesse, @lumbric,看起来最新版的 Meld 使用 --output 标志来表示 $MERGED 的结果。我在查看我的 Git 版本附带的 Meld 启动脚本时发现了这一点:https://github.com/git/git/blob/master/mergetools/meld。 - Johann
1
@lumbric 我相信它可以,对于带有 --output 选项的 Meld 1.7.x+。请参见启动脚本中的此行:"$merge_tool_path" --output "$MERGED" "$LOCAL" "$BASE" "$REMOTE" - Johann
13
在最新的meld版本(>1.8.4),我们必须使用--auto-merge选项。 命令为:meld --diff $BASE $LOCAL --diff $BASE $REMOTE --auto-merge $LOCAL $BASE $REMOTE --output $MERGED - RoboAlex
8
我和@pingpongboss遇到了相同的问题,使用Meld 1.8.4时,Meld会在一个单独的窗格中打开文件,而不是在第三个选项卡中打开。最终有效的命令是:cmd = meld $LOCAL $BASE $REMOTE --auto-merge --output $MERGED。这样会打开3个选项卡(传统方式),自动将不冲突的合并内容合并到中间位置,中间位置为$MERGED,将用作冲突解决输出。 - farmir
2
输出的语法可以是 --output=<file>-o <file>,请参见 meld --help - levsa
显示剩余7条评论

69

涉及到4个文件:

  1. $LOCAL 在你要合并的分支中的文件;在向你展示时未被合并过程更改

  2. $REMOTE 在你要合并的分支源头上的文件;在向你展示时未被合并过程更改

  3. $BASE $LOCAL 和 $REMOTE 的共同祖先, 即这两个分支在考虑的文件上开始偏离的点;在向你展示时未被合并过程更改

  4. $MERGED 部分合并的文件,其中包含冲突;这是唯一被合并过程更改的文件,实际上,在 meld 中从未向您展示。


$MERGED 文件包含的是标记冲突的 <<<<<<, >>>>>>, ===== (还有可能是 ||||||)。您需要手动编辑 该文件 来纠正冲突。

手动解决冲突和视觉解决冲突在不同的文件上进行,提供不同的信息。

当使用合并工具时(假设使用 meld),其中看到的文件是: $LOCAL, $BASE, $REMOTE。请注意,您看不到 $MERGED 文件,尽管它作为隐藏参数传递给 meld 以将编辑结果写入那里。

换句话说,在 meld 中,您正在编辑中间的文件,即 $BASE 文件,并手动从左侧或右侧选择所有更改。它是一个干净的文件,没有被合并过程触及。唯一的问题是,当您保存时,您并不保存到 $BASE 文件中,而是保存到 meld 的第四个隐藏参数中,即 $MERGED 文件(您甚至看不到它)。$BASE 文件不包含任何冲突或部分成功的合并,因为它不是 $MERGED 文件。
在可视化编辑中,当向您展示 $BASE 文件(而不是 $MERGED 文件)时,git 基本上放弃了所有尝试进行合并的操作(如果您想的话,这些尝试可以在 $MERGED 文件中看到),并让您从头开始完全进行合并。
总之,在手动和可视合并冲突中,您看到的不是同一个文件,但最终结果写入同一个文件(即 $MERGED 文件)。
冲突的手动校正是在 $MERGED 上完成的,因为 git 没有办法呈现三个文件,所以它将来自三个文件($LOCAL$BASE$REMOTE)的信息压缩在 $MERGED 文件中。

但是视觉工具有一些方法可以向你展示三个文件:它们会向你展示 $LOCAL$BASE$REMOTE 文件。你需要从 $LOCAL$REMOTE 文件中挑选更改,并将其带入 $BASE 文件,完全重建并覆盖合并失败的文件 $MERGED


1
我只是想说,有一些工具(例如Beyond Compare)可以显示所有4个文件。 - yoniLavi
@yoniYalovitsky:是的,或者p4merge。 - user1284631
我曾经使用ClearCase软件包中的合并工具。 - mishmashru
@yoniLavi - 这些工具显示4个“窗格”,但不一定像这个答案中描述的那样显示所有四个文件。特别是,您可以设置这些4个窗格工具,以向您显示$LOCAL$REMOTE$BASE和最初等于$BASE的输出,但它与$MERGED不同,因为它没有git尝试合并文件和冲突标记等。实际上,这将是使用这些工具最类似于LOCAL/REMOTE/BASE+OUTPUT的3窗格方法,该方法不显示已合并的内容。第四个窗格只允许您将基础与输出分开。 - BeeOnRope

16

Cosmin的解决方案可行,但是更新的是$BASE文件而不是$MERGED文件。以下操作将会更新$MERGED文件:

Meld: v1.8.4

[merge]
  conflictstyle = diff3
  tool = mymeld
[mergetool "mymeld"]
  cmd = meld --auto-merge --output $MERGED $LOCAL $BASE $REMOTE --diff $BASE $LOCAL --diff $BASE $REMOTE

我可以确认这一点。Saad的解决方案在Ubuntu上对我有效。就原始问题而言,这是当前的正确答案。 - cosmin
3
在我的meld-3.11版本中,这个命令非常好用:cmd = meld --auto-merge --output $MERGED $LOCAL $BASE $REMOTE - MartinM
为什么你需要在结尾加上 --diff $BASE $LOCAL --diff $BASE $REMOTE?对我来说,在1.8.4版本中,这个命令可以正常工作(就我所知):cmd = meld --auto-merge --output $MERGED $LOCAL $BASE $REMOTE - farmir
1
@farmir:不是必要的。它会在Meld中打开两个额外的标签,这样您就可以将LOCAL和REMOTE与BASE分别进行比较。 - Sam Kauffman
1
无论我尝试以什么顺序使用这些参数,三向选项卡始终是第三个选项卡,而默认情况下始终选择第一个选项卡。有没有办法使三向选项卡成为默认选中的选项卡? - Sam Kauffman
我升级到Debian 9,随之而来的是Meld 3.16.4,但它不再起作用了。即使命令包括三个选项卡,Meld只会打开一个选项卡。有人知道如何解决吗? - Sam Kauffman

13

使用 Meld 1.7 后,Tomek Bury 的解决方案不再起作用。

默认设置 不能满足我的需求:

默认设置

相反地,对于 Meld >=1.7,我建议使用以下两种其中之一的解决方案。

第一个解决方案:

 meld $LOCAL $BASE $REMOTE --auto-merge

第一个解决方案

第二个解决方案:

 meld $LOCAL $MERGED $REMOTE

第二个解决方案

.gitconfig

将以下内容复制并粘贴到您的.gitconfig文件中,以获得上述描述的解决方案:

[merge]
    tool = meld16
[mergetool "meld17"]
    # use this for Meld >=1.7
    # see https://dev59.com/O2gu5IYBdhLWcg3wt5Lt#22911793
    # second solution:
    cmd = meld $LOCAL $MERGED $REMOTE
    # first solution:
    #cmd = meld $LOCAL $BASE $REMOTE --auto-merge
[mergetool "meld16"]
    cmd = meld --diff $BASE $LOCAL --diff $BASE $REMOTE --diff $LOCAL $BASE $REMOTE --output $MERGED

[include]
    # requires git v1.7.10+
    path = .gitconfig.local

如果您在多台机器上使用您的.gitconfig文件,那么请将以下内容复制并粘贴到.gitconfig.local文件中,以便仅在该机器上设置meld17或meld16:

[difftool]
    prompt = false
    trustExitCode = true
[difftool "meld"]
    cmd = meld17 \"$LOCAL\" \"$REMOTE\"
[difftool "meld16"]
    cmd = meld16 \"$LOCAL\" \"$REMOTE\"
[merge]
    tool = meld
[mergetool "meld"]
    cmd = meld17 --auto-merge \"$LOCAL\" \"$BASE\" \"$REMOTE\" --output=\"$MERGED\"
[mergetool "meld16"]
    cmd = meld16 --auto-merge \"$LOCAL\" \"$BASE\" \"$REMOTE\" --output=\"$MERGED\"
# This is a host specific config file!
# Note that git 1.7.10+ is needed
# https://dev59.com/yHI_5IYBdhLWcg3wBuX3#9733277
[merge]
    tool = meld17

这在 Meld 1.8.4 上不起作用。如果你运行 cmd = meld $LOCAL $BASE $REMOTE --auto-merge,中间的窗格将是 $BASE,而不是实际用作冲突解决输出的 $MERGE。 - farmir
1
@farmir 你选择了 $BASE 作为第二个选项卡。 - Alex78191

11

我发现默认显示的文件都没有被保存。meld 默认显示 $LOCAL$REMOTE$BASE。为了让它正常工作,我需要让 meld 显示 $MERGED 而不是 $BASE。将下面的内容加入到我的 ~/.gitconfig 文件中后,问题得以解决:

[merge]
        tool = mymeld
[mergetool "mymeld"]
        cmd = meld "$LOCAL" "$MERGED" "$REMOTE"

我正在使用Arch,配备:

$ git --version
git version 1.8.2
$ meld --version
meld 1.7.1

请问,这个配置是兼容Linux的吗? - MadMad666

2
请查看Saad的答案,获取正确答案。
在Ubuntu上使用meld 1.8.1时,我会收到“wrong number of arguments supplied to --diff”的错误提示。在$MERGED之前添加--output对我有用并解决了这个问题:
[mergetool "mymeld"]
cmd = meld --diff $BASE $LOCAL --diff $BASE $REMOTE --diff $LOCAL $BASE $REMOTE --output $MERGED

2

由于某些原因,最新版本的meld无法显示为冲突添加的标记线(<<<<<<<,=======,>>>>>>>)。如果您想看到这些线,请安装meld v 1.3.3或更早版本。


我发现@lumbric的答案非常有用 https://dev59.com/O2gu5IYBdhLWcg3wt5Lt#22911793 - wnasich

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接