显示Git存储库的移动/重命名文件

我在一个最近经过重组的大型代码库中工作,许多文件不再是我所期望的。

如何查看在两次提交之间移动/重命名的文件列表?我对这些或其他文件的更改不感兴趣,只是以某种方式将旧名称映射到新名称的视图。

回答

TL;DR:使用git diff --find-renames,可能与--diff-filter=R和 一起使用--name-status

Git 实际上并不存储重命名。它只存储快照。然而,Git 可以比较两个快照,并且——在你的控制和指导下,在某种程度上——推断出以前命名的某个文件path/to/file现在被命名了new/name/of/file:即,尽管这些是不同的文件,但它们在某种程度上也是同一个文件,就像一个忒修斯之船的复制品可能忒修斯之船,即使它不是。

Git 的重命名检测是通过添加-M--find-renames来启用的git diff,或者如果您使用的是现代版本的git diff. 如果您使用的是底层 diff 程序(git diff-treegit diff-index等)之一,则需要显式选项。然后,您让 Git 比较任意两个快照或其他树(例如由 Git 的索引或您的工作树表示的那个),它会尽力找到具有不同名称的“足够相似”的文件,然后会声称此类文件必须已重命名。(请注意,例如,即使我只是删除一个旧文件,然后使用足够相似的新名称创建一个新文件,它也会这样做。)

这里的“足够相似”很棘手。Git 将其所谓的相似性指数计算为百分比,但未明确定义的百分比。1 尽管如此,内容与之前某个文件完全匹配的文件将获得 100% 的相似度指数;完全不共享字节的文件将获得 0% 的相似度索引;和共享一些但不是所有字节的文件将获得介于两者之间的索引,至少在某种模糊的意义上,它表示文件的相似程度。

Git 的默认设置是在发现该文件path/to/file时声明该文件已重命名为new/name/of/file

  1. path/to/file在右侧提交中new/name/of/file根本不存在,而在左侧提交中根本不存在。(两个“边”来自左右提交哈希或您提供的其他说明符git diff,例如,git diff a123456 b789abca123456左侧的提交b789abc与右侧的提交进行比较。)

  2. 内容是相似的。

  3. 没有其他未配对的左右文件配对相似。2

  4. (关键)相似度指数达到或超过您在命令行中指定的阈值

如果启用重命名检测而未设置特定阈值,则默认阈值为 50% 相似。的-M--find-renames选项有一个可选的阈值; 如果给定,则设置最小阈值。

如果没有配对达到所需的阈值,Git 会声明删除左侧文件并新添加右侧文件。否则,从潜在配对列表中删除配对的相似文件,并考虑剩余文件。放置在这些左侧和右侧配对池中的文件名队列的长度也有限制,但在大多数情况下,您不必担心这一点。

通过 diff 引擎运行所有文件以找到正确的对,如果/根据需要计算相似性,等等,Git 然后也会对配对的文件进行 diff ,除非你用--name-only或抑制它--name-status。与重命名检测一样,这个差异只是找到了一种方法来转换左侧文件以匹配右侧文件。这不一定是任何人所做或将要做的:这只是从左侧到右侧的一种方式,最好使用最少的编辑命令。

如果您确实使用了--name-status,Git 将找到每个重命名并在输出中显示它们,状态码为R,后跟导致配对的相似性索引,以及两个名称(左侧和右侧的文件名以斜线)。(这也抑制了内容差异,如上所述。)如果你添加--diff-filter=R到你的git diff命令中,你可以告诉 Git打印重命名的文件。有更多过滤选项可用;有关详细信息,请参阅文档。


1该算法使用增量压缩代码来查找在一个文件针对另一个文件进行增量压缩时将保留的字节序列,以及将简单地作为“新字节”引入的字节序列。如果我没记错的话,这也会删除回车符,以便文本文件中的 CRLF 更改不计入相似性索引,但我可能记错了。在任何情况下,然后将这些除以整体文件大小,以得出相似性指数值。

2为打破僵局,如果新文件名以相同的最终路径组件结尾,则树代码用于将计算的相似性指数加 1 。也就是说,假设我们从发现75%的相似性old/path/namenew/namenew/xyz。这给了我们一个平局:文件是从old/path/nameto重命名的new/name,还是重命名为new/xyz?stringxyz与string不匹配name,但两name部分匹配,所以这部分得到 1% 的奖励。

自从我查看这段代码以来,Git 在内部目录重命名方面变得更加聪明:也就是说,如果它看起来path/to/file变成了path/four/file并且path/to/X变成了path/four/X,它开始看起来就像to- 一个目录名 - must have become X。如果模式对 each 重复path/to,那么确实,目录重命名是表示这一点的方式。这种重命名检测要好得多,而且不需要 1% 的奖金,前提是 Git 实际上提前将所有旧文件和新文件配对。


以上是显示Git存储库的移动/重命名文件的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>