大規模リポジトリの課題
リポジトリが大きくなると、様々な問題が発生します:
🐌 パフォーマンスの低下
- 「git cloneに30分以上かかる」
- 「git statusが遅すぎて使えない」
- 「ブランチ切り替えに数分かかる」
💾 ストレージの問題
- 「リポジトリサイズが数GB〜数十GB」
- 「ローカルディスクを圧迫」
- 「CI/CDでのチェックアウトが遅い」
🗂️ 管理の複雑さ
- 「必要ないファイルまでダウンロードされる」
- 「履歴が膨大で追跡が困難」
- 「大きなバイナリファイルがリポジトリを肥大化」
Git LFS(Large File Storage)
Git LFSとは
Git LFSは、大きなファイルを効率的に管理するための拡張機能です。実際のファイルはリモートストレージに保存し、リポジトリにはポインタファイルのみを保存します。
LFSのセットアップ
# Git LFSをインストール
git lfs install
# 特定のファイルタイプをLFSで管理
git lfs track "*.psd"
git lfs track "*.zip"
git lfs track "assets/videos/*"
# .gitattributesが作成される
cat .gitattributes
# *.psd filter=lfs diff=lfs merge=lfs -text
# *.zip filter=lfs diff=lfs merge=lfs -text
# 既存のファイルをLFSに移行
git lfs migrate import --include="*.mp4,*.mov"
LFSの使用例
# LFSで管理されているファイルを確認
git lfs ls-files
# LFSファイルの詳細情報
git lfs status
# 特定のファイルのみダウンロード
git lfs pull --include="assets/images/*"
# LFSファイルをスキップしてクローン
GIT_LFS_SKIP_SMUDGE=1 git clone <repository>
Sparse-checkout(部分的チェックアウト)
Sparse-checkoutとは
必要なディレクトリやファイルのみをチェックアウトする機能です。モノレポなど、巨大なリポジトリの一部だけを扱いたい場合に有効です。
基本的な使い方
# sparse-checkoutを有効化
git sparse-checkout init --cone
# 必要なディレクトリを指定
git sparse-checkout set src tests docs
# 設定を確認
git sparse-checkout list
# パターンを追加
git sparse-checkout add lib/core
# 無効化
git sparse-checkout disable
高度な設定
# .git/info/sparse-checkout ファイルを直接編集
cat > .git/info/sparse-checkout << EOF
# 含めるパス
/src/
/tests/
/docs/
!/docs/legacy/ # 除外
*.md # すべてのMarkdownファイル
EOF
# 設定を適用
git sparse-checkout reapply
Shallow Clone(浅いクローン)
履歴を限定したクローン
# 最新の履歴のみクローン(深さ1)
git clone --depth 1 <repository>
# 特定の深さまでクローン
git clone --depth 100 <repository>
# 特定のブランチのみ
git clone --single-branch --branch main <repository>
# 後から履歴を取得
git fetch --unshallow
タグを含むshallow clone
# タグなしでクローン
git clone --depth 1 --no-tags <repository>
# 特定のタグからの履歴
git clone --shallow-since="2023-01-01" <repository>
パフォーマンス最適化
1. Git設定の最適化
# ファイルシステムキャッシュを有効化
git config core.fscache true
# 並列処理を有効化
git config core.preloadindex true
git config index.threads true
# パックファイルの最適化
git config pack.threads 0 # CPUコア数を自動検出
git config pack.windowMemory 512m
# 差分アルゴリズムの最適化
git config diff.algorithm histogram
2. 定期的なメンテナンス
# ガベージコレクション
git gc --aggressive --prune=now
# パックファイルの最適化
git repack -a -d -f --depth=250 --window=250
# 不要な参照を削除
git remote prune origin
# reflogの整理
git reflog expire --expire=now --all
3. 分割戦略
# サブモジュールで分割
git submodule add <repository> modules/component
# subtreeで分割(履歴を保持)
git subtree split --prefix=lib/old-module -b old-module-branch
モノレポの管理
ワークスペース戦略
# lerna.json(JavaScript)
{
"packages": ["packages/*"],
"version": "independent",
"npmClient": "npm",
"command": {
"publish": {
"ignoreChanges": ["*.md", "tests/**"]
}
}
}
効率的なCI/CD
# 変更されたパッケージのみビルド
name: Monorepo CI
on:
pull_request:
paths:
- 'packages/**'
jobs:
changed:
runs-on: ubuntu-latest
outputs:
packages: ${{ steps.filter.outputs.changes }}
steps:
- uses: dorny/paths-filter@v2
id: filter
with:
filters: |
package-a: packages/package-a/**
package-b: packages/package-b/**
実践演習
CommandAcademy Terminal
Welcome to CommandAcademy Terminal!
Type "help" to see available commands.
user@cmdac:~$
█
ファイルツリー
/
etc
hosts35B
passwd76B
home
user
tmp
usr
bin
share
var
log
トラブルシューティング
Git LFSの問題
# LFSオブジェクトが見つからない
git lfs fetch --all
# LFSのポインタファイルが表示される
git lfs pull
# LFSの帯域制限
git config lfs.concurrenttransfers 3
パフォーマンスの問題
# インデックスの破損
rm .git/index
git reset
# packファイルの破損
git fsck --full
rm -f .git/objects/pack/pack-*.idx
git index-pack .git/objects/pack/pack-*.pack
ベストプラクティス
1. リポジトリ設計
- 分割を検討: 50GB以上は分割を推奨
- バイナリファイル: 必ずLFSを使用
- 履歴の整理: 定期的にgc実行
2. 開発フロー
# 開発者向けREADME
cat > DEVELOPMENT.md << 'EOF'
# 開発環境セットアップ
## 最小構成でのクローン
```bash
git clone --depth 1 --single-branch <repo>
cd <repo>
git sparse-checkout init --cone
git sparse-checkout set <必要なディレクトリ>
完全なクローン
git clone <repo>
cd <repo>
git lfs pull
EOF
### 3. CI/CD最適化
```yaml
# 浅いクローンでCI高速化
- uses: actions/checkout@v3
with:
fetch-depth: 1
lfs: false # LFSファイルをスキップ
まとめ
大規模リポジトリの管理には、適切なツールと戦略が必要です:
- Git LFS: バイナリファイルの効率的な管理
- Sparse-checkout: 必要な部分のみチェックアウト
- Shallow clone: 履歴を限定してクローン
- 最適化設定: パフォーマンスチューニング
これらを組み合わせることで、数百GBのリポジトリでも快適に作業できます。
次のレッスンでは、実際の問題に対処する「トラブルシューティング」について学びます。