金曜の夕方に、公開中のアプリでクラッシュが出ているという連絡を受け取りました。手元では別のアプリの機能追加を半分ほど進めていて、変更はまだコミットできる形になっておりません。ブランチを切り替えれば作りかけが宙に浮き、切り替えなければ修正に取りかかれない——この二択が、個人開発でいちばん胃が痛くなる場面です。
その日から、私は作業ツリーそのものを分ける方に倒しました。Git の worktree でディレクトリを 1 本増やし、そこで Claude Code をもう一つ起動します。ブランチの付け替えではなく、場所の移動として扱う運用です。
以下は、手元の Git 2.34 で挙動を一つずつ確かめながら書いた実装メモです。コマンドの一覧よりも先に「何が分かれていて、何が分かれていないのか」を押さえた方が、事故はずっと減るのだと思います。
worktree が分けるもの、共有するもの
worktree を足すと、ディレクトリは増えますが、リポジトリは増えません。ここを取り違えると、片方の worktree での操作がもう片方に波及して驚くことになります。
追加した worktree の .git は、ディレクトリではなくテキストファイルです。中身は親リポジトリへの道しるべだけで、実体はありません。
$ git worktree add ../wt-a feature/a
Preparing worktree (checking out 'feature/a' )
HEAD is now at dc6dc31 a
$ file ../wt-a/.git
. ./wt-a/.git: ASCII text
$ cat ../wt-a/.git
gitdir: /home/me/demo/.git/worktrees/wt-a
共有されている度合いは、stash で確かめるのがいちばん早いと感じています。メイン側で退避した内容が、追加した worktree からもそのまま見えます。
# メイン側の作業ツリーで退避
$ git stash
Saved working directory and index state WIP on main: c88bbda init
# 追加した worktree から一覧を見る
$ git -C ../wt-a stash list
stash@ {0} : WIP on main: c88bbda init
ローカル設定も同じく共有です。片方で git config --local を書けば、もう片方から読めます。
$ git config --local demo.key mainside
$ git -C ../wt-a config --get demo.key
mainside
実測した結果を一覧にすると、次のようになります。
分かれているのは「いま触っている手元」だけで、リポジトリの記憶は一つです。 この一行を最初に腹落ちさせておけば、「stash したのにもう一方で見えている」といった戸惑いは起きません。
新しい worktree には、動く環境が付いてきません
初日にいちばんつまずいたのがここでした。git worktree add が展開するのはコミットに入っているものだけで、.gitignore に置いたものは一切付いてきません。
$ ls -a ../wt-a
. .. .git .gitignore src
node_modules: なし
.env.local: なし
.claude/settings.local.json: なし
つまり、新しい worktree はリポジトリとしては完成していても、開発環境としては空です。依存も、ローカルの環境変数も、エディタやツールの手元設定も、自分で運び込む必要があります。私は最初、git worktree add の直後に npm run dev を叩いて動かず、Git の不具合を疑って 20 分ほど溶かしました。原因は単に依存が入っていないことでした。
そこで、作る操作と環境を揃える操作を一つのスクリプトにまとめています。
#!/usr/bin/env bash
# wt-new.sh — worktree を 1 本作り、追跡外の作業環境まで揃える
set -euo pipefail
BRANCH = " $1 " # 例: hotfix/login-timeout
DIR = "../wt-${ BRANCH // \/ / - }"
ROOT = "$( git rev-parse --show-toplevel )"
git fetch -q origin main
git worktree add " $DIR " -b " $BRANCH " origin/main
# 追跡外だが手元に必要なものだけを、明示的に運ぶ
for f in .env.local .claude/settings.local.json ; do
[ -f " $ROOT / $f " ] || continue
mkdir -p " $DIR /$( dirname " $f ")"
cp " $ROOT / $f " " $DIR / $f "
done
( cd " $DIR " && npm ci --prefer-offline )
echo "ready: $DIR "
運ぶファイルを列挙式にしているのには理由があります。.gitignore の中身を丸ごと複製してしまうと、本番の鍵や他アプリの資格情報まで新しいディレクトリに増えてしまうためです。持ち出すものは、毎回名前で書く。 面倒でも、この一手間だけは省かないようにしております。
CLAUDE.md はリポジトリに追跡されているので、何もしなくても新しい worktree に付いてきます。一方で .claude/settings.local.json のような手元だけの設定は来ません。この非対称が、次の節の話につながります。
同じブランチは二度チェックアウトできません
worktree の制約で最初に出会うのが、このエラーです。
$ git worktree add ../wt-dup main
fatal: 'main' is already checked out at '/home/me/demo'
Git が意地悪をしているわけではなく、同じブランチの HEAD を二か所から動かせてしまう状態を避けているだけです。逃げ道は三つあります。
新しいブランチを切って割り当てる方法(通常の開発)
既存の別ブランチを割り当てる方法(レビュー用の取り込み)
ブランチから切り離して置く方法(読むだけの参照用)
# 1. 作業用に新規ブランチごと生やす
git worktree add ../wt-hotfix -b hotfix/login-timeout origin/main
# 2. 同僚のブランチをレビューのために展開する
git worktree add ../wt-review review/pr-482
# 3. タグを読むためだけに置く(ブランチを消費しない)
git worktree add --detach ../wt-inspect v2.3.0
三つ目の --detach は、リリース済みのコードを手元で動かして再現確認したいときに重宝します。ブランチを一本も消費しないので、後片付けの心理的な負担が軽いのです。
Claude Code を worktree ごとに起動する
ここが本題ですが、拍子抜けするほど単純です。worktree のための専用フラグを覚える必要はありません。Claude Code は起動したディレクトリを作業対象にしますので、worktree へ移動して立ち上げれば、それがそのまま分離になります。
# 端末タブ 1:機能開発(作りかけを触り続ける)
cd ~/projects/wt-feature-dashboard
claude
# 端末タブ 2:割り込みの修正(別ブランチ・別ディレクトリ)
cd ~/projects/wt-hotfix-login-timeout
claude
運用してみて効いたと感じているのは、次の三点です。
会話の文脈がタスクごとに残ります — 履歴は作業ディレクトリ単位で積み上がるため、翌朝に再開しても別タスクの話が混ざりません
許可の記憶は引き継がれません — .claude/settings.local.json は追跡外のため、新しい worktree では一度出し直すか、先ほどのスクリプトで運びます
CLAUDE.md を worktree 側で上書きできます — 修正専用のディレクトリには「触ってよい範囲はここだけ」と書いた一枚を置いています
他の worktree のコードも参照させたい場面では、起動時に --add-dir で対象ディレクトリを明示的に足します。逆に言えば、明示しない限り別の worktree には手が伸びません。この既定の狭さが、並行して走らせるときの安心材料になっています。
一つだけ、はっきり線を引いている点があります。同じファイルを二つのセッションに同時に触らせないことです。所有権はディレクトリ単位で分け、共通のユーティリティを両方から直す必要が出たときは、片方を止めて順番に入れます。並列で速くなるのは待ち時間だけで、衝突の解決は結局こちらの手仕事なのだと気づいたのは、同じ関数を二重に書き換えて半日を無駄にしたあとでした。
割り込みが入った日の一連の流れ
冒頭の金曜日を、いまの手順でなぞると次のようになります。
作りかけには触りません。ディレクトリを移動するだけで、そのまま残します
./wt-new.sh hotfix/login-timeout で修正用の worktree を生やします
新しいディレクトリで Claude Code を起動し、再現条件とログを渡して原因を絞ります
修正できたらその worktree の中だけでテストを回し、git push して PR を出します
レビュー待ちのあいだに、元のディレクトリへ戻って機能開発を続けます
マージされたら worktree を畳み、ブランチも消します
# 2. 生やす
./wt-new.sh hotfix/login-timeout
# 4. その worktree の中だけで検証して出す
cd ../wt-hotfix-login-timeout
npm test && npm run build
git push -u origin hotfix/login-timeout
# 6. 畳む
cd -
git worktree remove ../wt-hotfix-login-timeout
git branch -d hotfix/login-timeout
手順 5 が、この運用のいちばんの果実です。レビューの待ち時間に別の頭を使うのではなく、中断されていた元の作業に戻れる という点が、体感の差として残ります。
後片付け — prunable を残さない
worktree は、ディレクトリを手で消しても登録が残ります。放っておくと一覧が実態とずれていきます。
$ rm -rf ../wt-a
$ git worktree list
/home/me/demo c88bbda [main]
/home/me/wt-a dc6dc31 [feature/a] prunable
$ git worktree prune
$ git worktree list
/home/me/demo c88bbda [main]
prunable の表示が、掃除してよい印です。逆に、長期間残したい検証用の worktree は錠を掛けておくと、うっかり消す事故を防げます。
$ git worktree lock ../wt-b --reason "再現検証中"
$ git worktree remove ../wt-b
fatal: cannot remove a locked working tree, lock reason: 再現検証中
use 'remove -f -f' to override or unlock first
定期的な棚卸しをスクリプトに寄せる場合は、人向けの一覧ではなく機械可読の出力を使います。
# パスだけを取り出して、2 週間触っていないものを洗い出す
git worktree list --porcelain \
| awk '/^worktree /{print $2}' \
| while read -r d ; do
[ -d " $d " ] || continue
find " $d " -maxdepth 1 -mtime +14 -name .git -print
done
容量の心配は、思っていたより小さいものでした。手元の検証では .git 全体が 308 KB のリポジトリで、worktree 1 本あたりの管理ファイルは 32 KB。全体の約 10% です。clone を増やす場合と違ってオブジェクトは複製されませんので、増えるのは実際のソースファイル分だけになります。
どこまで並べるか
最初のうち、私は worktree を 5 本ほど並べて、それぞれにセッションを立てていました。結果は芳しくありませんでした。どれも半端に進み、翌日どのディレクトリが何の途中だったのか思い出せなくなったのです。
いまは同時に 2 本、多くても 3 本までという線引きにしています。worktree を増やす目的は、待ち時間を埋めることであって、同時に複数のことを考えることではありません。
手を動かす場所は増やしても、判断する場所は増やしません。 決めるのはいつも一か所、という感覚を保てているうちは、並列にしても取り違えが起きにくいのだと感じています。
判断に迷ったときの目安として、私は次の三つを見ています。ディレクトリ名からいま何の途中か言えるか、止めても明日の自分が再開できるか、そして、そのタスクは本当に「待ち」で止まっているのか。三つ目に「いいえ」が付くなら、それは並列にすべきタスクではなく、順番に片付けるべきタスクなのかもしれません。
明日の割り込みのために
まずは worktree を 1 本だけ、次の割り込みが来たときの逃げ場所として用意しておいていただければと思います。git worktree add --detach ../wt-inspect HEAD のように、ブランチを消費しない形で置いておくだけでも、切り替えの摩擦は目に見えて軽くなります。
私も、作りかけを抱えたまま金曜の連絡に応えられるようになってから、週末の気持ちがずいぶん軽くなりました。お読みいただきありがとうございました。