Git之深入解析如何解決.git目錄過大的問題
一、前言
Git 是一個分布式版本控制軟件,最初目的是為更好地管理 Linux 內核開發,Git 在本地磁盤上就保存著所有有關當前項目的歷史更新,處理速度快。Git 中的絕大多數操作都只需要訪問本地文件和資源,不用實時聯網。
Git LFS(Large File Storage - ??件存儲)是可以把?樂、圖?、視頻等指定的任意?件存在 Git 倉庫之外,?在 Git 倉庫中??個占?空間 1KB 不到的?本指針來代替的??具。通過把??件存儲在 Git 倉庫之外,可以減? Git 倉庫本身的體積,使克隆 Git 倉庫的速度加快,也使得 Git 不會因為倉庫中充滿??件?損失性能。
使? Git LFS,在默認情況下,只有當前簽出的 commit 下的 LFS 對象的當前版本會被下載。此外,也可以做配置,只取由 Git LFS 管理的某些特定?件的實際內容,?對于其他由 Git LFS 管理的?件則只保留?件指針,從?節省帶寬,加快克隆倉庫的速度;也可以配置?次獲取??件的最近版本,從?能?便地檢查??件的近期變動。
二、問題描述
使用過 Git 的同學都知道,隨著代碼的更新迭代,倉庫的體積越來越大,如果操作和使用都比較恰當的情況下,倉庫體積不會突增的。但是如果使用不恰當的話,那就非常尷尬了,比如下面要說的這種情況,.git 這個隱藏目錄特別大:
? du -d 1 -h 680M ./.git 500K ./misc 68K ./docker ... 1.1G .
1
2
3
4
5
6
雖然 .git 這個隱藏目錄并不算在代碼體積之后,但是拉取代碼的時候,是需要拉下來的,因為里面包含之前的提交記錄等信息,這就會會非常費時費力,因為下載速度變的很慢。
三、問題分析
當使用 git add 和 git commit 命令的過程中,Git 不知不覺就會創建出來 blob 文件對象,然后更新 index 索引,再然后創建 tree 對象,最后創建出 commit 對象,這些 commit 對象指向頂層 tree 對象以及先前的 commit 對象。
而上述創建出來的對象,都以文件的方式保存在 .git/objects 目錄下。所以,當在使用的過程中,提交了一個體積特別大的文件,就會被 Git 追蹤記錄在 .git/objects 文件夾下面。
此時,如果再次刪除這個體積特別大的文件,其實 Git 只會記錄我們刪除的這個操作,但并不會把文件從 .git 文件夾下面真正的刪除,即 .git 文件夾完全不會變小。
四、解決方法
① 重建倉庫
重建倉庫的這種做法,算是一種比較一勞永逸且相對而言比較簡單的方式。既然現在的倉庫已經讓我們無法忍受,與其這樣,還不是刪除重建來的爽快。但是,這種做法一般情況下,都是不可行,除非是自己的本地項目。
② 刪除大文件
直接找到 .git 目錄下的大文件,將其刪除掉,之后推送到遠程代碼庫里面。這樣做的前提是,刪除所有其他分支,保留 master 或者 main 分支。
# 查找大文件 $ git verify-pack -v .git/objects/pack/*.idx 12235d...dewaaaa34 tree 135 137 144088922 a453ab...34se212qz blob 3695898 695871 144734158 ...... # 篩除前五個且保留第一列 $ git verify-pack \ -v .git/objects/pack/*.idx | \ sort -k 3 -n | tail -5 | awk '{print}' 12q626a...23a3 2z32ax1...azfd ...... # 查找出最大的5個文件和對應Commit信息 $ git rev-list --objects --all | \ grep "$(git verify-pack -v .git/objects/pack/*.idx | \ sort -k 3 -n | tail -5 | awk '{print}')" 91266a...sdfa3 data/xxx.pkl 232ax1...acafd data/yyy.pkl ...... # rev-list: 列出Git倉庫中的所有提交記錄 # --objects: 列出該提交涉及的所有文件ID # --all: 所有分支的提交(位于/refs下的所有引用) # verify-pack: 顯示已打包的內容(找大文件)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 將其刪除掉 $ git filter-branch \ --force --prune-empty --index-filter \ "git rm -rf --cached --ignore-unmatch YOU-FILE-NAME" \ --tag-name-filter cat -- --all # filter-branch: 重寫Git倉庫中的提交 # --index-filter: 指定后面命令進行刪除 # --all: 所有分支的提交(位于/refs下的所有引用)
1
2
3
4
5
6
7
8
9
# 強制推送 $ git push --force --all # 徹底清除 $ rm -rf .git/refs/original/ $ git reflog expire --expire=now --all $ git gc --prune=now
1
2
3
4
5
6
7
③ 使用工具清理
Github 上有一個叫做 git-filter-branch 的工具,就是幫助我們來清理大文件對象的,其使用 Scala 語言進行編寫的,且操作起來也十分方便。只需要簡單幾步,就可以完成我們的需要,最新版需要確保本地的 java 為 Jdk8+。
# 下載封裝好的jar包 $ wget https://repo1.maven.org/maven2/com/madgag/bfg/1.13.0/bfg-1.13.0.jar # 克隆的時候需要--mirror參數 $ git clone --mirror git://example.com/big-repo.git # 運行BFG來清理存儲庫 $ java -jar bfg.jar --strip-blobs-bigger-than 100M big-repo.git # 去除臟數據 $ cd big-repo.git $ git reflog expire --expire=now --all $ git gc --prune=now --aggressive # 推送上去 # 此推將更新遠程服務器上的所有refs分支 $ git push
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 下載封裝好的jar包 $ wget https://repo1.maven.org/maven2/com/madgag/bfg/1.13.0/bfg-1.13.0.jar # 克隆的時候需要--mirror參數 $ git clone --mirror git://example.com/big-repo.git # 運行BFG來清理存儲庫 $ java -jar bfg.jar --strip-blobs-bigger-than 100M big-repo.git # 去除臟數據 $ cd big-repo.git $ git reflog expire --expire=now --all $ git gc --prune=now --aggressive # 推送上去 # 此推將更新遠程服務器上的所有refs分支 $ git push
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
④ 使用 migrate 命令優化 .git 目錄
遷移已有的 git 倉庫,使? git lfs 來進行管理,重寫歷史后的提交需執? git commit --force,請確認在本地的操作合適?誤后再進?提交。如有遷移? git lfs 前的倉庫有多份拷?,其他拷?可能需要執? git reset --hard origin/master 來重置其本地的分?,注意執? git reset --hard 命令將會丟失本地的改動。
# 重寫master分? # 將歷史提交(指的是.git目錄)中的*.zip都?lfs進?管理 $ git lfs migrate import --include-ref=master --include="*.zip" # 重寫所有分?及標簽 # 將歷史提交(指的是.git目錄)中的*.rar,*.zip都?lfs進?管理 $ git lfs migrate import --everything --include="*.rar,*.zip" # 切換后需要把切換之后的本地分支提交到遠程倉庫了,需要手動push更新遠程倉庫中的各個分支 $ git lfs push --force # 切換成功后,GIT倉庫的大小可能并沒有變化 # 主要原因可能是之前的提交還在,因此需要做一些清理工作 # 如果不是歷史記錄非常重要的倉庫,建議不要像上述這么做,而是重新建立一個新的倉庫 $ git reflog expire --expire-unreachable=now --all $ git gc --prune=now
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
五、總結
比較好的避免上述問題的出現,就是及時使用 lfs 來追蹤、記錄和管理大文件,這樣大文件既不會污染 .git 目錄,也可以更方便的使用:
# 1.開啟lfs功能 $ git lfs install # 2.追蹤所有后綴名為“.psd”的文件 $ git lfs track "*.iso" # 3.追蹤單個文件 git lfs track "logo.png" # 4.提交存儲信息文件 $ git add .gitattributes # 5.提交并推送到GitHub倉庫 $ git add . $ git commit -m "Add some files" $ git push origin master
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
同時,還有一個方法,就是靈活使用 .gitignore 文件,及時排除倉庫不需要的特殊目錄或者文件,從而不會讓不應該存在的文件,出現在代碼倉庫里面:
.DS_Store node_modules /dist *.zip *.tar.gz
1
2
3
4
5
6
Git GitHub
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。