🙌

Git Worktreeをわかりやすく解説

に公開

プログラマーなら一度は経験したことがあるのではないでしょうか?作業中に急な修正依頼が来て、今の作業を中断して別のブランチに切り替えなければならない状況を。

これまではgit stashを使ったり、作業を一時的にコミットしたりして対応していたかもしれませんが、Git Worktreeを使えば解決することができます。
また、並行開発するときにも便利な機能で、Claude Codeを使う場合にも有効です。
チュートリアルにも記載されています。

Git Worktreeとは?

Git Worktreeは、同じリポジトリに複数の作業ディレクトリ(working tree)を持つことができる機能です。簡単に言うと、一つのGitリポジトリから複数のフォルダを作成し、それぞれで異なるブランチを同時に作業できるということです。

従来の方法では、一つのリポジトリで一つのブランチしかチェックアウトできませんでした。別のブランチで作業するには、現在の変更をstashしたりコミットしたりしてからブランチを切り替える必要がありました。

なぜGit Worktreeが便利なのか?

実際の使用例1:緊急修正への対応

リファクタリング作業の最中に、上司から緊急の修正依頼が来たとします。通常ならgit stashを使って変更を一時保存しますが、作業ツリーが混乱状態(新規ファイル、移動ファイル、削除ファイルなどが散らばっている)だと、何かを壊してしまうリスクがあります。そんな時、緊急修正用の一時的なlinked worktreeを作成し、修正完了後に削除して元のリファクタリング作業に戻ることができます。

シナリオの詳細

あなたは現在、feature/refactoringブランチで大規模なコードリファクタリングを行っています。ファイルの移動、関数名の変更、新機能の追加など、まだ途中の作業がたくさんあります。このとき、本番環境で重要なバグが発見され、今すぐ修正する必要が生じました。

通常の対応だと、現在の作業をすべてstashして、mainブランチに切り替え、修正作業を行い、再びfeature/refactoringブランチに戻ってstashを復元する必要があります。しかし、作業が複雑で複数のファイルにまたがっている場合、この作業は非常に面倒で、ミスが起こりやすくなります。

Worktreeを使った解決方法

  1. 現在の作業をそのまま残しておく
  2. 緊急修正用の新しいフォルダを作成:git worktree add -b hotfix/critical-bug ../emergency main
  3. ../emergencyフォルダに移動して修正作業を実施
  4. 修正をコミットしてmainブランチにマージ
  5. git worktree remove ../emergencyで緊急修正フォルダを削除
  6. 元のフォルダで即座にリファクタリング作業を継続

この方法なら、元の作業に一切影響を与えることなく緊急対応が可能です。

実際の使用例2:複数機能の並行開発

シナリオの詳細

Webアプリケーションの開発で、以下の3つの機能を同時に開発する必要があるとします。

  • ユーザー認証システム(大きな機能、2週間の開発期間)
  • 商品検索機能(中規模機能、1週間の開発期間)
  • 緊急バグ修正(小さな修正、数時間で完了)

従来の方法では、一つの機能を開発中に別の機能に取り掛かる際、必ずブランチの切り替えが必要でした。しかし、開発中のコードが不安定な状態だと、コミットしたくない場合もあります。

Worktreeを使った解決方法

# メインディレクトリ:緊急修正や小さな作業用
# ユーザー認証システム用のworktree
git worktree add ../auth-system feature/user-authentication

# 商品検索機能用のworktree  
git worktree add ../search-feature feature/product-search

これにより、3つの独立した作業環境が手に入ります。それぞれのフォルダで独立して作業でき、コミットのタイミングも自由に決められます。また、同時にテストを走らせることも可能です。

実際の使用例3:バージョン比較とデバッグ

シナリオの詳細

本番環境で問題が発生し、「前のバージョンでは動いていたのに、最新版で動かなくなった」という状況に遭遇したとします。問題の原因を特定するために、現在の開発版と過去の安定版を並行して調査したいケースです。

従来の方法では、コミットハッシュやタグを切り替えながら調査する必要があり、比較作業が非常に困難でした。

Worktreeを使った解決方法

# 現在の開発版:メインディレクトリで作業
# 過去の安定版用のworktree
git worktree add ../stable-version v2.1.0

# 問題が発生したバージョン用のworktree
git worktree add ../problem-version v2.2.0

これにより、3つの異なるバージョンのコードを同時に開き、ファイルを並べて比較できます。デバッガーやテストツールも各バージョンで同時実行でき、問題の特定が格段に効率化されます。

基本的な使い方

よく使う基本パターン

1. 新しいブランチを自動作成して作業開始

git worktree add ../feature-name

これは最もシンプルな使い方です。feature-nameという新しいブランチが自動的に作成され、../feature-nameフォルダにチェックアウトされます。フォルダ名がそのままブランチ名になるのが特徴です。

2. 既存のブランチで作業開始

git worktree add ../work-folder existing-branch

すでに存在するexisting-branchを新しいフォルダ../work-folderにチェックアウトします。他の人が作成したブランチで作業を始めたい場合や、以前作業していたブランチに戻りたい場合に使います。

3. 明示的にブランチ名を指定して作成

git worktree add -b new-branch-name ../folder-name

フォルダ名とブランチ名を別々に指定したい場合に使います。new-branch-nameという新しいブランチが作成され、../folder-nameフォルダにチェックアウトされます。現在のブランチ(通常はmain)から分岐します。

4. 特定のコミットから新しいブランチを作成

git worktree add -b new-branch ../folder-name main

mainブランチからnew-branchを作成し、../folder-nameにチェックアウトします。特定のブランチやコミットから新しい作業を始めたい場合に使います。

5. 実験用の一時的な作業環境

git worktree add --detach ../experiment

ブランチに紐付かない実験用の環境を作成します。ちょっとした検証や実験を行いたいけれど、ブランチを作るほどでもない場合に便利です。作業後は簡単に削除できます。

Worktreeの状態確認

現在のworktreeを一覧表示

git worktree list

出力例

/path/to/main-project        abc1234 [main]
/path/to/feature-work        def5678 [feature/new-feature]
/path/to/experiment          abc1234 (detached HEAD)

詳細情報付きで表示

git worktree list --verbose

より詳しい情報(ロック状態、削除可能かどうかなど)が表示されます。

Worktreeの削除

通常の削除

git worktree remove ../folder-name

作業が完了したworktreeを削除します。未保存の変更がある場合は削除が拒否されます。

強制削除

git worktree remove --force ../folder-name

未保存の変更があっても強制的に削除します。使用には注意が必要です。

基本的なメンテナンス

不要な管理ファイルのクリーンアップ

git worktree prune

手動で削除されたworktreeの管理ファイルを自動的にクリーンアップします。定期的に実行すると良いでしょう。

高度な機能

Worktreeのロック機能

ポータブルデバイスやネットワーク共有上にworktreeがある場合、常にマウントされているとは限りません。そのような場合、管理ファイルが自動的に削除されるのを防ぐためにロックできます。

git worktree lock --reason "USBドライブ上のため一時的に利用不可" ../portable-work

Worktreeの修復機能

メインworktreeやlinked worktreeを手動で移動した場合、接続が切れてしまうことがあります。そんな時はrepairコマンドで接続を復旧できます。

git worktree repair ../moved-worktree

リファレンスの共有

複数のworktreeを使用する場合、一部のrefは全worktree間で共有されますが、他のrefは個別のworktreeに固有です。一例として、HEADは各worktreeで異なります。

一般的に、すべての疑似refはworktree毎に個別で、refs/で始まるすべてのrefは共有されます。ただし、refs/bisect、refs/worktree、refs/rewrittenの中のrefは共有されません。

注意点と制限

複数チェックアウト機能は基本的には安定していますが、サブモジュールのサポートは不完全です。特にスーパープロジェクト(サブモジュールを含むプロジェクト)での複数チェックアウトは推奨されていません。

主な制限事項

  1. 同じブランチの重複チェックアウト: 通常、同じブランチを複数のworktreeでチェックアウトできません(--forceで回避可能だが非推奨)

  2. サブモジュールの制限: メインworktreeやサブモジュールを含むlinked worktreeはmoveコマンドで移動できません

  3. 削除の制限: クリーンでないworktree(追跡されていないファイルや変更されたファイルがある)は通常削除できません(--forceオプションが必要)

  4. メインworktreeの制限: メインworktreeは削除できません

ベストプラクティス

  1. 定期的なクリーンアップ: git worktree pruneで不要な管理ファイルを定期的に削除
  2. 適切な命名: worktreeのパス名は用途がわかりやすいものにする
  3. ロック機能の活用: 一時的に利用できないworktreeは適切にロック
  4. --forceの慎重な使用: 強制オプションは必要最小限に留める

まとめ

Git Worktreeは、複数の作業を並行して進める必要がある開発者にとって非常に強力なツールです。緊急対応が必要な場面や、複数のブランチで同時作業が必要な場合に、作業の中断を最小限に抑えて効率的に開発を進めることができます。

基本的な使い方をマスターするだけでも、日常の開発作業が格段に効率化されるはずです。

Git Worktreeを上手に活用して、より効率的な開発ワークフローを構築してみてください。

参考資料

Git - git-worktree Documentation

追記

「もっと快適に!」を追求した結果、筆者は以下のように使っています。

プロジェクトディレクトリ内で完結したい

Git Worktreeを使ってて感じたのが、前述のやり方だと、プロジェクトディレクトリ外にディレクトリやファイルを作成してしまい、散らばるのがすごく嫌でした。そのため最近では.git内で完結するよう以下のようにコマンドを使用しています。

# 例:feature-branchという名前のworktreeを作る場合
git worktree add .git/worktrees/feature-branch feature-branch

# または、新しいブランチを同時に作る場合
git worktree add .git/worktrees/new-feature -b new-feature

メリットとしては以下が挙げられます。

  • .gitディレクトリ内なので、プロジェクトの外に散らばらない
  • gitignoreが不要
  • すべてのworktreeが一箇所にとどまる
  • プロジェクト外に散らばらない!(これがホント快適)

コマンド打つのめんどくさい

stashに慣れてしまっているというのもあるのですが、やはり入力がめんどくさい。特に前述の.git内に完結させるやり方をしようとするとさらにめんどくさい。
まぁこういうときの常套手段としてはエイリアスですね。筆者は以下のエイリアスを作って使用しています。

基本コマンドである、add,list,removeを.git/worktrees/をベースのディレクトリに設定し、addはオプションを使えるようにラップしました。

以下を~/.gitconfigに追加。

[alias]
    # worktree addのラッパー(.git/worktrees/に固定)
    wta = "!f() { \
        branch=\"${@: -1}\"; \
        path=\".git/worktrees/$branch\"; \
        args=\"${@:1:$#-1}\"; \
        if [ \"$1\" = \"-b\" ] || [ \"$1\" = \"-B\" ]; then \
            git worktree add $args \"$branch\" \"$path\"; \
        else \
            git worktree add $args \"$path\" \"$branch\"; \
        fi; \
    }; f"

    # 削除
    wtr = "!f() { \
        git worktree remove ${2:+--force} .git/worktrees/$1; \
    }; f"

    # 一覧
    wtl = worktree list

使い方

# 基本的な使い方(既存ブランチ)
git wta feature-x
# → git worktree add .git/worktrees/feature-x feature-x

# 新しいブランチを作成
git wta -b new-feature
# → git worktree add -b new-feature .git/worktrees/new-feature

# 一覧表示
git wtl

# 削除
git wtr feature-x

IDEを立ち上げるのは、そのままの流れで、コマンドで実施

code -n .git/worktrees/feature-branch

至極快適w

ぜひご参考に!!

さらに追記

しばらく.gitディレクトリ内でgit worktreeを運用していました、ある時git worktree listを実行すると以下のような表示が……

prunable……

これはworktreeが削除可能な状態になっているということです。
しかし筆者としては作業中の状態でした。

保存しているディレクトリを参照すると、見事にファイルが削除されていました……

.gitディレクトリは、Gitがリポジトリのメタデータ(コミット履歴、ブランチ情報など)を管理するための内部的な場所です。

そのためこの場所に保存すると、Gitは内部構造のディレクトリやファイルと混同してしまい、不必要なデータだとして削除してしまったのです。

そうであれば、プロジェクト内にworktree用のディレクトリを作成して、そこに保存しようと考えるでしょう。しかしこれもあまりおすすめできません。Git自体はうまく機能していそうですが、他のツールでトラブルを引き起こす可能性があるそうです。(筆者はそのトラブルを経験していませんでしたが、安全に使いたいため、この構成をやめました。)

Gitの公式が推奨している兄弟ディレクトリに作成するようにして、その親のディレクトリで工夫するのが良さそうです。

ということで今は.gitに作るのではなく、兄弟ディレクトリに作成するエイリアスに変更しています。

[alias]
    # worktree追加(ブランチ名だけ指定)
    wta = "!f() { \
        branch=\"$1\"; \
        safe_branch=$(echo \"$branch\" | sed 's/\\//-/g'); \
        path=\"../$safe_branch\"; \
        if git show-ref --verify --quiet \"refs/heads/$branch\"; then \
            git worktree add \"$path\" \"$branch\"; \
        else \
            git worktree add -b \"$branch\" \"$path\"; \
        fi; \
    }; f"
    
    # ブランチ名で削除(パスを探して削除)
    wtr = "!f() { \
        branch=\"$1\"; \
        worktree_path=$(git worktree list --porcelain | grep -B2 \"branch refs/heads/$branch\" | grep \"worktree\" | cut -d' ' -f2); \
        if [ -n \"$worktree_path\" ]; then \
            git worktree remove ${2:+--force} \"$worktree_path\"; \
        else \
            echo \"Worktree for branch '$branch' not found\"; \
        fi; \
    }; f"
    
    # 一覧表示
    wtl = worktree list

Discussion