如何瘦身 Git 倉庫
對 Git 倉庫的維護通常是為了減少倉庫的大小。如果你從另外一個版本控制系統導入了一個倉庫,你可能需要在導入後清除掉不必要的文件。本文著重於從一個 Git 倉庫中刪除大文件,並且包含下列主題:
- 理解從 Git 的歷史記錄中刪除文件
- 使用 BFG 重寫歷史記錄
- 可選,使用
git filter-branch
重寫歷史記錄 - 垃圾回收
請格外小心.....
本文中的步驟和工具使用的高級技術涉及破壞性操作。確保您在開始之前仔細讀過並備份了你的倉庫,創建一個備份最容易的方式是使用 --mirror 標誌對你的倉庫克隆,然後對整個克隆的文件進行打包壓縮。有了這個備份,如果在維護期間意外損壞了您的倉庫的關鍵元素,那麼你可以通過備份的倉庫來恢復。
請記住,倉庫維護對倉庫的用戶可能會是毀滅性的。與你的團隊或者倉庫的關注者進行溝通會是一個不錯的主意。確保每個人都已經檢查了他們的代碼,並且同意在倉庫維護期間停止開發。
理解從 Git 的歷史記錄中刪除文件
回想一下,克隆倉庫會克隆整個歷史記錄——包括每個源代碼文件的所有版本。如果一個用戶提交了一個較大的文件,比如一個 JAR,則隨後的每次克隆都會包含這個文件。即使用戶最終在後面的某次提交中刪除了這個文件,但是這個文件仍然存在於這個倉庫的歷史記錄中。要想完全的從你的倉庫中刪除這個文件,你必須:
- 從你的項目的當前的文件樹中刪除該文件;
- 從倉庫的歷史記錄中刪除文件——重寫 Git 歷史記錄,從包含該文件的所有的提交中刪除這個文件;
- 刪除指向舊的提交歷史記錄的所有 reflog 歷史記錄;
- 重新整理倉庫,使用 git gc 對現在沒有使用的數據進行垃圾回收。
Git 的 「gc」(垃圾回收)將通過你的任何一個分支或者標籤來刪除倉庫中所有的實際沒用的或者以某種方式引用的數據。為了使其發揮作用,我們需要重寫包含不需要的文件的所有 Git 倉庫歷史記錄,倉庫將不再引用它—— git gc 將會丟棄所有沒用的數據。
重寫存儲庫歷史是一個棘手的事情,因為每個提交都依賴它的父提交,所以任何一個很小的改變都會改變它的每一個隨後的提交的提交 ID。有兩個自動化的工具可以做到這:
- BFG Repo Cleaner 快速、簡單且易於使用,需要 Java 6 或者更高版本的運行環境。
- git filter-branch 功能強大、配置麻煩,用於大於倉庫時速度較慢,是核心 Git 套件的一部分。
切記,當你重寫歷史記錄後,無論你是使用 BFG 還是使用 filter-branch,你都需要刪除指向舊的歷史記錄的 reflog
條目,最後運行垃圾回收器來刪除舊的數據。
使用 BFG 重寫歷史記錄
BFG 是為將像大文件或者密碼這些不想要的數據從 Git 倉庫中刪除而專門設計的,所以它有一一個簡單的標誌用來刪除那些大的歷史文件(不在當前的提交裡面):--strip-blobs-bigger-than
$ java -jar bfg.jar --strip-blobs-than 100M
大小超過 100MB 的任何文件(不包含在你最近的提交中的文件——因為 BFG 默認會保護你的最新提交的內容)將會從你的 Git 倉庫的歷史記錄中刪除。如果你想用名字來指明具體的文件,你也可以這樣做:
$ java -jar bfg.jar --delete-files *.mp4
BFG 的速度要比 git filter-branch
快 10-1000 倍,而且通常更容易使用——查看完整的使用說明和示例獲取更多細節。
或者,使用 git filter-branch 來重寫歷史記錄
filter-branch
命令可以對 Git 倉庫的歷史記錄重寫,就像 BFG 一樣,但是過程更慢和更手動化。如果你不知道這些大文件在哪裡,那麼你第一步就需要找到它們:
手動查看你 Git 倉庫中的大文件
Antony Stubbs 寫了一個可以很好地完成這個功能的 BASH 腳本。該腳本可以檢查你的包文件的內容並列出大文件。在你開始刪除文件之前,請執行以下操作獲取並安裝此腳本:
1、 下載腳本到你的本地的系統。
2、 將它放在一個可以訪問你的 Git 倉庫的易於找到的位置。
3、 讓腳本成為可執行文件:
$ chmod 777 git_find_big.sh
4、 克隆倉庫到你本地系統。
5、 改變當前目錄到你的倉庫根目錄。
6、 手動運行 Git 垃圾回收器:
git gc --auto
7、 找出 .git 文件夾的大小
$ du -hs .git/objects
45M .git/objects
注意文件大小,以便隨後參考。
8、 運行 git_find_big.sh
腳本來列出你的倉庫中的大文件。
$ git_find_big.sh
All sizes are in kB's. The pack column is the size of the object, compressed, inside the pack file.
size pack SHA location
592 580 e3117f48bc305dd1f5ae0df3419a0ce2d9617336 media/img/emojis.jar
550 169 b594a7f59ba7ba9daebb20447a87ea4357874f43 media/js/aui/aui-dependencies.jar
518 514 22f7f9a84905aaec019dae9ea1279a9450277130 media/images/screenshots/issue-tracker-wiki.jar
337 92 1fd8ac97c9fecf74ba6246eacef8288e89b4bff5 media/js/lib/bundle.js
240 239 e0c26d9959bd583e5ef32b6206fc8abe5fea8624 media/img/featuretour/heroshot.png
大文件都是 JAR 文件,包的大小列是最相關的。aui-dependencies.jar
被壓縮到 169kb,但是 emojis.jar
只壓縮到 500kb。emojis.jar
就是一個待刪除的對象。
運行 filter-branch
你可以給這個命令傳遞一個用於重寫 Git 索引的過濾器。例如,一個過濾器可以可以將每個檢索的提交刪除。這個用法如下:
git filter-branch --index-filter 'git rm --cached --ignore-unmatch _pathname_ ' commitHASH
--index-filter
選項可以修改倉庫的索引,--cached
選項從索引中而不是磁碟來刪除文件。這樣會更快,因為你不需要在運行這個過濾器前檢查每個修訂版本。git rm
中的 ignore-unmatch
選項可以防止在嘗試移走不存在的文件 pathname
的時候命令失敗。通過指定一個提交 HASH 值,你可以從每個以這個 HASH 值開始的提交中刪除pathname
。要從開始處刪除,你可以省略這個參數或者指定為 HEAD
。
如果你的大文件在不同的分支,你將需要通過名字來刪除每個文件。如果大文件都在一個單獨的分支,你可以直接刪除這個分支本身。
選項 1:通過文件名刪除文件
使用下面的步驟來刪除大文件:
1、 使用下面的命令來刪除你找到的第一個大文件:
git filter-branch --index-filter 'git rm --cached --ignore-unmatch filename' HEAD
2、 重複步驟 1 找到剩下的每個大文件。
3、 在你的倉庫里更新引用。 filter-branch
會為你原先的引用創建一個 refs/original/
下的備份。一旦你確信已經刪除了正確的文件,你可以運行下面的命令來刪除備份文件,同時可以讓垃圾回收器回收大的對象:
git filter-branch --index-filter 'git rm --cached --ignore-unmatch filename' HEAD
選項 2:直接刪除分支
如果你所有的大文件都在一個單獨的分支上,你可以直接刪除這個分支。刪除這個分支會自動刪除所有的引用。
1、 刪除分支。
$ git branch -D PROJ567bugfix
2、 從後面的分支中刪除所有的 reflog 引用。
對不用的數據垃圾回收
1、 刪除從現在到後面的所有 reflog 引用(除非你明確地只在一個分支上操作)。
$ git reflog expire --expire=now --all
2、 通過運行垃圾回收器和刪除舊的對象重新打包倉庫。
$ git gc --prune=now
3、 把你所有的修改推送回倉庫。
$ git push --all --force
4、 確保你所有的標籤也是當前最新的:
$ git push --tags --force
via: https://confluence.atlassian.com/bitbucket/maintaining-a-git-repository-321848291.html
作者:atlassian.com 譯者:zhousiyu325 校對:wxy
本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive