[わ]

Pijul

現代のソフトウェア開発には分散型バージョン管理システムはかかせない。私に限って言えば、Git を一切つかわないようなソフトウェア開発には従事したことがないし、Git 以外のバージョン管理システムはつかったことがないレベルに等しい。それくらい、世の中では Git が主流となっている。

また、ソースコードの管理に GitHub をつかうのもデファクトスタンダードになっている。Stack Overflow の [Developer Survey](https://insights.stackoverflow.com/survey/2021 によると、90% 以上の回答者が Git をつかっていると解答している。 また、Github によると Fortune 100 の 84% が Github を利用している。これほどまでに Git が普及している現代において、Git 以外をつかうのは自ら茨の道を行くようなものである。

しかし、エンジニアとしては Git 以外の技術にも興味を持つべきではないか。本文章では、Git とは全く違った思想で設計されたバージョン管理システムである Pijul を取り上げる。

Git と Pijul の概要

以下に Git と Pijul の概要を示す。Git はスナップショットによりデータを管理し、Pijul はパッチを使っているところが大きく違う。

Git Pijul
読み方 ギット 不明(ピーフール、ピジュル)
開発者 Linus Torvalds Pierre-Étienne Meunier
ライセンス GPL 2 GPL 2
開発言語 C, Shell Rust
初回リリース 2005 2015
データ管理 スナップショット パッチ

Git

Git は Linus Torvalds が Linux カーネルのソースコードを管理するために作ったツールである。そのため、大規模なソースコードであっても高速に管理できるようになっている。

Git はソースコードやディレクトリ構造を Git Object として管理している。 Git Object には、コミット、Tree、Blob などの種類がある。ざっくりと説明すると Blob がソースコード、Tree は別のTreeへのポインタやソースコードのポインタ、コミットが Tree へのポインタをもっている。このコミットを起点とするグラフ構造が利用者からみえるソースコードのスナップショットをつくっている。

cherry-pick, merge, rebase などでは、元のブランチやコミットのスナップショットとHEAD のスナップショットを比較し、新しいスナップショットを作る。そして、その新しいスナップショットにポインタを向けたコミットを作る。そのため、指定されたコミットやブランチがコピーされたり移動するのではなく、元のコミットと差分が一致するような新しいコミットが作成される。

Pijul

Pijul は Pierre-Étienne Meunier が開発しているバージョン管理システムである。以下のような特徴がある。

Darcs は Git とちがい、リポジトリはパッチの集合であり、スナップショットのような履歴ではないとしている。Pijul も同様にリポジトリをパッチの集合として扱っている。そして、数学的に裏付けされ、かつ、高速に動作することを目標として開発されている。

Pijul におけるパッチ操作の特徴として、パッチが独立している場合はパッチが適用される順序を問わないところがある。

比較

この図は、公式ドキュメントにある図である。Git であれば行がシャッフルされる問題が発生するが、 Pijul では発生しないという例である。

Image from Gyazo

Git の場合各ブランチのスナップショットである ABGAB と AXB が比較され、行の順番が入れ替わった AXBGAB が生成される。Pijul はパッチ間の依存関係や、パッチに記録された変更位置などを考慮し、 正しい結果である ABGAXB を生成する。また、ABGAB と AXB をマージするだけではなく、GAB→AXB を先にマージしてから ABGAB をマージするというように、パッチを適用する順番を細かく変えても正しく処理された。Git の場合は、最初とは違った結果が表示されることになる

データ保持の方法

前述したように、Git はスナップショットによりデータを保持し、Pijul はパッチによってデータを保持している。それが、マージの順序が混乱する場合がある理由である。 詳しく説明すると、以下の図は上記の図を簡略化したものである。

Image from Gyazo 緑の丸がAB, GAB…… などの、ある時点でのソースコードを表す状態であり、スナップショットである。p,q,r……はその状態へにコードを変更するためのパッチである。 また、この図をグラフとして考える場合、スナップショットはノードと呼ばれパッチはエッジと呼ばれる。

Cの状態を求める

Git は 3 way merge という方法によりソースコードを比較している。これは、2つの比較したいファイルと共通の親を使った比較方法となる。2つのうち一つで行が削除されている場合などに、親と比較することで削除されたという事実を見つけだせるところが、この方法のメリットである。 Git の場合はただ単純に親を探すだけではなく、いろいろなことをやっているようだが、Pijulには関係ないので、割愛する。

A2 と B をマージする場合、以下の3つのノードが利用される。

A1 は使われない。そのため、A2 に含まれる二つの AB がどの時点で追加された AB なのかという情報を Git では使えない。

Pijul はノードではなく、エッジを利用している。C を求めるには、rq をマージするという扱いになる。そして、 r は親にp を含みA2に含まれる二つの AB がどの時点で追加された AB なのかがわかる。Git では使っていない情報をもっているため、Pijul は Git より頭のいいマージを行うことができる可能性が高い。なお、この動作は Pijul に限ったことではなく、 パッチを使ったバージョン管理システムである Darcs でも同様にマージできる。

また、prmqnは独立している。Pijul では、独立しているパッチの場合には、パッチをあてる順序を問わないという利点がある。(グラフ理論が使われているそう)

コンフリクトの扱い

たとえ、Pijul が Git より頭のいいマージシステムを持っていようとも、コンフリクトは発生する。それは、防ぎようがないことである。

Git の場合、コンフリクトが発生したら >>>>>> ======= <<<<<<< 的な文字列でコードを区切って、add が一回なされたらこの区切り文字はソースコードの一部と見なされる。

Pijul はコンフリクトを状態の一つとして扱う。 コンフリクトが発生している状態を一つの状態として扱っているため、コンフリクトを含んだまま、ブランチを切り替えたりできる。また、パッチとしての扱いとなっているため、簡単にコンフリクトを含むコミットだけを取り出すなどのようなこともできる。

Pijul が Darcs より優れている点

(コードや理論を理解しきれているわけじゃないので、ざっくり)

C という状態は、pqrmn が集まってできた物である。逆に言うと、C を求めるためには、pqrmn から現在の状態を導き出さなければならない。もしも、これがN万コミットあるようなリポジトリで、共通の親が 100 世代以上先のような開発の進み具合だった場合に、ブランチを切り替えたりすると、その間の処理をすべて計算する必要がでてくる。

しかし、Pijul は単純にパッチのみですべてを管理している分けではない。pristine というスナップショットも使ってデータを管理している。ブランチの最新状態も保持しているため、新しくパッチを追加した場合などにも、高速に計算できるようである。(このあたりの詳細はいまいち理解指定ない)

気になる点

まとめ

Pijul と Git をざっくりと比較した。パッチベースのバージョン管理システムで、内部の話をスルーするならば、Git がパッチではなくスナップショットを使っているんだよという話が、直感から少し外れているため、より直感的に使えるのではないだろうか。 気になる点はいくつもあったが、ある程度のエコシステムやドキュメントが十分に整備されたならば、 パッチを使った Pijul のシステムは、Git よりよいと判断される可能性もあるのではないかと思った。

完全に Git を使いこなしているごく一部の人をのぞいて、パッチによるマージ処理の簡単化は大いに価値があるのではないだろうか。

まあ、Git から他のシステムに移行する程のメリットをだせるかというと非常に疑問ではあるが。。。