Git の次へ。jj(Jujutsu)が変えるバージョン管理の常識
はじめに
「git stash し忘れてチェックアウトできない」
「git rebase でコンフリクトの嵐」
「git reset --hard で作業が消えた...」
Git を使っていて、こんな経験はありませんか?
jj(Jujutsu) は、これらの Git の痛みをすべて解消するために設計された、次世代のバージョン管理システムです。Google のエンジニアが開発し、Rust で書かれたこのツールは、Git リポジトリとの完全な互換性を持ちながら、根本的に優れたワークフローを提供します。
この記事では、jj の魅力と基本的な使い方を紹介します。
jj とは何か
Jujutsu(柔術)は、Git と互換性のあるバージョン管理システムです。既存の Git リポジトリの上にレイヤーとして動作し、チームメイトに影響を与えることなく導入できます。
最大の特徴:ロックインなし
jj は Git リポジトリをストレージとして使用します。つまり:
- 既存の Git リポジトリでそのまま使える
- チームメイトは jj を使っていることに気づかない
- いつでも Git に戻れる
- VS Code などの Git 連携ツールもそのまま動く
「試してみてダメならやめる」が簡単にできるのは、新しいツールを試す上で最高の条件です。
Git の何が問題なのか
jj の良さを理解するために、まず Git の設計上の問題点を整理します。
1. ステージングエリア(インデックス)の複雑さ
# Git: 3つの状態を管理しなければならない
git add -p # ワーキングツリー → ステージング
git commit # ステージング → コミット
git reset HEAD # ステージング解除
git checkout -- . # ワーキングツリーの変更破棄
git reset の各フラグ(--soft、--mixed、--hard)の違いを正確に説明できる人はどれくらいいるでしょうか?ステージングエリアは Git の学習コストを大幅に上げている元凶です。
2. ダーティなワーキングツリー問題
$ git checkout feature-branch
error: Your local changes to the following files would be overwritten by checkout:
src/main.rs
Please commit your changes or stash them before you switch branches.
Git ユーザーなら何度も見たことのあるエラーです。「ちょっとブランチ切り替えたいだけなのに...」
3. コンフリクト解消の強制
Git では rebase 中にコンフリクトが発生すると、その場で解消しないと先に進めません。複数コミットの rebase では、コミットごとにコンフリクトを解消する必要があり、気が遠くなることも。
4. 取り消しの難しさ
# やってしまった...
git reset --hard HEAD~3
# 戻したい...でも reflog って何?
git reflog # ???
Git の undo は reflog という分かりにくい仕組みに頼っており、初心者にはハードルが高すぎます。
jj が解決する世界
1. ステージングエリアが存在しない
jj ではワーキングコピーそのものがコミットです。ファイルを編集した瞬間、その変更は自動的に現在のコミットに記録されます。
# jj: ファイルを編集するだけ。add は不要
vim src/main.rs
# 変更は自動的に現在のコミットに反映されている
jj diff # 現在の変更を確認
jj describe -m "機能Aを実装" # コミットメッセージを設定
jj new # 新しい空のコミットを作成して次の作業へ
git add の呪縛から解放されます。
2. ダーティなワーキングツリーがない
ワーキングコピーが常にコミットなので、「ダーティな状態」という概念がありません。
# jj: いつでも自由にコミット間を移動
jj edit <revision> # 任意のコミットに移動。stash 不要!
git stash は永遠に不要になります。
3. コンフリクトをコミットできる
jj ではコンフリクトは第一級オブジェクトです。コンフリクトを含むコミットをそのまま保存でき、後で好きなタイミングで解消できます。
# jj: コンフリクトが発生しても作業を続行できる
jj rebase -r @- -d main
# コンフリクトが発生しても、そのままコミットされる
# 後で解消すればOK
jj resolve # 好きなタイミングで解消
さらに、コンフリクトを解消すると子孫コミットにも自動的に伝播されます。
4. 何でも undo できる
jj はすべての操作をオペレーションログに記録します。
jj undo # 直前の操作を取り消し
jj op log # 操作履歴を確認
jj op restore <op-id> # 任意の時点に復元
git reset --hard で消してしまった作業も、jj なら一発で戻せます。
5. 自動リベース
コミット履歴を変更すると、子孫コミットが自動的にリベースされます。
# あるコミットを修正すると、その下のコミットも自動で追従
jj edit <古いコミット>
# 修正を加える
jj new # 新しい作業に戻る
# → 子孫コミットは自動的にリベースされている!
Git のような手動 rebase の苦行がなくなります。
Git vs jj コマンド比較
| やりたいこと | Git | jj |
|---|---|---|
| リポジトリ初期化 | git init |
jj git init |
| クローン | git clone <url> |
jj git clone <url> |
| 状態確認 | git status |
jj st |
| 差分確認 | git diff HEAD |
jj diff |
| コミット | git add -A && git commit |
jj commit |
| メッセージ変更 | git commit --amend --only |
jj describe |
| ログ表示 | git log --oneline --graph |
jj log |
| ブランチ作成 | git checkout -b <name> |
jj bookmark create <name> |
| スタッシュ | git stash |
jj new @-(概念自体が不要) |
| スカッシュ | git commit --amend -a |
jj squash |
| リベース | git rebase <target> |
jj rebase -d <target> |
| 変更の破棄 | git reset --hard |
jj abandon |
| blame | git blame <file> |
jj file annotate <file> |
| 取り消し | ❌(reflog で頑張る) | jj undo |
| フェッチ | git fetch |
jj git fetch |
| プッシュ | git push |
jj git push |
インストール方法
macOS(Homebrew)
brew install jj
Linux / macOS(cargo-binstall)
cargo binstall --strategies crate-meta-data jj-cli
ソースからビルド
cargo install --git https://github.com/jj-vcs/jj.git --locked --bin jj jj-cli
mise を使う場合
mise install jujutsu@latest
セットアップ
初期設定
# ユーザー名とメールアドレスの設定(必須)
jj config set --user user.name "Your Name"
jj config set --user user.email "your@email.com"
既存の Git リポジトリで使い始める
cd your-git-repo
jj git init --colocate
これだけで、既存の Git リポジトリで jj が使えるようになります。.git ディレクトリはそのまま残り、jj が自動的に同期を保ちます。
実践ワークフロー
基本的な開発フロー
# 1. 新しいコミットを作成して作業開始
jj new -m "ユーザー認証機能を追加"
# 2. ファイルを編集(add 不要、自動追跡)
vim src/auth.rs
vim src/routes.rs
# 3. 変更を確認
jj diff
jj st
# 4. 次の作業へ(現在のコミットはそのまま確定)
jj new -m "テストを追加"
# 5. ログで確認
jj log
過去のコミットを修正する
# 1. ログで修正したいコミットを探す
jj log
# 2. そのコミットを編集(stash 不要!)
jj edit <change-id>
# 3. ファイルを修正
vim src/auth.rs
# 4. 最新のコミットに戻る
jj new
# → 間のコミットは自動リベースされる
コミットを分割する
# 大きすぎるコミットを分割
jj split
# → TUI が開き、ファイルやハンクを選択して分割できる
GitHub にプッシュ
# ブックマーク(= Git ブランチ)を作成
jj bookmark create feature-auth -r @-
# プッシュ
jj git push
jj ならではの便利機能
Revsets:コミットのクエリ言語
jj には Revsets という強力なクエリ言語があります。
# 自分のコミットだけ表示
jj log -r "author(email:your@email.com)"
# main から分岐したコミットを表示
jj log -r "main..@"
# マージコミットを除外
jj log -r "~merges()"
任意のリビジョンに対して操作
Git では多くの操作がチェックアウト中のブランチに制限されますが、jj では任意のリビジョンに対して操作できます。
# チェックアウトせずに任意のコミットを squash
jj squash -r <revision>
# チェックアウトせずに任意のコミットにリベース
jj rebase -r <revision> -d <destination>
並行作業の安全性
jj は並行操作に対して安全に設計されています。rsync や Dropbox でリポジトリを同期しても、リポジトリが壊れることはありません。
こんな人に jj がおすすめ
- Git の stash / index / reset に疲れた人
- rebase のコンフリクト解消で消耗している人
- 「Git わからん」と思いつつ使い続けている人
- 新しいツールを試すのが好きな人
- Git のパワーは欲しいが、もっとシンプルなインターフェースが欲しい人
注意点・現状の限界
jj はまだ発展途上のプロジェクトです。導入前に知っておくべき点:
- GitHub / GitLab との親和性:rebase 主体のワークフローは、force push でレビューコメントが消える GitHub の仕組みと相性が良くない場面がある
- IDE サポート:VS Code 向けの VisualJJ 拡張があるが、Git ほどの充実度ではない
- エコシステム:Git に比べるとコミュニティやツールはまだ小さい
- 学習コスト:Git とは異なるメンタルモデル(特に change vs commit の概念)に慣れる必要がある
ただし、Git リポジトリと完全に共存できるため、リスクは最小限です。合わなければ jj を使うのをやめるだけで済みます。
まとめ
| 観点 | Git | jj |
|---|---|---|
| ステージングエリア | あり(複雑) | なし(シンプル) |
| ワーキングコピー | ダーティ状態あり | 常にコミット |
| コンフリクト | 即時解消が必要 | 後回しにできる |
| undo | reflog(難しい) |
jj undo(簡単) |
| リベース | 手動 | 自動 |
| Git 互換性 | - | 完全互換 |
jj は「Git をもっと良くしたもの」です。Git の知識は無駄にならず、Git リポジトリもそのまま使えます。失うものは何もなく、得るものは大きい。
まずは既存のリポジトリで jj git init --colocate を試してみてください。Git では味わえない快適さが待っています。
参考リンク
- jj 公式リポジトリ(GitHub)
- jj 公式ドキュメント
- jj チュートリアル
- Git コマンド比較表
- Jujutsu for Everyone
- Steve Klabnik の jj チュートリアル
- jj チートシート(PDF)
- jj VCS Introduction and Patterns
- What I've learned from jj
発表情報
この記事の内容を chigasaki.rb #14 の勉強会で発表しました。
Discussion
呪術かもしれない…
えぇぇ 呪術だったんですね😆
ずっと柔術だと思ってました。ありがとうございます!直します!
確かに柔術ならjiu-jitsuですしね。ジウジツ
英語圏では慣習的に日本の古流柔術を Jujutsu、ブラジリアン柔術を Jiu-jitsu と綴るらしいです。
作者の Zweigbergk 氏は jj というコマンドを先に決めていて、それに見合いそうな言葉を探して Jujutsu に行き着いたとのこと。
だから本人曰く、柔術なのか呪術なのかはどっちでもいい。魔法のようなという意味で呪術は魅力的だけど、『呪術廻戦』の影響でスラングっぽく聞こえるのは望ましくない。自分は日本語を知らないので、どうするかは決めかねる。というところで議論は終わってます。
英語では ju を伸ばしても伸ばさなくても発音がさして変わらない(ジュ→ジュ↑ツ→)というのもあって、そこまで重要な問題ではなさそう。
日本人としてはちゃんと決めてほしいとこですが、Jujutsu は Jujutsu でしかないということのようです。
そんなところも、change の description は空でもいいし、jj desc でいつでも気軽に書き換えられる Jujutsu らしいと言えるかもしれません。
「ジェイジェイ」でいいんじゃないかなー
検索性は落ちそうだけど。
コードベースに詳しくない人(客先の社内SEさんとかデザイナーさんとか?)にjj使ってもらうというのもいいと思いました。マージはエンジニア頑張ってもらって。
そのためにもGUIなど周辺ツールやエコシステムが広がると嬉しいですね。