本文へスキップ

「ブランチを切る」が Jujutsu にないと知って、初めてブランチを理解した話

公開

『じゅじゅちゅ!』『りあクト!』シリーズの著者。

Jujutsu には branch がない?

Git を長年使い続けてそのワークフローに慣れきった頭で Jujutsu を使い始め、最初にとまどったのが 「現在の変更が属すべき branch が存在しない」 ことでした。Git 初心者は、本格的な作業の前には「まず branch を切る」ことを教えられます。カジュアルな個人開発などでその過程を省略するときでも、少なくとも main branch 上にはいる必要があります。

しかし Jujutsu の公式ドキュメントや有名なチュートリアルを見ても、ガイドではいきなり作業が始まっていて、branch を作ったり branch に移動したりする過程がない。というか、探しても Jujutsu には「branch」という名前の操作可能なオブジェクトが見つかりません。代わりに bookmark というものがあって、Git 連携時にはそれが branch にマッピングされるという。しかし bookmark イコール branch ではない。何のことやらさっぱりです。

ところがガイドを参考に Jujutsu 上で行った作業を push して PR にすることを何回か繰り返すうち、あることに気づきました。Git と Jujutsu では「branch」という言葉の指すものが異なっているのではないかと。その前提で『Pro Git』1の branch の章や Jujutsu 公式ドキュメントの bookmark のページを読み直したところ、ようやく腑に落ちたのです。Git の branch は文字通りの「枝」ではないこと、そして Git の branch に対応するものを Jujutsu で bookmark と呼ぶことの合理性に。

「Git の branch はポインタである」の意味

そもそも Git の branch とは何なのでしょうか? 前掲の『Pro Git』の「3.1 ブランチとは」では次のように記述されています。

ブランチとは、開発の本流から分岐し、本流の開発を邪魔することなく作業を続ける機能のことです。

さらに読み進めていくと、こうも書いてあります。

Git におけるブランチとは、単にこれら 3 つのコミットを指す軽量なポインタに過ぎません。
(中略)
新しいブランチを作成したら、いったいどうなるのでしょうか? 単に新たな移動先を指す新しいポインタが作られるだけです。

「branch はポインタである」。Git を長く使っている開発者なら、一度は聞いたことがある言い回しです。しかし恥ずかしながら、私はこの説明を聞いても全然ピンと来なくて、ずっと雰囲気で branch を使い続けていました。今ならわかりますが、この説明はいくつもの暗黙の前提の上に成り立つものであって、特に Git から入ったような VCS 初心者にとってはほとんど意味を持ちえません。Git の公式ドキュメントや定番書は、 他の VCS をよく知るベテランユーザーが書いているため、Git が初めての VCS というユーザーがその前提を共有していないことを見逃しがちです。

では「branch はポインタである」ということを理解するために必要な前提とは何でしょうか? まず最初に押さえるべきは、Git では branch とは「履歴の分岐」そのものを意味しない ことです。branch と区別するため、履歴の分岐それ自体を示すのに『Pro Git』では「divergent history(分裂した歴史)」などの言葉が使われています2。

分散型 VCS の多くは、履歴の構造を表現するのにツリー型ではなく DAG3 を採用しています。そのため履歴の分岐と合流は、グラフの中で完結して表現されます。VCS によって異なるのが、それをユーザーに可視化し、操作できるようにするためのしくみ。それを多くの VCS では branch と呼んでいるのですが、たとえば Git とほぼ同時期に登場した Mercurial では、branch とは任意のタイムラインに属する commit のそれぞれに永続的に埋め込まれるメタデータのことです。それが Git では、ひとつのタイムラインにつきひとつの commit を指し示すポインタが用いられているのです。

まだよくわからない? ではもうひとつの前提に移りましょう。Git が他の VCS と違って特徴的なのは、「どこにいても常に履歴の先端に自動で押し出される」 という挙動です。それまでの VCS では branch を作るのも切り替えるのも非常に重い処理で、Linux カーネルのような大規模プロジェクトでは開発の足を大きく引っ張っていました。それらを超高速に行えるようにしたいというのが、Git 設計時の重要な課題でした。そして生まれたのが、branch をわずか数十バイトのテキストファイルで表現される、ひとつの commit のみを指し示す可動式のポインタにするという仕様です。

それを実現するために、Git ではコードの変更は通常どこかの branch が示す作業ラインで行うことが求められ、かつ branch のポインタは常に最新の commit に自動的に移動するというシステムになりました。そして branch を切り替えるという操作は、現在の居場所を表すポインタである HEAD をその branch のポインタに一致させるという動作のため、Git ではどこにいても常に履歴の先端に自動で押し出されるという挙動になるわけです。

私は分散型 VCS は Git しか知らなかったので、分散型 VCS とはそういうものだと思っていました。その挙動が当たり前ではなく Git 特有のものであるということ、かつその裏で動いているしくみを知ったことで、「Git の branch はポインタである」という説明に対する解像度が上がりました。

branch がつかない履歴の分岐はどうなるか

では Git に branch ではない履歴の分岐は存在するのでしょうか? 実は簡単に作ることができます。過去の commit をハッシュで指定して checkout するのです。

$ git checkout 9effb1cc737824d9c14e1a8ecbb725e44ccae104
Note: switching to '9effb1cc737824d9c14e1a8ecbb725e44ccae104'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
⋮
HEAD is now at 9effb1c create b.txt
$ echo '444' > d.txt
$ git add .
$ git commit -m 'create d.txt'
[detached HEAD 46b43c3] create d.txt
1 file changed, 1 insertion(+)
create mode 100644 d.txt

「あなたは今 detached HEAD の状態にある」ということを指摘されています。Git では「今どこにいるか」を示す HEAD は通常いずれかの branch のポインタと一致しているのですが、HEAD が branch ではなく特定の commit ハッシュを直接指している状態を detached HEAD と言います。この状態で commit を積み上げるのはリスクが高く、できれば避けるべきとされています。そこでの履歴が以下のようになってしまうためです。

  1. 後から見つけられなくなる
  2. ゴミとして扱われ、そのうち消されてしまう
  3. 他のユーザーと共有できない

Git の commit は branch や tag に属することで初めて人間がたどれるようになっています。detached HEAD で積んだ commit は branch を切り替えた瞬間に参照先を見失い、git log --all でも表示されません。reflog が唯一の救済手段です。そしてシステム側からも不要な履歴と見なされ GC の対象となるため、その期限が経過すると完全に削除されてしまいます。そのような自分でもすぐ見失ってしまい、いつ消えてしまうかわからない変更を、他人に共有するのは当然難しいです。

Jujutsu に branch がなくて bookmark がある理由

Jujutsu には「branch」という名前の操作可能なオブジェクトがないというのは冒頭でも述べました。しかし Jujutsu のドキュメントには branch という言葉が頻繁に出てきます。それが文字通り「履歴の分岐」という意味で。Jujutsu には「現在の branch」という概念がなく、かつ履歴の分岐を識別する専用のしくみもないため、「branch」が言葉本来の意味で用いられているのです。

ではなぜ Jujutsu には、Git や Mercurial のような branch がないのでしょうか? Git の commit は amend や rebase をするとそのハッシュが全く別のものに変わってしまいます。コロコロ変わるハッシュをシステムも人間も追跡するのが難しいため、それがどの作業ラインにあるのかを示す不変の名前ラベルである branch に属している必要がある。いっぽう Jujutsu の履歴の基本単位である change は、改訂されると変わる commit ID とは別に 不変の change ID を持っています。中身を何度書き換えても、どれだけ rebase しても同じ ID で change を追跡できるため、いちいち作業ラインのラベルをつけておく必要性が薄いのです。

また履歴の分岐は常に、ASCII アートを用いる形でログに可視化されます。ログから目で確認できるツリーグラフと、そのノードを形成する change の description から、それぞれがどの作業ラインに属するかは一目瞭然です。そして名前(description)がつけられているか、もしくはファイルの変更を含んでいる change は、明示的に破棄しない限りは履歴から消されることがありません。

Jujutsu のログ

branch に名前がない Jujutsu では、Git の detached HEAD に当たる状態で作業するのが通常です。Jujutsu ではこれを 匿名 branch と呼んでいます。detached HEAD は危険な状態とされていますが、Git においてその欠点だった「1. 後から見つけられなくなる」「2. ゴミとして扱われ、そのうち消されてしまう」が Jujutsu の匿名 branch にはありません。では「3. 他のユーザーと共有できない」はどうか。Jujutsu でそのために別途用意されているのが bookmark です。

bookmark とは change につけられる名前付きポインタのこと。同じ名前つきポインタでも Git の branch と異なるのは、純粋に他人への共有や自分が忘れないようにするための目印であって、履歴の分岐とは直接関係がないこと。ゆえに同じ作業ライン上に複数の bookmark をつけることができるいっぽう、新しく履歴を積んでも Git の branch のように自動的にそこに進むことはありません。

Jujutsu では履歴の分岐に名前をつける必要性や、他人と履歴を共有するのに分岐した作業ラインを丸ごと指定する必要性を認めません。それが Jujutsu には branch がなくて、bookmark がある理由です。

匿名 branch + bookmark は VCS 進化の歴史の着地点

Git では履歴の分岐を作るのも、それをマージするのも基本的に branch が単位です。branch がない Jujutsu ではどうなっているのでしょうか。

Jujutsu で履歴の基本単位である change を新規に作成するコマンドは jj new です。では履歴の分岐を作るには? すでに子を持っている change の ID を引数に渡して jj new です。では分岐している履歴をマージするには? これもマージしたい複数の change の ID を引数に渡して jj new。Jujutsu では 履歴を上に重ねるのも、分岐を作るのも、マージするのも change が単位 であり、かつ驚くべきことにそれらは皆同じ「新しい change を作る」という基本の動作でしかありません。

そうやって作業を進め、ひとまずの完了を見ました。PR を作って提出しましょう。ここで初めて一連の履歴に名前を付ける必要が生じます4。change に bookmark をセットして push すると、main から分岐してそこに至るまでの一連の change が Git の commit にマッピングされた状態で branch としてリモートの Git リポジトリに登録されます。

共有の単位が branch に束縛されない ことのメリットは何でしょう? まず名前や共有範囲を決めないまま作業が始められ、共有が必要になったときにそれらを決めればいいという柔軟さが挙げられます。また依存する一連の PR が作りやすいことは、最近になってその価値が高まってきました。PR を親子関係を持った小さな単位に分割できる GitHub Stacked Pull Requests が 2026 年 10 月 6 日についに一般提供されるようになりましたが5、これは Jujutsu の bookmark だと運用が非常に楽です。Git で stacked PR を作るには、gh stack init で最初の branch を作り、作業が一段落するたびに gh stack add で子の branch を作成するという手順を繰り返し、最後に gh stack submit で push して PR 作成という、なかなか煩雑かつ専用ツールに強く依存したプロセスが必要になります。ところが Jujutsu なら作業ラインに順番に bookmark をつけて push した後、gh stack link <bookmark1> <bookmark2> <bookmark3> のように実行するだけ。拍子抜けするほど簡単です。

Jujutsu では branch がないことで、Git において branch を基本単位としていた各種操作が問い直され、より小さな粒度、より柔軟な単位で操作できるようになっています。branch がない Jujutsu を一通り体験したことで、長年曖昧なまま使っていた Git の branch の正体がようやくわかりました。すると今度は、Git と異なる Jujutsu の各種概念が、驚くほど合理的なものに見えてきたのです。それは VCS の歴史を俯瞰すると、より強く感じられます。

Mercurial に Git と異なる形式の branch があることは先にも述べました。しかし Mercurial の branch は commit ひとつひとつに埋め込まれる永続的でグローバルなデータのため、数日で終わる機能開発やバグ修正に向きません。そこで軽い作業向けに、push 時の受け渡しや今どこで作業しているかの目印として使えるポインタ式の bookmark が後から追加されました。しくみは Git の branch を参考にしながら、branch とは独立した共有のための機能。これが Jujutsu の bookmark のルーツです。

Jujutsu はそこからさらに進み、履歴の分岐に branch は不要として持たないことにしました。興味深いことに Meta が Mercurial をフォークして独自に発展させた Sapling も、branch がない代わりに bookmark を持っています。「匿名 branch + bookmark」は、Git から Mercurial を経て行き着いた、VCS の進化の歴史におけるひとつの着地点とも言えるでしょう。

最後に補足ですが、この記事は拙著『じゅじゅちゅ! jj new で始める Jujutsu × AI ワークフロー』の「2-4-2. Branch と Bookmark のちがい」および「2-4-3. 匿名 Branch で作業する」をベースに作成したものです。

じゅじゅちゅ! jj new で始める Jujutsu × AI ワークフローJujutsu(jj)を Claude Code など用いた AI コーディングに活用するための実践ガイド。自動スナップショット、いつでも取り消せる操作、安全な履歴の書き換えで、AI が生んだ大量の変更をレビューできる履歴に変えます。juju-chu.com

本書の序盤は Git と Jujutsu のメンタルモデルの違いを理解することに重点を置いており、branch と bookmark のちがいの他にも、初心者がつまずきやすい箇所を丁寧に説明しています。この記事を読んで Jujutsu を始めてみたい、もしくは一度挫折したけど再挑戦したいと思った方は、ぜひチェックしてみてください。

Footnotes

  1. Git の公式ドキュメントとしても採用されている、Scott Chacon と Ben Straub 著のオープンソースの Git 解説書。オンラインで無料公開されている。https://git-scm.com/book/ja/v2 ↩

  2. https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell#divergent_history ↩

  3. Directed Acyclic Graph の略で、日本語では「有向非巡回グラフ」。矢印(有向)で結ばれたノードが、決して閉路(循環)を形成しないグラフ構造のこと。 ↩

  4. 最初に名前をつけるほうが好みなら、もちろんそうすることもできます。作業開始時に jj bookmark create で bookmark を作り、push 前にそれを jj bookmark move もしくは jj bookmark advance で対象の change まで進めます。 ↩

  5. 「Stacked pull requests generally available - GitHub Changelog」 ↩