首页 > 解决方案 > 列出源分支中冲突文件路径的命令是什么

问题描述

当我尝试将 a 合并source branchtarget branch时,git 识别出两个文件存在冲突。但是这两个文件都对应着不同的文件路径source branchtarget branch文件已被移动和修改)。我需要在source branch.

请注意,我可以git diff --name-only --diff-filter=U用来列出合并冲突下的文件,但这会列出目标分支中的路径(因为我签target branch出并拉source branch入其中)。

是否有命令列出源分支中冲突文件的路径?

标签: gitmerge-conflict-resolution

解决方案


当我尝试将“源分支”合并到“目标分支”时,git 识别出两个文件存在冲突。但是这两个文件对应“源分支”和“目标分支”中的不同文件路径(文件已被移动和修改)。

更具体地说,我相信你做到了:

git checkout tgtbranch
git merge srcbranch

并得到:

CONFLICT (rename/delete): orig-filename deleted in HEAD and renamed
to new-filename in A. Version A of new-filename left in tree.
Automatic merge failed; fix conflicts and then commit the result.

(请注意,我使用名为AandB而不是srcbranchand的分支tgtbranch分别复制了此冲突。此外,CONFLICT消息都在一行上。我将其分成两部分仅用于发布目的。)

我需要在“源分支”中获取这个冲突文件的路径。

从上面可以看出,此信息在打印的消息中可用CONFLICTgit merge

不幸的是,这些信息在之后的任何地方都无法获得,至少不能直接获得。您必须保存消息。(如果您没有保存消息,请参阅下面的另外两个替代方案。)CONFLICTCONFLICT

可用的是打印git status的内容:

$ git status --short
UA new-filename
$ git status
On branch B
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)

    added by them:   new-filename

no changes added to commit (use "git add" and/or "git commit -a")

此时我们还可以使用git ls-files --stage来查看 Git索引中的内容:

$ git ls-files --stage
100644 4466f6b253fc4b67ec2749befdd29b7c1f573e36 0       README
100644 923932af0c5b6ddc43c116c648cd793759949b66 3       new-filename

只有新的文件名存在!

如果您没有保存CONFLICT线路,您可以做什么

方法一:重复合并

一种可能是最简单的选择是重复合并。当然,如果您正在解决合并问题,您可能不想弄乱当前索引和您的工作树,这使得这有点棘手。你可以:

  • 克隆存储库,以便您拥有一个具有新索引和工作树的新克隆,或者
  • 使用git worktree add,只要你有git worktree

然后在新的克隆或工作树中重新合并。用于git checkout检查tgtbranch原始克隆(如果您重新克隆)或当前克隆(如果您正在使用git worktree)中的哪些点的提交,然后运行git merge以将提交合并到tgtbranch原始克隆中的哪些点(如果您重新克隆)或当前克隆(如果您正在使用git worktree)。那是:

$ git rev-parse tgtbranch
<hash>
$ git rev-parse srcbranch
<hash>
$ cd /tmp && git clone <path> reclone && cd reclone
$ git checkout <hash> && git merge <hash>

如果您正在使用重新克隆程序,或者:

$ git worktree add --detach <path> tgtbranch
$ cd <path> && git merge srcbranch

如果您正在使用该git worktree方法。

此合并将吐出CONFLICT线。保存!然后删除新的克隆或添加的工作树——它的全部目的是获取CONFLICT行。您的问题现已解决:您拥有原始文件名。

方法2:git diff针对合并基础使用

合并操作在using中检测到重命名。重命名查找的阈值设置为您随参数提供的任何值,如果您未指定数字,则设置为 50%。tgtbranchgit diff --find-renames-X find-renames=number

git diff是两个特定提交之间的差异:Git 自动找到的合并基础提交,以及tgtbranch或的提示提交srcbranch(无论哪个分支 Git “看到”重命名:一个提示提交有重命名,另一个有删除,如与合并基础相比)。

找到合并基础提交。这里有一个并发症,但如果有并发症,它会立即出现。跑:

git merge-base --all srcbranch tgtbranch

例如,就我而言,我得到:

$ git merge-base --all A B
3a5c9ae0669e9969f8986810ea03c5283e0ac693

如果这仅打印出一个提交哈希,那么您很好。如果它打印出多个,您可能应该转而使用方法#1。

现在您知道有一个合并基础,您可以运行两个git diff --find-renames命令。添加--name-status限制输出量的选项。您可能还想添加--diff-filter=RD以仅显示重命名或删除的文件。就我而言,我忽略了这些选项,因为我所做的只是重命名或删除:

$ git diff --find-renames --name-status A...B
D       orig-filename
$ git diff --find-renames --name-status B...A
R064    orig-filename   new-filename

这显示了重命名/删除的文件:原始名称是orig-filename,新名称是new-filename.

这种三点语法A...B(或在您的情况下为tgtname...srcnameand srcname...tgtname)具有 . 独有的特殊含义git diff。在git diff——但不是在其他 Git 命令1中——这意味着在两个指定的提交之间找到一个合并基础,并将其用作 diff 的左侧。使用右手提交说明符作为差异的右侧。 由于只有一个合并基础,这会将合并基础与右侧参数命名的提交进行比较。


1特殊的三点语法适用于所有常见的 Git 差异引擎,包括git diff-tree面向用户的git diff.

在许多版本的 Git 中实现这一点的方式存在一个错误,即如果有多个合并基础,则结果是不可预测的。因此,请确保只有一个合并基础。


边注

如果我们直接检查索引文件,在这种未合并的状态下,我们会找到一条REUC带有原始文件名的记录:

$ od -c .git/index
0000000    D   I   R   C  \0  \0  \0 002  \0  \0  \0 002   ] 264 256   x
0000020    2 270   )   P   ] 264 256   x   2 227 221 220  \0  \0  \0   ~
0000040   \0 320   !   a  \0  \0 201 244  \0  \0 003 351  \0  \0 003 351
0000060   \0  \0  \0   W   D   f 366 262   S 374   K   g 354   '   I 276
0000100  375   қ  **   | 037   W   >   6  \0 006   R   E   A   D   M   E
0000120   \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000140   \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0 201 244
0000160   \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0 222   9   2 257
0000200   \f   [   m 334   C 301 026 306   H 315   y   7   Y 224 233   f
0000220    0  \f   n   e   w   -   f   i   l   e   n   a   m   e  \0  \0
0000240   \0  \0  \0  \0   T   R   E   E  \0  \0  \0 006  \0   -   1    
0000260    0  \n   R   E   U   C  \0  \0  \0   -   o   r   i   g   -   f
0000300    i   l   e   n   a   m   e  \0   1   0   0   6   4   4  \0   0
0000320   \0   0  \0 214 354   + 214 330 360 004   N   O 355  \b 243   '
0000340    4 331 225  \n   W   [ 037 026 216   K   } 367   ? 373 354 364
0000360    [   ^ 364   b 001 371   6   ,   3 370 320                    
0000373

索引必须以幻数开头DIRC,然后是我的两个文件的普通索引条目(我只有两个,如git ls-files --stage输出所示)。但是它有一个TREE记录,我们可以在 offset 附近看到它0000240,然后是REUC下一行的记录。此 REUC 记录是允许git checkout -m重现合并冲突的“撤消”记录。而且,正如我们所见,撤消记录实际上存储了原始文件名

Git 真的应该让我们访问这些信息,也许通过git ls-files或更方便地通过git status. 唉,事实并非如此。


推荐阅读