Claude Codeの--worktree終了時自動削除をgit worktree lockで防ぐ方法
本記事では、Claude Code(v2.1.263)が`--worktree`フラグで作成したワークツリーを終了時に自動削除する内部ロジックを解析します。変更がなく新規コミットもないセッションが終了すると、ワークツリーとブランチは警告なしに削除されます。バンドルされたソースコードから、削除が発生する正確な条件を明らかにし、Claude Codeがセッション追跡のために特定のフォーマットで`git worktree lock`を使用していることを示します。このフォーマットに一致しない理由でロックされている場合、外部ロックと見なされ削除を免れます。手動でカスタム理由でロックし直す方法や、`SessionStart`フックで自動化する方法を紹介します。
背景と課題
Claude Codeが提供する`--worktree`モードは、開発者がメインの作業ディレクトリを汚さずに独立したGit worktree上でセッションを実行できる便利な機能です。しかし、`claude --worktree feature-x`でセッションを作り、コード調査や試行的な変更を行った後、`/exit`で終了すると、何の確認もなくworktreeと関連ブランチが自動的に削除されてしまうという厄介な現象が発生します。多くの開発者は「変更を加えなければ残る」と誤解し、せっかく構築した調査環境やターミナルの履歴、チャットの文脈を失ってしまいます。
公式ドキュメントでは、これは設計上の意図された動作であると明記されています。未コミットの変更がなく、かつセッションに名前が付けられていない場合、worktreeとブランチは自動的に削除されます。名前付きセッションの場合は「Keep/Remove」の選択ダイアログが表示されますが、この動作をグローバルに無効化する設定項目は提供されていません。つまり、「読み取り専用の調査」や「変更後に元に戻した」といった一時的な調査シナリオでは、常にworktreeが静かに消滅するリスクがあります。コミュニティでは複数のIssue(#27753、#46444、#58432など)が報告されましたが、いずれも「expected behavior」としてクローズされており、この設計に対する議論は収まっていません。
アーキテクチャと実装詳細
Claude Codeのクリーンアップロジックは、パッケージ化されたJavaScriptソースコードの中に隠されています。解析により、`/exit`時に以下の2つのGit状態を順にチェックしていることが判明しています。まず`git status --porcelain`を実行し、未ステージの変更や未追跡ファイルの有無を確認します。次に`git rev-list --count <session-start-HEAD>..HEAD`で、セッション開始時点のHEADから現在のHEADまでの新規コミット数をカウントします。これらの結果に応じて、動作は3パターンに分類されます。両方ともゼロでセッションが無名の場合は静かに削除、両方ともゼロだが名前付きセッションの場合はKeep/Removeダイアログ、いずれかが非ゼロの場合は名前の有無にかかわらずダイアログが表示されます。
問題の核心は、第一のケース(変更ゼロ、コミットゼロ、無名)がまさに「調査後に何も変更していない」あるいは「変更を元に戻した」という典型的なワークフローに該当する点です。さらに重要なのは、Claude Code自身が`git worktree lock`を用いてセッションのロックを管理していることです。セッション開始時には`claude session <name> (pid <PID> start <timestamp>)`という固定フォーマットでロック理由が書き込まれます。終了時には、正規表現`/^claude (?:agent|session) .{1,255} \( pid (\d{1,10})(?: start (.{1,255}))? \)$/`でロック理由が自身のものか検証し、パターンにマッチしない場合(例えばカスタムテキストに変更されている場合)は「他のプロセスにロックされている」と判断してクリーンアップをスキップします。これがGitレベルで用意されたセーフティネットです。公式ドキュメントでも「sweepはあなた自身が設定したロックを決して解放しない」と説明されています。
実測ベンチマークと実用的価値
テスト用リポジトリで実際に検証してみると、静かな削除を正確に再現できます。`claude --worktree wt-clean`で空のworktreeに入り、すぐに`/exit`を実行すると、端末には`Cleaning up worktree (no pending changes)…`というメッセージが一瞬表示されるだけで、worktreeディレクトリと対応するブランチが消えてしまいます。`git worktree list`を確認すると、メインリポジトリのworktreeだけが残っている状態です。この処理は1秒もかからず、回復の余地はほとんどありません。
防御策は上記のロックメカニズムを利用します。セッション実行中に別のターミナルを開き(またはClaude Code内部で`!`を使ってシェルコマンドを実行し)、まず`git worktree unlock .claude/worktrees/<name>`でClaude Code自身のロックを解除し、次に`git worktree lock --reason "pinned by ryan" .claude/worktrees/<name>`でカスタム理由による再ロックを行います。既存ロックが存在するため、unlock→lockの順序を守る必要があります。再ロック後、`/exit`を実行するとロック理由が正規表現にマッチしないため、Claude Codeは削除処理をスキップします。検証の結果、worktreeはリストに残り、状態は`locked`と表示され、後日`claude --worktree wt-lock`で再利用できることも確認できました。
自動化を追求する場合は、プロジェクトの`.claude/hooks/`ディレクトリに`SessionStart`フックスクリプト(例:`pin-worktree.sh`)を作成します。スクリプト内で`git rev-parse --git-dir`と`git rev-parse --git-common-dir`を使って現在のworktreeがリンク先であるか(メインリポジトリではないか)を判定し、その場合にunlock→lockを実行します。こうすることで、セッション開始時に自動でロックが設定され、終了時には削除を免れるため、手動操作が不要になります。
今後の展望と技術的示唆
この問題の本質は、AIアシストコーディングツールが「自動クリーンアップ」機能を設計する際に、ユーザーのメンタルモデルや実際のワークフローに対する理解が不足している点にあります。開発者は`--worktree`を使用するとき、worktreeを独立した開発コンテナとして捉え、Gitの痕跡を残していなくても、その上で行った調査環境やターミナル履歴、チャットの文脈には保存する価値があると考えます。Claude Codeは「変更の有無」だけを判断基準にしていますが、worktreeが環境コンテナとして持つ存在意義を軽視していると言わざるを得ません。
エンジニアリングの観点では、Claude Codeのロック機構はコミュニティに対して柔軟な防御インタフェースを提供しています。公式に設定スイッチは用意されていませんが、Git本来の機能を活用することで同等の効果を得られるようになりました。これは、優れたツール設計には「自動化の便利さ」と「ユーザーのコントロール感」の間にカスタマイズ可能な余地を残すべきだという教訓を与えています。将来のバージョンで`--keep-worktree`のような明示的なフラグや、設定ファイルに`worktreeCleanupPolicy`オプションが追加されれば、根本的にこの問題は解決するでしょう。また、コミュニティから提案されている`WorktreeRemove`フックの導入も注目に値します。これはクリーンアップ前にユーザー定義のチェックを実行できるようにするもので、安全性をさらに高めることが期待されます。
Claude Codeを深く活用するチームには、worktreeのロックスクリプトをプロジェクトの初期化テンプレートに組み込むことをお勧めします。これは一時的な作業領域を保護するだけでなく、ブランチの誤削除によるコラボレーションの混乱を防ぐ効果もあります。細かな技術的改善が、チーム全体の開発効率を大きく向上させる好例と言えるでしょう。
Sources
FAQ
Claude Code の worktree 自動削除はどのようにトリガーされますか?
--worktree モードで一時調査後、変更や新コミットがなく、セッション名がない状態で終了すると、worktree とブランチが無言で削除されます。
worktree の削除を防ぐ方法は?
`git worktree lock` でロックするか、SessionStart フックを利用して自動的に unlock してから lock することで永続的に保持できます。
自動削除を無効にする設定はありますか?
公式ドキュメントでは設計上の機能とされ、設定スイッチはなく、手動ロックやフックによる外部対策のみ可能です。