遅いGit操作のトラブルシューティング:よくある落とし穴と解決策

ステータス、クローン、フェッチ、プッシュ、フック、ファイルシステム、ネットワーク、リポジトリサイズの原因を切り分けて、遅いGitコマンドを診断します。

遅いGit操作のトラブルシューティング:よくある落とし穴と解決策

Gitの動作が遅い場合、その原因はどのコマンドが遅いかによって異なります。git statusが遅いのは、通常、ローカルファイルシステムまたはインデックスの処理が原因です。git fetchが遅いのは、多くの場合、ネットワーク、リモートのサイズ、またはネゴシエーションが原因です。git checkoutが遅いのは、ファイル数、ウイルススキャン、スパースチェックアウトの問題、または生成されたファイルが原因である可能性があります。git pushが遅いのは、大きなオブジェクト、フック、圧縮、またはリモートサーバーが原因である可能性があります。

したがって、最初の修正はgit gcではありません。最初の修正は、正確な操作を測定することです。

macOSまたはLinuxの場合:

time git status
time git fetch --prune
time git checkout main

PowerShellの場合:

Measure-Command { git status }

コマンドを2回実行します。OSキャッシュがコールドなため、最初の実行は遅くなる可能性があります。最初のgit statusが10秒かかり、2回目が1秒だった場合、ディスクキャッシュの動作が原因である可能性があります。両方とも遅い場合は、さらに調査を続けてください。

Gitには、時間の消費場所を示す組み込みのトレース機能があります:

GIT_TRACE=1 git status
GIT_TRACE_PERFORMANCE=1 git status
GIT_TRACE_PACKET=1 GIT_TRACE=1 git fetch

GIT_TRACE_PACKETはノイズが多いですが、フェッチまたはプッシュがプロトコルネゴシエーション中にハングした場合に役立ちます。プライベートリポジトリのURLやトークンを含むトレース出力を公開チケットに貼り付けないでください。

git statusが遅い場合

git statusはインデックスとワーキングツリーをチェックします。リポジトリに膨大な数のファイルがある場合、ワーキングツリーが遅いファイルシステム上にある場合、ファイルのメタデータの読み取りにコストがかかる場合、または別のプログラムがGitが触れるすべてのファイルをスキャンする場合に遅くなります。

基本から始めましょう:

git status --short
git config --show-origin --get core.fsmonitor
git config --show-origin --get core.untrackedCache
git config --show-origin --get core.preloadIndex

大規模なワーキングツリーの場合、これらの設定は多くのシステムで役立ちます:

git config core.untrackedCache true
git config core.preloadIndex true

最初にローカル設定を使用して、リポジトリごとにテストできるようにします。効果がある場合は、後でグローバルに設定します。

Gitの組み込みファイルシステムモニターは、サポートされているプラットフォームとGitバージョンでフルスキャンを回避することでステータスを高速化できます:

git config core.fsmonitor true

有効にした後にステータスが正しくなくなったり、おかしくなったりした場合は、オフにしてGitを更新してから再試行してください:

git config --unset core.fsmonitor

追跡されていないファイルは隠れた問題になる可能性があります。ビルド出力、依存関係ディレクトリ、生成されたレポート、ローカルログは通常無視する必要があります。Gitが何をスキャンしているかを確認します:

git status --untracked-files=all --short | head -100

node_modules/dist/.venv/target/、または同様の生成されたディレクトリが表示された場合は、適切なパターンを.gitignoreに追加します。ステータスを高速化するためだけにソースファイルを無視しないでください。本当にバージョン管理すべきでないファイルを無視します。

Windowsでは、リアルタイムのウイルススキャンがGitを遅くする一般的な理由です。Gitは.gitとワーキングツリー内の多くの小さなファイルを読み取り、セキュリティソフトウェアが各アクセスを検査する場合があります。組織が許可している場合は、信頼できる開発ワークスペースをリアルタイムスキャンから除外します。信頼できないコードを実行するディレクトリは除外しないでください。

また、アクティブなリポジトリをOneDrive、Dropbox、iCloud Driveなどのクラウド同期フォルダに配置しないでください。同期ツールはファイルをロックし、メタデータを書き換え、Git自身のファイル操作と競合する可能性があります。

クローンまたはフェッチが遅い場合

遅いクローンは、大規模な履歴、多くの大きなブロブ、遅いリモート、またはレイテンシの高いネットワークパスを意味する可能性があります。クローン後にリポジトリサイズを測定します:

git count-objects -vH
du -sh .git 2>/dev/null

CIジョブや一時的な環境では、履歴が必要ない場合にシャロークローンを使用します:

git clone --depth 1 <url>

ブランチビルドの場合:

git clone --depth 1 --branch main <url>

シャロークローンはすべてのワークフローに理想的ではありません。履歴、タグ、マージベース、またはバージョン計算を必要とするコマンドは、失敗したり不完全な回答を生成したりする可能性があります。CIでは、それは許容されることがよくあります。開発者のマシンでは、イライラする可能性があります。

部分クローンは、リポジトリ履歴が必要だがファイルブロブを遅延ダウンロードできる場合に役立ちます:

git clone --filter=blob:none <url>

これは、部分クローンを適切にサポートする最新のGitサーバーで最も効果的です。チームの公式推奨にする前に、ホストでテストしてください。

モノレポの一部のみが必要な場合は、スパースチェックアウトを通常または部分クローンと組み合わせます:

git clone --filter=blob:none --sparse <url>
cd repo
git sparse-checkout set services/api shared/lib

スパースチェックアウトはワーキングツリーのサイズを削減します。すべてのGit操作を魔法のように安くするわけではありませんが、ファイル数が主な問題である場合に役立ちます。

削除されたリモートブランチが多いフェッチの場合は、古い参照を整理します:

git fetch --prune

これをデフォルトにするには:

git config --global fetch.prune true

プッシュが遅い場合

プッシュ速度は、送信する新しいオブジェクトデータの量、ローカルパッキングのコスト、フックの実行、リモートがパックを受け入れる速度に依存します。

誤って大きなファイルをコミットしていないか確認します:

git rev-list --objects --all | sort -k 2 | tail

このコマンドはサイズを表示しないため、大まかです。より詳細な検査には、利用可能な場合はgit-sizergit filter-repo分析コマンドなどのツールを使用します。実用的なポイントは単純です。動画、データベースダンプ、アーカイブ、ビルドアーティファクトが履歴に入った場合、履歴が書き換えられるかプロジェクトがより良いストレージパターンに移行するまで、すべてのクローンがその代償を払う可能性があります。

Git LFSは、プロジェクトに属するが通常のGitブロブとして存在すべきではない大きなバイナリアセットに対する通常の答えです:

git lfs install
git lfs track "*.psd"
git lfs track "*.mp4"
git add .gitattributes

Git LFSは、大きなファイルが履歴に入る前に採用された場合に最も効果的です。既存の履歴を移行することは可能ですが、コミットを書き換え、チームの調整が必要です。

http.postBufferを増やすという古いアドバイスには注意してください。プッシュの問題でよく提案されますが、最新のGitでの一般的な遅さを修正することはほとんどありません。プッシュが特定のHTTPエラーで失敗する場合は、ランダムなバッファ設定を適用する前に、正確なエラー、プロキシ、サーバー制限、Gitバージョンを確認してください。

リポジトリメンテナンス:git gc、コミットグラフ、再パック

Gitはオブジェクトをパックファイルに保存します。時間の経過とともに、ローカルリポジトリはルーズオブジェクトと非効率的なパックを蓄積する可能性があります。Gitは多くのワークフローで自動的にメンテナンスを実行しますが、手動メンテナンスは古いリポジトリやビジーなリポジトリに依然として役立ちます。

安全なメンテナンスコマンドから始めます:

git maintenance run

または古いコマンド:

git gc

git gc --prune=nowを気軽な最初の手段にしないでください。すぐに剪定すると、そうでなければしばらく回復可能な到達不能オブジェクトが削除されます。何をしているかわかっている場合は問題ありませんが、無害なスピードボタンではありません。

大規模な履歴を持つリポジトリの場合、コミットグラフは、log、merge-base、fetchネゴシエーションなどのコマンドで使用される履歴ウォークを改善できます:

git commit-graph write --reachable

最新のGitメンテナンスはこれを自動的に処理する場合があります。バージョンを確認します:

git --version

Gitを最新の状態に保つことは、最も劇的でないパフォーマンス修正の1つです。新しいバージョンは、スパースチェックアウト、部分クローン、ファイルシステムモニタリング、メンテナンス動作を定期的に改善します。

大規模リポジトリとモノレポ

リポジトリが本当に大きいために遅い場合、ローカルの調整だけでは限界があります。ワークフローの変更が必要です。

バイナリが多いリポジトリの場合は、大きなアセットをGit LFSまたはアーティファクトストアに移動します。生成されたファイルの場合は、再構築可能な出力のコミットを停止します。モノレポの場合は、スパースチェックアウトとプロジェクトの境界を理解するビルドツールを使用します。CIの場合は、ジョブが完全な履歴を必要としない限り、フルデプスクローンを避けます。

1つのサービスで作業する開発者にとって便利なモノレポ設定は次のとおりです:

git clone --filter=blob:none --sparse <url>
cd repo
git sparse-checkout set services/billing packages/common

単純なテストジョブのための便利なCI設定は次のとおりです:

git fetch --depth 50 origin main

適切な深さはジョブによって異なります。バージョニングツールが数ヶ月前のタグを使用している場合、深さ1では壊れます。

フックと外部ツール

Gitが遅い部分ではない可能性があります。pre-commitフックは、フォーマッタ、リンター、テスト、シークレットスキャン、依存関係チェックを実行できます。post-checkoutフックはファイルを再構築できます。クレデンシャルヘルパーは、キーチェーンのロック解除を試みている間、一時停止する場合があります。

フックを確認します:

git config --get core.hooksPath
ls -l .git/hooks .githooks 2>/dev/null

リスクを理解している場合にのみ、フックを無効にして一時的に比較します:

git commit --no-verify

コミット以外のコマンドの場合は、メインチェックアウトからチームフックを削除するのではなく、リポジトリのテストコピーでフックを移動または無効にします。

IDEがGitを遅くするがターミナルは高速な場合は、IDEのGit統合を検査します。一部のツールはgit statusを繰り返し実行し、追跡されていないファイルをスキャンしたり、バックグラウンドでブランチ状態を更新したりします。

ネットワークとリモートの確認

リモート操作の場合は、Gitをネットワークパスから分離します。試してみてください:

GIT_TRACE_PERFORMANCE=1 git ls-remote <url>
GIT_TRACE_PERFORMANCE=1 git fetch

git ls-remoteが遅い場合、遅延はリポジトリデータの転送が始まる前に発生します。DNS、プロキシ、VPN、SSH認証、リモートの可用性、クレデンシャルプロンプトを考えてください。ls-remoteが高速だがフェッチが遅い場合、リポジトリデータサイズとネゴシエーションの可能性が高くなります。

SSHリモートの場合は、SSHを直接テストします:

ssh -T [email protected]

実際のGitホストを使用します。HTTPSリモートの場合、クレデンシャルマネージャのプロンプトがGUIウィンドウの背後に隠れている可能性があります。停止したフェッチは認証を待っている可能性があります。

短い決定木

git statusが遅い場合は、追跡されていないファイル、生成されたディレクトリ、ウイルス対策、クラウド同期フォルダ、ファイルシステムモニター、インデックス設定を検査します。

クローンが遅い場合は、シャロークローン、部分クローン、スパースチェックアウト、Git LFS、およびリポジトリ履歴に大きなブロブが含まれているかどうかを検討します。

フェッチが遅い場合は、古い参照を整理し、Gitを更新し、ネットワークトレースを検査し、リモートに多くのブランチやタグがあるかどうかを確認します。

プッシュが遅い場合は、大きな新しいオブジェクト、遅いフック、サーバー側のチェック、ネットワークまたはプロキシの問題を探します。

すべてのGitコマンドが遅い場合は、ディスクの健全性、空き容量、セキュリティソフトウェア、Gitバージョン、およびリポジトリがネットワークマウント上にあるかどうかを確認します。

最善の修正は、測定されたボトルネックに一致するものです。すべての提案を一度に適用すると、Gitパフォーマンスの作業は混乱します。1つのことを変更し、再度測定し、実際に役立つ場合にのみ変更を保持します。

チームレベルの修正は個人の調整に勝る

1人の開発者だけがGitの遅さを経験している場合、ローカル設定とマシンの健全性は良い出発点です。全員がGitの遅さを経験している場合、リポジトリに注意が必要です。個人の調整はしばらく痛みを隠しますが、新しい開発者とCIジョブはコストを支払い続けます。

コミットされるべきではなかった大きなオブジェクト、.gitignoreに属する生成されたディレクトリ、不要な履歴を生かし続ける古いブランチを探します。履歴を書き換える前に、チームと話し合ってください。履歴の書き換えは、すべてのクローンとすべてのオープンブランチに影響します。価値がある場合もありますが、調整が必要です。

正当な大きなアセットを持つリポジトリの場合は、記憶に頼るのではなくポリシーを定義します。例:ソースコードはGitに、デザインのエクスポートはGit LFSに、ビルドアーティファクトはアーティファクトリポジトリに、データベースダンプは管理されたストレージに、ローカルスクラッチファイルは無視します。これらのルールを.gitattributes.gitignoreに配置して、Gitがリポジトリの形状を強制できるようにします。

CIも独自のレビューに値します。多くのパイプラインは、何年も前にコピーされたデフォルトであったため、完全な履歴をクローンします。ジョブが単体テストのみを実行する場合、すべてのタグとすべてのブランチは必要ないかもしれません。ジョブがリリースをビルドする場合、タグは必要かもしれませんが、モノレポ内のすべてのブロブは必要ありません。クローン時間をビルド時間とは別に測定して、リポジトリのコストを可視化します。

簡単なCI監査では次のように質問します:

このジョブは完全な履歴を必要としますか?
タグは必要ですか?
すべてのサブモジュールは必要ですか?
モノレポ内のすべてのディレクトリは必要ですか?
読み取らないLFSファイルをフェッチしますか?

これらに正直に答えることは、あいまいなGitオプションを調整するよりも多くの時間を節約することがよくあります。

最後に、プロジェクトに推奨されるクローンコマンドを文書化します。新しい開発者が部分クローンとスパースチェックアウトを使用する必要がある場合は、READMEにそのように記載します。チェックアウトの前にGit LFSが必要な場合も、それも記載します。1人のシニア開発者のシェル履歴にのみ存在するパフォーマンスガイダンスは、次の人には役立ちません。