プログラミング
Magit で Git Worktree を使う
Working with Git Worktrees in Magit (emacsredux.com)
要約
この記事では、Gitのworktree機能と、それをEmacsのMagitエディタでどのように活用するかを解説しています。worktreeは、同じリポジトリに対して複数の作業ディレクトリを持つことを可能にし、AIコーディングエージェントの利用や、ブランチ間の切り替えに伴うビルド状態の差異による手間を省くのに役立ちます。Magitはworktreeをシームレスにサポートしており、特別な学習なしに各worktreeで独立した操作が可能です。
全文翻訳
最近まで、git worktreeというものが存在することを知りませんでした。それはgitに10年間も搭載されていた機能ですが、一度も使う必要がありませんでした。フィーチャーブランチで十分だったのです。ブランチを作成し、作業を行い、マージし、削除して、また始めるという流れです。worktreeを知るきっかけとなったのは、意外にもAIコーディングエージェントでした。Claude Codeのようなツールは、タスクごとにworktreeを作成するため、複数のエージェント(あるいは同じエージェントの複数のセッション)が、互いに、あるいはあなたに干渉することなく、同じリポジトリで並行して作業できます。突然、私のプロジェクトディレクトリはcider-thisやcider-thatといった兄弟のようなものでいっぱいになり、そこで何が起きているのか理解する必要があると思いました。
worktree 対 ブランチ
ブランチはコミットへの移動可能なポインタに過ぎず、作成は基本的に無料です。ただし、リポジトリには単一の作業ディレクトリしかなく、2つのブランチで作業するにはそのディレクトリを切り替える必要があります。おなじみの手順です。半端な作業をスタッシュ(またはコミット)し、別のブランチをチェックアウトし、作業を行い、最初のブランチを再度チェックアウトし、アンスタッシュします。これは機能しますが、面倒であり、2つのブランチがプロジェクトを異なるビルド状態にする場合、切り替えごとに世界の半分を再コンパイルする必要が生じ、さらに悪化します。
worktreeは、同じリポジトリに接続された追加の作業ディレクトリを提供します。
$ git worktree add ../cider-smart-targeting -b smart-form-targeting
これで~/projects/cider-smart-targeting はそのブランチの完全なチェックアウトになりますが、メインのチェックアウトは元の場所にそのまま残ります。オブジェクトデータベース、参照、スタッシュ、リモートはすべて共有されます。worktreeはクローンではないため、一方でのフェッチはすべてでフェッチされ、作成はほぼ瞬時に行われます。各worktreeは独自のHEADとインデックスを持ち、gitは1つの単純なルールを強制します。1つのブランチは、一度に1つのworktreeでしかチェックアウトできません。
いつ使う価値があるのか?
2つのことが同時に起こる必要があるときです。一方のブランチで長時間テストを実行しながら、もう一方のブランチで作業する。半端な作業を邪魔せずにPRをレビューする。あるいは、最近話題になっている理由ですが、AIエージェントが孤立した状態で作業を行う場合です。
その代償はかなり控えめです。作業ファイルはディスク上に複数存在し、gitで追跡されていないもの(依存関係、ビルドキャッシュ、node_modulesなど)は、各worktreeで再度設定する必要があります。ブランチがこれまであなたにとって制限になったことがないなら、それはそれで構いません。私も10年以上そうでした。worktreeは、ワークフローが変わるまで必要性を感じない機能の一つです。
Jujutsuについては?
作業コピーの話をしているついでに、バージョン管理で今最も興味深いのはJujutsu(jj)です。これはgit互換のVCSで、worktreeが解決する問題をほとんどなくします。jjでは、作業コピーはコミットであり、作業中に自動的にスナップショットが取られます。ステージングエリアもスタッシュもなく、失われたり邪魔になったりする可能性のある未コミットの状態がないため、コンテキストの切り替えは常に安全です。また、並列作業ディレクトリ(jj workspace)の適切なサポートと、事実上すべてを元に戻せる操作ログも備えています。この最後の点は、AIエージェントの群衆がそれに興味を持っている理由の一部でもあります。
既存のgitリポジトリの上にjjを使用できます(これをコロケーションリポジトリと呼びます)。そうすれば、同僚やMagitは通常のgitリポジトリを見続けることになります。私はまだ観察者ですが、これは明らかに注目すべきプロジェクトです。
Magit での worktree
Emacsの世界に戻りましょう。Magitは長年worktreeをサポートしており、Zキーコマンドの背後に隠されています。
キー コマンド 説明
Z b magit-worktree-checkout 新しいworktreeで既存のブランチをチェックアウトする
Z c magit-worktree-branch 一度に新しいブランチとworktreeを作成する
Z g magit-worktree-status 別のworktreeのステータスバッファにジャンプする
Z m magit-worktree-move worktreeを移動する
Z k magit-worktree-delete worktreeを削除する
最良の部分は、他に学ぶべきことがないことです。各worktreeは独自のMagitステータスバッファを持ち、すべてのMagitコマンドは現在のバッファが属するworktreeに対して操作されます。Z gはあなたが必要とする唯一の切り替えメカニズムであり、それすらも別のステータスバッファを訪問するためのショートカットに過ぎません。
私の(明らかに最近の)経験からのいくつかの実用的なヒント:
デフォルトでは、ステータスバッファはworktreeをリストしません。これを修正するには:
(magit-add-section-hook 'magit-status-sections-hook #'magit-insert-worktrees nil t)
これで、すべてのステータスバッファがリポジトリのすべてのworktreeを表示し、いずれかにRETを入力するとそこにジャンプできます。
worktreeをメインのチェックアウトの兄弟として、ネストするのではなく、説明的な名前(ciderの隣にcider-smart-targeting)で作成します。ネストするとgrep、find、その他の多くのツールが混乱します。
Magitのブランチ選択は、別のworktreeでチェックアウトされているブランチにworktreeのパスを注釈付けし、再度チェックアウトすることを拒否します(これはgitのルールであり、Magitが難しくしているわけではありません)。ブランチが「チェックアウトできない」理由を不思議に思ったことがあるなら、それが通常の原因です。
各worktreeは、project.el(またはProjectile)にとって独自のプロジェクトであるため、プロジェクト切り替え、バッファごとのプロジェクト、検索がすべて自然に機能します。
gitで追跡されていないものは何も一緒に運ばれません。worktreeごとに依存関係を1回インストールし、コールドビルドキャッシュから開始する必要があります。Elispの場合は何もコストがかかりませんが、大規模なJVMまたはJSプロジェクトの場合は、このアプローチ全体の主な欠点です。
worktreeが不要になったら、Z k(またはコマンドラインからgit worktree remove)で削除します。手動でディレクトリを削除した場合、git worktree pruneが残ったブックキーピングをクリーンアップします。
終わりに
最近、エージェントとのワークフローは通常次のようになります。エージェントがworktreeで作業を行い、私はMagitでそこで変更を確認します(しばしば別のworktreeで別のタスクが実行されている間に)。そして、ブランチがマージされるとworktreeは削除されます。いつかworktreeの他の用途を見つけるかもしれません。どうなるか見てみましょう。
あなたはgit worktreeを使用していますか?そして、私と同じような回り道でそれらを発見しましたか?コメントでぜひ聞かせてください!
今日はここまでです。ハッキング(並行して)を続けてください!
それらはgit 2.5で導入されました。これは2015年7月にリリースされたものです。↩ « Meet Utterson, my Jekyll blogging helper