17 人が閲覧(直近 30 日) 自動化

Git Worktree(ワークツリー)とは|使い方・削除(remove/prune)とsubmodule併用の注意点

Git Worktree(ワークツリー)とは|使い方・削除(remove/prune)とsubmodule併用の注意点

git worktreeは、1つのリポジトリに作業ディレクトリ(ワークツリー)を複数ぶら下げ、別々のブランチを同時にチェックアウトできるGitの標準機能です。Git 2.5(2015年7月)で追加され、現在は add・list・lock・move・prune・remove・repair・unlock の8つのサブコマンドがあります。追加のツールは要りません。

この記事では、仕組み(.git ファイルと .git/worktrees)、add と list の使い方、検索の多い削除の手順(remove と prune の違い、ブランチの後始末、一括削除)、submodule を含むリポジトリで worktree を使うと何が起きるかを扱います。仕様はGit 2.55.0(2026年6月29日リリース)のgit-worktreeの公式マニュアルに、出力例はGit 2.50.1のものに合わせています。

まとめ:git worktreeの要点

  • worktreeは「同じリポジトリの別ブランチを、別ディレクトリで同時に開く」機能。コミット・ブランチ・設定は全worktreeで共有し、HEAD・indexや一部の参照などがworktreeごとに分かれる。
  • 作成は git worktree add ../hotfix(パス末尾の名前で新ブランチを作る)か git worktree add ../review 既存ブランチ。通常は、同じブランチを2か所で同時に開く操作をGitが拒否する。
  • 削除はディレクトリを直接消さず git worktree remove パス。先に rm -rf で消した場合は git worktree prune で管理情報を掃除する。どちらでもブランチは残るため、ブランチも不要になった場合だけ git branch -d ブランチ名 で削除する。
  • removeは未コミットの変更と未追跡ファイルがあると止まるが、.gitignoreで無視されたファイル(.env や node_modules)は確認なしで一緒に消える。
  • submoduleは別リポジトリを埋め込む仕組みで、worktreeとは目的が違う。併用すると新しいworktreeではsubmoduleが空のままで、初期化するとremoveには --force が必要になり、moveは使えなくなる。
  • 公式マニュアルはsubmoduleを持つ親リポジトリ(superproject)の複数チェックアウトを推奨していない。submoduleの多いリポジトリでは、削除・移動の制約を避けるため、cloneを分ける運用を勧める。

以下、仕組み→作成→確認→削除→submodule併用の順に、コマンドと出力例を示します。

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

通常のGitは、1つのリポジトリに作業ディレクトリが1つです。別のブランチを見るには git switch で中身を入れ替える必要があり、切り替えによって作業途中の変更が失われる場合は、先にコミットするか退避する必要があります。worktreeを使うと、ブランチごとに別のディレクトリを用意して、両方を開いたまま行き来できます。

公式マニュアルは、git init や git clone で作られた最初の作業ディレクトリを「main worktree」、git worktree add で追加したものを「linked worktree」と呼び分けています。main worktreeは1つ、linked worktreeは0個以上です。

git switch・stash・再cloneとの違いと使い分け

方法 同時に開けるブランチ 履歴データ 作業中の変更
git switch 1本 1つ 変更が失われる場合は退避が必要
git stash + switch 1本 1つ stashに退避
別ディレクトリにclone 制限なし cloneごとに複製 各cloneに残る
git worktree add worktreeの数だけ 全worktreeで共有 各worktreeに残る

数分で終わる確認ならswitchとstashで足ります。レビュー中のブランチでテストを回しながら自分のブランチで作業を続ける、本番障害の修正を割り込ませる、AIコーディングエージェントを複数ブランチで並列に動かす、といった「両方を開いたままにしたい」場面でworktreeが効きます。公式マニュアルの例も、散らかったリファクタリング途中の作業ツリーをstashで崩さずに一時worktreeで緊急修正を済ませ、終わったらremoveする流れです。

「worktreeとbranchの違い」もよく調べられていますが、両者は階層が違います。branchはコミットを指す名前で、worktreeはそのbranchを展開するディレクトリです。worktreeには新規または既存のbranchを割り当てられるほか、detached HEADでbranchを割り当てずにコミットを展開することもできます。

Git 2.5以降のサブコマンド追加時期

Gitのバージョン 追加・変更された内容
2.5(2015年7月) add と prune
2.7 list
2.10 lock と unlock
2.17(2018年4月) move と remove
2.21 未初期化submoduleならmove・removeが可能に
2.29 repair
2.31 listがprunableを表示、–verbose
2.42 add –orphan
2.48 相対パスでのリンク(–relative-paths)

Git 2.17より前の環境には remove がありません。古い解説に「ディレクトリを消してからpruneする」とだけ書かれているのはこのためです。各版の変更はGitのリリースノートで確認できます。手元のバージョンは git --version で確かめ、古ければGitの最新バージョンの確認とアップデート手順を参照して更新してください。

git worktreeの仕組み:.gitファイルと.git/worktreesの対応関係

linked worktreeの最上位には、ディレクトリではなく .git という1行のテキストファイルが置かれます。中身はmain worktree側の管理ディレクトリへのパスです。

$ cat /work/hotfix/.git
gitdir: /work/app/.git/worktrees/hotfix

$ ls /work/app/.git/worktrees/hotfix
HEAD  ORIG_HEAD  commondir  gitdir  index  logs  refs

$ cat /work/app/.git/worktrees/hotfix/gitdir
/work/hotfix/.git

.git/worktrees/名前/ の名前はパスの末尾から付き、重複すると test-next1 のように数字が足されます。gitdir が作業ディレクトリへの逆向きのリンク、commondir が共有部分(main worktreeの .git)の場所です。この双方向リンクがあるため、作業ディレクトリだけを消すと片側が宙に浮き、後述の prune が必要になります。

区分 対象
worktreeごとに別 HEAD、index、refs/bisect・refs/worktree・refs/rewritten
全worktreeで共有 コミット等のオブジェクト、ブランチ、タグ、config、hooks

ブランチが共有されるので、どのworktreeでコミットしても、ほかのworktreeから git log feature-x で見えます。bisectの状態はworktreeごとに独立しているため、2つのworktreeで別々のbisectを同時に進めることもできます。どちらのディレクトリが何を指しているかは git rev-parse で確かめられます。

$ cd /work/hotfix
$ git rev-parse --git-dir --git-common-dir
/work/app/.git/worktrees/hotfix
/work/app/.git

configは既定で全worktree共通です。sparse-checkoutのようにworktreeごとに設定を変えたい場合は git config extensions.worktreeConfig true を有効にし、git config --worktree で書き込みます。有効化前に共有configを確認し、設定済みの core.worktree と core.bare=true はmain worktreeの config.worktree へ移します。この拡張に対応していない古いGitからはリポジトリを開けなくなります。

git worktree addの使い方:新規ブランチ・既存ブランチ・detached HEAD

# パス末尾の名前(hotfix)で新しいブランチを作って開く
git worktree add ../hotfix

# ブランチ名と起点を指定して作る
git worktree add -b feature-x ../feature-x main

# 既存ブランチを別ディレクトリで開く(レビュー・テスト用)
git worktree add ../review feature-y

# ブランチを作らずにコミットだけ展開する(ビルド確認・検証用)
git worktree add -d ../try v2.1.0

パスだけを渡すと、パス末尾と同じ名前のブランチがあればそれを開き、無ければHEADから新しく作ります。パスに続けてブランチ名を指定し、そのブランチがローカルになく、同名のリモート追跡ブランチがちょうど1つのリモートにある場合は、--track -b 相当で追跡ブランチが作られます。-B は同名の既存ブランチを起点に付け替えて作り直すため、pushしていないコミットがあるブランチには使わないでください。

置き場所は、リポジトリの外(上の例の ../ のような兄弟ディレクトリ)を勧めます。リポジトリの中に worktrees/in のように作ると、main worktreeの git status に ?? worktrees/ と未追跡で出続けるため、.gitignoreへの追記が必要になります。

worktreeに展開されるのは追跡対象のファイルだけです。.env、node_modules、ビルド成果物はコピーされないので、新しいworktreeでは依存のインストールや環境変数ファイルの用意をやり直します。

「is already used by worktree」エラーと同一ブランチの制約

$ git worktree add ../x main
Preparing worktree (checking out 'main')
fatal: 'main' is already used by worktree at '/work/app'

既定では、1つのブランチを2つのworktreeで同時にチェックアウトしようとすると拒否されます。linked worktreeの中で git switch main しても同じエラーになります。Git 2.42より前は「is already checked out at」という文言でした。

対処は、別ブランチを切る(-b)か、コミットだけ見られればよいなら -d でdetached HEADにするかのどちらかです。--force で制約を外すこともできますが、同じブランチを2か所で進めると片方のHEADの前提が崩れるため、実務では使いません。

git worktree listで状態を確認する方法

$ git worktree list
/work/app     ab063ac [main]
/work/hotfix  ab063ac [hotfix]
/work/try     ab063ac (detached HEAD)
/work/wt2     ab063ac [f2] prunable

$ git worktree list --verbose
# 以下はwt2の表示部分のみ抜粋
/work/wt2     ab063ac [f2]
	prunable: gitdir file points to non-existent location

先頭行が必ずmain worktreeです。末尾の locked はロック中、prunable はディレクトリが消えていて prune の対象になることを示します。--verbose を付けると理由が次の行に出ます。

スクリプトで扱うなら git worktree list --porcelain を使います。1属性1行で、公式マニュアルは「Gitのバージョンやユーザー設定にかかわらず安定」と明記しています。パスに改行を含む可能性まで考えるなら -z を併用します。

git worktreeの削除:removeとpruneの使い分けとブランチの後始末

状況 使うコマンド
不要になったworktreeを消す git worktree remove パス
未コミットの変更・未追跡ファイルがある 中身を確認して remove –force
lock中 unlock してから remove
ディレクトリを先に手で消した git worktree prune
ブランチも消したい remove の後に git branch -d

git worktree removeが止まる条件とforce指定の判断

$ git worktree remove ../wt2
fatal: '../wt2' contains modified or untracked files, use --force to delete it

removeは作業ディレクトリと .git/worktrees の管理情報をまとめて消します。止まるのは、追跡ファイルに変更があるか、未追跡ファイルがあるときです。git -C ../wt2 status で中身を確かめ、残す必要が無いと判断してから --force を付けます。ロック中のworktreeは --force を2回(-f -f)付けない限り消せません。main worktreeはremoveの対象外です。

注意したいのは、.gitignoreで無視されているファイルはこの確認に引っかからない点です。node_modules/ を無視設定にしたリポジトリで、worktreeの中に node_modules を置いたまま remove すると、無視対象のファイルもディレクトリごと削除されます。worktreeにだけ置いた .env やローカルの検証データは、remove の前に退避してください。

worktreeの指定は、パスの末尾が一意なら末尾だけで足ります。/work/hotfix なら git worktree remove hotfix で消せます。

rm -rfで消した後のgit worktree prune

エクスプローラーや rm -rf で先にディレクトリを消すと、.git/worktrees 側の管理情報だけが残ります。listで prunable と表示される状態です。

$ git worktree prune -n -v
Removing worktrees/wt2: gitdir file points to non-existent location
$ git worktree prune -v
Removing worktrees/wt2: gitdir file points to non-existent location

-n は何を消すかの表示だけで、実際には消しません。管理情報を残したまま同じパスに作り直そうとすると、次のエラーで止まります。

fatal: '../same' is a missing but already registered worktree;
use 'add -f' to override, or 'prune' or 'remove' to clear

pruneを手で打たなくても、git gc が実行されると git worktree prune --expire 3.months.ago も呼ばれ、3か月より古い管理情報が掃除されます。gcが走らなければ、3か月を過ぎても残ります。期間は gc.worktreePruneExpire で変えられます。USBドライブやネットワーク共有に置いたworktreeが、未接続の間にこの掃除で管理情報ごと消されるのを防ぐのが git worktree lock --reason "USB" パス です。

removeしても残るブランチとgit branch -dの順序

remove も prune も、worktreeに割り当てていたブランチは消しません。ブランチまで片付けるには、worktreeを消した後に git branch -d ブランチ名 を実行します。-d は、上流ブランチ(未設定なら現在のHEAD)に取り込まれていないコミットがあるブランチを消さないので、先にマージ状況を確かめます。worktreeを消す前に実行した場合は、次のエラーで止まります。

$ git branch -d feature
error: cannot delete branch 'feature' used by worktree at '/work/app-feat'

もう1つの落とし穴がdetached HEADのworktreeです。-d で作ったworktreeでコミットしてからremoveすると、そのコミットはどのブランチからもたどれなくなり、git fsck --lost-found で「dangling commit」として見つかるだけの状態になります。残したいコミットは、remove の前に git branch 名前 でブランチを付けてください。

linked worktreeをまとめて削除するスクリプト

# main worktree(先頭)以外をすべて remove する
git worktree list --porcelain \
  | sed -n 's/^worktree //p' \
  | tail -n +2 \
  | while IFS= read -r p; do git worktree remove "$p"; done
git worktree prune

porcelain出力の worktree 行からパスを抜き出し、先頭のmain worktreeを飛ばして1つずつremoveします。空白を含むパスでも動きますが、改行を含むパスには対応しません。削除に失敗したworktreeは残り、処理は次のworktreeへ進むので、一覧を見直してから個別に --force を判断します。最初から --force を付けて回すと、未コミットの変更や未追跡ファイルも消えます。通常のブランチ上にコミット済みの変更は、未pushでも残ります。

git worktreeとsubmoduleの違いと併用時の挙動

worktreeとsubmoduleの目的と管理単位の違い

項目 worktree submodule
扱う対象 同じリポジトリの別ブランチ 別のリポジトリ
主な目的 複数ブランチの並行作業 依存先を特定コミットで固定
設定の置き場所 .git/worktrees(ローカルのみ) .gitmodules(コミットされる)
他メンバーとの共有 されない clone時に共有(初期化は必要)

worktreeは個人の作業環境の話で、リポジトリの履歴には何も残りません。submoduleは親リポジトリに「どのリポジトリの、どのコミットを使うか」を記録する依存管理の仕組みです。似た名前のsubtreeは、別リポジトリの中身を親の履歴へ取り込む方式で、こちらもworktreeとは用途が重なりません。

実務で問題になるのは、submoduleを含むリポジトリでworktreeを使う場合です。以下はGit 2.50.1で、submodule libs/lib を持つリポジトリでの操作を説明する出力例です。

新規worktreeのsubmodule未初期化と初期化手順

$ git worktree add ../app-feat -b feature
$ cd ../app-feat
$ git submodule status
-17e5b7d7cfcfbe9e90e2db39c7de3740855b3d77 libs/lib

先頭の - は未初期化の印で、libs/lib は空のディレクトリです。git -c submodule.recurse=true worktree add としても初期化されず、addに --recurse-submodules オプションもありません(Git 2.55のマニュアルにも無し)。新しいworktreeごとに初期化します。

$ git submodule update --init --recursive
Submodule path 'libs/lib': checked out '17e5b7d7cfcfbe9e90e2db39c7de3740855b3d77'
$ cat libs/lib/.git
gitdir: ../../../app/.git/worktrees/app-feat/modules/libs/lib

submoduleのリポジトリ本体は .git/worktrees/app-feat/modules/ の下に、worktreeごとに別々に作られます。main worktreeの .git/modules は共有されないため、submoduleの数×worktreeの数だけcloneが走り、容量と時間がかかります。大きなsubmoduleを抱えるリポジトリでworktreeを量産すると、ここが最初に効いてきます。

初期化済みsubmoduleを含むworktreeの削除・移動制限

$ git worktree remove ../app-feat
fatal: working trees containing submodules cannot be moved or removed
$ git worktree move ../app-feat ../app-feat2
fatal: working trees containing submodules cannot be moved or removed

公式マニュアルも、removeについて「submoduleを含むworktreeは --force で削除できる」、moveについて「submoduleを含むlinked worktreeは移動できない」と定めています。Git 2.21からは未初期化のsubmoduleは無視されるので、初期化していないworktreeなら通常どおり消せます。

初期化済みのworktreeを消すときは、本体とsubmoduleの両方で変更がpush済みかを確認してから git worktree remove --force ../app-feat を実行します。.git/worktrees/app-feat ごと、その下のsubmoduleのリポジトリも消えます。場所を変えたい場合、moveが使えないので、作り直すほうが確実です。submoduleの .git ファイルは相対パスで管理ディレクトリを指しているため、手で動かすと階層がずれてリンクが切れます。

post-checkoutフックによるsubmodule初期化の自動化

毎回の git submodule update --init を忘れないようにするには、post-checkoutフックが使えます。githooksのマニュアルによると、このフックは git worktree add の後にも(--no-checkout でない限り)呼ばれ、第1引数に「null-ref」、つまりゼロだけのコミットIDが渡されます。

#!/bin/sh
# .git/hooks/post-checkout
# 第1引数が0だけのとき(worktree add・clone直後)だけ実行する
case "$1" in *[!0]*) exit 0 ;; esac
git submodule update --init --recursive

既定ではhooksを共有するため、main worktreeの .git/hooks/post-checkout に保存し、main worktreeで chmod +x .git/hooks/post-checkout を実行します。既存フックがある場合は処理を統合し、core.hooksPath を設定している場合はその指定先を使います。ゼロ以外の文字を含む場合は抜ける書き方にしているのは、SHA-1(40桁)とSHA-256(64桁)のどちらのリポジトリでも動かすためです。通常の git switch では第1引数が直前のコミットIDになるので、ブランチ切り替えのたびに走ることはありません。

git worktreeを使わないほうがよい場面

submoduleの多いsuperprojectでは、worktreeを使わずcloneを分けます。公式マニュアルのBUGS節は「複数チェックアウトは全般にまだ実験的で、submoduleのサポートは不完全。superprojectの複数チェックアウトは推奨しない」と書いています。worktreeでも親リポジトリの履歴とブランチは共有されますが、submoduleの履歴はworktreeごとに複製され、初期化済みのworktreeは --force なしでは削除できず、moveでの移動はできません。公式が推奨しない構成でこの制約を抱えるより、cloneを分けるほうを選びます。

依存のインストールに時間がかかるプロジェクトも、worktreeの利点が薄れます。node_modules やビルドキャッシュはworktreeごとに別なので、worktreeを作るたびにインストールをやり直すことになります。pnpmのように共有ストアを持つパッケージマネージャーか、準備込みで数日使い続ける運用でないなら、割り込み作業はstashで済ませたほうが速い場面もあります。

自作のスクリプトやツールが .git/hooks のようにパスを直接組み立てている場合は、先に直します。linked worktreeでは .git がファイルなので、そのパスは存在しません。公式マニュアルは git rev-parse --git-path hooks で実際のパスを得るよう求めています。

よくある質問

git worktreeからpushできますか?

できます。ブランチとリモートの設定は全worktreeで共有されているので、linked worktreeの中で git push -u origin feature-x を実行すれば、通常のリポジトリと同じようにpushされます。pushしたブランチは、main worktreeからも git log origin/feature-x で見えます。worktreeごとにリモートを設定し直す必要はありません。

作ったworktreeの場所を移動するには?

git worktree move 移動元 移動先 を使います(Git 2.17以降)。main worktreeと、submoduleを初期化したworktreeはこのコマンドで動かせません。エクスプローラーや mv で手動移動してしまった場合は、移動後のworktreeの中で git worktree repair(Git 2.29以降)を実行すると、管理情報とのリンクが張り直されます。

VS CodeやVisual Studio、JetBrains製IDEでworktreeは使えますか?

使えます。linked worktreeは中身が普通のディレクトリなので、そのフォルダを開けば各IDEのGit機能がそのworktreeのブランチを対象に動きます。作成・削除までGUIで行いたい場合は、Git worktreeの基本コマンドとGUIツールでの操作や、worktree対応が入ったGitHub Desktop 3.6の変更点を参照してください。

Claude CodeなどのAIエージェントでworktreeを使うのはなぜですか?

複数のエージェントを同時に動かすと、同じ作業ディレクトリのファイルを取り合って変更が混ざります。エージェントごとにworktreeを分ければ、ファイルは別々で、コミットとブランチは共有されるため、終わった順にマージできます。Claude Codeの --worktree を使った並列開発の手順はClaude Codeのgit worktreeによる並列エージェント開発で解説しています。

worktreeを削除するとコミットも消えますか?

ブランチ上のコミットは消えません。コミットは全worktreeで共有されるオブジェクトなので、removeで消えるのは作業ディレクトリと管理情報だけです。例外はdetached HEADのworktreeでブランチを付けずにコミットした場合で、remove後はどこからもたどれなくなり、いずれ git gc で回収されます。残す場合はremoveの前にブランチを作ってください。

関連記事

お気に入りに入れた記事の一覧

この記事は以下の記事からリンクされています

ほか 4 件の記事からもリンクされています。

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2024.07.11 テックブログ PEP8とは?Pythonコーディング規約の基本ルールとチェックツール(Ruff対応)
  2. 2026.09.28 テックブログ タイムズカーの不正アクセスと免許証画像160万件の流出|退会者まで残さない保管設計
  3. 2026.09.30 テックブログ OpenAI Dotsとは?常時稼働エージェントの権限設計と自社システム接続【2026年9月】
  4. 2024.08.22 テックブログ Bokehとは?Pythonでインタラクティブなグラフを作る使い方【3.10対応】
  5. 2026.09.27 コラム 法定調書合計表とは?令和8年分の書き方と提出義務、給与・支払データからの集計自動化

RELATED POSTS 関連記事

目次