🌲

Claude Codeとgit worktreeで案件ごとの開発環境を自動構築する

に公開

はじめに

Claude Code を使って複数リポジトリを横断しながら開発していると、
「このチケットでどのコードを見てほしいか」を毎回設定するのが手間になります。

この記事では、git worktree × Claude Code設定自動生成を組み合わせて、
チケット単位で開発環境を一瞬で構築できるワークフローを紹介します。

1コマンドで:

  • 各リポジトリに git worktree add をまとめて実行
  • ~/ws/<ticket>/ 以下に環境を作成
  • Claude Codeの .claude/settings.json を自動更新

これで、チケットIDを入力するだけで開発環境とLLM文脈が自動的にセットアップされます。


構成概要

workspace/
  └── documents/
       ├── projects/          # 各案件の設計・議事録など
       ├── knowledges/        # 共通ナレッジ
       ├── .claude/
       │   ├── settings.json  # Claude Codeが参照する現行設定
       │   └── profiles/      # チケットごとの設定ファイル
       └── .tools/
           ├── ws             # worktree生成スクリプト
           └── ccprofile      # Claude設定生成スクリプト
~/git/org/
  ├── backend/.git
  ├── frontend/.git
  └── infra/.git
~/ws/
  ├── PROJ-123/
  │   ├── backend/
  │   ├── frontend/
  │   ├── infra/
  │   └── .meta/REPOS
  └── PROJ-999/
      ├── backend/
      └── analytics/

.claude/settings.json の役割

Claude Codeは .claude/settings.json に定義された context_paths を読み取り、
そのディレクトリ群を文脈として参照します。

例:

{
  "project": "PROJ-123",
  "context_paths": [
    "/Users/you/ws/PROJ-123/backend",
    "/Users/you/ws/PROJ-123/frontend",
    "/Users/you/ws/PROJ-123/infra"
  ],
  "extra_paths": [
    "/Users/you/workspace/documents/projects",
    "/Users/you/workspace/documents/knowledges"
  ]
}

つまり、これを自動生成・更新できれば、
Claude Code側での手動操作は一切不要になります。


スクリプト構成

自動化は2つのBashスクリプトで実現します:

  • .tools/ws — 複数リポに対して git worktree を自動作成
  • .tools/ccprofile — Claude Code設定を生成・切替

.tools/ws — 複数リポジトリをまとめてworktree作成

#!/usr/bin/env bash
# ws: 複数リポジトリに対して git worktree add を自動化
set -euo pipefail

DOCS="${DOCS:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.."; pwd)}"
WS_ROOT="${WS_ROOT:-$HOME/ws}"
REPO_CLONES_ROOT="${REPO_CLONES_ROOT:-$HOME/git/org}"
CCP="${DOCS}/.tools/ccprofile"

mkdir -p "${WS_ROOT}"

repo_src_path() {
  echo "${REPO_CLONES_ROOT}/$1"
}

create_worktree() {
  local ticket="$1"
  local repo="$2"
  local base="${3:-main}"
  local src; src="$(repo_src_path "$repo")"
  [ -d "${src}/.git" ] || { echo "❌ not a git repo: ${src}" >&2; exit 1; }

  local target="${WS_ROOT}/${ticket}/${repo}"
  mkdir -p "$(dirname "$target")"

  if [ -d "$target" ]; then
    echo "ℹ️  already exists: $target"
    return
  fi

  echo "🚧 creating worktree for $repo..."
  git -C "$src" fetch --prune
  git -C "$src" worktree add -b "$ticket" "$target" "origin/$base"
  echo "✅ done: $target"
}

create_all() {
  local ticket="$1"; shift
  local base="${BASE:-main}"

  local repos=("$@")
  [ ${#repos[@]} -gt 0 ] || { echo "❌ REPOS が空です"; exit 1; }

  echo "==> Creating worktrees under $WS_ROOT/$ticket"
  mkdir -p "${WS_ROOT}/${ticket}/.meta"
  printf "%s\n" "${repos[@]}" > "${WS_ROOT}/${ticket}/.meta/REPOS"

  # 並列実行(CPUコアに応じて-Pを調整)
  printf "%s\n" "${repos[@]}" | xargs -I{} -P4 bash -c "create_worktree '$ticket' '{}' '$base'"

  # Claude Code用の設定も生成
  "${CCP}" make "$ticket" "${repos[@]}"
  "${CCP}" use "$ticket"

  echo "✅ workspace ready for $ticket"
  echo "👉 Open Claude Code with: $DOCS (.claude/settings.json)"
}

clean_all() {
  local ticket="$1"
  local ws_dir="${WS_ROOT}/${ticket}"
  [ -d "${ws_dir}" ] || { echo "no workspace $ticket"; exit 0; }

  echo "🧹 cleaning worktrees for $ticket"
  find "$ws_dir" -mindepth 1 -maxdepth 1 -type d ! -name ".meta" | while read -r w; do
    local repo="$(basename "$w")"
    local src="$(repo_src_path "$repo")"
    git -C "$src" worktree remove -f "$w" || true
    echo "  removed $w"
  done
  rm -rf "$ws_dir"
}

usage() {
  cat <<USAGE
Usage:
  ws new <TICKET> <repo...> [BASE=develop]   # 複数リポに対して worktree を自動作成
  ws clean <TICKET>                          # まとめて worktree 削除
Env:
  DOCS=${DOCS}
  WS_ROOT=${WS_ROOT}
  REPO_CLONES_ROOT=${REPO_CLONES_ROOT}
USAGE
  exit 1
}

main() {
  local cmd="${1:-}"; shift || true
  case "$cmd" in
    new)
      local ticket="${1:-}"; shift || true
      [ -n "$ticket" ] || usage
      create_all "$ticket" "$@"
      ;;
    clean)
      local ticket="${1:-}"; shift || true
      [ -n "$ticket" ] || usage
      clean_all "$ticket"
      ;;
    *) usage ;;
  esac
}
main "$@"

.tools/ccprofile — Claude設定の生成と切替

#!/usr/bin/env bash
# ccprofile: Claude Code の context_paths を自動生成・切替
set -euo pipefail

DOCS="${DOCS:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.."; pwd)}"
WS_ROOT="${WS_ROOT:-$HOME/ws}"
PROFILES_DIR="${DOCS}/.claude/profiles"
ACTIVE_SETTINGS="${DOCS}/.claude/settings.json"

mkdir -p "${PROFILES_DIR}"

_repos_of() {
  local ticket="$1"
  local ws_dir="${WS_ROOT}/${ticket}"
  [ -f "${ws_dir}/.meta/REPOS" ] && cat "${ws_dir}/.meta/REPOS" ||     find "${ws_dir}" -maxdepth 1 -mindepth 1 -type d -not -name ".meta" -printf "%f\n" 2>/dev/null | sort
}

make_profile() {
  local ticket="$1"; shift
  local ws_dir="${WS_ROOT}/${ticket}"

  local repos=()
  if [ $# -ge 1 ]; then
    repos=("$@")
  else
    mapfile -t repos < <(_repos_of "${ticket}")
  fi
  [ ${#repos[@]} -ge 1 ] || { echo "❌ no repos"; exit 1; }

  local tmp="${PROFILES_DIR}/.${ticket}.json.tmp"
  {
    echo "{"
    echo "  \"project\": \"${ticket}\","
    echo "  \"context_paths\": ["
    local first=1
    for r in "${repos[@]}"; do
      local p="${ws_dir}/${r}"
      [ -d "$p" ] || continue
      [ $first -eq 1 ] && first=0 || echo ","
      printf "    \"%s\"" "$(realpath "$p")"
    done
    echo ""
    echo "  ],"
    echo "  \"extra_paths\": ["
    printf "    \"%s/projects\",\n" "$(realpath "$DOCS")"
    printf "    \"%s/knowledges\"" "$(realpath "$DOCS")"
    echo "  ]"
    echo "}"
  } > "${tmp}"

  mv "${tmp}" "${PROFILES_DIR}/${ticket}.json"
  echo "✅ profile: ${PROFILES_DIR}/${ticket}.json"
}

use_profile() {
  local ticket="$1"
  local src="${PROFILES_DIR}/${ticket}.json"
  [ -f "$src" ] || { echo "❌ profile not found: $src"; exit 1; }
  cp -f "$src" "$ACTIVE_SETTINGS"
  echo "🔁 activated: $ticket"
}

main() {
  local cmd="${1:-}"; shift || true
  case "$cmd" in
    make) make_profile "$@" ;;
    use)  use_profile "$1" ;;
    *) echo "Usage: ccprofile {make|use} <TICKET>"; exit 1 ;;
  esac
}
main "$@"

Makefile統合

.PHONY: ws-new ws-clean cc-use

ws-new:
	@test -n "$(TICKET)" || (echo "TICKET= を指定してください" && exit 1)
	@test -n "$(REPOS)"  || (echo "REPOS= を指定してください" && exit 1)
	@BASE=$(BASE) .tools/ws new "$(TICKET)" $(REPOS)

cc-use:
	@test -n "$(TICKET)" || (echo "TICKET= を指定してください" && exit 1)
	@.tools/ccprofile use "$(TICKET)"

実行例

# 案件用の環境を作成
make ws-new TICKET=PROJ-123 REPOS="frontend backend infra" BASE=develop

# Claude Codeをその文脈に切り替え
make cc-use TICKET=PROJ-123

結果:

~/ws/PROJ-123/backend
~/ws/PROJ-123/frontend
~/ws/PROJ-123/infra
workspace/documents/.claude/settings.json   # ← Claudeが参照

効果とメリット

項目 内容
手動操作 git worktree addを自動化、設定更新も自動
Claude切替 .claude/settings.jsonを1行コピーで即反映
並列開発 案件ごとに独立した環境を構築可能
一貫性 Documentsリポジトリ中心で構成が明確
チーム共有 設定とスクリプトのみ共有すれば全員同じ構成を再現可能

まとめ

  • git worktree で案件単位のコード環境を作る
  • .tools/ws で複数リポの環境を自動生成
  • .tools/ccprofile で Claude Code の文脈を即切替
  • make ws-new 一発で「コード+文脈」セットアップ完了

Claude CodeをIDEとしてではなく、
「文脈スイッチャー」として設計することで、
開発体験が格段に軽く、チームで再現性のある形になります。

Discussion