414

我在我的 GitHub 帐户上开发一个存储库,这是我偶然发现的一个问题。

  • Node.js 项目的文件夹中安装了一些 npm 包
  • 包裹在node_modules文件夹中
  • 将该文件夹添加到 git 存储库并将代码推送到 github(当时没有考虑 npm 部分)
  • 意识到您实际上并不需要该文件夹成为代码的一部分
  • 删除那个文件夹,推送它

在那种情况下,总 git repo 的大小约为6MB,而实际代码(除该文件夹外的所有代码)仅为300 KB左右。

现在我最终要寻找的是一种从 git 历史记录中删除该包文件夹详细信息的方法,因此如果有人克隆它,他们不必下载 6mb 的历史记录,他们将获得的唯一实际文件截至最后一次提交将是 300KB。

我为此查找了可能的解决方案并尝试了这两种方法

Gist 似乎在运行脚本后有效,它显示它摆脱了该文件夹,然后它显示修改了 50 个不同的提交。但它并没有让我推送那个代码。当我尝试推送它时,它说Branch up to date但显示 50 个提交在git status. 其他两种方法也没有帮助。

现在,即使它显示它已经摆脱了该文件夹的历史记录,但当我在本地主机上检查该存储库的大小时,它仍然在 6MB 左右。(我也删除了该refs/original文件夹,但没有看到 repo 大小的变化)。

我想要澄清的是,如果有一种方法不仅可以摆脱提交历史(这是我认为唯一发生的事情),而且 git 一直假设想要回滚的那些文件。

可以说为此提出了一个解决方案并应用于我的本地主机但无法复制到该 GitHub 存储库,是否可以克隆该存储库,回滚到第一个提交执行技巧并推送它(或者这是否意味着 git 将仍然有所有这些提交的历史吗? - 又名 6MB)。

我的最终目标是基本上找到从 git 中删除文件夹内容的最佳方法,这样用户就不必下载 6MB 的东西,并且仍然可能有其他从未触及模块文件夹的提交(这很漂亮很多)在 git 的历史中。

我怎样才能做到这一点?

4

9 回答 9

650

警告:官方不再推荐git filter-branch

如果您在这里复制粘贴代码:

这是一个node_modules从历史中删除的例子

git filter-branch --tree-filter "rm -rf node_modules" --prune-empty HEAD
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
echo node_modules/ >> .gitignore
git add .gitignore
git commit -m 'Removing node_modules from git history'
git gc
git push origin master --force

git 实际上做了什么:

第一行遍历--tree-filter与 HEAD(您的当前分支)相同的树()上的所有引用,运行命令rm -rf node_modules。此命令删除 node_modules 文件夹(-r,没有-rrm不会删除文件夹),不给用户提示(-f)。添加的--prune-empty删除无用(不改变任何东西)递归提交。

第二行删除对该旧分支的引用。

其余命令相对简单。

于 2013-07-24T03:28:27.743 回答
299

我发现--tree-filter其他答案中使用的选项可能非常慢,尤其是在具有大量提交的大型存储库中。

这是我使用该--index-filter选项从 git 历史记录中完全删除目录的方法,该选项运行得更快:

# Make a fresh clone of YOUR_REPO
git clone YOUR_REPO
cd YOUR_REPO

# Create tracking branches of all branches
for remote in `git branch -r | grep -v /HEAD`; do git checkout --track $remote ; done

# Remove DIRECTORY_NAME from all commits, then remove the refs to the old commits
# (repeat these two commands for as many directories that you want to remove)
git filter-branch --index-filter 'git rm -rf --cached --ignore-unmatch DIRECTORY_NAME/' --prune-empty --tag-name-filter cat -- --all
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d

# Ensure all old refs are fully removed
rm -Rf .git/logs .git/refs/original

# Perform a garbage collection to remove commits with no refs
git gc --prune=all --aggressive

# Force push all branches to overwrite their history
# (use with caution!)
git push origin --all --force
git push origin --tags --force

您可以在使用之前和之后检查存储库的大小gc

git count-objects -vH
于 2015-10-01T11:26:03.487 回答
95

似乎对此的最新答案是直接使用filter-branch(至少 git 本身不再推荐它),并将该工作推迟到外部工具。特别是,目前推荐使用git-filter-repo 。该工具的作者提供了关于为什么直接使用会导致问题的论点。filter-branch

上面要从历史记录中删除的大多数多行脚本dir可以重写为:

git filter-repo --path dir --invert-paths

显然,该工具比这更强大。您可以按作者、电子邮件、参考名称等应用过滤器(此处为完整手册页)。此外,它很快。安装很容易 - 它以多种格式分发

于 2020-05-01T14:58:23.503 回答
56

除了上面的流行答案之外,我还想为Windows 系统添加一些注释。命令

git filter-branch --tree-filter 'rm -rf node_modules' --prune-empty HEAD
  • 无需任何修改即可完美运行!因此,您不能使用Remove-Item,del或其他任何东西来代替rm -rf.

  • 如果您需要指定文件或目录的路径,请使用斜杠,例如./path/to/node_modules

于 2015-11-03T06:57:13.650 回答
26

我发现最好和最准确的方法是下载 bfg.jar 文件: https ://rtyley.github.io/bfg-repo-cleaner/

然后运行命令:

git clone --bare https://project/repository project-repository
cd project-repository
java -jar bfg.jar --delete-folders DIRECTORY_NAME
git reflog expire --expire=now --all && git gc --prune=now --aggressive
git push --mirror https://project/new-repository

如果要删除文件,请改用 delete-files 选项:

java -jar bfg.jar --delete-files *.pyc
于 2017-01-30T19:05:40.863 回答
8

完整的复制粘贴配方,只需在评论中添加命令(用于复制粘贴解决方案),经过测试:

git filter-branch --tree-filter 'rm -rf node_modules' --prune-empty HEAD
echo node_modules/ >> .gitignore
git add .gitignore
git commit -m 'Removing node_modules from git history'
git gc
git push origin master --force

在此之后,您可以从 .gitignore 中删除“node_modules/”行

于 2015-01-22T18:12:34.480 回答
8

对于 Windows 用户,如果另一个备份已经存在,请注意使用"而不是' Also added以强制执行该命令。-f

git filter-branch -f --tree-filter "rm -rf FOLDERNAME" --prune-empty HEAD
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
echo FOLDERNAME/ >> .gitignore
git add .gitignore
git commit -m "Removing FOLDERNAME from git history"
git gc
git push origin master --force
于 2019-03-18T19:42:35.280 回答
3

我在 Windows 上使用 git 从旧 C# 项目中删除了 bin 和 obj 文件夹。小心

git filter-branch --tree-filter "rm -rf bin" --prune-empty HEAD

它通过删除 git install 文件夹中的 usr/bin 文件夹来破坏 git 安装的完整性。

于 2018-01-04T21:58:27.340 回答
0

对于复制粘贴(从这里):

git filter-repo --invert-paths --path PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA
echo "YOUR-FILE-WITH-SENSITIVE-DATA" >> .gitignore
git add .gitignore
git commit -m "Add YOUR-FILE-WITH-SENSITIVE-DATA to .gitignore"
git push origin --force --all
于 2021-09-02T20:50:32.067 回答