Claude Codeのworktreeが再利用されるバグ — 前回セッションを誤って引き継ぐ
新規セッションのはずが前回のworktreeディレクトリを引き継ぎ、別ブランチや未追跡ファイルまで混入するClaude Code Desktopの既知issue(#79366)を、reflogとレジストリの証拠から検証します。
Claude Code Desktopでworktree(ワークツリー)を有効にして新しいセッションを始めても、まっさらなディレクトリが与えられるとは限りません。GitHub issue #79366には、前のセッションが使っていたディレクトリが新しいセッションに割り当てられ、別ブランチの履歴や未追跡ファイルまで引き継がれるという報告です。2026年7月20日以降、macOSとWindowsの両方から寄せられています。
指摘されている原因は、アプリ側が持つworktreeの貸し出しプール(WorktreePool)です。セッションをアーカイブしてもディレクトリは削除されず、次のセッションへ回されます。
v2.1.31以降の/resumeがセッション履歴を表示できない不具合は3つの原因で扱っています。
Claude Code Desktopで何が起きているか
最初の報告者(Claude Code 2.1.197、macOS)は、worktree隔離を有効にした新規セッションの置き場を問題にしました。無関係な過去タスクのために作られた既存のworktreeディレクトリに置かれたと書いています。新しいブランチは作られますが、チェックアウト先は古いディレクトリのままです。
該当worktreeのreflogには、別タスクのブランチをチェックアウトしていた履歴がそのまま残り、その上に新しいブランチへの切り替えが積まれていました。
報告者が挙げる影響は3点です。
- ディレクトリ名がブランチやタスクの内容と一致しなくなる
- 前タスクの未追跡ファイルや
.gitignore対象ファイルが、新しいタスクのコンテキストやPRに紛れ込みうる - 同じリポジトリで繰り返し起きる
同じ投稿には、バックグラウンドのサブエージェントがworktreeではなくメインの作業コピーへ書き込む別の症状も添えられています。別の参加者も、関連する別の問題としてこの症状に触れています。
ラベルにはbug・platform:macos・area:agentsが付いていますが、スレッドにはWindowsの報告も続いています。コメントに出てくるClaude Codeのバージョンは2.1.104、2.1.197、2.1.212、2.1.222とばらけており、特定の版に限った話ではないようです。
スレッドの主な報告
- 7月20日最初の報告と再現手順
Claude Code 2.1.197のmacOS。同日、2.1.212の報告者が「アーカイブが引き金」という再現手順を投稿。
- 7月26日貸し出しレジストリの発見
git-worktrees.jsonのleasedByが、プールの存在を示す直接の証拠として共有された。 - 8月3日WorktreePoolの内部解析
2.1.104の報告者が、プールの掃除ルールと隠しプリファレンスを非公式に解析。
- 8月13日Windowsでも同型の挙動
2.1.222のWindows 11でも、同型の挙動が確認された。
- 8月28日「非アクティブ」なチャットが再利用された例
自動アーカイブを切っていても、2つのworktreeが
detached HEADになった。
アーカイブが引き金になる再現手順
参加者の一人は、worktreeディレクトリ名がリポジトリ名など決定論的な規則で決まっているなら衝突は避けられず、セッションID込みの命名ならクリーンアップ側の不具合だと整理しました。これに対して別の報告者(macOS 26.5.2、Claude Code 2.1.212)は、制御したテストで引き金を特定しています。
再利用が起きるまで
- 1
worktree有効の新規セッションを作る
フォルダ
X-79ed71とブランチclaude/X-79ed71が割り当てられる。名前が一致しているこの状態が「新規」の見た目。 - 2
コミットしてからセッションをアーカイブする
git worktree listで見ると、フォルダの表示が[claude/<branch>]から(detached HEAD)に変わるだけで、ディスクからは消えない。 - 3
別のworktree有効セッションを作る
新しいブランチ
claude/Y-d6f752が発行されるが、チェックアウト先は古いフォルダX-79ed71のまま。フォルダの生成時刻は新セッションより前になる。
この投稿には、見落とされやすい点が2つ含まれています。
1つ目は、使用中のセッションも巻き込まれるという報告です。チーム規約(feat/<ticket>-…)にブランチ名を変えたセッションのフォルダが、そのセッションを開いたまま新規セッションに取られ、チェックアウト先が足元で切り替わったとされています。
2つ目は、ブランチ名をclaude/*から変えていたセッションをアーカイブしても、detachされなかったという観察です。所有の判定は、アプリが発行したclaude/*ブランチ名に紐づいているように見える、と書かれています。
手元のgitで同じ状態を作る
デスクトップアプリを使わなくても、gitの操作だけで同じ状態は作れます。一時ディレクトリで、報告にあるフォルダ名を使って流れをなぞった結果が次のとおりです(gitのみ。アプリの動作を再現したものではありません)。
git worktree add .claude/worktrees/X-79ed71 -b claude/X-79ed71
# ここで作業してコミット。未追跡ファイル leftover.txt も作る
git checkout --detach # アーカイブ後の状態
git worktree list
# .../repo b027e06 [main]
# .../worktrees/X-79ed71 7609c08 (detached HEAD)
git checkout -b claude/Y-d6f752 # 次のセッションが同じ場所に入る
git status --porcelain
# ?? leftover.txt
git worktree prune # 実行後も worktree list は変わらないgit worktree pruneは、実在するディレクトリを持つworktreeを消しません。プルーニングは無害ですが、このバグには効かないことが分かります。前タスクのleftover.txtが新ブランチのgit statusにそのまま現れる点も、報告の「未追跡ファイルの混入」と同じ形です。
reflogとgit-worktrees.jsonが示す再利用の痕跡
新規worktreeかどうかを見分ける簡易チェックとして、次のコマンド群が共有されました。macOS向けのstat -fを使っているため、Linuxでは生成時刻の取り方が変わります。
basename "$(git rev-parse --show-toplevel)" # フォルダ名
git branch --show-current # ブランチ名
stat -f "%SB" "$(git rev-parse --git-dir)/gitdir" # フォルダの生成時刻
git status --porcelain # 引き継いだ未追跡ファイルフォルダ名とブランチ名が一致し、生成時刻がセッション開始直後で、状態がクリーンなら新規worktreeです。どれか1つでも外れていれば再利用が疑われます。なお、更新時刻(mtime)はあてにならないため、生成時刻を見る点が要所だとされています。
別の報告者は、デスクトップアプリがmacOSでは~/Library/Application Support/Claude/git-worktrees.jsonにworktreeの貸し出し状況を記録していることを突き止めました。エントリにはleasedBy(貸し出し先セッションID)とbranchが入っています。フォルダ名が示す元タスクとは無関係な値が入っているケースが確認されました。
このレジストリは、アプリが作ったフォルダだけを追跡します。.claude/worktrees/の下にあるフォルダはすべてレジストリに載っており、leasedBy: nullのまま残っているものは次の再利用を待つ状態だと説明されています。手元で試すなら、git worktree listとレジストリのキーを突き合わせるだけで確かめられます。
WorktreePoolの内部設計を読み解く
別の参加者(Claude Code 2.1.104、Desktop 1.24012.9)は、配布されているデスクトップアプリのファイルとローカルログを解析し、症状の背後にある仕組みを書き込んでいます。非公式のリバースエンジニアリングという前提付きです。
- リポジトリ単位でworktreeのプールを持ち、セッション終了時にプールへ返却する
WorktreePoolという機構がある - ログには
[WorktreePool] Reused worktree <dir> for session <id> (was leased by none)という再利用の記録が残る worktreePoolEnabledの既定値はfalseと読めるのに実際には動いており、機能ゲート経由で既存インストールに対して有効化された可能性がある- 掃除は経過時間ではなく保持数の上限で行われる
隠しプリファレンスとして観測された値は次のとおりです。
| 設定キー | 既定値 | 内容 |
|---|---|---|
ccMaxWarmWorktrees | 既定値3 | 内容リポジトリごとに保持する最新worktreeの数。超えた分は経過時間に関わらず回収対象になる |
ccWorktreeReapAfterHours | 既定値24 | 内容maxWarmが0のときに使われるアイドル判定のしきい値 |
この保持数キャップから、追加の症状が2つ報告されています。leasedByが入ったままでも、セッションが動いていなければworktreeが回収されうること。回収されたworktreeを持つセッションが、通知なくメインの作業コピーへ切り替わること。後者はログにfalling back to origin repoという行が出るとされています。
関連issueとして#43730、#62431、#46444などが挙げられています。同じ報告者は、cleanupPeriodDays(既定30日)がこの掃除に効かないとも書いています。これらのキーが公開されているsettings.jsonの項目にないことを、報告者はopt-outと文書化を求める理由にしています。
実運用で起きている具体的な被害
macOSの報告者は、自分のリポジトリで1つのディレクトリが3つの異なるセッション・3つの異なるブランチを渡り歩いた例を示しています。ここで目を引くのは、再利用がアーカイブ時の掃除を止めてしまう点です。
プレビュー用に作られる.claude/launch.jsonのような未追跡ファイルは、本来ならworktreeと一緒に消えます。再利用でディレクトリが生き続けると、このファイルが残って「dirty」の判定を出し続けます。その報告者の環境では、残っていた10個のworktreeのうち6個が、この1ファイルのためにdirtyでした。
プールの狙いはチェックアウトとnode_modulesの準備コストの償却だと推測した上で、pnpmのようにインストールが高速なリポジトリでは、得られる利益が小さいとも書かれています。
Windowsでも同型の挙動が報告されています。レジストリは%APPDATA%\Claude\git-worktrees.jsonにあり、45件のエントリのうち25件が貸し出し中、16件がpooledAt付きでleasedBy: nullのまま残っていました。あるディレクトリのreflogは、4日間で5つの無関係なセッション・ブランチを渡り歩いています。ディレクトリ名は最初のセッションのトピックのまま固定されたままです。
この命名の食い違いは、セッションの内容が別のタスクの名前で見えてしまう機密性の問題にもなるとして、別issue(#86239)に切り出されています。セッションがgitブランチではなく作業ディレクトリに紐づく仕様と重なると、どのセッションがどのブランチなのかの見分けはさらに難しくなります。
8月28日のmacOS報告も印象的です。4つのチャットに4つのworktreeがあった環境で、git worktree listを見たら2つがdetached HEADになり、レジストリではleasedByがnullでした。報告者は「非アクティブなセッションをアーカイブ」と「PRマージ・クローズ後の自動アーカイブ」を両方オフにしていました。そのチャットを順にクリックすると、worktreeが元のブランチに戻り、leasedByも埋まったと書かれています。
報告者は「セッション状態を自動分類する」設定を疑っていますが、この仮説を検証した続報はスレッドにありません。
公式の説明と報告された挙動のずれ
デスクトップの公式ドキュメントでは、worktreeは<project-root>/.claude/worktrees/に作られ、サイドバーでセッションにカーソルを合わせてアーカイブのアイコンを押すと、worktreeを取り除けると説明されています。自動アーカイブの設定は、完了したローカルセッションにだけ適用されます。
報告が示すのは、実際にはアーカイブがleasedByを空にするだけで、git worktree removeまでは走らない点です。
CLIの--worktreeは様子が違います。公式のworktreeドキュメントでは、claude --worktree <name>が.claude/worktrees/<name>/にworktree-<name>ブランチを作ると説明されています。終了時にworktreeがクリーンなら、名前を付けていないセッションではworktreeとブランチが自動で削除されます。名前付きのセッションや変更が残るセッションでは、保持するか削除するかを尋ねられます。
サブエージェントとバックグラウンドセッションのworktreeには、cleanupPeriodDaysより古いものを消す定期処理が別にあります。報告に出てくるclaude/<task>-<hash>というブランチ名とは、接頭辞が異なります。
利用経路別に見る影響の有無
worktreeを使う経路は複数ありますが、報告が集中しているのはデスクトップアプリの並列セッションです。
| 利用経路 | 報告の有無 | 備考 |
|---|---|---|
| デスクトップアプリ(macOS) | 報告の有無報告あり(多数) | 備考症状の中心。git-worktrees.jsonのプールが直接の原因とされる |
| デスクトップアプリ(Windows) | 報告の有無報告あり | 備考レジストリの保存場所が異なるだけで同型の挙動 |
CLIの--worktreeフラグ | 報告の有無スレッド内に報告なし | 備考プールの仕組みはアプリ層にあるとされる |
サブエージェント隔離(isolation: worktree) | 報告の有無直接の再利用報告なし | 備考最初の報告者から、書き込み先がメインの作業コピーになる別症状が報告されている |
CLIのヘルプでは、-w, --worktree [name]が「このセッション用に新しいgit worktreeを作る(名前は任意)」と説明されています(v2.1.289のclaude --help)。--tmuxはこのフラグとの併用が前提です。
自分でworktreeを作り再利用を避ける
報告者が示した回避策は、アプリのworktree機能そのものを使わないことです。セッション開始時にworktreeのチェックボックスを外し、自分でworktreeを作って、EnterWorktreeツールでそこへ固定します。
git fetch origin main --quiet
git worktree add ~/dev/worktrees/<repo>/<TICKET> -b <branch> origin/main自分で作ったディレクトリはアプリのレジストリに登録されず、プールに戻される対象にもなりません。ディレクトリ名は選んだ名前のままで、ブランチ名も最初から意図したものになります。公式ドキュメントでも、git worktree addで自作したworktreeは、サブエージェントやバックグラウンドセッションの定期掃除から外れると説明されています。
引き換えに、後片付けはgit worktree removeとブランチ削除を自分で行います。セッション一覧の表示も、worktreeではなくリポジトリルート側になります。
もう一つ、同じ報告者が注意している癖があります。EnterWorktreeで固定したセッションで/clearを実行すると、作業ディレクトリが起動時のディレクトリへ戻り、固定が外れるという報告です。クリア後にファイルを書き込む前には、EnterWorktreeで入り直す必要があるとされています。
EnterWorktreeの挙動には公式の記述もあります。.claude/worktrees/の外にあるパスへ入るときは、権限ルールや「今後は確認しない」の選択にかかわらず、承認を求められます(bypassPermissionsモードを除く)。これはv2.1.206からの動作です。リポジトリ外に作った自作worktreeへ固定するときは、この確認が1回入ります。
日常的な運用手順はClaude Code Worktree実践ガイドにあります。設定としてはworktree.baseRefやworktree.bgIsolationのようなCLI側の設定キーが公開されています。
まとめ
デスクトップでworktreeを使うなら、新しいセッションの最初にフォルダ名・ブランチ名・git statusを見る習慣が、混入を避ける一番安い対策です。
ディレクトリを自分で管理したいなら、チェックボックスを外して自作のworktreeにEnterWorktreeで入る運用が、スレッドで共有されています。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。