Jujutsu - シンプルで強力な Git 互換の分散バージョン管理システム
(github.com/martinvonz)- Jujutsu は、ソフトウェアプロジェクトのコードのコピー、変更の追跡、変更の公開に使うバージョン管理システムであり、新規ユーザーと経験者、個人の新規プロジェクトと大規模な履歴・チームの双方を考慮して、使いやすいように設計されている
- ユーザーインターフェイスとバージョン管理アルゴリズムを、コンテンツを提供するストレージシステムから内部的に分離しており、現在は Git リポジトリをストレージ層として使用することで、Git ベースのツールと互換性がある
- Git にはコミットとファイルのみが保存され、bookmark(branch)やその他の上位メタデータは Git の外部にあるカスタムストアに保存される
- デフォルトの Git バックエンドは gitoxide Rust ライブラリを使用し、Git リモートリポジトリから branch を取得してリモートへ push でき、通常の Git コミットのように見えるコミットを生成する
- 作業コピーを実際のコミットとして表現する working-copy-as-a-commit モデルを採用しており、ファイル変更は通常のコミットとして自動的に記録され、その後の変更ごとに amend される
- dirty working copy が原因でコマンドが失敗しない
git stashが不要- 作業が終わる前でも、作業コピーコミットにコミットメッセージを設定できる
- リポジトリ上のすべての操作は、操作後のリポジトリ状態のスナップショットとともに記録され、以前のリポジトリ状態の復元、操作単位での undo、最新ではない特定操作の revert が可能
- コンフリクトをコミットと同じ第一級オブジェクトとして記録し、コンフリクトが発生した操作も成功扱いとなり、その後は単一のコンフリクト解決ワークフローで処理できる
- コミットを修正すると、既存コミットのすべての descendant が新しいコミットの上に自動的に rebase され、コンフリクト解決結果も descendant に伝播し、rebased コミットを指す bookmark と作業コピーも更新される
- 明示的な index や staging area はなく、commit の選択には revset 言語を使用し、出力フォーマットはユーザーが設定可能な template 言語で処理する
- 履歴書き換え機能として、任意のコミットのメッセージを編集する
jj describe、チェックアウトせずにコミットの変更を編集するjj diffedit、コミット分割用のjj split、変更の一部を別のコミットへ移すjj squash -i --from X --into Yを提供する - 安全な同時複製機能は実験的機能であり、Dropbox・
rsyncのような分散ファイルシステムまたはバックアップツールを使う場合にリポジトリが破損状態にならないよう設計されており、最悪の場合でもローカル状態とリモート状態の間のコンフリクトを露出させることを目指している jjは実験的なバージョン管理システムであり、Git 互換性は安定しているものの、開発中の機能、最適とはいえない UX、特定の利用方法におけるワークフロー上の空白が存在する可能性がある- 機能はかなり完成しているが、Git submodule のサポートはまだ完了しておらず、複数の性能バグがあり、メールベースのワークフローのネイティブサポートはない
- 1.0.0 より前には、ワークフローの変更やオンディスク形式の後方互換性のない変更が予定されており、形式変更については透過的なアップグレード、または要求時に使えるアップグレードコマンド・スクリプトの提供を試みる
- Apache 2.0 ライセンスのオープンソースソフトウェアであり、Google のサポート対象製品ではなく、サポートはコミュニティによって提供される
2件のコメント
Jujutsu - シンプルで強力な Git 互換 DVCS
私が1年半前に投稿したものですが、またHNで話題になっていますね。
Hacker News の意見
ここに上がっていてうれしい。2〜3週間前くらいに jj に乗り換えたが、戻るつもりはない。頭の中の作業スタイルはかなり変える必要があったものの、
jjが提供するフローはかなり気に入っている何をコミットするかを別途考える時間がなくなり、すべてのファイル変更が自動的に作業コピーのコミットに反映される。もちろん望ましくない場合もあるので、コミットを2つに分けたり移動したりする機能があり、すべての操作がコミットを対象に動作する点が良い
jj logは複数ブランチの全体図を見るのに優れていて、コンフリクトは一部だけ解決してから別のブランチへ移動でき、短縮された changeset ID も便利。たとえばjj logでリベースしたい changeset を見て、jj rebase -s qr -d masterのように実行できるので、ブランチ名を全部打つより速いDropbox のようなバックエンドと使えると書かれているが、IPFS と簡単に使えるシステムが出てくるとよさそう。大容量データでは GitHub に 1TB を保存できず、LFS は有料なので除外して考えている
データセットを使おうとする人はどうせローカルに持っているので、ダウンロード元が1つではなく数千になる IPFS は自然な解決策。データストアに IPFS を使うならすぐ乗り換えるが、そうでなければ調べる価値はあまりないと思う
Jujutsu は作者の個人プロジェクトとして始まり、今では Google で作者のフルタイムプロジェクトになっている。Git Merge 2022 で発表されたことがある
Jujutsu: A Git-Compatible VCS - Git Merge 2022:
Video:
https://youtu.be/bx_LGilOuE4
Slides:
https://docs.google.com/presentation/d/1F8j9_UOOSGUN9MvHxPZX...
ほぼ1年にわたって jj を毎日使ってきたが、Git のユーザー体験より明らかにずっと良いと思う。変更をアトミックなコミットに分けることに神経を使うタイプなので、自動リベースが中核機能になっている
コンフリクトをコミットに記録できるため、Git や Mercurial のようにリベースの途中で止まってコンフリクトを直す必要がない。コミットを修正したり削除したりしても、jj がすべての子孫コミットを自動でリベースしてくれ、Git rebase と違って Mercurial evolve のように複数の子も処理する
コミット履歴を扱う基本コマンドも良い。
jj move|restoreであるコミットの変更を別のコミットへ移動またはコピーし、jj splitでコミットを分割し、jj rebaseでコミットを移動する。作業ツリーやインデックスなしに、すべてがコミット履歴の中にあるので、同じコマンドとフラグで処理できる作業ログも素晴らしく、ほとんどの作業を問題なく元に戻せる。Git で悪い rebase や
commit --amendを直そうとすると、reflog から該当するコミットを探し、ref を手動で直さなければならないのとは対照的一番不便なのは、自動作業コピーコミットが望まないファイルまでコミットしようとすることがある点と、リネーム検出がないためマージを Git ほど上品に扱えない点。それでも壊れたコミットは
jj split|restoreで比較的簡単に直せるし、jj の開発者たちもリネーム問題に取り組んでいる作業結果としてコンフリクトが出ても、その情報をコミットに記録して作業自体は成功させ、あとでコンフリクトを解決できるという点がうれしい。ここ10年ほど協調編集アルゴリズムを扱ってきており、理想的には Git のようなツールが最終的に CRDT ベースのアプローチに置き換わるといいと思っている
CRDT を使えば同じツールでペアプログラミングができ、Git で起こり得る自己コンフリクトのような複雑なマージもよりうまく扱える。より強力なモデルでもある
ただし現代のテキスト CRDT は同時編集を自動的にコンフリクトなしで解決するが、オフラインでコード共同作業をする場合は、通常はコンフリクトが明示されて人間が直接解決することを望む。CRDT は Git より編集履歴の情報を多く持っているので理論上は可能だが、きちんとやるにはコンフリクト自体をテキストファイルのデータモデルに入れる必要がありそうで、現代のエディタとどう噛み合うのかはよく分からない
jj が同じコツを見つけたように聞こえるので、実際にどれほどよく動くのか期待している。CRDT ベースのコードリポジトリという夢に一歩近づいた感じがする
git bisectに価値を感じたことがないのか気になる再現可能なバグがあり原因が複雑なら、どのコミットがバグを持ち込んだのかを見つけて、どの変更が原因かを見るのは有用ではないかと思う。少なくとも何を直すべきかの最初の手がかりを得るには良い
現在の
git bisectは、すべてのコミットがビルドおよび実行可能なときに最もうまく機能する。コンフリクトマーカーが入っていてビルドや実行ができないコミットがあると、はるかに多くの手作業が必要になるこのようなシナリオで、
git bisectに相当する機能をどう適応できるのか気になる。単純にコンフリクトマーカー文字列をスキャンするのは適切ではないかもしれない。ドキュメントでコンフリクトマーカーを説明していたり、ある種のマークアップ言語では=======が下線として合法的に使われたりするためだソース管理履歴は意味のある成果物であり、パッチの束は開発マシンを離れる前に、漸進的でアトミックな変更の連続として整理されるべきだという考えと衝突するように見える
ただ、周囲の開発者の大半はすでに Git の履歴を無限のエディタの取り消し履歴のように扱っているので、このアプローチはその事実を固定化してしまう気もする
長期的な履歴がどのように機能すると見ているのか、今より有用になるのか、それとも有用でなくなるのか気になる
作業コピーが自動コミットされるというのは最初は良さそうに見えるが、好ましくない状況が多い。
新しい生成物ファイルができたのに、まだ
.gitignoreに入れる前だと自動コミットされるし、あるブランチで無視ファイルを追加してから別のブランチに切り替えると、作業コピーには残っているが.gitignoreにはないため自動コミットされる可能性がある。それに、後からコミットを分割するより、一部のファイルだけをステージングしてコミットするほうがずっと簡単。
自分もその考えが頭に染みついているが、本当に正しいのかは分からない。実際には、インデックスを直して後からコミットを amend することはかなり多いし、事前にやることもできる。
どうせ事後編集を完全には避けられないなら、そうした編集のための単一のワークフローのほうが良いのではないかと思う。まだ使ってはいないが、当たり前だと思っていた慣習が実は役に立っていないかもしれない、という点をかなりもっともらしく突いているアプローチに見える。
生成・変更されたすべてのファイルが常にコミットに入るようなユースケースは自分にはない。
別のブランチに切り替えたとき、ファイルが作業コピーに残るのに
.gitignoreにはないというのは、Git の失敗だと思う。.gitignoreに加えて.local.gitignoreのようなものがあるべきだ。自分にだけ必要な無視ファイルはとてもよくあり、プロジェクトの.gitignoreに入れる性質のものではない。方法はあるがどれもぎこちないので、デフォルトで簡単に提供されるべきだ。それでも興味深い。Git のステージングを好きだったことはないが、ここで言われているケースは経験したことがなかったので、ファイルシステムとコミットの間に中間段階が必要なのだと今になって理解した。
.gitignoreに追加すると、次の自動コミット時に匿名の作業コピーコミットから再び外れる仕組みだと理解している。ファイルがそもそも追加されないのではなく、匿名の作業コピーコミットに入ってから外れるという流れは直感的ではないが、その匿名コミットがほとんどのユーザーに見えないなら差はなさそうだ。
ドキュメントを見ると、懸念しているいくつかのケースは明示的に扱われている。
https://github.com/martinvonz/jj/blob/main/docs/git-comparis...
いくつかの特殊な場合を除けば、ここ数年コマンドラインで Git をほとんど使っていない。日常的には IntelliJ のようなIDE 内蔵の統合機能に頼っていて、なぜわざわざ手作業で苦労するのか理解しにくい。
エディタ内で行単位に選んで部分コミットできるし、すべてのブランチを見て、マージや cherry-pick をし、コミットや amend をし、更新を pull し、タグを編集する、といったことをショートカット一発で処理している。
ただ、ここでは自分は少数派のようだ。Git は必須スキルで、本物のプログラマーなら目をつぶってでもコマンドを打てるべきだ、というような雰囲気まで考えるとなおさらで、なぜなのか気になる。
このスキルは環境を選ばないので、リモートマシンに接続してリポジトリを見るのもローカルと同じだし、エディタごとに VCS 連携のやり方を新しく覚える必要もなく、慣れたツールをそのまま使える。
例に挙げられた作業はコマンドラインでも同じくらい簡単にできるので、エディタ統合が特別だとは思わない。reflog を触るような変わった高度な作業が必要になれば、結局コマンドラインを取り出すことになる可能性が高い。
もちろん GUI を使う人に不満はない。道具で生産的になれているなら判断することではないが、なぜかと聞かれたので答えただけで、自分のやり方のほうが優れていると主張しているわけではない。
人々が「Git CLI を学ぶべきだ」と言う最も一般的な文脈はこの場合だ。
数日前にも、Git コマンドを覚えたくないので GitHub Desktop を使おうという例があった: https://www.reddit.com/r/learnprogramming/comments/15b7pra/s...
Git 互換性を前面に出すアプローチのおかげで、Jujutsu はこれまで見たものの中でも Git の代替候補としてより有力に見える
Git 互換性の説明を読んで気になったのは、Jujutsu ユーザーが GitHub などにホストされた Git リポジトリへ jj でアクセスするユースケースに主に焦点を当てているように見える点。逆に、ネイティブな Jujutsu リポジトリへ Git でアクセスする方法もサポートしているのか気になる
ほとんどの開発者はすでに Git CLI に慣れているので、実運用では同じコードベースで jj と Git を併用する開発者が出てきそう。あるいは現実的な運用シナリオとしては、両方の CLI からアクセスするために、常にネイティブな Jujutsu データベースではなく Git をバックエンドリポジトリとして使うのか気になる
このバックエンドは主に、後で Git バックエンドには簡単に追加できない機能を入れられることを保証するために存在している
すでに Jujutsu を使っているプロジェクトで作業して貢献するなら、プロジェクトが自分のワークフローに合わせるのではなく、自分がプロジェクトに合わせるのが合理的
Git 互換性優先のアプローチのおかげで、Jujutsu はこれまで見た Git 代替候補の中でもより有力に見える
コンフリクト管理が気になる。pmeunier が Pijul のためにパッチ理論を形式的に整理することにかなり力を入れていたのは知っているし、健全でない、あるいは問題のあるパッチやコンフリクトの概念は大きな問題につながり得る。Darcs の性能問題はそうやって生じたのではなかったかと思う
リポジトリ Wiki の比較ページが、Pijul のコンフリクト処理は「似ている」という程度より、もう少し詳しく説明してくれるとよい
このような方法でコンフリクトを記号的に保存すると、後でコンフリクトを再現したり、特定のコンフリクトを自動解決したりもできるが、実際のコンフリクト内容そのものを解決する問題までは扱わない
誰かが実装するなら、Pijul を jj のバックエンドとして使って両方の長所を得ることもできそう
rsync、Dropbox、分散ファイルシステム経由で 安全に複製できるとはいい。最近はだんだん、単純な同期と相性のいいシステム寄りになっている
どこにでも、どんなツールで放り込んでもそのまま動くからだ。新しいツールがこういう方向を取るのはいつでも歓迎
こういう問題があるとは聞いたことがあるが、自分では直接経験したことがない。Git と rsync/Dropbox のようなツールの間でどのような 競合状態が起きて問題になるのか気になる
作業コピーが自動的にコミットされるとはありがたい。ずっと前から必要だった機能だ
エレガントなツールを作るために尽力した Martin に拍手を送りたい