この記事は ZOZO Advent Calendar 2022 #2 24日目 の記事です。
Scalarとは
今年の10月にリリースされたgit 2.38に内蔵された大規模リポジトリ向けツールです。
Scalar自体はMicrosoftが以前から開発を進めていたものになります。
- Highlights from Git 2.38 | The GitHub Blog
- microsoft/scalar: Scalar: A set of tools and extensions for Git to allow very large monorepos to run on Git without a virtualization layer
何ができるのか
Scalarは以下の機能を提供します。
- Partial clone
- cloneするときに、すべてのGitオブジェクトをダウンロードしない。それによって取得時間を短縮する。
- 参考) Get up to speed with partial clone and shallow clone | The GitHub Blog
- Background prefetch
- 1時間ごとにリモートリポジトリからGitオブジェクトをダウンロードし、git fetchの実行時間を短縮する。
- 参考) Highlights from Git 2.31 | The GitHub Blog
- Sparse-checkout
- 作業ディレクトリのサイズを制限する。
- 参考) Bring your monorepo down to size with sparse-checkout | The GitHub Blog
- File system monitor
- 最近の更新ファイルを追跡し、ワークツリー全てをスキャンさせないようにする。
- 参考) Improve Git monorepo performance with a file system monitor | The GitHub Blog
- Commit-graph
- コミット・ウォークと到達可能性計算を高速化しgit logなどのコマンドを高速化する。
- 参考) Git's database internals II: commit history queries | The GitHub Blog
- Multi-pack-index
- 複数のパックファイル(複数のGitオブジェクトをまとめて圧縮したバイナリファイル)からオブジェクトを高速に検索できるようになる。
- 参考) Scaling monorepo maintenance | The GitHub Blog
- Incremental repack
- Multi-pack-indexを使用し、実行中のコマンドを中断させずに、パックされたGitデータをより少ないパックファイルにリパックする。
はじめかた
バージョン2.38以降のGitをインストールすればscalarコマンドが使えます。
リモートリポジトリからcloneする際にScalarの機能を有効化する場合は、下記のコマンドを実行します。
scalar clone /path/to/repo
既存のローカルリポジトリに対しては、下記のコマンドを実行します。
cd /path/to/repo
scalar register
比較してみる
試しにcommit数10万を超える Visual Studio Code のリポジトリを使って比較したいと思います。
ちなみに、マシンスペックは下記です。
system_profiler SPHardwareDataType
Hardware:
Hardware Overview:
Model Name: MacBook Pro
Model Identifier: MacBookPro18,1
Chip: Apple M1 Pro
Total Number of Cores: 10 (8 performance and 2 efficiency)
Memory: 32 GB
cloneとstatus
まずは、通常のclone。
time git clone git@github.com:microsoft/vscode.git
Cloning into 'vscode'...
remote: Enumerating objects: 1358701, done.
remote: Counting objects: 100% (51/51), done.
remote: Compressing objects: 100% (42/42), done.
remote: Total 1358701 (delta 16), reused 26 (delta 9), pack-reused 1358650
Receiving objects: 100% (1358701/1358701), 821.71 MiB | 5.73 MiB/s, done.
Resolving deltas: 100% (841866/841866), done.
Updating files: 100% (6382/6382), done.
git clone git@github.com:microsoft/vscode.git 53.98s user 21.04s system 48% cpu 2:35.57 total
git statusを実行。
time git status
On branch main
Your branch is up to date with 'origin/main'.
nothing to commit, working tree clean
git status 0.02s user 0.15s system 69% cpu 0.248 total
次にScalarを使ったclone。
time scalar clone git@github.com:microsoft/vscode.git
Initialized empty Git repository in /Users/hoge
warning: fetch normally indicates which branches had a forced update,
but that check has been disabled; to re-enable, use '--show-forced-updates'
flag or run 'git config fetch.showForcedUpdates true'
warning: fetch normally indicates which branches had a forced update,
but that check has been disabled; to re-enable, use '--show-forced-updates'
flag or run 'git config fetch.showForcedUpdates true'
remote: Enumerating objects: 23, done.
remote: Counting objects: 100% (18/18), done.
remote: Compressing objects: 100% (17/17), done.
remote: Total 23 (delta 1), reused 1 (delta 1), pack-reused 5
Receiving objects: 100% (23/23), 261.01 KiB | 569.00 KiB/s, done.
Resolving deltas: 100% (1/1), done.
warning: fetch normally indicates which branches had a forced update,
but that check has been disabled; to re-enable, use '--show-forced-updates'
flag or run 'git config fetch.showForcedUpdates true'
Updating files: 100% (23/23), done.
branch 'main' set up to track 'origin/main'.
Already on 'main'
Your branch is up to date with 'origin/main'.
scalar clone git@github.com:microsoft/vscode.git 9.42s user 4.78s system 38% cpu 36.465 total
git statusを実行。
time git status
On branch main
Your branch is up to date with 'origin/main'.
You are in a sparse checkout with 1% of tracked files present.
nothing to commit, working tree clean
git status 0.01s user 0.02s system 12% cpu 0.180 total
Enumerating objectsが非常に少なくなり、clone時間が1/5以下に短縮されました。またstatusも早くなっているのが確認できました。
config
.git/configを比較します。
まずは、通常のcloneで取得したリポジトリのconfig。
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[remote "origin"]
url = git@github.com:microsoft/vscode.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
remote = origin
merge = refs/heads/main
次にScalarを使ったcloneで取得したリポジトリのconfig。
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
FSCache = true
multiPackIndex = true
preloadIndex = true
untrackedCache = true
autoCRLF = false
safeCRLF = false
fsmonitor = true
[remote "origin"]
url = git@github.com:microsoft/vscode.git
fetch = +refs/heads/*:refs/remotes/origin/*
promisor = true
partialCloneFilter = blob:none
[extensions]
worktreeConfig = true
[am]
keepCR = true
[credential "https://dev.azure.com"]
useHttpPath = true
[credential]
validate = false
[gc]
auto = 0
[gui]
GCWarning = false
[index]
threads = true
version = 4
[merge]
stat = false
renames = true
[pack]
useBitmaps = false
useSparse = true
[receive]
autoGC = false
[feature]
manyFiles = false
experimental = false
[fetch]
unpackLimit = 1
writeCommitGraph = false
showForcedUpdates = false
[status]
aheadBehind = false
[commitGraph]
generationVersion = 1
[log]
excludeDecoration = refs/prefetch/*
[branch "main"]
remote = origin
merge = refs/heads/main
[maintenance]
auto = false
strategy = incremental
Scalarによって、Partial clone(partialCloneFilter)やFile system monitor(fsmonitor)、Multi-pack-index(multiPackIndex)などの設定が追加されているのがわかります。
また、先ほどcloneした際に出ていたwarningはScalarがshowForcedUpdatesオプションをfalseにしたためということがわかりました。
ファイル
ダウンロードしたファイルを比較してみます。
まずは、通常のcloneでダウンロードしたファイル。
ls -laf
total 1736
drwxr-xr-x 38 takaaki.sato staff 1216 12 24 21:25 .
drwxr-xr-x 4 takaaki.sato staff 128 12 24 21:23 ..
-rw-r--r-- 1 takaaki.sato staff 181356 12 24 21:25 ThirdPartyNotices.txt
-rw-r--r-- 1 takaaki.sato staff 101 12 24 21:25 .yarnrc
drwxr-xr-x 11 takaaki.sato staff 352 12 24 21:25 test
drwxr-xr-x 7 takaaki.sato staff 224 12 24 21:25 resources
-rw-r--r-- 1 takaaki.sato staff 116 12 24 21:25 .lsifrc.json
drwxr-xr-x 9 takaaki.sato staff 288 12 24 21:25 cli
drwxr-xr-x 6 takaaki.sato staff 192 12 24 21:25 .devcontainer
-rw-r--r-- 1 takaaki.sato staff 365 12 24 21:25 .editorconfig
drwxr-xr-x 98 takaaki.sato staff 3136 12 24 21:25 extensions
-rw-r--r-- 1 takaaki.sato staff 6861 12 24 21:25 README.md
-rw-r--r-- 1 takaaki.sato staff 153 12 24 21:25 .mention-bot
-rw-r--r-- 1 takaaki.sato staff 509411 12 24 21:25 yarn.lock
-rw-r--r-- 1 takaaki.sato staff 746 12 24 21:25 tsfmt.json
-rw-r--r-- 1 takaaki.sato staff 227 12 24 21:25 .gitignore
-rw-r--r-- 1 takaaki.sato staff 9462 12 24 21:25 package.json
-rw-r--r-- 1 takaaki.sato staff 5696 12 24 21:25 CONTRIBUTING.md
-rw-r--r-- 1 takaaki.sato staff 6 12 24 21:25 .nvmrc
drwxr-xr-x 29 takaaki.sato staff 928 12 24 21:25 scripts
drwxr-xr-x 14 takaaki.sato staff 448 12 24 21:25 .github
-rw-r--r-- 1 takaaki.sato staff 167 12 24 21:25 .gitattributes
-rw-r--r-- 1 takaaki.sato staff 382 12 24 21:25 gulpfile.js
-rw-r--r-- 1 takaaki.sato staff 1306 12 24 21:25 .eslintignore
drwxr-xr-x 34 takaaki.sato staff 1088 12 24 21:25 build
-rw-r--r-- 1 takaaki.sato staff 2758 12 24 21:25 product.json
drwxr-xr-x 24 takaaki.sato staff 768 12 24 21:25 .eslintplugin
-rw-r--r-- 1 takaaki.sato staff 265 12 24 21:25 .mailmap
-rw-r--r-- 1 takaaki.sato staff 699 12 24 21:25 .git-blame-ignore
drwxr-xr-x 13 takaaki.sato staff 416 12 24 22:44 .git
-rw-r--r-- 1 takaaki.sato staff 1109 12 24 21:25 LICENSE.txt
drwxr-xr-x 10 takaaki.sato staff 320 12 24 21:25 .vscode
-rw-r--r-- 1 takaaki.sato staff 60677 12 24 21:25 cgmanifest.json
-rw-r--r-- 1 takaaki.sato staff 15092 12 24 21:25 .eslintrc.json
-rw-r--r-- 1 takaaki.sato staff 2780 12 24 21:25 SECURITY.md
-rw-r--r-- 1 takaaki.sato staff 20522 12 24 21:25 cglicenses.json
drwxr-xr-x 22 takaaki.sato staff 704 12 24 21:25 src
drwxr-xr-x 6 takaaki.sato staff 192 12 24 21:25 remote
次にScalarを使ったcloneでダウンロードしたファイル。
cd src
ls -laf
total 1736
drwxr-xr-x 26 takaaki.sato staff 832 12 24 21:38 .
drwxr-xr-x 3 takaaki.sato staff 96 12 24 21:37 ..
-rw-r--r-- 1 takaaki.sato staff 181356 12 24 21:38 ThirdPartyNotices.txt
-rw-r--r-- 1 takaaki.sato staff 101 12 24 21:38 .yarnrc
-rw-r--r-- 1 takaaki.sato staff 116 12 24 21:38 .lsifrc.json
-rw-r--r-- 1 takaaki.sato staff 365 12 24 21:38 .editorconfig
-rw-r--r-- 1 takaaki.sato staff 6861 12 24 21:38 README.md
-rw-r--r-- 1 takaaki.sato staff 153 12 24 21:38 .mention-bot
-rw-r--r-- 1 takaaki.sato staff 509411 12 24 21:38 yarn.lock
-rw-r--r-- 1 takaaki.sato staff 746 12 24 21:38 tsfmt.json
-rw-r--r-- 1 takaaki.sato staff 227 12 24 21:38 .gitignore
-rw-r--r-- 1 takaaki.sato staff 9462 12 24 21:38 package.json
-rw-r--r-- 1 takaaki.sato staff 5696 12 24 21:38 CONTRIBUTING.md
-rw-r--r-- 1 takaaki.sato staff 6 12 24 21:38 .nvmrc
-rw-r--r-- 1 takaaki.sato staff 167 12 24 21:38 .gitattributes
-rw-r--r-- 1 takaaki.sato staff 382 12 24 21:38 gulpfile.js
-rw-r--r-- 1 takaaki.sato staff 1306 12 24 21:38 .eslintignore
-rw-r--r-- 1 takaaki.sato staff 2758 12 24 21:38 product.json
-rw-r--r-- 1 takaaki.sato staff 265 12 24 21:38 .mailmap
-rw-r--r-- 1 takaaki.sato staff 699 12 24 21:38 .git-blame-ignore
drwxr-xr-x 15 takaaki.sato staff 480 12 24 22:45 .git
-rw-r--r-- 1 takaaki.sato staff 1109 12 24 21:38 LICENSE.txt
-rw-r--r-- 1 takaaki.sato staff 60677 12 24 21:38 cgmanifest.json
-rw-r--r-- 1 takaaki.sato staff 15092 12 24 21:38 .eslintrc.json
-rw-r--r-- 1 takaaki.sato staff 2780 12 24 21:38 SECURITY.md
-rw-r--r-- 1 takaaki.sato staff 20522 12 24 21:38 cglicenses.json
Scalarの特徴の1つですが、リポジトリにsrcディレクトリが自動で作成され、その下にファイルがダウンロードされます。
さらに、ファイルのみでディレクトリが無いことが確認できます。これはPartial cloneとSparse-checkoutによって、ダウンロードするオブジェクトを制限しているためです。これによって高速なcloneができるようです。
ちなみに partialCloneFilter = blob:none
とは最新のcommitにおけるblobオブジェクト(ファイルを圧縮したGitオブジェクト)を除き、過去のblobオブジェクトをcloneしない。という設定になります。これを tree:0
にすると、blobオブジェクトに加えて過去のtreeオブジェクトもダウンロードしない設定になります。
開発時には blob:none
を設定し、ビルド環境などで最新のcommitのみを使用するだけならば tree:0
を設定するのが良さそうです。
Sparse-checkoutについて
特定のディレクトリをダウンロードするには git sparse-checkout
を実行します。
git sparse-checkout set src
remote: Enumerating objects: 3917, done.
remote: Counting objects: 100% (1906/1906), done.
remote: Compressing objects: 100% (1835/1835), done.
remote: Total 3917 (delta 238), reused 71 (delta 71), pack-reused 2011
Receiving objects: 100% (3917/3917), 10.15 MiB | 2.65 MiB/s, done.
Resolving deltas: 100% (440/440), done.
warning: fetch normally indicates which branches had a forced update,
but that check has been disabled; to re-enable, use '--show-forced-updates'
flag or run 'git config fetch.showForcedUpdates true'
Updating files: 100% (3963/3963), done.
このように余計なファイルはダウンロードせず、リポジトリのサイズを少ないままにできます。
まとめ
ZOZOTOWNがサービスを開始してから18年が経ち、弊社にも巨大なリポジトリが複数存在します。Scalarによって普段の開発スピードやCI/CDの効率化が期待できそうです。
まだまだ実験的な機能も含まれているそうなので、今後も注目していきたいと思います。
25日の大トリは、 @gold-kou さんの記事です。