Git能否真正跟踪单个函数从1个文件到另一个文件的移动?如果是这样,怎么样?

时间:2011-02-05 17:25:11

标签: git git-diff git-log

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

但我对Git的一些引擎盖设计有一点认识,我不知道这是怎么回事。所以我想知道......这是正确的陈述吗?如果是这样,这怎么可能?

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

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

根据我的理解,这将导致存在2个新blob(一个用于修改的foo,一个用于修改的bar)。我意识到可以计算diff以显示函数从一个文件移动到另一个文件。但我不知道有关该函数的历史可能与bar相关而不是foo(不是自动的,无论如何)。

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

所以...声明是否正确?如果它是正确的,那么我理解中缺少什么?

5 个答案:

答案 0 :(得分:26)

此功能通过git blame -C

提供

-C选项驱动git尝试查找正在审阅的文件中添加或删除文本块与在相同更改集中修改的文件之间的匹配。附加-CC或-CCC扩展搜索范围。输入手册页的git help blame。

尝试使用git blame -C进行测试回购,你会看到你刚刚移动的代码块来自它所属的原始文件。

答案 1 :(得分:12)

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

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

对于默认行为,请尝试

git diff --color-moved

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

git diff --color-moved=zebra

至于 如何完成,你可以从this email exchange by the author of the functionality收集一些理解。

答案 2 :(得分:5)

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

我真正想要的是将git log作为一个参数赋予文件路径之外的行号范围,然后它将显示此代码块的历史记录。如果文件是正确的,那就没有这样的选择。是的,根据Linus的陈述,我也认为这样的命令应该随时可用。

答案 3 :(得分:3)

git实际上并不跟踪的重命名。重命名只是删除和添加,这就是全部。任何显示重命名的工具都会根据此历史信息重建它们。

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

我不知道基本的git工具是否真的这样做 - 他们试图保持语言中立,而且功能识别非常不是语言中立。

答案 4 :(得分:2)

git diff会显示某些行从foo消失并重新出现在bar中。如果在同一次提交中这些文件中没有其他更改,则很容易发现更改。

智能git客户端可以向您展示线条如何从一个文件移动到另一个文件。语言感知IDE可以将此更改与特定功能相对应。

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