- Git
git tagとセマンティックバージョニング|命名規則・コマンド早見表
git tag は特定のコミットに v1.2.3 のような名前を付ける機能で、セマンティックバージョニング(SemVer)は「MAJOR.MINOR.PATCH」の3つの数字で変更の大きさを伝えるバージョン番号の付け方です。 2つを組み合わせると、「どのコミットが、どの程度の変更を含むリリースなのか」を誰でも同じ基準で読み取れるようになります。
この記事では、SemVer 2.0.0 の規則を公式仕様(semver.org)で確認したうえで、「この変更ならどの数字を上げるか」の判断表と、プレリリース(-rc.1 など)の並び順を npm の semver パッケージで実際に並べ替えた結果を載せています。さらに、git tag の作成・一覧・push・削除・付け替え・署名・git describe を、ローカルリポジトリと bare リモートで実際に実行した出力つきで早見表にしました(Git 2.43.0)。タグ push から GitHub Release を作るワークフローや、付け間違い・同名ブランチとの衝突といったトラブルの直し方もまとめています。
git tag とバージョン番号の早見表
よく使う操作はこの表で足ります。 リリースには注釈付きタグ(-a)を使うのが基本です。
| やりたいこと | コマンド |
|---|---|
| 今のコミットにリリース用タグを付ける | git tag -a v1.2.0 -m "Release v1.2.0" |
| 過去のコミットにタグを付ける | git tag -a v1.1.0 <コミットID> -m "Release v1.1.0" |
| 軽量タグを付ける(目印用) | git tag tmp-check |
| タグの一覧 | git tag |
| パターンで絞り込む | git tag -l "v1.0.*" |
| バージョン順に並べる | git tag --sort=v:refname |
| プレリリースを正式版より前に並べる | git -c versionsort.suffix=- tag --sort=v:refname |
| メッセージも一覧に出す | git tag -n1 |
| タグの中身を見る | git show v1.2.0 --no-patch |
| あるコミットを含むタグ | git tag --contains <コミットID> |
| HEAD に付いているタグ | git tag --points-at HEAD |
| 1つだけ push | git push origin v1.2.0 |
| すべて push | git push origin --tags |
| ブランチと一緒に注釈付きタグだけ push | git push --follow-tags origin main |
| リモートのタグを確認 | git ls-remote --tags origin |
| ローカルのタグを削除 | git tag -d v1.2.0 |
| リモートのタグを削除 | git push origin --delete v1.2.0 |
| タグを付け替える | git tag -fa v1.2.0 <コミットID> -m "..." → git push --force origin v1.2.0 |
| 署名付きタグ | git tag -s v1.2.0 -m "..."、検証は git tag -v v1.2.0 |
| 直近のタグからの位置 | git describe(例:v1.0.0-1-g1772455) |
| 直近のタグ名だけ | git describe --tags --abbrev=0 |
| 削除されたリモートタグを手元からも消す | git fetch --prune --prune-tags |
| 変更の内容 | 上げる番号 | 例(1.4.2 から) |
|---|---|---|
| 互換性を壊す変更(API の削除・引数の変更) | MAJOR | 2.0.0 |
| 互換性を保った機能追加・非推奨化 | MINOR | 1.5.0 |
| 互換性を保ったバグ修正 | PATCH | 1.4.3 |
| 正式版の前の試験版 | プレリリース | 2.0.0-rc.1 |
| ビルド情報の付記(順序に影響しない) | ビルドメタデータ | 1.4.2+build.20261001 |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「git tagとセマンティックバージョニング|命名規則・コマンド早見表」
セマンティックバージョニング(SemVer 2.0.0)の規則
SemVer は「公開している API に対して、互換性がどう変わったか」を番号で伝える仕組みです。 2026年10月時点の最新仕様は 2.0.0 で、要点は次のとおりです。
| 規則 | 内容 |
|---|---|
| 形式 | X.Y.Z(MAJOR.MINOR.PATCH)。各数字は0以上の整数で、先頭に0を付けない(01 は不可) |
| MAJOR | 後方互換性のない変更をしたら上げる。MINOR と PATCH は0に戻す |
| MINOR | 後方互換性のある機能追加、または機能を非推奨にしたら上げる。PATCH は0に戻す |
| PATCH | 後方互換性のあるバグ修正だけなら上げる |
| 0.y.z | 初期開発用。いつでも何でも変わりうるため、API は安定していないものとして扱う |
| 1.0.0 | 公開 API が定まった時点で付ける |
| 一度出したら変えない | リリース済みのバージョンの中身は変更しない。直すなら新しいバージョンを出す |
| プレリリース | PATCH の後に - と識別子([0-9A-Za-z-] をドットで区切る)。例:1.0.0-alpha、1.0.0-rc.1 |
| ビルドメタデータ | + の後に識別子。例:1.0.0+20261001。優先順位の判定では無視する |
v 接頭辞 |
v1.2.3 はセマンティックバージョンそのものではないが、バージョン番号であることを示す一般的な書き方(公式 FAQ) |
この変更ならどれを上げるか(判断表)
迷ったら「今の利用者が、コードを変えずに新しい版へ上げられるか」で判断します。 上げられないなら MAJOR です。
| 変更の例 | 利用者のコード修正 | 上げる番号 |
|---|---|---|
| 関数・エンドポイントを削除した | 必要 | MAJOR |
| 必須の引数を追加した | 必要 | MAJOR |
| 戻り値の型や JSON のフィールド名を変えた | 必要 | MAJOR |
| 対応する言語ランタイムの最低バージョンを上げた | 環境の更新が必要 | MAJOR(多くのライブラリの慣習) |
| 新しい関数・エンドポイントを追加した | 不要 | MINOR |
| 省略可能な引数を追加した(既定値で従来どおり動く) | 不要 | MINOR |
| 機能を非推奨にした(まだ動く) | 不要 | MINOR |
| バグを直した(仕様どおりの動きに戻した) | 不要 | PATCH |
| 内部のリファクタリング・性能改善だけ | 不要 | PATCH |
| ドキュメント・テストだけの変更 | 不要 | リリース不要(出すなら PATCH) |
| 依存ライブラリを互換性のある範囲で更新 | 不要 | PATCH |
| 0.x のうちに互換性を壊した | 必要 | MINOR(0.3.1 → 0.4.0 が慣習) |
「バグ修正だが、その誤った動きに頼っている利用者がいる」場合は判断が分かれます。 仕様書や API ドキュメントに書かれた動きに戻すなら PATCH、ドキュメントにない動きでも多くの利用者が前提にしているなら MINOR か MAJOR にして、変更履歴で丁寧に説明すると親切です。
番号を上げる操作を npm の semver で確認する
npm の依存解決にも使われている semver パッケージ(7.8.5)で、番号の上げ方と互換範囲を確認しました。
npm init -y
npm install semver
// inc.mjs
import semver from "semver";
console.log("patch:", semver.inc("1.4.2", "patch"));
console.log("minor:", semver.inc("1.4.2", "minor"));
console.log("major:", semver.inc("1.4.2", "major"));
console.log("premajor rc:", semver.inc("1.4.2", "premajor", "rc"));
console.log("次のrc:", semver.inc("2.0.0-rc.0", "prerelease"));
console.log("v1.2.3 は有効?:", semver.valid("v1.2.3"));
console.log("1.2 は有効?:", semver.valid("1.2"), "/ coerce:", semver.coerce("1.2")?.version);
console.log("^0.3.1 は 0.4.0 を許す?:", semver.satisfies("0.4.0", "^0.3.1"), "/ 0.3.9:", semver.satisfies("0.3.9", "^0.3.1"));
console.log("^1.0.0 は 1.5.0-rc.1 を許す?:", semver.satisfies("1.5.0-rc.1", "^1.0.0"));
patch: 1.4.3
minor: 1.5.0
major: 2.0.0
premajor rc: 2.0.0-rc.0
次のrc: 2.0.0-rc.1
v1.2.3 は有効?: 1.2.3
1.2 は有効?: null / coerce: 1.2.0
^0.3.1 は 0.4.0 を許す?: false / 0.3.9: true
^1.0.0 は 1.5.0-rc.1 を許す?: false
ここからわかることは次の3つです。
^0.3.1は 0.4.0 を含みません。 0.x では MINOR が上がると互換性がないものとして扱われるため、0.x のうちは「互換性を壊したら MINOR、それ以外は PATCH」と運用すると利用者側の範囲指定と合います。^1.0.0のような範囲指定は、プレリリースを自動では選びません。 rc 版を公開しても、通常の利用者が誤って取り込むことはありません。- npm の semver は
vを取り除いて解釈します(v1.2.3→1.2.3)。一方、1.2のように3つ揃っていない番号は無効です。
プレリリースの大小比較を実際に並べ替える
プレリリースは同じ番号の正式版より小さく、識別子は「数字は数値として」「文字は ASCII 順で」比較されます。 仕様の例を含む12個を並べ替えました。
// sort.mjs
import semver from "semver";
const vs = ["1.0.0", "1.0.0-rc.1", "1.0.0-alpha", "1.0.0-beta.11", "1.0.0-alpha.1",
"1.0.0-beta", "1.0.0-alpha.beta", "1.0.0-beta.2", "1.0.0+build.5", "0.9.12", "1.0.0-rc.10", "1.0.0-rc.2"];
console.log(semver.sort([...vs]).join("\n"));
console.log("compare(1.0.0, 1.0.0+build.5):", semver.compare("1.0.0", "1.0.0+build.5"));
console.log("compareBuild(1.0.0, 1.0.0+build.5):", semver.compareBuild("1.0.0", "1.0.0+build.5"));
0.9.12
1.0.0-alpha
1.0.0-alpha.1
1.0.0-alpha.beta
1.0.0-beta
1.0.0-beta.2
1.0.0-beta.11
1.0.0-rc.1
1.0.0-rc.2
1.0.0-rc.10
1.0.0
1.0.0+build.5
compare(1.0.0, 1.0.0+build.5): 0
compareBuild(1.0.0, 1.0.0+build.5): -1
| 比較 | 結果 | 理由 |
|---|---|---|
1.0.0-rc.1 と 1.0.0 |
プレリリースが小さい | プレリリースは正式版より前 |
1.0.0-alpha と 1.0.0-alpha.1 |
識別子が少ないほうが小さい | 前が同じなら、識別子が多いほうが大きい |
1.0.0-rc.2 と 1.0.0-rc.10 |
rc.2 が小さい | 数字だけの識別子は数値で比較(2 < 10) |
1.0.0-beta と 1.0.0-rc.1 |
beta が小さい | 文字の識別子は ASCII 順(b < r) |
1.0.0 と 1.0.0+build.5 |
等しい(compare は 0) | ビルドメタデータは優先順位に影響しない |
semver.sort は内部で compareBuild を使うため、ビルドメタデータ付きが後ろに並びます。 仕様上の優先順位は compare の 0(等しい)なので、ビルドメタデータでリリースを区別しない運用にしておくと混乱しません。
rc1 ではなく rc.1 と書くのがおすすめです。 rc.1 なら1が数値として比較されますが、rc1 は文字列として比較されるため rc10 が rc2 より前に並びます。
Python のパッケージは SemVer ではなく PEP 440 という別の規則に従います。 packaging 26.1 で試すと、1.0.0-rc.1 は 1.0.0rc1 に正規化され、1.0.0-alpha.beta は InvalidVersion になりました。Python では 1.0.0rc1 の形に合わせると対応がわかりやすくなります。
git tag のコマンドを実行結果つきで確認する
ここからは、4つのコミットを持つリポジトリと bare リモート(remote.git)を用意し、Git 2.43.0 で実行した結果です。日時は再現のため固定しています。
git init --bare remote.git
git init -b main demo && cd demo
git remote add origin ../remote.git
# 4回コミットした状態
git log --oneline
708a2e4 feat: change 4
c5aba36 feat: change 3
c718451 feat: change 2
d8bd556 feat: change 1
軽量タグと注釈付きタグの違い
リリースには、作成者・日時・メッセージを持つ注釈付きタグ(-a)を使います。 軽量タグはコミットを指す名前だけです。
git tag v0.1.0-light
git tag -a v1.0.0 -m "Release v1.0.0"
git cat-file -t v0.1.0-light
git cat-file -t v1.0.0
git show v1.0.0 --no-patch
commit
tag
tag v1.0.0
Tagger: Dev <dev@example.com>
Date: Thu Sep 10 10:00:00 2026 +0900
Release v1.0.0
commit 708a2e4b9c4a2ab994527d996486e90afdf805bb
Author: Dev <dev@example.com>
Date: Fri Sep 4 10:00:00 2026 +0900
feat: change 4
確認に使った軽量タグ v0.1.0-light は、この後 git tag -d v0.1.0-light で削除しています。
| 比較 | 軽量タグ | 注釈付きタグ |
|---|---|---|
| 作り方 | git tag 名前 |
git tag -a 名前 -m "..." |
| Git の中身 | コミットを直接指す(cat-file -t は commit) |
タグオブジェクトを作る(cat-file -t は tag) |
| 作成者・日時・メッセージ | なし | あり |
| 署名 | できない | -s で可能 |
git describe の対象 |
--tags を付けたときだけ |
既定で対象 |
--follow-tags で push |
されない | される |
| 向いている用途 | 一時的な目印 | リリース |
過去のコミットに付ける・一覧・絞り込み
git tag -a v0.9.0 d8bd556 -m "Release v0.9.0"
git tag -a v1.0.0-rc.1 c718451 -m "Release candidate 1"
git tag -a v1.0.0-rc.2 c5aba36 -m "Release candidate 2"
git tag -a v1.0.0-rc.10 c5aba36 -m "Release candidate 10"
git tag
v0.9.0
v1.0.0
v1.0.0-rc.1
v1.0.0-rc.10
v1.0.0-rc.2
git tag の既定は文字列順なので、rc.10 が rc.2 より前に並びます。 バージョン順にするには --sort=v:refname を使います。
git tag --sort=v:refname
v0.9.0
v1.0.0
v1.0.0-rc.1
v1.0.0-rc.2
v1.0.0-rc.10
数字は正しく並びましたが、今度は正式版の v1.0.0 がプレリリースより前に来ています。versionsort.suffix で「- 以降はプレリリース」と教えると、SemVer と同じ順になります。
git -c versionsort.suffix=- tag --sort=v:refname
v0.9.0
v1.0.0-rc.1
v1.0.0-rc.2
v1.0.0-rc.10
v1.0.0
毎回指定する代わりに git config --global versionsort.suffix - と git config --global tag.sort v:refname を設定しておくと、git tag だけでこの順になります。
git tag -l "v1.0.0-rc*"
git tag -n1
git tag --contains c5aba36
git tag --points-at HEAD
v1.0.0-rc.1
v1.0.0-rc.10
v1.0.0-rc.2
v0.9.0 Release v0.9.0
v1.0.0 Release v1.0.0
v1.0.0-rc.1 Release candidate 1
v1.0.0-rc.10 Release candidate 10
v1.0.0-rc.2 Release candidate 2
v1.0.0
v1.0.0-rc.10
v1.0.0-rc.2
v1.0.0
--contains は「その修正がどのリリースに入っているか」を調べるときに便利です。
push する
git push だけではタグは送られません。 タグ名を指定するか、--tags / --follow-tags を使います。
git push origin v1.0.0
git ls-remote --tags origin
To ../remote.git
* [new tag] v1.0.0 -> v1.0.0
49de5d45ddc42342e02398ff48020ba878d38f67 refs/tags/v1.0.0
708a2e4b9c4a2ab994527d996486e90afdf805bb refs/tags/v1.0.0^{}
^{} の行は、注釈付きタグが最終的に指しているコミットです(708a2e4 と一致しています)。
git push origin --tags
To ../remote.git
* [new tag] v0.9.0 -> v0.9.0
* [new tag] v1.0.0-rc.1 -> v1.0.0-rc.1
* [new tag] v1.0.0-rc.10 -> v1.0.0-rc.10
* [new tag] v1.0.0-rc.2 -> v1.0.0-rc.2
| push の方法 | 送られるタグ | 向いている場面 |
|---|---|---|
git push origin v1.0.0 |
指定した1つ | リリースのたびに1つずつ(いちばん確実) |
git push origin --tags |
ローカルのすべて(軽量タグも) | 初回にまとめて送るとき |
git push --follow-tags origin main |
push するコミットから辿れる注釈付きタグだけ | ブランチとリリースタグを一緒に送るとき |
git config --global push.followTags true を設定すると、git push だけで注釈付きタグも送られます。
git describe で「今どこか」を表す
git describe は、直近の注釈付きタグからの距離でコミットを表します。 アプリのバージョン表示やビルド名に使えます。v1.0.0 の後に1コミット追加した状態です。
git describe
git describe --tags --abbrev=0
v1.0.0-1-g1772455
v1.0.0
v1.0.0-1-g1772455 は「v1.0.0 から1コミット進んだ、コミット 1772455」という意味です(g は git の印)。作業ツリーに未コミットの変更があるときは、git describe --dirty で末尾に -dirty が付きます(例:v1.0.1-dirty)。
付け替える(付け間違えたとき)
git tag -a v1.0.1 HEAD~1 -m "Release v1.0.1" # 1つ前のコミットに付けてしまった
git tag -a v1.0.1 -m "Release v1.0.1" # もう一度付けようとすると
fatal: tag 'v1.0.1' already exists
同じ名前で付け直すには -f を付けます。 push 前なら、これで終わりです。
git tag -fa v1.0.1 -m "Release v1.0.1"
git push origin v1.0.1
Updated tag 'v1.0.1' (was fdf9c61)
To ../remote.git
* [new tag] v1.0.1 -> v1.0.1
push 済みのタグを付け替えると、通常の push は拒否されます。
git tag -fa v1.0.1 HEAD~1 -m "Release v1.0.1 (retag test)"
git push origin v1.0.1
Updated tag 'v1.0.1' (was 0343834)
To ../remote.git
! [rejected] v1.0.1 -> v1.0.1 (already exists)
error: failed to push some refs to '../remote.git'
hint: Updates were rejected because the tag already exists in the remote.
git push --force origin v1.0.1
To ../remote.git
+ 0343834...e6cb9ba v1.0.1 -> v1.0.1 (forced update)
ただし、既に取得した人の手元のタグは自動では変わりません。 別のクローンで git fetch --tags すると、次のように拒否されます(パスは一部省略)。
$ git fetch --tags
From .../remote
! [rejected] v1.0.1 -> v1.0.1 (would clobber existing tag)
$ git fetch --tags --force
From .../remote
t [tag update] v1.0.1 -> v1.0.1
SemVer の「一度出したバージョンは変えない」という規則とも合わせて、公開済みのタグは付け替えず、v1.0.2 として出し直すのが安全です。 付け替えるのは、push 直後で誰も取得していないと確認できるときに限ると混乱が起きません。
削除する(ローカル・リモート)
git tag -d v1.0.0-rc.10
git push origin --delete v1.0.0-rc.10
git push origin :refs/tags/v1.0.0-rc.2 # 古い書き方(同じ意味)
git ls-remote --tags origin "v1.0.0-rc*"
Deleted tag 'v1.0.0-rc.10' (was cad090c)
To ../remote.git
- [deleted] v1.0.0-rc.10
To ../remote.git
- [deleted] v1.0.0-rc.2
0bea70e2096766c7cff0b8ff4b927ff44f290c9c refs/tags/v1.0.0-rc.1
c718451863127751d71886e8ca473df63857e7c8 refs/tags/v1.0.0-rc.1^{}
リモートから消しても、ローカルの v1.0.0-rc.2 は残っています。 この状態で --follow-tags 付きで push すると、消したはずのタグが再び送られました。
git push --follow-tags origin main
To ../remote.git
* [new branch] main -> main
* [new tag] v1.0.0-rc.2 -> v1.0.0-rc.2
* [new tag] v1.1.0 -> v1.1.0
リモートのタグを消すときは、ローカルの git tag -d とリモートの git push origin --delete を両方行い、他の人には git fetch --prune --prune-tags で手元から消してもらいます。
$ git fetch --prune --prune-tags
From .../remote
- [deleted] (none) -> v1.0.0-rc.1
署名付きタグ
署名付きタグを使うと、そのタグを誰が作ったかを検証できます。 GPG のほか、Git 2.34 以降は SSH 鍵でも署名できます。
ssh-keygen -t ed25519 -C dev@example.com -f ~/.ssh/signkey
git config gpg.format ssh
git config user.signingkey ~/.ssh/signkey.pub
git tag -s v1.1.1 -m "Release v1.1.1"
# 検証用:信頼する公開鍵の一覧
echo "dev@example.com $(cat ~/.ssh/signkey.pub)" > ~/.ssh/allowed_signers
git config gpg.ssh.allowedSignersFile ~/.ssh/allowed_signers
git tag -v v1.1.1
Good "git" signature for dev@example.com with ED25519 key SHA256:KVBclrLqYAeguThxwJOmG/vrpCO2WCrnAq2bs3MtGGI
object 00348272b057999b4896d213cd8e923d4d10115b
type commit
tag v1.1.1
tagger Dev <dev@example.com> 1789002000 +0900
Release v1.1.1
GitHub にも同じ公開鍵を「Signing Key」として登録すると、タグに「Verified」と表示されます。
タグの命名規則:v を付けるか、モノレポではどうするか
v を付けるか
Git のタグは v1.2.3 のように v を付けるのが広く使われている形です。 SemVer の公式 FAQ でも、v1.2.3 はセマンティックバージョンそのものではないものの、バージョン番号であることを示す一般的な方法と説明されています。
| 方針 | 例 | 向いている場面 |
|---|---|---|
v を付ける |
v1.2.3 |
多くの場合。Go モジュールは v 付きが必須 |
v を付けない |
1.2.3 |
既存のタグが v なしで揃っている場合 |
大切なのは、どちらかに統一することです。 混在するとソートや CI のタグ条件(v*)から漏れます。パッケージの package.json などに書く番号は v なし(1.2.3)、Git のタグは v 付き、と分けるのが一般的です。
モノレポのタグ命名
1つのリポジトリで複数のパッケージを別々にリリースする場合は、タグにパッケージ名を入れて区別します。
| 形式 | 例 | 使われている場面 |
|---|---|---|
パス/vX.Y.Z |
api/v2.3.0 |
Go のサブディレクトリにあるモジュール(この形式が必須) |
パッケージ名@X.Y.Z |
@example/ui@1.4.0、web@1.4.0 |
npm のモノレポ管理ツール(changesets など)の既定 |
パッケージ名-vX.Y.Z |
web-v1.4.0 |
release-please などで設定できる形式 |
/ 区切りにすると、git describe --match でパッケージごとのバージョンを取り出せます。
git tag -a api/v2.3.0 -m "api v2.3.0"
git tag -a web/v1.4.0 -m "web v1.4.0"
# api に修正を1コミット追加
git tag -l "api/v*" --sort=-v:refname
git describe --tags --match "api/v*"
git describe --tags --match "web/v*" --abbrev=0
api/v2.3.0
api/v2.3.0-1-ge15cc51
web/v1.4.0
タグ push で GitHub Release を作る(GitHub Actions)
タグの push をきっかけにリリースを自動作成すると、「タグを付ける=リリースする」という一つの操作にまとまります。 次のワークフローは、タグが SemVer 形式かを確認し、- を含むタグはプレリリースとして登録します。GitHub CLI(gh)はランナーに入っているため、外部の Action を追加せずに書けます。
# .github/workflows/release.yml
name: Release
on:
push:
tags:
- "v[0-9]+.[0-9]+.[0-9]+"
- "v[0-9]+.[0-9]+.[0-9]+-*"
permissions:
contents: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Check tag format (SemVer)
env:
TAG: ${{ github.ref_name }}
run: |
if ! echo "$TAG" | grep -Eq '^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z.-]+)?$'; then
echo "::error::タグ $TAG は vMAJOR.MINOR.PATCH 形式ではありません"
exit 1
fi
- name: Create GitHub Release
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ github.ref_name }}
run: |
if [[ "$TAG" == *-* ]]; then
gh release create "$TAG" --verify-tag --generate-notes --prerelease
else
gh release create "$TAG" --verify-tag --generate-notes
fi
このワークフローは actionlint 1.7.12 で構文を検証し、指摘ゼロを確認しました。GitHub 上での実行は行っていません。 タグ形式を確認する正規表現は、手元で次のように判定されることを確認しています。
v1.2.3 OK
v1.0.0-rc.1 OK
v01.2.3 NG
1.2.3 NG
v1.2 NG
参考に、よくある書き間違いを actionlint にかけた結果です(tags を tag、ubuntu-latest を ubuntu-lates と書いた例)。
.github/workflows/bad.yml:4:5: unexpected key "tag" for "push" section. expected one of "branches", "branches-ignore", "paths", "paths-ignore", "tags", "tags-ignore", "types", "workflows" [syntax-check]
.github/workflows/bad.yml:8:14: label "ubuntu-lates" is unknown. available labels are "windows-latest", ...(以下省略) [runner-label]
tag: と書くと YAML としては正しいため、GitHub 上ではワークフローが動かないまま気づきにくくなります。 actionlint を pre-commit や CI に入れておくと、push 前に見つけられます。
CHANGELOG・Conventional Commits との関係
コミットメッセージを Conventional Commits の形式で書いておくと、次に上げるべき番号をコミット履歴から決められます。 書き方の詳細は別記事「コミットメッセージの書き方」にまとめているので、ここでは SemVer との対応だけを示します。
| コミットの種類 | 例 | SemVer |
|---|---|---|
fix: |
fix: 在庫数が負になる不具合を修正 |
PATCH |
feat: |
feat: タスクの一括削除を追加 |
MINOR |
! 付き、または本文に BREAKING CHANGE: |
feat!: 注文 API のレスポンス形式を変更 |
MAJOR |
docs: test: chore: など |
docs: README を更新 |
通常は番号を上げない |
flowchart LR
A[Conventional Commits で記録] --> B[次の番号を決める]
B --> C[CHANGELOG.md を更新]
C --> D[git tag -a vX.Y.Z]
D --> E[git push origin vX.Y.Z]
E --> F[GitHub Actions で Release 作成]
CHANGELOG.md は、バージョンごとに「Added / Changed / Fixed / Removed」などで分けて書く形式(Keep a Changelog)がよく使われます。release-please、semantic-release、changesets などのツールを使うと、番号の決定・CHANGELOG の更新・タグ付けを自動化できます。 まず手作業で流れを理解してから自動化すると、ツールの設定も迷わず決められます。
よくあるトラブルと直し方
| 症状 | 原因 | 直し方 |
|---|---|---|
| タグを付けるコミットを間違えた(push 前) | 付ける位置の指定漏れ | git tag -fa v1.2.0 <正しいコミット> -m "..." |
| タグを付けるコミットを間違えた(push 後) | 公開済み | 新しいバージョン(v1.2.1)を出す。付け替えるなら git push --force origin v1.2.0 と周知 |
git push したのにリモートにタグがない |
git push はタグを送らない |
git push origin v1.2.0、または push.followTags を設定 |
--follow-tags で一部のタグが送られない |
軽量タグは対象外 | -a で注釈付きタグにする |
| GitHub Actions がタグ push で動かない | on.push.tags のパターンが合っていない、tag: の書き間違い |
パターンを見直し、actionlint で確認 |
fatal: tag 'v1.0.1' already exists |
同名のタグがある | 付け替えなら -f。別のバージョンなら番号を変える |
! [rejected] ... (already exists) |
リモートに同名のタグがある | 本当に付け替えるなら --force |
(would clobber existing tag) で fetch が拒否される |
リモートでタグが付け替えられた | git fetch --tags --force |
| 消したタグがリモートに戻ってくる | ローカルに残ったタグが再 push された | git tag -d とリモート削除の両方を行う |
warning: refname 'v1.1.0' is ambiguous. |
同名のブランチとタグがある | 下記を参照 |
同名ブランチとの衝突
タグと同じ名前のブランチがあると、Git はどちらを指すか曖昧だと警告します。 v1.1.0 というタグとブランチの両方がある状態で試しました。
$ git checkout v1.1.0
warning: refname 'v1.1.0' is ambiguous.
Switched to branch 'v1.1.0'
$ git push origin v1.1.0
error: src refspec v1.1.0 matches more than one
error: failed to push some refs to '../remote.git'
git checkout ではブランチが選ばれ、git push は失敗します。refs/tags/v1.1.0 のように完全な名前で指定すると曖昧さがなくなります。
git push origin refs/tags/v1.1.0
git checkout refs/tags/v1.1.0
根本的には、ブランチ名を release/1.1 のように、タグ名(v1.1.0)と重ならない形にすると衝突しません。
よくある質問
Q. 最初のバージョンは 0.1.0 と 1.0.0 のどちらから始めればよいですか? A. SemVer の FAQ では、初期開発は 0.1.0 から始めることが紹介されています。利用者がすでに本番で使っている、または API が定まったと判断できた時点で 1.0.0 にします。
Q. 軽量タグと注釈付きタグのどちらを使えばよいですか?
A. リリースには注釈付きタグ(git tag -a)を使います。作成者・日時・メッセージが残り、git describe や --follow-tags の対象にもなります。軽量タグは手元での一時的な目印に向いています。
Q. アプリケーション(ライブラリではないもの)にも SemVer は使えますか?
A. 使えます。その場合は「公開 API」を、利用者から見える画面や外部向け API、設定ファイルの形式などと読み替えます。互換性の考え方がなじみにくい場合は、日付ベースのバージョン(例:2026.10.1)を選ぶ方法もあります。
まとめ
- SemVer は MAJOR.MINOR.PATCH。互換性を壊したら MAJOR、機能追加は MINOR、バグ修正は PATCH
- 0.x のうちは互換性を壊したら MINOR を上げると、
^0.3.1のような範囲指定と合う - プレリリースは
-rc.1のようにドットで区切ると数値で比較され、rc.10も正しく並ぶ - リリースには注釈付きタグ(
git tag -a)を使い、git push origin vX.Y.Zで個別に送る git -c versionsort.suffix=- tag --sort=v:refnameで SemVer と同じ順に並べられる- 公開済みのタグは付け替えず次の番号で出し直す。削除はローカルとリモートの両方で行う
- タグ push から GitHub Actions でリリースを作ると、タグ付けとリリースが一つの手順にまとまる
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「git tagとセマンティックバージョニング|命名規則・コマンド早見表」
株式会社RJCでは、バージョン管理とリリースの手順を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。