Claude Code を git 以外のバージョン管理(SVN / Mercurial)で使っていて並列セッション機能を自前で再現したい人、または git 環境でも worktree 作成・削除に独自処理を仕込みたい人向け
SVN や Mercurial で動いているプロジェクトに Claude Code を導入して --worktree を成立させたいとき、または git プロジェクトで作成時に依存関係インストールや認証ファイルのコピー、削除時にログ送信などの独自処理を1か所にまとめたいときに .claude/settings.json の hooks セクションに書いて仕込む
Claude Code は並列セッション機能を持っています。同じプロジェクトを別のフォルダにコピーして、同時に複数の作業を走らせる仕組みです。これを発動するとき、内部では git worktree add が自動で叩かれます。WorktreeCreate は、この「作成処理そのものを丸ごと自分のスクリプトに差し替える」ためのフックです。git の代わりに SVN や Mercurial で working copy を切り出したり、作成時に依存関係のインストールやログ送信を仕込みたいときに使います。
WorktreeRemove はその対になる削除フック。ただし役割は非対称で、こちらは観察専用です。セッション終了や subagent の完了で worktree が消されるタイミングで発火しますが、削除自体を止めることはできません。掃除処理・ログ送信・リソース解放を仕込む場所として割り切るのが正解です。
噛み砕くと
会社のオフィスを想像してください。普通は「会議室を取る予約システム」が決まったやり方で部屋を割り当てます。WorktreeCreate は、その予約システムごと「うちの会社独自のルールで部屋取りますんで」と差し替える仕組みです。Claude Code は本来 git で作業フォルダを切り出しますが、このフックを置くと git をすっ飛ばして、自分の書いた手順だけが走ります。
差し替えなので、git 環境でこのフックを置くと git worktree add が動かなくなります。Claude Code は「うちのスクリプトに任せたから git は呼ばない」と判断します。これが落とし穴になりやすいので頭に入れておきます。
もう1つ大事なこと。Claude Code のフックは普通「exit 2 だけが操作を止める特別な合図」です。それ以外の非ゼロ終了は警告扱いで操作は続行します。ところが WorktreeCreate はこの慣例から外れていて、非ゼロ終了が全部 blocking。1でも127でも、ゼロ以外で終わると worktree 作成は中止されます。これも頭に入れておきます。
大事な前提:git 以外で使うときと git 環境で使うときで意味が違う
このフックを置く動機は2パターンあります。1つ目は git 以外のバージョン管理を使っているケース。SVN や Mercurial で動いているプロジェクトに Claude Code を導入すると、デフォルトの git worktree は当然失敗します。そこで WorktreeCreate に「svn checkout してこい」と書いて、git の代わりを務めさせます。2つ目は git 環境のままで作成時の独自処理を足したいケース。並列セッション起動時に自動で npm install を走らせたり、社内ログサーバーに通知したりという用途です。
もう1つ、地味だけど重要な前提が .worktreeinclude の扱い。git デフォルトの worktree 作成では、本来コピーされない gitignore 対象ファイルを「これは新しい worktree にも持っていってほしい」と指定する仕組みがあります。たとえば .env や node_modules の一部設定がここで指定対象になります。WorktreeCreate を置いた瞬間、この自動コピーは止まります。差し替えなので当然です。必要なら自分のスクリプトの中で cp なりなんなりで明示的に運ぶ必要があります。
「会社のSVNで動いている古いウェブサービス」を例に、実際の手順を見る
題材として、会社の SVN サーバーで動いている古いウェブサービスを想定します。普段は1人で1つの feature を進めていますが、急なバグ修正が降ってきて、いま触っている feature とは別の場所で trunk から checkout し直して並行作業したい場面。claude --worktree fix-login-bug と叩けば、Claude Code が新しい作業フォルダを切ってくれて、いまのセッションを止めずに別件を進められるようにします。git なら標準機能で済みますが、SVN なのでフックで差し替えます。
ステップ1: フックを書く場所を決める
プロジェクトのルートに .claude/settings.json があるので、ここに hooks セクションを足します。チーム全員に配るなら .claude/settings.json(git 管理下)、自分だけなら .claude/settings.local.json(git 管理外)に書きます。
ステップ2: WorktreeCreate の中身を書く
公式が SVN 向けに出しているサンプルをそのまま使います。受け取った JSON から name(worktree の名前)を取り出し、$HOME/.claude/worktrees/<名前> に SVN の trunk を checkout し、最後に標準出力へ作業フォルダの絶対場所を1行で出す形です。
{
"hooks": {
"WorktreeCreate": [
{
"hooks": [
{
"type": "command",
"command": "bash -c 'NAME=$(jq -r .name); DIR=\"$HOME/.claude/worktrees/$NAME\"; svn checkout https://svn.example.com/repo/trunk \"$DIR\" >&2 && echo \"$DIR\"'"
}
]
}
]
}
}
標準入力で受け取る JSON に含まれるのは session_id cwd hook_event_name、そして worktree の作成リクエスト名 name など。今回は jq -r .name で名前だけ取り出して使っています。svn checkout の進捗ログは >&2 という書き方で標準エラー側へ逃がします。標準出力は最後の echo "$DIR" 1行だけにします。ここが大事で、Claude Code は標準出力に出てきた文字列を「作成された作業フォルダの場所」として受け取ります。余計な出力が混ざると場所が壊れます。
ステップ3: WorktreeRemove で掃除する
セッションを閉じたとき、SVN の working copy が $HOME/.claude/worktrees/ に残り続けるとローカルが散らかります。WorktreeRemove で消すように仕込みます。
{
"hooks": {
"WorktreeRemove": [
{
"hooks": [
{
"type": "command",
"command": "bash -c 'DATA=$(cat); DIR=$(echo \"$DATA\" | jq -r .worktree_path); rm -rf \"$DIR\"'"
}
]
}
]
}
}
受け取る JSON にはすでに削除対象の worktree_path が入っています(これは WorktreeRemove 固有のフィールドで、消そうとしているフォルダの絶対場所が文字列で渡ってきます)。これをそのまま rm -rf に渡すだけ。注意:このフックは観察用なので、ここで exit 1 しても削除自体は止められません。ログ送信や掃除の失敗があっても Claude Code は粛々と次に進みます。
ステップ4: 実際に並列セッションを切ってみる
ターミナルで以下を叩きます。
$ claude --worktree fix-login-bug
Claude Code は内部で WorktreeCreate フックを発火し、JSON を渡して結果を待ちます。フックが $HOME/.claude/worktrees/fix-login-bug を出力して終わると、Claude Code はそのフォルダで新しいセッションを開始します。これで元のセッションはそのまま、別のセッションでバグ修正を進められる状態になりました。
ステップ5: ここで初心者がよくやる勘違い
「svn checkout の進捗ログを echo で出しちゃえばいいや」と書くと、Claude Code はその進捗ログを「作業フォルダの場所」として受け取ってしまい、存在しない場所を指して落ちます。SVN や git のコマンドが普通に出してくる進捗・ステータスは、全部 1>&2 で標準エラー側に逃がす。標準出力は 最後の絶対場所1行だけ。これがコマンド型フックの作法です。
ステップ6: セッションを閉じる
Claude Code を終了すると WorktreeRemove が発火し、$HOME/.claude/worktrees/fix-login-bug が rm -rf で消えます。ローカルに散らかさず、必要なときだけ working copy を持つ運用が成立します。
つまり WorktreeCreate / WorktreeRemove は何をしてくれるのか
- やってくれる: 並列セッションのために作業フォルダを作る処理(WorktreeCreate)と、消す処理(WorktreeRemove)をユーザー側のスクリプトに丸ごと差し替えること。git 以外のバージョン管理でも
--worktree機能を成立させられる - やってくれない: WorktreeRemove での削除中断。観察専用なので、ここで止めようとしても無視される。掃除や記録に限定して使う
- 意味が薄い場面: 純粋な git プロジェクトで標準動作のままで満足できているとき。フックを置いた瞬間に
.worktreeincludeも標準処理も全部止まるので、足したい処理が少しだけなら別フック(PostToolUse など)で済ませた方が安全
使いどころ3シナリオ(具体題材で再現)
シナリオ1: 会社の SVN で動く古い基幹システムで Claude Code を試したいとき
SVN は 2026年現在も金融・製造系の社内システムでは現役です。Claude Code を導入したくても git worktree が動かないので並列セッション機能が使えない、と諦める前に WorktreeCreate を置きます。1つの基幹システムに対して「経費精算画面の改修」と「ログイン周りの調査」を同時に走らせたい、みたいな並列ニーズが出てきたとき、本文サンプルの形で SVN checkout を差し込めば --worktree がそのまま使えます。チーム全員に配る場合は .claude/settings.json に置いて git で配布します。
シナリオ2: git プロジェクトだが worktree 作成時に npm install を自動で走らせたいとき
家計簿アプリのフロントエンドが Node.js 系で、新しい worktree を切るたびに node_modules を入れ直す必要がある状況。普通は人間が手で cd して npm install しますが、これを WorktreeCreate に書いておくと自動化できます。ただし注意:このフックを置くと git worktree add も自分のスクリプトで再現する必要が出てきます。「git の標準動作にちょい足ししたいだけ」なら、後述するように別の場所で npm install を走らせる方が無難です。本フックは「処理全体を差し替える」性質なので、軽い追加処理には重い選択肢になります。
シナリオ3: 並列セッション終了時に社内ログ基盤へ作業時間を送りたいとき
料理ブログ運営チームで Claude Code の利用時間を社内 Slack に通知したい、というような運用。WorktreeRemove はちょうど「セッションを閉じた瞬間」に発火するので、JSON から session_id と worktree_path を拾って curl で Slack Webhook に投げれば、誰がどの作業を何分やったかが自動で記録できます。削除を止められない仕様は、こういう「観察と通知」用途とは相性が良い設計です。
初心者が踏みやすい落とし穴
- git 環境で WorktreeCreate を置いた瞬間に
git worktree addが動かなくなる。フックは「差し替え」なので、ちょい足し用途では使わない。標準動作に追加処理を足したいだけなら、別フックを検討する - 標準出力に絶対場所以外を1文字でも出すと作成失敗扱いになる。
svn checkoutやgit cloneの進捗ログは必ず1>&2で標準エラーに逃がす。echo "$DIR"だけが標準出力に残る形が正解 - WorktreeCreate だけ「非ゼロ終了が全部 blocking」という例外仕様。他のフックは exit 2 だけが特別だが、ここは違う。スクリプトの最後で意図せず
exit 1が走らないようにset -eの挙動とエラーハンドリングを確認する .worktreeincludeの自動コピーは止まる。.envや認証ファイルを新しい worktree でも使いたい場合、フックスクリプト側でcpを仕込まないと参照できずに作業が止まる- WorktreeRemove で
exit 1しても削除は止められない。「失敗したから消さない」ロジックは書けない。観察と記録に徹する。掃除を止めたい場合はSessionEndなど別のフックを使う - Matcher が使えないので両フックとも常時発火する。特定の worktree 名や条件でだけ動かしたい場合は、スクリプトの中で JSON の
name(作成時)やworktree_path(削除時)を見て自分で条件分岐する必要がある - システム本体の保存場所には絶対に作業フォルダを切らない。
$HOME/.claude/worktrees/や/tmp/配下に限定する。rm -rfが走るので、間違った場所を指すと取り返しがつかない
書き方
settings.json に hooks.WorktreeCreate / hooks.WorktreeRemove を定義(どちらも Matcher 指定なし、常時発火)
やってみるとこうなる
入力
{
"hooks": {
"WorktreeCreate": [
{
"hooks": [
{
"type": "command",
"command": "bash -c 'NAME=$(jq -r .name); DIR=\"$HOME/.claude/worktrees/$NAME\"; svn checkout https://svn.example.com/repo/trunk \"$DIR\" >&2 && echo \"$DIR\"'"
}
]
}
],
"WorktreeRemove": [
{
"hooks": [
{
"type": "command",
"command": "bash -c 'DIR=$(jq -r .worktree_path); rm -rf \"$DIR\"'"
}
]
}
]
}
}
出力例
WorktreeCreate の標準入力には {"session_id":"abc123","cwd":"/Users/me/projects/legacy-svn-app","hook_event_name":"WorktreeCreate","name":"fix-login-bug"} のような JSON が流れてくる(公式 SVN サンプルが jq -r .name で読み取るフィールド)。スクリプトは標準出力に作業フォルダの絶対場所1行だけを出して exit 0 で終わると成功。1文字でも余計な出力が混ざると場所が壊れる。他のフックと違い、ゼロ以外の終了コードはすべて作成中止(blocking)になる例外仕様。WorktreeRemove は {"session_id":"abc123","hook_event_name":"WorktreeRemove","worktree_path":"/Users/me/.claude/worktrees/fix-login-bug"} を受け取って掃除するだけで、終了コードは無視される(削除自体を止められない観察フック)
このページに出てきた言葉
- worktree
- 同じプロジェクトを別フォルダにコピーして複数作業を同時に走らせる仕組み。Claude Code は <code>--worktree</code> を付けて起動すると <code>~/.claude/worktrees/</code> 配下に作業用コピーを切り出す
- working copy
- SVN や Mercurial で「サーバーから取ってきたファイル一式が置いてある作業用フォルダ」のこと
- subagent
- Claude Code がメインの会話とは別に裏で走らせる小さなセッション。完了すると WorktreeRemove が発火する場面もある
- SVN
- Subversion の略。git より古いバージョン管理の仕組みで、企業の昔からあるシステムでまだ現役
- Mercurial
- git と同時期に登場したバージョン管理。今は git に押されているが残っている現場がある
- blocking
- 「処理を止めてその先に進ませない」という意味。WorktreeCreate ではゼロ以外の終了コードがすべて blocking 扱いになる例外仕様がある
- .worktreeinclude
- git の worktree 作成時に「gitignore 対象だけど新しい worktree にも持っていく」ファイルを指定する仕組み。WorktreeCreate を置くと自動処理は止まる
- trunk
- SVN でいう「メインの最新版」の置き場所。git でいう <code>main</code> ブランチに相当
- 標準出力 / 標準エラー
- コマンドが結果を吐く出口が2つあり、片方が普通の結果用、もう片方が進捗・エラー用。<code>1>&2</code> は普通の結果用に出そうとしたものをエラー側に流す書き方