eftshift0的回答是正确的(我已经点赞了)。但这是为什么,还有一个可能出现问题的地方。
在大多数情况下,git show commit -- path
是正确的,并会显示:
- 指定提交的日志消息,以及
- 与其父级进行比较而生成的该特定文件的补丁。
该补丁与 git diff commit^1 commit -- path
所生成的补丁相同。 (请注意,此处的^1
后缀为文字文本,而commit
部分是要替换的哈希ID。 后缀语法的含义是“查找第一个父提交”。 您可以将此后缀添加到大多数提交选择器中,但不能用于使用某些搜索模式的选择器。请参见 gitrevisions文档。)
有两个重要的例外。只有其中一个适用于此处,因为git blame
通常不会追溯合并,它试图追踪进入合并的更改的源。 但我还是想提一下它,因为git show
在合并时的行为很有趣。:-)
如果使用git show
查看合并提交,则默认情况下将显示组合差异(“所有父项”与合并提交的内容进行比较)。在这种情况下,您可能希望直接回退到git diff
,以便您可以指定要比较的父项(^1
、^2
,如果这是八达通合并,则可以使用更多级)。原因是组合差异有意忽略与一个父提交中的版本匹配的任何文件:这意味着从该点起,存储库中的任何内容都来自于合并的两个(或N> 2)“方面”中的一个。
使用git blame
时,您正在寻找“谁改变了什么”。做合并的人通常不是做出更改的人,所以您应继续“穿过合并”以找到真正做出更改的人。
第二个例外是导致您在此处出现问题的原因,它实际上更多地涉及文件在开发过程中重命名时git blame
的工作方式。
当使用git blame
分析更改文件时,例如include/svl/itemset.hxx
,它会从指定的提交开始一次回退一个提交。如果您没有选择自己的起始点,则从当前提交HEAD
开始。然后,它查看父提交(例如,通过git show
)。例如,如果当前提交922e935c8812
是普通提交,其父提交为22c6554c98e2
,则它将比较提交922e935c8812
和22c6554c98e2
。如果22c6554c98e2
有一个相同的文件名,那很可能是同一个文件...但如果没有,Git会尝试确定22c6554c98e2
中哪个文件与include/svl/itemset.hxx
是相同的文件。
在这种情况下,在提交b9337e22ce1d
处发生了这件事。在提交b9337e22ce1d
中有一个名为include/svl/itemset.hxx
的文件,但在提交b9337e22ce1d^
或f4e1642a1761
中,该文件被命名为svl/inc/svl/itemset.hxx
。当从提交b9337e22ce1d
回退到提交f4e1642a1761
时,Git检测到此重命名,并且git blame
会将新名称从提交f4e1642a1761
带回提交d210c6ccc3
。
但是,当您运行git show d210c6ccc3
时,Git直接跳转到d210c6ccc3
(及其父级7f0993d43019
)。它不再知道在HEAD
中名为include/svl/itemset.hxx
的文件在d210c6ccc3
中被命名为svl/inc/svl/itemset.hxx
。因此,您必须发现这一点,并将早期的名称传递给Git。
您可能会想知道如何找到这个。答案是使用git log --follow
。对于git log
,--follow
代码并不好,但它与git blame
使用相同的代码,因此至少产生相同的答案。以下是我的操作:
$ git log --oneline --follow --name-status include/svl/itemset.hxx
00aa9f622c29 Revert "used std::map in SfxItemSet"
M include/svl/itemset.hxx
afaa10da2572 make SfxItemSet with SAL_WARN_UNUSED
M include/svl/itemset.hxx
[snip]
a7724966ab4f Bin comments that claim to say why some header is included
M include/svl/itemset.hxx
b9337e22ce1d execute move of global headers
R100 svl/inc/svl/itemset.hxx include/svl/itemset.hxx
还有一个更早的重命名。以下是另一种更短的方法,用于查找仅重新命名的内容:
$ git log --oneline --follow --diff-filter=R --name-status include/svl/itemset.hxx
b9337e22ce1d execute move of global headers
R100 svl/inc/svl/itemset.hxx include/svl/itemset.hxx
e6b4345c7f40 #i103496#: split svtools in two libs, depending on whether the code needs vcl or not
R100 svtools/inc/svtools/itemset.hxx svl/inc/svl/itemset.hxx
如果d210c6ccc3
出现在e6b4345c7f40
之前,您将需要使用更早的路径名称。但是,提交d210c6ccc3
是e6b4345c7f40
的后代,而不是祖先。
1当合并发生更改时,在根本意义上,这确实需要同时跟随两个(或所有)输入提交。然而,Git目前无法做到这一点:git log --follow
和git blame
实际上只遍历最终版本的文件“来自”的任一父级。也就是说,如果我们查看包含合并文件F
的典型合并提交M
,则有两个输入M^1:F
和M^2:F
,以及一个输出M:F
。如果M:F
与M^1:F
相同,我们应该完全忽略M^2:F
:所有M:F
的内容都来自提供M^1:F
的人。如果M:F
与M^2:F
相同,我们应该完全忽略M^1:F
:所有M:F
的内容都来自提供M^2:F
的人。
请注意,这仅适用于每次一个文件,即使如此,它只在文件完全匹配两个输入之一时才起作用。否则,我们应该查看组合差异,以查看文件如何从两个输入中修改。这就是组合差异和历史简化背后的逻辑。但在某些罕见的情况下,它过于简化,因此有时会出错。
d210c6ccc3
修改了svl/inc/svl/itemset.hxx
,而文件include/svl/itemset.hxx
则被4b3a535ae3
修改。但是你的提交+文件组合在责任记录中没有出现。也许中间有重命名或符号链接之类的东西? - rodrigoinclude/svl
目录已经被重命名为svl/inc/svl
(或者反过来)。只需使用备用名称即可查看差异。 - rodrigo