問題の背景
コーディングに集中している最中、すべてが順調に進んでいて、最新の変更を取り込もうと git fetch を実行したとします。しかし、クリーンなアップデートの代わりに、Gitが突然停止します。「参照のロック(locking references)」に関するイライラするようなエラーが表示されます。ブランチ名を確認しても問題なさそうに見えますが、Gitは一向に動作を受け付けてくれません。
これはGitとmacOSの間の根本的な不一致が原因で発生します。Linuxは 'File.txt' と 'file.txt' を2つの異なるエンティティとして扱いますが、macOSは 大文字・小文字を区別しないが、維持はする(case-insensitive but case-preserving) という性質を持っています。'Feature' という名前のフォルダが既に存在する場合、同じディレクトリ内に 'feature' という名前の別のフォルダを作成することはできません。Macにとっては、それらは同じパスなのです。
実際のエラー内容
error: cannot lock ref 'refs/heads/feature/abc': 'refs/heads/feature/ABC' exists; cannot create 'refs/heads/feature/abc'
なぜこれが発生するのか
Gitは単なるツールではなく、ファイルシステムを使用してデータを保存するデータベースです。デフォルトでは、ローカルブランチを .git/refs/heads/ ディレクトリ内の個別のファイルとして保存します。たとえば feature/ABC という名前のブランチの場合、Gitは feature というフォルダを作成し、その中に ABC というファイルを作成します。
コンフリクトは、チームメイトが feature/abc (小文字)をサーバーにプッシュしたときに始まります。あなたがそれをプルしようとすると、Gitは abc という名前の新しいファイルを書き込もうとします。しかし、macOSは ABC と abc を同じファイルと見なすため、Gitはループに陥ります。参照は既に存在しているのに、データが一致しないことに気づき、「cannot lock ref」の失敗につながります。
ロック競合の修正方法
1. 古くなったリモート参照の削除(Prune)
多くの場合、ブランチの「古い」バージョンは既にサーバー上で削除または名前変更されていますが、ローカルマシンがその参照の残骸を保持し続けています。Gitにメモリを同期するよう指示する必要があります。
git remote prune origin
このコマンドは手軽な整理ツールです。リモートに存在しなくなった origin/ 追跡ブランチを削除します。feature/ABC がリモート追跡ブランチのみであった場合、通常はこれで即座に解決します。
2. ローカルの競合を削除する
pruneで解決しない場合、原因はローカルディスク上に存在するブランチである可能性が高いです。衝突の原因となっている具体的な名前を見つける必要があります。
git branch -a | grep -i "feature/abc"
feature/abc と feature/ABC の両方が表示される場合、どちらかを削除する必要があります。Macではこれらを区別できないため、チームの命名規則に合わない方を削除してください。
# 競合しているローカルブランチを強制削除する
git branch -D feature/ABC
3. .gitフォルダの直接クリーンアップ
ファイルシステムの状態が悪化し、標準のGitコマンドでは残骸を消去できないことがあります。このような稀なケースでは、問題のあるファイルを手動で削除できます。これはGitの「内部臓器」を触る作業なので、慎重に行ってください。
- プロジェクトのルートでターミナルを開きます。
- 非表示の参照フォルダに移動します:
cd .git/refs/heads/ - プレフィックス(例:
feature)に一致するディレクトリを探します。 - 競合するブランチ名に一致するファイルを削除します。
コマンド例:
rm .git/refs/heads/feature/ABC
ファイルが削除されたら、再度フェッチを試してください:git fetch --prune。
4. 「最終手段」:git pack-refs
数多くのブランチがあり、エラーが頻発する場合は pack-refs を使用します。このコマンドは、何百もの小さなブランチファイルを packed-refs という単一のフラットなテキストファイルに圧縮します。
git pack-refs --all
ブランチがディスク上の個別のファイルではなく、テキストファイル内の行として扱われるようになるため、macOSのファイルシステムの制限が適用されなくなります。これにより、大文字・小文字の区別の問題を事実上完全に回避できます。
確認作業
修正が正しく適用されたか確認するために、最新の変更をプルします:
git pull origin main
git fetch --all
エラーなくコマンドが終了すれば完了です。最後に git branch -a を実行し、正しい大文字・小文字のブランチのみが残っていることを確認してください。
今後のための対策
- 小文字に統一する: トラブルを未然に防ぎましょう。ブランチ名には小文字のみを使用するようチーム内で合意してください(例:
Feature/Fix-Headerではなくfeature/fix-header)。 - デフォルトでPruneする:
git config --global fetch.prune trueを実行して、自動的に古い参照を削除するように設定します。これにより、手間をかけずにローカル環境をクリーンに保てます。 - 環境の違いを意識する: Macは大文字・小文字を区別しませんが、Linuxのプロダクションサーバーなどは区別します。大文字・小文字の違いは常に意味のあるバグとして扱いましょう。

