本ページはプロモーションが含まれています

開発ツール

git worktreeとは?.gitの共有構造と仕組みを図解

トム

SESで約8年、環境を変えられないもどかしさの中にいました。32歳で自社開発企業に転職し、今はClaude Codeと自動化で、仕事も時間も組み直している途中です。
・Javaバックエンドエンジニア(Java歴12年以上)
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎
詳細なプロフィール

急ぎの修正依頼が来たとき、手元のブランチは編集の途中。定番の対処は、stash、中途半端なコミット、もう1つのcloneの三択です。

git worktreeとは、1つのGitリポジトリに複数の作業ディレクトリを紐づけ、ブランチごとに別のフォルダで同時に開いておける機能です。

ただし、追加した側のフォルダを覗くと、.gitがディレクトリではなくファイルになっています。

履歴を共有しているのに、なぜブランチごとに独立して動くのか。手元で動かした出力を見ながら、その仕組みを順にたどります。

この記事でわかること

  • git worktreeとは何か(1つのリポジトリで複数ブランチを同時に開く機能)
  • .gitがファイルになる仕組みと、共有される情報と独立する情報
  • cloneやstash、checkoutとの違い
  • Claude Codeの並列実行で使うときの落とし穴(未コミットの変更は持ち越されない)

git worktreeとは?1つのリポジトリで複数ブランチを開く機能

git worktreeは、Git 2.5(2015年7月)で追加された標準機能です。追加のインストールは要りません。

Gitでは、作業ファイルが並ぶフォルダをworktree(作業ツリー)と呼びます。リポジトリを作ると最初の1つが自動でできていて、これをmain worktreeと呼びます。

git worktreeは、このフォルダをあとから何個でも増やす仕組みです。

通常のGitは作業ディレクトリが1つだけ

git initやgit cloneで作られるフォルダは1つです。ブランチを切り替えるたびに、そのフォルダの中身がまるごと入れ替わります。

作業途中のファイルがあると、checkoutは変更の衝突で止まるか、変更を抱えたまま切り替わります。

だから割り込みの修正が入ると、stash、仮コミット、もう1つのcloneのどれかを選ぶことになります。

通常のGit:フォルダは1つ

📁 app/

main → feature-a → hotfix

切り替えるたびに、同じフォルダの中身が入れ替わる。

git worktree:ブランチごとにフォルダ

📁 app/

main

📁 app-feature-a/

feature-a

📁 app-hotfix/

hotfix

切り替えず、フォルダを移動するだけ。

worktreeで解決できる3つの場面

worktreeのメリットは、今の作業を止めずに済むことです。効くのは、次の3つの場面です。

  • 緊急の修正:作業中のブランチはそのまま置いて、別フォルダでhotfixを書く
  • レビュー:相手のブランチを別フォルダで開き、動かしながら確認する
  • 長い処理の待ち時間:ビルドやテストを回している間に、別のブランチで作業を進める
bash
git worktree add ../app-hotfix -b hotfix
git worktree list

1行目で、隣のフォルダapp-hotfixに新しいブランチhotfixを作って開きます。2行目で、いま存在するworktreeの一覧を確認できます。

git worktreeの仕組み|.gitファイルと共有の境界

worktreeが別フォルダで別ブランチを動かせる原理は、リポジトリの中身を「全員で共有する部分」と「フォルダごとに持つ部分」に分けている点にあります。

共有するのはオブジェクトとブランチ、独立して持つのはHEADとindex。この境界は、.gitの中を見るとはっきりわかります。

flowchart TD A["linked worktree<br>app-feature-a/.git(ファイル)"] -->|"gitdirで指す"| B[".git/worktrees/app-feature-a/<br>HEAD、index、gitdir、commondir"] B -->|"commondirで戻る"| C[".git/ 本体<br>objects、refs、config"] D["main worktree<br>app/.git(ディレクトリ)"] --- C

main worktreeとlinked worktreeの違い

最初からある作業ディレクトリがmain worktree、git worktree addで追加したものがlinked worktreeです。

リポジトリの本体である.gitディレクトリを持つのは、main worktreeだけです。

項目main worktreelinked worktree
作り方git init、git clonegit worktree add
.gitの実体ディレクトリファイル(1行だけ)
個数1つ(bareリポジトリを除く)0個以上
削除できないgit worktree removeで削除できる

.gitがファイルになり.git/worktrees/を指す

linked worktreeの.gitを開くと、ディレクトリではなく1行だけのテキストファイルです。Git 2.50.1(Apple Git)で実際に作って確認しました。

text
$ cat ../app-feature-a/.git
gitdir: /path/to/app/.git/worktrees/app-feature-a

指す先の.git/worktrees/app-feature-a/には、このworktree専用の情報が入っています。

text
$ ls .git/worktrees/app-feature-a
HEAD  ORIG_HEAD  commondir  gitdir  index  logs  refs
$ cat .git/worktrees/app-feature-a/HEAD
ref: refs/heads/feature-a
$ cat .git/worktrees/app-feature-a/commondir
../..

ここにobjectsはありません。commondirの../..が、共有側の本体である.git/へ戻る道です。

linked worktreeの中でgit rev-parseを実行すると、この2つの場所が別々に返ります。

text
$ git rev-parse --git-dir --git-common-dir
/path/to/app/.git/worktrees/app-feature-a
/path/to/app/.git

前者がworktree専用の置き場、後者が全員で共有する置き場です。これが「履歴は共有、状態は独立」の正体です。

共有されるものと独立するもの

公式ドキュメントの分類を、手元の挙動と突き合わせて、図と表にします。

main worktree(app/)

独立:作業ファイル
独立:HEAD → main
独立:index

linked worktree(app-feature-a/)

独立:作業ファイル
独立:HEAD → feature-a
独立:index

▼ どちらも同じ .git/ を参照する ▼

共有:.git/

オブジェクトDB(コミットとファイルの中身)
refs/heads/*、refs/tags/*(ブランチとタグ)
config(リモートなどの設定)

区分対象中身
共有オブジェクトDBコミットとファイルの中身。全worktreeが同じものを参照する
共有refs/heads/*、refs/tags/*ブランチとタグ。片方で進めるともう片方からも見える
共有.git/configリモート設定などのリポジトリ設定
独立HEADいま開いているブランチ(worktreeごとに別)
独立indexステージング状態
独立ORIG_HEAD、MERGE_HEAD操作の途中で使う一時的な参照

ブランチ名の一覧は共有です。別のworktreeで開いているブランチには、git branchの一覧で+が付きます。

text
$ git -C ../app-feature-a branch --list
* feature-a
  feature-b
+ main

同じブランチを2か所で開けない理由

すでに開いているブランチを別のworktreeで開こうとすると、Gitは拒否します。

text
$ git worktree add ../dup main
Preparing worktree (checking out 'main')
fatal: 'main' is already used by worktree at '/path/to/app'

理由は、ブランチの先端が共有なのに、HEADとindexがworktreeごとに独立しているからです。

片方でコミットするとブランチが進み、もう片方のindexと作業ファイルは古いままになります。その状態でコミットすると、直前の変更を巻き戻してしまいます。

① 同じブランチを開く

worktree AとBが、どちらも main(コミットC1)を指す。

② Aでコミットする

共有の main がC2へ進む。Bの HEAD も、ブランチ経由でC2を指す。

③ Bだけ古いまま

Bのindexと作業ファイルはC1のまま。そのままコミットすると、C2の変更を巻き戻す。

--forceで回避できますが、この状態を意図して作る場面は通常ありません。

ブランチを占有せずに同じコミットを開きたいときは、git worktree add --detach ../app-tmp mainのようにdetached HEADで開きます。この形なら、mainが別のworktreeで使用中でも拒否されません。

cloneやstash、checkoutとの違い

worktreeは、clone、stash、checkoutの間を埋める道具です。何が同じで何が違うのかを、表にまとめます。

選ぶ基準は2つあります。未コミットの変更を手放せるか、履歴を重複させたくないか、です。

方法作業フォルダ履歴(オブジェクト)未コミットの変更向く場面
git worktreeブランチごとに別1つを共有フォルダごとに独立して残る複数ブランチを同時に開いておきたい
git cloneリポジトリごとに別丸ごと複製それぞれ独立完全に別のリポジトリとして扱いたい
stash + checkout1つを使い回す1つを共有stashに退避して、あとで戻す数分で終わる割り込み

cloneより軽い理由

cloneは.gitを丸ごと複製するため、履歴が大きいほどディスクも時間も使います。worktreeは作業ファイルだけを増やし、履歴は1つを共有します。

git clone:履歴が2つ

📁 app/

.git/(履歴すべて)

📁 app-copy/

.git/(履歴すべて)

fetchも、フォルダごとに行う。

git worktree:履歴は1つ

📁 app/

.git/(履歴すべて)

📁 app-feature-a/

.git は1行のファイルで、上の .git/ を指す

fetchは1か所で、全フォルダに反映される。

cloneは別のリポジトリ扱いなので、fetchやブランチの更新も個別に行う必要があります。worktreeなら、1か所でfetchすれば全員に反映されます。

stashやcheckoutでは足りない場面

  • stashの戻し忘れで、退避した作業が行方不明になる
  • 切り替えのたびにビルド成果物や依存関係が入れ替わり、再ビルドが走る
  • 2つのブランチの動作を、並べて比べられない

数分で終わる割り込みならstashで足ります。フォルダを分ける価値が出るのは、切り替えの回数が増えたときです。

Claude Codeの並列実行とworktree

AIエージェントに複数のタスクを同時に任せると、同じフォルダを2つのセッションが書き換えて衝突します。worktreeは、この衝突をフォルダを分けることで避ける手段です。

Claude Codeには、並列実行の土台としてworktreeが組み込まれています。

AIエージェントと相性がよい理由

セッションごとに作業フォルダとブランチが独立するため、ファイル編集が互いに触れ合いません。履歴は共有なので、作業が終わったあとにマージして統合できます。

bash
claude --worktree feature-auth

公式ドキュメントによると、このコマンドは.claude/worktrees/feature-auth/にworktreeを作り、ブランチ名はworktree-feature-authになります。

サブエージェントも、isolation: worktreeを指定すれば専用のworktreeで動きます。

サブエージェントそのものの使い方は、Claude Codeサブエージェントの使い方にまとめています。

複数セッションを並べる方法はtmuxで並列実行する方法、役割を分けたエージェントを走らせる方法はAgent Teamで専門家チームを作る実践ガイドで扱っています。

あわせて読む

worktree内のセッションを再開する手順は、セッション再開ガイドが参考になります。

Claude Codeの導入から基本操作までは、【2026年版】Claude Code使い方|導入から実践活用までが入口になります。

未コミットの変更は持ち越されない(私の失敗談)

worktreeは、コミット済みの内容から新しくチェックアウトされます。作業フォルダにある未コミットの変更は、コピーされません。この仕様で、ヒヤリとする経験をしました。

2026年9月7日、ブログ記事のリライトをClaude Codeのサブエージェントに任せたときの話です。

事前にWordPressから最新の本文を取得し、ローカルのHTMLを最新の状態に更新していました。ただし、このHTMLはgit管理下にあり、更新はコミット前でした。

サブエージェントをisolation: worktreeで起動したところ、作業の土台になったのは8月24日にコミットした古い本文です。

編集の途中で、サブエージェント自身が「本文の書き方が、取得済みの内容と食い違っている」と報告してきて、発覚しました。

実害は出ませんでした。WordPressへのアップロードが、環境の都合でたまたま失敗していたからです。アップロードが通っていたら、本番の記事が約2週間前の状態に巻き戻っていたことになります。

公式ドキュメントでは、新しいworktreeの土台は既定でリモートのデフォルトブランチ(通常はmain)とされています。設定worktree.baseRefを"head"にすると、手元のHEADに変わります。

どちらを選んでも、未コミットの変更は含まれません。

flowchart TD R["リモートのmain<br>最新のコミット"] -->|"baseRef = fresh(既定)"| W["新しいworktree<br>.claude/worktrees/名前/"] L["手元のHEAD<br>未pushのコミットを含む"] -->|"baseRef = head"| W U["未コミットの変更<br>作業中のファイル"] -.->|"引き継がれない"| W

対策は単純で、worktreeを作る前に、必要な変更をコミットしておくことです。生成物や取得結果をgit管理下に置いている場合は、特に効きます。

git worktreeを使うときの注意点

新しく作ったworktreeは、コミット済みのファイルだけが並んだ、ほぼ新品のフォルダです。そう考えると、つまずきにくくなります。よくある落とし穴を2つ挙げます。

公式ドキュメントには、複数チェックアウトはまだ実験的な機能で、サブモジュール(submodule)を含むスーパープロジェクトでの利用は推奨されない、とも書かれています。

node_modulesや.envは引き継がれない

Gitで管理されていないファイルは、新しいworktreeに存在しません。node_modules、.env、ビルド成果物が該当します。依存関係のインストールは、worktreeごとにやり直します。

Claude Codeなら、プロジェクト直下の.worktreeincludeに.gitignore形式で書いたファイルを、新しいworktreeへコピーしてくれます。対象は、gitignore済みのファイルだけです。

text
.env
.env.local

フォルダを直接消すとpruneが必要になる

worktreeのフォルダをFinderやmvで動かしたり消したりすると、Git側の登録だけが残ります。実際に隣のフォルダへ移動してみると、一覧にprunableと出ました。

text
$ git worktree list
/path/to/app             f000549 [main]
/path/to/app-feature-b   f000549 [feature-b] prunable
$ git worktree prune -v
Removing worktrees/app-feature-b: gitdir file points to non-existent location

正規の手順はgit worktree removeです。未コミットの変更があると拒否され、--forceを付けたときだけ消せます。git worktree pruneは、登録だけ残った残骸を掃除するコマンドです。

フォルダを動かしたいときはgit worktree moveを使います。mvで動かしてしまった場合は、git worktree repairでGit側の登録を直せます。

手元では、mvの直後に一覧へprunableと出ましたが、repairを実行すると新しい場所で正常に表示されました。動かしたフォルダを残したいなら、pruneより先にrepairです。

git worktreeのよくある質問

git worktreeについて、検索でよく見かける疑問を、Q&A形式で7つにまとめました。

回答は、本文で実機確認した挙動と、GitおよびClaude Codeの公式ドキュメントの記述をもとにしています。仕組みの要点を確認したいときにも使えます。

git worktreeとは何ですか?

1つのGitリポジトリに複数の作業ディレクトリを紐づけ、ブランチごとに別のフォルダで同時に開ける機能です。履歴とブランチは共有し、HEADとindexはフォルダごとに独立します。

git worktreeとgit cloneの違いは何ですか?

cloneは.gitを丸ごと複製して別のリポジトリを作ります。worktreeは履歴を1つ共有したまま、作業フォルダだけを増やします。履歴の重複がなく、fetchも1か所で済みます。

同じブランチを複数のworktreeで開けますか?

通常は開けません。すでに別のworktreeで使われているブランチを指定すると、fatalエラーで拒否されます。--forceで回避できますが、HEADとindexが食い違うため、推奨されません。

worktreeを削除するにはどうしますか?

git worktree removeを使います。未コミットの変更があると拒否され、--forceを付けると削除できます。フォルダを直接消した場合は、git worktree pruneで登録の残骸を掃除します。

未コミットの変更は新しいworktreeに引き継がれますか?

引き継がれません。新しいworktreeはコミット済みの内容からチェックアウトされます。必要な変更は、worktreeを作る前にコミットしてください。

.envやnode_modulesはコピーされますか?

Gitの標準動作ではコピーされません。依存関係はworktreeごとにインストールし直します。Claude Codeでは、.worktreeincludeに書いたgitignore済みファイルを新しいworktreeへコピーできます。

git worktreeはGitのどのバージョンから使えますか?

Git 2.5(2015年7月)から使えます。現在の一般的なGitであれば、追加の設定なしで利用できます。

git worktreeの理解を整理する

急ぎの修正が来たときの三択に、4つ目の選択肢が加わります。仕組みの要点は次のとおりです。

  • worktreeとは、1つのリポジトリに複数の作業ディレクトリを紐づける機能(Git 2.5以降)
  • linked worktreeの.gitはファイルで、.git/worktrees/名前/を指す
  • オブジェクト、ブランチ、設定は共有。HEADとindexはworktreeごとに独立
  • 同じブランチは2か所で開けない
  • 新しいworktreeは未コミットの変更を持たない。作る前にコミットし、依存関係や.envは別途用意する

次に割り込みの修正が飛んできたら、stashの前に、隣のフォルダを1つ増やしてみてください。

  • この記事を書いた人
  • 最新記事

トム

SESで約8年、環境を変えられないもどかしさの中にいました。32歳で自社開発企業に転職し、今はClaude Codeと自動化で、仕事も時間も組み直している途中です。
・Javaバックエンドエンジニア(Java歴12年以上)
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎
詳細なプロフィール

-開発ツール