【Git】タグ完全ガイド|SemVer設計・GPG署名・git describe・GitHub Actions自動リリース・semantic-release・モノレポ戦略まで

Git

Gitのtagは「特定のコミットに固定ラベルを貼る」シンプルな機能ですが、実はソフトウェアリリース管理の骨格を担う重要な仕組みです。タグが雑に運用されると「どのコミットで本番に出したバージョンか不明」という致命的トラブルを招きます。

多くの入門記事はgit tagの作成・削除までで終わっていますが、実務で本当に役立つのはSemVer設計・GPG署名・CI/CDトリガー・GitHub Releases連携・semantic-release自動化・モノレポ戦略まで含めた運用知識です。

この記事では、軽量タグ/注釈付きタグの違いから始めて、バージョン命名規則(SemVer/CalVer)、git describeによる自動バージョニング、gh releaseでのアセット配布、GitHub Actionsでタグpushをトリガーに自動デプロイする実例、semantic-releaseでConventional Commitsから自動タグ付けする最先端ワークフロー、モノレポでのpackage@1.0.0戦略、Protected tagによる保護設定、そして「タグが消えた」「同名タグで衝突」などのトラブル対処まで、2026年のリリース管理スタンダードを網羅します。

この記事で学べること

  • 30秒で使えるタグ操作クイックリファレンス
  • 軽量タグと注釈付きタグの本質的な違いと使い分け
  • Semantic Versioning (SemVer)の設計ルールとpre-release運用
  • git tag -sでGPG署名し改ざんを防ぐ方法
  • git describeでビルドごとに一意なバージョン文字列を自動生成
  • GitHub Releases連携(gh release create/アセット配布)
  • タグpushをトリガーにデプロイするGitHub Actions実例
  • semantic-releaseでConventional Commitsから自動タグ付け
  • モノレポのpackage@versionタグ戦略
  • Protected tagで本番タグ改変を物理禁止する設定
  • 「タグが消えた」「同名タグ衝突」「detached HEAD」などのトラブル対処
スポンサーリンク
  1. 30秒で使えるタグ操作クイックリファレンス
  2. 軽量タグと注釈付きタグ:本質的な違い
  3. タグ作成と確認の完全リファレンス
    1. さまざまなタイミングでタグを作る
    2. タグの一覧とフィルタ
    3. タグの詳細を見る
  4. タグをリモートに送受信する
    1. タグ取得時のよくあるエラー
  5. タグの削除と「移動」:注意点だらけ
    1. 削除の正規ルート
    2. タグの「移動」は強制上書き
    3. 削除したタグを復元する
  6. Semantic Versioning (SemVer):プロが使うバージョン設計
    1. 基本フォーマット
    2. pre-release/build metadata
    3. v-prefix論争:vをつけるか否か
    4. MAJOR/MINOR/PATCHの判断基準
    5. CalVer(Calendar Versioning)という選択肢
  7. GPG署名タグで改ざんを防ぐ
    1. GPG鍵を用意する
    2. 署名タグを作成・検証
  8. git describe:ビルドごとに一意なバージョン文字列を自動生成
    1. ビルド時のバージョン埋め込み
    2. package.jsonやVite/Webpackでの利用
  9. GitHub Releases連携:gh release create実践
    1. リリースノートの自動生成ルール
  10. GitHub Actions:タグpushで自動デプロイ
    1. pre-release用の分岐ジョブ
  11. semantic-release:Conventional Commitsから自動タグ付け
    1. Conventional Commitsルール
    2. semantic-releaseの設定例
  12. モノレポのタグ戦略:package@versionパターン
    1. パッケージごとのタグを絞り込む
  13. Protected tag:本番タグを物理的に守る
    1. GitHubの設定手順
    2. GitLabの設定
  14. よくあるタグのトラブルと対処
    1. pushしたつもりが反映されない
    2. detached HEADになった
    3. タグ名を間違えた
    4. CIが古いタグでビルドしてしまう
  15. よくある質問
  16. 関連記事
  17. まとめ

30秒で使えるタグ操作クイックリファレンス

日常でよく使う操作を最初にまとめておきます。詳細は後述のセクションで深掘りします。

やりたいこと コマンド
注釈付きタグを作成 git tag -a v1.0.0 -m "Release 1.0.0"
GPG署名タグを作成 git tag -s v1.0.0 -m "Signed"
特定のタグをpush git push origin v1.0.0
全タグをpush git push origin --tags
タグ一覧(パターン) git tag -l "v1.*"
タグの詳細表示 git show v1.0.0
ローカルのタグを削除 git tag -d v1.0.0
リモートのタグを削除 git push origin --delete v1.0.0
リモートのタグを取得 git fetch --tags
タグからバージョン文字列生成 git describe --tags
タグ時点のソースを参照 git switch --detach v1.0.0
タグから新ブランチ作成 git switch -c hotfix/v1.0.1 v1.0.0

原則:リリース用途は必ず-a(注釈付き)か-s(署名)を使う。軽量タグは一時的なブックマーク用途限定。GitHubのReleasesを生成するには注釈付きタグが必要です。

軽量タグと注釈付きタグ:本質的な違い

Gitのタグには2種類あり、実体が根本的に違います。軽量タグは単なるコミットへのポインタ注釈付きタグは独立したGitオブジェクトとして作者・日付・メッセージ・署名を保持します。

比較 軽量タグ (lightweight) 注釈付きタグ (annotated)
実体 コミットへの名前付きref 独立したtagオブジェクト
作成コマンド git tag v1.0.0 git tag -a v1.0.0 -m "..."
作者・日付 ✗ 記録されない ◯ 記録される
メッセージ ✗ 持てない ◯ 持てる(リリースノート代替)
GPG署名 ✗ 不可 -sで可能
GitHub Releases連携 △ 使えるが本文を別途管理 ◯ メッセージがそのままリリースノートに
git describe --tags指定が必要 デフォルトで検出
推奨シーン 一時的なブックマーク リリース全般(推奨)
タグオブジェクトの中身を確認
# 軽量タグの場合(コミットそのものが返る)
$ git cat-file -t v0.1-light
commit

# 注釈付きタグの場合(tagオブジェクトが独立で存在)
$ git cat-file -t v1.0.0
tag

# 注釈付きタグのメタ情報
$ git cat-file -p v1.0.0
object 3a8b2c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b
type commit
tag v1.0.0
tagger Taro Yamada <taro@example.com> 1737859200 +0900

Release 1.0.0 - 初回リリース

実務では注釈付きタグ(-aまたは-s)が事実上の標準

Git公式ドキュメントでもリリース用途は注釈付きタグを推奨。作者・日付・メッセージが残り、監査証跡として使える上、GitHub/GitLabのリリース機能と自然に連携します。軽量タグは個人の作業ブックマークや、スクリプトから一時的に付けるマーカーとして限定的に使用。

タグ作成と確認の完全リファレンス

さまざまなタイミングでタグを作る

タグ作成のバリエーション
# 現在のHEADに注釈付きタグ
git tag -a v1.0.0 -m "Release 1.0.0"

# 過去の特定コミットにタグ
git tag -a v0.9.0 3a8b2c1 -m "Beta release"

# タグメッセージを外部エディタで書く
git tag -a v1.0.0

# GPG署名タグ(推奨)
git tag -s v1.0.0 -m "Signed release 1.0.0"

# 既存タグを強制上書き(非推奨/注意)
git tag -f v1.0.0

# 軽量タグ(ブックマーク用途)
git tag nightly-2026-04-21

タグの一覧とフィルタ

タグ検索の実用コマンド
# 全タグ
git tag

# パターン指定
git tag -l "v1.*"
git tag -l "v1.0.*"
git tag -l "*-rc.*"     # pre-releaseのみ

# バージョン順で並べる
git tag -l --sort=-v:refname | head -10    # 新しい順

# タグの詳細一覧(メッセージ付き)
git tag -n5

# 含まれるコミットを元にフィルタ
git tag --contains a1b2c3d          # 該当コミットを含むタグ
git tag --no-contains a1b2c3d       # 含まないタグ

# タグ同士の差分
git diff v1.0.0..v1.1.0
git log v1.0.0..v1.1.0 --oneline    # コミット一覧(リリースノート元ネタ)

タグの詳細を見る

show/log でタグ内容を確認
# タグの全情報(タグメッセージ+コミット+diff)
git show v1.0.0

# タグを含むログ表示
git log --tags --oneline --graph

# 特定タグから現在までのコミット
git log v1.0.0..HEAD --oneline

# 2つのタグ間の変更ファイル一覧
git diff --name-only v1.0.0 v1.1.0

タグメッセージはリリースノートの母体として扱うのが鉄則。git log v1.0.0..v1.1.0 --onelineの出力をそのままタグメッセージに貼り付けると、GitHub Releasesで自動的にリリースノート化されます。

タグをリモートに送受信する

タグはデフォルトでgit pushされないのが最大の落とし穴です。明示的にpushしないと「ローカルにしかタグが無い」状態になります。

push/fetch のパターン
# 特定タグだけpush(推奨:意図が明確)
git push origin v1.0.0

# 全タグpush(大規模リポジトリでは避ける)
git push origin --tags

# pushの際にタグも自動でpushする設定
git config --global push.followTags true
# → git pushすると関連タグも自動送信される

# リモートのタグを全部取得
git fetch --tags

# リモートに存在しないローカルタグを削除(整理)
git fetch --prune --prune-tags

git push --tags全タグを送信するため、大量の古いタグや試験用タグが混ざっているリポジトリでは思わぬタグまで公開されます。リリースでは必ずgit push origin v1.0.0のように個別指定するか、push.followTags=true設定で関連タグだけ送るのが安全。

タグ取得時のよくあるエラー

“tag already exists” の対処
# エラー例(リモートのタグとローカルのタグが別物)
$ git fetch
! [rejected]        v1.0.0 -> v1.0.0  (would clobber existing tag)

# 解決:強制的にリモートのタグで上書き
git fetch --tags --force

# または該当タグだけ削除して再取得
git tag -d v1.0.0
git fetch origin tag v1.0.0

タグの削除と「移動」:注意点だらけ

タグは削除・移動できますが、公開済みタグを動かすのは重大なアンチパターンです。

削除の正規ルート

ローカル/リモート削除
# ローカルで削除
git tag -d v1.0.0

# リモートで削除(新しい書き方)
git push origin --delete v1.0.0

# リモートで削除(昔の書き方・現役で動く)
git push origin :refs/tags/v1.0.0

# 両方一気に
git tag -d v1.0.0 && git push origin --delete v1.0.0

タグの「移動」は強制上書き

タグを別コミットに付け替える
# 同じ名前で別コミットに作り直す(ローカル)
git tag -f v1.0.0 a1b2c3d

# リモートに強制push(--forceが必須)
git push origin v1.0.0 --force

公開済みタグを移動するのは実質”履歴の書き換え”です。他の開発者・CI/CD・Dockerイメージレジストリ・パッケージレジストリ(npm等)がそのタグを信頼して参照しているため、移動するとビルドの再現性が崩壊します。タグは不変(immutable)として運用するのが鉄則。間違えたら削除せず、v1.0.1 としてパッチリリースするのが正規フロー。

削除したタグを復元する

reflog/リモートから復元
# ローカルのreflogから探す
git reflog --all | grep v1.0.0

# コミットハッシュが分かれば復元
git tag -a v1.0.0 a1b2c3d -m "Restore v1.0.0"

# リモートに残っていればfetchで戻る
git fetch origin tag v1.0.0

Semantic Versioning (SemVer):プロが使うバージョン設計

タグ名にはルールがあります。業界標準のSemVersemver.org)は、npm/Composer/Cargo/Goなど主要パッケージマネージャが採用しているバージョン体系です。

基本フォーマット

MAJOR.MINOR.PATCH の増やし方
v<MAJOR>.<MINOR>.<PATCH>

例: v1.2.3
    │ │ └── PATCH : 後方互換のバグ修正
    │ └──── MINOR : 後方互換の機能追加
    └────── MAJOR : 破壊的変更(API互換性が壊れる)

pre-release/build metadata

pre-release(rc/beta/alpha)とビルドメタ
v1.0.0-alpha.1        # アルファ版
v1.0.0-beta.2         # ベータ版
v1.0.0-rc.1           # リリース候補
v1.0.0                # 正式リリース
v1.0.0+20260421       # ビルドメタデータ(順序に影響しない)
v1.0.0-rc.1+exp.sha.5114f85  # pre-release + メタ複合

# SemVerでの並び順
# 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-beta < 1.0.0-rc.1 < 1.0.0

v-prefix論争:vをつけるか否か

結論:つける派が圧倒的多数

  • vを付ける派:Kubernetes/Docker/Go(公式ツール)/Linux kernelなど
  • vを付けない派:npm(package.jsonのversion欄はvなし)/SemVer仕様自体
  • 実務では:Gitタグ名にはvを付ける(v1.0.0)、package.jsonには付けない("version": "1.0.0"
  • 混在を避けるためチーム内でルールを明文化しCONTRIBUTING.mdに記載

MAJOR/MINOR/PATCHの判断基準

変更内容 影響箇所 上げるべきバージョン
バグ修正(API変更なし) 内部実装 PATCH(1.2.3→1.2.4
機能追加(後方互換) 新API追加 MINOR(1.2.3→1.3.0
破壊的変更 既存APIの削除・引数変更 MAJOR(1.2.3→2.0.0
API deprecation 非推奨マーク(まだ動く) MINOR
セキュリティ修正 内部(API互換) PATCH(ただし速やかにリリース)

CalVer(Calendar Versioning)という選択肢

日付ベースのバージョニング
# 年.月.パッチ
2026.04.1
v2026.04.1

# 年.リリース番号
2026.3

# Ubuntu風 (YY.MM)
26.04

CalVerは破壊的変更の概念があいまいなOS・SaaS・フレームワークで採用されます(Ubuntu/JetBrains製品/pip等)。ライブラリ開発ではSemVer一択

GPG署名タグで改ざんを防ぐ

git tag -sで作成した署名タグは、作成者本人が正規の鍵でサインした証拠を持つため、リリース配布や企業配信で改ざん検出の要として使われます。

GPG鍵を用意する

GPG鍵の生成と設定
# 鍵を生成(対話式)
gpg --full-generate-key
# RSA/ED25519 → 4096 → 無期限 or 2年 → 名前・メール入力

# 鍵IDを確認
gpg --list-secret-keys --keyid-format=long
# sec  ed25519/AB12CD34EF567890 ...

# Gitに署名鍵を登録
git config --global user.signingkey AB12CD34EF567890
git config --global tag.gpgSign true  # デフォルトで署名

# 公開鍵をエクスポート(GitHub/GitLabに登録)
gpg --armor --export AB12CD34EF567890

署名タグを作成・検証

sign & verify
# 署名タグ作成
git tag -s v1.0.0 -m "Signed release 1.0.0"

# 署名付きかどうか確認
git tag -v v1.0.0
# gpg: Good signature from "Your Name <you@example.com>"

# 署名が無いタグは弾く(厳格なリリース時)
if ! git tag -v v1.0.0 2>/dev/null; then
  echo "ERROR: v1.0.0 is not signed" >&2
  exit 1
fi

GitHubでは署名タグにVerifiedバッジが表示されます。Linux kernelやGnuPGなどセキュリティ重視のプロジェクトでは署名タグが必須。企業配布ではsignatureを検証しないCI/CDはサプライチェーン攻撃の入口になるため要注意。

git describe:ビルドごとに一意なバージョン文字列を自動生成

git describe最も近いタグ+それ以降のコミット数+短縮ハッシュを連結したバージョン文字列を出力します。CI/CDや--version出力に最適です。

describeの実用例
# 最も近い注釈付きタグを取得
$ git describe
v1.0.0

# タグ以降にコミットがある場合
$ git describe
v1.0.0-3-ga1b2c3d
#  └────┘ └─┘ └───────┘
#   タグ  差分  shortハッシュ

# 軽量タグも対象にする
git describe --tags

# dirty(未コミット変更あり)時は-dirtyを付与
git describe --dirty
# → v1.0.0-3-ga1b2c3d-dirty

# 最初に見つかるタグのみ(matchパターン)
git describe --match "v*" --tags
git describe --exclude "*-rc.*"    # RCを除外

# タグ完全一致(タグが付いていないコミットはエラー)
git describe --exact-match

ビルド時のバージョン埋め込み

Makefile / Goでのバージョン注入例
# Makefile
VERSION := $(shell git describe --tags --always --dirty)

build:
	go build -ldflags "-X main.version=$(VERSION)" ./cmd/myapp

# 実行時確認
$ ./myapp --version
myapp v1.0.0-3-ga1b2c3d-dirty

package.jsonやVite/Webpackでの利用

Node.js環境での埋め込み
# package.json scripts
{
  "scripts": {
    "version": "git describe --tags --always > src/version.txt"
  }
}

# または環境変数
VITE_APP_VERSION=$(git describe --tags --always) npm run build

GitHub Releases連携:gh release create実践

GitHubのReleases機能はタグに紐づくページを自動生成し、ビルド成果物(アセット)の配布やリリースノートの公開を行えます。CLIのgh(GitHub CLI)を使えば完全自動化が可能です。

gh releaseコマンドの基本
# タグを作ってpush
git tag -a v1.0.0 -m "Release 1.0.0"
git push origin v1.0.0

# GitHubでReleaseを作成(メモを自動生成)
gh release create v1.0.0 --generate-notes

# タイトル・本文を指定
gh release create v1.0.0 \
  --title "v1.0.0 - 初回リリース" \
  --notes "主な変更点..."

# アセットをアップロード
gh release create v1.0.0 \
  --generate-notes \
  dist/myapp-linux-amd64.tar.gz \
  dist/myapp-windows-amd64.zip

# pre-release(RC/ベータ)
gh release create v1.0.0-rc.1 --prerelease --generate-notes

# 下書き
gh release create v1.0.0 --draft

リリースノートの自動生成ルール

.github/release.yml でフィルタ設定
# .github/release.yml
changelog:
  exclude:
    labels:
      - ignore-for-release
  categories:
    - title: "新機能"
      labels:
        - enhancement
    - title: "バグ修正"
      labels:
        - bug
    - title: "その他"
      labels:
        - "*"

GitHub Actions:タグpushで自動デプロイ

タグが付いたタイミングをトリガーに、ビルド・テスト・配布・デプロイを完全自動化できます。手動デプロイのミスを排除する最重要パターンです。

.github/workflows/release.yml
name: Release

on:
  push:
    tags:
      - 'v*.*.*'
      - '!v*.*.*-*'   # pre-releaseは別jobで

permissions:
  contents: write   # gh release createに必要

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0      # git describeのためタグ含め全履歴取得

      - name: Get version
        id: version
        run: echo "tag=${GITHUB_REF_NAME}" >> $GITHUB_OUTPUT

      - name: Verify signed tag
        run: git tag -v ${{ steps.version.outputs.tag }}

      - name: Build
        run: |
          npm ci
          npm run build

      - name: Create GitHub Release
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          gh release create "${{ steps.version.outputs.tag }}" \
            --generate-notes \
            dist/*.tar.gz

pre-release用の分岐ジョブ

RC/beta タグに反応させる
on:
  push:
    tags:
      - 'v*.*.*-rc.*'
      - 'v*.*.*-beta.*'

jobs:
  prerelease:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Create prerelease
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          gh release create "${GITHUB_REF_NAME}" \
            --prerelease \
            --generate-notes

タグ運用の自動化パターン

  • タグpush→ビルド→Docker Hub:Dockerタグも自動でv1.0.0に
  • タグpush→npm publish:公開ライブラリの自動配布
  • タグpush→本番デプロイ(EC2/ECS/Cloud Run)
  • Release公開→Slack通知:リリース通知の自動化

semantic-release:Conventional Commitsから自動タグ付け

semantic-releaseはコミットメッセージを解析して次のバージョンを自動計算し、タグ作成・Release作成・CHANGELOG更新まで行うツール。人間がバージョン番号を決める手間をゼロにする現代的なアプローチです。

Conventional Commitsルール

コミットメッセージ規約
feat: 新機能を追加            → MINORアップ
fix: バグを修正               → PATCHアップ
docs: ドキュメント変更のみ    → バージョン据え置き
chore: ビルド雑務など         → バージョン据え置き
refactor: リファクタ          → バージョン据え置き

# 破壊的変更(MAJORアップ)
feat!: 新認証方式に変更

feat: 認証を刷新

BREAKING CHANGE: 認証方式を OAuth2 に統一

semantic-releaseの設定例

package.json(Node.jsプロジェクト)
{
  "scripts": {
    "release": "semantic-release"
  },
  "release": {
    "branches": ["main", { "name": "beta", "prerelease": true }],
    "plugins": [
      "@semantic-release/commit-analyzer",
      "@semantic-release/release-notes-generator",
      "@semantic-release/changelog",
      "@semantic-release/npm",
      "@semantic-release/github",
      [
        "@semantic-release/git",
        {
          "assets": ["CHANGELOG.md", "package.json"],
          "message": "chore(release): ${nextRelease.version} [skip ci]"
        }
      ]
    ]
  }
}
GitHub Actionsでsemantic-releaseを実行
name: Semantic Release

on:
  push:
    branches: [main]

jobs:
  release:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      issues: write
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npm ci
      - env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
        run: npx semantic-release

semantic-releaseを使うとmainへのmergeだけで自動的にタグが切られ、Releaseが作成され、CHANGELOG.mdが更新されます。人間がバージョン番号を決める必要がなくなり、リリース漏れやミスが激減。ただしConventional Commits運用が前提なので、PRタイトル規約+PRタイトルリントの併用がおすすめ。

モノレポのタグ戦略:package@versionパターン

1つのリポジトリに複数パッケージを含むモノレポ(Turborepo/Nx/Rush)では、単純なv1.0.0ではどのパッケージのバージョンか分かりません。プレフィックス付きタグでパッケージを識別します。

モノレポでのタグ命名例
# Lerna/Changesets系
@myorg/core@1.2.0
@myorg/cli@0.5.3
@myorg/ui@2.0.0-beta.1

# プレフィックスだけ
core-v1.2.0
cli-v0.5.3

# フラット運用(全パッケージ同期)
v2.0.0   # 全パッケージが2.0.0にそろう

パッケージごとのタグを絞り込む

個別パッケージのタグだけ取得
# @myorg/core のタグ履歴
git tag -l "@myorg/core@*"

# 最新タグ
git tag -l "@myorg/core@*" --sort=-v:refname | head -1

# describe でcore専用の最近タグ
git describe --match "@myorg/core@*" --tags

モノレポ自動化のおすすめ

  • Changesets:パッケージごとにSemVer管理、PR単位でchangeファイル追加
  • Lernalerna publishでバージョンUP+タグ付与が一気通貫
  • Nx Release:Nx公式のリリース機構、独立版/固定版を選べる

Protected tag:本番タグを物理的に守る

誤ったタグ移動や悪意ある書き換えから本番を守るには、GitHub/GitLabのProtected tag機能を使います。設定するだけで--forceや削除を禁止できます。

GitHubの設定手順

Settings → Tags → New rule
# 保護パターン例
v*.*.*           # 全SemVerタグ
v*.*.* & !*-rc.*  # rc除くすべての正式版

# 保護されると:
# - タグの作成は指定ロールのみ
# - タグの削除・force pushが禁止

GitLabの設定

GitLab Protected tags
# Settings → Repository → Protected tags
Tag: v*
Allowed to create: Maintainers

本番リリース用タグ(v*.*.*)は必ず保護。RCや試験用タグは保護せず柔軟に運用するのが実用的。保護設定はSettingsから5分で完了するので、リポジトリ立ち上げ時の初期設定タスクに組み込むと事故を未然に防げます。

よくあるタグのトラブルと対処

pushしたつもりが反映されない

原因:タグは自動pushされない
# ダメな例:コミットと一緒にpushしたつもり
git commit -am "Release 1.0.0"
git tag v1.0.0
git push          # ← タグはpushされない!

# 正しい方法
git push origin v1.0.0
# または push.followTagsを有効化しておく
git config --global push.followTags true

detached HEADになった

タグ参照時の状態管理
# タグをcheckoutするとdetached HEAD
git switch --detach v1.0.0
# → HEAD is now at a1b2c3d Release 1.0.0

# この状態でコミットするとどのブランチにも紐づかない
# → hotfix/パッチリリースしたい場合はブランチを切る
git switch -c hotfix/v1.0.1 v1.0.0

タグ自体は移動できますがコミット履歴の分岐を作るにはブランチ化が必要です。「v1.0.0の地点からバグ修正→v1.0.1リリース」ならgit switch -c hotfix/v1.0.1 v1.0.0でhotfixブランチを作り、修正後にgit tag -a v1.0.1 -m "..."。detached HEADからの詳細復帰は【Git】detached HEAD状態から元の作業ブランチに戻す方法で解説しています。

タグ名を間違えた

リネームの正規フロー
# v1.00 → v1.0.0 に改名したい
# 1) 新しいタグを同じコミットに付ける
git tag -a v1.0.0 v1.00 -m "Release 1.0.0"

# 2) 旧タグをローカル・リモート両方削除
git tag -d v1.00
git push origin --delete v1.00

# 3) 新タグをpush
git push origin v1.0.0

CIが古いタグでビルドしてしまう

actions/checkoutデフォルトで浅いクローンfetch-depth: 1)のため、git describeが古いタグしか見つけられないケースがあります。タグ情報を完全に取得するにはfetch-depth: 0fetch-tags: trueを指定。

actions/checkoutでタグ完全取得
- uses: actions/checkout@v4
  with:
    fetch-depth: 0
    fetch-tags: true

よくある質問

Q軽量タグと注釈付きタグ、結局どちらを使うべき?
Aリリース用途は必ず注釈付きタグ(-a)またはGPG署名タグ(-s。軽量タグは一時的なブックマークや、個人の作業マーカーに限定します。GitHubのReleases機能は注釈付きタグ前提で動作するため、迷ったら-aにしておけば後悔しません。
Qタグ名にvを付けるべき?
AGitタグ名には付けるのが業界慣習です(v1.2.3)。Kubernetes/Docker/Linuxカーネル/Goツールなど主要OSSはほぼ全てvあり。ただしnpmのpackage.json"version"フィールドには付けない"1.2.3")。タグと内部バージョン文字列で扱いが違う点に注意。
Qタグを付け忘れてコミットしてしまった
A過去のコミットに遡ってタグを付けられます。git log --onelineで対象コミットのハッシュを確認し、git tag -a v1.0.0 <ハッシュ> -m "Release 1.0.0"で付与。その後git push origin v1.0.0でリモートへ反映。
Qタグを消したが間違いだった、復元できる?
A可能性3通り。①ローカルに残っていればgit push origin v1.0.0で戻る。②リモートに残っていればgit fetch origin tag v1.0.0で取得。③どちらにも無ければgit reflog --all | grep v1.0.0でコミットを探し、同じコミットに再度git tag -aで付与。GitHubではAdminが90日以内の削除タグをサポートに依頼して復旧できる場合もある。
Qタグを付けた後、ブランチを削除しても大丈夫?
A問題ありません。タグはコミットオブジェクトへの参照なので、ブランチが消えてもコミット自体はタグがある限りGCされません。featureブランチにタグを付けて本番化し、feature/*を削除する運用は一般的。
Qpre-release(rc/beta)のバージョン付与ルールは?
ASemVer仕様に従い、ハイフンの後にpre-release識別子を置きます。例:v1.0.0-alpha.1v1.0.0-beta.1v1.0.0-rc.1v1.0.0。識別子はalpha < beta < rcの順序が暗黙慣習。ドット区切りで番号を増やします(rc.1rc.2)。
QタグをトリガーにDockerイメージを自動ビルドしたい
AGitHub Actionsでon.push.tagsにパターン指定し、docker/build-push-actionを使うのが定番です。${{ github.ref_name }}でタグ名を取得し、Docker tagに使用。さらにdocker/metadata-actionを使うとv1.2.3v1.2v1latestを自動生成できます。
Q何度もタグを移動してしまった、もう戻せない?
A原則としてタグ移動は履歴改変なので、他メンバーがfetch済みだと混乱が残ります。正しい対処は「現状のタグをそのままにして新しいパッチバージョン(v1.0.1)を発行」。公開タグは不変(immutable)として扱い、再リリースで問題を解決するのがSemVerの哲学です。

関連記事

まとめ

  • リリース用途は注釈付きタグ(-a署名タグ(-s。軽量タグは一時ブックマーク用
  • タグは自動pushされないgit push origin v1.0.0またはpush.followTags=true設定
  • 命名はSemVer(v1.2.3が業界標準。pre-releaseはv1.0.0-rc.1形式
  • 改ざん対策はGPG署名タグgit tag -v検証。企業配布では必須
  • git describe --tags --dirtyでビルドごとに一意なバージョン文字列を自動生成
  • GitHub Actions+gh release createでタグpushを起点に完全自動リリース
  • semantic-releaseとConventional Commitsでバージョン決定を自動化
  • モノレポは@org/pkg@1.0.0スタイルでパッケージ識別
  • 本番タグはProtected tag設定で移動・削除を物理禁止
  • 公開タグは不変(immutable)として扱い、間違えたら再リリースで解決

タグは「コミットにラベルを付ける」以上の意味を持ち、リリース管理・CI/CD・サプライチェーンセキュリティ・バージョン決定の自動化を支える基盤です。SemVer+注釈付きタグ+署名+Protected tag+GitHub Actions自動化、そしてモノレポではpackage@version戦略——これらを組み合わせれば、リリース作業は「mainへmergeするだけ」にまで自動化できます。2026年のGit運用では、タグはもはや手動管理するものではなく、自動化パイプラインで守り抜く成果物です。