- Git
git worktreeの使い方|仕組み・全コマンド早見表・エラー対処と実測
結論
git worktree は、1つの .git を共有したまま作業ディレクトリを増やし、別ブランチを同時に開く機能です。git worktree add で作り、remove で消します。
この記事でわかること
- 状況別13パターンの早見表(add・remove・prune)
- エラー文17件の直し方と、やってはいけない操作6つ
- 10個作った容量と、pnpm・uv で依存を共有した実測
worktree は、1つの .git からブランチごとの作業ディレクトリを何本も出す仕組みです。
flowchart TD
G[".git<br>履歴・ブランチ・設定を共有"]
G --> M["app/<br>main"]
G --> X["app-fix/<br>fix"]
G --> F["app-feature/<br>feature"]
clone と違って履歴・ブランチ・設定は1か所だけ、stash と違って切り替えの操作がありません。
| やりたいこと | コマンド | 実行結果の見出し |
|---|---|---|
| 新しいブランチを作って追加 | git worktree add -b feature-a ../app-feature-a |
add の使い方 |
| 既存ブランチを別ディレクトリで開く | git worktree add ../app-fix-b fix-b |
add の使い方 |
| 一覧(ロック理由も) | git worktree list -v |
list の見方 |
| 削除 | git worktree remove ../app-feature-a |
remove |
| 別の worktree へ移動 | cd ../app-feature-a(switch サブコマンドは無い) |
git worktree switch |
| rm -rf で消した後の片付け | git worktree prune -v |
prune |
| リモートにしかないブランチを開く | git fetch → git worktree add ../app-feature-c feature-c |
add の使い方 |
| 変更が残っていても削除 | git worktree remove --force ../app-feature-a |
remove |
| 過去のコミットをブランチなしで開く | git worktree add --detach ../app-review HEAD~1 |
add の使い方 |
| 場所を移す | git worktree move ../app-fix-b ../wt/fix-b |
move |
| mv で動かした後の修復 | git worktree repair ../wt/fix-b-moved |
repair |
| 削除・移動・prune から守る | git worktree lock ../app-review |
lock |
| ロック解除 | git worktree unlock ../app-review |
lock |
確認環境:Git 2.43.0(Ubuntu 24.04)。使い捨てのリポジトリと、ローカルに作った bare リポジトリ(リモートの代わり)で、この記事のコマンドをすべて実行し、出力を英語表示(LANG=C)のまま載せています。作業ディレクトリは ~/work と表記し、コミットIDは実行のたびに変わります。容量と時間の実測は expressjs/express の clone(コミット a419aa73、6,181コミット・214ファイル)、依存の実測は npm 10.9.7・pnpm 12.13.0・uv 0.13.1・Node.js 22.22.2・Python 3.11.15 で行いました。仕様は Git 公式マニュアル(2.56.0版)と Git のソース(branch.c、リリースノート)、VS Code は公式ドキュメント、IntelliJ IDEA は 2026.2 Help で確かめました(確認日 2026年10月16日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「git worktreeの使い方|仕組み・全コマンド早見表・エラー対処と実測」
git worktreeとは|.gitと各worktreeの関係
答えは3行です。
git cloneやgit initで作ったディレクトリが「メインの worktree」、git worktree addで足したディレクトリが「リンクされた worktree」です。- リンクされた worktree には
.gitディレクトリではなく、1行の.gitファイルが置かれ、メインの.git/worktrees/<名前>/を指します。 - コミット・ブランチ・設定・フック・stash は全 worktree で共有、
HEAD・index(ステージ)・作業ファイルは worktree ごとに専用です。
flowchart LR
subgraph main["~/work/app(メインの worktree)"]
direction TB
G[".git/ ディレクトリ<br/>objects・refs/heads・config・hooks・refs/stash<br/>=全 worktree で共有"]
W1[".git/worktrees/app-feature-a/<br/>HEAD・index・logs・gitdir・commondir<br/>=feature-a 専用"]
W2[".git/worktrees/app-hotfix/<br/>HEAD・index・logs・gitdir・commondir<br/>=hotfix 専用"]
end
A["~/work/app-feature-a/.git(1行のファイル)<br/>gitdir: ~/work/app/.git/worktrees/app-feature-a"] -- "gitdir" --> W1
B["~/work/app-hotfix/.git(1行のファイル)<br/>gitdir: ~/work/app/.git/worktrees/app-hotfix"] -- "gitdir" --> W2
W1 -- "commondir: ../.." --> G
W2 -- "commondir: ../.." --> G
実物を見ると、図のとおりになっています。
$ cat ../app-feature-a/.git
gitdir: ~/work/app/.git/worktrees/app-feature-a
$ ls .git/worktrees/app-feature-a
HEAD
ORIG_HEAD
commondir
gitdir
index
logs
$ cat .git/worktrees/app-feature-a/HEAD
ref: refs/heads/feature-a
$ cat .git/worktrees/app-feature-a/commondir
../..
$ cat .git/worktrees/app-feature-a/gitdir
~/work/app-feature-a/.git
$ git -C ../app-feature-a rev-parse --git-dir --git-common-dir
~/work/app/.git/worktrees/app-feature-a
~/work/app/.git
$ git -C ../app-feature-a rev-parse --git-path HEAD --git-path refs/heads/main --git-path index
~/work/app/.git/worktrees/app-feature-a/HEAD
~/work/app/.git/refs/heads/main
~/work/app/.git/worktrees/app-feature-a/index
$ du -sh .git/worktrees/app-feature-a
32K .git/worktrees/app-feature-a
--git-dir(その worktree 専用の場所)と --git-common-dir(共有の場所)が別で、HEAD と index は専用側、refs/heads/main は共有側に置かれています。管理ファイルは1個あたり 32KB でした。
| 種類 | 共有/専用 | 確かめた内容 |
|---|---|---|
コミット・ブランチ・タグ(refs/ 以下) |
共有 | hotfix 側のコミットが、メインの git log hotfix にすぐ出る |
stash(refs/stash) |
共有 | 別の worktree の git stash list にも同じ項目が出る |
設定(.git/config)・リモート |
共有 | git config user.email が全 worktree で同じ |
フック(.git/hooks) |
共有 | メインに置いた pre-commit が worktree のコミットでも動く |
HEAD(今いるブランチ) |
専用 | worktree ごとに別のブランチを開ける |
index(ステージ) |
専用 | git add した内容は混ざらない |
refs/bisect・refs/worktree・refs/rewritten |
専用 | bisect や rebase の途中状態(マニュアル) |
| 作業ファイル | 専用 | もちろん別々 |
未追跡・ignore 済みのファイル(.env・node_modules/) |
コピーされない | 新しい worktree には存在しない(後述) |
共有の様子は、こうして確かめました。
$ cd ../hotfix && echo 'wip' >> README.md && git stash && cd ../app
Saved working directory and index state WIP on hotfix: 620096d hotfix: readme
$ git stash list
stash@{0}: WIP on hotfix: 620096d hotfix: readme
$ git -C ../app-fix-b stash list
stash@{0}: WIP on hotfix: 620096d hotfix: readme
$ printf '#!/bin/sh\necho "[pre-commit] in $(pwd)"\n' > .git/hooks/pre-commit
$ chmod +x .git/hooks/pre-commit
$ cd ../app-fix-b && echo fix >> README.md && git commit -q -am 'fix-b: readme'; cd ../app
[pre-commit] in ~/work/app-fix-b
stash は全 worktree 共通の一覧なので、git stash pop を別の worktree で実行すると、そちらに変更が展開されます。stash の退避と復元は関連記事「git stashの使い方|全コマンド実行結果つきで退避と復元を解説」で扱っています。
worktree ごとに設定を変えたいとき(extensions.worktreeConfig)
.git/config は共有なので、git config --worktree は既定ではエラーになります。拡張を有効にすると、worktree 専用の config.worktree に書けます。
$ git config --worktree core.sparseCheckout true
fatal: --worktree cannot be used with multiple working trees unless the config
extension worktreeConfig is enabled. Please read "CONFIGURATION FILE"
section in "git help worktree" for details
$ git config extensions.worktreeConfig true
$ git -C ../app-fix-b config --worktree user.email fixb@example.com
$ git -C ../app-fix-b config user.email
fixb@example.com
$ git config user.email
dev@example.com
$ git -C ../app-fix-b rev-parse --git-path config.worktree
~/work/app/.git/worktrees/app-fix-b/config.worktree
マニュアルでは、この拡張を有効にしたら core.bare と core.worktree を共有の config からメインの config.worktree に移すこと、拡張を知らない前の版の Git はこのリポジトリを開けなくなることが注意されています。
版ごとの追加時期
| Git の版 | 追加されたもの |
|---|---|
| 2.5(2015年) | git worktree add・prune |
| 2.7 | list。bisect の参照が worktree ごとに |
| 2.10 | lock・unlock。add の -(直前のブランチ) |
| 2.17 | move・remove |
| 2.29 | repair。-d(--detach の短縮) |
| 2.42 | add --orphan |
| 2.43 | 同じブランチのエラー文が is already used by worktree at に |
| 2.48 | 相対パスでのリンク(--relative-paths・worktree.useRelativePaths) |
出典はそれぞれの版のリリースノートです。2.43.0 では --relative-paths を付けると error: unknown option 'relative-paths' になりました。
clone・stash・switchとの違いと使い分け
| 方法 | Git データ | 作業ファイル | 向いている場面 | 向かない場面 |
|---|---|---|---|---|
git switch |
同じ | 1つ(切り替え) | 1つずつ順に進める | ビルドやサーバーを動かしたまま別の作業 |
git stash |
同じ | 1つ(退避して切り替え) | 数分の割り込み | 退避したまま日をまたぐ |
git worktree |
共有 | ブランチごとに別 | 並行作業・レビュー・AI エージェント | 設定やフックまで分けたい |
git clone |
別々 | 別々 | 設定・フック・リモートを分けて試す | 容量と取得時間を節約したい |
flowchart TD
Q1{"元の作業に数分で戻る?"} -- "はい" --> S["git stash<br/>(退避して切り替え)"]
Q1 -- "いいえ" --> Q2{"2つの作業を同時に動かす?<br/>(ビルド・サーバー・テスト・AI)"}
Q2 -- "いいえ" --> B["git switch<br/>(コミットしてから切り替え)"]
Q2 -- "はい" --> Q3{"設定・フック・リモートまで<br/>分けたい?"}
Q3 -- "いいえ" --> W["git worktree add<br/>(履歴を共有、作業ツリーだけ増やす)"]
Q3 -- "はい" --> C["git clone<br/>(--reference で objects だけ共有も可)"]
容量と時間の差を実測しました。 expressjs/express(.git 12MB、作業ツリー 1.6MB)で、worktree の追加と clone を比べた結果です。
| 方法 | 時間 | 増える容量 |
|---|---|---|
git worktree add --detach ../express-wt11 HEAD |
0.017秒 | 作業ツリー 1.6MB + 管理ファイル 52KB |
git clone file:///…/express ../express-clone(objects をコピー) |
1.06秒 | .git 11MB + 作業ツリー 1.6MB |
git clone --reference …/express file:///…/express ../express-ref |
0.044秒 | .git 216KB + 作業ツリー 1.6MB |
git clone /…/express ../express-local(ローカルパス、ハードリンク) |
0.027秒 | .git は見かけ 12MB だが実体は共有 |
--reference は .git/objects/info/alternates に元のリポジトリの objects を書き、オブジェクトを借りる方式です。
$ cat ../express-ref/.git/objects/info/alternates
~/work/big/express/.git/objects
マニュアルには、元のリポジトリでブランチを消すなどして参照されなくなったオブジェクトが git gc で消えると、借りている側が壊れるという注意があります。worktree は objects を「借りる」のではなく同じ .git を使うので、この問題は起きません。切り替えのコマンドそのものは関連記事「git switchとcheckoutの違い|切り替え・作成・restore」で扱っています。clone のオプションは関連記事「git cloneの使い方|ブランチ指定とshallow clone」で扱っています。
git worktree addの使い方|オプション別の実行結果
| 書き方 | 作られるもの | 使う場面 |
|---|---|---|
add -b X <パス> |
新しいブランチ X(起点は HEAD) | いちばん多い形 |
add -b X <パス> <起点> |
起点を指定した新しいブランチ X | main や origin/main から切る |
add <パス> X |
既存のブランチ X をチェックアウト | 他の人のブランチを開く |
add <パス> X(X が origin/X にだけある) |
--track -b X … origin/X と同じ |
fetch 済みのリモートブランチを開く |
add --track -b X <パス> origin/X |
追跡設定つきの新しいブランチ X | 上と同じことを明示する |
add --detach <パス> <コミット> |
ブランチなし(detached HEAD) | レビュー・動作確認・ビルド |
add <パス> |
パスの末尾名のブランチ(無ければ作る) | 名前を2回書きたくない |
add -B X <パス> <起点> |
既存のブランチ X を起点にリセット | 作り直し(元の先端は失われる) |
add --orphan -b X <パス> |
履歴のない新しいブランチ(空の worktree) | GitHub Pages 用の gh-pages など |
add --lock --reason '…' <パス> |
作成と同時にロック | 外付け・ネットワーク上に作る |
add --no-checkout <パス> |
ファイルを展開しない | sparse-checkout を先に設定する |
add <パス> - |
直前にいたブランチ(@{-1}) |
git switch - と同じ感覚 |
-b:新しいブランチを作って追加
$ git worktree add -b feature-a ../app-feature-a
Preparing worktree (new branch 'feature-a')
HEAD is now at a5d90eb add readme
起点を変えるときは最後に書きます(例:git worktree add -b feature-a ../app-feature-a origin/main)。既存の名前を -b に渡すと止まります。
$ git worktree add -b fix-b ../app-dup
Preparing worktree (new branch 'fix-b')
fatal: a branch named 'fix-b' already exists
既存のブランチで追加
$ git worktree add ../app-fix-b fix-b
Preparing worktree (checking out 'fix-b')
HEAD is now at a5d90eb add readme
--detach:ブランチなしで過去のコミットを開く
$ git worktree add --detach ../app-review HEAD~1
Preparing worktree (detached HEAD 02a7195)
HEAD is now at 02a7195 init
ブランチを作らないので、同じブランチの衝突(後述)が起きず、後片付けも remove だけです。
--track:リモートブランチを追跡して追加
$ git fetch -q origin
$ git worktree add --track -b feature-c ../app-feature-c origin/feature-c
Preparing worktree (new branch 'feature-c')
branch 'feature-c' set up to track 'origin/feature-c'.
HEAD is now at 734c533 feature c
ブランチ名だけ書いても、手元に無く origin/ にだけあれば同じ結果になります。 ただし fetch 前は invalid reference になります。
$ git worktree add ../app-feature-d feature-d
fatal: invalid reference: feature-d
$ git fetch -q origin
$ git worktree add ../app-feature-d feature-d
Preparing worktree (new branch 'feature-d')
branch 'feature-d' set up to track 'origin/feature-d'.
HEAD is now at a5d90eb add readme
パスだけ指定して追加
$ git worktree add ../hotfix
Preparing worktree (new branch 'hotfix')
HEAD is now at a5d90eb add readme
末尾の名前(hotfix)のブランチが作られます。同じ名前のブランチがすでにあればそれをチェックアウトし、--guess-remote を付けると origin/hotfix から作って追跡します(設定 worktree.guessRemote で既定にできます)。
-B:既存のブランチを起点にリセットして追加
$ git branch old-branch HEAD~1
$ git worktree add -B old-branch ../app-old main
Preparing worktree (resetting branch 'old-branch'; was at 02a7195)
HEAD is now at a5d90eb add readme
was at 02a7195 のとおり、ブランチの先端が main に書き換わります。元の先端に戻すには git reflog から ID を探します。
--orphan・--lock・--no-checkout
$ git worktree add --orphan -b docs-site ../app-docs
Preparing worktree (new branch 'docs-site')
$ git -C ../app-docs status
On branch docs-site
No commits yet
nothing to commit (create/copy files and use "git add" to track)
$ git worktree add -q --lock --reason 'network drive' --detach ../app-net HEAD
$ git worktree list -v
~/work/app a5d90eb [main]
~/work/app-feature-c 734c533 [feature-c]
~/work/app-net a5d90eb (detached HEAD)
locked: network drive
~/work/hotfix 620096d [hotfix]
~/work/wt/fix-b 8b4eab9 [fix-b]
$ git worktree add --no-checkout --detach ../app-sparse HEAD
Preparing worktree (detached HEAD 5a430d5)
$ ls -A ../app-sparse
.git
--no-checkout の後は git sparse-checkout set <ディレクトリ> と git checkout で、必要な部分だけ展開できます。
同じブランチを2つのworktreeで開けないエラーと回避
1つのブランチは、同時に1つの worktree でしか開けません。 Git 2.43 以降は is already used by worktree at、2.42 以前は is already checked out at という文言です(Git のソース branch.c の v2.42.0 と v2.43.0、2.43.0 のリリースノートで確認)。
$ git worktree add ../app-feature-a2 feature-a
Preparing worktree (checking out 'feature-a')
fatal: 'feature-a' is already used by worktree at '~/work/app-feature-a'
$ git switch feature-a
fatal: 'feature-a' is already used by worktree at '~/work/app-feature-a'
$ git checkout feature-a
fatal: 'feature-a' is already used by worktree at '~/work/app-feature-a'
メインで git switch しても同じエラーです。どのブランチがどこで開かれているかは git branch -vv の先頭の + と括弧内のパスで分かります。
$ git branch -vv
+ feature-a a5d90eb (~/work/app-feature-a) add readme
+ feature-c 734c533 (~/work/app-feature-c) [origin/feature-c] feature c
+ fix-b a5d90eb (~/work/app-fix-b) add readme
+ hotfix a5d90eb (~/work/hotfix) add readme
* main a5d90eb [origin/main] add readme
| 状況 | 回避 |
|---|---|
| そのブランチで作業したい | cd でその worktree に移る(git branch -vv のパス) |
| 同じコミットを見たい・ビルドしたい | git worktree add --detach ../app-review feature-a |
| 同じ地点から別の変更を始めたい | git worktree add -b feature-a-alt ../app-feature-a-alt feature-a |
| もうその worktree は要らない | git worktree remove ../app-feature-a してから開く |
--force で開くと、片方のコミットがもう片方で打ち消される
--force(git switch --ignore-other-worktrees も同じ)を付けると開けますが、2つの worktree が同じブランチを動かすため、次のことが起きました。
$ git worktree add --force ../app-feature-a2 feature-a
Preparing worktree (checking out 'feature-a')
HEAD is now at a5d90eb add readme
# app-feature-a2 で README.md を変えてコミット
$ cd ../app-feature-a2 && echo 'from a2' >> README.md && git commit -q -am 'a2: edit readme' && cd ../app
# app-feature-a 側は何もしていないのに、ステージに変更が現れる
$ git -C ../app-feature-a status -sb
## feature-a
M README.md
# app-feature-a で別のファイルを -a でコミットすると…
$ cd ../app-feature-a && echo 'from a' >> src/app.js && git commit -q -am 'a: edit app.js'; cd ../app
$ git show --stat --oneline 42b7df1
42b7df1 a: edit app.js
README.md | 1 -
src/app.js | 1 +
2 files changed, 1 insertion(+), 1 deletion(-)
app-feature-a の index は a2 のコミット前の内容のままなので、ブランチが進んだ分が「ステージされた取り消し」として見え、次のコミットで a2 の変更(README.md | 1 -)が打ち消されました。打ち消したコミットは git reflog から戻せますが、同じブランチを2か所で開く必要がある場面は、--detach か別ブランチで置き換えられます。
git worktree listの見方
$ git worktree list
~/work/app 5a430d5 [main]
~/work/app-docs 0000000 [docs-site]
~/work/app-feature-a 5a430d5 [feature-a]
~/work/app-feature-c 734c533 [feature-c]
~/work/app-feature-login 1c861cd [feature-login]
~/work/app-net 5a430d5 (detached HEAD) locked
~/work/app-review 938b13f (detached HEAD)
~/work/hotfix 5a430d5 [hotfix] prunable
| 表示 | 意味 |
|---|---|
| 1行目 | メインの worktree(bare なら (bare)) |
[ブランチ名] |
そのブランチをチェックアウト中 |
0000000 [docs-site] |
--orphan で作った、まだコミットのないブランチ |
(detached HEAD) |
ブランチなし |
locked |
ロック中(-v で理由も表示) |
prunable |
ディレクトリが見つからない(-v で理由も表示) |
-v は理由を次の行に出し、--porcelain はスクリプト向けの固定書式です。パスに改行などが入る可能性があるときは -z を足します。
$ git worktree list -v
~/work/app 5a430d5 [main]
~/work/app-docs 0000000 [docs-site]
~/work/app-feature-a 5a430d5 [feature-a]
~/work/app-feature-c 734c533 [feature-c]
~/work/app-feature-login 1c861cd [feature-login]
~/work/app-net 5a430d5 (detached HEAD)
locked: USB drive
~/work/app-review 938b13f (detached HEAD)
~/work/hotfix 5a430d5 [hotfix]
prunable: gitdir file points to non-existent location
$ git worktree list --porcelain
worktree ~/work/app
HEAD 5a430d5b0ce7f63c969cae34572421934b462ac6
branch refs/heads/main
worktree ~/work/app-docs
HEAD 0000000000000000000000000000000000000000
branch refs/heads/docs-site
worktree ~/work/app-net
HEAD 5a430d5b0ce7f63c969cae34572421934b462ac6
detached
locked USB drive
worktree ~/work/app-review
HEAD 938b13f206654e9406cfeb569503fc37ca153028
detached
worktree ~/work/hotfix
HEAD 5a430d5b0ce7f63c969cae34572421934b462ac6
branch refs/heads/hotfix
prunable gitdir file points to non-existent location
$ git worktree list --porcelain | awk '/^worktree /{print $2}'
~/work/app
~/work/app-docs
~/work/app-feature-a
~/work/app-feature-c
~/work/app-feature-login
~/work/app-net
~/work/app-review
~/work/hotfix
--porcelain では、worktree 行が区切り、branch か detached のどちらかが必ず出て、locked と prunable は該当するときだけ出ます(理由があれば続けて表示。上の出力は一部を省略しています)。
worktreeの削除(git worktree remove)とブランチの後始末
$ git worktree remove ../app-feature-a
$ git worktree list
~/work/app a5d90eb [main]
~/work/app-feature-c 734c533 [feature-c]
~/work/app-fix-b 8b4eab9 [fix-b]
~/work/app-review 02a7195 (detached HEAD)
~/work/hotfix 620096d [hotfix]
$ git branch --list feature-a
feature-a
worktree を消してもブランチは残ります。 不要なら git branch -d feature-a で消します。逆に、worktree で開いているブランチは消せません。
$ git branch -d fix-b
error: cannot delete branch 'fix-b' used by worktree at '~/work/app-fix-b'
$ git branch -D fix-b
error: cannot delete branch 'fix-b' used by worktree at '~/work/app-fix-b'
$ git branch -f fix-b HEAD~1
fatal: cannot force update the branch 'fix-b' used by worktree at '~/work/app-fix-b'
-D でも消えません。先に remove します。名前の変更(git branch -m)はでき、その worktree の HEAD も新しい名前に追従しました。ブランチの削除は関連記事「gitブランチの削除と名前変更|-d・-D・-mとリモートブランチ削除」で扱っています。
変更が残っていると止まる(--force)
$ git worktree remove ../app-feature-a
fatal: '../app-feature-a' contains modified or untracked files, use --force to delete it
$ git -C ../app-feature-a status --short
M README.md
?? notes.txt
$ git worktree remove --force ../app-feature-a
追跡ファイルの変更と未追跡ファイルがあると止まります。一方、.gitignore に入っているファイルは「変更」と見なされず、確認なしでディレクトリごと消えます。
$ git -C ../app-tmp status --short --ignored
!! .env
!! node_modules/
$ git worktree remove ../app-tmp
$ ls ../app-tmp
ls: cannot access '../app-tmp': No such file or directory
worktree 専用に書き換えた .env や、ignore 済みのメモ・ビルド成果物は、remove の前に退避します。
メインは消せない・中から消すとシェルが迷子になる
$ git worktree remove .
fatal: '.' is a main working tree
$ cd ../app-inside && git worktree remove . ; pwd; git status
~/work/app-inside
fatal: Unable to read current working directory: No such file or directory
リンクされた worktree の中から remove . は通りますが、シェルの現在地が消えたディレクトリのままになります。メインか別の worktree に移ってから実行すると、この状態になりません。パスは末尾の一意な名前だけでも指定できます(git worktree remove app-feature-a)。
削除し忘れ・ディレクトリを手で消したとき(git worktree prune)
rm -rf で消すと、.git/worktrees/<名前>/ の登録だけが残り、一覧に prunable と出ます。同じパスに作り直そうとすると止まります。
$ rm -rf ../hotfix
$ git worktree list -v
~/work/app a5d90eb [main]
~/work/app-feature-c 734c533 [feature-c]
~/work/app-fix-b 8b4eab9 [fix-b]
~/work/app-review 02a7195 (detached HEAD)
~/work/hotfix 620096d [hotfix]
prunable: gitdir file points to non-existent location
$ git worktree add ../hotfix hotfix
Preparing worktree (checking out 'hotfix')
fatal: '../hotfix' is a missing but already registered worktree;
use 'add -f' to override, or 'prune' or 'remove' to clear
$ git worktree prune -n -v
Removing worktrees/hotfix: gitdir file points to non-existent location
$ git worktree prune -v
Removing worktrees/hotfix: gitdir file points to non-existent location
$ git worktree add ../hotfix hotfix
Preparing worktree (checking out 'hotfix')
HEAD is now at 620096d hotfix: readme
-n は消さずに対象だけ表示します。git gc は git worktree prune --expire 3.months.ago を自動で呼ぶので(設定 gc.worktreePruneExpire、now で即時、never で無効)、放っておいても3か月後には消えますが、作り直したいときは手動で prune します。--expire 1.day.ago のように期間を付けると、それより新しい登録は残ります。
ロック中の登録は prune の対象外です。ロックしたまま rm -rf した場合の手順は、次の節の missing but locked の項にまとめました。
move・lock・unlock・repair|場所の変更・持ち運び・修復
move:登録ごと場所を移す
$ mkdir -p ../wt
$ git worktree move ../app-fix-b ../wt/fix-b
$ git worktree list
~/work/app a5d90eb [main]
~/work/app-feature-c 734c533 [feature-c]
~/work/hotfix 620096d [hotfix]
~/work/wt/fix-b 8b4eab9 [fix-b]
$ cat ../wt/fix-b/.git
gitdir: ~/work/app/.git/worktrees/app-fix-b
$ cat .git/worktrees/app-fix-b/gitdir
~/work/wt/fix-b/.git
.git/worktrees/ 側の名前(app-fix-b)はそのままで、gitdir の中身だけが新しいパスになります。移動先が既存のディレクトリなら、その中に入ります(git worktree move ../wt/fix-b ../dest → ../dest/fix-b)。ファイルがあるパスを指定すると already exists、メインは is a main working tree で止まります。マニュアルでは、submodule を含む worktree は move できないとされています。
lock・unlock:remove・move・prune から守る
USB ドライブやネットワーク上に置いた worktree は、つながっていない間に prunable と見なされます。lock しておくと、prune に加えて remove・move も拒否されます。
$ git worktree lock --reason 'USB drive' ../app-review
$ git worktree lock ../app-review
fatal: '../app-review' is already locked, reason: USB drive
$ cat .git/worktrees/app-review/locked
USB drive
$ git worktree remove ../app-review
fatal: cannot remove a locked working tree, lock reason: USB drive
use 'remove -f -f' to override or unlock first
$ git worktree move ../app-review ../app-review2
fatal: cannot move a locked working tree;
use 'move -f -f' to override or unlock first
$ git worktree unlock ../app-review
$ git worktree remove ../app-review
ロックの実体は .git/worktrees/<名前>/locked という理由を書いたファイルです。-f -f(--force を2回)で押し切れますが、unlock してから remove する順が分かりやすい手順です。
missing but locked:ロックしたまま消した場合
$ rm -rf ../app-net
$ git worktree prune -v
(何も消えない)
$ git worktree add --detach ../app-net HEAD
Preparing worktree (detached HEAD a5d90eb)
fatal: '../app-net' is a missing but locked worktree;
use 'add -f -f' to override, or 'unlock' and 'prune' or 'remove' to clear
$ git worktree unlock ../app-net
$ git worktree prune -v
Removing worktrees/app-net: gitdir file points to non-existent location
エラー文のとおり、unlock → prune の順で片付きます。
repair:mv で動かした後の修復
move を使わず mv で動かすと、メイン側の gitdir が前のパスのままになり、prunable と表示されます。動かした worktree の中では git status が動くので気づきにくい状態です。
$ mv ../wt/fix-b ../wt/fix-b-moved
$ git worktree list
~/work/app a5d90eb [main]
~/work/app-feature-c 734c533 [feature-c]
~/work/hotfix 620096d [hotfix]
~/work/wt/fix-b 8b4eab9 [fix-b] prunable
$ git -C ../wt/fix-b-moved status -sb
## fix-b
$ git worktree repair ../wt/fix-b-moved
repair: gitdir incorrect: ~/work/app/.git/worktrees/app-fix-b/gitdir
$ git worktree list
~/work/app a5d90eb [main]
~/work/app-feature-c 734c533 [feature-c]
~/work/hotfix 620096d [hotfix]
~/work/wt/fix-b-moved 8b4eab9 [fix-b]
動かした worktree の中で引数なしの git worktree repair を実行しても同じ結果でした。メインのリポジトリを動かした場合は、各 worktree の .git ファイルが壊れ、worktree 側で Git が使えなくなります。メインで repair すると全部まとめて直ります。
$ cd ~/work && mv app app-main
$ git -C app-feature-c status -sb
fatal: not a git repository: ~/work/app/.git/worktrees/app-feature-c
$ cd app-main && git worktree repair
repair: .git file broken: ~/work/app-feature-login
repair: .git file broken: ~/work/app-feature-c
repair: .git file broken: ~/work/hotfix
repair: .git file broken: ~/work/wt/fix-b
$ git -C ../app-feature-c status -sb
## feature-c...origin/feature-c
$ cat ../app-feature-c/.git
gitdir: ~/work/app-main/.git/worktrees/app-feature-c
やってはいけない順番:mv の後に prune
prunable を見て先に prune すると、登録(.git/worktrees/app-fix-b/)が消え、動かした worktree の .git ファイルは行き先のない状態になります。repair でも戻りません。
$ mv ../wt/fix-b ../wt/fix-b-moved
$ git worktree prune -v
Removing worktrees/app-fix-b: gitdir file points to non-existent location
$ git -C ../wt/fix-b-moved status -sb
fatal: not a git repository: ~/work/app/.git/worktrees/app-fix-b
$ git worktree repair ../wt/fix-b-moved
error: unable to locate repository; .git file does not reference a repository: ~/work/wt/fix-b-moved/.git
やり直し方:ブランチとコミットは .git に残っているので、worktree を作り直し、コミットしていない変更だけ写します。
$ git branch --list fix-b
fix-b
$ mv ../wt/fix-b-moved ../wt/fix-b-moved.bak
$ git worktree add ../wt/fix-b fix-b
Preparing worktree (checking out 'fix-b')
HEAD is now at 8b4eab9 fix-b: readme
$ rsync -a --exclude .git ../wt/fix-b-moved.bak/ ../wt/fix-b/
$ git -C ../wt/fix-b status -sb
## fix-b
M README.md
コミット前の変更(M README.md)も、ファイルごと写したので戻りました。動かしたときの順番は「prunable → repair」、消したときの順番は「prunable → prune」です。
stateDiagram-v2
[*] --> 使用中: git worktree add
使用中 --> [*]: git worktree remove
使用中 --> prunable: rm -rf で手で消した
使用中 --> prunable: mv で手で動かした
prunable --> [*]: git worktree prune(登録を削除)
prunable --> 使用中: git worktree repair <新しいパス>(動かしただけなら)
使用中 --> locked: git worktree lock
locked --> 使用中: git worktree unlock
locked --> locked: remove・move・prune は拒否
使用中 --> 使用中: git worktree move(登録も更新)
ディレクトリの置き場所の設計
| 配置 | 例 | 利点 | 注意 |
|---|---|---|---|
| 兄弟に並べ、接頭辞を付ける | ~/work/app-feature-a |
一覧しやすく、IDE で別ウィンドウとして開きやすい | 親ディレクトリに増えるので命名をそろえる |
| 専用フォルダにまとめる | ~/work/wt/feature-a |
まとめて片付けやすい | ブランチ名にスラッシュがあるとサブフォルダになる |
リポジトリの中(.worktrees/) |
~/work/app/.worktrees/feature-a |
リポジトリと一緒に移動できる | .gitignore に追加。grep やビルドの対象に混ざる |
| bare リポジトリ+worktree | ~/work/app.git/main、~/work/app.git/feature-a |
メインが無く、全ブランチが対等 | remote.origin.fetch を自分で設定する |
リポジトリの中に作ると未追跡になる
$ git worktree add -q -b feature-search .worktrees/feature-search
$ git status --short
?? .worktrees/
$ echo '.worktrees/' >> .gitignore
$ git status --short
M .gitignore
$ grep -rl 'Hello' --include='*.js' . | head
./src/app.js
./.worktrees/feature-search/src/app.js
.gitignore に入れれば git status には出なくなりますが、grep -r やテストランナーの探索には入り込みます。入れずに git add . すると、warning: adding embedded git repository: .worktrees/feature-search と出て、worktree がサブモジュールのような gitlink として add されました(git rm --cached .worktrees/feature-search で外せます)。IntelliJ IDEA のドキュメントも、プロジェクトのディレクトリの中に worktree を作らないよう注意しています(マルチルートのプロジェクトと誤認するため)。Claude Code が claude --worktree で作る .claude/worktrees/ もこの配置なので、.gitignore への追記が公式のヒントに載っています。
兄弟に並べる
$ git worktree add -q -b feature-search ../app-feature-search
$ ls -d ../app*
../app
../app-feature-login
../app-feature-search
../<リポジトリ名>-<ブランチ名> の形にそろえると、ls だけで何が開いているか分かります。
bare リポジトリ+worktree の配置と、fetch の落とし穴
「メイン」を持たず、すべてのブランチを worktree として並べる配置です。
$ git clone -q --bare <remote> app.git
$ cd app.git
$ git worktree add -q main main
$ git worktree add -q feature-c feature-c
$ git worktree list
~/work/app.git (bare)
~/work/app.git/feature-c 734c533 [feature-c]
~/work/app.git/main 5a430d5 [main]
$ git config --get remote.origin.fetch; echo exit=$?
exit=1
$ cd main && git status -sb
## main
git clone --bare は remote.origin.fetch を設定しないため、origin/main などの追跡ブランチが作られず、git status に ...origin/main が出ません。 次の2行で通常の clone と同じ状態になりました。
$ git config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*'
$ git fetch -q origin && git branch -u origin/main main
branch 'main' set up to track 'origin/main'.
$ cd main && git status -sb
## main...origin/main
node_modules・venv・ビルド成果物の扱い
新しい worktree には、追跡していないファイルが一切ありません。 .env も node_modules/ も .venv/ も dist/ も、worktree ごとに用意します。
$ du -sh node_modules
33M node_modules
$ git worktree add -q -b feature-api ../app-feature-api
$ ls -A ../app-feature-api
.git
.gitignore
README.md
package-lock.json
package.json
pyproject.toml
src
uv.lock
$ cd ../app-feature-api && node -e 'require("express")'
Error: Cannot find module 'express'
| 対象 | 新しい worktree では | 対応 |
|---|---|---|
node_modules/・.venv/ |
無い | worktree ごとに入れる。pnpm・uv ならキャッシュからハードリンク |
.env・ローカル設定 |
無い | メインからコピーし、ポートや DB 名を worktree 用に変える |
ビルド成果物(dist/・target/) |
無い | worktree ごとにビルドする(別々で正しい) |
ビルドキャッシュ(~/.gradle・~/.m2・~/.npm) |
ホーム配下なので共有 | 同時に書き換える処理を避ける |
| 開発サーバーのポート・ローカル DB・コンテナ名 | マシンで1つ | worktree ごとに番号・名前を分ける |
npm ci は worktree の数だけ容量が増える
同じ package-lock.json(express・lodash・typescript、70パッケージ)を2つの worktree で npm ci した結果です。
$ cd ../app-feature-api && time npm ci --no-audit --no-fund >/dev/null
real 0m1.511s
$ cd ../app-feature-ui && time npm ci --no-audit --no-fund >/dev/null
real 0m1.099s
$ du -sh ../app-feature-api/node_modules ../app-feature-ui/node_modules
33M ../app-feature-api/node_modules
33M ../app-feature-ui/node_modules
$ du -shc ../app-feature-api/node_modules ../app-feature-ui/node_modules | tail -1
65M total
$ find ../app-feature-api/node_modules -type f | wc -l
1788
pnpm・uv はキャッシュからハードリンクするので2つ目が軽い
pnpm はストア(既定は ~/.local/share/pnpm/store)から node_modules へ、Linux ではハードリンクで取り込みます(設定 packageImportMethod の既定 auto は、Linux ではハードリンクを先に試します)。uv もキャッシュ(Linux の既定は ~/.cache/uv)から環境へリンクします。どちらもキャッシュと worktree が同じファイルシステムにあることが条件です(uv のドキュメントも、別のファイルシステムだとコピーに落ちると説明しています)。この記事では、ストアとキャッシュを worktree と同じファイルシステムに置き、取り込み方法をハードリンクに指定して実行しました。
$ cd ../app-feature-api && time pnpm install --store-dir ~/pnpm-store --config.package-import-method=hardlink 2>&1 | tail -1
Done in 1.6s using pnpm v12.13.0
$ cd ../app-feature-ui && time pnpm install --store-dir ~/pnpm-store --config.package-import-method=hardlink 2>&1 | tail -1
Done in 173ms using pnpm v12.13.0
$ du -sh ../app-feature-api/node_modules ../app-feature-ui/node_modules
33M ../app-feature-api/node_modules
1.3M ../app-feature-ui/node_modules
$ du -shc ../app-feature-api/node_modules ../app-feature-ui/node_modules ~/pnpm-store | tail -1
36M total
$ stat -c '%h %n' ../app-feature-api/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/lodash.js
3 ../app-feature-api/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/lodash.js
du はハードリンクされたファイルを1回しか数えないので、2つ目の node_modules は 1.3MB(リンクでない部分だけ)で、2つとストアの合計も 36MB に収まりました。stat のリンク数 3 は、ストアと2つの worktree が同じ実体を指していることを示します。2つ目のインストールは 0.17秒でした。
uv(requests・rich)でも同じ結果です。
$ cd ../app-feature-api && time uv sync -q
real 0m0.719s
$ cd ../app-feature-ui && time uv sync -q
real 0m0.042s
$ du -sh ../app-feature-api/.venv ../app-feature-ui/.venv
9.5M ../app-feature-api/.venv
392K ../app-feature-ui/.venv
$ stat -c '%h %n' ../app-feature-api/.venv/lib/python3.11/site-packages/requests/api.py
3 ../app-feature-api/.venv/lib/python3.11/site-packages/requests/api.py
| 方法 | 1つ目 | 2つ目 | 2つの合計容量 |
|---|---|---|---|
npm ci(70パッケージ) |
1.51秒・33MB | 1.10秒・33MB | 65MB |
pnpm install(同じ lock 内容) |
1.6秒・33MB | 0.17秒・1.3MB | 36MB(ストア込み) |
uv sync(requests・rich) |
0.72秒・9.5MB | 0.04秒・392KB | 11MB(キャッシュ込み) |
node_modules をシンボリックリンクで共有する方法(ln -s ../app/node_modules node_modules)は、2つのブランチの依存が同じときだけ成り立ちます。片方のブランチでパッケージを追加・更新すると、もう片方も同じ node_modules を見てしまいます。VS Code の設定 git.worktreeSymlinkFolders(実験的)はこの方式を自動化するもので、同じ注意が当てはまります。
.env のコピーを自動にする
| 環境 | 仕組み |
|---|---|
| 手動・スクリプト | cp .env ../app-feature-a/.env の後、PORT などを書き換える |
| VS Code | 設定 git.worktreeIncludeFiles(実験的)に .gitignore 済みファイルのパターンを書く |
| Claude Code | リポジトリ直下の .worktreeinclude にパターンを書く |
作成からコピー・ポートの割り当てまでをまとめるスクリプトと、ポート番号・DB 名を worktree 名から決める方法は、関連記事「Claude Code×git worktree並列開発|設定例と衝突対策表」で扱っています。
実例:hotfixとfeatureを同時に進める
feature-login の作業が途中(コミット済み1件+編集中のファイル)のところへ、急ぎの修正が入った場面です。
$ git worktree add -b feature-login ../app-feature-login main
Preparing worktree (new branch 'feature-login')
HEAD is now at a5d90eb add readme
# …src/login.js を追加してコミットし、さらに編集中…
$ git -C ../app-feature-login status -sb
## feature-login
M src/login.js
feature 側はそのままにして、main から hotfix 用の worktree を開きます。
$ git worktree add -b hotfix-greet ../app-hotfix main
Preparing worktree (new branch 'hotfix-greet')
HEAD is now at a5d90eb add readme
$ git worktree list
~/work/app a5d90eb [main]
~/work/app-feature-login 7ca72fc [feature-login]
~/work/app-hotfix a5d90eb [hotfix-greet]
$ cd ../app-hotfix && sed -i 's/Hello, /Hello /' src/app.js && git commit -q -am 'fix: remove comma in greeting' && git push -q -u origin hotfix-greet; cd ../app
$ git -C ../app-hotfix status -sb
## hotfix-greet...origin/hotfix-greet
$ git -C ../app-hotfix log --oneline -2
834452c fix: remove comma in greeting
a5d90eb add readme
両方の状態を、メインから git -C <パス> で同時に確かめられます。
$ git -C ../app-feature-login status -sb
## feature-login
M src/login.js
$ git -C ../app-feature-login log --oneline -2
7ca72fc feat: login skeleton
a5d90eb add readme
$ git branch -vv
+ feature-login 7ca72fc (~/work/app-feature-login) feat: login skeleton
+ hotfix-greet 834452c (~/work/app-hotfix) [origin/hotfix-greet] fix: remove comma in greeting
* main a5d90eb [origin/main] add readme
$ git log --oneline --graph --all
* 7ca72fc feat: login skeleton
| * 834452c fix: remove comma in greeting
|/
* a5d90eb add readme
* 02a7195 init
hotfix をメインでマージして push し、hotfix の worktree とブランチを片付けます。worktree が開いたままでも、メインで git merge hotfix-greet はできます(動かすのは main だからです)。
$ git merge -q --no-ff -m 'Merge hotfix-greet' hotfix-greet
$ git push -q origin main
$ git worktree remove ../app-hotfix
$ git branch -d hotfix-greet
Deleted branch hotfix-greet (was 834452c).
feature 側は編集中のまま、--autostash で main に追いつきます。
$ cd ../app-feature-login && git rebase --autostash main
Created autostash: d3e7527
Successfully rebased and updated refs/heads/feature-login.
Applied autostash.
$ git status -sb
## feature-login
M src/login.js
$ git log --oneline -4
1c861cd feat: login skeleton
e6b90b3 Merge hotfix-greet
a5d90eb add readme
834452c fix: remove comma in greeting
編集中だった src/login.js の変更は残ったまま、hotfix を含む main の上に乗りました。feature の作業を一度も中断せず、stash も git switch も使っていません。
worktreeの数と容量の実測(10個作ったとき)
expressjs/express(6,181コミット・214ファイル、.git 12MB・作業ツリー 1.6MB)で、worktree を10個作りました。
$ time (for i in $(seq 1 10); do git worktree add -q --detach ../express-wt$i HEAD; done)
real 0m0.184s
$ du -sh .git
12M .git
$ du -sh .git/worktrees
524K .git/worktrees
$ du -sh --exclude=.git ../express-wt1
1.6M ../express-wt1
$ du -shc --exclude=.git ../express-wt* | tail -1
16M total
| 項目 | 値 |
|---|---|
| 10個作る時間 | 0.184秒(1個あたり約 0.02秒) |
.git(履歴)の増加 |
0(12MB のまま) |
.git/worktrees/(10個分の管理ファイル) |
524KB(1個あたり 52KB) |
| 作業ツリー10個の合計 | 16MB(1個 1.6MB × 10) |
| 同じ数を clone で作った場合 | 1個あたり .git 11MB + 1.6MB、1.06秒 |
増えるのは「展開したファイル」の分だけで、履歴は増えません。 作業ツリーが大きいリポジトリでは worktree の数に比例して容量を使うので、終わったものから remove し、git worktree list で数を見ます。
IDEでの開き方(VS Code・IntelliJ IDEA)
どちらの IDE も、worktree のディレクトリを普通に「フォルダーを開く」で開けます(.git ファイルから元のリポジトリをたどります)。専用の操作は次のとおりです。
VS Code
| 操作 | 場所 |
|---|---|
| worktree を作る | Source Control Repositories ビューでリポジトリを選び、More Actions(...)→ Worktrees → Create Worktree。ブランチと場所を選ぶ |
| 開く | worktree を右クリック → Open Worktree in New Window/Open Worktree in Current Window |
| 削除 | メインのリポジトリのフォルダーを開いた状態でコマンドパレット「Git: Delete Worktree...」。変更が残っていれば Force Delete の確認 |
| 変更をメインへ移す | 「Git: Migrate Worktree Changes...」(未追跡ファイルを含むコミット前の変更をメインの worktree へ) |
| ファイルをメインと比べる | worktree を選んで変更ファイルを右クリック → Compare with Workspace |
| 設定 | 内容 |
|---|---|
git.detectWorktrees |
既存の worktree を検出して Source Control Repositories ビューに出す |
git.detectWorktreesLimit |
表示する worktree の上限(既定 50) |
git.worktreeIncludeFiles(実験的) |
.gitignore 済みファイルのうち、新しい worktree にコピーするパターン |
git.worktreeSymlinkFolders(実験的) |
追跡ファイルを含まない ignore 済みフォルダーをシンボリックリンクで共有 |
Source Control Repositories ビューの Worktrees ノードは、VS Code 1.108(2025年12月)で実験的に加わりました。削除してもブランチは残る点はコマンドラインと同じです。出典:VS Code Docs「Git Branches and Worktrees in VS Code」。
IntelliJ IDEA
| 操作 | 場所 |
|---|---|
| worktree を作る | メニュー Git → New Worktree(または Git ツールウィンドウの Worktrees タブの New Worktree)。From branch・Project name・Location を指定。チェックアウト中のブランチを元にするときは New Branch を選ぶ |
| 開く | Worktrees タブでダブルクリック、またはメニュー Git → Worktrees。ブランチを右クリック → Open Worktree |
| 削除 | Worktrees タブで選んで Delete(メインと、今開いている worktree は消せない) |
| prune | Worktrees タブの Prune で、Prunable の worktree をまとめて掃除 |
注意点として、ドキュメントは「単一の Git リポジトリのプロジェクトだけ対応」「同じブランチを2つの worktree で開けない」「プロジェクトのディレクトリの中に worktree を作らない」「.idea/workspace.xml をコミットしていると ProjectId が重複して同じプロジェクト扱いになる(.gitignore に入れる)」を挙げています。出典:IntelliJ IDEA 2026.2 Help「Use Git worktrees」。
git worktree switchはない|worktree間の移動とブランチ切り替え
git worktree に switch サブコマンドはありません。
$ git worktree switch
error: unknown subcommand: `switch'
usage: git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]
[--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]
| やりたいこと | 方法 |
|---|---|
| 別の worktree へ移る | cd ../app-feature-a(パスは git worktree list で確認) |
| 移らずに別の worktree で実行する | git -C ../app-feature-a status |
| worktree の中でブランチを切り替える | いつもどおり git switch <ブランチ>(ただし別の worktree で使用中のブランチは不可) |
| ブランチ名で worktree へ飛ぶ | 下の wt 関数 |
.bashrc や .zshrc に書いておく関数です。ブランチ名か、パスの一部で移動します。
# wt: ブランチ名(またはパスの一部)で worktree へ移動する
wt() {
local target
target=$(git worktree list --porcelain | awk -v q="$1" '
/^worktree /{p=$2}
/^branch /{b=$2; sub("refs/heads/","",b); if (b==q || index(p,q)) {print p; exit}}')
if [ -z "$target" ]; then echo "no worktree for: $1" >&2; return 1; fi
cd "$target" || return 1
}
$ wt feature-login && pwd && git branch --show-current
~/work/app-feature-login
feature-login
$ wt main && pwd
~/work/app
$ wt nothing
no worktree for: nothing
AIコーディングエージェントの並列開発での要点
エージェントを複数動かすときは、1エージェント=1 worktree=1ブランチにすると、作業ファイルとステージが混ざりません。この記事で確かめたとおり、分かれるのはファイル・HEAD・index だけで、ブランチ名・stash・フック・ポート・ローカル DB は共有です。
| 気をつける点 | 理由 | 対策 |
|---|---|---|
| ブランチ名を重ねない | ブランチは全 worktree で共有 | agent1/task-x のように接頭辞を付ける |
| 同じブランチを2つで開かない | is already used by worktree で止まる |
1エージェント1ブランチ。確認だけなら --detach |
| ポート・DB・コンテナ名を分ける | マシンで1つ | worktree 名から番号・名前を決める |
| 依存をエージェントに入れさせる | node_modules は無い |
pnpm・uv ならキャッシュからリンクされ速い |
| 終わったら remove と branch -d | 放置すると一覧と容量が増える | git worktree list を定期的に見る |
Claude Code の claude --worktree、.worktreeinclude、worktree.baseRef、サブエージェントの isolation: worktree、3本の worktree をマージして競合を解消する実行結果は、関連記事「Claude Code×git worktree並列開発|設定例と衝突対策表」で扱っています。
よくあるエラーと直し方
すべてこの記事の環境で再現したエラー文です。
| エラー文 | 原因 | 直し方 |
|---|---|---|
fatal: 'X' is already used by worktree at '…'(2.42以前は is already checked out at) |
同じブランチは1か所でしか開けない | その worktree へ cd/--detach/別ブランチ |
fatal: a branch named 'X' already exists |
-b に既存の名前を渡した |
git worktree add <パス> X(既存を開く)か別名 |
fatal: invalid reference: X |
ブランチが手元にも origin/ にも無い |
git fetch してから。名前の綴りを確認 |
fatal: '…' already exists |
パスに空でないディレクトリやファイルがある | 別のパスにする。空のディレクトリなら通る |
fatal: '…' is a missing but already registered worktree |
rm -rf した登録が残っている |
git worktree prune |
fatal: '…' is a missing but locked worktree |
ロックしたまま消した | git worktree unlock <パス> → prune |
fatal: '…' contains modified or untracked files, use --force to delete it |
変更や未追跡ファイルが残っている | コミット・退避してから、または --force |
fatal: cannot remove a locked working tree |
ロック中 | unlock してから remove(または -f -f) |
fatal: '…' is a main working tree |
メインは remove・move できない | リンクされた worktree のパスを指定 |
fatal: '…' is not a working tree |
パスが worktree ではない | git worktree list でパスを確認 |
fatal: '…' is already locked, reason: … |
二重に lock した | そのまま(すでにロック済み) |
error: cannot delete branch 'X' used by worktree at '…' |
使用中のブランチは -d でも -D でも消せない |
先に git worktree remove |
fatal: cannot force update the branch 'X' used by worktree at '…' |
使用中のブランチは branch -f できない |
その worktree で git reset |
fatal: not a git repository: …/.git/worktrees/… |
メインを動かした、または登録を prune した | メインで git worktree repair。prune 済みなら作り直し |
fatal: --worktree cannot be used with multiple working trees unless … |
拡張が無効 | git config extensions.worktreeConfig true |
error: unknown subcommand: 'switch' |
そのサブコマンドは無い | cd で移動、git switch はブランチ用 |
error: unknown option 'relative-paths' |
2.48 より前の版 | 絶対パス(既定)のまま使う |
やってはいけない操作と、やり直す方法
| 操作 | 起きること | やり直す方法 |
|---|---|---|
add --force や switch --ignore-other-worktrees で同じブランチを2か所で開く |
片方のコミットがもう片方の次のコミットで打ち消される | git reflog で打ち消し前の ID を探して git reset --hard。片方を remove |
.env を書き換えた worktree を remove |
ignore 済みファイルは確認なしで消える | 消える前に退避する。消えた後は Git に記録が無い |
mv で動かした後に prune |
登録が消え、repair も効かない |
worktree を作り直して、コミット前の変更を写す(上の手順) |
rm -rf だけで済ませる |
登録が残り、同じパスに作れない | git worktree prune |
-B で既存ブランチを起点にリセット |
ブランチの先端が移る(was at 02a7195 と表示) |
その worktree で git reset --hard 02a7195(was at の ID。git reflog でも探せる) |
.worktrees/ を .gitignore に入れずに git add . |
warning: adding embedded git repository と出て、worktree が gitlink として add される |
git rm --cached .worktrees/<名前> で外し、.gitignore に追記 |
よくある質問
Q. git worktree とは何ですか?
A. 1つのリポジトリから作業ディレクトリを複数作り、別々のブランチを同時に開く機能です。履歴・ブランチ・設定は .git を共有し、HEAD・ステージ・作業ファイルだけが worktree ごとに分かれます。仕組みは「git worktreeとは」の節の図のとおりです。
Q. git worktree の使い方の基本は?
A. git worktree add -b <ブランチ> ../<ディレクトリ> で作り、その中で普通に commit・push し、終わったら git worktree remove ../<ディレクトリ> と git branch -d <ブランチ> です。既存ブランチなら git worktree add ../<ディレクトリ> <ブランチ> です。
Q. git ワークツリー(working tree)と worktree は同じものですか?
A. 「ワークツリー(作業ツリー)」は、チェックアウトしたファイルが置かれるディレクトリそのものを指す用語です。git worktree コマンドは、そのワークツリーを1つのリポジトリに複数持たせる機能で、clone したディレクトリが「メインの worktree」、追加したものが「リンクされた worktree」と呼ばれます。
Q. git worktree switch はありますか?
A. ありません(error: unknown subcommand)。worktree 間の移動は cd、ブランチの切り替えは worktree の中で git switch です。別の worktree で使用中のブランチには切り替えられず、--ignore-other-worktrees で押し切ると、本文で示したようにコミットが打ち消される状態になります。
Q. worktree を消したらブランチも消えますか?
A. 消えません。remove の後に git branch --list で残っていることを確かめました。不要なら git branch -d で消します。
Q. 同じブランチを2つの worktree で開けますか?
A. 通常は開けません(is already used by worktree at)。確認やビルドだけなら --detach で同じコミットを開きます。
Q. worktree のディレクトリを rm -rf で消してしまいました。
A. git worktree prune で登録を掃除すると、同じパスに作り直せます。動かしただけなら prune ではなく git worktree repair <新しいパス> です。
Q. worktree の中で git pull や git push はできますか?
A. できます。この記事の hotfix の例では、worktree の中から git push -u origin hotfix-greet し、git status -sb に ## hotfix-greet...origin/hotfix-greet と出ました。リモートの設定は全 worktree で共有です。
Q. worktree は何個まで作れますか?容量は?
A. Git 側に上限はありません。express で10個作った実測では、.git は増えず、管理ファイルが1個 52KB、作業ツリーが1個 1.6MB ずつ増えました。作業ツリーの大きさ × 個数が目安です。
まとめ
- git worktree は
.gitを共有して作業ディレクトリを増やす。 共有はコミット・ブランチ・設定・フック・stash、専用はHEAD・index・作業ファイル - 追加は
add -b(新規)・add <パス> <ブランチ>(既存)・--detach(ブランチなし)・--track(リモート)。-Bは既存ブランチをリセットする - 同じブランチは1か所だけ。
--forceで2か所開くと、片方のコミットが打ち消される - 消すのは
remove、rm -rfしたらprune、mvしたらrepair。 順番を逆にすると登録が消える - ignore 済みの
.envやnode_modulesはremoveで確認なしに消え、新しい worktree にはコピーされない。 pnpm・uv ならキャッシュからハードリンクされ、2つ目は 0.2秒・1.3MB/0.04秒・392KB だった - 10個作っても
.gitは増えず、増えるのは作業ツリー(1.6MB × 10)と管理ファイル(52KB × 10) git worktree switchは無い。 移動はcd、別の worktree への操作はgit -C <パス>
参考・出典
確認日はいずれも 2026年10月16日です。
- Git 公式マニュアル git-worktree(2.56.0):https://git-scm.com/docs/git-worktree
- Git 公式マニュアル git-clone(--reference・--shared・--local):https://git-scm.com/docs/git-clone
- Git 公式マニュアル git-config(gc.worktreePruneExpire・worktree.guessRemote・worktree.useRelativePaths・extensions.worktreeConfig):https://git-scm.com/docs/git-config
- Git ソース branch.c(v2.42.0 / v2.43.0 のエラー文):https://github.com/git/git/blob/v2.43.0/branch.c
- Git 2.43.0 リリースノート:https://github.com/git/git/blob/v2.43.0/Documentation/RelNotes/2.43.0.txt
- Git 2.5.0・2.7.0・2.10.0・2.17.0・2.29.0・2.42.0・2.48.0 リリースノート:https://github.com/git/git/tree/master/Documentation/RelNotes
- VS Code Docs「Git Branches and Worktrees in VS Code」:https://code.visualstudio.com/docs/sourcecontrol/branches-worktrees
- VS Code 1.108 リリースノート(Worktrees in the Source Control Repositories view):https://code.visualstudio.com/updates/v1_108
- IntelliJ IDEA 2026.2 Help「Use Git worktrees」:https://www.jetbrains.com/help/idea/use-git-worktrees.html
- pnpm Settings(packageImportMethod):https://pnpm.io/settings/node-modules
- uv Docs「Caching」:https://docs.astral.sh/uv/concepts/cache/
- Claude Code Docs「worktree を使用して並列セッションを実行する」:https://code.claude.com/docs/ja/worktrees
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「git worktreeの使い方|仕組み・全コマンド早見表・エラー対処と実測」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。