重命名/移动修改后的文件后,Git不会跟踪历史记录

问题:我需要更改存储库中整个目录的位置.为此,我使用git mv,如果需要,我将include头名称更改为当前正确的名称.问题是当我在一次提交期间执行两个操作时.在那种情况下,文件历史记录丢失(git认为这是删除和创建新文件).

解决方法:
如果我将这些操作拆分为单独的提交,则不会发生此问题.

问题再次出现:但是,即使我使用上述解决方案,问题也会在与master合并时返回.我有义务只使用no-ff合并.在这种情况下,对master分支的新提交是由两个提交的更改组成的……历史记录无论如何都没有正确跟踪.

另一个丑陋的解决方法:我可以单独提交这些提交来掌握.我无法提供无法编译的代码但是如果我将它从构建过程中排除它可能是可行的……但它很丑陋而且错误……

我想知道是否有更好的解决方案来解决这个问题.

最佳答案 如果您熟悉几乎任何其他版本控制系统(VCS),则很难理解
Git对文件历史记录的作用.

事实是Git没有文件历史记录.它在VCS中可能是独一无二的(尽管我没有使用过许多更神秘的VCS).它最近的堂兄Mercurial确实有文件历史记录:Mercurial调用清单时,为Mercurial添加的每个文件都分配了一个唯一的编号,这决定了文件的标识.如果更改文件的名称或整个文件的目录,则会保留其标识,因为清单中存在此信息.

Git完全废除了这个概念. Git根本没有文件历史记录. Git只提交了.

每个提交都存储源树的完整快照.每个提交也有一些父提交,通常只有一个.这更像是传统的基于提交的VCS:可以跟踪各种提交,或查看文件历史记录.但由于Git没有文件历史记录,因此它唯一拥有的是提交历史记录.

为了实现git log –follow和其他有用的项目,Git提供的是重命名检测,而不是文件历史记录. Git可以查看任何一个特定的提交,并将该提交与其父提交或合并提交进行比较,并与其所有父提交进行比较.当它进行这种比较时,它提供了检测通过该提交重命名的文件的选项:在父项中具有一个名称但在子项1中具有不同名称的文件.

当比较两个任意提交时,Git甚至提供了这种重命名检测,这些提交不仅仅是父和子.运行:

git diff --find-renames $hash1 $hash2

比较两个提交,并且只要有一个“具有路径a / b / c.txt在$hash1中的文件确实看起来很像$hash2中的路径为d / e / f.log的文件”,Git就可以声称该文件已重命名(然后也可能已修改).但重要的是要记住,Git只是合成了将第一个文件转换为第二个文件的方法.两个提交中的两个实际文件以永久方式存储.它们永远不会被更改:只要存在这些提交,这两个文件就会以这种方式存储在这两个提交中.除非你想要它们,否则这两个文件实际上并不相关. Git通过比较它们的相似性来“找到”重命名.给Git一组不同的“相似性”标准 – 例如-M75%而不是-M50% – Git可以选择一组不同的“足够相似”的文件.

任何提交都没有发生任何事情.他们都被冻结了.但是使用一组不同的“重命名阈值”值,“中断阈值”等等,Git可能会配对不同的路径名.给定–no-renames,Git永远不会配对不同的路径名(虽然它仍然会配对相同名称的文件).

(这种动态重命名检测在合并时非常重要,因为merge会运行两个git diff –find-rename操作,从合并基础提交到正在合并的两个分支提示提交中的每一个.如果Git找到一个重命名,它相信它.如果它没有找到重命名,它认为在提示中删除了基本文件,并且在提示中创建了一个不同的文件.您可以控制重命名阈值,但是您不能设置中断或复制阈值,至少在今天的Git版本中,2.15.)

1对于合并提交,这一点的含义不太清楚,因为有多个父项:文件child.txt在父#1中的名称为p1.txt,在父#2中的名为p2.txt是什么意思?传统的VCS具有确定文件标识的独特内部编号系统,在这里给出了明确的含义,但在实践中,这种意义并不总是有用,而Linus Torvalds在这里的选择,完全取消了这个概念,可能已经在部分是对此的反应.

点赞