コンテンツにスキップ

gitワークツリーとブランチを分けることの違い

概要

Gitにおける「ワークツリー (worktree)」とは、Gitリポジトリの内容をローカルファイルシステム上に展開し、作業を行うためのディレクトリ群を指します。これには、バージョン管理下のファイル群だけでなく、.git ディレクトリ(リポジトリ本体)も含まれます。一方、「ブランチ (branch)」とは、コミットへのポインタであり、プロジェクトの異なる開発ラインを表すものです。通常、一つのGitリポジトリは一つのワークツリーを持ち、そのワークツリーは特定のブランチの最新コミットの状態を反映しています。

「gitワークツリーとブランチを分ける」という表現は、主に git worktree コマンドが提供する機能に関連します。このコマンドを使用することで、単一のGitリポジトリに対して複数の独立したワークツリーを作成し、それぞれのワークツリーで異なるブランチを同時にチェックアウトして作業を進めることが可能になります。これは、通常の git checkoutgit switch コマンドを使ってブランチを切り替える際に、既存のワークツリーの内容が上書きされる挙動とは根本的に異なります。

注目される背景

従来のGitのワークフローでは、複数のタスクやブランチでの作業を並行して行う場合、以下のような課題がありました。

  1. コンテキストスイッチのコスト: あるブランチから別のブランチに切り替える際(git checkout または git switch)、Gitはワークツリー内のすべてのファイルやディレクトリを切り替えるブランチの状態に合わせて更新します。この操作は、未コミットの変更がある場合にはコミットまたはスタッシュする必要があり、またファイルシステムの更新に時間がかかることがあります。
  2. 並行作業の難しさ: 例えば、ある機能ブランチで作業中に緊急のバグ修正ブランチに切り替える必要がある場合、現在の作業を中断し、変更をコミットまたはスタッシュしてからブランチを切り替える必要がありました。これは、頻繁にブランチを切り替える必要がある開発者にとって、効率の低下を招きます。
  3. レビューの困難さ: 複数のブランチでの変更を同時に確認したり、比較したりする際に、いちいちブランチを切り替える手間がありました。

git worktree コマンドは、これらの課題を解決し、開発者がより柔軟かつ効率的に複数のタスクを並行して進められるようにするために登場しました。

核心的な考え方

git worktree の核心的な考え方は、「単一のGitリポジトリ(オブジェクトデータベース、履歴)を共有しながら、複数の独立した作業ディレクトリ(ワークツリー)を持つ」という点にあります。これにより、各ワークツリーは異なるブランチをチェックアウトした状態を維持できるため、開発者はブランチを切り替えることなく、複数のブランチ上での作業を同時に進めることができます。

仕組み・詳細

従来のGitのワークツリーとブランチの関係

通常、Gitリポジトリは一つの .git ディレクトリと、その .git ディレクトリの隣にある一つの作業ディレクトリ(ワークツリー)から構成されます。

プロジェクトルート/
├── .git/               <-- リポジトリ本体 (オブジェクト、参照、履歴)
│   ├── objects/
│   ├── refs/
│   ├── HEAD (現在チェックアウトしているブランチへのポインタ)
│   └── ...
└── (作業ディレクトリ)  <-- 現在のブランチの状態を反映したファイル群
    ├── src/
    └── README.md

このモデルでは、git checkout <branch-name> コマンドを実行すると、.git/HEAD が新しいブランチを指すように更新され、同時に作業ディレクトリ内のファイル群が新しいブランチの状態に合わせて変更されます。

git worktree を使用したワークツリーとブランチの関係

git worktree コマンドを使用すると、メインの .git ディレクトリとは別に、追加の作業ディレクトリを作成できます。これらの追加のワークツリーは、それぞれ独自の HEAD、インデックス (index)、および参照 (refs) を持ちますが、全てのオブジェクトデータ(コミット、ツリー、ブロブ)はメインの .git ディレクトリを共有します。

メインリポジトリ/
├── .git/                       <-- メインのリポジトリ本体
│   ├── objects/                <-- 全ワークツリーで共有されるオブジェクト
│   ├── refs/                   <-- 全ワークツリーで共有される参照
│   ├── worktrees/              <-- 各追加ワークツリーの情報
│   │   └── <worktree_id>/      <-- 追加ワークツリーごとの独立した情報 (HEAD, index, refs)
│   └── ...
├── (メインの作業ディレクトリ)    <-- 例えば 'master' ブランチをチェックアウト
│   └── src/
│   └── README.md
└── (追加の作業ディレクトリ1)   <-- 例えば 'feature-A' ブランチをチェックアウト
    ├── .git                    <-- メインの .git/worktrees/<id> を指すファイル
    └── src/
    └── README.md
└── (追加の作業ディレクトリ2)   <-- 例えば 'bugfix-B' ブランチをチェックアウト
    ├── .git                    <-- メインの .git/worktrees/<id> を指すファイル
    └── src/
    └── README.md

git worktree のコマンド例

  1. 新しいワークツリーを追加する: 特定のブランチをチェックアウトした新しいワークツリーを作成します。

    # メインリポジトリのディレクトリ内で実行
    # 'worktrees/feature-A' という新しいディレクトリに 'feature-A' ブランチをチェックアウト
    git worktree add ../worktrees/feature-A feature-A
    
    • ../worktrees/feature-A: 新しいワークツリーを作成するパス。
    • feature-A: 新しいワークツリーでチェックアウトするブランチ名。存在しない場合は新しいブランチとして作成されます。
  2. 既存のワークツリーを一覧表示する:

    git worktree list
    

    出力例:

    /path/to/main-repo      HEAD (master)
    /path/to/worktrees/feature-A d1e2f3g4 (feature-A)
    /path/to/worktrees/bugfix-B  h5i6j7k8 (bugfix-B)
    

  3. ワークツリーを削除する: 不要になったワークツリーを削除します。物理的なディレクトリも削除されます。

    git worktree remove ../worktrees/feature-A
    

    または、物理ディレクトリを先に削除してから、Gitリポジトリから情報を削除することもできます。

    rm -rf ../worktrees/feature-A
    git worktree prune # 不要になったworktree情報を削除
    

関連手法・技術との比較

比較項目 通常の git checkout / git switch git worktree コマンド 複数のリポジトリをクローン
ワークツリーの数 1つ (メインのリポジトリディレクトリ) 複数 (メイン + 追加ワークツリー) 複数 (各クローンごとに1つ)
リポジトリの共有 完全な共有 (同じ作業ディレクトリ内でブランチを切り替える) オブジェクトデータベース (.git/objects) を共有。各ワークツリーは独自の HEAD, index, refs を持つ。 各クローンが独立した .git ディレクトリを持つため、共有なし
ブランチ切り替え ワークツリーの内容が変更される。未コミットの変更はスタッシュ/コミットが必要。 各ワークツリーは特定のブランチに固定され、ワークツリー内でブランチ切り替えは発生しない (別のブランチで作業したい場合は新しいワークツリーを追加)。 各クローン内でブランチ切り替えを行う。
ディスク使用量 最小限 (オブジェクトは共有) 作業ファイルは複製されるが、オブジェクトデータベースは共有されるため、クローンよりは効率的。 各クローンが完全なリポジトリを持つため、最も多くディスクを使用。
コンテキストスイッチ 高い (ファイルの変更、IDEの状態リフレッシュなど) ほぼゼロ (別ディレクトリで作業するため) 低い (別ディレクトリで作業するため)
利用シナリオ 単一のタスク、頻繁なブランチ切り替えが少ない場合 複数のタスクを並行して進める、長期的な機能ブランチと緊急修正、レビュー作業 完全に独立した開発環境が必要な場合、CI/CD環境

メリット

  • コンテキストスイッチの削減: ブランチを切り替える際のファイルシステムの更新やIDEのリロードが不要になります。これにより、開発者は中断なく異なるタスク間で移動できます。
  • 複数のタスクの並行処理: 異なるブランチでの作業を同時に進めることができます。例えば、メイン機能開発中に別のワークツリーで緊急のバグ修正を行ったり、プルリクエストのレビューを行ったりできます。
  • レビューの容易化: 他の開発者のプルリクエストブランチを自分のワークツリーの一つにチェックアウトし、自分の作業を中断することなくコードをテスト・レビューできます。
  • 実験的なブランチでの作業: 新しいアイデアを試すための実験的なブランチを簡単に作成し、メインの作業を邪魔することなく試すことができます。
  • 開発効率の向上: 開発者の生産性を高め、マルチタスクをよりスムーズに行えるようになります。

課題・注意点

  • ディスクスペースの消費: 各ワークツリーは、バージョン管理下のファイルのコピーを保持するため、リポジトリのサイズによってはディスクスペースを消費します。ただし、Gitオブジェクト自体は共有されるため、完全にリポジトリをクローンするよりは効率的です。
  • 管理の複雑さ: 複数のワークツリーを管理することになり、どのワークツリーでどのブランチを扱っているのかを把握する必要が出てきます。git worktree list コマンドで確認する習慣をつけることが重要です。
  • ワークツリーの削除忘れ: 不要になったワークツリーを削除し忘れると、ディスクスペースを無駄にしたり、混乱の原因になったりする可能性があります。
  • ホック (hooks) の挙動: 一部のGitフック(特に pre-push など)は、メインの .git ディレクトリに対して実行されるため、追加のワークツリーからプッシュした際の挙動に注意が必要です。
  • 特定のIDEとの連携: IDEによっては、複数のワークツリーを同時に開いて作業する際に、プロジェクト設定やGit連携機能の扱いが異なる場合があります。

代表的なツール / 実装例

git worktree は、Git自体が提供する標準機能です。したがって、追加のツールは不要で、Gitがインストールされていればどの環境でも利用できます。

  • Git コマンドライン: 上記の git worktree add, git worktree list, git worktree remove などが基本的な操作です。
  • Git GUI クライアント: 一部の高機能なGit GUIクライアント(例: GitKraken, Sublime Merge, Fork)では、git worktree の機能がUIから操作できるようになっている場合があります。

参考URL