我正在GitHub帐户上的存储库上工作,这是我偶然发现的问题。


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

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

现在我最后要寻找的是一种从git的历史记录中删除该软件包文件夹的详细信息的方法,因此,如果有人对其进行了克隆,则无需下载6mb的历史记录,截至上一次提交时,它们将获得的唯一实际文件为300KB。

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


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

Gist似乎在运行脚本后在哪里工作,它表明它删除了该文件夹,并表明修改了50个不同的提交。但这并没有让我推送该代码。当我尝试推送它时,它说Branch up to date,但显示对git status修改了50次提交。其他两种方法也无济于事。

现在,即使表明它摆脱了该文件夹的历史记录,当我在本地主机上检查该存储库的大小时,它仍然约为6MB。 (我还删除了refs/original文件夹,但是没有看到存储库大小的变化。)

我想澄清的是,是否有一种方法可以消除不仅提交的问题历史(这是我认为唯一发生的事情),但是git仍在假设那些人想要回滚的那些文件。

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

我的最终目标是从根本上找到摆脱git中文件夹内容的最佳方法,从而使用户不必下载6MB的内容,而仍然可以进行其他提交从来没有碰过git历史记录中的modules文件夹。(

我该怎么做?

评论

如果以下任何答案解决了您的问题,则也许您应该考虑接受一个作为您问题的答案。 meta.stackexchange.com/questions/5234/…

最好的答案是:stackoverflow.com/a/32886427/5973334

#1 楼

如果您要在此处复制粘贴代码:

这是一个从历史记录中删除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实际做什么: br />
第一行通过运行--tree-filter命令遍历与HEAD(当前分支)相同的树(rm -rf node_modules)上的所有引用。该命令删除node_modules文件夹(-r,不带-rrm不会删除文件夹),而没有提示用户(-f)。添加的--prune-empty递归删除无用(不做任何更改)的提交。

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

其余命令相对简单。

评论


附带说明:我使用git count-objects -v检查文件是否实际上已删除,但是存储库的大小保持不变,直到再次克隆存储库为止。 Git保留了我认为的所有原始文件的副本。

– Davide Icardi
15年7月21日在9:39

如果使用的是非古旧的git,则应该读为--force-with-lease,而不是--force。

–Griwes
16-4-20在22:47

这些命令在Windows上均不起作用。或者至少不是Windows 10,请发布可进行“剪切和粘贴”的操作系统

–大卫
16年11月2日,19:48



对于Windows 10用户,这在Windows的Bash下效果很好(我使用过Ubuntu)

– Andrej Kyselica
17年8月13日在1:31

我用Windows Shell和git bash尝试了一下,但是没有用。第一条命令通过,第二条命令失败!

– Mohy Eldeen
17年11月9日在4:44

#2 楼

我发现其他答案中使用的--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


评论


您能解释一下为什么这么快吗?

– knocte
2015年11月18日,下午3:46

@knocte:来自docs(git-scm.com/docs/git-filter-branch)。 “ --index-filter:...与树过滤器类似,但是不检出树,这使它快得多”

–李·尼瑟顿
15年11月18日在12:54

为什么这不是公认的答案?太彻底了。

–疯狂物理学家
15年12月18日在12:51

如果在Windows中执行此操作,则需要双引号而不是单引号。

–克里斯·莫尼斯(Kris Morness)
17年8月1日在20:45

将--quiet传递给上面的git rm至少使因子4加快了我的重写速度。

– ctusch
18 Mar 8 '18 at 11:04

#3 楼

除了上面流行的答案外,我还要为Windows系统添加一些注意事项。命令

git filter-branch --tree-filter 'rm -rf node_modules' --prune-empty HEAD



无需任何修改即可完美运行!因此,切勿使用Remove-Itemdel或其他任何形式代替rm -rf

评论


如果目录包含,则在Windows上将无法使用。 (点)的名称。

– Corneliu Serediuc
17年5月6日在11:31

我找到了解决方案。对rm命令使用双引号,例如:“ rm -rf node.modules”。

– Corneliu Serediuc
17年5月6日在11:39

#4 楼

看来,最新的解决方法是不要直接使用filter-branch(至少git本身不再建议这样做),而将工作推迟到外部工具上进行。特别是,目前推荐使用git-filter-repo。该工具的作者提供了有关为什么直接使用filter-branch会导致问题的争论。

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

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


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

评论


不错的工具!在Ubuntu 20.04上运行良好,您只能在pip3上安装git-filter-repo,因为它仅是stdlib且不会安装任何依赖项。在Ubuntu 18上它与发行版的git版本不兼容错误:需要一个git版本,其diff-tree命令具有--combined-all-paths选项,但是在docker run -ti ubuntu:20.04上运行它很容易

– Kubanczyk
20 May 27'9:29

它只是工作,简单而优雅!感谢您的推荐!

–汤姆汤姆(Tom Tang)
20年7月1日在22:01



你是对的!但是,如果您可以将答案与关于filter-repo的信息分开。.我的意思是,也许写所有关于filter-repo的信息来替换filter-branch,然后编写-------运算符,然后给出我们提供有关命令本身的更多信息-例如--invert-paths是什么。谢谢!

–baruchiro
20 Jul 7'4:23



重要提示:如果您的目录不在顶层,则必须提供其完整路径。目录/子目录

–stef
20-09-22在9:46

#5 楼

我发现最好,最准确的方法是下载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


评论


非常简单:)如果要确保仅删除特定文件夹,这将有所帮助:stackoverflow.com/questions/21142986/…

– Emjay
17年8月25日在9:51

#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 /“

评论


为什么要从.gitignore中删除node_modules?这样,他们可能会再次被意外犯下??

–亚当斯基
19 Mar 25 '19在15:20

它不会从gitignore中删除,而是添加到了gitignore中。提交消息显示为“ git history”,而不是“ gitignore” :)

– Danny Tuppeny
19年4月8日在8:07

但是评论说您可以从.gitignore中删除node_modules。

– Zavr
19-10-18在14:57

#7 楼

对于Windows用户,请注意使用"代替'
,如果已经存在另一个备份,还添加了-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


#8 楼

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

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


它会删除git install文件夹中的usr / bin文件夹,从而破坏git安装的完整性。