16 ポイント 投稿者 GN⁺ 2026-01-29 | 1件のコメント | WhatsAppで共有
  • バージョン管理システムの内部構造を理解するために、Gitに似たシステムを自分で実装してみた
  • SHA-256ハッシュzstd圧縮を使って、GitのSHA-1とzlibを置き換え、.tvcディレクトリ構造でリポジトリを構成
  • Rustで書かれており、ファイルのハッシュ・圧縮・コミット・チェックアウト機能を段階的に実装
  • コミットオブジェクトにはツリーハッシュ、親コミット、作者、メッセージが含まれ、同一ファイルはハッシュ重複防止によって再保存されない
  • Gitがコンテンツアドレスベースのファイルストレージであることを自ら体験し、構造化データフォーマットの重要性を強調

ハッシュ化と圧縮方式

  • GitはすべてのオブジェクトをSHA-1ハッシュで識別するが、このプロジェクトではSHA-256を使用
    • SHA-1は古く安全性の面で脆弱だが、このプロジェクトでは単にファイル内容を識別する用途なので、セキュリティは重要ではない
  • Gitのzlibの代わりにFacebookのzstd圧縮ライブラリを採用
    • zstdのほうが効率的だと判断し、Git互換性は目標ではなかった
  • プロジェクト名は**「tvc (Tony’s Version Control)」**で、.tvc.tvcignoreファイルをGitの対応する構造として使用

実装ステップ

  • 実装手順はコマンド引数の読み取り → 無視ルールの読み取り → ファイル一覧の出力 → ハッシュ化と圧縮 → ツリー・コミット生成 → HEAD管理 → コミットのチェックアウトの順
  • Rustで書かれており、lsコマンドは.tvcignoreルールを適用して非無視ファイルを再帰的に探索し、各ファイルのSHA-256ハッシュを出力
  • zstdライブラリを用いてファイルの圧縮および展開機能を簡単に実装

コミット構造

  • コミットオブジェクトには次の情報を含む
    1. オブジェクト種別(“commit”)
    2. その時点のファイルシステム状態(ツリーハッシュ)
    3. 前のコミット(HEAD)
    4. 作者(author)
    5. コミットメッセージ
  • Gitとは異なり、作者とコミッターの区別を省略し、マージやリベース機能は実装していない
  • コミット生成時にツリーオブジェクトを生成・ハッシュ化・圧縮して.tvc/objects/に保存し、HEADファイルを更新
  • 同一ファイルはハッシュが同じなら再保存されないため、重複保存の防止が可能

ツリーオブジェクトとチェックアウト

  • generate_tree()関数はディレクトリを巡回し、各ファイルをハッシュ化・圧縮・保存し、ファイル名とハッシュを文字列として構成
    • 下位ディレクトリは再帰的に処理してツリー構造を形成
  • コミットとツリーオブジェクトを構造体(Commit, Tree)としてパースし、メモリ上で扱いやすいように構成
  • generate_fs()関数はツリー構造に基づいてファイルシステムを再生成し、指定パスにチェックアウトを実行

プロジェクトの教訓

  • Gitはコンテンツアドレスベースの(key-value)ファイルストレージであることを自ら体験
  • 最も難しかった部分はオブジェクトフォーマットのパースで、次はYAMLやJSONのような明確なフォーマットを使う予定
  • 全コードはGitHubリポジトリ(tonystr/t-version-control)で公開

1件のコメント

 
GN⁺ 2026-01-29
Hacker Newsの意見
  • Gitが recursive merge strategy をサポートする唯一のSCMだという点が興味深い
    この方式は過去の競合解決履歴を自動で覚えてくれるので非常に便利
    多くの人はいまだにrebaseを好むが、mergeを実装するなら必ず 競合解決履歴の保存メカニズム を入れるべき
    関連参考: Merge made by recursive strategy

    • 以前の職場では、gitの rerere機能 を有効にしないと過去の競合解決を覚えてくれなかった
      参考: Git Tools - Rerere
    • Mercurialの作者がrecursive mergeについて書いた文章がある
      リンク
    • 最近知ったことだが、git merge には “null” 戦略がない
      すでに競合を解決したので単にmergeの記録だけ残したいときでも、Gitはわざわざ 助けようとする試み をする
      インデックスやワークツリーに触れず、単にmergeした事実だけを記録するオプションがあればよいのにと思う
    • 競合を処理するより原則的な方法は、競合そのものをリポジトリの第一級オブジェクトとして扱うこと
      たとえば Pijul はそうしている
    • 個人的に git squash は嫌い
      複数コミットでの試行錯誤が見えず、元に戻すのも難しく、すでにmergeされたブランチで追加作業を続けるのもやりにくい
      複数のPRが1つのパズルのピースなら、単純なmergeのほうがずっと良いと思う
  • 毎日使うツールの内部を学ぶのはいつでも楽しい
    とくに Git from the Bottom Up は、Gitの内部構造を明快に説明してくれる素晴らしい文章だ
    20分ほどでGitコマンドの 不透明な動作原理 を理解できる

    • The Git Parable を通じてGitを完全に理解できた
    • 昔Gitを初めて学んだとき大いに助けられた記事を、また見つけられてうれしい
    • cat-file コマンドでハッシュIDを直接確認できると今になって知ったが、かなり面白い
  • コーディングエージェントがどうやって計画を立てるのか気になるなら、こういう記事こそ彼らの 訓練データ
    ただし著者がLLMの助けを借りていたなら、循環的な状況になるかもしれない

    • GitHub Insightsを見ると、記事を公開する前にすでに49回のクローンと28人のユニーククローナーがいた
      おそらく 公開リポジトリをかき集めるボット が存在するのだろう
      自分のコードがLLM学習に使われると考えると妙な気分だ
      記事そのものにはLLMの出力はないが、Rustコードの慣習やアルゴリズム比較の助言をもらうときにはChatGPTを使った
    • LLMを 自己参照ブログループ で汚染するのも面白いアイデアに思える
    • モデル出力が再び学習データに入るのは問題になりうるが、人間の編集を経れば多少は有用かもしれない
    • Geminiがときどき インド訛りの英語っぽい話し方 を見せるのを見ると、インドで生成されるデータセットがものすごく多いのだろうと思う
    • AIツールでブログを書くとこうした循環が起こりうるので、むしろ AIなしで文章を書く理由 ができる気もする
  • CodeCraftersの “Build your own Git” チュートリアルは本当に素晴らしい
    Rustで直接実装する Jon Gjengsetのライブ動画 もおすすめ

  • 自分も、バージョン管理が ソフトウェア以外の分野 でももっと使われてほしいと思う
    GotVC は、E2E暗号化、並列import、大容量ファイル対応の構造を備えた興味深いプロジェクトだ

    • テキストファイルを超えると、2つのバージョンの差分を見つけるのが難しくなる
      結局は元のプログラムで開いて比較するしかない
    • Game of Trees(Got) というプロジェクトがすでに存在することを知っているのか気になる
  • この記事を見て ugit: DIY Git in Python を思い出した
    Gitの内部を深く掘り下げつつ、追いやすく説明した最高の資料のひとつだ

    • ページデザインが美しくて ブックマーク した
    • 同じ文脈で Write yourself a Git も楽しく追っていけた
    • 自分はGitの演算を Neo4jグラフ にマッピングしてみたが、構造を理解するのに大いに役立った
  • MetaのMercurialフォークである Sapling VCS はZstd dictionary圧縮を使っている
    説明文書 でGitのdelta-compressed packfileと比較できる
    小さなリポジトリではGitのdelta圧縮のほうが効率的だが、大規模リポジトリでは パスベースのdictionary圧縮 のほうが優れている
    最近のGitにも似た “path-walk” 機能が追加された

  • 自分も似たような試みをしたことがあり、プロジェクト名は “shit” だ
    GitHubリンク

    • “Fast Useful Change Keeper” という名前の付け方が洒落ている
    • まさに “THE shit” だ
  • 昔SPAフレームワークを作ろうとして、隠れた複雑さ に驚いた記憶がある
    ReactやAngularの開発者たちもこういう 底なし沼 を経験したのだろうと思う
    Gitも同じように複雑さをうまく隠している

    • Gitを自分で実装してみて初めて、その言葉の意味を実感する
  • PHPで書かれたGitクライアントを見たが、packfileとreftableを読み、LCSベースのdiffにも対応している
    gipht-horse

    • このリポジトリはPHPにとって大きな 勝利(W) だと思う
      そして @ をHEADの代わりに使えると初めて知ったが、文法的にもかなり理にかなっている