Git在重构过程中如何跟踪历史记录?

17

我很清楚Git如何支持文件移动:由于它使用文件哈希,因此“添加”的文件很容易被检测出与“删除”的文件相同。

我的问题是关于重构的:考虑Java,包声明会更改,因此文件内容将不同。在这种情况下,Git如何确定“添加”的文件与“删除”的文件共享历史记录?它会检查“最相似的内容”,假设我只进行了一些小修改,还是采用类似的非确定性解决方案?


等一下...就在我面前的那本《Apache Maven》书籍上有一个作者的名字让人感到非常熟悉... - VonC
我知道了!我仍然试图忘记最近在JUG上看到的“C'est pas sorcier”中疯狂的Fred谈论maven3迁移的画面...好时光;)欢迎来到SO。 - VonC
1个回答

20

Git FAQ所述,它将基于一种启发式方法检测相似内容。

Git必须与许多不同的工作流程进行交互,例如,某些更改可能来自补丁,其中重命名信息可能无法使用。依赖显式重命名跟踪会使得不可能合并两个树,除了一个是作为补丁(创建/删除),另一个使用其他启发式方法完成。

另外,跟踪重命名实际上只是跟踪内容在树中移动的特殊情况。在某些情况下,您可能更感兴趣的是查询何时添加或将函数移动到不同的文件中。通过仅依赖在需要时重新创建此信息的能力,Git旨在提供更灵活的跟踪树如何变化的方式。

但这并不意味着Git不支持重命名。
Git中的差异机制支持自动检测重命名,这由"-M"开关打开,适用于git-diff-*系列命令。
重命名检测机制被git-log(1)和git-whatchanged(1)使用,因此,例如,"git log -M"将给出具有重命名信息的提交历史记录。
Git还支持跨重命名的有限合并形式。
两个分配责任的工具git-blame(1)git-annotate(1)都使用自动重命名检测代码来跟踪重命名。


git log会提供关于该启发式的一些详细信息:

-B[<n>][/<m>]

将完全重写的更改分成删除和创建的一对。这有两个目的:
- 它使得文件的完全重写不是一个由删除、插入和匹配文本上下文组成的系列,而是一个由所有旧内容的单个删除和所有新内容的单个插入组成的系列,并且数字m控制了此方面的-B选项(默认为60%)。
-B/70%指定结果中应该保留少于30%的原始文本以便Git认为它是完全重写(否则生成的补丁将是一个由删除、插入和上下文行混合组成的系列)。 - 当与-M一起使用时,一个完全重写的文件也被视为重命名的源文件(通常,-M只把消失的文件视为重命名的源文件),数字n控制了此-B选项的方面(默认为50%)。
-B20%指定相对于文件大小的增加和删除变化达到20%或更多的更改可被拾取为可能来源于另一个文件的重命名的源文件。
-M[<n>]

如果生成差异,检测并报告每个提交的重命名。要在遍历历史时跨越重命名后跟踪文件,请参见--follow。如果指定了n,则它是相似性索引的阈值(即添加/删除与文件大小相比的量)。例如,-M90%表示git应该将删除/添加对视为重命名,如果文件的超过90%没有更改

其他参考资料:


注意:从Git 2.18(2018年第二季度)开始,git status现在应该显示您移动/重命名文件时的重命名(而不是删除/添加文件)。
请参见“如何告诉Git它只是一个不同的名称相同目录”。

1
好的,但是对于原始情况是否有简单的通俗易懂的答案呢?如果我通过将Java类移动到不同的包目录来重构它,以便在(例如)100行中修改了一行表示Java包,那么默认的日志和责任会识别移动/重命名吗?我在GitHub / BitBucket上仍然能看到正确的责任吗?换句话说,如果我执行这个(非常,非常,非常)常见的活动,所有默认设置的东西都会“正常工作”吗? - Garret Wilson
@GarretWilson 是的,在本地端会有这个功能(你可以调用 git log --follow (参考 https://dev59.com/4XE95IYBdhLWcg3wbtXO)或是 git blame -C)。但是这个功能不会在 Git 托管服务器端被执行(GitHub:https://dev59.com/Cm035IYBdhLWcg3wErvM#5647721)(或是 BitBucket:https://bitbucket.org/site/master/issues/589/file-history-should-follow-copies-and)。 - VonC
1
感谢您快速提供链接进行澄清!看来我没有选择,只能使用Git了... :) - Garret Wilson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接