git worktreeを使ってみたらFlutterでもとっても便利だった!

git worktreeを使ってみたらFlutterでもとっても便利だった!

2026.04.09

こんにちは。きんくまです!
リテールアプリ共創部マッハチーム所属です!

今回はgit worktreeについて書きます!

はじめに。こんなことに困ってました!

ふだん案件開発のお仕事をしているときに、こんなことがありとても困っていました。

  • 手動テストアプリ書き出し作業をやりつつ、PRレビューと、開発作業を並行してやりたい
  • PRレビューは複数同時にやりたい
  • 開発作業も複数同時にやりたい
  • 今まではgit checkoutで、単一ディレクトリ上でブランチを切り替えていた
  • でもFlutterの場合はriverpodなどのコード生成が毎回必要
  • ブランチを切り替えるたびに実行するので、作業開始までの時間がたくさんかかっていた
  • できれば、全部並行してすすめたい

そんなときにgit worktreeを知りました。すでに使っている方もいるかもしれませんが、自分はちょっと前に知って、とても便利だったので紹介します!

git worktreeとは

ざっくりいうと、ひとつのリポジトリから複数の作業ディレクトリを作れる機能です。

通常、gitリポジトリはひとつのディレクトリにひとつのブランチがチェックアウトされています。
git worktreeを使うと、作業用の複数のディレクトリにそれぞれ別のブランチをチェックアウトできます。

ポイントとしては:

  • .gitの実体はひとつだけ。worktreeディレクトリはそこを参照する軽量なコピー
  • それぞれのディレクトリごとにブランチがひもづいて完結しているので、他のディレクトリの状態に影響を受けない
  • worktreeディレクトリは、元のディレクトリの下に作ってもいいし、横に並べて作ってもOK
  • worktreeを一度作れば、あとはいつも通りのcheckoutによるブランチ切り替えがいつでも可能です
  • 例えばそのディレクトリで予定していた作業が終わったら、worktreeを作り直さずに別のブランチを割り当てることができます。ディレクトリを使い回せるので便利です

基本コマンド

# worktreeを作成(既存ブランチ)
git worktree add <> <ブランチ>

# worktreeを作成(新規ブランチ)
git worktree add -b <新規ブランチ> <> <ベースブラン>

# worktree一覧を確認
git worktree list

# worktreeを削除
git worktree remove <>

実際にこんな感じでやってます

自分はFlutterのモバイルアプリ開発で使っています。ディレクトリ構成はこんな感じです。

sample-project/
├── sample-repository/  ← 本体(メインのリポジトリ)
└── worktrees/          ← worktree置き場
    ├── task/           ← 汎用開発作業用
    ├── task2/          ← 汎用開発作業用2
    ... 複数task
    ├── review/         ← 汎用レビュー用
    ├── review2/        ← 汎用レビュー用2
    ... 複数review
    └── feature_xxxx-xxxx/  ← 大規模な専用開発タスク

本体とworktreeを横に並べる構成にしています。こうすると、本体ディレクトリの中にworktreeを作るのに比べて、それぞれのディレクトリ単体で独立するのでわかりやすく、並行作業がとてもやりやすいです。

Claude Codeのスキルで自動化しています

worktreeの作成・セットアップを毎回手動でやるのは大変なので、Claude Codeのスキルを作って自動化しています。

create-worktreeスキル

まずworktreeを作る create-worktreeスキル です。

create-worktreeスキル(SKILL.md)
.claude/skills/create-worktree/SKILL.md
---
name: create-worktree
description: Git worktreeを指定ディレクトリに作成し、シンボリックリンクとセットアップを行います。
---

# Git Worktree 作成

指定ディレクトリに git worktree を作成し、必要なシンボリックリンクの作成と
Flutterセットアップを一括実行するスキルです。

## 定数

- **Worktree ベースディレクトリ**: `/path/to/project/worktrees/`
- **元リポジトリ**: `/path/to/project/sample-repository`

## 実行手順

### ステップ 1: ブランチの確認

AskUserQuestion を使って、新規ブランチか既存ブランチかをユーザーに確認する。

**新規ブランチの場合:**
- ブランチ名を入力してもらう
- ベースブランチを確認する(develop / HEAD / 任意)

**既存ブランチの場合:**
- ブランチ名を入力してもらう
- ローカル / リモートの指定を判別する

### ステップ 2: Worktree の作成

ブランチ名の `/``_` に変換したものをディレクトリ名とする。

**新規ブランチの場合:**
git worktree add -b <ブランチ名> /path/to/project/worktrees/<ディレクトリ名> <ベースブランチ>

**既存ブランチの場合:**
git worktree add /path/to/project/worktrees/<ディレクトリ名> <ブランチ名>

### ステップ 3: .claude ディレクトリのシンボリックリンク化

worktree には git で管理されている `.claude` ディレクトリがチェックアウトされているため、
削除してシンボリックリンクに置き換える。

# 1. .claude 内の全ファイルに assume-unchanged を設定
git ls-files .claude | xargs git update-index --assume-unchanged

# 2. git 管理の .claude を削除
rm -rf .claude

# 3. 元リポジトリの .claude へのシンボリックリンクを作成
ln -s /path/to/project/sample-repository/.claude .claude

### ステップ 4: シンボリックリンクの作成

# private_docs ディレクトリ
ln -s /path/to/project/sample-repository/private_docs private_docs

### ステップ 5: ビルドに必要なファイルのコピー

元リポジトリから以下のファイルをコピーする。
- .vscode/launch.json
- .vscode/tasks.json
- .mcp.json

### ステップ 6: ビルドツールの設定

# FVM 設定
fvm use <version>

# Flutter クリーン&依存取得(pod install に必要な Generated.xcconfig を生成するため)
fvm flutter clean
fvm flutter pub get

# Gemfile から CocoaPods をインストール
bundle config set --local path 'vendor/bundle'
bundle install

# iOS の CocoaPods 依存をインストール
cd ios && bundle exec pod install && cd ..

# Flutter Doctor で環境確認
flutter doctor

### ステップ 7: Flutter セットアップ

Skill ツールで `/flutter-post-checkout-setup` を実行する。

remove-worktreeスキル

次に、worktreeを削除する remove-worktreeスキル です。

remove-worktreeスキル(SKILL.md)
.claude/skills/remove-worktree/SKILL.md
---
name: remove-worktree
description: Git worktreeとそのブランチを削除します。
---

# Git Worktree 削除

指定した git worktree を削除し、オプションでローカルブランチも削除するスキルです。

## 定数

- **Worktree ベースディレクトリ**: `/path/to/project/worktrees/`
- **元リポジトリ**: `/path/to/project/sample-repository`

## 実行手順

### ステップ 1: 削除対象の選択

元リポジトリで `git worktree list` を実行し、メインリポジトリ以外の worktree を一覧表示する。
AskUserQuestion で削除対象を選択してもらう。

### ステップ 2: 変更の確認

削除対象の worktree で `git status --short` を実行し、未コミットの変更を確認する。
変更がある場合はユーザーに確認を取る。

### ステップ 3: Worktree の削除

git worktree remove --force <worktree パス>

`--force` は、シンボリックリンクやビルド成果物(untracked files)があるため必須。

### ステップ 4: ブランチの削除確認

ローカルブランチも削除するかユーザーに確認する。
削除する場合は `git branch -D <ブランチ名>` を実行する。

flutter-post-checkout-setupスキル

あとは、worktreeとは関係ないのですが、ブランチを切り替えたときに毎回呼び出して、ビルド状態を初期化する flutter-post-checkout-setupスキル です。

工夫した点は、シェルスクリプトにするのではなく、毎回 tasks.jsonなどを読み取ることによって、その時の最新のコマンドが実行されることです。(シェルスクリプトにすると、tasks.jsonを書き換えたら、このスキルも同期をとらないといけなくなる)

flutter-post-checkout-setupスキル(SKILL.md)
.claude/skills/flutter-post-checkout-setup/SKILL.md
---
name: flutter-post-checkout-setup
description: Gitブランチ切り替え後のFlutterプロジェクト初期化を一括実行します。
---

# Flutter Post-Checkout Setup

Git のブランチ切り替え(checkout)後に必要な Flutter プロジェクトの
初期化・再セットアップを一括で実行するスキルです。

## tasks.json からのコマンド取得

ステップ 4〜8 のコマンドは `.vscode/tasks.json` から動的に取得する。

## 実行手順

### ステップ 1: Flutter Clean

fvm flutter clean

### ステップ 2: パッケージ取得

fvm flutter pub get

### ステップ 3: iOS Pod インストール

bundle install
cd ios && bundle exec pod install && cd ..

### ステップ 4: ローカライゼーションファイル生成

tasks.json のラベル "Scripts: Generate Localization Files" のコマンドを取得して実行する。

### ステップ 5: Pigeon ファイル生成

tasks.json のラベル "Flutter: Generate Pigeon File" のコマンドを取得して実行する。

### ステップ 6: Build Runner(コード生成)

fvm dart run build_runner clean
fvm dart run build_runner build

### ステップ 7: Androidのみビルド

`.vscode/launch.json` から設定を読み取ってコマンドを組み立てる。
(次のライセンススクリプトを実行するための android/gradlew を作成するため)

### ステップ 8: ライセンス収集

tasks.json のラベル "Scripts: Collect Licenses" のコマンドを取得して実行する。

注意点

worktreeを使うにあたって、少し工夫が必要だったポイントをパラパラと書いていきます。

private_docsディレクトリのシンボリックリンク

AI用にコンテキストをためているprivate_docsディレクトリを本体リポジトリ直下に作ってます。
これは.gitignore指定のgitで管理されていないファイルです。なので、worktreeにはコピーされません。

そのため、シンボリックリンクを貼って共有しています。

cd worktrees/<ディレクトリ>
ln -s /path/to/sample-repository/private_docs private_docs

.claudeディレクトリの扱い

.claudeディレクトリはプロジェクトとしてgit管理されています。
ですが、個人で作成したスキルもたくさん入っています。
これらのスキルは本体側で .git/info/exclude で gitignoreされてます。

worktreeを作成して、ブランチをチェックアウトすると、git管理されたものだけが作成されて、個人でgitignoreされているスキルは入ってきません。

これらの管理をどうするか考えたのですが、以下のようにしました。

まず、作成された .claudeディレクトリは一度削除してしまいます。
そして、本体側からシンボリックリンクを貼ります。

cd worktrees/<ディレクトリ>

# .claude内の全ファイルに assume-unchanged を設定(gitが変更を追跡しなくなる)
git ls-files .claude | xargs git update-index --assume-unchanged

# git管理の.claudeを削除
rm -rf .claude

# 本体の.claudeへのシンボリックリンクを作成
ln -s /path/to/sample-repository/.claude .claude

git update-index --assume-unchangedがポイントです。これを設定すると、.claude内のファイルを削除してもgitが変更として検知しなくなります。gitignoreではなく、すでにgit管理されているファイルの変更を一時的に無視するためのコマンドですね。

ただしこの場合、シンボリックリンクの.claudegit statusにuntrackedファイルとして常に表示され続けます。いろいろ試したのですが、自分はこの状態でも気にしない運用にしました。

使用済みworktreeの見分け方

たくさん作業用の汎用worktreeを作ると、「このディレクトリはまだ使っているのか?」がわからなくなってきます。
(毎回専用worktreeを作っても良いのですが、worktreeのセットアップ自体もそれなりに手間なので汎用を使いまわしてます)

そこで、使い終わったworktreeはgit checkout --detachでブランチのひもづけを外すようにしています。

cd worktrees/<使い終わったディレクト>
git checkout --detach

こうすると、VSCodeのステータスバーに表示されるのがブランチ名ではなくコミットIDになります。

  • ブランチ名が表示されている → 作業中(レビューや開発)
  • コミットIDが表示されている → 作業にひもづいていない(削除してもOK)

という見分け方ができて便利です。

使ってみた感想

  • 複数の並行作業がいつでもできるので、気持ちがとっても楽になりました。便利〜
  • 開発中で何か別作業を依頼されたときに「いまのブランチの状態を切り替えちゃうことになるけど、作業途中のものをどうしよう?」という悩みがなくなりました
  • PRレビューのやりとりも、レビュー時のディレクトリはそのままにしておけます。なので、レビュー指摘した内容の反映が行われたら、pullしてくるだけ。すぐに反映確認ができます!

まとめ

Flutterのような、チェックアウト時に毎回時間のかかる初期設定が必要なプロジェクトには、worktreeはぴったりだと思いました!

ではでは。

この記事をシェアする

関連記事