プログラミング
Gitリポジトリのリリース履歴を追跡する、より良い方法かもしれない
Maybe a better way to track the release history of a Git repo (blog.plover.com)
要約
この記事では、Gitリポジトリにおけるリリース履歴の管理方法について、従来のタグオブジェクトを大量に使用する方法の欠点を指摘し、代替案を提案しています。大量のタグはパフォーマンス低下や管理の煩雑さを招くため、各アプリのリリース履歴を、メタ情報を持つコミットの連鎖として管理する方式が提案されています。これにより、Gitの履歴メカニズムを活用し、シンプルかつ効率的な履歴管理と監査・ロールバック機能の実現を目指します。
全文翻訳
宇宙の言論空間
Mark Dominus (陶敏修) mjd@pobox.com
私について RSS Atom
最近のエントリ 12件
L-システムはゾウムシ、ピザのトッピング、母系の血統を生成する
イプシロンゼロへの道:「ショートレックス」は実際には単一の順序ではない
もう一つの家の修繕作業の勝利に注目する
仕事日記の勝利
ジョージ・オーウェルの原子爆弾に関するエッセイは「1984年」を予期する
イプシロンゼロへの道:ショートレックス順序は数値のシーケンスも順序付ける
イプシロンゼロへの道:生産的なプログラムと健全な順序
定理には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 Math 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つは新しいリリースHのメタ情報コミットです。メタ情報コミットは、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
リリースリファレンスは、メタ情報コミットを指すようになります。