
Squash mergeでgitタグが孤立する — git describeで検出できなくなる原因と対策
GitHub の squash merge を使っていたら git describe --tags --abbrev=0 で最新タグが見つからなくなりました。原因はフィーチャーブランチで打ったタグが squash merge で孤立すること。再現手順、診断方法、対策をまとめます。
前提・環境
- Git 2.49
- GitHub(PR の squash merge を使用)
- リリース自動化スクリプトで
git describe --tagsによるバージョン検出を使用
何が起きたか
リリース自動化スクリプトでバージョンを取得するために以下のコマンドを使っていました。
CURRENT=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
v0.6.3 をリリースしたはずなのに、このコマンドが v0.6.0 を返す。v0.6.1、v0.6.2、v0.6.3 のタグが見つからない状態でした。
原因: squash merge によるタグの孤立
リリースフローの問題
当時のリリースフローは以下の順序でした。
- フィーチャーブランチで実装
- バージョンバンプコミット
- フィーチャーブランチ上でタグを作成 (
git tag v0.6.3) - ブランチとタグをpush
- PR作成 → squash merge
squash merge が何をするか
squash merge は、フィーチャーブランチの全コミットを1つにまとめた新しいコミットを main 上に作成します。

重要なのは、squash merge コミット S は C とは異なるSHAだということです。タグ v0.6.3 はコミット C を指していますが、C は main の祖先(ancestor)には存在しません。
git describe の動作
git describe --tags --abbrev=0 は、現在のコミットの祖先をたどって最初に見つかるタグを返します。main 上で実行すると X → Y → Z → S の祖先しかたどらないため、コミット C に付いたタグは見つかりません。
# main 上で実行
$ git describe --tags --abbrev=0
v0.6.0 # v0.6.1, v0.6.2, v0.6.3 は見つからない
これがタグの「孤立」です。タグ自体は存在していますが、main のコミット履歴からは到達できません。
診断方法
タグが孤立しているか確認する
# タグが指すコミットが main の祖先にあるか確認
git merge-base --is-ancestor $(git rev-parse v0.6.3) main
echo $? # 0 = 祖先にある, 1 = 孤立している
全タグの孤立状況を一覧表示
for tag in $(git tag --sort=-v:refname); do
if git merge-base --is-ancestor "$(git rev-parse "$tag")" main 2>/dev/null; then
echo "OK $tag"
else
echo "ORPHAN $tag"
fi
done
実行結果:
ORPHAN v0.6.3
ORPHAN v0.6.2
ORPHAN v0.6.1
OK v0.6.0
OK v0.5.0
タグとコミットの関係を確認
# タグが指すコミットのSHA
git rev-parse v0.6.3
# main の最新コミットのSHA(squash merge コミット)
git rev-parse main
# 異なるSHAが表示される → タグが main 上にない
対策: タグは squash merge 後に main 上で打つ
修正後のフロー
- フィーチャーブランチで実装
- バージョンバンプコミット(タグはまだ打たない)
- ブランチをpush、PR作成
- squash merge
- main をチェックアウトしてpull
- squash merge コミット上でタグを作成
- タグをpush
# PR を squash merge した後
git checkout main
git pull origin main
# squash merge コミット上でタグを打つ
git tag v0.6.3
git push origin --tags
こうすれば、タグは確実に main の履歴上のコミットを指します。

既存の孤立タグを修復する
既に孤立してしまったタグは、git tag -f で main 上の正しいコミットに付け替えられます。
まず、各タグに対応する squash merge コミットを特定します。
# squash merge コミットのメッセージにはPR番号が含まれるので、それで特定
git log --oneline main | head -20
対応するコミットが見つかったら付け替えます。
# ローカルでタグを付け替え
git tag -f v0.6.3 <squash-merge-commit-sha>
git tag -f v0.6.2 <squash-merge-commit-sha>
git tag -f v0.6.1 <squash-merge-commit-sha>
# リモートに強制push
git push origin --tags --force
修復後の確認:
$ git describe --tags --abbrev=0
v0.6.3 # 正しく検出される
$ for tag in v0.6.{1,2,3}; do
git merge-base --is-ancestor "$(git rev-parse "$tag")" main && echo "OK $tag" || echo "ORPHAN $tag"
done
OK v0.6.1
OK v0.6.2
OK v0.6.3
通常の merge では起きない
通常の merge(non-squash)ではフィーチャーブランチのコミットが main の祖先に含まれるため、この問題は発生しません。

merge コミット M から C へたどれるので、git describe でタグが見つかります。
rebase merge も同様に問題が起きます。rebase はコミットを cherry-pick するため、新しいSHAが生成されます。
| merge 方式 | タグが孤立するか |
|---|---|
| 通常 merge | しない |
| squash merge | する |
| rebase merge | する |
まとめ
- squash merge はフィーチャーブランチのコミットとは異なるSHAの新コミットを main 上に作る
- フィーチャーブランチで打ったタグは main の祖先から到達できず、
git describeで検出できなくなる - 対策: タグは squash merge 後に main 上で打つ
git merge-base --is-ancestorでタグの孤立を診断できる- 孤立済みのタグは
git tag -fで main 上のコミットに付け替え可能
リリース自動化で git describe --tags を使っている場合、squash merge との組み合わせには注意が必要です。



