Claude Codeでブランチをまたぐセッションはどう扱われるか
セッションはgitブランチではなく作業ディレクトリに紐づきます。ブランチを切り替えても会話が続く理由と、ブランチごとに独立させたいときにワークツリーが要る理由を仕様と実機の出力から追います。
Claude Codeのセッションは何に紐づいているか
Claude Codeのセッションが紐づく単位は、gitブランチではなく作業ディレクトリです。会話のトランスクリプトは~/.claude/projects/<project>/<session-id>.jsonlに保存されます。<project>は、作業ディレクトリのパスの英数字以外の文字を-に置き換えた文字列です。変換後の名前が200文字を超えるときは、200文字に切り詰めてパス全体のハッシュを付けます。ブランチ名を決める要素は、この名前のどこにもありません。
ホストアプリにClaude Codeを組み込む用途では、この名前も変えられます。CLAUDE_CONFIG_DIRとCLAUDE_CODE_PROJECT_DIR_NAMEを併用すると、<project>の部分に選んだ名前が使われます。CLAUDE_CONFIG_DIRを指定しない場合、CLAUDE_CODE_PROJECT_DIR_NAMEは無視されます。作業ディレクトリごとに名前が変わらなくなるので、既定の~/.claudeのままでは全プロジェクトが1つに混ざってしまうためです。
同じディレクトリでブランチだけを切り替えたとき、この構造が効いてきます。ディレクトリが変わらない限り、Claude Codeにとっては同じセッションのままです。保存先のトランスクリプトも同じファイルが使われ続けます。
ブランチを切り替えたら会話はどうなるか
作業ディレクトリの中でgit checkoutやgit switchを実行しても、セッションは終了しません。Claudeが読むファイルの中身は新しいブランチのものに変わり、会話履歴はそのまま残ります。
食い違うのはその先です。ブランチAで「この関数を直して」と頼んだ直後にブランチBへ切り替えると、会話は関数Aの話のまま続きます。ところがディスク上にあるのはブランチBの関数です。過去の発言を根拠に、ブランチBに存在しないコードを前提にした提案が出る余地が生まれます。
1つのセッションで複数ブランチを行き来するなら、切り替えた直後に「今のブランチの状態を確認して」と一言添えるのが手軽な対策です。
未コミットの変更でも同じ種類のずれが起きます。ブランチAで編集中のファイルをコミットせずにブランチBへ切り替えたとき、変更が持ち越されるか切り替え自体が拒否されるかは、gitの挙動で決まります。Claude Codeのセッションの仕組みは関係しません。切り替え前にコミットかスタッシュで片付ける判断は、Claude Codeを使わない場合と同じです。
別ブランチのセッションが見つからないときはどこを探すか
ブランチを行き来していると、「さっきのセッションがピッカーに出ない」という場面に当たります。探す範囲は、狭いものから順に広げられます。
セッションが一覧に出ないときの探し方
- 1
ブランチの絞り込みを外す
ピッカー(
/resumeやclaude --resume)でCtrl+Bを押すと、現在のgitブランチのセッションだけに絞り込まれます。もう一度押すと全ブランチに戻ります。 - 2
ワークツリーまで広げる
Ctrl+Wで、同じリポジトリの全ワークツリーのセッションが並びます。このキーはワークツリーが複数あるリポジトリでだけ表示されます。 - 3
名前かタイトルで直接開く
claude --resume <name>は、リポジトリとその全ワークツリーから完全一致を探して再開します。 - 4
マシン全体へ広げる
Ctrl+Aで、このマシン上の全プロジェクトのセッションを表示します。セッションIDが分かれば、claude --resume <session-id>を別のディレクトリから実行しても見つかります。
ピッカーの既定の一覧には、現在のワークツリーのセッションと、/add-dirで現在のディレクトリを加えた他所発のセッションが並びます。各行には、名前(なければAI生成のタイトル)、最終活動からの経過時間、gitブランチ、ファイルサイズが出ます。ハイライトしたセッションの名前はCtrl+Rで変えられます。
保存はディレクトリ、探すのはブランチ。この分担のおかげで、ディレクトリに紐づく仕組みを崩さずに「今のブランチに関係する会話だけ見たい」という需要に応えられています。/resumeのピッカー操作全般はClaude Codeの/resumeコマンドで過去の会話を再開するにまとめています。
最近のセッションを開くだけなら、claude --continueが使えます。起動したディレクトリに記録された直近のセッションが対象です。
名前を付けていないセッションも探せるのか
名前の解決には、/renameで付けた名前のほかに、AIが生成したタイトルも使えます。生成タイトルは最初のプロンプトを要約したもので、小型で高速なモデルへのバックグラウンドリクエストで作られます。解決の対象にならないのは、作業ディレクトリ名と2文字の接尾辞でできた既定の表示名(my-app-3fのような形)だけです。シェルから直接起動したclaude -pのセッションには、生成タイトルも付きません。
同じ名前のセッションが同じマシンで動いているときは、後から名付けた側にauth-refactor-graceful-unicornのような2語の接尾辞が付きます。ブランチごとに「fix」「wip」のような名前を使い回していると、気づかないうちに別名になっていることがあります。
頻繁に探すセッションは、/renameで名前を固定しておくのが確実です。どのディレクトリ・どのブランチで作業していたかを覚えていなくても、名前が一致すれば直接開けます。
同時に別ブランチで作業するには何が要るか
ここまでは、1つのディレクトリでブランチを切り替える話でした。ブランチAとブランチBで独立した会話を同時に走らせたいなら、事情が変わります。切り替えでは実現できません。1つの作業ディレクトリは、同時に1つのブランチしかチェックアウトできないためです。
実際にgitで試すと、次のように拒否されます。mainをチェックアウト中のリポジトリで、別の場所にmainのワークツリーを足そうとした結果です。
git branch feature
git worktree add ../wt-main main
git worktree add ../wt-feature feature
git worktree add ../wt-x feature1行目のgit branch featureは、現在のコミットからfeatureブランチを作るだけです。2行目は、mainがすでに本体のディレクトリでチェックアウトされているため失敗します。
fatal: 'main' is already used by worktree at '~/work/demo/repo'3行目は成功し、featureを専用のディレクトリwt-featureに出します。ところが4行目、同じfeatureをさらに別のディレクトリへ出そうとすると、また拒否されます。
fatal: 'feature' is already used by worktree at '~/work/demo/wt-feature'つまり、ブランチごとにディレクトリが1つ要ります。この「ブランチごとに別のディレクトリ」を作るのがワークツリーです。ディレクトリが分かれれば、セッションもディレクトリ単位で自動的に分かれます。
ブランチを切り替える/ワークツリーを分ける
同じディレクトリで切り替える
トランスクリプトは1本のままです。会話は引き継がれますが、ファイルの中身だけが入れ替わります。同時に動かせるブランチは1つです。
ワークツリーに分ける
ディレクトリごとにトランスクリプトが分かれます。ブランチAとBを同時に開けます。claude --worktreeで起動したセッションでは、本体のチェックアウトへの編集が機械的にブロックされます。
Claude Codeからワークツリーを作るときはclaude --worktree [name](短縮形-w)を使います。--tmuxを足すと、そのワークツリー用のtmuxセッションも作れます。ワークツリーの作り方・baseRefの選び方・依存関係の扱いはClaude Code Worktree実践ガイドが扱っています。
ワークツリーのセッションを再開するとどうなるか
ワークツリーの中で終わったセッション(ワークツリーを終了せずに閉じたもの)は、再開すると同じワークツリーへ自動的に戻ります。対話モードのほか、-pの--continue / --resumeとAgent SDKからの再開でも同じです。
戻る前に、Claude Codeはそのワークツリーが本体とは別のチェックアウトのままかをgitのメタデータで確かめます。確認の結果、再開時の画面メッセージと挙動は次のとおり分かれます。
| メッセージの出だし | 何が起きたか |
|---|---|
Your worktree <path> no longer exists | 何が起きたかディレクトリが削除済み。起動したディレクトリで隔離なしに続行し、紐づけを解除する |
Could not verify your worktree <path> this time | 何が起きたか一時的な要因で確認できなかった。紐づけは保持し、起動したディレクトリで隔離なしに続行する。再度の再開で再確認する |
Did not re-enter your worktree <path> | 何が起きたか安全でないと判断して再入場を拒否した。紐づけを解除し、隔離なしで続行する |
Could not re-enter your worktree <path> | 何が起きたか起動した場所からは保証できなかった。最も多い原因はワークツリーの内側からの起動で、紐づけは保持される。メッセージの続きに対処が書かれ、内側から起動した場合は本体のチェックアウトから起動し直す |
-pやAgent SDK経由の再開は挙動が違います。ワークツリーが削除済みの場合を除き、上の拒否はどれも再開そのものをstderrのエラーで止めます。--output-format stream-jsonなら、同じ理由がerror_during_executionのresultメッセージにも出ます。バッチ実行やSDK統合でワークツリー再開を組み込むなら、削除以外の理由でプロセスが止まる前提で組みます。
--fork-sessionで再開した場合は別扱いです。複製したセッションは起動したディレクトリで始まり、元のセッションのワークツリーには手を付けません。
対話モードで続行する限り、セッション自体は消えません。ワークツリーという隔離が外れても、会話とトランスクリプトは残ります。
ワークツリーの内外を移るとき、トランスクリプトも追従します。Claude Codeがgitで作ったワークツリーに入ると、セッションはワークツリー側のディレクトリの下に記録し直されます。出るときは元へ戻ります。この追従はv2.1.198以降です。WorktreeCreateフックで作ったワークツリーのトランスクリプトは、起動したディレクトリに残ります。
--worktreeで起動した場合やEnterWorktreeで入った場合、ワークツリーのセッションを再開した場合は、隔離中に次の操作が機械的にブロックされます。本体のチェックアウトへのファイル編集と、作業ディレクトリが本体に解決されるコマンドです。gitを本体へ向けるリダイレクト(git -CやGIT_DIRなど)も止まります。BashやMonitorのコマンドは、文面からgitの向き先がワークツリー内に収まると確認できないときもブロックされ、この検査は無効にできません。ワークツリーの中の作業が、本体のチェックアウトを誤って変更しにくくなります。
実機で確認した--resumeとワークツリーのフラグ
v2.1.287でclaude --helpを実行し、関係するオプションの説明を拾いました。
claude --version
claude --help | grep -E -A3 "^\s+(-c|-r|-w|-n|--fork-session|--from-pr)"2.1.287 (Claude Code)
-c, --continue Continue the most recent conversation in the current directory
-r, --resume [value] Resume a conversation by session ID, or open interactive picker with optional search term
-w, --worktree [name] Create a new git worktree for this session (optionally specify a name)
-n, --name <name> Set a display name for this session (shown in the prompt box, /resume picker, and terminal title)
--fork-session When resuming, create a new session ID instead of reusing the original (use with --resume or --continue)
--from-pr [value] Resume a session linked to a PR by PR number/URL, or open interactive picker with optional search term出力は読みやすいよう1項目1行に詰めています。--continueの説明が「current directory」の最新の会話となっている点が、ディレクトリ単位の保存と対応します。ブランチを指定するオプションは、この一覧にありません。
--from-prは、PRに紐づいたセッションを開く別の入口です。ブランチ名を覚えていなくても、PR番号から会話へ戻れます。
/loopで始めたセッションが一覧から消えるのはなぜか
もう1つ、見落としやすいのが/loopです。最初のプロンプトが/loopだったセッションは、ピッカーに表示されず、claude --continueも飛ばします。定期実行を仕込んだセッションが、ブランチを行き来した後で見つからなくなったように見えます。
これは/loopの仕様で、ブランチをまたいだことによる不具合ではありません。会話の途中で/loopを実行した場合は、セッションは隠れません。v2.1.211より前は、会話の早い段階で/loopを実行しただけでも、ピッカーから永久に消えました。
ディレクトリ単位だと切り替えと並行作業はどう分かれるか
ここまでの挙動をまとめると、保存の単位がディレクトリであることの帰結は2つあります。同じディレクトリでの軽いブランチ切り替えでは、会話が保たれます。複数ブランチを並行させるなら、ワークツリーでディレクトリごと分ければ、会話もファイルも別になります。
仮にセッションをブランチに直接紐づけると、ブランチを切り替えるたびに別の会話になり、直前までの文脈は引き継がれません。逆にすべてを1つのディレクトリに閉じる場合は、並行作業の隔離を自分で組む手間が残ります。実際の分担は、切り替えなら同じ会話、隔離したいならワークツリーという2段階です。
まとめ
会話を分けたいなら、切り替えではなくワークツリーを使う。それが、ブランチをまたぐセッションで読者が持ち帰る判断です。