プログラミング
Gitリポジトリのリリース履歴を追跡するための、より良い方法の可能性
Maybe a better way to track the release history of a Git repo (blog.plover.com)
要約
この記事では、Gitのモノリポジトリでデプロイメントごとに大量のタグオブジェクトが生成される問題について論じています。このアプローチは、Gitの操作を遅くし、リファレンスリストを煩雑にします。著者は、各(ティア、アプリ)ペアに対して単一のリファレンスを使用し、メタ情報コミットの連鎖によってリリース履歴を追跡する代替案を提案しています。これにより、Gitのコミット履歴メカニズムを活用し、効率的かつ有用な方法で履歴を管理できます。
全文翻訳
Universe of Discourse
Mark Dominus (陶敏修) mjd@pobox.com
私について RSS Atom
最近の投稿 12件
Gitリポジトリのリリース履歴を追跡するための、より良い方法の可能性
記事への最近の追加
202609: エラー、省略、および撤回
Lシステムはゾウムシ、ピザのトッピング、母系制の系譜を生成する
イプシロンゼロへの道:「Shortlex」は実際には単一の順序ではない
別の家の修繕作業の勝利に注目
仕事日記の勝利
ジョージ・オーウェルの原子爆弾に関するエッセイは「1984年」を予期する
イプシロンゼロへの道:Shortlex順序は数値のシーケンスも順序付ける
イプシロンゼロへの道:生産的なプログラムと健全な順序
定理には2種類ある
イプシロンゼロへの道:コインのないコイン移動ゲーム
私が大切にしている7冊の本、なぜならそれが好きだから
アーカイブ:
2026: JFMAMJ JASO
2025: JFMAMJ JASOND
2024: JFMAMJ JASOND
2023: JFMAMJ JASOND
2022: JFMAMJ JASOND
2021: JFMAMJ JASOND
2020: JFMAMJ JASOND
2019: JFMAMJ JASOND
2018: JFMAMJ JASOND
2017: JFMAMJ JASOND
2016: JFMAMJ JASOND
2015: JFMAMJ JASOND
2014: JFMAMJ JASOND
2013: JFMAMJ JASOND
2012: JFMAMJ JASOND
2011: JFMAMJ JASOND
2010: JFMAMJ JASOND
2009: JFMAMJ JASOND
2008: JFMAMJ JASOND
2007: JFMAMJ JASOND
2006: JFMAMJ JASOND
2005: OND
サブトピック:
数学252
プログラミング102
言語98
その他77
本52
技術49
語源36
Haskell33
おっと30
Unix27
宇宙の呼び声25
数学SE25
法律23
物理学21
脳17
Perl17
生物学16
カレンダー15
食べ物15
コメントは無効になっています
2026年10月6日火曜日
Gitリポジトリのリリース履歴を追跡するための、より良い方法の可能性
ZipRecruiterにいた頃、私たちのGitモノリポジトリは厄介な問題を抱えていました。それは、リファレンス(refs)が多すぎることでした。リポジトリ内のアプリケーションのいずれかがステージングまたは本番環境にデプロイされるたびに、デプロイメントツールは次のような名前のタグオブジェクトを作成していました。
refs/release/stg.www.our-app-2019-09-06T114410.954-0700-treesha-179dab6fd11bee5195f0f8e31cea3edfb86ae23b
このタグには、デプロイメントのターゲットティア(ここでは「ステージング」を意味するstg)、アプリ名とそれが実行されたホストのクラス、www.our-app、日付と時刻、およびデプロイされたツリーのハッシュが含まれていました。これにより、何が、どこで、いつデプロイされたかの完全な記録が作成されました。
しばらくの間これを続けていくと、数千ものタグオブジェクトができました。Gitは数千のコミットを処理するようにうまく設計されていますが、数千のリファレンスを処理するように設計されていません。おそらくこれは変更されたかもしれませんが、当時、Gitはリファレンスをpacked-refsというファイルに保存していました。このファイルは、各リファレンスについて、リファレンスの名前とその参照先のオブジェクトIDのエントリを含んでいました。(Gitのすべてのことと同様に、実際の状況はやや複雑です。これらの追加の分岐は、以下の議論には影響しません。)
リファレンス名をそのオブジェクトに解決する必要がある操作は、packed-refsファイルを検索する必要があり、これはかなりの時間を要していました。これは、毎日何度も行う操作としては望ましくないほど遅いものでした。これはローカル操作だけでなく、Gitサーバー上の操作にも影響を与え、時間が経つにつれてさらに遅くなることは明らかでした。また、git fetchやgit branchなどのコマンドの出力は、リリース履歴を記録するためだけの目的を持つタグオブジェクトの名前でますます散らかっていました。
デプロイメント履歴をリポジトリに記録することは理にかなっていると思われましたが、数千のタグオブジェクトを使用することには明らかに深刻な欠点がありました。パフォーマンスの問題に加えて、メタデータ自体はタグ名の形式における単なる規約であり、Git自体には読み取れませんでした。ツリーIDはありましたが、タグがそのツリーを参照するコミットを参照することを強制するルールはありませんでした。また、タグオブジェクト自体がその目的に対して設計が悪く思われました。
履歴データをリポジトリに保存する良い理由の1つは、それを不変にすることです。しかし、タグは不変ではありません!いつでも削除したり、任意の名前で新しいものを挿入したりできます。そしてさらに重要なこと:誰かがリリース履歴から知りたいであろう最も自然な質問を考えてみてください。www.our-appアプリを本番環境に最後にデプロイしたのはいつですか?この質問に答える唯一の方法は、すべてのタグの完全なリストを取得し、prod.www.our-appでフィルタリングし、日付でソートして最後のアイテムを取得することでした。あるいは同様に:最新のステージングデプロイメントが悪かった場合、その前のバージョンにすぐにロールバックしたい場合はどうなりますか?ここでも、私たち(またはロールバックプロセス)は、数千のタグのリストをフィルタリングしてソートし、2番目に新しいものを選択する必要がありました。
私は2019年にこの代替案を提案しました。それは、各アプリのデプロイメント履歴を、名前のないが関連付けられたGitオブジェクトのシリーズとして保存するものです。Git自身の履歴メカニズムを使用するため、シンプルで効率的であり、Gitのコマンドとうまく連携します。採用されたとは思えませんが、私が予見していない深刻な欠点があるかもしれません。しかし、同様の状況にある誰かにとっては役立つかもしれません。
数千のリファレンスなしで監査およびロールバックの追跡を維持する
現在、アプリの新しいバージョンがデプロイされるたびに、リファレンスを作成しています。
refs/release/stg.www.our-app-2019-09-06T114410.954-0700-treesha-179dab6fd11bee5195f0f8e31cea3edfb86ae23b
これらのリファレンスが増殖するにつれて、Gitリポジトリの多くの操作はますます遅くなります。この情報は、少なくとも有用で、リファレンスリストを煩雑にしない方法で記録できます。
現在の慣行
現在、リリース手順では次のようなリファレンスを作成します。
新しいリリースがコミットHで作成される
新しいタグオブジェクトが作成され、Hを指す。例:git tag -a refs/release/stg.www.our-app-2019-09-.... H
各新しいタグオブジェクトは、リファレンスデータベースに名前を持ちます。これにより、リリースごとに名前が追加されます。
構造は次のようになります。
リリースごとの1つのリファレンスオブジェクト、それぞれが独自のヘッドコミットを指す
代替提案
各(ティア、アプリ)ペアは、リリースコミットの連結リストで表される
リストのヘッドは最新のリリースである
1つのリファレンスがリストのヘッドを指す
古いリリースは、リンクをたどって過去に戻ることで見つけることができる
この提案の主なポイント:リリースがいくつ行われたかに関わらず、(ティア、アプリ)ペアごとにリファレンスは1つだけです。
これは次のようになります。
前のメタ情報コミットを最初の親とし、実際のリリースコミットを2番目の親とするコミットを含む、メタ情報コミットの連鎖
1つのリファレンス(実際のオブジェクトリファレンスである場合もそうでない場合もある)は、stg.www.our-appという名前です。これは実際のリリースコミットを指すのではなく、ツリーハッシュなどのメタ情報を含むコミットを指します。
メタ情報はいくつかの方法で格納できます。(以下参照。)
リリースの正確な時刻は、メタ情報コミットが作成された正確な時刻であるため、明示的に格納する必要はありません。
メタ情報コミットは2つの親コミットを持ちます。1つは現在のリリースに対応するメタ情報コミットです。もう1つは前のリリースのメタ情報コミットです。
メタ情報コミットは、Gitが通常のコミット履歴を連鎖するのと同じ方法で互いに連鎖しているため、通常のGitコマンドはリリース履歴に対して合理的かつ有用な方法で機能します。
特に、git log stg.www.our-appコマンドは、リリース履歴のリストを生成します。
技術的な詳細
この種の2つの親を持つコミットを作成するのは簡単です。
リファレンスstg.www.our-appが現在のメタ情報コミットを指していると仮定します。そして、新しいコミットHをリリースしたいとします。次のようにします。
(有用なコミットメッセージを/tmp/message.$$に書き込む)
git commit-tree -p refs/release/stg.www.our-app -p H \
H^{tree} < /tmp/message.$$
これにより、-pオプションの引数で指定された2つの親を持つ新しいメタ情報コミットが作成されます。最初の親は現在のリリースのメタ情報コミットであり、2番目の親は新しいコミットHです。git commit-treeコマンドは新しいメタ情報コミットを作成し、そのSHAを標準出力に書き込みます。このSHAをNとします。
git push origin N:refs/release/stg.www.our-app
これでリリースリファレンスはメタ情報コミットを指します。