git worktree — 並行作業の仕組み

書きかけを置いたまま急ぎの直しをしたい、Claude Code のセッションを2つ並べて同時に進めたい。そういうときに使うのが git worktree です。worktree は、同じリポジトリに「作業ツリー・ステージ・HEAD」の組をもう1つ増やすコマンドで、コミットとブランチは両方で共有します。この回では、となりのフォルダに作業ツリーを増やして行き来し、同じブランチを2か所で開けない理由、Claude Code が使う --detach の形、片づけるときの remove と --force の違いを見ます。

画面の見方
  • 上のターミナルは、実際に打ったときと同じ出力です(Git は英語で答えます)。cd でとなりのフォルダに入ると、プロンプトが ~/memo-app-fix (fix) のように変わります
  • 下の中で起きていることは、コマンドの結果、Git の中の何が変わったかを図にしたものです。丸がコミット、黒い札がブランチ、ピンクの札が HEAD です
  • 作業ツリーが2つあると、HEAD も2つ出ます。塗りのピンクの札が、いまターミナルがいる作業ツリーの HEAD、白地にピンク枠の HEAD(memo-app-fix) がもう一方の作業ツリーの HEAD です
  • 3つの場所の見出しには、いま入っている作業ツリーの名前が「作業ツリー(memo-app-fix)」のように出ます
  • 右の説明をスクロールして読み進めると、左のターミナルでコマンドが1つずつ打たれます。→ キーでも1つずつ進みます(← で1つ戻ります。スマホでは「次へ」ボタン)。最後まで進んでから → を押すと次の区間へ移ります。黄色く光っているのが、そのコマンドで変わった場所です

1. git worktree add — 作業ツリーをもう1つ増やす

ファイルは作業ツリー・ステージ・コミットの3つの場所にあり、HEAD は今乗っているブランチを指す付箋でした。ふだんのリポジトリには、この「作業ツリー・ステージ・HEAD」が1組しかありません。だから別のブランチで作業するには、git switch で HEAD を乗り換え、作業ツリーの中身を入れ替える必要がありました。

git worktree add は、この組をもう1つ増やします。増やした組は別のフォルダになり、その中では別の HEAD と別のステージが使われます。一方、コミットとブランチの付箋は1か所にしかなく、どちらの作業ツリーからも同じものが見えます。

作業ツリーごとに別々両方で共有
作業ツリー(フォルダの中のファイル)・ステージ・HEAD(どのブランチに乗っているか)コミット・ブランチの付箋(main・fix)

次の区間で打つ git worktree add ../memo-app-fix -b fix の ../memo-app-fix は作るフォルダ(memo-app のとなり)、-b fix は「新しいブランチ fix を作って、そこに乗せる」という意味です。

worktree add — 作業ツリーをもう1つ増やす

  1. main で、todo.txt に「バターを買う」の書きかけがあります。ここで急ぎの直しを頼まれたとします。stash で退避する代わりに、作業ツリーをもう1つ増やします。

    $ git status
    On branch main
    Changes not staged for commit:
      (use "git add <file>..." to update what will be committed)
      (use "git restore <file>..." to discard changes in working directory)
    	modified:   todo.txt
    
    no changes added to commit (use "git add" and/or "git commit -a")
  2. となりのフォルダ memo-app-fix に もう1つの作業ツリー ができ、新しいブランチ fix がそこで開かれました。グラフには白地にピンク枠の HEAD(memo-app-fix) が増えています。塗りの HEAD が、いまターミナルがいる memo-app の HEAD です。

    $ git worktree add ../memo-app-fix -b fix
    Preparing worktree (new branch 'fix')
    HEAD is now at e5c0a62 パンを追加
  3. このリポジトリの作業ツリーが1行ずつ出ます。左から、フォルダの場所・その作業ツリーの HEAD のコミット・乗っているブランチです。

    $ git worktree list
    /Users/you/memo-app      e5c0a62 [main]
    /Users/you/memo-app-fix  e5c0a62 [fix]
  4. * は、いまいる作業ツリーが乗っているブランチです。fix の前の + は ほかの作業ツリーが乗っているブランチ の印です。

    $ git branch
    + fix
    * main
  5. となりのフォルダに入ると、プロンプトが ~/memo-app-fix (fix) になり、塗りの HEAD が fix に移りました。3つの場所も memo-app-fix のものに切り替わっています。

    $ cd ../memo-app-fix
  6. こちらの todo.txt に「バターを買う」はありません。作業ツリーとステージは作業ツリーごとに別々で、ここは パンを追加 の中身から始まっています。

    $ cat todo.txt
    牛乳を買う
    卵を買う
    パンを買う
  7. fix での用事を済ませます。

    $ echo "ジャムを買う" >> todo.txt
  8. fix の付箋だけが ジャムを追加 へ進みました。白枠の HEAD(memo-app) は、main に乗ったままです。

    $ git commit -am "ジャムを追加"
    [fix bbe8599] ジャムを追加
     1 file changed, 1 insertion(+)
  9. 元のフォルダに戻ります。塗りの HEAD が main に戻り、3つの場所も memo-app のものに戻りました。

    $ cd ../memo-app
  10. 「バターを買う」の書きかけはそのまま残り、「ジャムを買う」は入っていません。もう一方の作業ツリーでコミットしても、こちらのファイルは変わりません。

    $ cat todo.txt
    牛乳を買う
    卵を買う
    パンを買う
    バターを買う
  11. コミットとブランチは2つの作業ツリーで共有なので、memo-app からも fix の ジャムを追加 が見えます。書きかけを片づけたら、ここで git merge fix を打てば取り込めます。

    $ git log --oneline --all
    bbe8599 (fix) ジャムを追加
    e5c0a62 (HEAD -> main) パンを追加
    8b41d07 卵を追加
    3f2a9c1 牛乳を追加

戻ったあとの todo.txt に、「ジャムを買う」は入っていませんでした。片方の作業ツリーでコミットしても、動くのはそのブランチの付箋だけで、もう片方のファイルには触りません。

git worktree list の各行は「フォルダの場所・HEAD のコミット・乗っているブランチ」です。1行目は、どの作業ツリーから打っても、最初からあったメインの作業ツリー(.git フォルダの本体がある方)です。

2. stash・switch との違い

書きかけがあるときに別のブランチで作業する方法は、ほかに 1-4 checkout の乗り換えと 1-7 stash の退避がありました。3つの違いは、書きかけをどこに置いたまま、別の作業をするかです。

方法書きかけはどうなるか向いている場面
git switch fix作業ツリーに載せたまま乗り換える。上書きされるファイルがあると止まる書きかけが無いとき
git stash → 乗り換え → git stash pop退避の棚にしまい、戻ってから作業ツリーに戻す短い用事を済ませて、すぐ戻るとき
git worktree add元のフォルダに置いたまま。触らない2つの作業を並行して進めるとき

worktree は作業ツリーそのものを増やすので、元のフォルダで開いているエディタや dev サーバーも止まりません。その代わり、追跡していないもの(node_modules など)は新しいフォルダに入らないので、入れ直しが要ります。

3. 同じブランチは2か所で開けない

作業ツリーを増やしても、1つのブランチに乗れるのは1つの作業ツリーだけです。fix を memo-app-fix で開いているあいだは、memo-app から fix に乗ることも、fix の付箋を剥がすこともできません。

同じブランチは2か所で開けない

  1. fix を、となりの memo-app-fix の作業ツリーで開きました。ターミナルがいるのは、main に乗った memo-app のままです。

    $ git worktree add ../memo-app-fix -b fix
    Preparing worktree (new branch 'fix')
    HEAD is now at e5c0a62 パンを追加
  2. memo-app から fix に乗ろうとすると止まります。already used by worktree at '/Users/you/memo-app-fix' は「fix は memo-app-fix の作業ツリーがもう使っている」という意味です。

    $ git switch fix
    fatal: 'fix' is already used by worktree at '/Users/you/memo-app-fix'
  3. fix を3つ目のフォルダで開こうとしても、同じ理由で止まります。1つのブランチに乗れる作業ツリーは1つだけ です。

    $ git worktree add ../memo-app-fix2 fix
    Preparing worktree (checking out 'fix')
    fatal: 'fix' is already used by worktree at '/Users/you/memo-app-fix'
  4. 付箋を剥がすこともできません。memo-app-fix の HEAD が乗っている付箋を剥がすと、その作業ツリーは乗る場所を失うからです。

    $ git branch -d fix
    error: cannot delete branch 'fix' used by worktree at '/Users/you/memo-app-fix'
  5. 反対側からも試します。memo-app-fix に入りました。

    $ cd ../memo-app-fix
  6. 今度は main が memo-app の作業ツリーに使われているので、同じように止まります。

    $ git switch main
    fatal: 'main' is already used by worktree at '/Users/you/memo-app'
  7. * fix がいまいる作業ツリーのブランチ、+ main がもう一方の作業ツリーのブランチです。+ の付いたブランチに乗りたいときは、それを使っている作業ツリーで別のブランチに移るか、その作業ツリーを消してからにします。

    $ git branch
    * fix
    + main

止める理由は、付箋が共有されていることにあります。かりに2つの作業ツリーが両方 fix に乗っていて、片方でコミットすると、もう片方から見てHEAD のコミットだけが勝手に進み、作業ツリーとステージが取り残された状態になります。付箋を剥がせないのも、乗っている作業ツリーが乗る場所を失わないためです。git branch で + の付いたブランチに止められたら、git worktree list で使っているフォルダを確かめます。

4. Claude Code の形 — git worktree add --detach

Claude Code の作業ログに出てくる git worktree add は、--detach を付けた形でした(10回)。--detach は、ブランチに乗らず、コミットを直接指す HEAD(1-4 の detached HEAD)で作業ツリーを作るオプションです。

ブランチに乗らないので、3 の「同じブランチは2か所で開けない」に引っかかりません。自分が main で作業していても、並行セッションは同じコミットから、ブランチを取り合わずに始められます。その代わり、作業が終わったら、ブランチの付箋を貼るか、main に取り込むかを決める必要があります。

Claude Code の形 — worktree add --detach

  1. Claude Code の作業ログに出てくる形です。--detach は、ブランチに乗らずにコミット e5c0a62 を直接指す detached HEAD で作業ツリーを作ります。新しいブランチは増えていません。

    $ git worktree add --detach ../memo-app-wt
    Preparing worktree (detached HEAD e5c0a62)
    HEAD is now at e5c0a62 パンを追加
  2. (detached HEAD) は、この作業ツリーがどのブランチにも乗っていない印です。ブランチを使わないので、main を開いている作業ツリーとブランチを取り合いません。

    $ git worktree list
    /Users/you/memo-app     e5c0a62 [main]
    /Users/you/memo-app-wt  e5c0a62 (detached HEAD)
  3. memo-app-wt に入ります。プロンプトの括弧には、ブランチ名の代わりにコミットの ID が出ます。

    $ cd ../memo-app-wt
  4. 並行セッションでの作業です。

    $ echo "ジャムを買う" >> todo.txt
  5. [detached HEAD bbe8599] は「ブランチに乗らずにコミットした」という意味です。進んだのはこの作業ツリーの HEAD だけで、ジャムを追加 を指すブランチの付箋はまだありません。

    $ git commit -am "ジャムを追加"
    [detached HEAD bbe8599] ジャムを追加
     1 file changed, 1 insertion(+)
  6. 残したいコミットには付箋を貼ります。git branch jam は、いまの HEAD の位置に jam を貼るだけで、HEAD は detached のままです。

    $ git branch jam
  7. 元の memo-app に戻ります。

    $ cd ../memo-app
  8. main に取り込みます。main は jam の祖先なので、付箋を前へずらすだけの fast-forward で済みました。

    $ git merge jam
    Updating e5c0a62..bbe8599
    Fast-forward
     todo.txt | 1 +
     1 file changed, 1 insertion(+)
  9. 用が済んだ作業ツリーを消します。書きかけも追跡していないファイルも無いので、--force 無しで何も言わずに消えます。

    $ git worktree remove ../memo-app-wt
  10. jam は main に取り込み済みなので、-d で剥がせます。

    $ git branch -d jam
    Deleted branch jam (was bbe8599).
  11. 並行セッションの ジャムを追加 が、main の歴史に入りました。

    $ git log --oneline -3
    bbe8599 (HEAD -> main) ジャムを追加
    e5c0a62 パンを追加
    8b41d07 卵を追加

detached HEAD で作ったコミットを指しているのは、その作業ツリーの HEAD だけです。付箋を貼らずに作業ツリーを消すと、次の 5 で見るように辿れなくなります。なお、Claude Code は作業ツリーをリポジトリの中の .claude/worktrees/ などに作ることがありますが、場所が違うだけで仕組みは同じです(この教材の画面では、となりのフォルダに作っています)。

5. 片づける — git worktree remove と --force

用が済んだ作業ツリーは git worktree remove フォルダ で消します。消えるのはフォルダと、その作業ツリーの HEAD・ステージで、共有のコミットとブランチは残ります。ただし、コミットしていない変更や一度も add していないファイルがあると、何もせずに止まります。

worktree remove と --force

  1. fix 用の作業ツリーを、となりの memo-app-fix に作ります。

    $ git worktree add ../memo-app-fix -b fix
    Preparing worktree (new branch 'fix')
    HEAD is now at e5c0a62 パンを追加
  2. memo-app-fix に入ります。

    $ cd ../memo-app-fix
  3. 1行書き足します。

    $ echo "ジャムを買う" >> todo.txt
  4. fix が ジャムを追加 へ進みました。

    $ git commit -am "ジャムを追加"
    [fix bbe8599] ジャムを追加
     1 file changed, 1 insertion(+)
  5. もう1行書き足しますが、今度はコミットしません。これが 書きかけ として残ります。

    $ echo "チーズを買う" >> todo.txt
  6. memo-app に戻ります。memo-app-fix のフォルダには、「チーズを買う」の書きかけが残っています。

    $ cd ../memo-app
  7. git worktree remove は止まります。contains modified or untracked files, use --force to delete it は「書きかけか追跡していないファイルがある。消すなら --force を付けて」という意味です。

    $ git worktree remove ../memo-app-fix
    fatal: '../memo-app-fix' contains modified or untracked files, use --force to delete it
  8. --force を付けると、フォルダごと消えて白枠の HEAD もなくなりました。「チーズを買う」は一度もコミットもステージもしていないので、reflog でも取り戻せません。

    $ git worktree remove --force ../memo-app-fix
  9. 残った作業ツリーは memo-app だけです。

    $ git worktree list
    /Users/you/memo-app  e5c0a62 [main]
  10. 一方、コミット済みの ジャムを追加 は残っています。作業ツリーを消しても、ブランチの付箋 fix は消えない からです。

    $ git log --oneline --all
    bbe8599 (fix) ジャムを追加
    e5c0a62 (HEAD -> main) パンを追加
    8b41d07 卵を追加
    3f2a9c1 牛乳を追加
  11. fix を main に取り込みます。付箋を前へずらすだけの fast-forward で済みました。

    $ git merge fix
    Updating e5c0a62..bbe8599
    Fast-forward
     todo.txt | 1 +
     1 file changed, 1 insertion(+)
  12. 「ジャムを買う」は入り、「チーズを買う」はありません。--force で消えたのは、コミットしていなかった書きかけだけです。

    $ cat todo.txt
    牛乳を買う
    卵を買う
    パンを買う
    ジャムを買う

3-4 reflog は HEAD とブランチが指したコミットの記録なので、一度もコミットしていない書きかけは、reflog でも取り戻せません。コミット済みのものが残るかどうかは、付箋で決まります。

消した作業ツリーにあったものremove のあと
ブランチ(fix)に積んだコミット残る。git merge fix で取り込める
detached HEAD で作り、付箋を貼っていないコミット辿れなくなる。指していた HEAD と、その動きの記録(reflog)が一緒に消えるため
コミットしていない変更・追跡していないファイル--force のときだけ消える。付けなければ remove が止まる

フォルダを rm -rf などで直接消すと、作業ツリーの記録が残って git worktree list に出続けます。そのときは git worktree prune で片づけます。

6. Claude Code が打つ worktree

Claude Code が打った Git のコマンド延べ約14,000回のうち、git worktree は204回でした。内訳は git worktree list が103回、git worktree remove --force が25回、git worktree add --detach が10回です。見るだけの list が約半分を占めます。

気をつけたいのは git worktree remove --force です。そのフォルダに書きかけが残っていれば、確認なしに消えます。許可する前に git -C ../memo-app-wt status(-C は「そのフォルダで打ったことにする」)で書きかけが無いかを見て、git log --oneline --all で残したいコミットに付箋があるかを確かめてください。

なお worktree はその PC の中だけの仕組みで、Windows のノート PC で増やした作業ツリーは Mac からは見えません。もう1台に渡したい作業は、ブランチにコミットして push します。

7. Claude Code の許可画面で見かけたら

この回で見たコマンドを、「何が消えうるか」で並べるとこうなります。

コマンド何が起きるか消えうるもの
git worktree list作業ツリーの一覧を見るだけなし
git worktree add ../x -b fix / git worktree add --detach ../xとなりに作業ツリーを増やす。いまの作業ツリーには触らないなし(使用中のブランチなら止まる)
git worktree remove ../xその作業ツリーのフォルダを消す。ブランチに積んだコミットは残る注意 detached HEAD で作って付箋を貼っていないコミットは辿れなくなる(書きかけや追跡していないファイルがあれば止まる)
git worktree remove --force ../x書きかけがあっても、フォルダごと消すその作業ツリーのコミットしていない変更(reflog でも戻らない。付箋の無い detached のコミットも辿れなくなる)

見分け方は、remove に --force が付いているかどうかです。付いていなければ、書きかけがあるときは Git が止めてくれます。付いていたら、そのフォルダの git status を先に見てください。

まとめ

  • git worktree add は、同じリポジトリに作業ツリー・ステージ・HEAD の組をもう1つ増やす。コミットとブランチの付箋は共有
  • git worktree add ../memo-app-fix -b fix で、となりのフォルダに fix 用の作業ツリーができる。cd で行き来する
  • 片方でコミットしても、もう片方のファイルは変わらない。書きかけを置いたまま別の作業ができる
  • 同じブランチは2か所で開けない。使用中のブランチは switch も branch -d もできず、git branch では + が付く
  • Claude Code の作業ログに出てくる git worktree add --detach は、ブランチを取り合わずに作業ツリーを作る形。終わったら付箋を貼るか、main に取り込む
  • git worktree remove は書きかけがあると止まる。--force は書きかけごと消し、reflog でも戻らない。ブランチに積んだコミットは残る

これで「Git の中身が見える講座」はおしまいです。第1部では3つの場所と、ブランチと HEAD という付箋を、第2部では origin/main という GitHub の写しを見ました。第3部では歴史をつなぎ・書き換え・取り戻し、第4部ではそれを Claude Code のコマンドと許可画面に当てはめました。どのコマンドも、動かしているのは3つの場所と付箋のどれかです。許可画面に見慣れないコマンドが出たら、どの場所と付箋が動き、何が消えうるかを、この講座の図に当てはめてみてください。