【完全ガイド】git error: cannot lock ref の原因と解決方法|stale locks・packed-refs・namespace衝突まで徹底解説

【完全ガイド】git error: cannot lock ref の原因と解決方法|stale locks・packed-refs・namespace衝突まで徹底解説

Git の pull / fetch / push で発生する厄介なエラー:

$ git pull
error: cannot lock ref 'refs/remotes/origin/main': Unable to create '.git/refs/remotes/origin/main.lock': File exists.

# または
error: cannot lock ref 'refs/remotes/origin/feature/login': 'refs/remotes/origin/feature' exists; cannot create 'refs/remotes/origin/feature/login'

# または
error: cannot lock ref 'refs/heads/main': unable to resolve reference 'refs/heads/main': reference broken

index.lock の姉妹エラーですが、こちらは ref(reference)のロックに関するもの。エラーメッセージも複数のパターンがあり、原因も5種類以上に分かれます:

  • stale lock file(前コマンド異常終了)
  • 大文字小文字衝突(macOS / Windows)
  • packed-refs 破損
  • 並列操作(IDE + CLI)
  • namespace collision(feature vs feature/login)

現場では:

  • git fetch 後に急に発生
  • remote branch を消したのに残る
  • case-sensitive な filesystem で頻発
  • force push 後のリファレンス不整合
  • CI/CD で謎に発生

このエラーは、単純に lock ファイル削除だけでは解決しないケースが多く、原因を正しく判別する必要があります。特にnamespace collisionfeaturefeature/login のような競合)は、削除しても再発するため、根本的な対処が必要です。

本記事では、cannot lock ref完全な原因と解決方法を、リファレンスとして実用的に整理します。ref lock の仕組み、5大原因、git update-ref -dgit fetch --prunegit gcgit pack-refs の使い方、実践シナリオ、予防のベストプラクティス、FAQまで完全網羅。この1本で ref lock 関連エラーを根本から解決できるようになります。


目次

結論:原因は5パターン

時間がない方向けに、最速の対処を先に示します。

5大原因と最速解決

#原因症状最速解決
stale lock file前コマンド異常終了lock ファイル削除
namespace collisionref A と A/B が衝突片方削除
packed-refs 破損reference brokengit pack-refs
並列操作IDE + CLI待機・停止
stale remote refs削除されたリモートgit fetch --prune

一括対処(安全確認後)

# 1. Git プロセス確認
ps aux | grep git

# 2. lock ファイル一括削除
find .git -name "*.lock" -type f -delete

# 3. remote 同期
git fetch --prune

# 4. 検証
git fsck --full

エラーメッセージ別対処

# パターンA: Unable to create ... .lock: File exists
find .git -name "*.lock" -delete

# パターンB: 'refs/xxx' exists; cannot create 'refs/xxx/yyy'
git update-ref -d refs/xxx

# パターンC: reference broken
git pack-refs --all --prune

# パターンD: stale remote
git fetch --prune

詳細は以下で解説します。


まず理解する:ref と lock の仕組み

Git ref(reference)とは

ref = commit へのポインタ。ブランチもタグも、実はすべて ref:

.git/refs/
├── heads/           ← ローカルブランチ
│   ├── main         ← ファイル:SHA を1行だけ含む
│   └── feature
├── remotes/         ← リモートトラッキング
│   └── origin/
│       └── main
└── tags/            ← タグ
    └── v1.0

各ファイルの中身:

cat .git/refs/heads/main
# abc1234567890abcdef1234567890abcdef123456

ただの40文字の SHA が1行。これがブランチの実体。

ref lock の仕組み

ref を更新する時、Git は専用の lock ファイルを作成:

Before:
.git/refs/heads/main         # 通常状態

During update:
.git/refs/heads/main
.git/refs/heads/main.lock    # ロック中

After:
.git/refs/heads/main         # 更新完了、lock 削除

同時更新を防ぐため。index.lock と同じ設計。

packed-refs との関係

Git は定期的にloose refspacked-refsに統合:

# loose refs(1ファイル1 ref)
.git/refs/heads/main
.git/refs/heads/develop
.git/refs/heads/feature-1

# ↓ git gc or git pack-refs

# packed-refs(1ファイルにまとめる、高速化)
.git/packed-refs

大規模リポジトリでは packed-refs 中心。これが破損すると別種のエラー。


【原因①】stale lock file

症状

最頻出パターン:

git pull
error: cannot lock ref 'refs/remotes/origin/main': 
Unable to create '.git/refs/remotes/origin/main.lock': File exists.

診断

# 全 lock ファイル一覧
find .git -name "*.lock" -type f

# 出力例:
# .git/refs/remotes/origin/main.lock
# .git/refs/heads/feature.lock
# .git/packed-refs.lock

解決

Git プロセス確認:

ps aux | grep git
# 動いていなければ、lock 削除OK

個別削除:

rm -f .git/refs/remotes/origin/main.lock

一括削除:

find .git -name "*.lock" -type f -delete

⚠️ 他の Git プロセスが動いていないことを必ず確認

なぜ発生するか

  • Ctrl+C で中断した git fetch / pull
  • ネットワークタイムアウト(fetch 中に切断)
  • IDE の自動 fetch がクラッシュ
  • ディスクフルなどのシステムエラー

プロセス管理(ps aux | grep gitpkill)の使い方は、Linux kill vs pkill vs killall の記事、ss コマンドの使い方の記事も参照してください。


【原因②】namespace collision(超要注意)

症状

片方だけ削除しても再発する、厄介な問題:

git fetch
error: cannot lock ref 'refs/remotes/origin/feature/login': 
'refs/remotes/origin/feature' exists; 
cannot create 'refs/remotes/origin/feature/login'

なぜ発生するか

Git の ref はファイルシステムのパスとして保存されます:

  • refs/heads/featureファイル
  • refs/heads/feature/loginディレクトリ feature/ の中の login ファイル

同じ名前でファイルとディレクトリは共存できない(OS の制約)。

❌ 不可能な状態:
.git/refs/heads/feature       ← ファイル
.git/refs/heads/feature/      ← ディレクトリ
    └── login                 ← ファイル

診断

# ref 一覧
ls -la .git/refs/heads/
ls -la .git/refs/remotes/origin/

# packed-refs も確認
grep feature .git/packed-refs

両方に存在すれば namespace collision。

解決

方法A: 古い ref を削除

# バックアップ(念のため)
git branch feature-backup origin/feature

# ref 削除
git update-ref -d refs/heads/feature
# または
git update-ref -d refs/remotes/origin/feature

# 再 fetch
git fetch

方法B: リモート側で名前変更

チームで合意して、featurefeature-main などにリネーム。

方法C: 特定 ref を除外

.git/config:

[remote "origin"]
    fetch = +refs/heads/*:refs/remotes/origin/*
    fetch = ^refs/heads/feature

予防策

ブランチ命名規則を明確に:

  • 親子関係を避ける
  • feature/login, feature/logout → OK(feature 自体は使わない)
  • feature, feature/x → NG

【原因③】packed-refs 破損

症状

git pull
error: cannot lock ref 'refs/heads/main': 
unable to resolve reference 'refs/heads/main': reference broken

.git/packed-refs が壊れた or 不整合。

診断

# packed-refs の中身
cat .git/packed-refs
# 予期しない内容、または途中で切れている

# 整合性チェック
git fsck --full
# 出力にエラーメッセージ

解決

方法A: git pack-refs で再作成

git pack-refs --all --prune

すべての loose refs を再度 packed-refs に統合、古い packed-refs 上書き。

方法B: packed-refs 削除して再構築

# バックアップ
cp .git/packed-refs .git/packed-refs.backup

# 削除
rm .git/packed-refs

# 再構築(loose refs から)
git pack-refs --all --prune

方法C: git fsck で検証

git fsck --full
# エラーがあれば個別対処

方法D: 最終手段:fresh clone

cd ..
git clone <URL> fresh-repo

【原因④】並列操作

症状

# ターミナル1
git fetch

# ターミナル2(同時)
git pull
# error: cannot lock ref ...

IDE + CLI複数ターミナルCI ジョブの並列実行などで発生。

診断

# 動いている git プロセス
ps aux | grep git

# lock ファイルを保持しているプロセス
lsof .git/refs/heads/main.lock

解決

待機して再実行:

sleep 5
git pull

IDE のバックグラウンド動作を停止:

  • VS Code: settings.jsongit.autofetch: false
  • JetBrains IDE: Version Control 設定
  • SourceTree, GitKraken: 更新間隔

VS Code等のIDE との並行動作については、Unable to create ‘.git/index.lock’ の記事にも詳細な対処法を書いています。

並列実行を避ける:

# ❌
git fetch & git pull &

# ✅
git fetch && git pull

【原因⑤】stale remote refs(削除された remote branch)

症状

git fetch
# error: cannot lock ref 'refs/remotes/origin/deleted-branch'
  • リモートで削除されたブランチ
  • 強制 push で歴史が書き換わったブランチ

診断

# 現在のリモートトラッキング
git branch -r
# origin/main
# origin/deleted-branch  ← リモートには存在しない

# リモートの実際の状態
git ls-remote --heads origin

解決

--prune オプション:

git fetch --prune
# または
git fetch -p

恒久的に自動 prune:

git config --global fetch.prune true

以降、git fetch は自動的に --prune

特定 ref を手動削除:

git update-ref -d refs/remotes/origin/deleted-branch

使い方リファレンス

git update-ref

任意の ref を直接操作するコマンド。

# 削除
git update-ref -d refs/heads/branch-name

# 作成
git update-ref refs/heads/new-branch abc1234

# 更新
git update-ref refs/heads/main abc1234

# 検証付き更新(前の値と一致する時のみ)
git update-ref refs/heads/main abc1234 old_sha

低レベル操作なので慎重に。

git pack-refs

loose refs を packed-refs に統合:

# 全て統合
git pack-refs --all

# 統合 + loose 削除
git pack-refs --all --prune

大量ブランチ時に高速化。

git fetch –prune

削除された remote branch を同期:

git fetch --prune
# または短縮
git fetch -p

恒久設定:

git config --global fetch.prune true

git gc

Git のガベージコレクション:

git gc
# または積極的に
git gc --aggressive --prune=now

lock 関連のクリーンアップも含まれる。

git fsck

リポジトリ整合性チェック:

git fsck --full
# --full: object 全体をチェック
# --unreachable: 到達不能な object も表示

実践シナリオ

シナリオ1:git pull で stale lock

git pull
# error: cannot lock ref 'refs/remotes/origin/main'

# 対処
find .git -name "*.lock" -delete
git pull
# → 成功

シナリオ2:feature vs feature/xxx の namespace collision

git fetch
# error: cannot lock ref 'refs/remotes/origin/feature/login':
# 'refs/remotes/origin/feature' exists

# 診断
ls .git/refs/remotes/origin/feature*
# → feature(ファイル) と feature(ディレクトリ)の衝突

# バックアップ
git branch feature-backup origin/feature

# 古い ref 削除
git update-ref -d refs/remotes/origin/feature

# 再 fetch
git fetch
# → 成功

シナリオ3:packed-refs 破損

git pull
# error: unable to resolve reference: reference broken

# 診断
git fsck --full
# → エラー多数

# packed-refs 再構築
git pack-refs --all --prune
git fsck --full
# → クリーン

# 動作確認
git pull

シナリオ4:CI/CD で並列実行

# .github/workflows/parallel.yml
# 悪い例
jobs:
  job1:
    runs-on: ubuntu-latest
    steps: [uses: actions/checkout@v4]
  job2:
    runs-on: ubuntu-latest
    steps: [uses: actions/checkout@v4]
    # 同じキャッシュを使うと lock 競合

# 良い例
jobs:
  job1:
    concurrency:
      group: git-ops
      cancel-in-progress: false

シナリオ5:チーム開発でブランチ削除多い

# 毎日多くのブランチが削除される
git config --global fetch.prune true

# 以降自動で削除同期
git fetch

シナリオ6:GitLab CI で頻発

# .gitlab-ci.yml
variables:
  GIT_STRATEGY: clone   # 毎回 clean
  # または
  GIT_STRATEGY: fetch
  GIT_CLEAN_FLAGS: -ffdx

before_script:
  - find .git -name "*.lock" -delete 2>/dev/null || true

シナリオ7:Rails 開発でよくある

# feature ブランチで大量の変更 + rails db:migrate
# 中断されて lock 残り

git pull
# error: cannot lock ref

# 対処
find .git -name "*.lock" -delete
git pull

Rails 開発では、rails db:migrate 使い方の記事、Rails 8 アップグレードガイドの記事とセットで対処すると効果的です。

シナリオ8:Kamal デプロイで発生

kamal deploy
# ビルド中に fetch などが並行
# → lock 競合の可能性

# 対処: デプロイ中は他の git 操作を控える

Kamal デプロイの詳細は、Kamal 2 デプロイの記事を参照してください。

シナリオ9:Docker 内で発生

docker exec -it container bash
cd /app
git fetch
# error: cannot lock ref

# 対処
find /app/.git -name "*.lock" -delete
git fetch

Docker関連の問題は、docker daemon 接続エラーの記事、Docker no space left on device の記事にも詳しい対処法があります。

シナリオ10:大規模リポジトリで発生

# 数万ブランチのリポジトリ
git fetch
# ref 数が多く、lock 競合しやすい

# 対処
git config core.fsyncMethod fsync  # 安全性向上
git config core.packedRefsTimeout 10000  # timeout 延長

予防のベストプラクティス

1. fetch.prune を有効化

git config --global fetch.prune true

削除されたリモート branch を自動同期

2. ブランチ命名規則

❌ 避ける
feature
feature/x

✅ 推奨
feature/main
feature/login
feature/logout

親名を予約語にせず、全てサブディレクトリ形式に。

3. Ctrl+C は最終手段

git fetch   # 実行中
# ❌ Ctrl+C

途中で止めるより 完了を待つ。どうしても止めるなら、git fetch --abort などが利用可能な操作を使う。

4. IDE の Git 統合を意識

VS Code settings.json:

{
  "git.autofetchPeriod": 600,
  "git.autoRefresh": false
}

5. git gc の定期実行

# 週次で
git gc --auto

自動 pack で lock 競合を減らす。

6. 並列実行を避ける

# ❌
git fetch & git pull &

# ✅
git fetch && git pull

7. git fsck で健康チェック

git fsck --full

定期的に整合性確認


トラブルシューティング

lock 削除しても再発

プロセスが動いている証拠:

# 全 git プロセス確認
ps aux | grep git

# 全部 kill
pkill -f git

# lock 削除
find .git -name "*.lock" -delete

git update-ref -d が動かない

git update-ref -d refs/heads/feature
# fatal: cannot lock ref

これも lock ファイルが原因:

rm .git/refs/heads/feature.lock
git update-ref -d refs/heads/feature

namespace collision が繰り返す

リモート側の問題。チームで:

  • ブランチ命名規則の合意
  • 問題ブランチを削除・リネーム
  • CI/CD の設定見直し

GitHub / GitLab で発生

リモート側で強制 push・履歴書き換えが原因の可能性:

# 完全リセット
git fetch --prune
git remote update --prune
git gc --prune=now

submodule で発生

cd submodule
find .git -name "*.lock" -delete
cd ..
git submodule update --recursive

Windows で頻発

Git for Windows のプロセス残留。Task Manager で確認・終了

Windows 特有のプロセス残留の対処は、Unable to create ‘.git/index.lock’ の記事に詳しく書いています。


よくある質問(FAQ)

Q1. .lock ファイル、いつ削除していい?

Git プロセスが動いていないことを確認してから。動作中に削除は危険(データ破損)。

Q2. index.lock との違い

  • index.lock: staging area(.git/index)のロック
  • ref lock: ブランチ・タグ等の参照ロック

作られる場所が違うだけで、仕組みは類似。

Q3. namespace collision は Windows / macOS だけ?

いいえ、Linux でも発生。ファイルとディレクトリの共存はどのOSでも不可能。

Q4. git update-ref -d は危険?

低レベル操作なので慎重に。誤ると ref を失うが、reflog で救済可能な場合が多い。

事前バックアップ:

git branch backup-branch <target-ref>

Q5. packed-refs を直接編集していい?

基本ダメ。フォーマット厳密、間違うとリポジトリ破損。git pack-refs 経由推奨。

Q6. git fetch --prune の効果

削除されたリモートブランチのローカル参照を削除。lock 系エラーの予防に効果大。

Q7. fetch.prune = true の副作用

ローカル作業中のブランチが削除されることはない(trackingブランチのみ対象)。安心して有効化推奨。

Q8. reflog で救済可能?

削除された ref も、reflog に残っていれば復旧可能:

git reflog
# 過去の操作履歴

# 該当 commit を確認して
git branch recovered abc1234

Q9. 大規模リポジトリで頻発

git config core.packedRefsTimeout 10000
git gc --auto

タイムアウト延長と定期 GC。

Q10. GitHub Actions で発生

- name: Clean lock files
  run: find .git -name "*.lock" -delete 2>/dev/null || true

- uses: actions/checkout@v4
  with:
    fetch-depth: 0

CI 開始時にクリーニング。

Q11. リモートに存在するのに削除される

fetch.prune = true の副作用ではない。ネットワーク一時断で誤削除は稀。

対処: git fetch 再実行。

Q12. Git 2.46+ での改善

Git 2.46 / 2.47(2024年)で ref 関連の診断情報が強化。Git 更新推奨:

git --version
# 2.46.0 以降がおすすめ

参考リンク

Git 公式


まとめ

cannot lock ref の解決、要点を再整理します。

5大原因まとめ

#原因症状解決
stale lockFile existsfind .git -name "*.lock" -delete
namespace collision'refs/X' exists; cannot create 'refs/X/Y'git update-ref -d refs/X
packed-refs 破損reference brokengit pack-refs --all --prune
並列操作同時実行順次実行
stale remote refs削除されたリモートgit fetch --prune

最速の解決手順

# 1. Git プロセス確認
ps aux | grep git

# 2. lock ファイル一括削除
find .git -name "*.lock" -type f -delete

# 3. remote 同期
git fetch --prune

# 4. 検証
git fsck --full

覚えるべきコマンド

# ref 直接削除
git update-ref -d refs/heads/branch-name

# packed-refs 再構築
git pack-refs --all --prune

# 削除されたリモートを同期
git fetch --prune

# 整合性チェック
git fsck --full

# ガベージコレクション
git gc --auto

必須設定

git config --global fetch.prune true
git config --global gc.auto 6700

多くの問題を予防

予防策

  • fetch.prune = true 恒久設定
  • ブランチ命名規則を明確化
  • 並列実行を避ける
  • IDE の自動 fetch を意識
  • 定期的な git gc
  • git fsck で健康チェック

事故防止

  • プロセス確認せずに削除しない
  • git update-ref は慎重に
  • packed-refs 直接編集禁止
  • バックアップ習慣
  • どうにもならなければ fresh clone

環境別

環境特徴
Windowsプロセス残留、大文字小文字非区別
macOS大文字小文字非区別(デフォルト)
Linux大文字小文字区別
CI/CD並列実行問題
Dockervolume 権限問題

これらの知識は、日常の Git 操作・チーム開発・大規模リポジトリ・CI/CD・Docker / Kamal デプロイなど、あらゆる場面で活用できます。本記事をブックマークしておけば、ref 関連の複雑なエラーを確実に解消できるようになります。


本記事は2026年6月時点の情報をもとに、Git 2.40+ での動作確認・公式ドキュメントに基づき作成しています。Git のバージョンによって挙動が異なる場合があるため、最新の情報は Git 公式ドキュメント(git-scm.com/docs)もあわせてご確認ください。