在源分支中列出冲突文件路径的命令是什么?

3
当我试图将一个“源分支”合并到“目标分支”时,Git 发现两个文件冲突。但是这两个文件对应于“源分支”和“目标分支”中不同的文件路径(该文件已被移动和修改)。我需要获取“源分支”中此冲突文件的路径。
请注意,我可以使用“git diff --name-only --diff-filter=U”列出合并冲突下的文件,但是这会列出目标分支中的路径(因为我检出了“目标分支”并将“源分支”拉入其中)。
是否有一种命令可以列出源分支中冲突文件的路径?
1个回答

1
当我尝试将“源分支”合并到“目标分支”时,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.

(请注意,我使用了分支名A和B来复制此冲突,而不是分别使用srcbranch和tgtbranch。另外,CONFLICT消息都在一行上。 我将其拆分为两个目的)。

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

正如您从上面所看到的,这些信息可以在git合并打印的CONFLICT消息中找到。
不幸的是,此信息至少在之后任何地方都不直接可用。 您必须保存CONFLICT消息。(如果您没有保存CONFLICT消息,请参见下面的两个其他选择。)
可用的是git状态打印的内容:
$ 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行,可以采取的措施

方法1:重复合并

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

  • 克隆存储库,以便您拥有一个带有新索引和工作树的新克隆,或者
  • 使用git worktree add,前提是您已经安装了git worktree

然后在新的克隆或工作树中执行重新合并。使用git checkout检出原始克隆(如果您重新克隆)或当前克隆(如果您正在使用git worktree)中tgtbranch指向的提交,然后运行git mergetgtbranch指向的提交合并到原始克隆(如果您重新克隆)或当前克隆(如果您正在使用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对比合并基础
合并操作使用git diff --find-renames检测到了tgtbranch中的重命名。重命名查找的阈值设置为您使用-X find-renames=number参数提供的任何内容,如果未指定数字,则为50%。
这个git diff是两个特定提交之间的差异:Git自动找到的合并基础提交和tgtbranchsrcbranch的末尾提交(Git在其中“看到”重命名的分支:一个末尾提交进行了重命名,另一个进行了删除,与合并基础相比)。

找到合并基础提交。这里有一个复杂的问题,但如果存在该问题,它将立即显示出来。运行:

git merge-base --all srcbranch tgtbranch

例如,在我的情况下,我得到:

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

如果只打印出一个提交哈希值,那就没问题。如果打印出多个,那你可能应该使用第一种方法。

现在你知道只有一个合并基,你可以运行两个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...srcnamesrcname...tgtname),在git diff中具有一种特殊的意义,而其他Git命令则没有。它的意思是:寻找两个指定提交之间的合并基础,并将其用作差异的左侧。使用右侧提交说明符作为差异的右侧。由于只有一个合并基础,因此这会将合并基础与右侧参数命名的提交进行比较。

1特殊的三个点语法适用于所有常见的Git diff引擎,包括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记录,我们可以在偏移0000240附近看到,接着是在下一行的REUC记录。这个REUC记录是一个“撤消”记录,使得git checkout -m能够再现合并冲突。正如我们所见,撤消记录实际上存储了原始文件名。
Git确实应该让我们访问这些信息,可能通过git ls-files或者更方便的是通过git status。但是不幸的是,它没有。

我的合并冲突信息中只有 CONFLICT (content): <new-file-name>。另外请注意,合并提交消息保存在 .git/MERGE_MSG 文件中,该文件不包含旧文件名(对于我的情况)。 - Goutam Lavudiya
“CONFLICT(content)”表示旧文件名不存在,无法为此文件进行重命名。(在重命名情况下,保存的提交消息中不包含旧的文件名,因此这并不能说明问题。) - torek

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