azukipochette's weblog

memory dump (mini)

巨大な Git リポジトリで役立つクローンの知識

巨大な Git リポジトリを扱う際に課題となりやすいクローン時間の短縮や、ディスク使用量を削減する方法について解説する。

近年では、様々な理由から巨大な Git リポジトリを扱うケースが増えている。
たとえば、GitHub 上で管理されている 日本語版 Azure Docs のリポジトリ azure-docs.ja-jp では、Azure Docs の日本語記事がすべて含まれているため、4 GB 近い巨大なリポジトリとなっている。

このようなリポジトリに対して貢献したい場合、通常の開発時と同様に完全クローン (Full Clone) する必要は無い。
Git にはシャロー クローン (Shallow Clone)、パーシャル クローン (Partial Clone) といった機能に加えて、スパース チェックアウト (Sparse Checkout) という便利な機能を備えているので、これらの機能を使用するとよい。

実験と結果

どの程度の効果が見込まれるのかは実際のリポジトリを例にしたほうがわかりやすい。
そこで、GitHub 上で管理されている日本語版 Azure Docs のリポジトリを使って実験を行った。

github.com

各方法でクローンした場合の処理時間とディスク使用量の比較表を以下に示す。

なお、実行時間についてはハードウェアの構成やネットワーク状況によっても大きくことなることに加えて、スパース チェックアウトの場合はチェックアウト処理は含んでいないのであくまで参考値として扱ってほしい (それでも爆速/省容量であることがわかるはずだ)。

Full Shallow (depth=1) Partial (blobless) Partial (treeless) Sparse Partial+Sparse
実行時間 04:26.5 00:56.4 01:54.1 01:19.6 03:02.4 00:10.9
Full Clone との比較 100% 472% 233% 335% 146% 2440%
ディスク使用量 (bytes) 3,836,788,432 1,051,743,115 1,181,308,994 1,063,763,634 3,179,861,292 128,503,442
Full Clone との比較 100% 27% 31% 28% 83% 3%

続いて、各方法と実際の実行方法と結果について見ていく。

完全クローン (Full Clone)

単純に git clone を実行すると、完全なクローンがローカルに作成される。

git clone https://github.com/MicrosoftDocs/azure-docs.ja-jp.git full

実行結果を以下に示す。

Cloning into 'full'...
remote: Enumerating objects: 968479, done.
remote: Counting objects: 100% (67154/67154), done.
remote: Compressing objects: 100% (32479/32479), done.
remote: Total 968479 (delta 43253), reused 56056 (delta 34042), pack-reused 901325
Receiving objects: 100% (968479/968479), 2.93 GiB | 14.55 MiB/s, done.
Resolving deltas: 100% (810289/810289), done.
Updating files: 100% (24894/24894), done.

参考情報として、Git-Sizer による分析結果を以下に示す。

Processing blobs: 611123
Processing trees: 329734
Processing commits: 27622
Matching commits to trees: 27622
Processing annotated tags: 0
Processing references: 41
| Name                         | Value     | Level of concern               |
| ---------------------------- | --------- | ------------------------------ |
| Overall repository size      |           |                                |
| * Blobs                      |           |                                |
|   * Total size               |  13.4 GiB | *                              |
|                              |           |                                |
| Biggest objects              |           |                                |
| * Trees                      |           |                                |
|   * Maximum entries      [1] |  1.72 k   | *                              |
| * Blobs                      |           |                                |
|   * Maximum size         [2] |  34.0 MiB | ***                            |
|                              |           |                                |
| Biggest checkouts            |           |                                |
| * Number of directories  [3] |  4.58 k   | **                             |
| * Maximum path length    [3] |   196 B   | *                              |
| * Total size of files    [4] |  1.64 GiB | *                              |

[1]  73096cbd631076a715fc9f97908869d8c7e5fd7c (053c624595ab0dc29c3181d5580999ededb65514:includes)
[2]  bedffc08616540404d9e6275a11a5751688b43dd (refs/remotes/origin/repo_sync_working_branch:articles/search/media/search-create-service-portal/AnimatedGif-AzureSearch.gif)
[3]  249165cef1d1a8ed4fbd9378524786374437c773 (cb3e7bb5b27eb350d9934bab302e5ce4918a1ab2^{tree})
[4]  d63a1d9a82f8008c7ebf935bd0c5f2cd14538ffd (d143e24c40ed15e40065f382d89e6803c40530bd^{tree})

シャロー クローン (Shallow Clone)

シャロー (Shallow) とは "浅い" を意味し、指定した履歴数分だけの浅いクローンがローカルに作成できる。 たとえば、リポジトリ内で管理されているフォルダやファイルには幅広くアクセスしたいが、履歴情報は直近のみでよい、といったような場合に便利なオプションといえる。

下記の場合、--depth 1 を指定して履歴情報として最新のみを保持したクローンを作成している。

git clone --depth 1 https://github.com/MicrosoftDocs/azure-docs.ja-jp.git shallow

実行結果を以下に示す。

Cloning into 'shallow'...
remote: Enumerating objects: 26823, done.
remote: Counting objects: 100% (26823/26823), done.
remote: Compressing objects: 100% (23953/23953), done.
remote: Total 26823 (delta 3879), reused 8600 (delta 2570), pack-reused 0
Receiving objects: 100% (26823/26823), 384.99 MiB | 21.46 MiB/s, done.
Resolving deltas: 100% (3879/3879), done.
Updating files: 100% (24894/24894), done.

パーシャル クローン (Partial Clone)

パーシャル (Partial) とは "部分的" を意味し、クローン時にツリーやブロブなどの情報の一部の情報を除外してクローンすることができる。 パーシャル クローンを利用した省スペースなクローンは以下の 2 種類に分類される。

  • ブロブレス クローン (Blobless Clone)
  • ツリーレス クローン (Treeless Clone)

ブロブレス クローン (Blobless Clone)

ブロブレス クローンはコミットとツリーを取得し、ブロブは必要に応じて取得する方式である。
正確には、HEAD のみブロブが取得され、履歴上のブロブgit diff などのコマンドによって必要になった際に取得される。最新のファイルはビルドなどで必要とするが、履歴上のファイルは必要に応じて取得したいといった開発用途で使えるオプションである。

git clone --filter=blob:none https://github.com/MicrosoftDocs/azure-docs.ja-jp.git blobless

実行結果を以下に示す。

Cloning into 'blobless'...
remote: Enumerating objects: 357356, done.
remote: Counting objects: 100% (30851/30851), done.
remote: Compressing objects: 100% (12095/12095), done.
Rremote: Total 357356 (delta 25309), reused 23517 (delta 18123), pack-reused 326505
), 109.79 MiB | 24.34 MiB/s
Receiving objects: 100% (357356/357356), 112.84 MiB | 24.55 MiB/s, done.
Resolving deltas: 100% (299963/299963), done.
remote: Enumerating objects: 24817, done.
remote: Counting objects: 100% (20056/20056), done.
remote: Compressing objects: 100% (17677/17677), done.
remote: Total 24817 (delta 3698), reused 2523 (delta 2379), pack-reused 4761 eceiving objects: 100% (24817/24817), 380.85 MiB | 7.55 MiB/s
Receiving objects: 100% (24817/24817), 384.11 MiB | 9.52 MiB/s, done.
Resolving deltas: 100% (3853/3853), done.
Updating files: 100% (24894/24894), done.

ツリーレス クローン (Treeless Clone)

ツリーレス クローンはコミットのみを取得し、ツリーとブロブは必要に応じて取得する方式である。
HEAD のブロブが取得されるので最新のコードをビルドする際には便利だが、git log のようなコマンドで履歴を参照すると、ツリーの情報取得が開始されてしまうため、開発時の用途には不向きなオプションといえる。 逆に、自動ビルド時のようにコミット履歴やツリーなどを参照せず、最新のファイルのみを取得したい場合に便利なオプションといえる。

git clone --filter=tree:0 https://github.com/MicrosoftDocs/azure-docs.ja-jp.git treeless

実行結果を以下に示す。

Cloning into 'treeless'...
remote: Enumerating objects: 27622, done.
remote: Counting objects: 100% (1483/1483), done.
remote: Compressing objects: 100% (1474/1474), done.
remote: Total 27622 (delta 9), reused 1402 (delta 9), pack-reused 26139
Receiving objects: 100% (27622/27622), 8.72 MiB | 12.15 MiB/s, done.
Resolving deltas: 100% (1597/1597), done.
remote: Enumerating objects: 2005, done.
remote: Counting objects: 100% (895/895), done.
remote: Compressing objects: 100% (893/893), done.
remote: Total 2005 (delta 1), reused 65 (delta 1), pack-reused 1110
Receiving objects: 100% (2005/2005), 935.11 KiB | 3.64 MiB/s, done.
Resolving deltas: 100% (1/1), done.
remote: Enumerating objects: 24817, done.
remote: Counting objects: 100% (19924/19924), done.
remote: Compressing objects: 100% (17535/17535), done.
remote: Total 24817 (delta 3665), reused 2533 (delta 2389), pack-reused 4893 eceiving objects: 100% (24817/24817), 381.3Receiving objects: 100% (24817/24817), 384.13 MiB | 17.86 MiB/s, done.

Resolving deltas: 100% (3845/3845), done.
Updating files: 100% (24894/24894), done.

スパース チェックアウト (Sparse Checkout)

スパース (Sparse) とは「わずかに」を意味し、スパース チェックアウトを使用することで特定のディレクトリおよびファイルのみをチェックアウト対象にすることができる。
スパース チェックアウトを利用するには、--sparse オプションを使用する。また、そのままではクローン時に自動的にチェックアウトが行われてしまうため --no-checkout オプションを指定して、無効にしておくとよい。

git clone --sparse --no-checkout https://github.com/MicrosoftDocs/azure-docs.ja-jp.git sparse

実行例を以下に示す。

Cloning into 'sparse'...
remote: Enumerating objects: 968479, done.
remote: Counting objects: 100% (67154/67154), done.
remote: Compressing objects: 100% (32441/32441), done.
remote: Total 968479 (delta 43273), reused 56078 (delta 34080), pack-reused 901325 eceiving objects: 100% (968479/968479Receiving objects: 100% (968479/968479), 2.94 GiB | 20.81 MiB/s, done.

Resolving deltas: 100% (810293/810293), done.

その後、git sparse-checkout コマンドで実際に必要なフォルダを指定し、チェックアウトを行う。
articles/devops-project のみを対象とする例を以下に示す。

git sparse-checkout set articles/devops-project
git checkout

実行結果を以下に示す。

remote: Enumerating objects: 45, done.
remote: Counting objects: 100% (18/18), done.
remote: Compressing objects: 100% (18/18), done.
remote: Total 45 (delta 12), reused 0 (delta 0), pack-reused 27 eceiving objects:  91% (41/45)
Receiving objects: 100% (45/45), 2.13 MiB | 10.01 MiB/s, done.
Resolving deltas: 100% (12/12), done.
remote: Enumerating objects: 1, done.
remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 1
Receiving objects: 100% (1/1), 279 bytes | 279.00 KiB/s, done.
Updating files: 100% (48/48), done.
Your branch is up to date with 'origin/master'.

ブロブレス クローンとスパース チェックアウトの組み合わせ

最初の表で示したとおり、実はスパースチェックアウトだけを使用すると、ブロブやツリーなどの過去情報を取得してしまうため、完全クローンに近い実行時間とディスク容量が必要となる。

このため、一般的には上記のブロブレス クローンと組み合わせ、過去のブロブは取得せずに必要なファイルのみスパース チェックアウトするのがよい。

git clone --filter=blob:none --sparse --no-checkout https://github.com/MicrosoftDocs/azure-docs.ja-jp.git blobless+sparse

実行結果を以下に示す。

Cloning into 'blobless+sparse'...
remote: Enumerating objects: 357356, done.
remote: Counting objects: 100% (30851/30851), done.
remote: Compressing objects: 100% (12094/12094), done.
remote: Total 357356 (delta 25298), reused 23528 (delta 18124), pack-reused 326505
Receiving objects: 100% (357356/357356), 112.98 MiB | 24.98 MiB/s, done.
Resolving deltas: 100% (300023/300023), done.

備忘録

この記事は以下の時点での azure-docs.ja-jp リポジトリを対象にした。

github.com

実験用バッチ スクリプト

計測用に作ったバッチ ファイルを以下に示す。

@ECHO OFF

ECHO ::: FULL CLONE :::
ECHO %TIME%
git clone https://github.com/MicrosoftDocs/azure-docs.ja-jp.git full
ECHO %TIME%

ECHO ::: SHALLOW CLONE :::
ECHO %TIME%
git clone --depth 1 https://github.com/MicrosoftDocs/azure-docs.ja-jp.git shallow
ECHO %TIME%

ECHO ::: BLOBLESS CLONE :::
ECHO %TIME%
git clone --filter=blob:none https://github.com/MicrosoftDocs/azure-docs.ja-jp.git blobless
ECHO %TIME%

ECHO ::: TREELESS CLONE :::
ECHO %TIME%
git clone --filter=tree:0 https://github.com/MicrosoftDocs/azure-docs.ja-jp.git treeless
ECHO %TIME%

ECHO ::: SPARSE CLONE :::
ECHO %TIME%
git clone --sparse --no-checkout https://github.com/MicrosoftDocs/azure-docs.ja-jp.git sparse
ECHO %TIME%

ECHO ::: BLOBLESS+SPARSE CLONE :::
ECHO %TIME%
git clone --filter=blob:none --sparse --no-checkout https://github.com/MicrosoftDocs/azure-docs.ja-jp.git blobless+sparse
ECHO %TIME%

参考情報