0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

複数ブランチを効率的に管理する ― Git Worktreeと一括操作シェルの実践

0
Last updated at Posted at 2026-05-04

はじめに

以前、Qiita CLI × GitHub Actionsを使った記事管理フローを構築し、記事ごとにブランチを分けてGitHubで管理するようになりました。この運用に移行してから記事の変更履歴が追えるようになり、快適に執筆できています。

一方で、複数ブランチにまたがるタイトルやタグの一括修正をしたいときに、ブランチを一つずつ切り替えながらファイルを編集し、ステージング・コミット・pushを繰り返すのが手間になるという課題が出てきました。

この記事では、Git Worktreeを使って複数ブランチを同時展開し、一括でコミット・pushするまでの実践的な運用をまとめます。実際にハマったポイントも含めて整理しているので、同じような構成で運用している方の参考になれば幸いです。


全体のユースケース

本記事で扱うユースケースは以下の3つです。


1. Git Worktreeとは

通常のGitは1つのリポジトリに対して1つのワーキングツリー(作業フォルダ)しか持てません。別のブランチで作業するにはブランチを切り替える必要があります。

Git Worktreeを使うと、複数のブランチを別々のフォルダとして同時に展開できます。ブランチの切り替えなしに、複数ブランチのファイルを並行して編集できるようになります。

~/dev/
├── my-qiita-articles/     # メインリポジトリ(main)
└── qiita_worktree/
    ├── build01/           # article/20260601_build01 ブランチ
    ├── build02/           # article/20260609_build02 ブランチ
    ├── embc01/            # article/20260501_embc01 ブランチ
    └── ...

2. ユースケース1:worktreeの初回セットアップ

前提

  • メインリポジトリ:~/dev/my-qiita-articles
  • worktree展開先:~/dev/qiita_worktree/

リモートのブランチ一覧を確認

# origin/article/で始まるリモートブランチを一覧表示
git branch -r | grep 'origin/article/' | tr -d ' ' | sed 's/origin\///'

全ブランチを一括展開

# メインリポジトリに移動
cd ~/dev/my-qiita-articles

# worktree展開先フォルダを作成(既に存在する場合はスキップ)
mkdir -p ~/dev/qiita_worktree

# origin/article/で始まるリモートブランチを全て取得し、順番にworktreeとして展開
for branch in $(git branch -r | grep 'origin/article/' | tr -d ' ' | sed 's/origin\///'); do
  # ブランチ名からプレフィックス(article/日付_)を除いてフォルダ名を生成
  # 例: article/20260429_build01 → ~/dev/qiita_worktree/build01
  folder=~/dev/qiita_worktree/$(echo $branch | sed 's/article\/[0-9]*_//')
  echo "=== $branch -> $folder ==="
  git worktree add "$folder" "$branch"
done

展開確認

# 各worktreeのパス・コミットハッシュ・ブランチ名を一覧表示
git worktree list

3. ユースケース2:編集・コミット・push

編集が完了したら、変更があるworktreeだけを対象に一括でコミット・pushします。
ただし、コミットメッセージは同じになってしまいます。

# メインリポジトリに移動(worktreeの一覧取得はここから行う)
cd ~/dev/my-qiita-articles

for worktree in $(git worktree list --porcelain | grep '^worktree' | awk '{print $2}' | tail -n +2); do
  echo "=== $worktree ==="
  cd "$worktree"

  # 現在のブランチ名を取得
  branch=$(git branch --show-current)

  # 変更があるファイルが存在する場合のみコミット・pushを実行
  if [ -n "$(git status --porcelain)" ]; then
    git add .
    git commit -m "コミットメッセージ"
    # 上流ブランチを設定しながらpush(未設定でもエラーにならない)
    git push --set-upstream origin $branch
  else
    echo "変更なし、スキップ"
  fi
done

未pushのブランチだけpushする場合

コミット済みだがpushし忘れたブランチだけを対象にする場合はこちらを使います。

cd ~/dev/my-qiita-articles

for worktree in $(git worktree list --porcelain | grep '^worktree' | awk '{print $2}' | tail -n +2); do
  cd "$worktree"
  branch=$(git branch --show-current)

  # リモートとローカルのコミット差分を確認し、未pushのブランチだけpush
  if [ -n "$(git log origin/$branch..HEAD 2>/dev/null)" ]; then
    echo "=== $branch をpush ==="
    git push --set-upstream origin $branch
  fi
done

4. ユースケース3:後片付け

# 全worktreeを強制削除(未コミットの変更があっても削除)
for worktree in $(git worktree list --porcelain | grep '^worktree' | awk '{print $2}' | tail -n +2); do
  git worktree remove --force "$worktree"
done

# 手動削除などで残った登録情報を掃除
git worktree prune

# リモートで削除済みのブランチ追跡情報をローカルから除去
git fetch --prune

# mainにマージ済みのarticle/ブランチを全て削除
git branch --merged main | grep 'article/' | xargs git branch -d

5. ハマりどころ

worktreeのパスがずれる

worktreeを展開したフォルダを手動で別の場所に移動すると、Gitの登録情報と実際のパスがずれて prunable(削除可能)と表示されます。

/Users/yharada/dev/qiita-build01  fb6b2fe [article/20260601_build01] prunable

この場合、フォルダを登録情報のパスに合わせて移動するか、再展開します。

# 登録情報を掃除して再展開
git worktree prune
git worktree add ~/dev/qiita_worktree/build01 article/20260601_build01

現在チェックアウト中のブランチは展開できない

メインリポジトリで現在チェックアウトしているブランチは、worktreeに展開できません。

fatal: 'article/doxygen03' is already used by worktree at '...'

事前に git checkout main でメインブランチに移動してから展開します。

git checkout main
git worktree add ~/dev/qiita_worktree/doxygen03 article/20260520_doxygen03

pushでupstream未設定エラーが出る

fatal: The current branch article/xxx has no upstream branch.

git pushgit push --set-upstream origin ブランチ名 に変えることで解決します。スクリプト内では branch=$(git branch --show-current) でブランチ名を取得して渡しています。

WebUI上で変更するとローカルとリモートに差分が出る

GitHubのWebUI上でファイルを編集すると、ローカルのworktreeと差分が生じます。この場合は以下で強制的にリモートに合わせます。

# リモートの最新情報を取得
git fetch --all

# 全article/ブランチをリモートの状態に強制的に合わせる
for branch in $(git branch -r | grep 'origin/article/' | tr -d ' ' | sed 's/origin\///'); do
  git checkout $branch
  git reset --hard origin/$branch
done

6. 運用Tips(任意)

リモートで削除されたブランチを自動で掃除する

GitHubのPRマージ後にブランチを削除した場合、ローカルの追跡情報が残ります。以下の設定をしておくと git fetch のたびに自動で掃除されます。Gitのグローバル設定に影響するため、任意で設定してください。

git config --global fetch.prune true

おわりに

Git Worktreeを使うことで、複数ブランチの並行編集と一括コミット・pushが実現できました。ブランチ切り替えのコストがなくなり、作業効率が大きく上がります。

記事ごとにブランチを分けて管理しているQiita CLIユーザーや、複数機能を並行開発しているプロジェクトでも同様の運用が使えます。

追記(2026-06-26):「そもそも複数クローンでよくないか?」という疑問については、以下の記事で比較しています。

Git Worktreeは本当に必要?複数クローンと比較して選ぶ

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?