12k
All articles

Git Worktrees 徹底解説:いつ、なぜ使うべきか

Git worktreeの仕組みを解説。stashや別クローンとの違い、使いどき、管理に使う基本コマンドが分かります。

OpenReplay Team
OpenReplay Team
Git Worktrees 徹底解説:いつ、なぜ使うべきか

Git worktree とは、同じリポジトリに紐づく追加の作業ディレクトリのことです。それぞれが独自のチェックアウト済みブランチを持つため、クローンを作ることなく、2 つのフォルダで 2 つのブランチを同時に開いておけます。

多くの開発者は、イライラの最中にこの機能の必要性に気づきます。stash を間違ったブランチに適用してしまった、チェックアウト後に language server が再インデックスで唸り続けている、同じリポジトリの 2 つ目のクローンがいつまで経っても同期しない、といった場面です。worktree はこの 3 つすべてを解決します。しかも 2015 年 7 月の Git 2.5 から同梱されているので、インストールするものは何もありません。

本記事では、worktree がディスク上で実際に何であるか、この機能を動かす 3 つのコマンド、stash より優れている場面、2 つ目のクローンより優れている場面、そしてそのコストについて解説します。

要点

  • Git worktree は同じリポジトリに紐づく 2 つ目の作業ディレクトリで、独自のチェックアウト済みブランチを持ちます。リンクされたディレクトリが持つのは完全な .git フォルダではなく、メインリポジトリを指し示す小さな .git ファイルです。
  • git worktree add -b hotfix-bug ../hotfix-workspace main の 1 コマンドで、stash → checkout → branch → 修正 → stash pop という一連の手順すべてを置き換えられます。
  • すべての worktree は 1 つのオブジェクトデータベースと 1 組の ref を共有するため、git fetch 1 回ですべての worktree が更新され、履歴がディスク上で重複することもありません。
  • Git が追跡するのはコミットされたファイルであって、あなたの環境ではありません。インストール済みの依存関係、.env ファイル、ビルドキャッシュは元のディレクトリに残されたままです。
  • デフォルトでは、Git は同じブランチを 2 つの worktree でチェックアウトすることを拒否します。コミットのビルドやテストだけが目的なら、git worktree add -d で detached HEAD の worktree を作成しましょう。

ディスク上で Git worktree とは何なのか

リンクされた worktree は、チェックアウトされたファイルのディレクトリであって、リポジトリのコピーではありません。トップレベルの .git はディレクトリではなくプレーンなファイルで、メインリポジトリの .git/worktrees/ 配下にある小さなメタデータフォルダを指しています。このレイアウトは git worktree のドキュメントで詳しく説明されています。重いものはすべて 1 箇所に留まります。1 つのオブジェクトデータベース、1 つの履歴、1 組の ref がすべての worktree に供され、個々の worktree に属するのは HEAD やインデックスを含むごくわずかなファイルだけです。

この 1 点が、worktree に関する他のすべてを説明します。worktree の作成にかかるコストはチェックアウト分であって、クローン分ではありません。どの worktree で行ったコミットも、他のすべての worktree から即座に見えます。その下にあるリポジトリは 1 つだけだからです。

worktree はどんなワークフローを置き換えるのか

worktree がなければ、緊急の割り込みが入るたびに現在の状態を中断しなければなりません。よく知られた手順は次のとおりです(メッセージが実際に付与されるよう git stash push 形式を使用):

git stash push -m "wip: login form"
git checkout main
git checkout -b hotfix-bug
# fix, commit, push, wait for the merge
git checkout feature-login
git stash pop

5 つのコマンド、2 回のコンテキストスイッチ、そして stash を間違ったブランチに pop してしまう機会が 1 回。worktree 版は 1 コマンドです:

git worktree add -b hotfix-bug ../hotfix-workspace main

これで兄弟フォルダが作成され、main を起点とする hotfix-bug ブランチが作られ、そこにチェックアウトされます。元のディレクトリは手つかずです。同じブランチ、同じ変更済みファイル、開いたままのエディタ。修正がマージされたら、git worktree remove ../hotfix-workspace でフォルダを削除して登録を解除します。

3 つのコマンド: add、list、remove

日常的な worktree の利用は 3 つのサブコマンドに集約されます。git worktree add <path> <branch> は既存ブランチを新しいパスにチェックアウトします。パスの前に -b <new-branch> を付ければ、その場でブランチを作成できます。add にまだ存在しないパスを指定すれば、Git がディレクトリを作成してくれます。

git worktree list はすべての worktree を、メインのものを先頭に、パス・短縮コミットハッシュ・角括弧内のチェックアウト済みブランチとともに表示します:

/home/you/project             abc1234 [feature-login]
/home/you/hotfix-workspace    def5678 [hotfix-bug]

git worktree remove <path> は worktree を削除します。Git の削除ルールは厳格で、未追跡ファイルが 1 つでも、編集された追跡対象ファイルが 1 つでもあればコマンドは停止します。--force を付ければ実行できますが、それらの変更は破棄されます。メインの worktree は一切手放せず、ロックされた worktree には --force が 2 回必要です。フォルダを手動で削除してしまった場合は、git worktree prune が残されたメタデータを掃除します。

worktree が stash に勝るのはどんなときか

stash は作業を中断させますが、worktree は作業を走らせたままにできます。この違いが、どちらを選ぶかを決めます。2 行の差分を 10 分ほど棚上げするだけなら stash で十分です。しかし、ディレクトリ内の作業がまだ進行中の場合には不適切なツールです。たとえば、テストスイートや長時間ビルドが実行中の場合、dev server がファイルを監視している場合、ブランチ切り替え後に language server がプロジェクト全体を再インデックスしてしまうエディタを使っている場合などです。

worktree なら、元のディレクトリに一切触れないので、そうした状態は何も乱されません。新しいフォルダを 2 つ目のエディタウィンドウで開き、割り込みタスクをそこで済ませ、戻ってくればすべてが元のままです。この同じ隔離性ゆえに、AI コーディングエージェントを並列実行するツール群が worktree を作業モデルとして採用しています。より主張の強い見解としては、matklad の記事が、ブランチではなく並行する活動に対応づけた 5 つの worktree を固定的に使う、ある開発者のやり方を紹介しています。ただしこれは個人的なセットアップであり、標準的なプラクティスではないと捉えてください。

worktree か、2 つ目のクローンか

両方のディレクトリが同じリポジトリを追跡することを意図しているなら、worktree が 2 つ目のクローンに勝ります。すべての worktree が 1 つのオブジェクトストアと 1 組の ref を共有するため、git fetch 1 回ですべてが更新され、一方から push したブランチは他方から即座に見え、worktree を追加しても履歴がディスク上で重複することはありません。2 つ目のクローンではそのいずれも得られません。fetch 先のオブジェクトストアが 2 つ、乖離していく ref セットが 2 組、そしてディスク使用量は倍になります。

それでもクローンが正解となるケースは 2 つあります。作業が本当に別のリモートに属する場合(別途やり取りする fork など)、そして使い捨ての実験をメインリポジトリから完全に隔離したい場合、つまり ref の共有すら望ましくない場合です。

StashWorktree2 つ目のクローン
作業を走らせたままにできるいいえはいはい
セットアップコスト即時チェックアウト 1 回分フルクローン
ディスク上の履歴共有共有重複
fetch 1 回で更新されるはいはいいいえ

worktree のコストは何か

Git が追跡するのはコミットされたファイルであって、あなたの環境ではありません。これが新しい worktree ごとにかかる主な税金です:

  • インストール済みの依存関係は付いてきません。npm や pip プロジェクトの新しい worktree では、通常ビルド前に独自のインストール手順が必要です。
  • 未追跡のローカルファイルは残されたままです。.env ファイル、ローカル設定、ビルドキャッシュはすべて元のディレクトリに存在します。Git がそれらを記録していないためです。
  • リポジトリ内部に作成した worktree フォルダは未追跡の雑多なファイルとして現れるため、.gitignore へのエントリが必要になります。兄弟ディレクトリ(../hotfix-workspace)にすれば、この問題は完全に回避できます。
  • デフォルトでは、1 つのブランチを同時にチェックアウトできる worktree は 1 つだけです。回避策はあります(add--forceswitch--ignore-other-worktrees)が、コミットのビルドやテストだけが目的なら、detached HEAD の worktree のほうがきれいな答えです:
git worktree add -d ../build-check 1a2b3c4

-d フラグは新しいフォルダ内で HEAD を detached のままにするので、ブランチが専有されることはなく、この制限が発動することもありません。既存の worktree 内で git switch -d <commit> を実行しても同じことができます。

同時進行の作業は十分にありますか

導入すべきかの判定は簡単です。実際に未コミットの作業が進行中のときに、割り込みが頻繁に入りますか。ブランチを切り替えると、体感できるほどの依存関係の再インストールや language server の再インデックスが発生しますか。同じリポジトリの 2 つ目のクローンをすでに維持していますか。2 つ「はい」があれば、worktree は最初の 1 週間で元が取れるでしょう。0 なら、stash とブランチで本当に十分です。まずは 1 つから始めましょう。次に機能開発の途中で緊急修正が舞い込んだら、git stash の代わりに git worktree add を実行し、修正がマージされたらそのフォルダを削除してみてください。

FAQ

git worktree は submodule を使うリポジトリでも動作しますか?

部分的には動作します。Git 自身のマニュアルは BUGS の注記で submodule サポートが未完成であると明記しており、superproject を複数箇所で同時にチェックアウトしないよう警告しています。実際のところ、submodule を含むリンクされた worktree は git worktree move で移動できず、git worktree remove で削除するには force フラグが必要です。リポジトリが submodule に大きく依存しているなら、2 つ目のクローンのほうが安全な選択です。

stash やブランチは git worktree 間で共有されますか?

はい。refs/ 配下のものはすべての worktree に共通なので、ブランチ、タグ、stash はどこにいても同じように見えます。例外は 3 つの名前空間で、これらは 1 つの worktree にローカルなままです: refs/bisect、refs/worktree、refs/rewritten。それ以外では、worktree 自身のポインタ、つまり HEAD やそのインデックスなどだけがプライベートです。したがって、ある worktree で作成した stash は、同じリポジトリの他のどの worktree からでも一覧表示でき、pop することもできます。

作成後に worktree を別のフォルダへ移動できますか?

はい。git worktree move に対象の worktree と移動先のパスを渡せば、Git がフォルダの移動とメタデータの書き換えを一度に行います。対応できないケースは 2 つ: メインの worktree と、submodule を含むリンクされた worktree です。ロックされた worktree には force フラグが 2 回必要です。すでに自分でフォルダを別の場所へ移動させてしまった場合は、git worktree repair が再接続してくれます。

git worktree add をパスだけ指定してブランチを指定せずに実行するとどうなりますか?

Git はパスの末尾部分をブランチ名として扱い、そのブランチを HEAD から開始して、新しいフォルダにチェックアウトします。つまり git worktree add ../hotfix を実行すると、hotfix という名前のブランチが ../hotfix にチェックアウトされた状態になります。その名前がすでに使われている場合は、他の worktree がそのブランチを現在保持していない限り、Git は既存のブランチをチェックアウトします。

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — self-hosted, with full data ownership.

Star on GitHub

We use cookies to improve your experience. By using our site, you accept cookies.