ChatGPT解决这个技术问题 Extra ChatGPT

如何使用 vimdiff 解决 git 合并冲突?

我刚刚在 git 中将一个分支合并到我的 master 中,我得到了 Automatic merge failed; fix conflicts and then commit the result. 现在我运行 git mergetool 并打开 vimdiff 并显示下图。我不知道如何使用 vimdiff。这里的每个面板是什么意思,我应该如何着手解决合并冲突?

https://i.stack.imgur.com/kxn1g.png

请参阅this page。如果这就是“正确”的意思,那么代码的当前状态在左上角。
@romainl 读完之后我仍然很困惑,快捷方式是什么,如何选择哪个文件用作主分支?
另请参阅:this
按照这个接受的答案使用 fugitivestackoverflow.com/a/7313949/327074

c
codebox

所有四个缓冲区都提供了同一文件的不同视图。左上角的缓冲区 (LOCAL) 是文件在目标分支中的外观(您正在合并的内容)。右上角的缓冲区 (REMOTE) 是文件在您的源分支(您要合并的位置)中的外观。中间缓冲区 (BASE) 是两者的共同祖先(因此您可以比较左右版本的差异)。

我可能会误解以下几点。我认为合并冲突的根源是两个文件自 BASE 以来都更改了文件的同一部分; LOCAL 将引号从双引号更改为单引号,REMOTE 进行了相同的更改,但也将背景值从颜色更改为 URL。 (我认为合并不够聪明,无法注意到对 LOCAL 的所有更改也存在于 REMOTE 中;它只知道 LOCAL 自 BASE 以来在 REMOTE 具有的相同位置进行了更改)。

在任何情况下,底部缓冲区都包含您可以实际编辑的文件——位于您的工作目录中的文件。您可以进行任何您喜欢的更改; vim 向您展示了它与每个顶视图有何不同,这是自动合并无法处理的区域。如果您不想进行远程更改,请从 LOCAL 中提取更改。如果您更喜欢 LOCAL 更改,请从 REMOTE 中提取更改。如果您认为 REMOTE 和 LOCAL 都错误,请从 BASE 中提取。如果您有更好的主意,请做一些完全不同的事情!最后,您在此处所做的更改是实际提交的更改。


快速提问如何在 vim 中保存?
:x:w:x 也退出)加上“返回”。
Anders:如果您不熟悉如何使用 vim,可以使用其他合并工具。
@AndersKitson,因为您使用的是 Mac OS X,所以 FileMerge 是完美的、免费的,并且随 XCode 一起提供。
为什么投反对票?如果有一些事实不正确的地方,请修复它,或者至少指出它。
C
Ciro Santilli Путлер Капут 六四事

@chepner 的回答很好,我想在问题的“我应该如何着手解决合并冲突”部分添加一些细节。如果您研究在这种情况下如何实际使用 vimdiff,它会在下面进行。

首先,解决“中止一切”选项 - 如果您不想使用“vimdiff”并想中止合并:按 Esc,然后键入 :qa! 并按 Enter< /kbd>。 (另见 How do I exit the Vim editor?)。 Git 会询问你合并是否完成,回复 n

如果你想使用 vimdiff,这里有一些有用的快捷方式。这假设您了解 Vim 的基础知识(导航和插入/正常模式):

导航到底部缓冲区(合并结果):Ctrl-W j

使用 j/k 导航到下一个差异;或者,更好的是,使用 ] c 和 [ c 分别导航到下一个和上一个差异

如果您想查看更多上下文,请在折叠时使用 zo 打开它

对于每个差异,根据@chepner 的回答,您可以从本地、远程或基本版本获取代码,或者编辑并重做您认为合适的从本地版本获取代码,使用 :diffget LO from remote: : diffget RE from base: :diffget BA 或者,如果您想自己编辑代码,请先从本地/远程/基础获取版本,然后进入插入模式并编辑其余部分

要从本地版本中获取它,请使用 :diffget LO

从远程: :diffget RE

从基础: :diffget BA

或者,如果您想自己编辑代码,请先从 local/remote/base 获取一个版本,然后进入插入模式并编辑其余部分

完成后,保存合并结果,然后退出所有窗口

如果您想中止合并当前文件并且不将其标记为已解决,请使用 :cquit 退出:如何取消外部 git diff?

通常,git 检测到合并已完成并创建合并提交

如果没有复制粘贴或自定义快捷方式,似乎无法同时添加本地和远程冲突块:https://vi.stackexchange.com/questions/10534/is-there-a-way-to-take-both-when-using-vim-as-merge-tool 这是一种耻辱,因为 add add 是一种常见的冲突类型。

要防止 vimdiff 每次启动时都要求您按 Enter,请添加到您的 .vimrc

set shortmess=Ot

如上所述:https://vi.stackexchange.com/questions/771/how-can-i-suppress-the-press-enter-prompt-when-opening-files-in-diff-mode

您可以在 Internet 上搜索其他 vimdiff 快捷方式。我发现这个很有用:https://gist.github.com/hyamamoto/7783966


这应该被赞成 x1000 次并被接受为更好的答案。
要快速跳转到下一个冲突,只需搜索 === 。执行“ /===" 并输入
如果使用 :diffget 找到多个匹配项,请参阅此帖子 (stackoverflow.com/questions/51520705/…)。
C
Ciro Santilli Путлер Капут 六四事

替代 vimdiff 的终极合并工具

这有点开玩笑,但这是我在尝试 vimdiff 后最终收敛为 vimer 的内容。

要解决合并冲突,我几乎总是需要查看:

偏僻的

当地的

两个差异: diff BASE REMOTE diff BASE LOCAL

差异基础遥控器

差异基础本地

然后尝试将它们放在一起。

虽然 vimdiff 确实在屏幕上显示 BASE、LOCAL 和 REMOTE:

    +--------------------------------+
    | LOCAL  |     BASE     | REMOTE |
    +--------------------------------+
    |             MERGED             |
    +--------------------------------+

我不知道如何使它清楚地显示我需要的这两个差异,除了通过左右看几次。

此外,本地和远程已经在 git 合并冲突标记中可见,所以我没有从再次显示它们的工具中获得太多。

因此,我创建了自己的微型“difftool”,它实际上显示了我缺少的差异:

~/bin/cirosantilli-mergetool

#!/usr/bin/env bash
BASE="$1"
LOCAL="$2"
REMOTE="$3"
diff --color -u "$BASE" "$LOCAL"
diff --color -u "$BASE" "$REMOTE"
exit 1

GitHub upstream

并安装它:

git config --global mergetool.cirosantilli-mergetool.cmd 'cirosantilli-mergetool $BASE $LOCAL $REMOTE'
git config --global mergetool.cirosantilli-mergetool.trustExitCode true
# If you want this to become your default mergetool.
#git config --global merge.tool 'cirosantilli-mergetool'

现在,当你这样做时:

git mergetool -t cirosantilli-mergetool

它显示了我想要在终端上的两个差异,例如:

--- ./src/dev/arm/RealView_BASE_15560.py        2019-12-27 13:46:41.967021591 +0000
+++ ./src/dev/arm/RealView_LOCAL_15560.py       2019-12-27 13:46:41.979021479 +0000
@@ -994,7 +994,7 @@                                                              
                                       
     def setupBootLoader(self, cur_sys, loc):
         if not cur_sys.boot_loader:                           
-            cur_sys.boot_loader = [ loc('boot_emm.arm64'), loc('boot_emm.arm') ]
+            cur_sys.boot_loader = [ loc('boot.arm64'), loc('boot.arm') ]
         cur_sys.atags_addr = 0x8000000                  
         cur_sys.load_offset = 0x80000000                    

@@ -1054,7 +1054,7 @@                                           
             ]                                                     
                       
     def setupBootLoader(self, cur_sys, loc):
-        cur_sys.boot_loader = [ loc('boot_emm_v2.arm64') ]
+        cur_sys.boot_loader = [ loc('boot_v2.arm64') ]
         super(VExpress_GEM5_V2_Base,self).setupBootLoader(
                 cur_sys, loc)                             
                                                           
--- ./src/dev/arm/RealView_BASE_15560.py        2019-12-27 13:46:41.967021591 +0000
+++ ./src/dev/arm/RealView_REMOTE_15560.py      2019-12-27 13:46:41.991021366 +0000
@@ -610,10 +610,10 @@           
     def attachIO(self, *args, **kwargs):              
         self._attach_io(self._off_chip_devices(), *args, **kwargs)
                                      
-    def setupBootLoader(self, cur_sys, loc):
-        cur_sys.boot_loader = loc('boot.arm') 
-        cur_sys.atags_addr = 0x100                           
-        cur_sys.load_offset = 0       
+    def setupBootLoader(self, cur_sys, boot_loader, atags_addr, load_offset):
+        cur_sys.boot_loader = boot_loader      
+        cur_sys.atags_addr = atags_addr     
+        cur_sys.load_offset = load_offset

所以你可以在这里看到转储到终端的两个差异:

RealView_BASE_15560.py 与 RealView_LOCAL_15560.py

RealView_BASE_15560.py 与 RealView_REMOTE_15560.py

如果差异很大,我将使用 my tmux superpowers 进行搜索。

TODO:要实现 Nirvana,剩下的最后一件事就是只为冲突的大块显示差异。因为如果差异很大但只有很小的块冲突,那么找到它会很烦人。

是的,您确实丢失了 vimdiff 提供的一些快捷方式,但通常解决冲突需要从两个版本中仔细复制粘贴,我可以在带有 git 冲突标记的普通 vim 会话中很好地完成。

vimdiff 运行时观察和比较文件

在我坐下来使用 cirosantilli-mergetool 自动完成我的完美设置之前,我正在这样做以获得我需要的两个差异。

git mergetool 运行 vimdiff 时,如果名为 main.py 的文件存在冲突,git 会为每个版本生成文件,命名为:

main_BASE_1367.py
main_LOCAL_1367.py
main_REMOTE_1367.py

在与 main.py 相同的目录中,其中 1367 是 git mergetool 的 PID,因此是一个“随机”整数,如下所述:In a git merge conflict, what are the BACKUP, BASE, LOCAL, and REMOTE files that are generated?

因此,要查看我想要的差异,我首先使用 git status 找到生成的文件,然后打开新终端并在我关心的文件对之间执行 vimdiff:

vim -d main_BASE_1367.py main_LOCAL_1367.py
vim -d main_BASE_1367.py main_REMOTE_1367.py

此信息与 git mergetool 一起帮助 A LOT 快速了解正在发生的事情!

此外,即使 mergetool 正在运行,您也可以打开文件:

vim main.py

如果您觉得使用更大的编辑器窗口会更容易,请直接在此处进行编辑。

直接跳转合并冲突

虽然 ]c 跳转到 vimdiff 中的下一个差异点,但那里并不总是存在合并冲突。

为了解决这个问题,我的 ~/.vimrc 中有:

# Git Merge conflict
nnoremap <leader>gm /\v^\<\<\<\<\<\<\< \|\=\=\=\=\=\=\=$\|\>\>\>\>\>\>\> /<cr>

直接找到冲突。

git imerge

也许最好的选择是放弃使用 vimdiff 并依赖常规的 vim + git imerge,在 How can I find out which Git commits cause conflicts? 中提到过,因为 vimdiff 的学习曲线很烦人,而且它不能完成我们最需要的功能。


@VonC 是的,我想你赢了! XD
我的 vimdiff 没有显示 MERGED 窗格......只有顶部的 3 个。我怎样才能让它显示合并的窗格?