肥大化してしまったGitリポジトリのパフォーマンス低下を軽減するTIPS
開発プロジェクトが長いとリポジトリが肥大化しがちです.
大きくなったリポジトリではcloneやcheckoutのパフォーマンスが重く、ストレスを感じることが多いと思います.
そこで、大きくなってしまったリポジトリとうまく付き合っていくためのTipsをまとめてみました.
- 1. 最新履歴のみをcloneする(shallow-clone)
- 2. 必要なオブジェクトのみをcloneする(partial-clone)
- 3. 必要なファイルのみをcheckoutする(sparse-checkout)
- 参考情報
1. 最新履歴のみをcloneする(shallow-clone)
通常のcloneではリポジトリ内のすべての履歴を取得しますが、 CIツールなど、最新コミットや一部の履歴のみあれば十分な場合に shallow-clone を使用すると、ダウンロードするリポジトリデータのサイズを抑えることができます.
1-1. shallow-cloneを行う
shallow-cloneを行うには、clone時に以下のオプションを指定します.
# デフォルトブランチで、履歴N個分をclone $ git clone [url] --depth N # 指定したブランチで、履歴N個分をclone $ git clone [url] --single-branch -b [ブランチ名] --depth N # 全てのブランチについて、履歴N個分をclone $ git clone [url] --no-single-branch --depth N
1-2. shallow-cloneされたブランチのupdate
shallow-cloneしたリポジトリのブランチをupdateするには、update-shallow オプションを指定します.
shallow-cloneで取得した起点コミット(grafted)以降の履歴のみを取得することができます.
# 指定ブランチについて現在のコミットより新しい履歴を取得 git fetch --update-shallow [remote名] [ブランチ名]
ブランチ名を省略することですべてのブランチについてshallow-clone起点コミット(grafted)以降の履歴を取得することもできます.
ただし、shallow-cloneが維持されるのは既存のブランチのみで、
ローカルに起点コミットがない(ex. リモートに新規ブランチがある)場合は、新規ブランチについてすべての履歴が取得されてしまいます.
これを防ぐには depth オプションを指定して、新規含むすべての最新コミットを起点コミットとして取得しなおします.
# すべてのブランチについて最新履歴のみ取得した状態にする $ git fetch --update-shallow [--depth=N]
1-3. shallow-cloneの解除
shallow-cloneを解除するには、unshallow オプションをつけてfetchをおこないます.
$ git fetch --unshallow
2. 必要なオブジェクトのみをcloneする(partial-clone)
リポジトリをcloneする際に、Gitの履歴オブジェクトのうち、実データ(blob)やファイル構成(tree)に関する情報は、実際に必要になったタイミングで行うよう設定します.
遅延取得するデータの種類によってはgitの一部機能に制限がかかることがありますが、初回clone時にダウンロードするローカルリポジトリのデータサイズを抑えることができます.
前提として、bareリポジトリのあるサーバ側のgit設定として下記が必要になります.
# bareリポジトリ側にfilterを許可する設定 # ※ --globalをつけてサーバ側の実行ユーザに設定してもOK. $ git config uploadpack.allowfilter true $ git config uploadpack.allowanysha1inwant true
2-1. partial-cloneの種類
cloneから除外するオブジェクトの種類によって以下の2種類があります.
(1) tree-less clone
最新コミットと履歴のみを初回cloneで取得.
(2) blob-less clone
最新コミットと履歴のtreeを取得.
partiah-cloneについては、以下のサイトの解説がわかりやすいです.
github.blog
2-2. partial-cloneの操作
partial-cloneリポジトリは、以下のように設定します.
2-2-1. 新規にcloneするとき
# tree-less clone $ git clone --fliter=tree:none [url] [clone_to] # blob-less clone $ git clone --fliter=blob:none [url] [clone_to]
2-2-2. completeリポジトリからpartial-cloneリポジトリへの変換
既存のリポジトリをblob-lessに変換する例.
# blob-less clone リポジトリへのconvert # 下記の設定を追加 $ git config remote.[remote_name].promisor true $ git config remote.[remote_name].partialclonefilter blob:none # 不要なlocalオブジェクトを破棄. (git のバージョンに注意. filter-toは最近追加されたもの) # filter-toには除外ファイルのprefixを指定する. # ./hoge/fugaのようにファイルパスでprefixを指定すれば、別ディレクトリへの出力も可能. $ git repack -ad --filter=blob:none --filter-to=pack --no-write-bitmap-index # 除外したオブジェクトのファイルを削除 $ rm -f pack-*
2-2-3. partial-cloneリポジトリからcompleteリポジトリへの変換
# partial-cloneのフィルタ設定を削除 $ git config --unset remote.[remote_name].partialclonefilter # リポジトリデータの再fetch $ git fetch --refetch # (任意) refetchの結果、リポジトリサイズが通常のcomplete-cloneリポジトリよりも大きくなることがある. # 重複した情報のオブジェクトをpackから削除して整理する $ git gc --aggressive
3. 必要なファイルのみをcheckoutする(sparse-checkout)
submoduleで他プロジェクトを参照するときなどは、プロジェクトの一部ファイルのみさえあればよいといった状況があります.
sparse-checkoutでは、作業領域に展開するファイルを指定したファイル/ディレクトリのみに限定します.
ブランチを切り替えるときに、すべてのファイルをローカルに再現する必要がなくなるので、checkoutによるブランチ切り替えが高速になります.
3-1. sparse-checkoutを設定する
コマンドで設定する場合は以下の手順を行います.
# sparse-checkoutの適用(.git/info/sparse-checkout の新規作成) $ git sparse-checkout init # chekcout対象を限定する # hogeディレクトリとfugaディレクトリに限定 $ git sparse-checkout set hoge/ fuga/ # 対象の追加など $ git sparse-checkout add hoga/
.git/info/sparse-checkoutファイルを直接作成/編集しても設定ができます.
.gitignoreと同じ書式で対象ディレクトリ/ファイルを指定します.
# ## sparse-checkoutファイルの例 ## # root直下のファイルをcheckout /* # 先頭に!をつけるとNot条件になります # root直下にあるディレクトリはcheckoutしない !/*/ # root直下にあるhogeディレクトリ以下はcheckoutする /hoge/ # root直下にあるfugaディレクトリ以下はcheckoutする /fuga/
ファイル単位で指定する場合は、.git/info/sparse-checkout ファイルを直接編集してから git read-treeで適用します.
# sparse-checkoutファイルを編集してから実施. # sparse-checkoutファイルの編集内容を作業領域に反映. 指定ファイルのみcheckoutされた状態にする. $ git read-tree -mu HEAD
3-2. sparse-checkoutの設定解除
以下コマンドを実行するとsparse-checkoutが解除できます.
# sparse-checkoutの適用解除
$ git sparse-checkout disable
3-3. sparse-checkoutの再設定
info/sparse-checkoutの内容は残っているので、ファイルの内容を再設定します.
# sparse-checkoutの再設定
$ git sparse-checkout reapply