Git可以真正追踪从一个文件到另一个文件的单个函数的移动吗? 如果是这样,怎么样?

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

但是我对Git的一些底层devise有一点点的认识,我不明白这是可能的。 所以我想知道…这是一个正确的说法? 如果是这样,这怎么可能?

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

所以,如果我有一个名为“foo”的文件,其中包含20个函数,而一个名为“bar”的文件包含5个函数,我将其中一个函数从foo移动到bar(分别导致19和6) Git如何检测到我将该函数从一个文件移动到另一个文件?

从我的理解,这将导致存在2个新的斑点(一个为修改的foo和一个为修改的栏)。 我意识到可以计算一个差异来显示函数从一个文件移动到另一个文件。 但是我不明白这个函数的历史如何可能与bar而不是foo(不是自动的)相关联。

如果Git真的要查看单个文件,并为每个函数计算一个blob (这将是疯狂/不可行的,因为你必须知道如何parsing任何可能的语言),那么我可以看到这是可能的。

所以……这个陈述是否正确? 如果这是正确的,那么我的理解缺乏什么?

这个function是通过git blame -C来提供的

-C选项可以使git试图在正在审查的文件中添加或删除大块文本和在相同的变更集中修改的文件之间find匹配。 其他-CC或-CCC扩展search。 键入git帮助指导手册页。

试着用git blame C来testing你自己,你会发现你刚刚移动的代码块是源于它所属的原始文件。

有一点这个function是在git gui blame (+文件名)。 它显示了一个文件行的注释,每个文件都指明了它的创build时间和最后一次更改的时间。 对于跨文件的代码移动,它显示原始文件作为创build的提交,以及作为最后更改添加到当前文件的提交。 尝试一下。

我真正想要的是给git log作为一些参数的行号范围文件path,然后它会显示这个代码块的历史。 没有这样的select,如果文件是正确的。 是的,从Linus的陈述我也会认为这样的命令应该是现成的。

git实际上并不实际跟踪重命名。 重命名只是一个删除和添加,就是这样。 任何显示重命名的工具都可以从这个历史信息中重build它们。

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

我不知道基础的git工具是否真的做到了这一点 – 他们试图做到语言中立,function识别非常不中立。

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

一个智能的git客户端将能够告诉你如何从一个文件移动到另一个文件。 一个语言感知的IDE将能够将这个改变与一个特定的function相对应。

一个非常类似的事情发生时,一个文件被重命名。 它只是消失在一个名下,并在另一个名下重新出现,但是任何合理的工具都能够注意到它,并代表重命名。

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

显然,它对于彩色terminal输出是有效的。 据我所知,没有select以纯文本格式表示移动,但这是有道理的。

对于默认行为,请尝试

 git diff --color-moved 

该命令还采取选项,目前是nodefaultplainzebradimmed_zebra (使用git help diff获取最新的选项和他们的描述)。 例如:

 git diff --color-moved=zebra