Git Worktree: stashやcloneなしで複数ブランチを同時に作業する

Git tutorial - IT technology blog
Git tutorial - IT technology blog

よくある場面:作業中に別の仕事を割り込まれる

feature/paymentブランチで新機能を開発中、ちょうど半分まで進んだところで上司からメッセージが届く:「緊急!本番でバグが出た、すぐ直して」。こんなとき、選択肢はいくつかある:

  • 仮コミット(履歴を汚すゴミコミット)
  • git stashを使う(あとで忘れて、探すのが一苦労)
  • 別ディレクトリにリポジトリをまるごとクローンする(容量を食うし、node_modulesのセットアップも必要)

自分も三つ全部経験した。あるときなんか、慌ててstashしながらパニックになって、間違ったブランチにgit push --forceしてしまい、丸々一朝分のコードを吹き飛ばしたこともある。それ以来、force pushには細心の注意を払うようになったが、同時にこういったコンテキストスイッチをうまく処理する方法を探し始めた。

そして見つけたのがGit Worktreeだ。

Git Worktreeとは?

Worktree(またはworking tree)とは、作業中のファイルが置かれたディレクトリのこと――VS Codeやターミナルで毎日開くあのフォルダだ。通常、一つのリポジトリに対してworking treeは一つしかない。

Git Worktreeを使うと、同じリポジトリにサブworking treeを追加でき、それぞれ別のブランチをチェックアウトできる。すべて同じ.gitフォルダを共有し――履歴もオブジェクトデータベースも共通――作業ファイルだけが完全に分離される。

イメージとしては、同じGoogle Docsを二つのブラウザタブで開いている感じ。それぞれ別のバージョンを見ているが、元データは一つ、という状態だ。

他の方法との簡単な比較

  • git stash:一時保存はできるが、ブランチを切り替える必要があり、コンテキストが失われる。大規模プロジェクトで何十回もstashすると混乱してくる。
  • リポジトリを新たにクローン:容量が倍になる――node_modulesが500MBあればすぐ倍増。さらに依存関係の再インストールも必要。
  • Git Worktree:作業ディレクトリとブランチは独立しているが、.gitは共有――速くて、コンパクトで、重複なし。

実践:Git Worktreeの使い方

1. 現在のworktreeを確認する

どのリポジトリにも最低一つのworktree――今作業しているディレクトリ――が存在する。以下のコマンドで確認できる:

git worktree list

出力はこのようになる:

/home/user/myproject  abc1234 [main]

2. hotfix用の新しいworktreeを作成する

最初の状況に戻ろう:feature/paymentにいる状態で、mainバグを直す必要がある。stashする代わりに、新しいworktreeを作成する:

# ../myproject-hotfixディレクトリを作成し、hotfix/critical-bugブランチをチェックアウト
git worktree add ../myproject-hotfix hotfix/critical-bug

ブランチがまだ存在しない場合は、-bフラグで同時に作成できる:

git worktree add -b hotfix/critical-bug ../myproject-hotfix main

このコマンドはmainから新しいブランチhotfix/critical-bugを作成し、../myproject-hotfixディレクトリにチェックアウトする。

あとは別のターミナルを開いてcd ../myproject-hotfixすれば、自由に作業できる――元のターミナルはfeature/paymentのまま、一切影響を受けない。

3. hotfix完了後、後片付けをする

hotfixをコミット・プッシュしたら、サブworktreeを削除する:

# worktreeを削除(ディレクトリも一緒に削除される)
git worktree remove ../myproject-hotfix

# 未コミットのファイルがあっても強制削除したい場合:
git worktree remove --force ../myproject-hotfix

その後、残ったメタデータを整理する(定期的に実行推奨):

git worktree prune

4. 実践的なワークフロー:コード中にPRをレビューする

これが自分が最もよく使うユースケースだ。チームメイトがPRを送ってきてレビューが必要なのに、自分はまだコード途中。stashやゴミコミットをする代わりに:

# PR #42をレビューするためのworktreeを作成
git worktree add ../review-pr-42 origin/feature/teammate-branch

# 別のエディタやターミナルでそのディレクトリを開く
code ../review-pr-42

レビューが終わったら、エディタを閉じてworktreeを削除する:

git worktree remove ../review-pr-42

5. bare repoと組み合わせる――よりプロフェッショナルなセットアップ

マルチブランチで頻繁に作業するなら、bare repo形式でセットアップするのがおすすめだ――デフォルトのworking treeなしでクローンし、必要なブランチごとにworktreeを作成する:

# bareクローン
git clone --bare https://github.com/user/myproject.git myproject.git
cd myproject.git

# ブランチごとにworktreeを作成
git worktree add ../myproject-main main
git worktree add ../myproject-feature feature/payment
git worktree add ../myproject-staging staging

結果として以下のような構成になる:

myproject.git/          ← .gitフォルダ(bare)
myproject-main/         ← mainブランチ
myproject-feature/      ← feature/paymentブランチ
myproject-staging/      ← stagingブランチ

各ディレクトリは独立したworking treeだ。どのターミナルで作業しても、他のターミナルに影響を与えない。

注意点

  • 一つのブランチは一つのworktreeでしか使えないmainを二つのworktreeで同時にチェックアウトしようとすると、Gitはエラーを出す。これは理にかなっている――二つのプロセスが同じブランチに書き込むことによるコンフリクトを防ぐためだ。
  • node modulesやビルド成果物は共有されない:各worktreeは独立したディレクトリのため、必要に応じてそれぞれでnpm installやビルドをやり直す必要がある。
  • サブworktree内の.gitはフォルダではなくファイル:メインの.gitへのパスが書かれており、これがGitのworktree間のリンク管理の仕組みだ。

まとめ

Git Worktreeの優れた点は、すべての開発者が一度は経験したシナリオに正面から対処してくれることだ:作業中に別の仕事を割り込まれる場面。ゴミコミットをしたり、stashして忘れたり、重いリポジトリをまるごとクローンしたりする代わりに――コマンド一つで、クリーンな新しい作業ディレクトリが、正しいブランチで即座に用意できる。

実際に自分がよく使うのは、緊急hotfixとPRレビューの二つの場面だ。どちらも数秒のセットアップで済み、今やっている作業のコンテキストを壊さない。一度試せば、昔のようにstashして後から迷子になるやり方には戻れなくなるだろう。

毎日Gitを使っているのにWorktreeをまだ知らないチームがいれば、これは今年学ぶ価値のある機能だ――小さくて、追加インストール不要、なのに時間とストレスをかなり節約してくれる。

Share: