ここまでの実戦編は、ずっと 1 本道でした。1 つの機能を feature ブランチで作り、main に取り込み、また次の機能へ。今回は、その道を2 本に増やします。ダークモードと並び替えという別々の機能を、同時並行して作ります。
そのために使うのが git worktree です。第7回で仕組みを見たとおり、worktree は「同じ .git(履歴の本体) を共有したまま、作業フォルダだけを複数持つ」道具です。フォルダが 2 つあれば、2 つの AI に 1 つずつ任せて、同時に走らせられます。これは、Claude Code が複数のエージェントを並列で動かすときに裏でやっていることそのものです。今日は、その worktree の用意から合流・片付けまで、私はコマンドを打たず、AI への言葉だけで進めます。実装する 2 つの機能も、それぞれ別の AI に任せます。
TL;DR
- 第11回のアプリに、ダークモードと並び替えを
git worktreeで並列開発します。2 つの作業フォルダが、同じ.gitを共有します。 - 2 つの AI を同時に走らせて、2 つの機能を一度に実装します。worktree の用意・合流・片付けも、私は手を動かさず AI に言葉で頼みます。Claude Code の並列エージェントが裏でやっていることを、言葉だけでなぞります。
- 別々の箇所を触ったので、
--no-ffマージを 2 回かけてもコンフリクトは 0。Git がAuto-mergingで自動統合します。 - 終わったら worktree は
git worktree remove、マージ済みのブランチはgit branch -dで片付けます。
この回でやること――2 本のレールを同時に敷く
今日の作りは、こういう形になります。1 つの .git の上に、3 つの作業フォルダがぶら下がります。
flowchart TD
G[".git(履歴の本体は 1 つだけ)"]
G --- M["todo-app-git-deep-dive/<br/>main(第 11 回のフィルタまで)"]
G --- D["todo-wt-darkmode/<br/>feature/dark-mode"]
G --- S["todo-wt-sort/<br/>feature/sort"]todo-wt-darkmode と todo-wt-sort は、物理的に別のフォルダです。それぞれで別の機能を作りますが、履歴の置き場である .git は元のフォルダと共有しています。だから、clone を 3 回するより軽いし、作ったコミットは全部同じ履歴につながります。第7回で「.git は増えない、ポインタが増えるだけ」と確かめたとおりです。
worktree を作る――AI に作業場を 2 つ用意してもらう
まず、作業場を 2 つ用意します。これも私が打つのではなく、AI に頼みます。
ダークモードと並び替えを、別々の作業場で並行して作りたい。git worktree を使って、 2 つの作業フォルダを用意して。
AI は git worktree add で、今の main(第11回でフィルタを入れた 6865a3a) から 2 本の枝を、それぞれ別フォルダとして切り出しました。
git worktree add ../todo-wt-darkmode -b feature/dark-mode git worktree add ../todo-wt-sort -b feature/sort
git worktree add <フォルダ> -b <ブランチ> は、「新しいブランチを切って、そのブランチをチェックアウトした作業フォルダを、指定の場所に作る」を一度に済ませます。フォルダ名 (todo-wt-darkmode など) もブランチ名 (feature/dark-mode など) も、私は指定していません。AI が機能の名前から付けました。今どうなっているかは git worktree list で見えます。
.../todo-app-git-deep-dive 6865a3a [main] .../todo-wt-darkmode 6865a3a [feature/dark-mode] .../todo-wt-sort 6865a3a [feature/sort]
3 つの作業場が並びました。3 つとも先頭は同じ 6865a3a です。ここから、それぞれが別の方向に進みます。
2 つの AI に同時に任せる――並列実装
作業場が分かれているので、2 つの AI を同時に走らせられます。お互いのフォルダは独立しているので、片方がもう片方の作業を壊す心配がありません。
ダークモード (feature/dark-mode)
todo-wt-darkmode のほうには、こう頼みました。
ダークモードに切り替えられるボタンを付けて。押すと暗い配色、もう一度で戻る。
AI は、body に dark クラスを付け外しする方式を選びました。ダーク時の配色は CSS の末尾にまとめて足し、本体のスタイルには手を入れません。
/* ダークモード配色(抜粋) */
body.dark {
background: #1a1a2e;
}
body.dark .container {
background: #16213e;
}
ボタンは画面の右上に固定で置き、押すたびに dark クラスを切り替えます。
<button id="dark-toggle">🌙ダーク</button>
// ダークモードトグル
const darkToggle = document.getElementById('dark-toggle');
const isDark = localStorage.getItem('darkMode') === 'true';
if (isDark) {
document.body.classList.add('dark');
darkToggle.textContent = '☀️ライト';
}
darkToggle.addEventListener('click', function () {
const dark = document.body.classList.toggle('dark');
darkToggle.textContent = dark ? '☀️ライト' : '🌙ダーク';
localStorage.setItem('darkMode', dark);
});
ここでも AI が勝手に決めた点があります。ボタンの位置 (右上に固定)、配色 (濃紺系の #1a1a2e)、ラベル (「🌙ダーク」と「☀️ライト」を切り替え)、そして選んだ状態を localStorage に保存してリロード後も維持すること。私は「切り替えボタン」「押すたびに反転」としか言っていません。第10回で保存を覚えた AI が、ダークモードでも当たり前のように状態を残しました。
並び替え (feature/sort)
もう一方の todo-wt-sort には、こう頼みました。
未完了が上、完了が下に自動で並ぶように。
AI が書いたのは、この関数です。
// 未完了を上、完了を下に並べ替える
function sortTodos() {
const items = Array.from(list.querySelectorAll('li'));
items.sort(function (a, b) {
return (a.classList.contains('done') ? 1 : 0) - (b.classList.contains('done') ? 1 : 0);
});
items.forEach(function (li) { list.appendChild(li); });
}
完了したものを 1、まだのものを 0 と数えて、その差で並べ替えるだけ。これを、タスクを追加したとき・完了を切り替えたとき・ページを読み込んだときに呼びます。ここでも AI は気を利かせていて、削除のときはソートを呼んでいません (並びは変わらないから不要)。さらに、JavaScript の並べ替えは同じ値どうしの順序を保つ性質があるので、完了済みタスクの中の順番は元のまま維持されます。
2 つの機能は、どちらも index.html を触りますが、触る場所が違います。ダークモードは CSS とボタン、並び替えは並べ替え関数。この「触る場所が重ならない」ことが、次の合流で効いてきます。
AI に頼むときの言い方
今日の作業も、指定した値と AI が勝手に決めた値に分かれていました。機能の中身だけでなく、worktree の運用そのものも同じです。
| 機能 | こちらが指定した値 | 指定しないと AI が勝手に決める値 |
|---|---|---|
| ダークモード | 「切り替えボタン」「押すたびに反転」 | ボタンの位置 (右上固定)・濃紺系の配色・ラベル文言・localStorage に状態を保存 |
| 並び替え | 「未完了が上、完了が下」 | 削除時はソートしない判断・完了内の順序を保つ安定した並べ替え |
| worktree 運用 | 「別々の作業場で並行」「合流して」「片付けて」 | フォルダ名・ブランチ名・--no-ff の選択(並行の履歴を残すため)・片付けの順序 |
方向だけ渡せば、細部は AI が埋めます。とくにダークモードの「状態を保存」は、頼んでいないのに付いてきました。前の回で一度教わった作法を、AI は次の機能でも自然に持ち込みます。
合流――コンフリクト 0 で自動統合する
2 つの機能ができました。合流も AI に頼みます。
2 つの作業場の成果を、両方 main に取り込んで。
AI は、2 つのブランチを順に main へマージしました。
git merge feature/dark-mode --no-ff # +94 行、コンフリクトなし git merge feature/sort --no-ff # +12 行、Auto-merging、コンフリクトなし
ここで AI は、頼んでいないのに --no-ff を選びました。理由を尋ねると「並行して作った 2 つの枝があったことを、履歴のグラフに残すため」と言います。早送りで一直線に吸収させると、並行で開発した跡が消えてしまう。第9回で見た --no-ff の使いどころを、AI は自分の判断で持ち出しました。
2 回とも、コンフリクトは起きませんでした。第9回では同じ行を別々に書き換えてわざとぶつけましたが、今回は逆です。ダークモードは CSS とボタン、並び替えは並べ替え関数と、触った場所が重ならない。だから Git は、両方の変更をそのまま 1 つにまとめられます。Auto-merging というログは、「自動で合わせました」という合図です。
この自動合流を裏で担うのが ort という統合方式 (Git 2.34 以降の既定) です。仕組みは難しくなく、「同じ行に手が入っていなければ、両方の変更を採用する」というだけ。私たちが意識することはありません。合流後の履歴は、こうなりました。
8515ebf Merge branch 'feature/sort': 並び替えを追加 0764122 Merge branch 'feature/dark-mode': ダークモードを追加 88c328e タスクを未完了→完了の順に自動ソートする機能を追加 c2a98d9 feat: ダークモードトグルボタンを追加 6865a3a feat: タスクを「すべて/未完了/完了」で絞り込むフィルタボタンを追加 (#1)
合流後のアプリを動かすと、3 件のタスクを入れて真ん中を完了にしたとき、並びは「牛乳・パン・卵 (完了)」と未完了が上にそろい、見出しは「残り 2 件」、そして右上のボタンでダーク背景にも切り替わる。並列で作った 2 つの機能が、同時に、ちゃんと動いています。
ここが今日の山場です。worktree は独立した作業場ですが、Git の合流は「フォルダが別かどうか」ではなく「内容が重なるかどうか」で判断します。だから、別々の箇所を触っている限り、何本の作業場で並列に作っても、衝突せずに合流できます。Claude Code が複数のエージェントを安全に並列で走らせられるのも、この原則の上に乗っているからです。
片付け――worktree を消して main だけに戻す
合流が終わったら、片付けも AI に頼みます。
もう使わない作業場とブランチを片付けて。GitHub にも反映して。
AI は、GitHub へ送ってから、作業フォルダとブランチを順に消しました。
git push origin main git worktree remove ../todo-wt-darkmode git worktree remove ../todo-wt-sort git branch -d feature/dark-mode feature/sort
git worktree remove で作業フォルダを消し、git branch -d でブランチを消します。ここで -d(小文字) がすんなり通るのは、2 つのブランチがもう main に取り込まれている証拠です。もしマージしていないブランチを消そうとすると、Git は「まだ取り込まれていませんよ」と止めて、-D(大文字) での強制削除を求めます。第5回や第11回で見たこの安全装置は、worktree のブランチにもそのまま効きます。
最後に git worktree list を見ると、残っているのは main の 1 行だけ。きれいに元どおりです。
まとめ
第 12 回として、git worktree で 2 つの作業場を同時に動かし、ダークモードと並び替えを並列で作りました。
git worktree addで、同じ.gitを共有する作業フォルダを 2 つ生やした。cloneより軽く、コミットは同じ履歴につながる。- worktree の用意・2 機能の並列実装・合流・片付けまで、私はコマンドを打たず AI への言葉だけで進めた。Claude Code の並列エージェントが裏でやっていることを、言葉でなぞった。
- 触った場所が重ならなかったので、
--no-ffマージ 2 回ともコンフリクト。Git がortで自動統合した。 - 片付けは
git worktree removeとgit branch -d。マージ済みなら-dで素直に消える。
並列開発というと難しそうですが、肝は 1 つだけです。別々の箇所を触る。そうすれば、何本の作業場で同時に作っても、Git が安全に合わせてくれます。次回は、ここまで育てたアプリをついにインターネットに公開します。GitHub Pages で誰でも開ける URL を用意し、v1.0 というタグでこの時点を「リリース」として刻みます。
なお、この記事で AI に渡したひと言は、すべて Claude(Sonnet) に実際に渡し、書いてあるとおりに動くことを確かめたものです。worktree の用意・並列実装・合流・片付けまで、私はコマンドを打たず、すべて AI への言葉だけで実機検証しました。別々の AI に 2 機能を同時実装させ、合流を頼むと AI が自分の判断で --no-ff を選び、2 回のマージがコンフリクト 0 で通り、合流後にダーク・ソート・件数が同時に動くことまで確認しています。モデルやその日の状態によって、AI が勝手に決める値 (配色やボタンの位置、フォルダ名やブランチ名など) は変わります。指定しなかった部分は変わりうる、と思って読んでください。
パイソンエンジニア部 

