Gitのindex.lockファイルが残り続ける問題の調査と解決
はじめに
Gitを使用していると、コマンドによっては.git/index.lock
ファイルが作成され、ファイルが存在する間は他のコマンドをブロックします。
今回私が直面した問題は、自分がコマンドを実行しようとしたらなぜか.git/index.lock
が作成されており、自分の操作がブロックされるというものでした。
本記事では、この問題に悩まされていた私が、どのプロセスが.git/index.lock
ファイルを作成しているのかを特定するためのスクリプトを作成し、問題を解決した経験を共有します。
課題とその背景
VSCode(実際はCursorを使っていますが、VSCodeの機能なのでVSCodeと記述します)やコマンドラインからGit操作をすると、以下のようなエラーが発生して、操作がブロックされることがたびたび発生していました。
fatal: Unable to create '/Users/me/Workspace/some-repo/.git/index.lock': File exists.
このファイルが作成されると他のGitコマンドがブロックされるため手動でindex.lock
ファイルを削除していましたが、すぐに再作成されるという状況が続いていました。
Gitコマンドがブロックされるというのは開発生産性に大きく影響します。
VSCodeのバックグラウンドプロセスが原因かなとあたりはつけていたのですが、具体的に何がindex.lock
ファイルを作成しているのかを特定することができなかったので、なぜこのファイルが作成されるのか、通常は直ぐに消えるはずなのになぜ残っているのか、その原因を特定する必要がありました。
アプローチ
問題を解決するために、このエラーが出た場合はlsof
でファイルを作成したプロセスのプロセス番号を表示し、さらにps
コマンドでプロセスを表示することで原因を特定しようとしました。
ですが、プロセスはツリー状になっているため大元のプロセスが何かは一度のps
コマンドではわかりませんでしたし、コマンドを実行しているうちにindex.lock
ファイルが消えてしまってやってられねーなとなり、シェルスクリプトを作成することにしました。
実装
以下のシェルスクリプトを実装しました。このスクリプトは、.git/index.lock
ファイルの作成を監視し、作成された時点でファイルを開いているプロセスとその親プロセスを再帰的に表示します。
#!/bin/bash
# ====== 設定 ======
REPO_DIR="/Users/me/Workspace/some-repo"
LOCK_FILE="$REPO_DIR/.git/index.lock"
# ====== ツリー表示用関数 ======
print_process_tree() {
local pid=$1
while [ "$pid" != "1" ] && [ -n "$pid" ]; do
ps -p "$pid" -o pid,ppid,command=
pid=$(ps -p "$pid" -o ppid= | tr -d ' ')
done | tac
}
# ====== 監視開始 ======
echo "監視開始: $LOCK_FILE を監視中…"
echo "Ctrl+C で終了します"
fswatch -0 "$REPO_DIR/.git" | while read -d "" event
do
if [ -f "$LOCK_FILE" ]; then
echo "[$(date)] .git/index.lock が作成されました"
sleep 0.2
# lsofでファイルを開いているプロセスを取得
pid=$(lsof -t "$LOCK_FILE" | head -n 1)
if [ -n "$pid" ]; then
echo "🔍 ロックファイルを開いているプロセス: PID=$pid"
echo "🌳 プロセスツリー:"
print_process_tree "$pid"
else
echo "⚠️ lsofでプロセスが見つかりませんでした"
fi
echo "-------------------------------"
fi
done
このスクリプトの主な機能は以下の通りです:
-
fswatch
を使用して.git
ディレクトリの変更を監視(参考:fswatch) -
index.lock
ファイルが作成されたら、lsof
でファイルを開いているプロセスを特定 -
print_process_tree
関数で、プロセスの親子関係を表示 - 結果を見やすく整形して出力
sleep 0.2
を入れているのでファイルがすぐ削除された場合は⚠️ lsofでプロセスが見つかりませんでした
と表示されるのですが、その場合はGit操作がブロックされることはまずないので問題ないです。
結果
作成したスクリプトを使用することで、以下のような情報が得られました。
[Mon Mar 31 22:41:58 JST 2025] .git/index.lock が作成されました
🔍 ロックファイルを開いているプロセス: PID=42501
🌳 プロセスツリー:
970 1 /Applications/Cursor.app/Contents/MacOS/Cursor
PID PPID
10678 970 Cursor Helper (Plugin): extension-host [22-25]
PID PPID
11009 10678 ~/.asdf/installs/golang/1.24.0/packages/bin/gopls -mode=stdio
PID PPID
42459 11009 ~/.asdf/installs/golang/1.24.0/go/bin/go list -overlay=...[省略]
PID PPID
42501 42459 git status --porcelain
PID PPID
この結果から、以下のことがわかりました。
- ロックファイルは
git status --porcelain
コマンドによって作成されていた - 1のコマンドは
go list
コマンドが内部的に実行していた - 2のコマンドは
gopls
(VSCodeのGo言語のlanguage server)から呼び出されていた -
gopls
はCursorエディタのプラグインとして動作していた
この調査により、gopls
が定期的に実行するgit status
コマンドが原因であることが特定できました。
また、このリポジトリには既存の問題として大きなリポジトリをsubmoduleとして含んでおり、Git操作が重いというものがありました。
普段はVSCodeの設定でsubmoduleをignoreしてGitコマンドを実行しているため、index.lock
ファイルが問題になることはありませんでした。
この調査結果を踏まえ、改めてsubmoduleを見直してsparsecheckoutを使用して必要なファイルのみsubmoduleとして管理する方針に変更したところ、index.lock
ファイルが作成されて長く残ることはなくなり、問題なくGit操作ができるようになりました。(参考:他のリポジトリからsubmoduleで一部ファイル、一部ディレクトリのみ取ってくる方法!)
おわりに
このスクリプトを使用することで、.git/index.lock
ファイルの作成元を特定し、適切な対策を講じることができました。
同様の問題に悩む方々の参考になれば幸いです。
また、このスクリプトは他のファイルの監視にも応用可能です。
例えば、特定のファイルが予期せず変更される場合のデバッグなどにも活用できます。
Discussion