Fragments of verbose memory

冗長な記憶の断片 - Web技術のメモをほぼ毎日更新

Jan 6, 2026 - 日記

jj workspace: コンフリクトで止まらないvibe coding並列開発

jj workspace cover image

最近、Claude Code を4並列で動かしながら開発していて、ちょっと困ったことがありました。

git worktreeで4つのディレクトリを作って、それぞれでAIに実装を任せる——ここまでは快適なんですが、いざマージしようとすると「CONFLICT」の嵐。1つのworktreeがコンフリクトで止まると、そこから派生させたい作業も全部ブロックされて、結局AIを遊ばせることになってしまう。

「コンフリクトが起きても作業を止めたくないんだけど…」と思って調べていたら、Googleのエンジニアが中心となって開発しているJujutsu (jj)というVCSを見つけました。これがなかなか良くて、vibe codingスタイルにかなりハマったので共有します。

Jujutsu(jj)とは?

jjはGoogleのエンジニアが始めた、Git互換のオープンソースVCSです。Googleの公式製品ではありませんが、現在もGooglerが中心となって開発を続けています。「Git互換」というのがポイントで、既存のGitリポジトリにそのまま導入できます。

で、何がいいかというと:

  • コンフリクトが起きても作業が止まらない: これが一番デカい。コンフリクトはコミットに記録されるだけで、他の作業は普通に続けられる
  • 操作を全部記録してる: jj op logで全履歴が見えて、いつでも戻せる。AIの出力が微妙だったときに地味に助かる
  • 自動コミット: ファイルを保存した時点でもうコミットされてる。git addとか要らない
  • Rust製で速い: まあこれはおまけ

インストール

macOSならbrew install jjで一発です。

1
2
3
4
5
6
7
8
# macOS (Homebrew)
brew install jj

# Linux (cargo)
cargo install --git https://github.com/jj-vcs/jj jj-cli

# バージョン確認
jj --version

Gitとの概念の違い

最初ちょっと戸惑ったのが、Gitと微妙に概念が違うところ。慣れると楽なんですが、最初は「え、ブランチないの?」ってなりました。

概念GitJujutsu (jj)
作業コピーステージング領域(index)で管理常に1つのコミット(@
ブランチ必須(detached HEAD は特殊状態)不要(匿名で作業可能)
コンフリクトエラーとして扱う(作業ブロック)コミットに記録(作業継続可能)
履歴の操作rebase(リスクあり)自動rebase(安全)

特に「ブランチ不要」は最初違和感ありましたが、vibe codingだと「とりあえず作業始めて、名前は後で決める」ができるので、むしろ便利でした。

jjの基本操作

既存のGitリポジトリでjjを試すのは簡単です。Gitと併用できるので、気に入らなければ元に戻せます。

リポジトリの初期化

1
2
3
4
5
6
# 既存のGitリポジトリでjjを初期化
cd your-git-repo
jj git init --git-repo .

# 現在の状態を確認
jj log

jj logの出力例:

1
2
3
4
5
@  qpvuntsm [email protected] 2026-01-05 14:30:00 
│  (empty) (no description set)
◉  rlvkpntz [email protected] 2026-01-05 14:25:00 main
│  Add README
~

@が現在のworking copyコミット。GitのHEADに近いけど、未コミットの変更も含めて常にコミットとして扱われます。

基本的な変更フロー

Gitに慣れてると最初「あれ?」ってなるんですが、git addgit commitも要りません。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 1. 新しい変更を開始
jj new main -m "Add feature A"

# 2. ファイルを編集
vim src/feature.js

# 3. 状態を確認(もうコミットされてる!)
jj log

# 4. 説明を追加/修正
jj describe -m "Add authentication feature"

# 5. 次の変更を開始
jj new -m "Add tests for feature A"

ファイルを保存した瞬間にもうコミットされてるので、git addgit commitの流れがなくなります。正直これだけでもだいぶ快適。

コミットの編集

過去のコミットを編集するのも楽です。Gitだとgit rebase -iで「ミスったらどうしよう」とビクビクしながらやってたんですが、jjは気軽にできます。

1
2
3
4
5
6
7
# 過去のコミットを編集
jj edit <change-id>
vim src/feature.js
jj describe -m "Updated feature"

# 元の位置に戻る
jj edit @

しかも子孫のコミットが自動的にrebaseされるので、依存関係を気にしなくていい。

git worktree vs jj workspace

さて、ここからが本題。vibe codingで並列開発するには「複数の作業ディレクトリ」が必要です。

Gitにはgit worktree、jjにはjj workspaceがありますが、一見似てるようで決定的に違う点がいくつかあります。

基本的な仕組みは同じ

ディレクトリ構造は同じ感じ:

repo/               # メインディレクトリ
├── .git/          # Gitデータ(共有)
├── .jj/           # jjデータ(共有)
└── src/

workspace-1/        # 並列作業ディレクトリ1
└── src/

workspace-2/        # 並列作業ディレクトリ2
└── src/

npm installは各ディレクトリで必要になるのは両者同じ。ここは諦めるしかないです。

決定的な3つの違い

で、ここからが重要。実際に使ってみて「あ、これ全然違うわ」と思った3点。

違い①: コンフリクト時の挙動(これが一番デカい)

git worktreeだとこうなります:

1
2
3
4
5
6
7
8
9
# worktree-1でfeature-Aをマージ
cd ../worktree-1
git merge feature-A  # ✅ 成功

# worktree-2でfeature-Bをマージ
cd ../worktree-2
git merge feature-B  # ❌ CONFLICT!
# この時点でworktree-2はブロック
# 他のworktreeで新ブランチを作っても、コンフリクト前のmainから分岐

worktree-2が止まると、そこから派生させたい作業も止まる。これが辛かった。

一方、jj workspaceだと:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# workspace-1でfeature-Aをrebase
cd ../workspace-1
jj rebase -s feature-A -d main  # ✅ 成功

# workspace-2でfeature-Bをrebase
cd ../workspace-2
jj rebase -s feature-B -d feature-A  # ⚠️ コンフリクト発生

# でも作業は継続可能!
jj log
# @  feature-B (conflict) ← コンフリクト状態が記録されている
# ◉  feature-A
# ◉  main

# workspace-3で別の作業を開始
cd ../workspace-3
jj new main -m "feature-C"  # ✅ 問題なく作業開始

コンフリクトは「記録される」だけで、他の作業はブロックされません。これ、vibe codingだとめちゃくちゃ助かります。

git worktreeとjj workspaceのコンフリクト処理の比較図 Technical diagram comparing git worktree vs jj workspace conflict handling. Split screen layout. Left side labeled 'git worktree': shows branch A (green check), branch B (red X with 'BLOCKED'), timeline stops. Right side labeled 'jj workspace': shows change A (green check), change B (yellow '(conflict)' recorded), change C continues (green check). Arrows showing git blocks but jj continues. Clean, professional diagram with color-coded status indicators. Minimal flowchart style. All text in the image must be in Japanese.
git worktreeはコンフリクトでブロックするが、jjは記録して続行可能

違い②: 状態の共有と可視性

git worktreeだと、各worktreeの未コミット変更は他から見えません:

1
2
3
4
5
6
7
# worktree-1で変更(未コミット)
cd ../worktree-1
echo "test" > newfile.txt

# worktree-2からは見えない
cd ../worktree-2
ls newfile.txt  # ❌ 存在しない

jjだと自動コミットされるので、全workspaceから見えます:

1
2
3
4
5
6
7
# workspace-1で変更(自動コミット)
cd ../workspace-1
echo "test" > newfile.txt

# workspace-2からも見える
cd ../workspace-2
jj log  # workspace-1の変更が @マークで表示される

「あれ、さっきどこで何やってたっけ?」が減ります。

違い③: 依存ブランチの自動rebase

これも地味に便利。feature-A → feature-B → feature-Cみたいな依存関係があるとき。

git worktreeだと、feature-Aを修正したらB、Cを手動でrebaseする必要があります:

1
2
3
4
5
6
7
8
9
cd ../worktree-A
git commit --amend -m "Fix feature A"

# feature-BとCは手動でrebase必要
cd ../worktree-B
git rebase feature-A  # 手動

cd ../worktree-C
git rebase feature-B  # 手動

jjだと勝手にやってくれます:

1
2
3
4
5
cd ../workspace-A
jj describe -m "Fix feature A"

# feature-BとCは自動的にrebaseされる
jj log  # すべて更新済み

AIが生成したコードを後から微調整することが多いので、これは嬉しい。

vibe codingでの実践ワークフロー

実際に自分がClaude Code 4並列でやってるワークフローを紹介します。

jj workspaceの並列開発アーキテクチャ図 Technical architecture diagram of jj workspace parallel development. Top view showing 4 workspace directories (workspace-1 to 4) arranged in a circle, each labeled with tasks: 'auth', 'database', 'UI', 'tests'. In the center: shared '.jj/' repository hub with bidirectional arrows to all workspaces. Each workspace shows status indicators (green progress bars). Clean, symmetric layout. Modern tech diagram style with consistent spacing and alignment. Blue and green color scheme. All text in the image must be in Japanese.
4つのworkspaceが共有リポジトリハブを中心に並列で動作

セットアップ

最初のセットアップはこんな感じ。npm installを4回やるのはちょっと面倒ですが、最初だけなので我慢。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# プロジェクトディレクトリで初期化
cd my-project
jj git init --git-repo .

# 4つのworkspaceを作成
jj workspace add ../workspace-1
jj workspace add ../workspace-2
jj workspace add ../workspace-3
jj workspace add ../workspace-4

# 各workspaceで依存関係をインストール(これは仕方ない)
for i in {1..4}; do
  cd ../workspace-$i
  npm install
done

並列タスクの開始

各workspaceで別々のタスクをAIに投げます。ターミナルを4つ開いて、それぞれでClaude Codeを動かす感じ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# workspace-1: 認証機能
cd ../workspace-1
jj new main -m "Add authentication"
# Claude Codeに「JWT認証を実装して」と投げる

# workspace-2: データベース最適化
cd ../workspace-2
jj new main -m "Optimize database queries"
# Claude Codeに「N+1クエリを直して」と投げる

# workspace-3: UIコンポーネント追加
cd ../workspace-3
jj new main -m "Add user profile component"
# Claude Codeに「プロフィール画面作って」と投げる

# workspace-4: テスト追加
cd ../workspace-4
jj new main -m "Add integration tests"
# Claude Codeに「E2Eテスト書いて」と投げる

で、AIが作業してる間に別のworkspaceに切り替えてレビューしたり、次のタスクを考えたりします。

コンフリクト発生時の対処

で、実際にコンフリクトが起きたとき。workspace-1とworkspace-2で同じファイルを編集してしまった場合:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# workspace-1の変更をmainに統合
cd ../workspace-1
jj rebase -s @ -d main  # ✅ 成功

# workspace-2の変更も統合しようとすると...
cd ../workspace-2
jj rebase -s @ -d main  # ⚠️ コンフリクト発生

# でも作業は続行可能
jj log
# @  workspace-2-change (conflict)
# ◉  workspace-1-change
# ◉  main

# workspace-3や4の作業は影響を受けない!
cd ../workspace-3
jj new main -m "Continue other work"  # ✅ 問題なし

「コンフリクト解決は後でまとめてやる」ができるのがポイント。AIを遊ばせずに済みます。

1
2
3
4
5
# 全タスク完了後、コンフリクトをまとめて解決
cd ../workspace-2
jj edit @  # コンフリクトのあるコミットに移動
jj resolve  # 対話的に解決
jj describe -m "Optimize queries (resolved conflict)"

最終的なマージ

全部終わったらまとめてpush。GitHubへのpushは普通にできます。

1
2
3
4
5
6
7
8
9
# 全workspaceの変更を確認
cd my-project
jj log --all

# 必要に応じてsquashで整理
jj squash -s <change-id> -d main

# Gitリポジトリとしてpush
jj git push

Operation Logでプロンプト履歴を追跡

これは副産物的に発見した使い方なんですが、「どのプロンプトで何が生成されたか」を追跡するのにoperation logが便利でした。

operation logのタイムライン図 Technical diagram illustrating jj operation log concept. Horizontal timeline showing sequence of operations from left to right. Each operation shown as a node with ID (op_001, op_002, etc.) and description ('edit file', 'rebase', 'resolve'). Above timeline: current state marked with '@'. Below: ability to restore to any previous operation shown with curved arrow going backwards. Clean, linear flowchart. Purple and blue color scheme. Timestamps on each node. All text in the image must be in Japanese.
すべての操作が記録され、任意の時点に復元可能

全操作の記録

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 全操作を表示
jj op log

# 出力例
@  qpvuntsm [email protected] 2026-01-05 15:30:00 op_abc123
│  describe "Implement authentication"
◉  sqpuoqvx [email protected] 2026-01-05 15:29:45 op_def456
│  edit src/auth.js
◉  rqxostpw [email protected] 2026-01-05 15:29:30 op_ghi789
   new --after main

プロンプトをコミットメッセージに記録

自分はAIへのプロンプトをそのままコミットメッセージに入れるようにしてます:

1
2
3
4
5
jj describe -m "Prompt: Add user authentication with JWT.
Implementation includes:
- Login endpoint with email/password
- Token generation and validation
- Middleware for protected routes"

後から「このコード、なんでこうなってるんだっけ」と思ったとき、プロンプトを見返せるので便利。

特定操作への復元

「AIの出力が微妙だったから戻したい」というとき、簡単に巻き戻せます:

1
2
3
4
5
# 過去の操作状態に戻る
jj op restore op_abc123

# 現在の状態に戻る
jj op restore @

Gitだとgit reflogで頑張る場面ですが、jjの方が直感的。

bookmarkでブランチを後から命名

これもvibe codingに合ってる機能。「とりあえず作業始めて、名前は後で決める」ができます。

1
2
3
4
5
6
7
8
9
# 匿名で作業開始(ブランチ名不要)
jj new main
# Claude Codeで実装...

# 完成してから名前を付ける
jj bookmark create user-auth-feature

# GitHub PRのためにpush
jj git push --bookmark user-auth-feature

Gitだと最初にgit checkout -b feature-xxxでブランチ名を決めないといけないんですが、AIに実装させてると「結局何ができたか」は最後までわからないことが多い。jjならその問題がなくなります。

jjを使い続けるべきか?

正直なところ、万人におすすめできるかというと微妙です。

jjが向いてる人

  • AIエージェントを並列で使う開発スタイル
  • コンフリクトが頻発する環境(同じファイルをよく触る)
  • 試行錯誤を繰り返す探索的な開発
  • 履歴の整形・編集を頻繁に行う

git worktreeのままでいい人

  • チーム全員がGitに慣れている(学習コストを避けたい)
  • IDEのGit統合を重視する(jjのIDE対応はまだ発展途上)
  • コンフリクトがほとんど発生しない
  • 1〜2並列で十分

まとめ

git worktreeで4並列開発してたら「コンフリクトで全部止まる」問題にハマって、jjに移行したら解決した、という話でした。

jjが優位な点を改めてまとめると:

  1. コンフリクトがブロックしない: 記録して作業継続可能
  2. 状態の可視性: 全workspaceから全changeが見える
  3. 自動rebase: 依存する変更を修正すると自動的に伝播

学習コストはありますが、vibe codingで「AIを遊ばせたくない」人には試す価値あると思います。Git互換なので、気に入らなければ元に戻せますし。

まずは既存プロジェクトでjj git init --git-repo .を試してみてください。

参考リンク