Git - 从分支历史中删除文件

Git - Removing files from a branch's history

我目前有一个本地存储库正在开发中,我想在 GitHub 上公开分享(部分)它。到目前为止我做了什么:

git checkout dev  # dev is the current development branch of my local repository
git branch public  # create a new branch from dev for the public repo
git checkout public
git remote add public git@github.com  # add the public repo as a new remote
git push -u public public:master  # push local 'public' branch to 'master' branch of 'public' remote

然而,这次推送失败了,因为我的存储库包含一些相当大的子目录。所以我开始清理它:

git rm -r --cached external  # remove large subdirectory 'external'
git rm -r --cached ...  # repeat for other large subdirectories

然后我将上述所有子目录也包含在 .gitignore 中并提交。对 git ls 的调用现在仅显示少量文件,其组合大小最多为几 MB,而对 git status 的调用显示没有未提交或未跟踪的文件。然而 git push 仍然失败,显然是因为大的子目录仍然包含在分支的历史中。

从历史记录中清除文件的正确方法似乎是使用 git filter-branch 命令,但是此命令附带了很多警告,我不想弄乱我的整个存储库进行中。如何从 public 分支(且仅 public 分支)的历史记录中正确清除上面使用 git rm 删除的子目录(仅子目录)?

由于该分支不太可能合并回其他分支,作为最后的手段,我也可以简单地从中删除所有历史记录。其他分支应该仍然保持原样,但是

从某种意义上说,分支并不真正存在于 git 中:它们只是指向特定提交的指针,并从那里指向导致该提交的历史记录。所以你的存储库可能看起来像这样,示意性的:

                    +-- E --- F   <- main branch
                   /
A --- B --- C --- D 
                   \
                    +-- G --- H   <- public branch

如果大文件存在于任何提交 A、B、C 和 D 中,那么根据定义它们存在于主分支和 public 分支的历史记录中。

要重写历史,您必须创建新的提交,回到首次添加这些文件的时间。您可以像这样使用 git-filter-repo 工具执行此操作:

git filter-repo --invert-paths --path '/directory/to/delete' --refs public

假设文件首先添加到提交 B 中;我们现在可能有这样的东西:

  +-- B --- C --- D -- E --- F   <- main branch
 /
A
 \
  +-- B2 --- C2 --- D2 --- G2 --- H2   <- public branch

这似乎是你想要的,但它不再是一个非常有用的分支 - 如果你曾经尝试将任何东西从 main 合并到它,你最终会得到这个:

  +-- B --- C --- D -- E --- F ----- X  <- main branch with new feature
 /                                    \
A                                      \
 \                                      \
  +-- B2 --- C2 --- D2 --- G2 --- H2 --- M  <- public branch with merge commit

包含我们大文件的提交 B 的原始版本现在回到分支历史记录中,以及 作为新提交 B2。

因此,与其担心哪些分支包含哪些文件,哪些不包含这些文件,可能更容易简单地使用新名称获取存储库的副本,并使其好像这些文件从未存在过存储库历史记录中的任意位置

git filter-repo --invert-paths --path '/directory/to/delete'

这将重写所有你的提交,给出一个全新的历史:

                       +-- E2 --- F2   <- main branch
                      /
A2 --- B2 --- C2 --- D2 
                      \
                       +-- G2 --- H2   <- public branch