git worktreeでブランチ切り替えの手間を減らす並行開発の基本操作と運用の工夫

git worktreeでブランチ切り替えの手間を減らす並行開発の基本操作と運用の工夫

公開: 2026/09/27
更新: 2026/11/05
目次
開閉

機能開発に集中している最中、Slackで「至急この不具合の再現確認をお願いできますか?」「別ブランチのPRを出したので手元で動作を見てほしい」と声をかけられる。Web開発の現場では、避けて通れない日常茶飯事の光景です。

こうした割り込み作業が入ったとき、多くの現場で取られているのが git stash で作業中の変更を一時退避し、目的のブランチへ git switch(または git checkout)する方法でしょう。

しかし、この手順には地味ながら無視できないコストが潜んでいます。

  • ブランチを行き来するたびに node_modules や依存パッケージの再インストールが発生する
  • ローカルの開発サーバーやDockerコンテナの再起動、ビルドキャッシュの破棄で数分〜十数分が奪われる
  • 用件を終えて元のブランチに戻り git stash pop を叩いた瞬間、退避したことを忘れていたファイルと衝突してコンフリクトする

作業のコンテキストが強制的にリセットされ、「さっき自分はどのファイルのどこまで手を付けていたんだっけ?」と思い出すまでの認知負荷は、開発のリズムを大きく削ぎ落とします。

こうした切り替え摩擦を根本から解消してくれるのが、Git標準機能(Git 2.5以降で導入)である git worktree です。

本稿では、特別な外部ツールを導入することなく、Git標準のコマンド(git-worktree ドキュメント)だけで「複数ブランチを別ディレクトリとして同時に展開し、並行開発を進める」ための基本操作と、現場で破綻させないための実践的な運用の工夫を整理します。※なお、本稿で紹介するブランチ切り替え操作は、Git 2.23以降で推奨されている git switch の併用を前提に解説しています(動作検証環境:Git 2.45+ / macOS & Windows 11 PowerShell 7)。


そもそも git worktree とは何か?

通常、Gitリポジトリを git clone すると、プロジェクトのルートディレクトリに .git という隠しフォルダが作られ、作業ディレクトリ(ワーキングツリー)と1対1で結びつきます。そのため、1つのディレクトリの中では「常に1つのブランチ」しかチェックアウトできません。

これに対して git worktree は、1つの .git ディレクトリ(オブジェクトデータベースや履歴情報)を共有したまま、複数の作業ディレクトリをローカルの別の場所に展開できる機能 です。

# 通常運用(1つの作業ディレクトリ)
my-project/
├── .git/          (履歴やブランチ情報を一元管理)
└── (作業ファイル群)  ← ここでブランチを都度切り替える(要stash・再ビルド)

# git worktree 運用(作業ディレクトリの並行展開)
my-project/             (メイン作業ツリー: mainブランチ)
├── .git/
└── (mainブランチの作業ファイル群)

my-project-hotfix/      (追加worktree: hotfixブランチ)
├── .git (ファイル)     ← 親の .git への参照ポインタ
└── (hotfixブランチの作業ファイル群)

追加されたworktreeの直下にある .git はディレクトリではなく、親リポジトリへのパスが1行だけ書かれたプレーンテキストファイルです。実際に手元で中身を確認してみると、その仕組みがよく分かります。

# 追加worktree直下の .git ファイルの中身を確認(PowerShell / bash 共通)
cat ../my-project-hotfix/.git

# 出力結果(親リポジトリ内の個別作業領域を指している)
gitdir: /Users/username/develop/my-project/.git/worktrees/my-project-hotfix

親リポジトリ側の .git/worktrees/my-project-hotfix/ には、そのworktree専用の HEAD や index(ステージング状態)のみが小さく保存されます。巨大なコミット履歴やオブジェクトデータベース(objects/)は親のものをそのまま直接共有するため、ストレージを消費せず、一瞬で新しいブランチを展開できるのです。

「別名で git clone する」との決定的な違い

「作業ディレクトリを分けたいだけなら、別のフォルダに新しく git clone すればいいのでは?」と思うかもしれません。

しかし、単純な別名クローンにはいくつかの明確なデメリットがあります。

  1. ストレージ容量の無駄遣い: プロジェクトの全履歴やコミットオブジェクトが丸ごと複製されるため、巨大なリポジトリではディスクを無駄に圧迫します。
  2. 履歴やローカルブランチが分断される: 別個のリポジトリ扱いになるため、手元で作成した未pushのブランチをもう一方から参照するには、一度リモートへpushするか手動で同期する手間が発生します。
  3. リモートへの不要な通信: 最新のコミットを取得するために、それぞれのクローン先で個別に git fetch を実行しなければなりません。

git worktree であれば、裏側のデータ実体は1つの .git を参照している ため、ディスク容量を最小限に抑えられます。さらに、メイン側で git fetch すれば追加した全worktreeで即座に最新リモートブランチが使えますし、手元で切ったローカルブランチも何の手間もなく相互にチェックアウト可能です。


基本のコマンド操作:これだけ覚えれば実務で動かせる

git worktree の操作体系は非常にシンプルです。日常の作業で必要になる基本コマンドは以下の4つに集約されます。

1. 既存のブランチを別ディレクトリに展開する

急ぎのレビューや動作確認などで、すでに存在するブランチを別ディレクトリにチェックアウトしたい場合は、git worktree add を使います。

# 書式: git worktree add <展開先パス> <既存ブランチ名>
git worktree add ../my-project-review origin/feature/user-auth

このコマンドを実行すると、1つ上の階層に my-project-review ディレクトリが生成され、指定したブランチの内容が展開されます。

2. 新規ブランチを切りながら別ディレクトリに展開する

「現在作業中のブランチはそのまま残し、別の緊急修正用ブランチを新しく切って作業したい」という場合は、-b オプションを付与します。通常の git switch -c と同じ感覚です。

# main から派生させた新ブランチ hotfix/login-bug を別ディレクトリに展開
git worktree add -b hotfix/login-bug ../my-project-hotfix main

これで、メインの作業ディレクトリは何一つ触ることなく、独立した my-project-hotfix ディレクトリでクリーンな状態から修正に着手できます。

3. 現在の worktree 一覧を確認する

今どのディレクトリにどのブランチが紐付いているかは、git worktree list でいつでも確認できます。

git worktree list

実行すると、以下のように一覧が表示されます。

/Users/username/develop/my-project          a1b2c3d [main]
/Users/username/develop/my-project-hotfix   e4f5g6h [hotfix/login-bug]
/Users/username/develop/my-project-review   7i8j9k0 [feature/user-auth]

一番上がメインのリポジトリで、それ以降が git worktree add によって追加された追加ツリーです(WindowsのPowerShellで実行した場合も C:/Users/username/... のように正規化された絶対パスで一覧表示されます)。

4. 用件が済んだ worktree を安全に削除する

修正をコミット・pushし、PRの作成やレビューが終わったら、不要になったworktreeを片付けます。

通常の rm -rf でフォルダごと消すのではなく、専用の git worktree remove コマンドを使うのが原則です。

# 書式: git worktree remove <worktreeのパス>
git worktree remove ../my-project-hotfix

このコマンドを実行すると、作業ディレクトリが削除されると同時に、親の .git/worktrees/ 配下に残っていた管理用メタデータも綺麗に掃除されます。

もし作業ディレクトリ内に未コミットの変更や未追跡ファイルが残っている場合は、Gitが自動で以下のように検知して削除をブロックしてくれます。

fatal: '../my-project-hotfix' contains modified or untracked files, use --force to delete it

誤って作業中のコードを吹き飛ばす心配がない設計になっている点も、手動フォルダ削除にはない大きなメリットです(本当に破棄して構わない場合のみ --force を指定します)。

Lowom 編集長 編集長
TIPS 手動でフォルダを消してしまった場合の対処
VS CodeやFinder等で直接ディレクトリをゴミ箱に入れてしまった場合、Gitの管理情報に古い参照が残ってしまいます。その時はリポジトリ内で `git worktree prune` を叩けば、存在しないディレクトリのメタデータが一括で安全にクリーンアップされます。

現場で破綻しない「ディレクトリ配置」の工夫

git worktree を導入したエンジニアが最初に悩むのが、「追加する作業ディレクトリをどこに配置するか」という運用のルール作りです。

自由に適当な場所へ作っていると、デスクトップや親フォルダにプロジェクト名のディレクトリが散乱し、どれが本尊でどれが使い捨てなのか分からなくなります。実務では、以下の2つのパターンのいずれかに揃えるのがおすすめです。

パターンA: 親ディレクトリに並べる「兄弟並列型」 (筆者推奨)

メインリポジトリと同じ階層に、接尾辞を付けて並べる構成です。

develop/
├── my-project/          ← メイン(常に最新の main または develop)
├── my-project-hotfix/   ← 緊急修正用
└── my-project-review/   ← PRレビュー用
  • メリット: ディレクトリ階層が浅く、ターミナルでの移動やVS Codeの起動(code ../my-project-hotfix)が直感的に行える。
  • 実務でこれを選ぶ理由: 私自身のチームでも長年この構成に落ち着いています。後述のサブディレクトリ型に比べ、各種ビルドスクリプトやCIの相対パス解決で事故が起きにくく、不要になった際のディレクトリ削除も迷わず行えるためです。
  • 運用のコツ: 親フォルダが散らからないよう、レビューや修正が終わったらその都度 git worktree remove で片付ける習慣を徹底します。

パターンB: リポジトリ内に隠す「サブディレクトリ集約型」

メインリポジトリの内部に .worktrees/ というディレクトリを掘り、その中にすべてを収める構成です。

my-project/
├── .git/
├── .worktrees/               ← すべての追加ツリーをここに集約
│   ├── hotfix/
│   └── review/
├── src/
└── package.json

この構成を採用する場合は、親リポジトリの .gitignore に必ず除外設定を追加 してください。

# .gitignore
.worktrees/
  • メリット: プロジェクト関連のファイルが単一のディレクトリ配下に収まるため、フォルダ全体の把握がしやすい。
  • 現場での注意点: 以前この構成を試した際、VS Codeやエディタの全体検索、ファイル変更監視(File Watcher)、Dockerのバインドマウントが .worktrees/ の中まで走査してしまい、CPU負荷が跳ね上がったりキャッシュが二重生成されたりするトラブルがありました。採用する場合はエディタの search.exclude や .dockerignore への指定が必須となります。

実務で直面する注意点と回避テクニック

日々の現場で git worktree を快適に運用するために、あらかじめ知っておくべき仕様と注意点が3つあります。

1. 「同一ブランチの二重チェックアウト」は禁止されている

もっとも遭遇しやすいエラーがこちらです。

fatal: 'main' is already checked out at '/Users/username/develop/my-project'

Gitは安全のため、複数のworktreeでまったく同一のブランチを同時にチェックアウトすることを許可していません。仮に2箇所から同じブランチへ同時にコミットされると、HEADのポインタ整合性が保てなくなるためです。

Lowom 編集長 編集長
CAUTION 同じブランチを見たいときの回避策
「メインでmainを開いているけれど、追加worktreeでもmainの最新状態をそのままビルド検証したい」という場合は、追加側では `git worktree add -d <パス> main` のように detached HEAD(ブランチポインタを持たない特定コミットの状態)として展開するか、一時的な検証用ブランチ(`test/main-check` など)を切って展開してください。

2. .env や非Git管理ファイルの引き継ぎ

git worktree add で新しく作られたディレクトリは、Gitで管理されているファイルのみが展開された「まっさらな状態」です。

そのため、ローカル開発に必要な環境変数ファイル(.env や .env.local)、各種ツールのローカル設定など、.gitignore されているファイルは自動的には引き継がれません。

この問題に対しては、以下のいずれかの方法で手早く整えるのが実務的です。

  • 方法1: 1行コマンドでコピーする (OS問わず最も確実)
    # macOS / Linux / WSL
    cp ../my-project/.env ../my-project-hotfix/.env
    
    # Windows PowerShell の場合
    Copy-Item ../my-project/.env ../my-project-hotfix/.env
  • 方法2: シンボリックリンクを貼る (Mac / Linux / WSL)
    ln -s ../my-project/.env ../my-project-hotfix/.env
    ※すべてのworktreeで同一のローカルDBやAPIキーを参照したい場合は、シンボリックリンクにしておくと更新の手間が省けます。ただしWindowsのネイティブ環境(cmd / PowerShell)では、シンボリックリンクの作成に開発者モードの有効化や管理者権限が必要となるケースがあります。OS混在のチーム開発で手順をドキュメント化する場合は、方法1の単純コピーを案内しておくのが最もトラブルが起きにくい現場の知恵です。

3. VS Codeなどのエディタは「別ウィンドウ」で開く

作業ディレクトリが分かれたら、エディタも作業ごとに独立したウィンドウで開くのがもっとも快適です。

VS Codeを使っている場合、既存のウィンドウに追加ワークスペースとして読み込ませる(マルチルートワークスペースにする)と、Gitレンズや言語サーバー(TypeScript / ESLint等)のプロセスが混ざり、型チェックやインデックス作成が混乱することがあります。

# 新しいウィンドウとして独立して起動する
code -n ../my-project-hotfix

ターミナルから -n オプション(新しいウィンドウで開く指定。詳細は VS Code Command Line Interface (CLI) を参照)を付けて開けば、完全に独立した環境として作業でき、修正が終わればそのウィンドウを閉じるだけで頭の切り替えが完了します。

なお、複数リポジトリやディレクトリの行き来をターミナルから素早く切り替えたい場合は、過去記事で紹介した「ghq × fzf」によるリポジトリ管理や、Gitのfixupとautosquashによる履歴整理、Rustのビルド待ち時間を削る高速リンカとsccacheの実践レシピとも相性が良いです。


思考のメモリを消費しない開発フローをつくる

「作業を一時退避するためにstashして、戻ってきたらpopして、衝突したら解消して……」という手順は、一つひとつの操作自体は数分で済むかもしれません。

しかし、「今の作業状態を壊したくない」「stashの中身が何だったか忘れないようにしなきゃ」という心理的なプレッシャーは、確実にエンジニアの集中力を削り取ります。

git worktree の本質的な価値は、単にコマンドの実行時間を節約すること以上に、「物理的にディレクトリを分けることで、自分の頭の中のワーキングメモリを一切汚さずに作業を並行できること」 にあります。

  • メイン作業はそのまま温存しておく
  • 割り込み作業は別ディレクトリでサクッと片付けてPRを出す
  • 用が済んだらディレクトリごと安全に破棄する

Gitが最初から備えているこの仕組みを取り入れるだけで、日々の割り込みタスクに対する身軽さは大きく変わります。次に「ちょっと別ブランチを見てほしい」と頼まれた際は、ぜひ git stash ではなく git worktree add を試してみてください。

Lowom 編集長
この記事を書いた人:Lowom 編集長 現役Webエンジニア

都内IT企業に勤める業界20年のWebエンジニア。業務効率化・自動化スクリプトや快適な開発環境の構築、厳選したツール・ガジェットの活用法など、手元で実際に検証したリアルな一次情報をお届けします。