0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pure RustのGit実装gixは、組み込み用途でどこまで使えるのか

0
Last updated at Posted at 2026-09-07

Rust製アプリケーションからGit repositoryを扱いたい。

ただし、Gitクライアントを作りたいわけではありません。

今回考えていた用途は、

  • ローカルのファイル群について過去との差分を見たい
  • repositoryやcommitのidentityを取得したい
  • 外部のgitコマンドに依存したくない
  • GitHubなどのクラウドサービスを前提にしたくない
  • できればPure Rustで完結させたい

というものでした。

そこで候補に上がったのが、Pure RustでGitを実装している gitoxide / gix です。

今回は、

gixをRustアプリケーションへ組み込んだ場合、Git相当の機能をどこまで任せられるのか

を2026年9月時点の公式情報から調べました。

なお、この記事は公式documentationとsourceを読んだ段階の調査です。

実際のrepositoryを使った動作検証や、binary size・依存crate数の実測はまだ行っていません。

調べ始めた時点では、アプリケーション内部の履歴管理そのものにgixを使うことも考えていました。

しかし最終的には、

内部VCSとしては採用しない。
既存Git repositoryを読み取るread-only provenance observerとして使う。

という位置に落ち着きました。

なぜそう判断したのかも含めて整理します。

まずgixの現在地

gixは現時点でも、Gitの低レベル機能をRustから利用するlibraryとしてかなり充実しています。

特に、

  • repository open / discovery
  • object read / write
  • commit
  • ref
  • index
  • status
  • blob diff
  • tree diff
  • rename tracking
  • revision traversal
  • clone / fetch

などのplumbingは既に提供されています。

一方で、Git CLIで日常的に使う、

  • checkout
  • switch
  • restore
  • reset
  • merge workflow
  • cherry-pick
  • revert
  • rebase
  • stash
  • push

などについては、公式のcrate-status.md上で高レベルworkflowとして未完成の領域が残っています。

つまり現在のgixは、

Git CLIを丸ごと置き換える完成済みporcelain

というより、

Gitのrepository構造や履歴をRustから直接扱うための強いplumbing layer

として見るのが適切です。

今回の用途では、むしろこの性質が都合よく働きました。

gixとは

gitoxideはGitをPure Rustで実装するプロジェクトです。

アプリケーションから使う場合は、トップレベルcrateであるgixを使います。

2026年9月時点のdocs.rsではgix 0.87.1が公開されています。

gix::RepositoryがGit repository操作の中心となるabstractionです。

たとえば既存repositoryを開いたあと、

let head = repo.head_commit()?;
let tree = repo.head_tree_id()?;

のように、現在のHEAD commitやtree identityへアクセスできます。

revision featureを有効にしていれば、

let previous = repo.rev_parse_single("HEAD^")?;

のようなrevision解決も可能です。

CLIとしてのgixも存在しますが、組み込み用途ならlibrary APIを直接利用する方が自然です。

Pure Rustである意味

RustからGitを扱う代表的な選択肢にはgit2もあります。

git2libgit2のRust bindingです。

一方gitoxideは、

  • object database
  • refs
  • index
  • diff
  • revision
  • protocol
  • worktree

などをRustで実装しています。

そのため、

Rust application
    ↓
gix
    ↓
Git repository

という構成が取れます。

外部のgit.exegitbinaryをsubprocessとして起動する必要がありません。

組み込み用途では、

  • PATH
  • shell quoting
  • インストール済みGitのversion差
  • subprocess lifecycle

などへの依存を減らせるのが魅力です。

remote操作に必要なplumbingもgix側に存在するため、必要になればclone / fetchまでRust側で扱えます。ただし今回の用途は、既にローカルに存在するrepositoryを観測することなので、remote機能は採用判断の中心ではありません。

ライセンス

ライセンスも組み込み用途では重要です。

gixMIT OR Apache-2.0 です。

比較対象となるgit2 crateも MIT OR Apache-2.0 ですが、内部で利用するlibgit2本体は GPLv2 with Linking Exception です。

このLinking Exceptionにより、libgit2をリンクしたアプリケーション全体がGPLになるわけではありません。

つまり両方とも商用アプリケーションへ組み込める選択肢ですが、

gix
→ Pure Rust
→ MIT OR Apache-2.0

git2
→ Rust binding
→ MIT OR Apache-2.0
→ libgit2: GPLv2 + Linking Exception

という違いがあります。

単なる「C依存があるか」だけでなく、配布artifactとライセンス監査の構造も異なります。

repositoryは普通に読める

gix::open()などから既存repositoryを開けます。

Repositoryからは、

  • HEAD
  • commit
  • tree
  • object
  • refs
  • index

などへアクセスできます。

つまり、

このdirectoryはGit repositoryか

を調べたり、

現在どのcommitを指しているか

を取得したり、

HEADが指すtreeは何か

を見る用途に使えます。

read-only provenance用途では、まずここが基本になります。

commitやrefの書き込みも技術的には可能

gix自体はread-only libraryではありません。

commitやref、object、indexのmutation plumbingも存在します。

つまり技術的には、

working tree
↓
index
↓
tree
↓
commit
↓
ref update

というGitの履歴をアプリケーション側から構築できます。

最初にgixを調べ始めた理由もここでした。

アプリ内部の履歴管理をGit repositoryで持てないか?

と考えたからです。

ただ、後述する理由で、この用途には採用しないことにしました。

statusはread-only用途でも重要

status featureを有効にすると、HEAD・index・working treeの関係を調べられます。

これは単に「dirtyか」を表示するためだけではありません。

アプリケーションが実際に読んだfileと、

HEAD commitが表しているfile

が同じ内容なのかを判断するために必要です。

たとえば、

HEAD
document.md = blob A

working tree
document.md = blob B

なら、HEAD commitのidentityだけをsource provenanceとして記録することはできません。

実際に読んだのはblob Bだからです。

この問題は後半の 「Git provenanceはsource identityそのものではない」 で詳しく扱います。

blob / tree diffも使える

gixにはblob diffとtree diffがあります。

そのため、

commit A
↓
tree A

commit B
↓
tree B

を比較して、

  • added
  • deleted
  • modified
  • rename候補

などを見ることができます。

テキストblobについてはline-orientedなdiffも扱えます。

source codeやMarkdownなどについて、

このcommit間でどのfileが変わったか

をアプリケーション側から調査する用途には十分魅力的です。

rename trackingもある

diffやstatus周辺にはrename trackingもあります。

たとえば、

docs/design.md
↓
architecture/design.md

へ移動しただけの場合、

deleted docs/design.md
added architecture/design.md

と見るか、

renamed

と見るかで、上位システムの意味が変わります。

Gitのrename detectionはrename情報を履歴として保存しているわけではなく、内容の類似度などから推定する仕組みです。

gixでも、この種のtrackingを利用できます。

Git CLI相当のworkflowは全部完成しているわけではない

ここは重要です。

公式のcrate-status.mdでは、以下のような操作について高レベルworkflowとして未完成の領域が残っています。

  • checkout
  • switch
  • restore
  • reset
  • merge
  • cherry-pick
  • revert
  • rebase
  • bisect
  • stash
  • am
  • apply
  • push

低レベルcomponentが既に存在するものもあります。

たとえばworktree mutationやlow-level checkoutに相当する部品があることと、

git restoreと同じsemanticを一つの完成済みworkflowとして提供している

ことは別です。

gixを評価するときは、

plumbingが存在する

ことと、

Git CLI相当の高レベルworkflowが完成している

ことを分けて見る必要があります。

ここで考え直した

ここまで見ると、

ローカル履歴管理にgixを使えばいいのでは?

と思えます。

技術的にはかなり可能です。

しかし、アプリケーション内部にGit repositoryを隠して持つ場合、別の問題が出てきます。

Gitは「歴史を残す」ためのシステム

Gitでは一度commitされたblobは、working treeからfileを削除しただけでは即座に物理消去されません。

object、reflog、packfile、GCなどのlifecycleがあります。

つまりユーザーから見ると、

文書を削除した

のに、

Git objectとして過去の本文は残っている

という状態が起こり得ます。

これはGitとしては正しい動作です。

むしろGitは過去を失いにくくするための仕組みです。

しかしアプリケーション側が、

  • 本文を必要以上に保持しない
  • 削除要求に明確に応える

ことを重視している場合、Gitの性質そのものが責任モデルと衝突します。

「利用者に意識させない」は責任境界ではない

最初は、

Gitを内部に同梱し、利用者にはGitを意識させない

という案も考えました。

しかしこれは責任境界を守ることにはなりません。

実態としてアプリケーションが、

ドキュメント化されていないversion control systemを内部に持つ

ことになるからです。

UIに表示しなくても、Git repositoryが本文の履歴を保持している事実は変わりません。

この時点で、内部VCSとして採用する案は止めました。

ではgixは使わないのか

逆でした。

採用範囲を変えれば、かなり噛み合います。

内部Git repositoryは作らない。

commitもしない。

historyを書き換えない。

その代わり、

source directoryが既にGit repositoryなら、その履歴をread-onlyで観測する

という使い方です。

既存Git repository
      ↓
     gix
      ↓
repository / HEAD / tree / blob / status
      ↓
application provenance

この位置なら、gixの強い部分をかなりそのまま使えます。

Git provenanceはsource identityそのものではない

単純に、

source_git_commit = abc123

と記録するだけでは不十分です。

なぜなら、アプリケーションが実際に読むのはworking treeだからです。

たとえば、

HEAD:
document.md = blob A

working tree:
document.md = blob B

というdirty repositoryで、アプリケーションがblob Bを読んだとします。

この状態で、

source_git_commit = abc123

だけ記録すると、

commit abc123の内容を取り込んだ

ように見えます。

しかし実際に読んだbytesはcommitに存在しません。

provenanceとしては誤りです。

fileごとにGitとの関係を見る

Git情報はfile単位で扱う方が安全です。

たとえば、

repo_scope:
  NOT_GIT
  SAME_REPO
  NESTED_REPO
  SUBMODULE

content_relation:
  EXACT_HEAD
  MODIFIED
  UNTRACKED
  DELETED_IN_HEAD

ignored:
  true / false

のように分離します。

repo_scopecontent_relationは別軸です。

たとえばsubmodule配下のfileがHEADと一致していることもありますし、ignored fileがmodifiedであることもあります。

単一enumにすると、こうした組み合わせを正しく表現できません。

ここで重要なのは、

EXACT_HEAD以外では、Git commitは実際に読んだcontentを証明していない

と一目で分かることです。

Git blobとの比較対象はraw bytes

Git blobとの比較対象は抽出後textではなく、実際に読んだraw bytesです。

たとえばdocxなら、Gitが保持しているのはdocx containerそのものです。

したがって、

observed_raw_identity
        ↓ compare
HEAD blob identity

という比較になります。

一方、parserやextractorによる処理結果には別identityを持たせます。

raw_identity
→ source bytesのidentity

extracted_identity
→ parser / extractor処理後のidentity

この二つを混同しない方が安全です。

HEADのTOCTOUにも注意が要る

もう一つ問題があります。

取り込み開始時に、

HEAD = abc123

だったとしても、処理中にHEADが変わる可能性があります。

特別な競合processを想定しなくても、

  • ユーザー自身のIDE
  • Git GUI
  • editor integration
  • background automation

などが普通にcommitやbranch操作を行い得ます。

そこで少なくとも、

  1. 取り込み前にHEADを読む
  2. fileを読む
  3. HEAD blobと比較する
  4. 取り込み完了後にHEADをもう一度読む

という確認が必要になります。

HEAD before = abc123
HEAD after  = abc123

なら、そのrepository observationはstableだったと判断できます。

一方、

HEAD before = abc123
HEAD after  = def456

なら、

HEAD_MOVED_DURING_INGEST

のようなrepository × generation単位のstatusとして扱う方が自然です。

この場合、個々のfileについてEXACT_HEAD判定が存在していても、repository observation全体をauthoritativeなGit provenanceとして扱うことはできません。

read-only用途とgixの成熟度がきれいに噛み合う

ここが今回いちばん面白かったところです。

read-only provenance observerとして欲しいのは主に、

  • repository discovery
  • HEAD resolution
  • tree traversal
  • blob lookup
  • status
  • diff
  • revision traversal
  • ignore / submodule情報

です。

逆に不要なのは、

  • checkout
  • restore
  • reset
  • merge workflow
  • rebase
  • stash
  • push

です。

そして後者は、ちょうど公式crate statusで高レベルworkflowとして未完成の領域に寄っています。

つまり、

gixの成熟しているread-side plumbingを中心に使い、未成熟なwrite-side workflowをほぼ踏まずに済む

ことになります。

最初に想定していた「内部VCS」より、read-only observerの方が現在のgixの成熟度分布とも相性がいいわけです。

Trust Modelもread-only用途と相性がいい

gixにはrepositoryのTrust Modelがあります。

repository ownerと現在processのuserなどからtrust levelを決め、Git configurationにもそのtrust informationを反映します。

未信頼sourceから得た、

  • executableへのpath
  • 外部commandに関連する値
  • その他sensitiveなconfiguration

を無条件に利用しない仕組みがあります。

つまり、

repositoryからdataは読みたいが、repository由来のprogram実行までは信用したくない

という用途を明確に意識した設計です。

他人や外部systemが作ったrepositoryをread-onlyで観測する用途では、これは単なる便利機能ではなく採用理由になります。

必要ならuntrusted repository自体を拒否する設定も取れます。

feature flagはread-only用途なら絞れる可能性がある

gix 0.87.1には54個のfeature flagがあり、そのうち32個がdefaultで有効になっています。

defaultでは、

  • blob diff
  • index
  • revision
  • status
  • merge
  • attributes
  • worktree mutation

など、かなり広い機能が入ります。

一方、公式のCargo.tomlでは、library developerに必要componentだけを選択してbuild timeなどを最適化することが推奨されています。

read-only provenance用途なら、

  • blob-diff
  • index
  • revision
  • status

などを中心に必要なfeature closureを調べる価値があります。

ただし、

実際に何個のdependencyまで減るのか
binary sizeが何MiB増えるのか

はまだ測定していません。

したがって現時点では、

featureを絞れる可能性がある

までが確認済みであり、

十分小さいbinaryになる

とはまだ言えません。

これは実際にcandidate binaryをbuildして測る必要があります。

公式documentation上の提供状況

以下は実動作を検証した結果ではなく、2026年9月時点の公式documentation / crate statusを読んだ整理です。

機能 ドキュメント上の提供状況
repository open / discovery 提供あり
HEAD / commit / tree lookup 提供あり
blob lookup 提供あり
status 提供あり
blob / tree diff 提供あり
rename tracking 提供あり
revision traversal 提供あり
clone / fetch plumbing 提供あり
commit / ref mutation plumbing 提供あり
low-level worktree mutation 提供あり
restore / reset相当の完成済みporcelain 未完成領域あり
merge / rebase等の高レベルworkflow 未完成領域あり
push workflow 未完成領域あり

今回の採用判断

上の機能提供状況とは別に、今回の用途についての判断はこうなりました。

用途 判断
既存repositoryのidentity取得 採用候補
HEAD / tree / blob provenance取得 採用候補
working treeとHEADの関係確認 採用候補
既存historyのread-only参照 採用候補
Git diff情報の補助利用 採用候補
アプリ内部で新規Git repositoryを作る 不採用
アプリが自動commitして履歴を所有する 不採用
restore / merge / rebase等を製品機能として提供 今回のscope外
remoteへのpushを製品機能として提供 今回のscope外

重要なのは、

gixが使えなかったから採用しなかった

のではありません。

むしろ逆です。

gixはかなり使えそうです。

そのうえで、

アプリケーションがGit historyそのものを所有するべきか

を考え直した結果、採用範囲が変わりました。

結論

2026年9月時点のgixは、

Pure RustでGit repositoryを観測・解析するlibrary

としてかなり有力に見えます。

特に、

  • repository identity
  • HEAD
  • tree
  • blob
  • status
  • diff
  • revision history

をRustアプリケーション側から直接読みたい場合、外部Git CLIに依存せず実装できる可能性があります。

一方、

アプリ内部の履歴管理そのものをGitへ任せる

場合は、技術的に可能かどうかだけではなく、

  • 誰がhistoryを所有するのか
  • 削除したcontentがいつ物理的に消えるのか
  • 利用者にどのようなversioning contractを約束するのか

まで考える必要があります。

今回の用途では、

内部VCSとしては不採用。
既存Git repositoryを読むread-only provenance observerとして採用候補。

という結論になりました。

偶然ですが、この使い方ならgixで現在まだ弱いwrite-side workflowをほぼ避けながら、read-side plumbingを中心に利用できます。

調べる前より、むしろgixの使いどころが明確になりました。

次は実際に小さなcandidateを作り、

  • dirty / clean repository
  • nested repository
  • submodule
  • ignored / untracked file
  • HEAD移動中の取り込み
  • Windows / Linux / macOS
  • binary size
  • dependency closure

あたりを実測してみたいところです。

なお、

Git historyそのものを所有せずに、Generation間の差分や削除可能性をどう両立するか

は別の設計問題になります。

これは別記事 削除できるKnowledge History で扱う予定です。

参考資料

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?