Windowsでgit worktreeを削除できないとき、ロック元プロセスを1コマンドで調べる
Claude CodeやCodexなどをworktree分離で使っていると、不要になったworktreeをgit worktree removeで削除しようとしても、「使用中のため削除できません」というエラーで失敗することがあります。これはworktreeを使うツール一般に起こりうる問題で、特定のツールだけの事情ではありません。
手作業だと何が面倒か
このエラーの原因を調べようとすると、通常は複数の確認が必要になります。
- git worktree listで一覧を確認
- git statusで未コミットの変更がないか確認
- pathが実際に存在するか確認
- handle.exeやProcess Explorerでロック元プロセスを探す
- PIDをタスクマネージャーで照合
- 必要なら個別ファイルを確認
ChatGPTに聴く場合でも、これらの情報を先に人間が集めてから質問する必要があります。
作ったもの
worktree-lock-diagnosticというPowerShellスクリプトを作りました。GitHubで公開しています。
https://github.com/iwadjp/worktree-lock-diagnostic
PowerShell 1コマンドで、以下をまとめて確認できます。
- worktreeの登録状態とbranch
- git status
- file-handle lockの有無
- ロックしているPIDとプロセス名
- 取得できる範囲でのロックされているresource
ロック検出方法
Windows標準のRestart Manager APIを使っています。Windows Installerやエクスプローラーが「このファイルは使用中です」と表示するときに使っているものと同じ仕組みです。handle.exeなどの外部ツール依存はありません。AI/LLMも使用していません。すべてGitとWindows APIの呼び出しによる確定的な判定です。
実際のdogfood
以下のパターンを実際に使って確認しました(実際のprivate repo名・worktree名は記載していません)。
- 正常なworktree(lockなしを正しく表示)
- 意図的にfile handleを保持したworktree(PID・プロセス名を検出)
- 存在しないpath(明確にnot found)
- gitでないディレクトリ
- 相対 path指定
prototypeで見つかったバグ
実際に手元でdogfoodしなければ見つからなかったバグが3件ありました。
1つ目は、ディレクトリパスをRestart Managerへ登録対象に混在させたためAPIがエラーを返していたのに、その戻り値を確認していなかったため、実際はロックされているのに「検出なし」と誤判定していた false negativeです。
2つ目は、Get-Help -Fullが全く動作していなかった問題です。#requiresの位置がコメントベースヘルプの解析を壊していました。
3つ目は、gitでないディレクトリを対象にすると、gitコマンドのstderr出力と$ErrorActionPreference=’Stop’が衝突し、未処理の例外でスクリプトが異常終了していました。
いずれも、実際に手元のdogfoodをしなければ見つからなかった問題です。特に1つ目は、「正常なworktreeだけで試していたら、本当に検出なしなのか、静かに検出に失敗しているのか区別できない」というタイプのバグで、テストの重要性を改めて実感しました。
使い方
GitHubのREADMEに応じて、以下のように実行します。
.\diagnose-worktree-lock.ps1 "..\my-feature-worktree"
Limitations
- ディレクトリを単にcurrent working directoryとして保持しているshell等のロックは、Restart Managerでは検出できない場合があります
- すべてのgit worktree remove失敗原因を診断できるわけではありません
- 一度にスキャンするファイル数にはMaxFilesToScan(既定500)の上限があります
- Restart Managerの結果は「ファイルとの関連」を示すものであり、原因であることの厳密な証明ではありません
Safety / Privacy
完全にローカルで動作し、読み取り専用です。プロセスのkill、git worktree removeの実行、git pruneの実行、ネットワークへの送信はいずれも行いません。
GitHub
ソースコードはこちらで公開しています。