Zenn
🔍

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

このスクリプトの主な機能は以下の通りです:

  1. fswatchを使用して.gitディレクトリの変更を監視(参考:fswatch
  2. index.lockファイルが作成されたら、lsofでファイルを開いているプロセスを特定
  3. print_process_tree関数で、プロセスの親子関係を表示
  4. 結果を見やすく整形して出力

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

この結果から、以下のことがわかりました。

  1. ロックファイルはgit status --porcelainコマンドによって作成されていた
  2. 1のコマンドはgo listコマンドが内部的に実行していた
  3. 2のコマンドはgopls(VSCodeのGo言語のlanguage server)から呼び出されていた
  4. goplsはCursorエディタのプラグインとして動作していた

この調査により、goplsが定期的に実行するgit statusコマンドが原因であることが特定できました。
また、このリポジトリには既存の問題として大きなリポジトリをsubmoduleとして含んでおり、Git操作が重いというものがありました。
普段はVSCodeの設定でsubmoduleをignoreしてGitコマンドを実行しているため、index.lockファイルが問題になることはありませんでした。
この調査結果を踏まえ、改めてsubmoduleを見直してsparsecheckoutを使用して必要なファイルのみsubmoduleとして管理する方針に変更したところ、index.lockファイルが作成されて長く残ることはなくなり、問題なくGit操作ができるようになりました。(参考:他のリポジトリからsubmoduleで一部ファイル、一部ディレクトリのみ取ってくる方法!

おわりに

このスクリプトを使用することで、.git/index.lockファイルの作成元を特定し、適切な対策を講じることができました。
同様の問題に悩む方々の参考になれば幸いです。

また、このスクリプトは他のファイルの監視にも応用可能です。
例えば、特定のファイルが予期せず変更される場合のデバッグなどにも活用できます。

Discussion

ログインするとコメントできます