大きな画面での利用をおすすめします — アニメーションと操作がより快適になります。
入力

タグとリリース

Git タグは選んだオブジェクトに版の名前を付け、ブランチは開発を続けられる。タグは Git 参照で、注釈付きタグはタグオブジェクトも持つ。ホスティング側のリリースは Git の外にあり、タグと関連付く。本レッスンの登録簿は公開時にタグを剥がして得たコミットも、ソースのスナップショットとして保存する。

main は積み込み中の台車の位置で、コミットが進める。タグは特定の箱に貼った名前で、通常のコミットでは動かない。ただし -f で同じ名前を別の箱へ付け直せる。本レッスンの出荷記録は、その時に使った箱の ID を残す。

ブランチは自動で進み、タグは進まない

ブランチもタグ参照もオブジェクト ID を保存する。違いは「タグは不変」ではない。HEAD がブランチに接続していれば commit はそのブランチを進めるが、旧先端のタグは進めない。タグは明示的な参照書き換えでのみ動く。

軽量タグは refs/tags/<name> に対象 ID を直接持つ。対象がコミットなら、剥離ですぐ到達できる。

注釈付きタグは参照とコミットの間にオブジェクトを挟む。本レッスンでは object 行、type commit、タグ名、tagger 情報、メッセージを持ち、参照にはタグオブジェクトの ID を書く。v1.0.0^{} は参照からタグオブジェクトへ進み、object をたどってコミットに達する。オブジェクトは内容で識別されるが、参照は書き換え可能だ。

ホスト側のリリースは Git オブジェクトではなく、タイトル、配布物、下書き状態などをプロバイダーのデータベースが持つ。全てが本レッスンのようにソースを固定記録するわけではない。GitHub のリリース API は tag_name で関連付け、target_commitish はタグを作る必要があるときに使う。既存タグの公開時コミットを常に保存する契約ではない。GitLab のリリース API も tag_name を中心にし、作成時に ref を使う。本レッスンの剥離コミット記録は、各ホスト共通の保証ではない。

git switch --detach v1.0.0 もタグを剥離し、コミット ID を HEAD へ直接書く。HEAD は main を名前に持たなくなる。そこでコミットしても分離 HEAD だけが進み、ブランチとタグは残る。

タグとリリースのステップ

既定では固定の空ツリーへ allow-empty コミットを作り、参照を観察する。

  1. HEAD -> main で始まり、main は未誕生。コミット、タグ、リリースはない。
  2. git commit --allow-empty -m "release-candidate" が親なしの C1 を作り、存在してから main を C1 へ進める。
  3. git tag -a v1.0.0 -m "First stable" が HEAD を C1 に解決し、object で C1 を指す注釈付きオブジェクトを作る。refs/tags/v1.0.0 をその ID へ向け、剥離結果は C1 になる。
  4. Publish hosted release from v1.0.0 が C1 へ剥離し、登録簿にタイトル VizLearn 1.0.0、配布物 vizlearn-1.0.0.zip、ソース C1 を保存する。Git の参照やオブジェクトは変えない。
  5. git commit --allow-empty -m "next-work" が C2 を作り、main だけを進める。タグと登録簿のソースは C1 に残る。
  6. git switch --detach v1.0.0 が C1 に戻り、HEAD を main の名前から直接 ID に変える。最終状態は main→C2、タグ→注釈オブジェクト→C1、リリースソース→C1、HEAD→C1(分離)。

開発ブランチが進んでも版ラベルと出荷記録は元コミットに残る。タグで元版を調べても main は戻らない。

版の名前だけでは来歴にならない

リリースが v1.0.0 という文字列だけを持ち、後で C2 上の git tag -f -a v1.0.0 ... で付け替えられたとする。現在の名前は C2 に解決しても、公開済み ZIP は C1 由来かもしれない。

強制移動プリセットは登録簿の C1 を保つため、リリースソース C1 と現在タグ C2 を同時に見せる。不一致を知らせるだけで自動修復はしない。Git は明示的な参照更新を許し、実ホストが残す証拠はそのデータモデルで決まる。

リリースのソースを確認する

ブランチ先端、剥離コミット、タグオブジェクトを比べる。

git rev-parse refs/heads/main
git rev-parse 'v1.0.0^{}'
git cat-file -t v1.0.0
git cat-file -p v1.0.0

注釈付きなら git cat-file -t は tag、-p は対象を示す。軽量なら対象オブジェクトの型を返す。剥離コミットを CI のビルド SHA と比較し、shasum -a 256 vizlearn-1.0.0.zip などで配布物自体のダイジェストを求め、リリースと共に示す。タグは名前、コミット ID と配布物のダイジェストは何をビルドして送ったかを表す。

境界ケース

  • 未誕生 HEAD には対象がなく、タグを作れない。
  • 同名タグがあり -f がなければ、新しい注釈オブジェクトや参照を書く前に失敗する。
  • タグがなければ公開と分離チェックアウトは失敗し、仮の参照やリリースを作らない。
  • 登録簿はタグ名ごとに一リリースだけを許し、重複時は元記録を保つ。
  • タグのチェックアウト後のコミットは HEAD だけを進め、main、タグ、リリースソースは動かさない。
  • 強制更新はタグの対象を変える。公開済みなら記録ソース、現在の剥離先、配布物のダイジェストを比較する。

よくある間違い

  • タグは不変だと言う。不変なのは内容アドレスのタグオブジェクトで、参照は書き換えられる。通常のコミットが自動で動かさないだけだ。
  • 全ホストが公開時の剥離コミットを保存すると思う。API と CI 記録を確認する必要がある。
  • リリースページや配布物を Git オブジェクトと考える。コミット、ツリー、blob、注釈タグは Git にあり、ホストのメタデータは別にある。
  • タグへ HEAD が接続すると思う。コミットへ剥離して HEAD を分離する。
  • タグ指定の公開で現在の main から作る。main は先へ進んでいる可能性があり、選んだタグを解決する。
  • 公開済み名を強制移動し、ID やダイジェストを比べない。一つの名前が異なる二ビルドを表してしまう。

モデルの範囲

一ブランチ、固定空ツリーの allow-empty コミット、コミットを直接指す注釈タグを扱う。署名、入れ子タグ、転送、packed refs、ロック、reflog、フック、CI 実行、チェックサムと SBOM の保存、認証、下書き・プレリリース、ダウンロード数、ホスト固有動作は省く。ID は SHA-1 形式で、SHA-256 リポジトリは別方式を使う。登録簿のソース記録は来歴のずれを見せるためで、GitHub や GitLab を完全再現するものではない。