worktree.symlinkDirectoriesでnode_modulesを共有(Claude Code)
Claude Codeのworktree.symlinkDirectoriesで、node_modulesなど大きなディレクトリを複製せず各worktreeへシンボリックリンク共有する設定方法と注意点。
worktreeごとに依存関係を入れ直したくないときの設定
worktree.symlinkDirectories は、Claude Codeが新しいgit worktreeを作るたびに、指定したディレクトリをメインリポジトリへのシンボリックリンク(symlink)にする設定です。settings.jsonの worktree オブジェクトに、リポジトリルートからの相対パスを文字列の配列で書きます。
{
"worktree": {
"symlinkDirectories": ["node_modules", ".cache"]
}
}型は文字列の配列、既定値はunsetで、何も書かなければどのディレクトリもリンクされません。スコープは「Any file」なので、プロジェクトの .claude/settings.json でもユーザー設定でも効きます。
--worktree(-w)で作るworktreeは、既定で .claude/worktrees/<name>/ に置かれ、ブランチは worktree-<name> になります。そこは新規のチェックアウトなので、gitが追跡していない node_modules は存在しません。ふだんはClaudeに依存関係のインストールを頼むか、自分で npm install を回します。並列セッションが増えるほど、同じパッケージがworktreeの数だけディスクに複製されます。symlinkDirectories に書いたディレクトリは、この複製もインストール待ちも発生せず、メインリポジトリの実体を指すリンクとして用意されます。
自動で共有されるものと、自分で指定するもの
設定を書く前に、worktreeがもともと何を共有しているかを知っておくと、書く量が減ります。
共有の仕組みは2系統ある
何も書かなくても共有される
メインリポジトリの .git ディレクトリ、プロジェクトスコープで入れたプラグイン、「Yes, and don't ask again」で保存したBashの権限ルール。後者はメインチェックアウトの .claude/settings.local.json に書かれ、全worktreeに効きます。
メインチェックアウトの未追跡の .claude/skills も読み通されます。worktreeのルートに .claude/skills ディレクトリが無いとき(.claude/skills をgitignoreしている場合など)は、メインチェックアウトのプロジェクトスキルがworktreeのセッションで読み込まれます。worktree自身が .claude/skills を持っていれば、そちらだけが読み込まれます。スキルの読み通しはv2.1.277以降で、--worktree でも git worktree add でもデスクトップアプリでも同じように働きます。
symlinkDirectoriesで指定する
node_modules や .cache のように、gitが追跡していない大きなディレクトリ。指定しなければ、新しいworktreeには存在しません。
プラグインの共有はv2.1.200以降、権限ルールがworktree側ではなくメインチェックアウトに保存される挙動はv2.1.211以降です。それ以前のバージョンでは、worktreeで承認したルールがそのworktree内に保存され、worktreeを削除すると消えました。WindowsなどClaude Codeがリポジトリルートを使わない環境では、ルールはそのworktreeに残ります。
設定してから確かめるまでの流れ
設定から確認までの3ステップ
- 1
settings.jsonに書く
上の例のように
worktree.symlinkDirectoriesを追加します。プロジェクト共有なら.claude/settings.json、自分だけならユーザー設定です。 - 2
worktreeを新しく作る
claude --worktree <名前>で起動します。すでにあるworktreeには、あとで説明する手順で自分でリンクを張ります。 - 3
リンクになっているか見る
worktreeのルートで
ls -laを実行し、行末が->でメインリポジトリを指していれば成功です。
手順3の確認コマンドと、リンクになっているときの出力はこうなります。リンク先は、メインリポジトリの node_modules の絶対パスです。
ls -la node_moduleslrwxr-xr-x 1 user staff 26 10月 2 22:57 node_modules -> /path/to/main/node_modules先頭の l がリンクの印です。-> が出ずに通常のディレクトリとして並ぶなら、配列のパスの綴りか、設定ファイルの置き場所を疑います。パスはリポジトリルートからの相対パスで書きます。
symlinkしたnode_modulesがgit statusに出るときの直し方
手でリンクを張る場合も、Claude Codeの設定が作るのも同じsymlinkなので、共通でつまずく点があります。.gitignore に node_modules/ と末尾スラッシュ付きで書いていると、symlinkの node_modules は無視されません。末尾スラッシュ付きのパターンは「ディレクトリ」だけに当たり、リンクはディレクトリではなく1つのファイルとして扱われるためです。
手元のgitで、.gitignore が node_modules/ のリポジトリにworktreeを作り、node_modules へのリンクを張って git status --short を実行すると、次のようになりました。
?? node_modules.gitignore を末尾スラッシュなしの node_modules に変えて同じ操作をすると、この行は出ませんでした。symlinkで共有するディレクトリは、末尾スラッシュなしでもignoreされるように書いておくと、worktreeでの git add -A にリンクが紛れ込みにくくなります。
.claude/worktrees/ 自体も、公式ドキュメントが .gitignore に入れるよう案内しています。worktreeの中身がメインチェックアウトで未追跡ファイルとして並ばないようにするためです。
共有ディレクトリへの書き込みは全worktreeに届く
symlinkは同じ実体を複数の場所から見せる仕組みで、コピーではありません。あるworktreeでの変更は、そのまま他のworktreeにも見えます。
向いているのは、並列で動く各セッションの package.json がほぼ同じケースです。依存関係を大きく変えるブランチを試すworktreeでは、そのworktreeだけリンクを外して独立した node_modules を持たせるという切り分けが考えられます。
ビルドキャッシュ(.next や .turbo など)も同じ理由で注意が要ります。ブランチによって出力が変わるディレクトリを共有すると、あるworktreeのビルドが別のworktreeから見えるファイルを書き換えます。node_modules のようにブランチを変えても中身がほぼ同じものから始めるのが無難です。
worktreeを削除したときリンク先まで消えないかも気になる点です。Windowsについては公式ドキュメントに記述があり、worktree内のフォルダがNTFSジャンクションやディレクトリsymlinkなら、削除されるのはリンクだけで、指す先のフォルダは残ります。v2.1.205より前は、サブディレクトリの奥にあるリンクを持つworktreeを削除すると、リンク先のフォルダまで消えることがありました。
ほかのworktree設定との役割分担
worktree オブジェクトには symlinkDirectories 以外にも、worktreeの作り方を変えるキーが3つあります。
worktreeの作り方を変える4つのキー
symlinkDirectories
指定ディレクトリを複製せずリンクにして、大きなディレクトリがworktreeの数だけディスクを占める状態を避けます。既定はunsetで、書かなければリンクは作られません。
sparsePaths
指定ディレクトリとルート直下のファイルだけをgitのsparse-checkoutで書き出します。巨大モノレポの作成を速くします。
baseRef
分岐元を選びます。既定の
"fresh"はorigin/<default-branch>、"head"は手元のHEADから分岐し、未pushのコミットも持ち込みます。ブランチ名は指定できません。bgIsolation
バックグラウンドセッションの編集を制御します。既定の
"worktree"では、EnterWorktreeを呼ぶまでメインチェックアウトへのEditとWriteを止めます。"none"なら直接編集します。
sparsePaths と symlinkDirectories は同じ worktree オブジェクトに並べて書けます。次の例は、packages/my-app と shared/utils だけを書き出し、packages/my-app/node_modules をリンクにする書き方です。
{
"worktree": {
"sparsePaths": ["packages/my-app", "shared/utils"],
"symlinkDirectories": ["packages/my-app/node_modules"]
}
}baseRef が "fresh" のとき、Claude Codeは origin/HEAD を最新に保つため、24時間以上取得していなければ最大5秒だけデフォルトブランチをfetchします。取得に失敗した場合は、手元にキャッシュされた参照を使います。リモートが設定されていない場合や、origin/HEAD が手元にキャッシュされておらず取得もできない場合は、手元の HEAD から分岐します。fetchは端末での入力を待たず、gitやsshがパスワード・鍵のパスフレーズ・新しいSSHホストの確認を求める場合も失敗として扱われます。v2.1.208より前は、手元にキャッシュ済みの origin/HEAD をそのまま使っていました。分岐元の選択とディレクトリ共有は独立した設定なので、片方だけ変えても構いません。
gitignoreされたファイルを持ち込みたいときは、設定ではなくプロジェクトルートの .worktreeinclude を使います。.env のような小さな環境ファイルはコピーで持ち込み、node_modules のような大きなディレクトリはこの設定でリンクにする、という分け方です。
WorktreeCreateフックで作成処理を置き換えているとき
WorktreeCreate フックはgitの既定の作成処理を丸ごと置き換えます。SVNやPerforceなど非gitのバージョン管理と組み合わせる場合、公式ドキュメントは「.worktreeinclude は処理されない」と明記しています。ローカル設定のコピーは、フックスクリプトの中で行う必要があります。リンクの作成も同じフックスクリプトの中に書けます。
gitコマンドで直接作ったworktreeには、手でリンクを張れます。
git worktree add ../feature-x -b feature-x
ln -s "$(pwd)/node_modules" ../feature-x/node_modulesworktreeの並列運用そのもの(隔離の仕組み・サブエージェントでの使い方・掃除の挙動)はClaude Code Worktree実践ガイドにあります。サブエージェントやバックグラウンドセッション用のworktreeは、cleanupPeriodDays より古くなると定期掃除で消えます。Claude Codeの settings.json では、allowedTools/disallowedToolsで権限を絞る設定や --mcp-configと--strict-mcp-configの使い分けも同じファイルに並べて書けます。