从git / GitHub的历史记录中删除文件夹及其内容

Remove folder and its contents from git/GitHub's history

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

  • 安装了几个NPM包的文件夹的node.js项目
  • 包裹在node_modules文件夹中。
  • 将该文件夹添加到Git存储库并将代码推送到GitHub(当时没有考虑NPM部分)
  • 意识到你并不真正需要这个文件夹作为代码的一部分
  • 删除了那个文件夹,推了它

在这种情况下,总Git回购的大小约为6MB,其中实际代码(除文件夹外的所有代码)仅为300KB左右。

现在,我最后要寻找的是一种从Git的历史记录中删除包文件夹细节的方法,这样,如果有人克隆了它,他们就不必下载价值6MB的历史记录,从上一次提交开始,他们将获得的实际文件只有300KB。

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

  • 从Git存储库中删除文件(历史记录)
  • http://help.github.com/remove-sensitive-data/
  • https://gist.github.com/1588371

gist看起来像是在运行脚本之后的什么地方工作的,它显示它已经摆脱了那个文件夹,之后它显示修改了50个不同的提交。但它并没有让我推那个代码。当我试图推动它时,它说Branch up to date,但显示有50个承诺在git status上被修改。其他两种方法也没有帮助。

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

我要澄清的是,如果有一种方法可以消除提交历史(这是我认为唯一发生的事情),而且Git保留的那些文件假设要回滚。

假设为此提供了一个解决方案,并应用于我的本地主机,但无法复制到Github repo,是否可以克隆该repo,回滚到第一次提交执行该技巧并将其推送(或者这是否意味着Git仍将具有所有这些提交的历史记录?-又名。6MB)。

我在这里的最终目标是找到从Git中删除文件夹内容的最佳方法,这样用户就不必下载价值6MB的内容,而且还可能有其他从未接触过Git历史上的模块文件夹(几乎全部)的提交。

我该怎么做?


如果您要复制粘贴代码:

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

1
2
3
4
5
6
7
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


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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 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之前和之后使用以下方法检查存储库的大小:

1
git count-objects -vH


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

1
git filter-branch --tree-filter 'rm -rf node_modules' --prune-empty HEAD
  • 工作完美,无需任何修改!因此,您不能使用Remove-Itemdel或其他任何东西来代替rm -rf

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


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

然后运行命令:

1
2
3
4
5
git clone --bare https://project/repository project-repository
cd project-repository
java -jar bfg.jar --delete-folders DIRECTORY_NAME  # i.e. 'node_modules' in other examples
git reflog expire --expire=now --all && git gc --prune=now --aggressive
git push --mirror https://project/new-repository

如果要删除文件,请改用"删除文件"选项:

1
java -jar bfg.jar --delete-files *.pyc


完成复制粘贴配方,只需在测试后将命令添加到注释中(对于复制粘贴解决方案):

1
2
3
4
5
6
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/"。


对于windows用户,请注意使用"而不是'还添加了-f,以在已经有其他备份的情况下强制执行该命令。

1
2
3
4
5
6
7
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


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

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

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