如何将我的代码重构到新文件并保留git历史记录?

5

我想将一个大文件的部分内容提取出来,保存git历史记录,这样我就能运行git blame,像重构之前一样查看更改记录。


不太确定你在问什么... 你可以举个例子吗? - Mladen B.
3
这个问题的答案是否能解决您的疑问?如何在重构成多个文件时保留Git历史记录 - andruso
1个回答

3
在Git中,历史记录就是提交。没有文件历史记录。这与大多数其他版本控制系统不同:那些需要跟踪“文件标识”的VCS需要您告知它们新文件path/to/new.ext是从path/to/existing.ext派生的,以便它们可以将新文件的历史与旧文件的历史关联起来。同样,他们需要您告知他们有关文件重命名的信息-尽管某些VCS(例如ClearCase)可以通过简单地充当工作树的文件系统来自动检测重命名。Git不需要任何此类操作,因为它不是这样工作的。1 相反,在Git中,当您比较一个提交(称其为a)与另一个提交(b)时,Git会尝试在比较时间动态地发现是否存在文件a/path/to/name与另一个文件b/some/other/path/to/anothername“相同”。比较的程度和决定这些是相同文件还是不同文件的算法由Git命令决定。git diff命令首先查看实际路径名:如果它们相同,则文件相同,否则它们可能不同。2“可能”部分是重命名检测发挥作用的地方,如果您已经启用它。常规的git diff还具有-C和--find-copies-harder选项,以启用“文件从哪里复制”的检测。使用两次-C(或--find-copies-harder)会将事情设置为查找从a提交中复制的任何文件(这被认为是自动执行的成本太高;通常,只有被视为“修改”的文件才被视为复制源候选项)。
git blame命令略有不同(a和b提交只是每个提交的自动父子关系),但它仍然具有-C选项。它的-C工作方式略有不同:一个-C寻找在提交a和b之间修改的文件中复制的行。使用两次-C会寻找从提交a中复制的这些行,并且使用三个-C标志,它将“更努力地查找副本”:它将查看每个提交中的每个文件以查找复制的代码。
因此,对于大多数情况,您只需在git blame中使用一个-C。如果复制的代码来自未修改的文件,则应使用-C -C。如果您认为某些代码已被删除多个修订版本,然后被恢复,并且您想找到原始来源,则使用三个-C。请注意,git blame-C选项打开了git blame-M选项,后者检测移动代码(因此与git diff-M选项非常不同-文件重命名检测,例如git log --follow3总是启用)。

1这是Git相对于其他VCS的一个很好的优势,因为Git可以检测到人类遗忘的情况,并且还可以在比较“远离”修订时检测重命名。这对Git来说是一个可怕的劣势,因为它必须检测即使人类不会忘记的情况,因此错过了重命名。这对于Git来说是一个巨大的优势,因为未来更智能的算法以更好的方式使用现有数据。简而言之,这是更好和更糟的原因,但最终它只是不同

2对于git diff,您可以使用其-B选项有条件地分开这些自动配对的“相同名称表示相同文件”配对。对于不执行此类配对的git blame,这是不可用的,也不必要。

3git log中启用--follow的代码是一种可怕的hack,基本上仅适用于git blame所需的一个案例。不要尝试在反向顺序的git log中使用--follow


不是说Git不需要那个,而是Git不想做那个,或者更确切地说,当Torvalds设计Git时选择不这样做,认为这并不必要。如果愿意的话,任何版本控制系统都可以使用启发式算法,只是那些明确存储用户意图的系统没有必要这样做,因为它们已经知道了真正发生了什么。启发式算法在简单情况下有效,但在某些情况下注定会失败,无论算法多么聪明(是的,我知道几乎没有版本控制系统存储除文件重命名以外的信息,可悲啊)。 - gbr
@gbr: 是的。Git目前没有这样元数据的地方,尽管它只是一个伪装成VCS的文件系统 :-) ,但可以事后添加它。主要问题在于Git的索引:您需要一个侧面文件或新的索引格式。 - torek
当然,有各种方法可以添加它,但我认为维护者们不太愿意接受这种大的更改。 - gbr
1
@torek 感谢您的详细解释,非常有帮助,我也点了赞。Git blame 很有用,我们都使用它。但回到我的问题,如何将文件 A 的一部分实际复制到新文件 B 中,同时使 B 中保留 git blame 信息呢? - valk
1
@valk:基本上,只需移动代码并希望git blame找到代码移动。如果没有找到,请尝试较低的-M和/或-C值。如果这些都不起作用,您可以尝试编写新的算法来进行git blame... - torek
@valk 你最好确保你的更改是“原子性”的。如果你正在复制一个文件并对其进行更改,请在一个提交中复制它,在另一个提交中进行更改。由于Git使用百分比变化来进行启发式分析,这将有助于确保它可以检测到更改。 - DylanYoung

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