Git,确保移动/重命名文件并保留历史记录的方法

29

我知道有很多看起来相似的问题,所以在问我的问题之前,让我总结一下它们。

我不同意第一个问题的答案,因为我以前做过。第二个问题的答案正是我问这个问题的原因。也就是说,

我发现我经常使用git mv,但有时它被视为移动/重命名,有时它被视为删除+添加。因此,我想知道如何始终将其视为移动/重命名?

这个作为例子,在底部,我们可以看到几个移动/重命名的案例,比如easygenapi/tf-varcaser.go → tf-varcaser.go。请注意,这些移动是跨/在文件夹之间进行的!也就是说,我做到了!

但是,在完全相同的更改日志中,有很多其他情况下git mv被视为删除+添加。再一次强调,我一直在使用git mv。为什么git的行为不同呢?

是否有任何确切的方法可以移动/重命名git文件而保留历史记录?

2个回答

37

简短版本:不

较长的版本: 根据我的经验,只要文件未被修改,Git在检测移动/重命名时非常出色。Git使用启发式方法来尝试定位移动位置。但是如果有几个文件太相似,或者文件在移动过程中被修改导致与原始文件差异太大,则可能会误判。

我发现最好的方法是进行多阶段提交,在一个提交中分离所有移动操作,然后在另一个提交中处理更改。例如...

git mv foo.txt bar.txt
git commit

... modify bar.txt ...

git add bar.txt
git commit

这并不能保证您的移动被正确检测,因为当有多个候选项时,它仍然可能会感到困惑。但是对我而言,它非常有效,并且能够捕捉到大多数情况。


3
谢谢。比起另一个回答声称的“Git不会追踪重命名,就这样。”并得到五个投票,这个回答更有帮助,但却没有得到任何投票。好的,我会确保从现在开始使用“多阶段提交”的技巧来实现重命名跟踪。 - xpt
公正地说,另一个答案更加详细 :) 我自己也点了赞。我添加了这个答案是因为我觉得另一个答案太抽象了,没有直接回答你的问题。 - Kevin Burdett
2
我刚刚尝试了这个,尽管在提交时git显示更改为重命名,但我仍然丢失了文件的所有更改历史记录! - AnotherHowie
1
@AnotherHowie Git默认不显示此内容。您可以使用--follow选项跟踪历史记录(https://git-scm.com/docs/git-log#git-log---follow)。 - Kevin Burdett
1
要跟踪此类移动的文件的完整历史记录,请使用“git log -p --follow bar.txt”。 - ino
公平起见,一个提交应该专注于一件事情,因此更改和重命名最好分为两个提交。 - kh42874

19

Git无法跟踪重命名。这就是事实。它也不能跟踪添加、删除、差异、补丁、移动或任何类型的更改。

Git是基于快照的。每个提交记录了整个项目的快照。只有这些。Git既不知道也不关心这些快照的形成方式。

各种可视化工具显示差异、补丁、添加、删除、移动、重命名等,它们使用启发式方法(另一种说法是“猜测”)在事后推断出这些变化。有时候,它们可能会正确地猜测您所做的事情,有时候则不会。但这无关紧要,因为这只是可视化,它并不是历史记录的一部分。

大多数工具使用某种相似度度量,并推断如果两个文件的相似度超过某个阈值,则发生了重命名。在某些工具中,此阈值是可配置的。在某些工具中,甚至算法也是可配置的。(一个略微相关的例子:`git diff`允许您选择不同的算法来推断文件内的差异。)

由于Git不记录更改,因此可以在后续版本的可视化工具中添加新的更改,这些工具可以从早期提交中推断出这些更改,这些提交是在编写了解新类型更改的新工具之前记录下来的。例如,想象一下一个理解您正在使用的编程语言的语法和语义的工具。它可以将某个提交可视化为重命名子例程并更新每个调用点(即“重命名方法重构”),而不是作为每个文件都有几行已更改的整体。

重命名实际上是一个很好的例子。例如,`git log --follow`使用的重命名检测启发式方法和相似度度量进行了多次改进。我IRC,在开始时根本没有推断重命名,这种功能是后来添加的。如果Git记录更改而不是快照,则根本不可能实现这一点。


2
在这个意义上,Git 不是一个“愚蠢的内容跟踪器”,而是一个“愚蠢的内容”。 - m35

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