坑的由来
repo中不小心上传了许多测试生成的data。结果可想而知,原本只有代码的仓库突然间变得无比臃肿(或者是慢慢臃肿),从早期的几十MB,迅速飙升至1G.
到底发生了什么
早些时候我对git的原理并不是很了解,只是随着日常使用,终于开始理解git其实是一个指针指向一次提交的对象,当你在各个分支间切换的时候,指针就随之切换,版本也随之更改。
那么,git 是如何做到的能在各个版本间无缝切换的呢。即使long long ago的代码,只要来一句git reset --hard sesd54f54sdf5sd4sd5f
照样给你打回原形。
在相应的git文件夹里面 按下面组合键 可以看到隐藏的.git版本控制文件
ctrl+h
真相只有一个,那就是其实所有的版本,不管是否存在了多久,都仍然存在于硬盘里。所以你才可以任性地对代码为所欲为。然而,为所欲为也要付出代价。
代价就是,你删了几次,就会有几个快照存在于硬盘里。删一个大视频,表面上少了500M空间,实则增加了一次至少500M的历史提交记录,虽然现在的代码仓库里不再有这个视频,但是你试试du -sh .git
看看.git 文件大小,是不是有惊喜?我在这儿体会的最恐怖的一次,见证了3个多G的.git
如何 解决
首先我们看看git相关文件占用的空间,运行
git count-objects -v
size-pack
以千字节为单位表示,那么这里就有0.3G大小,这对代码仓库来说可是个恐怖的数字了。
那么让我们来找出罪魁祸首——到底是哪些大文件在混淆视听。
将所有含这些大文件的历史提交记录,一个不漏的找出来。
tail -30 表示 列举前30项
git rev-list --objects --all | grep -E `git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -30 | awk '{print$1}' | sed ':a;N;$!ba;s/\n/|/g'`
然后移除对该文件的引用 也就是 (tests_run_one.....后面一长串) (使用filter-branch去除大文件)
git filter-branch --force --index-filter "git rm --cached --ignore-unmatch 'tests_run_one.....后面一长串'" --prune-empty --tag-name-filter cat -- --all
进行repack
git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin # 指示Git清除不需要的数据:
git reflog expire --expire=now --all && git gc --prune=now --aggressive
查看pack的空间使用情况
git count-objects -v
查看.git的实际大小
du -sh .git
强制把本地的分支(这里是master)更改推送到git服务器分支
git push origin --force --all
参数 origin 是远程仓库地址别名, 具体请参照 git remote -v 命令 查看选择
参数 --force 是强制推送, 这个参数表示强行覆盖远程仓库, 这个参数跟TNT一样危险不能乱用, 它可以简写为 -f
参数 --all 表示所有历史都要覆写, 当然我不知道这里的 “全部” 是多少, 教程里还提到了再提交一次 tags 的
强制push tags
git push origin --force --tags
需要把这一次的改动提交到远端仓库:
git push --force --verbose --dry-run
git push --force
依次执行以下命令
rm -rf .git/refs/original/ git reflog expire --expire=now --all git fsck --full --unreachable git repack -A -d git gc --aggressive --prune=now git push --force
注:这一步可能会收到错误,原因是protected的分支不能进行覆盖推送,可以首先把当前分支修改为unprotected,在仓库的设置中可以修改。
到此,大文件已经从仓库中清除了,重新拉一个仓库,速度变快了,可以看到仓库的体积已经变小了,注意:一定要重新拉取仓库。
最后告诉你的同事使用pull rebase而不是使用merge来更新他们分支。如果使用merge,他们本地的.git可能会覆盖你之前做的清理工作,再次push 的时候会让本地仓库再次引入到远程库中
或者告知他们下次 重新git clone
事实上有两种处理方法 参考gitlab官方文档 Reducing the repository size using Git
https://docs.gitlab.com/ee/user/project/repository/reducing_the_repo_size_using_git.html#reducing-the-repository-size-using-git
如果要使用BFG清除git仓库中的隐私文件或大文件参考下面
https://www.cnblogs.com/huipengly/p/8424096.html
当repo只有默认的master的时候 以上是可以完成需求的
实验证明 如果repo 有其他分支(除了master分支之外) 以上指令没有效果 也就是远程端 并没有减少库容量 因为分支没有得到push 实际上--all参数是失效的
首先创建本地
查看远程分支名称
git branch -r
远程先开好分支然后拉到本地( 本地分支会和远程分支建立一定关联)
git checkout -b Tests origin/Tests //检出远程的Tests分支到本地Tests
查看本地分支 是否切换到刚创建的
git branch
git gc
git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin
git reflog expire --expire=now --all && git gc --prune=now --aggressive
强制 PUSH远程分支
git push origin Tests:Tests --force //推送本地的Tests(冒号前面的)分支到远程origin的Tests(冒号后面的)分支(没有会自动创建)
本文参考
https://blog.csdn.net/qq_40233736/article/details/86668768
https://kyriejoshua.github.io/jo.github.io/2016/07/17/how-to-clear-huge-files-in-git/
https://www.jianshu.com/p/17e5adf47da5