git,确保 move/rename 文件同时保留历史记录的方法
git, sure-fire way to move/rename files while keeping the history
我知道有 "lots" 个看起来相似的现有问题,所以让我在提问之前总结一下。
- Is it possible to move/rename files in git and maintain their history? 的答案是 "it is not possible"。
- git moving file while keeping history的结论是,Git可能将其视为一步棋,或者可能仍然 把它当做删除+添加。
第一个答案,我不同意,因为我以前做过。第二个的答案是我问这个问题的原因。即,
我发现我一直在做git mv
,但有时被当做move/rename,有时被当做删除+添加。因此,我想知道如何让它始终是 move/rename?
以this one为例,在底部,我们可以看到几个move/rename的情况,比如easygenapi/tf-varcaser.go → tf-varcaser.go
。请注意,此类移动是 across/between 文件夹 !也就是说,我做了!
但还有很多其他情况git mv
被视为删除+添加,显示在完全相同的更改日志中。同样,我一直在做 git mv
。为什么 git
表现不同?
有没有万无一失的方法来move/renamegit文件同时保留历史记录?
Git 不跟踪重命名。时期。它也不跟踪添加。或者删除。或差异。或补丁。或移动。或者任何形式的改变,真的。
Git 是基于快照的。每次提交都记录了整个项目的快照。而已。 那个快照是如何的,Git既不知道也不关心。
差异、补丁、添加、删除、移动、重命名等由各种可视化工具显示,这些工具事后使用启发式方法推断它们(这是 "guessing" 的另一种说法)。有时,他们可能猜对了您所做的事情,有时则没有。不过没关系,因为它只是可视化,它不以任何方式、形状或形式成为历史的一部分。
大多数工具使用某种形式的相似性度量,如果两个文件的相似性大于某个阈值,则推断发生了重命名。在某些工具中,此阈值是可配置的。在某些工具中,甚至算法也是可配置的。 (一个稍微相关的示例:git diff
允许您在 文件中推断 差异的不同算法之间进行选择。)
由于 Git 不记录更改,因此可以在更高版本的可视化工具中添加新的更改,这些更改可以从较旧的提交中推断出这些更改,这些更改在理解新类型的新工具之前记录下来的变化甚至被写入。想象一下,例如,一个工具可以理解您正在使用的编程语言的语法和语义。它可以将某个提交可视化为不是一大堆文件,每个文件都有几行更改,而是作为重命名子例程并更新每个调用站点的单个更改(即 Rename Method Refactoring ).
重命名实际上就是一个很好的例子。例如,git log --follow
使用的重命名检测启发式和相似性指标已多次改进。 IIRC,一开始根本没有推断重命名,后来添加了该功能。如果 Git 记录更改而不是快照,这根本不可能。
tl;博士;没有
更长的版本:
根据我的经验,只要文件未修改,git 就非常擅长检测 move/rename。 Git 使用启发式方法尝试并定位移动。它可能被几个太相似的文件所愚弄,或者如果文件在移动过程中被修改,导致它与原来的太不相似。
我发现执行此操作的最佳方法是进行多阶段提交,将所有移动分成一个提交,然后在另一个提交中进行更改。例如...
git mv foo.txt bar.txt
git commit
... modify bar.txt ...
git add bar.txt
git commit
它不能保证你的动作被正确检测到,因为当有多个候选时它仍然会混淆。然而,它对我来说效果很好,并且可以捕捉到大多数情况。
我知道有 "lots" 个看起来相似的现有问题,所以让我在提问之前总结一下。
- Is it possible to move/rename files in git and maintain their history? 的答案是 "it is not possible"。
- git moving file while keeping history的结论是,Git可能将其视为一步棋,或者可能仍然 把它当做删除+添加。
第一个答案,我不同意,因为我以前做过。第二个的答案是我问这个问题的原因。即,
我发现我一直在做git mv
,但有时被当做move/rename,有时被当做删除+添加。因此,我想知道如何让它始终是 move/rename?
以this one为例,在底部,我们可以看到几个move/rename的情况,比如easygenapi/tf-varcaser.go → tf-varcaser.go
。请注意,此类移动是 across/between 文件夹 !也就是说,我做了!
但还有很多其他情况git mv
被视为删除+添加,显示在完全相同的更改日志中。同样,我一直在做 git mv
。为什么 git
表现不同?
有没有万无一失的方法来move/renamegit文件同时保留历史记录?
Git 不跟踪重命名。时期。它也不跟踪添加。或者删除。或差异。或补丁。或移动。或者任何形式的改变,真的。
Git 是基于快照的。每次提交都记录了整个项目的快照。而已。 那个快照是如何的,Git既不知道也不关心。
差异、补丁、添加、删除、移动、重命名等由各种可视化工具显示,这些工具事后使用启发式方法推断它们(这是 "guessing" 的另一种说法)。有时,他们可能猜对了您所做的事情,有时则没有。不过没关系,因为它只是可视化,它不以任何方式、形状或形式成为历史的一部分。
大多数工具使用某种形式的相似性度量,如果两个文件的相似性大于某个阈值,则推断发生了重命名。在某些工具中,此阈值是可配置的。在某些工具中,甚至算法也是可配置的。 (一个稍微相关的示例:git diff
允许您在 文件中推断 差异的不同算法之间进行选择。)
由于 Git 不记录更改,因此可以在更高版本的可视化工具中添加新的更改,这些更改可以从较旧的提交中推断出这些更改,这些更改在理解新类型的新工具之前记录下来的变化甚至被写入。想象一下,例如,一个工具可以理解您正在使用的编程语言的语法和语义。它可以将某个提交可视化为不是一大堆文件,每个文件都有几行更改,而是作为重命名子例程并更新每个调用站点的单个更改(即 Rename Method Refactoring ).
重命名实际上就是一个很好的例子。例如,git log --follow
使用的重命名检测启发式和相似性指标已多次改进。 IIRC,一开始根本没有推断重命名,后来添加了该功能。如果 Git 记录更改而不是快照,这根本不可能。
tl;博士;没有
更长的版本: 根据我的经验,只要文件未修改,git 就非常擅长检测 move/rename。 Git 使用启发式方法尝试并定位移动。它可能被几个太相似的文件所愚弄,或者如果文件在移动过程中被修改,导致它与原来的太不相似。
我发现执行此操作的最佳方法是进行多阶段提交,将所有移动分成一个提交,然后在另一个提交中进行更改。例如...
git mv foo.txt bar.txt
git commit
... modify bar.txt ...
git add bar.txt
git commit
它不能保证你的动作被正确检测到,因为当有多个候选时它仍然会混淆。然而,它对我来说效果很好,并且可以捕捉到大多数情况。