在我的功能分支中,我有大约 50 个提交。在第一次提交中,我创建了一个文件,该文件在后续提交中进行了大量修改。我现在意识到将该文件存储在不同的目录中会更好,所以我想回到第一次提交并在正确的位置开始创建它,以保持历史记录干净。我可以通过编辑第一个提交并移动文件来使用交互式 rebase 来做到这一点,但是随后所有涉及该文件的提交都会产生冲突,我必须手动解决这些冲突。有没有办法告诉每次提交文件已被移动,以便他们自动将更改应用到正确的位置?
使用git filter-branch
. 您可以使用--index-filter
for speed 但这更难使用;只需 50 次提交,使用--tree-filter
它会慢得多但更容易使用:
git filter-branch --tree-filter <fill this in> --tag-name-filter cat -- --all
您通常应该在原始存储库的副本(克隆)上执行此操作,因为很容易弄乱过滤器分支,而从中恢复的简单方法是删除副本并重新开始。
一旦它的工作原理,删除所有refs/original/
在描述名称的git filter-branch
文件。存储库最终会去膨胀(过滤器分支的大小会暂时增加一倍)。
在 Git 中,历史是(是?)提交。要更改历史记录,您需要将旧提交(提供旧历史记录)复制到新的不同提交(提供新历史记录)。因此,您的目标是将所有 50 次提交替换为相同的新提交,只是将文件重定位到其他路径。
正如您所提到的,您可以使用交互式 rebase来做到这一点,但这很痛苦:rebase 通过将每个提交到副本转换为变更集(通过将该提交与其父项进行比较,以查看更改的内容)然后应用相同的更改来工作到一些现有的提交。
有一个更重的命令,git filter-branch
,其目的是在应用某种提交修饰符的同时复制提交。它有很多选择,因为它本质上很慢;但从根本上说,它包括:
然后,从最根(最古老/最祖先)提交开始:
最后,在对每个要过滤的提交执行上述操作后,循环遍历您告诉它要更改的所有引用(主要是分支名称,但如果使用 a ,则标记名称也是如此--tag-name-filter
):
refs/whatever
为refs/original/refs/whatever
。refs/whatever
使用在地图中找到的新哈希创建一个新的。在此过程结束时,您拥有所有原始提交(refs/original
用于引用它们)以及所有新提交(使用分支名称)。
如果您只有一个分支名称(并且没有标签),那么您需要提供的唯一名称就是这个分支名称,可能是master
,但--all
会告诉 Git 查看所有引用,并--tag-name-filter cat
告诉 Git 它应该对标签名称,因为它更新它们毕竟是不做任何改变。
该--tree-filter
指导git filter-branch
,对于第1步(提取提交),它应该做的充分和完全的提取,到一个临时目录git filter-branch
将建立在其自身。(其他过滤器选项尝试使用更快的仅提取到临时索引的技巧。)您提供的一个或多个命令tree-filter
在此临时目录中运行,因此如果您需要做的只是重命名文件,命令:
mv old-relative-path new-relative-path
就足够了(假设是 Unix/Linux-ish 系统)。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句