iimon TECH BLOG

iimonエンジニアが得られた経験や知識を共有して世の中をイイモンにしていくためのブログです

呪術に触れてみた

こんにちは、kogureです。 勉強会のお題を考えているときにEMから今、巷で流行っているJujutsuどう?とお勧めされたので触ってみました!

そもそもJujutsuってなんなんでしょうか?

公式のreadme> Introductionを確認してみましょう

github.com

  • Git互換の新しいVCSで既存のGitツールやGitHub等とそのまま連携可能
  • 作業コピーへの変更が自動的にコミットとして記録される
  • 操作ログと元に戻し機能:すべての操作が記録され、ミスを簡単に取り消せる etc...

原文が英語なので翻訳しながら確認したのでわかっていない部分もありますが、 Gitよりあまり操作を意識せずにやってくれる印象でしょうか?

ちなみに読み方は「呪術」のようです

github.com

大枠を知ったところで実際に触ってみます!!!

まずはインストールから MacでHomebrewがインストール済みの場合は以下のコマンドでインストールができます。

brew install jj

他のインストール方法は以下のURLを参照して適宜実行してください。

docs.jj-vcs.dev

インストールが完了したことを確認しています

[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>

docs.jj-vcs.dev

特に説明することはないので実行してみましょう

[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

docs.jj-vcs.dev

jj log

docs.jj-vcs.dev

[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が 0233b6a43adf37ea に変わっています。 一方で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を指定します。

docs.jj-vcs.dev

$ 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対象のブックマークを指定します。

docs.jj-vcs.dev

$ 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 "自分のメールアドレス"

docs.jj-vcs.dev

実行後、また警告が表示されていました。

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を指定します。

docs.jj-vcs.dev

$ 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 switchgit 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で行けそうです。

docs.jj-vcs.dev

ただしこれはコミットを編集しているイメージなのでjj newを使うのが基本的にはセオリーのようです。 公式FAQでも触れられていて、あとからjj squashでまとめるのが通常の手順のようです。

jj-vcs.github.io

[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 <対象>

今回触れたコマンドはこんな感じですかね! 冒頭で触れた特徴がまだ全然触れられていないので 次回以降また学習してまとめていきたいです!

ここまで読んでくださりありがとうございます!

弊社ではエンジニアを募集しております!少しでもご興味がありましたら、カジュアル面談でお話ししましょう!

iimon採用サイト / Wantedly