こんにちは、kogureです。 勉強会のお題を考えているときにEMから今、巷で流行っているJujutsuどう?とお勧めされたので触ってみました!
そもそもJujutsuってなんなんでしょうか?
公式のreadme> Introductionを確認してみましょう
- Git互換の新しいVCSで既存のGitツールやGitHub等とそのまま連携可能
- 作業コピーへの変更が自動的にコミットとして記録される
- 操作ログと元に戻し機能:すべての操作が記録され、ミスを簡単に取り消せる etc...
原文が英語なので翻訳しながら確認したのでわかっていない部分もありますが、 Gitよりあまり操作を意識せずにやってくれる印象でしょうか?
ちなみに読み方は「呪術」のようです
大枠を知ったところで実際に触ってみます!!!
まずはインストールから MacでHomebrewがインストール済みの場合は以下のコマンドでインストールができます。
brew install jj
他のインストール方法は以下のURLを参照して適宜実行してください。
インストールが完了したことを確認しています
[kogure:~/Src] $ brew install jj ==> Fetching downloads for: jj ✔︎ Bottle Manifest jj (0.38.0) Downloaded 8.1KB/ 8.1KB ✔︎ Bottle jj (0.38.0) Downloaded 9.2MB/ 9.2MB ==> Pouring jj--0.38.0.arm64_tahoe.bottle.tar.gz 🍺 /opt/homebrew/Cellar/jj/0.38.0: 125 files, 22.7MB ==> Running `brew cleanup jj`... Disable this behaviour by setting `HOMEBREW_NO_INSTALL_CLEANUP=1`. Hide these hints with `HOMEBREW_NO_ENV_HINTS=1` (see `man brew`). ==> Caveats zsh completions have been installed to: /opt/homebrew/share/zsh/site-functions [kogure:~/Src] $ which jj /opt/homebrew/bin/jj [kogure:~/Src] $ jj --version jj 0.38.0
これにてインストールが完了しました。
次にまずはgit cloneに相当する操作を行います。 動作確認用のリポジトリは作成済みなので用意したリポジトリを使用します。
jj git clone <Git リポジトリの URL>
特に説明することはないので実行してみましょう
[kogure:~/Src] $ jj git clone git@github.com:a-kogure/jj-test.git Warning: Name and email not configured. Until configured, your commits will be created with the empty identity, and can't be pushed to remotes. Hint: To configure, run: jj config set --user user.name "Some One" jj config set --user user.email "someone@example.com" Fetching into new repo in "/Users/kogure/Src/jj-test" Nothing changed. Hint: Running `git clean -xdf` will remove `.jj/`! [kogure:~/Src] $ ls -l | grep jj drwxr-xr-x@ 4 kogure staff 128 Mar 1 22:27 jj-test
クローンも無事完了しました
次にファイルを作成、編集してみてGitとどのような違いがあるのかを確認してみます。 新たにファイルを作成してみます
確認は以下のコマンドを使っています
jj status
jj log
[kogure:~/Src/jj-test][main] $ touch hoge.txt [kogure:~/Src/jj-test][main] $ ls hoge.txt [kogure:~/Src/jj-test][main] $ jj status Working copy changes: A hoge.txt Working copy (@) : prvqumkn 0233b6a4 (no description set) Parent commit (@-): zzzzzzzz 00000000 (empty) (no description set) [kogure:~/Src/jj-test]!+[main] $ jj log @ prvqumkn (no email set) 2026-03-01 22:42:59 0233b6a4 │ (no description set) ◆ zzzzzzzz root() 00000000 [kogure:~/Src/jj-test]!+[main] $ touch fuga.txt [kogure:~/Src/jj-test]!+[main] $ jj status Working copy changes: A fuga.txt A hoge.txt Working copy (@) : prvqumkn 3adf37ea (no description set) Parent commit (@-): zzzzzzzz 00000000 (empty) (no description set) [kogure:~/Src/jj-test]!+[main] $ jj log @ prvqumkn (no email set) 2026-03-01 22:49:28 3adf37ea │ (no description set) ◆ zzzzzzzz root() 00000000 [kogure:~/Src/jj-test]!+[main] $ $
さっぱり見方がわからないのでチュートリアルを読んでみます。 docs.jj-vcs.dev
まずChange IDとCommit IDという2種類のIDがあるようです。
- Change ID(
prvqumkn):作業の論理的なID。中身を書き換えても変わらない - Commit ID(
3adf37ea):Gitのハッシュと同じ。ファイルを変更するたびに変わる
Gitだとamendやrebaseするとハッシュが変わって追えなくなりますが、 JujutsuはChange IDがあるので書き換え後も同じIDで追跡できるとのことですが 正直まだ何が嬉しいのかわからないですね。。。
これを踏まえて出力をもう一度見てみると、git add を一切していないのに
jj statusで A hoge.txt と表示されています。
さらにfuga.txtを作っただけでCommit IDが 0233b6a4 → 3adf37ea に変わっています。
一方でChange IDは prvqumkn のままでした。
これがJujutsuとGitの違う点で、作業中のファイル(ワーキングコピー)が 常にコミットとして扱われています。 Gitだと「編集 → add → commit」の3ステップですが、 Jujutsuだと編集するたびに自動でコミットに反映されるので、git addが不要になります。
では次の作業に進んでみます。
まず jj describe で今のコミットにメッセージを付けます。
Gitの git commit -m に近いですが、すでにコミットされているものに
後からメッセージを編集しているイメージです。
[kogure:~/Src/jj-test]!+[main] $ jj describe -m "hoge.txtとfuga.txtを追加" Working copy (@) now at: prvqumkn 4c1ce052 hoge.txtとfuga.txtを追加 Parent commit (@-) : zzzzzzzz 00000000 (empty) (no description set) Warning: Name and email not configured. Until configured, your commits will be created with the empty identity, and can't be pushed to remotes. Hint: To configure, run: jj config set --user user.name "Some One" jj config set --user user.email "someone@example.com"
次に jj new で新しいChange idのコミットを作ります。
[kogure:~/Src/jj-test]!+[main] $ jj new Working copy (@) now at: qszzwvvr aeb149b5 (empty) (no description set) Parent commit (@-) : prvqumkn 4c1ce052 hoge.txtとfuga.txtを追加 Warning: Name and email not configured. Until configured, your commits will be created with the empty identity, and can't be pushed to remotes. Hint: To configure, run: jj config set --user user.name "Some One" jj config set --user user.email "someone@example.com"
jj log で確認すると、コミットが積まれているのが分かります。
[kogure:~/Src/jj-test][jj/keep/4c1ce052d95c3e40b29f4a7c0b9983320fc525c6] $ jj log @ qszzwvvr (no email set) 2026-03-02 00:13:22 aeb149b5 │ (empty) (no description set) ○ prvqumkn (no email set) 2026-03-02 00:13:13 4c1ce052 │ hoge.txtとfuga.txtを追加 ◆ zzzzzzzz root() 00000000 [kogure:~/Src/jj-test][jj/keep/4c1ce052d95c3e40b29f4a7c0b9983320fc525c6] $
次にpushしてみたいと思います。
Jujutsuでは git push に相当するコマンドは jj git push です。
ただしGitと違い、Jujutsuのコミットはデフォルトでは匿名なので、
pushするにはまずブックマーク(Gitでいうブランチ)を作る必要があります。
jj bookmark create でブックマークを作成します。
-r オプションで対象のChange IDを指定します。
$ jj bookmark create my-branch -r prvqumkn Created 1 bookmarks pointing to prvqumkn 4c1ce052 my-branch | hoge.txtとfuga.txtを追加 Warning: Name and email not configured. Until configured, your commits will be created with the empty identity, and can't be pushed to remotes. Hint: To configure, run: jj config set --user user.name "Some One" jj config set --user user.email "someone@example.com"
jj git push でリモートにpushします。
--bookmark でpush対象のブックマークを指定します。
$ jj git push --bookmark my-branch Error: Won't push commit 4c1ce052d95c since it has no author and/or committer set Hint: Rejected commit: prvqumkn 4c1ce052 my-branch | hoge.txtとfuga.txtを追加
怒られました。よく見るとcloneした時からずっとWarningが出てましたね・・・
Warning: Name and email not configured. Until configured, your commits will be created with the empty identity, and can't be pushed to remotes. Hint: To configure, run: jj config set --user user.name "Some One" jj config set --user user.email "someone@example.com"
ヒントに従ってユーザー名とメールアドレスを設定してあげます。
Gitでいう git config --global user.name と同じですね。
$ jj config set --user user.name "自分の名前" $ jj config set --user user.email "自分のメールアドレス"
実行後、また警告が表示されていました。
Warning: This setting will only impact future commits. The author of the working copy will stay " <>". To change the working copy author, use "jj metaedit --update-author"
設定しただけでは、すでに作ったコミットの作者情報は更新されないようです。
jj metaedit --update-author で既存のコミットの作者情報を更新します。
-r で対象のChange IDを指定します。
$ jj metaedit -r prvqumkn --update-author Modified 1 commits: prvqumkn 73e8bf53 my-branch* | hoge.txtとfuga.txtを追加 Rebased 1 descendant commits Working copy (@) now at: qszzwvvr 23beab5c (empty) (no description set) Parent commit (@-) : prvqumkn 73e8bf53 my-branch | hoge.txtとfuga.txtを追加
改めてpushしてみます。
[kogure:~/Src/jj-test][heads/my-branch] $ jj git push --bookmark my-branch Changes to push to origin: Add bookmark my-branch to 73e8bf534f90 git: Enumerating objects: 3, done. git: Counting objects: 100% (3/3), done. git: Delta compression using up to 10 threads git: Compressing objects: 100% (2/2), done. git: Writing objects: 100% (3/3), 282 bytes | 282.00 KiB/s, done. git: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
今度は成功しました。
GitHubも確認してみます。
pushができていました。
コミットログも確認してみましょう。
問題なくできていました。
初回セットアップとしては jj git clone の直後に
jj config set しておくべきでしたね。
次にブランチの切り替えを試してみます。
Gitだと git switch や git checkout ですが、
Jujutsuには「ブランチにいる」という概念がそもそもありません。
やることは「別のコミットの上に jj new する」だけです。
今は my-branch のコミットの上にいますが、
jj new 'main()' で最初の状態のコミットに戻って新しい作業を始めてみます。
Gitでいう git switch main に近い操作です。
[kogure:~/Src/jj-test][heads/my-branch] $ jj log @ qszzwvvr ***@*** 2026-03-03 00:35:45 23beab5c │ (empty) (no description set) ○ prvqumkn ***@*** 2026-03-03 00:35:45 my-branch 73e8bf53 │ hoge.txtとfuga.txtを追加 ◆ zzzzzzzz root() 00000000 [kogure:~/Src/jj-test][heads/my-branch] $ jj new 'root()' Working copy (@) now at: qzwmkysw bd94fdb3 (empty) (no description set) Parent commit (@-) : zzzzzzzz 00000000 (empty) (no description set) Added 0 files, modified 0 files, removed 2 files [kogure:~/Src/jj-test][root] $ jj log @ qzwmkysw ***@*** 2026-03-03 00:56:27 bd94fdb3 │ (empty) (no description set) │ ○ prvqumkn ***@*** 2026-03-03 00:35:45 my-branch 73e8bf53 ├─╯ hoge.txtとfuga.txtを追加 ◆ zzzzzzzz root() 00000000 [kogure:~/Src/jj-test][root] $
ちょっと見方に自信がありませんが今いるところから見て前の操作でPUSHしたChange IDは分岐してますね
もう一度my-branchに戻ってみます
[kogure:~/Src/jj-test][root] $ jj new my-branch Working copy (@) now at: mxyyznrs 868aa59d (empty) (no description set) Parent commit (@-) : prvqumkn 73e8bf53 my-branch | hoge.txtとfuga.txtを追加 Added 2 files, modified 0 files, removed 0 files [kogure:~/Src/jj-test][heads/my-branch] $ jj log @ mxyyznrs ***@*** 2026-03-03 01:02:57 868aa59d │ (empty) (no description set) ○ prvqumkn ***@*** 2026-03-03 00:35:45 my-branch 73e8bf53 │ hoge.txtとfuga.txtを追加 ◆ zzzzzzzz root() 00000000 [kogure:~/Src/jj-test][heads/my-branch] $
ログを見た感じ戻っていそうですがまだ感覚が掴めない。。。 新たにファイルを作成してroot()に戻ってみます
[kogure:~/Src/jj-test][heads/my-branch] $ touch piyo.txt [kogure:~/Src/jj-test][heads/my-branch] $ jj new 'root()' Working copy (@) now at: uyxmlyny 34ca1247 (empty) (no description set) Parent commit (@-) : zzzzzzzz 00000000 (empty) (no description set) Added 0 files, modified 0 files, removed 3 files [kogure:~/Src/jj-test][root] $
stashをしないでmy-branchでの変更がなくなっていますね。 作業途中でも素早く切り替えられるのは便利かも。
my-branchに戻してpiyo.txtがいるか確認してみます
[kogure:~/Src/jj-test][root] $ ls [kogure:~/Src/jj-test][root] $ jj new my-branch Working copy (@) now at: mtopqmsk ccf6430c (empty) (no description set) Parent commit (@-) : prvqumkn 73e8bf53 my-branch | hoge.txtとfuga.txtを追加 Added 2 files, modified 0 files, removed 0 files [kogure:~/Src/jj-test][heads/my-branch] $ ls fuga.txt hoge.txt
あれ、いない。。。。
[kogure:~/Src/jj-test][heads/my-branch] $ jj log @ mtopqmsk ***@*** 2026-03-03 01:08:04 ccf6430c │ (empty) (no description set) │ ○ mxyyznrs ***@*** 2026-03-03 01:05:06 9a911590 ├─╯ (no description set) ○ prvqumkn ***@*** 2026-03-03 00:35:45 my-branch 73e8bf53 │ hoge.txtとfuga.txtを追加 ◆ zzzzzzzz root() 00000000
ログを確認すると分岐がありますね。 jj newコマンドは実行するたびに指定したコミットから分岐を作る感じですねなんとなく理解できました。
コミットを増やしたくない時はjj editで行けそうです。
ただしこれはコミットを編集しているイメージなのでjj newを使うのが基本的にはセオリーのようです。
公式FAQでも触れられていて、あとからjj squashでまとめるのが通常の手順のようです。
[kogure:~/Src/jj-test][heads/my-branch] $ jj edit 9a911590 Working copy (@) now at: mxyyznrs 9a911590 (no description set) Parent commit (@-) : prvqumkn 73e8bf53 my-branch | hoge.txtとfuga.txtを追加 Added 1 files, modified 0 files, removed 0 files [kogure:~/Src/jj-test]!+[heads/my-branch] $ ls -l total 0 -rw-r--r--@ 1 kogure staff 0 Mar 3 01:08 fuga.txt -rw-r--r--@ 1 kogure staff 0 Mar 3 01:08 hoge.txt -rw-r--r--@ 1 kogure staff 0 Mar 3 01:19 piyo.txt [kogure:~/Src/jj-test]!+[heads/my-branch] $ jj log @ mxyyznrs ***@*** 2026-03-03 01:05:06 9a911590 │ (no description set) ○ prvqumkn ***@*** 2026-03-03 00:35:45 my-branch 73e8bf53 │ hoge.txtとfuga.txtを追加 ◆ zzzzzzzz root() 00000000 [kogure:~/Src/jj-test]!+[heads/my-branch] $
まだ本当にさわり部分しか見れていないのですが Change IDとCommit IDの立ち位置が曖昧でこれ以上進めても理解が追いつかない気がするので今回はここまでにしたいと思います。 どうしてもGitベースで考えてしまうので理解するのに時間を要してしまいました。
| やりたいこと | Git | Jujutsu |
|---|---|---|
| ファイル追跡 | git add |
不要(自動) |
| コミットメッセージ | git commit -m |
jj describe -m |
| 次の作業へ | (次の git commit) |
jj new |
| 履歴確認 | git log |
jj log |
| push | git push |
jj git push |
| 新しいコミットを作って移動 | git switch -c |
jj new <対象> |
| 既存のコミットを編集 | git commit --amend(に近い) | jj edit <対象> |
今回触れたコマンドはこんな感じですかね! 冒頭で触れた特徴がまだ全然触れられていないので 次回以降また学習してまとめていきたいです!
ここまで読んでくださりありがとうございます!
弊社ではエンジニアを募集しております!少しでもご興味がありましたら、カジュアル面談でお話ししましょう!