前提
在友人的提醒下,發(fā)先自己的Github的項目體積過大,在只有一個src文件夾加上一些webpack配置,里面就2個html和一個輸出console.log的js的情況下,項目體積竟然又13MB,這顯然是不正常的,于是,開始解決這個問題!
問題所在
首先查看整個項目文件夾下的大小
$ du -d 1 -h
復(fù)制代碼
發(fā)現(xiàn)是.git文件夾過大,然后一個一個點進去,.git->objects->pack ,里面的文件太大了,其中一個竟然有12MB。
原因
github在你執(zhí)行g(shù)it init操作后,會創(chuàng)建一個.git的隱藏文件夾,該目錄結(jié)構(gòu)如下
$ ls
HEAD // 指向當(dāng)前分支
branches/ // 目錄
config // 項目特有的配置選項
description // 僅供 GitWeb 程序使用
hooks/ // 保存了客戶端或服務(wù)端鉤子腳本
index // 保存了暫存區(qū)域信息
info/ // 保存了一份不希望在 .gitignore 文件中管理的忽略模式 (ignored patterns) 的全局可執(zhí)行文件
objects/ // 存儲所有數(shù)據(jù)內(nèi)容
refs/ // 存儲指向數(shù)據(jù) (分支) 的提交對象的指針
復(fù)制代碼
在你git add 和 git commit 的過程中,保存修改了的文件的 blob,更新索引,創(chuàng)建 tree 對象,最后創(chuàng)建 commit 對象,這些 commit 對象指向了頂層 tree 對象以及先前的 commit 對象。這三類 Git 對象 ── blob,tree 以及 commit ── 都各自以文件的方式保存在 .git/objects 目錄下。
所以,當(dāng)你提交了一個體積特別大的文件后,會記錄在objects文件夾下,刪除一個文件,只是記錄了刪除這個操作,但并不會把文件從.git文件夾刪除。 當(dāng)你直接從項目中刪除該文件,.git文件夾完全不會變小(理論上還會變大一點,因為多記錄了一次刪除操作。。。)。
git會記錄你的每一次操作,這個是使用git中的很重要的概念
更多內(nèi)容請點擊git內(nèi)部原理
解決
解決問題之前,請先出去抽支煙,確定自己清楚了這個操作的危險性。(建議先弄一個測試庫試上幾遍)
操作前提:刪除所有的分支,只留下一個master
首先查找出大文件
$ git rev-list --objects --all | grep "$(git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -5 | awk '{print$1}')"
復(fù)制代碼
rev-list命令用來列出Git倉庫中的提交,我們用它來列出所有提交中涉及的文件名及其ID。 該命令可以指定只顯示某個引用(或分支)的上下游的提交。
--objects:列出該提交涉及的所有文件ID。
--all:所有分支的提交,相當(dāng)于指定了位于/refs下的所有引用。
verify-pack命令用于顯示已打包的內(nèi)容,我們用它來找到那些大文件。
-v(verbose)參數(shù)是打印詳細信息。
前面我們通過rev-list得到了文件名-ID的對應(yīng)關(guān)系,通過verify-pack得到了最大的5個文件ID。 用后者篩選前者便能得到最大的5個文件的文件名,比如:
$ git rev-list --objects --all | grep "$(git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -5 | awk '{print$1}')"
b96695fd6e62fff41723ab324368da394f3a504e package-lock.json
76ec4d5d5fd79da922b322d53fbd1298dc128dd3 node_modules/acorn/dist/acorn.mjs.map
286dc92a240dac765d81dfbda451fd19f9611e84 node_modules/ajv/dist/ajv.bundle.js
d2cc86eb230313af9bbdbd3faed7f8e7c7247788 node_modules/source-map-support/node_modules/source-map/dist/source-map.min.js.map
aad0620d70e16717ec338fc1d332279739e0d97c node_modules/terser/node_modules/source-map/dist/source-map.debug.js
復(fù)制代碼
然后我們需要刪除他們
$ git filter-branch --force --prune-empty --index-filter 'git rm -rf --cached --ignore-unmatch YOU-FILE-NAME' --tag-name-filter cat -- --all
復(fù)制代碼
filter-branch命令可以用來重寫Git倉庫中的提交
--index-filter參數(shù)用來指定一條Bash命令,然后Git會檢出(checkout)所有的提交, 執(zhí)行該命令,然后重新提交。
–all參數(shù)表示我們需要重寫所有分支(或引用)。
YOU-FILE-NAME 你查找出來的大文件名字
重復(fù)幾次,直到大文件全部刪除完畢。
如果你確定某一個文件夾下面都不是你需要的,那么你可以直接刪除整個文件夾,比如:
$ git filter-branch --force --prune-empty --index-filter 'git rm -rf --cached --ignore-unmatch node_modules' --tag-name-filter cat -- --all
復(fù)制代碼
完成后,以強制覆蓋的方式推送你的repo, 命令如下:
$ git push --force --all
復(fù)制代碼
最后,雖然上面我們已經(jīng)刪除了文件, 但是我們的repo里面仍然保留了這些objects, 等待垃圾回收(GC), 所以我們要用命令徹底清除它, 并收回空間,命令如下:
$ rm -rf .git/refs/original/
$ git reflog expire --expire=now --all
$ git gc --prune=now
Enumerating objects: 116, done.
Counting objects: 100% (116/116), done.
Delta compression using up to 4 threads
Compressing objects: 100% (53/53), done.
Writing objects: 100% (116/116), done.
Total 116 (delta 55), reused 116 (delta 55)
復(fù)制代碼
經(jīng)驗教訓(xùn)
雖然折騰了半天,也成功解決了這個問題。
從中吸取了一個教訓(xùn):
當(dāng)你創(chuàng)建了項目后,沒有設(shè)置好.gitignore之前,千萬不要提交代碼!!!還有就是,大文件盡量不要提交。
.gitignore設(shè)置: 在項目根目錄創(chuàng)建.gitignore文件
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw*
復(fù)制代碼
參考: 尋找并刪除Git記錄中的大文件 徹底刪除git中沒用的大文件
轉(zhuǎn)載鏈接:
https://juejin.cn/post/6844903848864137229