Git のタグ: git tag でリリースを管理し、バージョンを付ける方法
git tag は、特定のコミットに変わらない名前を付けます。通常はリリースのバージョン(v1.2.0 など)に使います。注釈付きタグは git tag -a v1.2.0 -m "メッセージ" で作成し、サーバーには git push origin v1.2.0 か git push --tags で別途送ります。
git tag は、特定のコミットに付ける名前です。ブランチと違い、新しいコミットが増えても動きません。v1.2.0 は1年後も同じコードのスナップショットを指し続けます。そのため、タグはリリースに使われ、CI/CD のシステムでは、タグが付いたときにビルドや公開を始めるように設定されることがよくあります。
軽量タグと注釈付きタグ
Git には2種類のタグがあり、その違いは重要です。
| 軽量タグ(lightweight) | 注釈付きタグ(annotated) | |
|---|---|---|
| 作成 | git tag v1.0.0 | git tag -a v1.0.0 -m "..." |
| 実体 | コミットへの単なるポインタ | Git のデータベースにある独立したオブジェクト |
| 作成者、日時、メッセージ | なし | あり |
| GPG/SSH による署名 | 不可 | 可能(-s) |
git describe の既定の対象 | いいえ | はい |
| 使いどころ | 個人的な一時的な目印 | リリースと、公開するすべてのもの |
リリースには注釈付きタグを使ってください。誰がいつバージョンを出したかが記録され、署名もできます。
タグを作成する
現在のコミットに付ける
git tag -a v1.2.0 -m "リリース 1.2.0: レポートのエクスポート、API のタイムアウトを修正"
-m を省略すると、Git がエディタを開きます。変更の一覧を書き込みたいときに便利です。
過去のコミットに付ける
リリース時にタグを付け忘れても問題ありません。任意のコミットにタグを付けられます。
git log --oneline -5
# 7be41d0 feat: экспорт отчётов
# 1c02e8f fix: таймаут запросов
git tag -a v1.1.0 1c02e8f -m "リリース 1.1.0"
署名付きタグ
git tag -s v1.2.0 -m "リリース 1.2.0"
git tag -v v1.2.0 # 署名を検証する
GPG の鍵(または gpg.format=ssh の場合は SSH の鍵)の設定が必要です。GitHub と GitLab は、このようなタグを検証済みとして表示します。
タグを確認する
git tag # すべてのタグ(アルファベット順)
git tag -l "v1.*" # パターンで絞り込む
git tag -n # 各タグのメッセージの1行目も表示
git tag --sort=-v:refname # バージョン順、新しいものが上
git show v1.2.0 # タグの情報とコミット
git describe --tags # 最も近いタグ: v1.2.0-3-g7be41d0
並び順に注意してください。既定では、文字列として比較されるため、v1.10.0 は v1.9.0 より前に来ます。--sort=v:refname を使うと、バージョンとして並びます。
git describe の出力の意味は、タグ v1.2.0 の後に3つのコミットがあり、現在のコミットは 7be41d0 である、ということです(g は git を表します)。リリースの間のビルドで、バージョン番号の元として使えます。
タグをサーバーに送る
通常の git push はタグを送りません。これが「GitHub にタグが表示されない」という質問の一般的な原因です。
git push origin v1.2.0 # 1つのタグ
git push --tags # ローカルのすべてのタグ
git push --follow-tags # コミットと、それを指す注釈付きタグ
--follow-tags は --tags より安全です。自分用に付けた軽量タグを誤って送ることがありません。既定の動作にするなら git config --global push.followTags true を設定します。
サーバーからタグを取得するには git fetch --tags を使います。通常の git fetch も、取得するコミットを指すタグを一緒に取ってきます。
タグを削除する・移動する
削除
git tag -d v1.2.0 # ローカルから
git push origin --delete v1.2.0 # サーバーから
# 古い書き方: git push origin :refs/tags/v1.2.0
別のコミットへ移動する
git tag -a -f v1.2.0 <新しいハッシュ> -m "リリース 1.2.0"
git push origin -f v1.2.0
技術的には可能ですが、公開されたタグを移動するのは公開プロジェクトではお勧めしません。すでにタグを取得した人の手元では、自動では更新されないからです。Git は、--force なしの fetch では既存のローカルのタグを上書きしません。その結果、人によって「v1.2.0」が違うコードになってしまいます。正しいのは、v1.2.1 をリリースすることです。
どのコミットがリリースに含まれたか
タグは、履歴を読むすべてのコマンドで、範囲の境界として使えます。
git log v1.1.0..v1.2.0 --oneline # リリース間のコミット
git diff v1.1.0 v1.2.0 --stat # 変更されたファイル
git tag --contains 1c02e8f # このコミットを含むリリースのタグ
git merge-base --is-ancestor 1c02e8f v1.2.0 && echo "リリースに含まれる"
最後の2つのコマンドは、サポートでよくある「この修正はもうユーザーに届いているか」という質問に答えます。修正が main にしかなく、リリースがそれより前に作られていれば、git tag --contains は空の一覧を返し、新しいタグが必要だと分かります。
もう1つ役立つのは、履歴を含まずに特定のバージョンのソースを取り出すことです: git archive --format=zip -o app-1.2.0.zip v1.2.0。顧客への納品用のアーカイブや、隔離された環境でのビルドに使えます。
タグのバージョンに切り替える
git switch --detach v1.1.0
# HEAD is now at 1c02e8f fix: таймаут запросов
これで detached HEAD の状態になります。見ることやビルドすることはできますが、新しいコミットはどのブランチにも属しません。古いバージョンを修正するなら、タグからブランチを作ります。
git switch -c hotfix/1.1.x v1.1.0
# 修正してコミット
git tag -a v1.1.1 -m "セキュリティ修正"
git push origin hotfix/1.1.x --follow-tags
リリース番号: SemVer
タグ名として最も一般的なのは、Semantic Versioning(SemVer)の MAJOR.MINOR.PATCH です。
- MAJOR: 互換性のない API の変更で増やす。例:
1.4.2 → 2.0.0。 - MINOR: 互換性を保った新機能で増やす。例:
1.4.2 → 1.5.0。 - PATCH: バグ修正で増やす。例:
1.4.2 → 1.4.3。 - プレリリースには接尾辞を付ける。例:
2.0.0-beta.1、2.0.0-rc.1。
接頭辞の v(v1.4.2)はタグ名でよく使われる慣習ですが、SemVer 自体は要求しません。大切なのは形式を1つに決めて守ることです。そうしないと、最新のリリースを探すスクリプトが壊れます。
リリース作業の中でのタグ
チームでの典型的なリリースの流れは次のとおりです。
- 変更を main に統合し、テストが通る。
- CHANGELOG と、プロジェクトで管理しているバージョン番号を更新する。
- 注釈付きタグを付ける:
git tag -a v1.5.0 -m "リリース 1.5.0"。 git push origin main --follow-tagsを実行する。- CI が新しいタグを検出し、成果物をビルドして公開する。GitHub や GitLab では、タグから説明とファイル付きの Release のページが作られる。
これで、本番環境の任意のバージョンを、コードに一意に対応づけられます。ビルドの名前にあるタグを見れば、git show でその中身が分かります。git log v1.4.0..v1.5.0 --oneline は、リリースノートのための変更一覧を出力します。
タグからリリースノートを作る
コミットメッセージが分かりやすければ、タグを使って変更履歴を簡単に作れます。直近のリリースと、その前のリリースは、手作業で探さなくても取得できます。
LAST=$(git describe --tags --abbrev=0)
PREV=$(git describe --tags --abbrev=0 "$LAST^")
git log "$PREV..$LAST" --no-merges --pretty=format:"- %s (%h)"
# - feat: экспорт отчётов (7be41d0)
# - fix: таймаут запросов (1c02e8f)
チームが Conventional Commits(feat:、fix:、feat!:)の規則でコミットしていれば、次のバージョン番号を自動で決められます。fix なら PATCH、feat なら MINOR、感嘆符や BREAKING CHANGE があれば MAJOR を増やします。このしくみで、タグの作成と CHANGELOG の生成を自動で行うツールもよく使われています。
タグと CI/CD
パイプラインは通常、ブランチへの push と、タグの出現という2つのイベントを区別します。ブランチではテストとテスト環境のビルドを行い、タグでは公開を行います。GitHub Actions では次のように書きます。
on:
push:
tags:
- "v*.*.*"
GitLab CI では、同じ役割を CI_COMMIT_TAG 変数と rules: - if: $CI_COMMIT_TAG のルールが果たします。パイプライン内でタグ名は、Docker イメージやパッケージのバージョン番号になるので、形式は厳密で予測しやすいものにしてください。
タグを移動してはいけない理由が、ここにもあります。パイプラインがパッケージ 1.2.0 をレジストリに公開済みなら、同じ番号での再公開は通常禁止されています。また、タグ 1.2.0 のイメージを使っているユーザーは、古いままになります。
タグ名として使えるもの
タグ名にもブランチ名と同じ規則が適用されます。空白を含めず、~ ^ : ? * [ と連続するピリオドは使えません。スラッシュは使えるので、モノレポでは api/v1.2.0 や web/v3.0.1 のように、パッケージごとにバージョンを分けるタグがよく使われます。事前に確認するには git check-ref-format "refs/tags/v1.2.0" を使います。形式が不正な場合は、ゼロ以外の終了コードを返します。
タグを保護する
チームのリポジトリでは、リリースタグを保護しておくべきです。GitHub と GitLab では、パターン(例: v*)に一致するタグの作成、上書き、削除を、保守担当者以外に禁止できます。これにより、別のマシンでの誤った git push --tags で予定外のリリースが公開されたり、誤った git push origin --delete でバージョンの履歴が消えたりすることを防げます。
よくあるミス
- タグがサーバーにない。
git push origin <タグ>や--follow-tagsを忘れている。 - ブランチと同じ名前のタグ。 Git が
refname is ambiguousの警告を出します。タグにはvの接頭辞を付け、名前を分けてください。 - 未コミットの状態にタグを付ける。 タグはコミットに付くもので、ファイルに付くものではありません。コミットされていない変更はリリースに含まれません。
- リリースに軽量タグを使う。 作成者、日時、メッセージがなく、
git describeの既定の対象にもなりません。
まとめ
git tag は、履歴の中に変わらない点を作ります。リリースには git tag -a で注釈付きタグを作り、SemVer で名前を付け、git push origin <タグ> か --follow-tags で明示的にサーバーへ送ります。公開したタグは移動させず、新しいバージョンをリリースしてください。タグと rebase、stash、cherry-pick の使い方は、Gitコースで実践的に扱っています。
よくある質問
タグとブランチの違いは何ですか?
ブランチは新しいコミットのたびに自動で先へ進みますが、タグは同じコミットに永久に固定されます。
1つのコミットに複数のタグを付けられますか?
はい。タグの数に制限はありません。たとえば v2.0.0 と latest-stable を同じコミットに付けられます。
ブランチを削除すると、タグも消えますか?
いいえ。タグはブランチから独立しており、ブランチを削除してもタグ付きのコミットは残ります。
あるコミットがどのリリースに含まれたかを知るには?
git tag --contains ハッシュ を実行してください。そのコミットを履歴に含むタグの一覧が表示されます。
スクリプトで最新のタグを取得するには?
git describe --tags --abbrev=0 を使います。ハッシュの接尾辞なしで、最も近いタグ名だけが出力されます。 </content>