kakakakakku blog

Weekly Tech Blog: Keep on Learning!

モノリポ時代に知っておくと便利な「git sparse-checkout」

今まで使っていなかった Git コマンドを学んでいく.今回は git sparse-checkout を試す.

git-scm.com

git sparse-checkout とは?

コマンド名にある sparse「わずかな」という意味で,Git リポジトリの「一部」を取得できる.git clone --depth を使ってコミットを刈り取るのではなく,指定したディレクトリを取得する.モノリポ(モノレポ)構成のときに効果的に使える.git sparse-checkout は歴史的な変化もあり,今年になって改善されている.記事を検索すると Git 2.25 より前の git sparse-checkout を紹介した記事が多いように感じたため,今回は Git 2.26.2 を使って試した.

derrickstolee/sparse-checkout-example を使う

git sparse-checkout を試すために,そこそこ大きめな GitHub リポジトリを探していた.以下の GitHub Blog の記事を読んだところ git sparse-checkout を試す手順(Git 2.25 前提)と,サンプルリポジトリが載っていたため,今回は derrickstolee/sparse-checkout-example リポジトリ(モノリポ構成)を使うことにした.

github.blog

github.com

1. 既存リポジトリで git sparse-checkout を使う

まず,既存リポジトリ(git clone をした状態)で git sparse-checkout を使っていく.git clone をして,ファイル数をカウントすると 1585個 となる.ディレクトリ構成は GitHub Blog に載っている画像を見ればすぐ理解できる.tree で簡単に紹介すると,以下のようにモノリポ構成(2階層)になっている.例えば client/android ディレクトリや web/browser ディレクトリなど.

$ git clone git@github.com:derrickstolee/sparse-checkout-example.git
Cloning into 'sparse-checkout-example'...
remote: Enumerating objects: 21, done.
remote: Counting objects: 100% (21/21), done.
remote: Compressing objects: 100% (19/19), done.
remote: Total 1901 (delta 2), reused 11 (delta 1), pack-reused 1880
Receiving objects: 100% (1901/1901), 170.93 MiB | 2.56 MiB/s, done.
Resolving deltas: 100% (177/177), done.
Updating files: 100% (1560/1560), done.

$ cd sparse-checkout-example

$ find . -type f | wc -l
    1585

$ tree -L 2
.
├── LICENSE.md
├── README.md
├── bootstrap.sh
├── client
│   ├── README
│   ├── android
│   ├── electron
│   └── iOS
├── service
│   ├── README
│   ├── common
│   ├── identity
│   ├── items
│   └── list
└── web
    ├── README
    ├── browser
    ├── editor
    └── friends

さっそく,git sparse-checkout init --cone を実行して sparse-checkout を有効化する.--cone オプションはパターンマッチによりディレクトリ探索のパフォーマンスを改善するオプションとして付けておく.直後にファイル数をカウントすると,なんと 30個 に減っている.client/android ディレクトリや web/browser ディレクトリも除外されている.設定は .git/info/sparse-checkout ファイルにあり,意味としては「作業ディレクトリをルートにあるファイルに制限する」となる.

$ git sparse-checkout init --cone
 
$ find . -type f | wc -l
      30

$ tree -L 2
.
├── LICENSE.md
├── README.md
└── bootstrap.sh

$ cat .git/info/sparse-checkout
/*
!/*/

今のままだと重要なコードも除外されているため,例えば「Android アプリを開発するチーム」の場合は,git sparse-checkout set client/android を実行して,ディレクトリを設定できる.実際にファイル数は 56個 になり,client/android ディレクトリを参照できるようになる.

$ git sparse-checkout set client/android

$ find . -type f | wc -l
      56

$ tree -L 2
.
├── LICENSE.md
├── README.md
├── bootstrap.sh
└── client
    ├── README
    └── android

$ cat .git/info/sparse-checkout
/*
!/*/
/client/
!/client/*/
/client/android/

次に「フロントエンドを開発するチーム」の場合は,git sparse-checkout set service web/browser を実行する.すると service ディレクトリと web/browser ディレクトリを参照できるようになる.set は上書きする仕様になっているため,既に client/android ディレクトリは参照できなくなっている.さらに .git/info/sparse-checkout ファイルを見なくても git sparse-checkout list を使えばディレクトリを確認できる.

$ git sparse-checkout set service web/browser

$ find . -type f | wc -l
     636

$ tree -L 2
.
├── LICENSE.md
├── README.md
├── bootstrap.sh
├── service
│   ├── README
│   ├── common
│   ├── identity
│   ├── items
│   └── list
└── web
    ├── README
    └── browser

$ git sparse-checkout list
service
web/browser

なお,Git 2.26 から git sparse-checkout add も使えるため,ディレクトリを追加するときに毎回 git sparse-checkout set をし直していた手順をシンプルにできる.次の手順で add を使っていく.

2. git clone --filter=blob:none --no-checkout と組み合わせる

git clone をするときに --filter=blob:none オプションを付けると,オブジェクトのダウンロードをせずに clone できる(Partial Clone とも言う).さらに --no-checkout オプションを付けると,チェックアウトをせずに clone できる.新しくリポジトリを取得する場合は git clone --filter=blob:none --no-checkout と組み合わせるとオーバーヘッドを減らして,モノリポを管理できる.

$ git clone --filter=blob:none --no-checkout git@github.com:derrickstolee/sparse-checkout-example.git
Cloning into 'sparse-checkout-example'...
remote: Enumerating objects: 13, done.
remote: Counting objects: 100% (13/13), done.
remote: Compressing objects: 100% (12/12), done.
remote: Total 373 (delta 1), reused 7 (delta 1), pack-reused 360
Receiving objects: 100% (373/373), 76.78 KiB | 644.00 KiB/s, done.
Resolving deltas: 100% (18/18), done.

$ cd sparse-checkout-example

$ find . -type f | wc -l
      29

同じように git sparse-checkout init --cone を実行して sparse-checkout を有効化する.今回は add を使って client/android ディレクトリと service ディレクトリと web/browser ディレクトリを参照できるようにする.git sparse-checkout の手順は同じだけど,オブジェクトのダウンロードを後回しにできる点はメリットと言える.

$ git sparse-checkout init --cone

$ git sparse-checkout add client/android

$ git sparse-checkout add service web/browser

$ find . -type f | wc -l
     667

$ tree -L 2
.
├── LICENSE.md
├── README.md
├── bootstrap.sh
├── client
│   ├── README
│   └── android
├── service
│   ├── README
│   ├── common
│   ├── identity
│   ├── items
│   └── list
└── web
    ├── README
    └── browser

まとめ

今まで使っていなかった Git コマンドとして,今回は git sparse-checkout を試した.モノリポ(モノレポ)構成のときに効果的に使える.もし新しくリポジトリを取得する場合は git clone --filter=blob:none --no-checkout と組み合わせるとオーバーヘッドを減らして,素早く clone できる点も覚えておくと良さそう!