Git 能够跟踪单个函数从一个文件到另一个文件的移动吗? 怎么做?

有好几次,我遇到这样的说法: 如果将一个函数从一个文件移动到另一个文件,Git 可以跟踪它。例如,这个条目说,“ Linus 说如果你将一个函数从一个文件移动到另一个文件,Git 会告诉你整个移动过程中这个函数的历史。”

但是我对 Git 的一些内部设计有一些了解,我不认为这是可能的。所以我在想... 这个说法正确吗?如果是这样,这怎么可能呢?

我的理解是,Git 将每个文件的内容存储为一个 Blob,并且每个 Blob 都有一个全局唯一的标识,这个标识来自其内容和大小的 SHA 散列。然后 Git 将文件夹表示为树。任何文件名信息都属于 Tree,而不属于 Blob,因此,例如,文件重命名显示为对 Tree 的更改,而不是对 Blob 的更改。

因此,如果我有一个名为“ foo”的文件,其中包含20个函数,还有一个名为“ bar”的文件,其中包含5个函数,并且我将其中一个函数从 foo 移动到 bar (结果分别是19和6) ,那么 Git 如何检测我将该函数从一个文件移动到另一个文件呢?

根据我的理解,这将导致2个新的 blobs 存在(一个用于修改的 foo,一个用于修改的 bar)。我意识到可以通过计算 diff 来表示函数从一个文件移动到了另一个文件。但是我不明白这个函数的历史怎么可能与 bar 而不是 foo 联系起来(至少不会自动联系起来)。

如果 Git 实际上查看单个文件的 在里面,并计算 每个函数的 blob 每个函数的 blob(这将是疯狂/不可行的,因为您必须知道如何解析任何可能的语言) ,那么我可以看到这可能是可能的。

那么... 这个陈述是否正确? 如果是正确的,那么我的理解还缺少什么?

11972 次浏览

有一个 git diff会告诉你某些线从 foo中消失,然后在 bar中重新出现。如果在同一次提交中这些文件中没有其他更改,那么很容易发现更改。

一个高智商的 git客户机将能够向您展示行是如何从一个文件移动到另一个文件的。支持语言的 IDE 将能够将这种更改与特定函数相应。

当文件被重命名时,也会发生类似的情况。它只是在一个名称下消失,然后在另一个名称下重新出现,但是任何合理的工具都能够注意到它,并将其表示为一个重命名。

Git 实际上并不跟踪重命名 完全没有。重命名只是删除和添加,仅此而已。任何显示重命名的工具都可以根据这些历史信息重新构建它们。

因此,跟踪函数重命名只是分析每次提交后所有文件的差异的一个简单问题。它没有什么特别不可能的地方; 现有的重命名跟踪已经处理了“模糊”重命名,其中对文件进行了一些更改,并对其进行了重命名; 这需要查看文件的内容。寻找函数重命名也是一个简单的扩展。

然而,我不知道基本的 git 工具是否真的做到了这一点——它们试图保持语言中立,而函数标识非常不保持语言中立。

这个功能的一部分在 git gui blame(+ 文件名)中。它显示了一个文件行的注释,每一行都指出了它的创建时间和最后一次更改的时间。对于跨文件的代码移动,它显示作为创建的原始文件的提交,以及作为 最后的改变添加到当前文件的提交。试试看。

我真正想要的是给 git log作为一个参数,除了文件路径之外,还有一个行号范围,然后它会显示这个代码块的历史。如果文档是正确的,就没有这样的选择。是的,从莱纳斯的陈述来看,我也认为这样的命令应该随时可用。

此功能通过 git blame -C <file>提供。

-C选项驱动 git 尝试在所审查的文件中添加或删除的文本块与在相同的变更集中修改的文件之间找到匹配。额外的 -C -C或者 -C -C -C扩展搜索。

尝试使用 git blame -C进行测试回购,您将看到刚才移动的代码块起源于它所属的原始文件。

来自 git help blame手册页:

在整个文件重命名过程中自动跟踪行的起始位置(目前没有关闭重命名跟踪的选项)。要跟踪从一个文件移动到另一个文件的行,或者跟踪从另一个文件复制和粘贴的行,等等,请参见 -C-M选项。

Git 2.15,git diff现在支持开始,使用 --color-moved选项检测移动的行。它适用于跨文件的移动。

很明显,它适用于彩色终端输出。据我所知,没有选项指示纯文本补丁格式的移动,但这是有意义的。

对于默认行为,请尝试

git diff --color-moved

该命令还接受选项,目前是 nodefaultplainzebradimmed_zebra(使用 git help diff获取最新的选项及其描述)。例如:

git diff --color-moved=zebra

至于 怎么做,它是做,你可以收集一些理解从 这个电子邮件交流的作者的功能