Git→Jujutsu移行
既存のGitワークフローからJujutsuへのスムーズな移行方法を解説し、操作対応表、co-located repoの運用、チームへの段階的導入戦略を提供する。
Git→Jujutsu移行
既存のGitワークフローからJujutsuへのスムーズな移行方法を解説し、操作対応表、co-located repoの運用、チームへの段階的導入戦略を提供する。
この章で学ぶこと
- Git→Jujutsu操作対応表 — 日常的なGit操作に対応するJujutsuコマンドの完全マッピング
- co-located repoの実践運用 — GitとJujutsuを併用する環境の設定と注意点
- 段階的移行戦略 — 個人利用からチーム導入までの移行ロードマップ
- 実践ワークフロー変換 — Feature Branch、Gitflow、Trunk-Based等のJujutsu化
- 移行時のトラブルシューティング — よくある問題と解決手順
前提知識
このガイドを読む前に、以下の知識があると理解が深まります:
- 基本的なプログラミングの知識
- 関連する基礎概念の理解
- Jujutsu応用 の内容を理解していること
1. Git→Jujutsu操作対応表
1.1 基本操作
| Git コマンド | Jujutsu コマンド | 備考 |
|---|---|---|
git init |
jj git init |
--colocateで既存gitと共存 |
git clone URL |
jj git clone URL |
|
git status |
jj status / jj st |
|
git diff |
jj diff |
ステージの概念なし |
git diff --staged |
(不要) | ステージが存在しない |
git diff HEAD~3..HEAD |
jj diff -r @---..@ |
範囲diff |
git log |
jj log |
デフォルトでグラフ表示 |
git log --oneline |
jj log --no-graph |
|
git log -p |
jj log -p |
パッチ付きlog |
git show COMMIT |
jj show REVISION |
|
git blame FILE |
(co-locatedでgit blameを使用) | jjにはblame未実装 |
git bisect |
(co-locatedでgit bisectを使用) | jjにはbisect未実装 |
1.2 変更の記録
| Git コマンド | Jujutsu コマンド | 備考 |
|---|---|---|
git add FILE |
(不要) | 自動的にworking copyに反映 |
git add . |
(不要) | 全変更が自動追跡 |
git add -p |
jj split |
後から分割する発想 |
git commit -m "MSG" |
jj commit -m "MSG" |
describe + new のショートカット |
git commit --amend |
jj describe -m "MSG" |
working copyを直接編集 |
git commit --amend --no-edit |
(ファイルを編集するだけ) | 自動的にcommitに反映 |
git reset HEAD FILE |
jj restore --from @- FILE |
|
git checkout -- FILE |
jj restore FILE |
|
git stash |
(不要) | jj newで新commitに移動 |
git stash pop |
jj edit CHANGE_ID |
元のcommitに戻る |
git stash list |
jj log -r 'description("wip")' |
WIPコミットを検索 |
git clean -fd |
jj restore |
working copyを親の状態に復元 |
1.3 ブランチ操作
| Git コマンド | Jujutsu コマンド | 備考 |
|---|---|---|
git branch NAME |
jj bookmark create NAME |
|
git branch -d NAME |
jj bookmark delete NAME |
|
git branch -m OLD NEW |
jj bookmark rename OLD NEW |
|
git branch -a |
jj bookmark list --all |
|
git branch --merged |
jj log -r 'bookmarks() & ::trunk()' |
trunkにマージ済みのブックマーク |
git checkout BRANCH |
jj new BRANCH |
新commitを作成 |
git checkout -b NAME |
jj new && jj bookmark create NAME |
|
git switch BRANCH |
jj new BRANCH |
|
git switch -c NAME |
jj new main && jj bookmark create NAME |
1.4 履歴操作
| Git コマンド | Jujutsu コマンド | 備考 |
|---|---|---|
git rebase -i |
jj rebase / jj squash / jj split |
個別の操作に分解 |
git rebase main |
jj rebase -d main |
|
git rebase --onto A B C |
jj rebase -r C -d A |
より直感的 |
git cherry-pick COMMIT |
jj duplicate COMMIT |
cherry-pick相当 |
git merge BRANCH |
jj new @ BRANCH |
マージcommitを作成 |
git merge --squash BRANCH |
jj new @ BRANCH && jj squash |
squash merge |
git revert COMMIT |
jj backout -r COMMIT |
|
git reset --hard HEAD~1 |
jj abandon @ |
|
git reset --soft HEAD~1 |
jj squash --from @ --into @- |
変更を親に移動 |
git reset --mixed HEAD~1 |
(Jujutsuにはstaging概念なし) | |
git reflog |
jj op log |
Operation Log |
git commit --fixup=COMMIT |
jj absorb |
自動振り分け |
1.5 リモート操作
| Git コマンド | Jujutsu コマンド | 備考 |
|---|---|---|
git fetch |
jj git fetch |
|
git fetch --all |
jj git fetch --all-remotes |
|
git pull |
jj git fetch && jj rebase -d main@origin |
pull = fetch + rebase |
git pull --rebase |
jj git fetch && jj rebase -d main@origin |
同上(デフォルトでrebase) |
git push |
jj git push |
|
git push -u origin BRANCH |
jj git push --bookmark NAME --allow-new |
|
git push --force-with-lease |
jj git push |
自動的にforce push判定 |
git push --delete origin BRANCH |
jj bookmark delete NAME && jj git push --deleted |
|
git remote add NAME URL |
jj git remote add NAME URL |
|
git remote -v |
jj git remote list |
|
git remote set-url NAME URL |
jj git remote set-url NAME URL |
1.6 その他の操作
| Git コマンド | Jujutsu コマンド | 備考 |
|---|---|---|
git tag v1.0 |
(co-locatedでgit tagを使用) | jjからタグ作成は制限あり |
git tag -a v1.0 -m "MSG" |
(co-locatedでgit tagを使用) | |
git grep "pattern" |
jj file grep "pattern" (予定) |
co-locatedでgit grepを使用 |
git log -S "code" |
(co-locatedでgit logを使用) | pickaxe検索 |
git worktree add PATH |
(未サポート) | |
git submodule update |
(co-locatedでgit submoduleを使用) | Git submoduleを直接使用 |
git lfs pull |
(co-locatedでgit lfsを使用) |
2. 概念の対応マップ
2.1 基本概念の比較
| Git と Jujutsu の概念マッピング |
|---|
| Git Jujutsu |
| ───────────────────── ───────────────────── |
| working directory → working copy (= commit) |
| staging area (index)→ (存在しない) |
| commit → change / commit |
| commit SHA → commit ID + change ID |
| branch → bookmark |
| HEAD → @ (working copy) |
| detached HEAD → (通常状態) |
| reflog → operation log |
| stash → (不要、全てcommit) |
| cherry-pick → duplicate / restore |
| rebase -i → squash, split, rebase |
| commit --amend → edit / describe |
| commit --fixup → absorb |
| tag → tag (Git互換) |
| submodule → (Git submoduleを使用) |
| hook → (未実装、Git hookを使用) |
| .gitignore → .gitignore (共有) |
| .gitattributes → .gitattributes (共有) |
2.2 メンタルモデルの違い
| メンタルモデルの違い |
|---|
| Git: |
| "変更をステージしてコミットする" |
| file edit → git add → git commit → git push |
| (4ステップ) |
| Jujutsu: |
| "常にコミットの上で作業している" |
| file edit → jj describe → jj new → jj git push |
| (file editは自動的にcommitに反映) |
| (3ステップ、実質的にはdescribeとnewだけ) |
2.3 ワークフローの本質的な違い
| Git のワークフロー | |
|---|---|
| 作業ディレクトリ | |
| ▼ git add (選択的にステージ) | |
| ステージングエリア | |
| ▼ git commit (スナップショット作成) | |
| ローカルリポジトリ | |
| ▼ git push | |
| リモートリポジトリ | |
| 特徴: | |
| - ステージングで「何をコミットするか」を制御 | |
| - コミットは明示的な操作 | |
| - 未コミットの変更は消失リスクがある | |
| Jujutsu のワークフロー | |
| ファイル編集 → 自動的にworking copy commitに反映 | |
| ▼ jj describe (メッセージを設定) | |
| working copy commit(常にcommitの上で作業) | |
| ▼ jj new (次のcommitに移動) | |
| commitチェーン(自動的にDAGに組み込み) | |
| ▼ jj git push | |
| リモートリポジトリ | |
| 特徴: | |
| - ステージングが不要 | |
| - ファイル保存 = 自動的にcommitに含まれる | |
| - 後から「分割」(split)で粒度を調整可能 | |
| - 全変更が常にcommitに記録され、消失リスクが低い |
2.4 IDシステムの違い
| Git: commit SHA(コミットID) |
|---|
| - コンテンツアドレス(内容から決定) |
| - rebaseするとSHAが変わる |
| - 参照はrefで管理(HEAD, branch, tag) |
| commit A (sha: abc123) |
| ↓ rebase |
| commit A' (sha: def456) ← SHAが変わる |
| Jujutsu: commit ID + change ID |
| - commit ID: Gitと同じSHA(rebaseで変わる) |
| - change ID: 論理的な変更の識別子(rebaseで不変) |
| commit A (commit-id: abc123, change-id: rlvkpntz) |
| ↓ rebase |
| commit A' (commit-id: def456, change-id: rlvkpntz) |
| ↑ 変わる ↑ 不変 |
| 利点: |
| - rebase後もchange IDで同じ変更を参照できる |
| - スタックドPRでbase変更後もIDが維持される |
| - 「この変更」という概念が安定して追跡可能 |
3. co-located repoの設定と運用
3.1 既存リポジトリでのセットアップ
# Step 1: 既存のGitリポジトリでJujutsuを初期化
$ cd my-git-project
$ jj git init --colocate
Initialized repo in "."
# Step 2: 状態の確認
$ jj log
@ rlvkpntz gaku@example.com 2025-02-11 abc12345
│ (empty) (no description set)
○ qpvuntsm gaku@example.com 2025-02-10 def67890
│ feat: latest commit
◆ zzzzzzzz root() 00000000
# Step 3: .gitignoreにjjのファイルを追加(通常は不要、.jjは自動でignore)
# Step 4: 設定の確認
$ jj config list
# → user.name, user.email等が表示される
# Gitの設定からユーザー情報を自動取得
# → git config のuser.name/emailがデフォルトで使われる3.2 新規リポジトリの作成
# 方法1: jjで新規作成(co-located)
$ jj git init --colocate my-project
$ cd my-project
# 方法2: git cloneしてからjjを追加
$ git clone https://github.com/user/repo.git
$ cd repo
$ jj git init --colocate
# 方法3: jj git clone(自動的にco-located)
$ jj git clone https://github.com/user/repo.git
$ cd repo
# → .git/ と .jj/ の両方が存在する
# 方法4: jjのみのリポジトリ(co-locatedではない)
$ jj git init my-project
$ cd my-project
# → .jj/ のみ、.git/ は .jj/repo/store/git/ 内に隠蔽
# → git コマンドは使えない(推奨しない移行時は)3.3 ディレクトリ構造
my-project/
├── .git/ ← Git のデータ
│ ├── objects/
│ ├── refs/
│ └── ...
├── .jj/ ← Jujutsu のメタデータ
│ ├── repo/
│ │ ├── store/
│ │ │ └── git_target ← "../../../.git" へのパス
│ │ ├── op_store/ ← Operation Log
│ │ └── op_heads/
│ └── working_copy/
├── .gitignore
├── src/
└── ...
3.4 共存時の注意点
# gitコマンドを使った後にjjの状態を同期
$ git fetch origin
$ jj git import # git→jj のref同期(通常は自動)
# jjコマンドを使った後にgitの状態を同期
$ jj git export # jj→git のref同期(通常は自動)
# 同期の確認
$ jj git import && jj git export| 操作 | 自動同期 | 手動同期が必要な場合 |
|---|---|---|
jjコマンド実行後 |
Yes | - |
git fetch実行後 |
Yes | jjコマンドを次に実行した時に自動import |
git commit実行後 |
Yes | jjコマンドを次に実行した時に自動import |
git rebase実行後 |
注意 | jj git importが安全 |
git reset --hard実行後 |
注意 | jj git import推奨 |
git merge実行後 |
Yes | jjコマンドを次に実行した時に自動import |
git stash実行後 |
部分的 | jjからはstashが見えない |
3.5 co-located repoの安全なGitコマンド
# 安全に使えるGitコマンド(jjとの不整合が起きにくい)
$ git status # 状態確認(読み取り専用)
$ git log # ログ確認(読み取り専用)
$ git diff # 差分確認(読み取り専用)
$ git blame # blame確認(読み取り専用)
$ git bisect # bisect(読み取り専用の調査)
$ git grep # ファイル内検索(読み取り専用)
$ git fetch # fetch(jjが自動importする)
$ git stash # stash(jjからは見えない)
$ git tag # タグ作成(jjが自動importする)
# 注意が必要なGitコマンド(jjのOperation Logと不整合の可能性)
$ git commit # → jj commit を推奨
$ git rebase # → jj rebase を推奨
$ git merge # → jj new @ BRANCH を推奨
$ git reset # → jj abandon / jj undo を推奨
$ git checkout -b # → jj new + jj bookmark create を推奨
$ git push --force # → jj git push を推奨4. 段階的移行戦略
4.1 Phase 1: 個人での試用(1-2週間)
# 既存プロジェクトにco-locatedで導入
$ cd my-project
$ jj git init --colocate
# 日常作業をjjで行ってみる
$ jj new main
$ vim src/feature.js
$ jj describe -m "feat: 新機能"
$ jj new
$ jj git push --bookmark feature --allow-new| Phase 1 の目標 |
|---|
| - jj log, jj status, jj diff を使いこなす |
| - jj new, jj describe のリズムを掴む |
| - jj git push/fetch の動作を理解 |
| - 困ったら git に戻れることを確認 |
| この段階では Git コマンドに戻ることを許容 |
| チェックリスト: |
| □ jj log で履歴が読める |
| □ jj new + describe で日常のcommitができる |
| □ jj git push でリモートにpushできる |
| □ jj undo で操作を取り消せる |
| □ jj git fetch で最新を取得できる |
4.2 Phase 2: 高度な機能の活用(2-4週間)
# jj edit による過去commit の直接編集
$ jj edit <change-id>
$ vim src/auth.js
$ jj new
# jj squash / jj split によるcommit整理
$ jj squash
$ jj split src/auth.js src/api.js
# revset クエリの活用
$ jj log -r 'mine() & (main..)'
# jj absorb の活用
$ jj absorb
# スタックドPRの作成
$ jj new main
$ vim src/types.ts
$ jj describe -m "feat: types"
$ jj bookmark create pr/types -r @
$ jj new
$ vim src/auth.ts
$ jj describe -m "feat: auth"
$ jj bookmark create pr/auth -r @
$ jj git push --bookmark pr/types --allow-new
$ jj git push --bookmark pr/auth --allow-new| Phase 2 の目標 |
|---|
| - jj edit で過去commitを直接修正できる |
| - jj squash / jj split でcommitを整理できる |
| - revset で複雑なクエリを書ける |
| - jj absorb で効率的に修正を振り分けられる |
| - スタックドPRのワークフローを実践できる |
| - Operation Log (jj op log) を活用できる |
| チェックリスト: |
| □ jj edit で中間commitを修正できる |
| □ jj squash で不要なcommitを統合できる |
| □ jj split で大きなcommitを分割できる |
| □ revset でフィルタリングできる |
| □ jj absorb で修正を自動振り分けできる |
| □ jj op log / jj undo を活用できる |
4.3 Phase 3: チームへの紹介(4-8週間)
# チーム共有の設定ファイル
# .jj/repo/config.toml (リポジトリレベル設定)
[revset-aliases]
'immutable_heads()' = 'trunk() | tags()'| チーム導入のポイント |
|---|
| 1. co-located repo なので既存の Git ユーザーに |
| 影響を与えない |
| 2. jj ユーザーと git ユーザーが同じリポジトリで |
| 並行して作業できる |
| 3. リモート(GitHub/GitLab)は Git 互換なので |
| サーバー側の変更は不要 |
| 4. PR/MR のワークフローはそのまま維持 |
| 5. CI/CD パイプラインの変更も不要 |
| 6. Git hookは co-located repoで引き続き動作 |
| 7. IDE (VS Code, IntelliJ等) のGit統合も維持 |
4.4 Phase 4: チーム運用の成熟(8週間〜)
# チーム共有の.jj/repo/config.toml
[revset-aliases]
# 保護するcommit
'immutable_heads()' = 'trunk() | tags() | bookmarks("release-")'
# よく使うrevset
'unpushed()' = 'mine() ~ ::remote_bookmarks()'
'review_ready()' = 'bookmarks() & mine() ~ empty() ~ conflict()'
'stale()' = 'bookmarks() & committer_date(before:"30 days ago")'# チーム運用のベストプラクティス
# 1. ブックマーク命名規則
$ jj bookmark create feature/AUTH-123-login -r @
$ jj bookmark create fix/BUG-456-null-check -r @
$ jj bookmark create chore/update-deps -r @
# 2. コミットメッセージ規則(Conventional Commits)
$ jj describe -m "feat(auth): ログイン機能を追加
Refs: AUTH-123"
# 3. PRの作成フロー
$ jj git push --bookmark feature/AUTH-123-login --allow-new
# → GitHub CLIでPR作成
$ gh pr create --base main --head feature/AUTH-123-login
# 4. PRマージ後のクリーンアップ
$ jj git fetch
$ jj bookmark delete feature/AUTH-123-login
$ jj git push --deleted5. 実践: よくあるGitワークフローのJujutsu化
5.1 Feature Branch ワークフロー
# Git:
$ git checkout -b feature/auth main
$ vim src/auth.js
$ git add . && git commit -m "feat: 認証"
$ git push -u origin feature/auth
# PRを作成
# Jujutsu:
$ jj new main
$ vim src/auth.js
$ jj describe -m "feat: 認証"
$ jj bookmark create feature/auth -r @
$ jj git push --bookmark feature/auth --allow-new
# PRを作成(GitHub CLIまたはWeb UI)
# レビュー指摘への対応(Git):
$ git checkout feature/auth
$ vim src/auth.js
$ git add . && git commit -m "fix: レビュー指摘対応"
$ git push
# レビュー指摘への対応(Jujutsu):
$ jj edit feature/auth # 元のcommitを直接編集
$ vim src/auth.js # 修正
$ jj git push --bookmark feature/auth # 更新(force push相当が自動)
# → コミット履歴がクリーンに保たれる5.2 mainへの追従(rebase)
# Git:
$ git fetch origin
$ git checkout feature/auth
$ git rebase origin/main
# コンフリクト解決...
$ git add .
$ git rebase --continue
# さらにコンフリクト解決...
$ git push --force-with-lease
# Jujutsu:
$ jj git fetch
$ jj rebase -d main@origin
# コンフリクトがあればcommitに記録(後で解決可)
$ jj resolve # マージツールで解決
$ jj git push --bookmark feature/auth
# → force push相当が自動的に行われる
# コンフリクト解決の違い
# Git: rebase中にコンフリクトが発生すると中途半端な状態になる
# → git rebase --abort で中断するか、解決して --continue
# jj: コンフリクトはcommitに記録されるので、いつでも解決可能
# → 中途半端な状態にならない
# → 他の作業をしてから後で解決することも可能5.3 コミットの修正
# Git: 直前のcommitを修正
$ git commit --amend -m "fix: corrected message"
# Git: 3つ前のcommitを修正
$ git rebase -i HEAD~3
# → pick → edit に変更
# → 修正
$ git commit --amend
$ git rebase --continue
# Jujutsu: 直前のcommitを修正(メッセージ)
$ jj describe -m "fix: corrected message"
# Jujutsu: 任意のcommitを修正(内容)
$ jj edit <change-id>
$ vim src/auth.js
# → 保存するだけ、自動的にcommitに反映
# → 子commitが自動リベース(コンフリクトがあれば記録)
$ jj new # working copyを先端に戻す
# Jujutsu: 修正をabsorbで自動振り分け
$ vim src/auth.js # 修正
$ vim src/api.js # 修正
$ jj absorb
# → 各行の修正が元のcommitに自動振り分け5.4 stash相当の操作
# Git:
$ git stash
$ git checkout another-branch
$ ... 作業 ...
$ git checkout original-branch
$ git stash pop
# Jujutsu:
# stashは不要 — 全てがcommitとして保存される
# 現在の作業にメモを付けて別の作業へ
$ jj describe -m "wip: ログイン画面の途中"
$ jj new main # mainの上で新しい作業を開始
$ ... 作業 ...
$ jj edit <元のchange-id> # 元の作業に戻る
# → stashは不要、全てがcommitとして保存されている
# 複数の「stash」を同時に管理
$ jj log -r 'description(regex:"^wip:")'
# → wip:で始まるcommitを一覧表示
# → jj edit で任意の作業に戻れる5.5 インタラクティブrebase相当
# Git: git rebase -i HEAD~4
# pick abc Fix A
# squash def Fix B (Aに統合)
# pick ghi Feature C
# drop jkl Remove D
# Jujutsu: 個別の操作に分解
$ jj squash --from def --into abc # BをAに統合
$ jj abandon jkl # Dを削除
# → 自動リベースで残りが整列
# Git: reorder commits
# git rebase -i → 行を並べ替え
# Jujutsu:
$ jj rebase -r ghi -d abc # ghiをabcの直後に移動
# → 他のcommitは自動リベース
# Git: split a commit
# git rebase -i → edit → git reset HEAD~ → git add -p → git commit → git rebase --continue
# Jujutsu:
$ jj split -r <change-id>
# → 対話的に分割(1コマンドで完結)5.6 Gitflowワークフローのjj化
# Gitflow: develop → feature → develop → release → main
# Jujutsuでのマッピング:
# develop = develop ブックマーク
# feature = 個別のcommit/ブックマーク
# feature ブランチ開始
$ jj new develop
$ jj bookmark create feature/login -r @
# feature 作業
$ vim src/login.tsx
$ jj describe -m "feat: ログイン画面"
# develop にマージ
$ jj new develop feature/login # マージcommitを作成
$ jj describe -m "merge: feature/login into develop"
$ jj bookmark set develop -r @
# release ブランチ
$ jj new develop
$ jj bookmark create release/1.0 -r @
# hotfix
$ jj new main
$ jj bookmark create hotfix/critical-fix -r @
$ vim src/fix.js
$ jj describe -m "hotfix: クリティカルな修正"
# mainとdevelopにマージ
$ jj new main hotfix/critical-fix
$ jj bookmark set main -r @
$ jj new develop hotfix/critical-fix
$ jj bookmark set develop -r @5.7 Trunk-Based Development のjj化
# Trunk-Based: mainに直接(短寿命ブランチで)
# 短寿命の作業ブランチ
$ jj new main
$ vim src/feature.js
$ jj describe -m "feat: 小さな機能追加"
$ jj bookmark create small-feature -r @
$ jj git push --bookmark small-feature --allow-new
# → PRを作成、レビュー後すぐにマージ
# スタックドPRで大きな変更を分割
$ jj new main
$ jj describe -m "refactor: 型定義の整理"
$ jj bookmark create pr/1-types -r @
$ jj new
$ jj describe -m "feat: 新しいAPI"
$ jj bookmark create pr/2-api -r @
$ jj new
$ jj describe -m "feat: UIの更新"
$ jj bookmark create pr/3-ui -r @
# 各PRを順番にレビュー&マージ
$ jj git push --bookmark pr/1-types --allow-new
$ jj git push --bookmark pr/2-api --allow-new
$ jj git push --bookmark pr/3-ui --allow-new6. 移行時のトラブルシューティング
6.1 よくある問題と解決法
# 問題1: jj と git の状態が不整合になった
$ jj git import # git→jj の同期を強制
$ jj git export # jj→git の同期を強制
# 問題2: jj git push で認証エラー
$ jj git remote set-url origin git@github.com:user/repo.git # SSH化
# または
$ gh auth setup-git # GitHub CLI のトークンを設定
# 問題3: working copy が予期しない状態になった
$ jj op log # 操作履歴を確認
$ jj undo # 直前の操作を取り消し
$ jj op restore <op-id> # 特定の時点に復元
# 問題4: .jjを完全に削除してGitだけに戻りたい
$ rm -rf .jj
$ git checkout . # working copyをGitの状態に復元
# 問題5: jj git fetch でエラー
# "unexpected response from remote"
$ jj git remote set-url origin git@github.com:user/repo.git
# SSH鍵の問題:
$ ssh -T git@github.com # SSH接続テスト
$ eval "$(ssh-agent -s)" && ssh-add ~/.ssh/id_ed25519
# 問題6: co-located repoでGitコマンドがworking copyを変更した
$ jj git import # gitの変更をjjに取り込む
# それでも不整合な場合:
$ jj workspace update-stale # working copyの状態を更新6.2 コンフリクト解決の違い
# Git: rebase中のコンフリクト
$ git rebase main
# CONFLICT (content): Merge conflict in src/auth.js
# → 中途半端な状態("rebase in progress")
# → 解決してgit add → git rebase --continue
# → または git rebase --abort で中断
# Jujutsu: rebase時のコンフリクト
$ jj rebase -d main
# → コンフリクトはcommitに記録される
# → 中途半端な状態にはならない
# → jj status でコンフリクトの状態を確認
$ jj status
# Working copy changes:
# C src/auth.js (conflict)
# コンフリクトの解決
$ jj resolve src/auth.js
# → マージツールが起動
# → 解決するとコンフリクトマーカーが消える
# または手動で解決
$ vim src/auth.js
# → コンフリクトマーカーを削除
# → 保存するだけでコンフリクトが解消される
# コンフリクトを含むcommitの上に作業を続けることも可能
# → 後で解決すればよい
$ jj new # コンフリクトを放置して次の作業へ
$ ... 別の作業 ...
$ jj edit <conflict-commit> # 後でコンフリクトを解決
$ vim src/auth.js6.3 大規模リポジトリでの注意点
# 大規模リポジトリでのパフォーマンス
# 1. watchmanの有効化
$ jj config set --user core.watchman.register-snapshot-trigger true
# 2. fsmonitorの設定
$ jj config set --user core.fsmonitor "watchman"
# 3. デフォルトrevsetの制限
$ jj config set --user ui.default-revset 'ancestors(heads(all()), 10)'
# → ログ表示を最新10世代に制限
# 4. .gitignoreの最適化
# → 不要なファイル(node_modules, build/等)を確実にignore
# → jjのworking copy snapshotが高速化される
# 5. 大規模なバイナリファイル
# → Git LFS を使用(co-located repoでgit lfsコマンドを使用)
$ git lfs install
$ git lfs track "*.psd"7. IDE統合
7.1 VS Code
# co-located repoではVS CodeのGit統合がそのまま使える
# → Source Control パネルでdiff、commit、pushが可能
# → ただし、jjのworking copy概念と少しズレがある
# 推奨設定:
# 1. VS CodeのTerminalでjjコマンドを使用
# 2. Source Control パネルはdiff確認用として使用
# 3. commit/pushはjjコマンドで実行
# VS Code拡張(存在する場合)
# → "Jujutsu" 拡張を検索してインストール
# settings.json
{
"git.enabled": true,
"git.autofetch": true,
// jjのworking copyと競合しないよう設定
"git.autoStash": false,
"git.confirmSync": true
}7.2 IntelliJ IDEA / JetBrains
# co-located repoではIntelliJのGit統合がそのまま使える
# → Version Control ツールウィンドウでdiff、log確認
# → ただしcommitはjjコマンドで行うのが安全
# 推奨設定:
# 1. IntelliJのTerminalでjjコマンドを使用
# 2. VCS操作はdiff/blame/log確認用として使用
# 3. commit/push/rebaseはjjコマンドで実行7.3 lazyjj (TUIツール)
# lazyjjのインストール
$ cargo install lazyjj
# 使用
$ lazyjj
# → lazygit風のTUIでjjの操作ができる
# → log表示、diff、describe、new、squash等が視覚的に操作可能
# キーバインド
# j/k: 上下移動
# Enter: 詳細表示
# d: diff表示
# n: jj new
# e: jj edit
# s: jj squash
# q: 終了8. チートシート
8.1 日常操作のクイックリファレンス
# === 朝一番の同期 ===
$ jj git fetch # リモートの最新を取得
$ jj rebase -d main@origin # mainに追従
# === 新しい作業の開始 ===
$ jj new main # mainから新しい変更を開始
$ jj describe -m "feat: 機能名" # メッセージを設定
# === ファイル編集中 ===
$ vim src/feature.ts # 編集(自動的にcommitに反映)
$ jj status # 状態確認
$ jj diff # 差分確認
# === 変更の確定と次の作業 ===
$ jj describe -m "feat: 完成した機能" # メッセージ確定
$ jj new # 次のcommitに移動
# === PRの作成 ===
$ jj bookmark create feature-x -r @- # 直前のcommitにブックマーク
$ jj git push --bookmark feature-x --allow-new
# === 過去のcommitを修正 ===
$ jj edit <change-id> # 過去のcommitに移動
$ vim src/fix.ts # 修正
$ jj new # 先端に戻る
# === 操作の取り消し ===
$ jj undo # 直前の操作を取り消し
$ jj op log # 操作履歴を確認
$ jj op restore <op-id> # 特定時点に復元8.2 GitユーザーのためのJujutsu早見表
| GitユーザーのためのJujutsu早見表 |
|---|
| "ファイルを修正した": |
| Git: git add . && git commit -m "msg" |
| jj: jj describe -m "msg" (addは不要) |
| "前のcommitに戻りたい": |
| Git: git reset --hard HEAD~1 |
| jj: jj abandon @ |
| "ブランチを作りたい": |
| Git: git checkout -b new-branch |
| jj: jj new && jj bookmark create new-branch |
| "他の人の変更を取り込みたい": |
| Git: git pull --rebase |
| jj: jj git fetch && jj rebase -d main@origin |
| "push前にcommitを整理したい": |
| Git: git rebase -i HEAD~5 |
| jj: jj squash / jj split / jj rebase |
| "間違った操作を取り消したい": |
| Git: git reflog + git reset |
| jj: jj undo (一発で完了) |
| "PRのレビュー指摘に対応したい": |
| Git: git commit -m "fix: review" (commitが増える) |
| jj: jj edit <commit> + 修正 (履歴がクリーン) |
| "作業を一時的に中断したい": |
| Git: git stash |
| jj: jj new main (作業は自動保存されている) |
| "複数のPRを効率的に管理したい": |
| Git: 複数ブランチの手動管理 |
| jj: スタックドPR (自動リベースで連動) |
9. アンチパターン
アンチパターン1: co-located repoでGitとJujutsuの破壊的操作を混在させる
# NG: jjで作業した後にgit reset --hardする
$ jj new main
$ vim src/feature.js
$ jj describe -m "feat: new feature"
$ git reset --hard HEAD # ← jjの変更が消失する可能性
# → jjのOperation Logと不整合が生じる
# OK: 一貫してjjコマンドを使う
$ jj undo # jjの操作取り消し
$ jj op restore <op-id> # 特定時点への復元理由: co-located repoではjjとgitがオブジェクトストアを共有している。gitの破壊的操作はjjのメタデータ(Operation Log等)と不整合を起こす可能性がある。
アンチパターン2: チーム全員に一度にJujutsuを強制する
# NG: "来週からJujutsu必須です"
# → 学習コストが高く、生産性が一時的に大幅低下
# → Gitに戻したい人のモチベーション低下
# OK: co-located repoで段階的に導入
# Phase 1: 興味のあるメンバーから試用
# Phase 2: 成功事例を共有
# Phase 3: 公式にサポートするが強制しない
# → GitユーザーとJujutsuユーザーが共存可能理由: Jujutsuはco-located repoにより、Gitユーザーに影響を与えずに導入できる。強制ではなく段階的な移行が最も効果的。
アンチパターン3: Gitの癖でjj add を探す
# NG: stagingの概念を持ち込む
$ vim src/auth.ts
$ jj add src/auth.ts # ← このコマンドは存在しない
# OK: jjにはstagingがないことを理解する
$ vim src/auth.ts
# → 保存するだけで自動的にworking copy commitに含まれる
# → 特定のファイルだけcommitしたい場合はjj split理由: Jujutsuの「常にcommitの上で作業する」モデルではstagingが不要。ファイルの保存が即座にcommitに反映される。粒度の調整は事後的にsplit/squashで行う。
アンチパターン4: jjでworking copyの変更を確認せずにjj newする
# NG: 意図しない変更が含まれたままnew
$ vim src/auth.ts
$ vim src/unrelated.ts # 関係ないファイルも編集してしまった
$ jj new # ← 両方の変更が前のcommitに含まれる
# OK: statusを確認してから次へ進む
$ jj status # 変更内容を確認
$ jj diff # 差分を確認
# 不要な変更を取り消す
$ jj restore --from @- src/unrelated.ts
$ jj new # クリーンな状態で次へ理由: jjではstagingがないため、全てのファイル変更がworking copy commitに含まれる。jj statusで確認し、不要な変更はjj restoreで取り消すか、後でjj splitで分離する。
アンチパターン5: bookmarkを作らずにpushしようとする
# NG: bookmarkなしでpush
$ jj new main
$ vim src/feature.ts
$ jj describe -m "feat: new feature"
$ jj git push # ← pushするブックマークがない!
# OK: bookmarkを作成してからpush
$ jj bookmark create feature-x -r @
$ jj git push --bookmark feature-x --allow-new
# または --change で自動生成
$ jj git push --change @ # ← change IDからブックマーク名を自動生成理由: jj git pushはブックマークをGitブランチに変換してpushする。ブックマークがないcommitはpushの対象にならない。--changeオプションで自動生成するか、明示的にブックマークを作成する。
アンチパターン6: jj editした後にjj newを忘れる
# NG: editしたまま作業を続ける
$ jj edit <old-change-id> # 過去のcommitに移動
$ vim src/fix.ts # 修正
$ vim src/new-feature.ts # ← 新しい機能も追加(混在!)
# OK: 修正が終わったらjj newで先端に戻る
$ jj edit <old-change-id> # 過去のcommitに移動
$ vim src/fix.ts # 修正のみ
$ jj new # 先端に戻る
$ vim src/new-feature.ts # 新しい機能は別のcommitで理由: jj editで過去のcommitを修正する場合、そのcommitのスコープを超える変更を加えるべきではない。修正が終わったらjj newで新しいcommitに移動する。
10. FAQ
Q1. Jujutsuに移行するとGitの履歴は失われるか?
A1. いいえ、全く失われません。Jujutsuは内部的にGitのオブジェクトストアを使用しており、co-located repoでは.git/がそのまま維持されます。全てのコミット履歴、タグ、ブランチ、reflogがそのまま保持されます。
# co-located repoの確認
$ ls -la .git/ .jj/
# → 両方が存在
$ git log --oneline -5
# → Gitの履歴がそのまま
$ jj log
# → 同じ履歴がjjからも見えるQ2. GitHub/GitLabのPRワークフローはそのまま使えるか?
A2. はい、完全に互換性があります。jj git pushはGitのブランチとしてリモートにpushされるため、GitHub/GitLabはそれを通常のGitブランチとして認識します。PR/MRの作成、レビュー、マージは従来通りです。
# JujutsuでPRを作成するフロー
$ jj new main
$ vim src/feature.js
$ jj describe -m "feat: new feature"
$ jj bookmark create feature-branch -r @
$ jj git push --bookmark feature-branch --allow-new
# → GitHubでPRを作成(通常のGitと全く同じ)
# GitHub CLIを使ったPR作成
$ gh pr create --base main --head feature-branch --title "feat: new feature"Q3. Jujutsuにはまだ不足している機能はあるか?
A3. 2025年時点で以下の機能がGitと比較して未実装または限定的です。
| 機能 | 状態 | 代替手段 |
|---|---|---|
bisect |
未実装 | co-locatedでgit bisectを使用 |
blame |
未実装 | co-locatedでgit blameを使用 |
| hooks | 未実装 | co-locatedでGit hooksを使用 |
| submodule | 部分サポート | Git submoduleコマンドを併用 |
| sparse checkout | 未実装 | git sparse-checkoutを使用 |
| GUI ツール | 限定的 | lazyjj(TUIツール) |
| IDE統合 | 一部IDE対応中 | co-locatedでGit IDE統合を使用 |
| shallow clone | 未実装 | git clone --depthを使用 |
| LFS | 部分的 | co-locatedでgit lfsを使用 |
| worktree | 未サポート | jj workspace(別の概念) |
Q4. Jujutsuを使い始めて最初に混乱するポイントは?
A4. 以下の3点が最も一般的な混乱ポイントです。
# 混乱1: staging(git add)がない
# → ファイルを保存するだけで自動的にcommitに含まれる
# → 「特定のファイルだけcommitしたい」場合はjj split
# 混乱2: working copy = commit
# → Gitでは「未コミットの変更」が存在するが、jjでは常にcommitの上にいる
# → jj log で @ がworking copy commit
# → jj new で「次のcommitに移動」
# 混乱3: bookmark ≠ branch
# → Gitのbranchは「移動するポインタ」
# → jjのbookmarkも類似だが、自動追従はしない
# → commitを追加してもbookmarkは自動的に移動しない
# → jj bookmark set NAME -r @ で明示的に移動Q5. Jujutsuを元に戻してGitだけに戻すには?
A5. co-located repoの場合、.jjディレクトリを削除するだけです。
# Jujutsuを完全に削除
$ rm -rf .jj
# Gitの状態を確認
$ git status
$ git log --oneline -5
# → Gitの履歴は完全に維持されている
# working copyの状態がおかしい場合
$ git checkout .
# → Gitの最新状態に復元Q6. jjのバージョンアップ時に注意することは?
A6. 基本的にjjはバージョン間の互換性を維持していますが、以下に注意してください。
# バージョン確認
$ jj --version
# アップグレード
$ cargo install jj-cli
# または
$ brew upgrade jj
# アップグレード後の注意
# - .jj/repo/ のフォーマットが変わる場合がある
# - jjが自動的にマイグレーションを行う
# - 問題がある場合は jj op log で確認して jj undoQ7. 大規模なモノレポでjjは使えるか?
A7. 使えますが、パフォーマンス設定が重要です。
# watchmanの有効化(必須)
$ jj config set --user core.watchman.register-snapshot-trigger true
$ jj config set --user core.fsmonitor "watchman"
# revsetのデフォルトを制限
$ jj config set --user ui.default-revset 'ancestors(heads(all()), 10)'
# .gitignoreの最適化
# → node_modules, build, dist等を確実にignore
# → jjのsnapshot処理が高速化
# 注意: 数GBのリポジトリでも動作するが、
# 数十万ファイルの場合はwatchman必須Q8. jjとgitを使い分けるべきシーンは?
A8. 基本的にjjで行い、以下の場面でgitコマンドを使います。
# gitを使うべきシーン(co-located repo):
$ git blame src/auth.ts # blameはgitで
$ git bisect start # bisectはgitで
$ git grep "searchterm" # ファイル内検索
$ git log -S "deleted_function" # pickaxe検索
$ git stash # 一時退避(jjからは見えない)
$ git tag -a v1.0 -m "Release" # アノテーションタグ
$ git lfs pull # LFSファイル取得
$ git submodule update # サブモジュール更新
# jjを使うべきシーン:
$ jj new / jj describe / jj commit # コミット操作
$ jj edit / jj squash / jj split # 履歴の編集
$ jj rebase # リベース
$ jj absorb # 修正の自動振り分け
$ jj git push / jj git fetch # リモート操作
$ jj undo / jj op restore # 操作の取り消し
$ jj log -r '<revset>' # 高度なログ検索FAQ
Q1: このトピックを学ぶ上で最も重要なポイントは何ですか?
実践的な経験を積むことが最も重要です。理論だけでなく、実際にコードを書いて動作を確認することで理解が深まります。
Q2: 初心者がよく陥る間違いは何ですか?
基礎を飛ばして応用に進むことです。このガイドで説明している基本概念をしっかり理解してから、次のステップに進むことをお勧めします。
Q3: 実務ではどのように活用されていますか?
このトピックの知識は、日常的な開発業務で頻繁に活用されます。特にコードレビューやアーキテクチャ設計の際に重要になります。
まとめ
| 概念 | 要点 |
|---|---|
| co-located repo | .git/と.jj/が共存、GitとJujutsu両方使用可能 |
| jj git init --colocate | 既存Gitリポジトリに即座にJujutsuを追加 |
| 操作対応 | git add→不要、git commit→jj commit、git branch→jj bookmark |
| メンタルモデル | "ステージ→コミット"から"常にcommit上で作業"に転換 |
| change ID | rebase後も不変の識別子、commit IDとは独立 |
| 自動同期 | jjコマンド実行時にgitとの同期が自動的に行われる |
| 段階的移行 | 個人試用→高度な活用→チーム紹介→チーム運用の4段階 |
| undo | jj undo / jj op restoreで安全に復元可能 |
| コンフリクト | commitに記録、後から解決可能(中途半端な状態にならない) |
| スタックドPR | 依存関係のあるPRを効率的に管理、自動リベースで連動 |
| IDE統合 | co-located repoでGit IDE統合をそのまま利用可能 |
次に読むべきガイド
- Jujutsu入門 — 基本概念と設計思想の復習
- Jujutsuワークフロー — 変更セットと自動リベースの実践
- Jujutsu応用 — revset、テンプレート、Git連携
参考文献
- Jujutsu公式ドキュメント — "Git comparison" https://martinvonz.github.io/jj/latest/git-comparison/
- Jujutsu公式ドキュメント — "Git compatibility" https://martinvonz.github.io/jj/latest/git-compatibility/
- Jujutsu公式ドキュメント — "Tutorial" https://martinvonz.github.io/jj/latest/tutorial/
- Steve Klabnik — "jj init" (Gitからの移行体験記) https://steveklabnik.com/writing/jj-init
- Jujutsu GitHub Discussions — Migration tips and tricks https://github.com/martinvonz/jj/discussions
- lazyjj — TUI for Jujutsu https://github.com/Cretezy/lazyjj