如何使用 git 真正显示重命名文件的日志

我对 Git 比较新,以前用过 颠覆(SVN)。

我注意到,如果文件被重命名,大多数图形 Git 前端和 IDE 插件似乎不能显示文件的历史记录。当我吸毒的时候

git log --follow

在命令行上,我可以看到整个重命名日志。

根据 Linus Torvalds (替代连接)的说法,--follow开关是一个“ SVN 菜鸟”; 严肃的 Git 用户并不使用它:

Follow 是一种彻头彻尾的黑客行为,目的只是为了满足那些从未 知道任何关于 亲子关系图或者漂亮的修正图 无论如何。

这不是完全基本的,但是 目前实施的「-跟进」 是一个快速的预处理过程 用螺栓固定在复习步上 逻辑,而不是任何东西 真的很重要。

它被设计成一个“ SVN” 讨好菜鸟,而不是真正的饭桶 “功能性”的东西。这个想法是 你会摆脱(破碎的) 重新命名的思维方式 在大局中的重要性。

当文件被重命名时,Git 的核心用户如何获得文件的历史记录?什么是“真正的”方式来做这件事?

50906 次浏览

我认为,Linus 观点背后的总体驱动力是,Git 的铁杆用户从来不关心“文件”的历史。您将内容放在 Git 存储库中,因为内容作为一个整体具有有意义的历史。

文件重命名是在路径之间移动的“内容”的一种小型特殊情况。您可能拥有一个在文件之间移动的函数,Git 用户可以使用“镐”功能(例如,log -S)来跟踪这些文件。

其他“路径”更改包括合并和分割文件; Git 并不真正关心您考虑重命名哪个文件,以及您考虑复制(或重命名和删除)哪个文件。它只是跟踪您的树的完整内容。

Git 鼓励“整体树”思维,而许多版本控制系统非常以文件为中心。这就是为什么 Git 更多地使用“路径”而不是“文件名”的原因。

我和你面临着同样的问题。尽管我不能给你任何答案,但我相信你可以阅读一下2005年李纳斯写的 这封邮件,它非常中肯,可能会给你一些关于如何处理这个问题的提示:

我要说的是,任何试图跟踪重命名的 SCM 除非由于内部原因(允许有效率地使用) (delta) ,正是因为重命名无关紧要。它们不会帮助你,而且 他们不是你感兴趣的 无论如何

重要的是找到“这是从哪里来的”,还有那个饭桶 建筑在这方面确实做得很好——比其他任何东西都要好得多 在那里

我发现 这篇博文,引用了它,它也可以帮助你找到一个可行的解决方案:

在消息中,Linus 概述了一个理想的内容跟踪系统如何让您找到代码块是如何形成当前形状的。您将从文件中的当前代码块开始,回到历史记录中查找更改文件的提交。然后检查提交的更改,看看您感兴趣的代码块是否被修改,因为更改文件的提交可能不会触及您感兴趣的代码块,而只会触及文件的其他部分。

当您发现在提交之前,该代码块不存在于文件中时,您可以检查提交的深度。你可能会发现这是许多可能的情况之一,包括:

  1. 提交确实引入了代码块。提交的作者是您正在寻找其起源的那个很酷的特性的发明者(或者是引入错误的有罪方) ; 或者
  2. 该代码块在文件中不存在,但是在不同的文件中存在五个相同的副本,所有这些副本在提交后都消失了。通过引入单个 helper 函数提交重构的重复代码的作者; 或者
  3. (作为一个特例)在提交之前,当前包含你感兴趣的代码块的文件本身并不存在,但是另一个具有几乎相同内容的文件确实存在,而你感兴趣的代码块,以及当时存在的文件中的所有其他内容,确实存在于另一个文件中。提交之后就消失了。提交的作者重命名了该文件,同时对其进行了一些小的修改。

在 git 中,Linus 的终极内容跟踪工具还没有以完全自动化的方式存在。但是大部分重要的原料已经可以买到了。

请随时向我们汇报你们的进展。

我注意到,如果文件被重命名,大多数图形 git 前端和 IDE 插件似乎不能显示文件的历史记录

您将很高兴地知道,一些流行的 Git UI 工具现在支持这一点。有很多 Git UI 工具可用,所以我不会全部列出来,但是举个例子:

  • Sourcetree 在查看文件日志时,在左下角有一个复选框“ Follow rename files”
  • TortoiseGit 在左下角的日志窗口中有一个“ follow renames”复选框。

更多关于 Git UI 工具的信息:

在 Linux 上,我已经验证了 SmartGitGitEye在跟踪特定文件的历史记录时能够跟踪重命名。但是, 与 Gitk和 GitEye 不同,SmartGit 显示了一个单独的文件视图和存储库视图(其中包含目录结构,但不包含文件列表)。

注意: git 2.9(2016年6月)将大大改进 git log --follow的“ bug”特性:

犯罪(2016年3月30日) by 赛德尔加博尔(szeder)
(由 朱尼奥 · C · 哈马诺 gitster于2016年4月13日在 承诺26effb8合并)

Defcore: 在重命名检测期间确定相同文件的迭代顺序

如果两个路径‘ dir/A/file’和‘ dir/B/file’具有相同的内容 然后父目录被重命名,例如‘ git mv dir other-dir’,然后 diffcore报告的确切名称如下:

renamed:    dir/B/file -> other-dir/A/file
renamed:    dir/A/file -> other-dir/B/file

(注意这里的反转: B/file -> A/fileA/file -> B/file)

虽然技术上没有错,但这不仅对用户造成困惑, 而且还适用于基于重命名作出决策的 git 命令 资料,例如「 git log --follow other-dir/A/file」 “ dir/B/file”过去的重命名。

这种行为是提交 v2.0.0-rc4 ~ 8 ^ 2 ~ 14的副作用 (diffcore-rename.c: 简化查找确切的重命名,2013-11-14) : 存储源代码的散列映射从同一个 bucket 返回条目,即。 匹配当前目标的源,按 LIFO 顺序。
因此,迭代首先检查“ other-dir/A/file”和“ dir/B/file”,在找到相同的内容和基名后,报告一个确切的重命名。


使用 Git 2.31(Q12021) ,改进了 diffcore的文件级重命名检测。

参见 提交350410f(2020年12月29日)和 提交350410f0的 提交9db2ac5犯下 b970b4e犯罪提交5c72261犯下81c4bf0犯罪提交00b8ccc犯下26a66a6(2020年12月11日)。
(由 朱尼奥 · C · 哈马诺 gitster犯下5ac31b合并,2021年1月25日)

diffcore-rename : 加速 rename_dst设置

由 Elijah Newren 签名

register_rename_src()只是引用 rename_src中传递的对。

相比之下,add_rename_dst()rename_dst做了一些完全不同的事情。
它没有复制传递的对,而是从传递的对中复制第二个 diff_filespec,引用它,然后将 diff_rename_dst.double 字段设置为 NULL
后来,当找到一个配对时,record_rename_pair()通过 diff_queue()分配了一个完整的 diff_filepair,并将其 srcdst字段指向适当的 diff_filespecs

rename_src数据结构的 register_rename_src()rename_dst数据结构的 add_rename_dst()之间的这种对比奇怪地不一致,需要比必要的更多的内存和工作。
[...] 这个补丁加快了安装时间约65% ,最终写回输出队列时间约50% ,导致重新基于几十个补丁的执行时间整体下降了3.5% 。

你要这么做:

git log -M --summary | grep rename | grep BASENAME

把基本名字放进去。

如果结果太多,那么还可以串行 grep每个中间目录的名称。