Codex CLIとgit worktreeを並列で
動かしたら終わらせ方で詰まった

関連記事

1. やったこと

やったこと 元の作業ツリー 未コミット差分あり 触らず保全 git work tree /tmp/dunque-main main ベース codex CLI 自動コミット 小さくコミットを積む 人間の作業継続 AIがリファクタを並列処理

元の作業ツリーに未コミットの差分が積まれた状態で、ExploreScenesession.tsの大規模な分割リファクタを進める必要がありました。その場でmainをいじると差分が混ざるので、git worktree/tmp/dunque-mainに別ツリーを切り、そこにcodex CLIを向けて作業させました1

自分は元のツリーで別のことを進めます。codexがmain上でコミットを積み、自分が手元の差分を整理する。並列で動かせば時間を節約できると思っていました2

実際、作業速度は上がりました。codexは小さいコミットを連続して積み、typechecktestも別ツリーで完結していました。問題は終わらせ方にありました。

2. worktreeを消すタイミングで詰まった

codexの作業が終わったタイミングでgit worktree removeを走らせました3

元のツリーへ戻すとき競合が起きた ① stash push -u 差分を退避 ② merge main mainを取り込む ③ stash pop 🔴 CONFLICT なぜ競合するか stashは「古い構造への差分」 main側が 大規模分割 →戻す先がない 競合ファイル: session.ts / ExploreScene.ts 解消方針 Updated upstream を採用 新しい構造をベースにする Stashed changes から 必要な差分だけ手動で拾う 旧実装の丸ごと復元は避ける stash popが失敗してもstashは残る git stash list で内容を確認できる 焦ってgit reset –hardしない → 回復可能

worktreeでの作業が完了したあと、元の場所へ差分を戻す段になってstash popが競合しました。

手順はこうです。
元のツリーでgit stash push -ugit merge maingit stash pop
競合したのはsession.tsExploreScene.tsで、どちらもcodexが大きく分割・再構成したファイルでした。

stashが保存しているのは「古い構造に対する差分」です。
main側の構造が大きく変わっていると、その差分を戻す先がなくなります。stash popが競合するのは当然でした4

解消の方針はシンプルで、Updated upstream(新しい構造)をベースにして、Stashed changesから本当に必要な差分だけ拾います5
旧実装をそのまま復元しようとすると分割が壊れるので、それは避けます。

3. 何が良くて何がコストだったか

良かったのは、元のツリーを汚さずにリファクタを進められたことと、codexがmain上でコミットを積んでいる間に自分も別の作業を進められたことです。
git checkoutで切り替えながら作業する方法に比べると、ミスの余地が減ります。

コストとして残るのは、「元の未コミット差分を新しいmainに載せ直す」という統合作業です。
これはworktreeを使っても消えません。物理的に分離できても、同じファイルに対する変更を最終的にどうマージするかは人間が判断しなければいけません6

4. 次回やるとしたら

worktreeで大きな作業に入る前に、元の未コミット差分が大きければstashではなくWIPコミットとして退避しておきます。

次回やるとしたら 差分の退避方法 ❌ git stash 大規模変更後は戻しにくい ✅ WIPコミット diffで確認しながら取り込める 差分が大きいときは WIPコミットを優先 終了処理の順序 1 finish_working.py 実行 + 成功確認 2 git worktree remove 実行 ✓ not found 防止 並列にしたくても 終了処理は直列に stash pop 競合対策 大規模リファクタ後は競合前提で stash apply を使う 適用後もstashが残る 差分を確認しながら取込 → 後で stash drop 競合しても stashは消えない → 回復可能 並列は有効。詰まるのは「終わらせ方の設計」を後回しにしたとき。

stashは「一時的な退避」に見えますが、大規模なリファクタを挟むと戻しにくい。ブランチとして残しておく方が、後でdiffを確認しながら取り込みやすいです7

終了処理の順序は先に決めます。
codexの作業が終わったら、まず終了スクリプトを走らせて成功を確認し、それからworktreeを削除する。

並列で動かすこと自体は有効でした。
詰まったのは終わらせ方の設計を後回しにしていたからで、次は開始前に「どう片付けるか」まで決めてから動かします。

  1. git worktreeはGit 2.5(2015年)で正式公開された機能で、1つのリポジトリから複数の作業ディレクトリを作り、それぞれで別のブランチを同時に操作できます。git checkoutによる切り替えと違い、ファイルシステム上に独立したディレクトリが並ぶため、ブランチ切り替え事故がなくなります。 – 徹底解説:git worktree の使い方
  2. Anthropic公式ドキュメントでも、git worktreeによる並列セッション運用は「the single biggest productivity unlock(単一最大の生産性向上)」と位置づけられています。長時間タスクの場合、1つのworktreeでAIが作業している間に別のworktreeで開発を続行できる点が主なメリットとして挙げられています。 – 一般的なワークフロー – Claude Code Docs
  3. worktreeの削除には必ずgit worktree remove <パス>を使います。rm -rfでディレクトリを直接消すと、.git/worktrees/以下の管理情報が残ったままになり、リポジトリの整合性が壊れる可能性があります。残ってしまった場合はgit worktree pruneで不要なエントリを掃除できます。 – git worktreeを使ってみる
  4. git stash popでコンフリクトが起きてもstashは削除されずに残ります(git stash applyと同じ挙動になります)。そのためgit stash listで確認すれば、退避した内容は取り戻せます。焦ってgit reset --hardをかける前に、まずstashの中身を確認するとよいです。 – git stash popがconflictしたとき
  5. コンフリクトが起きたファイルには<<<<<<< Updated upstream(現在のコミット側)、=======(区切り)、>>>>>>> Stashed changes(stashの内容)という3つのマーカーが入ります。大規模リファクタ後はUpdated upstreamが正しい新構造なので、これを残してStashed changesから必要な箇所だけ手動で取り込みます。 – git stash popでコンフリクトした時の解決方法
  6. worktreeを使って並列でAIエージェントと人間が作業する場合でも、「同じファイルに対して別々の変更を加えた」という状況は最後に必ず統合コストとして現れます。worktreeはファイルシステム上の物理分離には強いですが、意味的な変更の衝突を解消する仕組みは持っていません。 – Git Worktrees: The Secret Weapon for Running Multiple AI Coding Agents in Parallel
  7. git stash popの代わりにgit stash applyを使うと、適用後もstashのエントリが削除されずに残ります。コンフリクトが起きて解消しながら適用したい場合はapplyの方が安全で、必要な差分を取り込んだ後に手動でgit stash dropする運用が確実です。 – 変更を一時的に退避しよう!git stashを使いこなす5つのステップ