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
我目前有一个本地存储库正在开发中,我想在 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