Git真的可以跟踪单个功能从一个文件到另一个文件的移动吗?如果是这样,怎么办?


73

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

但是我对Git的一些底层设计有些了解,而且我不知道这怎么可能。所以我想知道……这是正确的说法吗?如果可以,这怎么可能?

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

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

据我了解,这将导致2个新的blob存在(一个用于修改的foo,一个用于修改的bar)。我意识到可以计算出差异以表明该功能已从一个文件移至另一个文件。但是我看不到关于函数的历史如何与bar而不是foo关联(无论如何不是自动的)。

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

那么...该陈述正确与否?如果是正确的话,那么我所缺乏的理解是什么?


2
我不认为它跟踪“函数”,而是跟踪“代码块”-因此,如果您有一个30行的函数并将其分成两个15行的函数,它将以与您大致相同的方式进行跟踪移动了整个功能。如果我错了,请有人纠正我。
MatrixFrog 2011年

1
我的理解(这很可能是错误的,这就是我要问的原因)是每个文件最多对应一个Blob。因此,将同一个文件中的一个函子拆分为两个较小的函子,只会导致您的旧Blob替换为新的Blob。如果这是正确的,那么它就不会真正跟踪“代码块”,因为它从不看文件内部。换句话说,它的最小粒度是一个完整的文件。
查理·

1
将GIT与语言解析器集成的有趣想法。我想我需要Delphi语言具有此功能,以便能够将单个* .pas拆分为多个* .pas文件,其中每个pas文件都包含单个对象和实现。然后希望通过对原始文件的更改使这些拆分的文件保持更新。这样它就可以用作“隐身跟踪”;)在主维护者不想进行重组的情况下可以从本地重组工作中受益。
天巴飞翔

Answers:


32

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

-C选项使git试图在正在检查的文件中的文本块的添加或删除与同一变更集中修改的文件之间找到匹配项。其他-C -C,或-C -C -C扩展搜索范围。

用一个测试仓库自己尝试一下,git blame -C您将看到刚移动的代码块起源于它所属的原始文件。

git help blame手册页:

整个文件重命名会自动跟随行的起点(当前没有选项可以关闭重命名跟随)。要跟随从一个文件移动到另一个文件的行,或者跟随从另一个文件复制并粘贴的行,等等,请参阅-C-M选项。


作为测试,我创建了一个包含三个文件的存储库,并向file1添加了一行,然后提交。然后,我将该行移至file2,然后再次提交。然后到file3,并提交。 git blame -C10 file3然后显示在该行添加到file1的位置的第一个提交,但是我真的很想查看移动该行的最新提交(即,将该行移动到file2的提交。)有什么方法可以实现?通过使用git log -S'my interesting line',我得到了一些有用的信息,但仍然不尽人意。
2013年

@Johann似乎普通git blame适合于此。
andrybak

@andrybak已经过了四年,所以我不记得我真正想完成什么。但是git blame只会显示该行的最新更改(无论是否移动),在我的评论中,它要求“移动该行的最新提交”(大概是在完成该行的一些提交之后)。
约翰·

2
-CC而且-CCC似乎不起作用...在这里git version 2.15.0.rc0,我需要-C多次单独通过隔离开关,以使其具有已记录的效果。文档种类至少对此有所暗示。然而,这个答案和其他评论表明这在过去是可行的。嗯
underscore_d

我认为从Git 2.15开始,有一种更好的方法
Inigo

16

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

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

对于默认行为,请尝试

git diff --color-moved

该命令还需要选择,目前是nodefaultplainzebradimmed_zebra(使用git help diff以获得最新的选项及其说明)。例如:

git diff --color-moved=zebra

至于如何完成,您可以从功能作者的这次电子邮件交流中获得一些理解。


1
有没有一种方法可以配置git使其--color-moved默认应用选项?
Eugen Konkov

2
@EugenKonkov是的,用于git config设置diff.colorMoved
Inigo

6

此功能的一部分位于git gui blame(+文件名)中。它显示文件行的注释,每行指示创建时间和上次更改时间。对于跨文件的代码移动,它将原始文件的提交显示为创建,并且将提交添加到当前文件的提交作为最后的更改。试试看。

我真正想要的是给git log文件路径加上行号范围作为参数,然后显示该代码块的历史记录。如果文档正确,则没有此类选项。是的,从Linus的发言中,我也认为这样的命令应该随时可用。


4
我现在才第一次看到gui怪。真好 我开始认为也许这就是Linus的意思。并不是说Git在内部存储了表示该函数已从一个文件移动到另一个文件的信息,而是考虑到Git确实存储了该信息,您可以确定该函数已移动(如git gui blame所做的那样,或者通过diff就像我在文档中提到的那样)。题)。如果是这样,那意味着我最初的理解是正确的,那就是关于提交,树和斑点的全部知识,而Git从不查看文件内部。但这足够的信息使您可以通过分析检测功能的移动。也许。
查理·

是的,我想就是这样。git后端现在对文件内容不执行任何操作(除了可能将它们存储为优化的差异文件),但是前端工具必须执行所有操作。
圣保罗Ebermann

似乎只有一个问题...我如何按时间顺序浏览历史?它位居榜首……

@AgentFriday您可能需要安装分开。例如,在Ubuntu上,它在git-gui软件包中可用。
圣保罗Ebermann

4

Git并不实际跟踪重命名所有。重命名只是删除和添加,仅此而已。显示重命名的任何工具都将从该历史信息中重建它们。

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

我不知道基本的git工具实际上是否这样做-它们试图做到语言中立,并且功能识别在很大程度上不是语言中立的。


我不是指“函数重命名”。相反,我要问的是将一个文件的文本子集移出该文件并移入另一个文件的情况。
查理·

您是正确的,但您的评论尚不清楚,开头的几句话会暗示(我)您误解了Q,请对其进行编辑或其他操作。在主题上,git使用(system?)diff,这就是它所具有的全部功能,它可以“跟踪”函数重命名,但是它并不是特别聪明。基本上只是一行差异,您可以跟踪该行。
Tomas Pruzina 2012年

2

还有git diff那会告诉你,从消失的某些行foo和再现bar。如果在同一提交中这些文件中没有其他更改,则更改很容易发现。

一个知识分子 git客户可以向您展示行是如何从一个文件移动到另一个文件的。语言感知的IDE将能够将此更改与特定功能对应。

重命名文件时会发生类似的事情。它只是以一个名称消失,而以另一个名称重新出现,但是任何合理的工具都可以注意到它并表示为重命名。


2
是否有一个现成的客户端,允许一个人显示功能的历史?
威廉·珀塞尔

1
威廉(William):您应该尝试“ git gui blame path / to / filename.ext”或“ git blame -CCCw path / to / filename.ext”(前者具有非常有用的GUI,而后者则包括更好的动作诊断和副本)。不幸的是,我认为没有办法将“ -CCCw”选项传递给git gui怪。
Mikko Rantalainen

实际上,通过使用高于1.5.3的git并在加载文件后从鼠标右键上下文菜单中选择“执行完全复制检测”,可以使用“ git gui blame”获得“ git blame -CCCw”的结果(我刚刚检查了源文件位于/usr/share/git-gui/lib/blame.tcl)。
Mikko Rantalainen

@MikkoRantalainen上班-CC还是-CCC上班?他们似乎现在似乎确实没有(git版本2.15.0.rc0)
underscore_d

@underscore_d您会收到某种警告消息吗?似乎仍然可以使用git version 2.7.4git help blame了解-C:“当该选项给出3次时,该命令还会在任何提交中从其他文件中查找副本。”
Mikko Rantalainen
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.