git - 在 git 历史记录中移动文件

标签 git git-rewrite-history

在我的功能分支中,我有大约 50 次提交。在第一次提交中,我创建了一个文件,该文件在后续提交中进行了大量修改。我现在意识到最好将该文件存储在不同的目录中,因此我想返回到第一次提交并在开始的正确位置创建它,以保持历史记录干净。我可以通过编辑第一个提交并移动文件来使用交互式 rebase 来完成此操作,但随后接触该文件的所有后续提交都会产生我必须手动解决的冲突。有没有办法告诉每个提交文件已被移动,以便他们自动将更改应用到正确的位置?

最佳答案

TL;DR

使用git filter-branch。您可以使用 --index-filter 来提高速度,但这比较难使用;如果只有 50 次提交,请使用 --tree-filter ,它速度慢得多,但更容易使用:

git filter-branch --tree-filter <fill this in> --tag-name-filter cat -- --all

您通常应该在原始存储库的副本(克隆)上执行此操作,因为很容易弄乱过滤器分支以及从中恢复恢复的简单方法就是删除副本并重新开始。

一旦生效,请按照 the git filter-branch documentation 中所述删除所有 refs/original/ 名称。 。存储库最终将消肿(过滤器分支的大小将暂时增加一倍)。

Git 中的历史记录是(是?)提交。要更改历史记录,您需要将旧提交(提供旧历史记录)复制到新的不同提交(提供新历史记录)。因此,您的目标是将所有 50 次左右的提交替换为相同的新提交,除了文件被重新定位到其他路径。

正如您所提到的,您可以通过交互式 rebase 来做到这一点,但这很痛苦: rebase 的工作原理是将每个提交到副本转换为变更集(通过将该提交与其父级进行比较,以查看发生了什么变化),然后将相同的更改应用于某些现有提交。

有一个相当重的命令,git filter-branch,其目的是在应用某种提交修饰符时复制提交。它有很多选择,因为它本身就非常慢;但从根本上来说,它包括:

  • 列出要操作的每个提交(按哈希 ID)。就您而言,这只是“每次提交”。另外,创建一个旧哈希 ID → 新哈希 ID 的空映射。
  • 然后,从最根(最旧/最祖先)提交开始:

    1. 将提交提取到临时工作区域。
    2. 应用各个过滤器。
    3. 根据结果构建新的提交。使用 HashMap 来映射父 ID,以便新提交指向先前复制的新提交。这为命令提供了新提交的哈希 ID。
    4. 从旧提交哈希→新提交哈希向映射添加条目。
  • 最后,在对每个要过滤的提交执行上述操作后,循环遍历您告诉它更改的所有引用(主要是分支名称,但如果您使用--标签名称过滤器):

    1. 将原始引用从 refs/whatever 重命名为 refs/original/refs/whatever
    2. 使用 map 中找到的新哈希创建新的 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 系统)。

关于git - 在 git 历史记录中移动文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50532572/

相关文章:

git - Mercurial -> 保留提交哈希的 Git 迁移

git - 如何编辑推送的 git commit 的提交消息

git - 在 Git 中编辑根提交?

git - 将2个分支 merge 为一个分支

git - 我如何将所有提交移动到另一个分支?

java - 如果使用 Maven,是否应该忽略 VCS 中特定于 Eclipse 的文件?

git - 将 master 重置为之前的提交 - 如何推送

git - 当您的项目中有 key 时,如何推送到 GitHub?

Git 交互式 rebase 没有删除我的其他提交

r - 撤消 Rstudio 中太大而无法推送的 git commit