Gitで最新のタグの1つ前のタグを取得する方法

2024/09/10

author

masyus

業務で

  • 最新のタグと1つ前のタグ間でソースコードの差分を確認したい
  • 特定ディレクトリ配下のファイルに差分がある時だけデプロイしたい

という要請に応えるべく、1つ前のタグを取得するコマンドがGitHub Actions(以下GHAと称します)上にて書かれているのを見つけて勉強になりました。今回はそのコマンドについて解説します。

バージョン情報

  • Git: 2.30.1 (Apple Git-130)

前提条件: セマンティックバージョニングを尊重してtagを付与してきたこと

セマンティックバージョニングはおそらくエンジニア界隈に広く浸透しているかと思いますが、X.Y.Zのフォーマットで

  • メジャーバージョン: X
  • マイナーバージョン: Y
  • パッチバージョン: Z

を管理するスタイルです。セマンティックバージョニングを採用してGitでタグ付けする場合、接頭辞にvをつけたv1.0.0のようにすることが多く、ほとんどの開発現場ではこのスタイルでタグ付けされているかと思われます。1つ注意があり、

v1.0.0は厳密にはセマンティックバージョンと呼ばず、あくまでタグ名である

という点は気をつけましょう。

ローカルリポジトリから1つ前のタグを取得する方法

下記で取得できます。

git tag --sort=v:refname | tail -n 2 | head -n 1
  • v:refnameversion:refnameと同値で、タグをバージョン番号として解釈するようソート時に指定します
  • tail -n 2head -n 1はそれぞれ、後方2行および前方1行だけを取得します

ここで1点注意したいのが、

git tag

だけですと辞書順(アルファベット順) でソートし、バージョン番号であると見なしてソートしてくれないことです。たとえばv0.0.0からv0.0.10までのタグが付与されおり、v0.0.10が最新のタグだった場合、セマンティックバージョニングに沿うならばv0.0.10git tagの実行結果の一番最後に来てほしいところですがソートオプションを何も指定しない場合、

v0.0.0
v0.0.1
v0.0.10
v0.0.2
v0.0.3
v0.0.4
v0.0.5
v0.0.6
v0.0.7
v0.0.8
v0.0.9

のような結果になってしまいます。

git tag --sort=v:refname

にすると

v0.0.0
v0.0.1
v0.0.2
v0.0.3
v0.0.4
v0.0.5
v0.0.6
v0.0.7
v0.0.8
v0.0.9
v0.0.10

のように、期待通りのソート結果となります。v:refnameの指定は他のGitコマンドでも使うことが多いため、覚えておくと何かと重宝します。

リモートリポジトリから1つ前のタグを取得する方法

下記で取得できます。

git ls-remote --tags --sort=v:refname origin 'v*.*.*' | tail -n 2 | head -n 1 | cut -f 2 | cut -d '/' -f 3
  • v*.*.*は、タグ名がこのフォーマットに沿ったものだけをフィルターしてくれる便利オプションです
git ls-remote --tags --sort=v:refname origin 'v*.*.*'

の実行結果は、各タグのコミットハッシュとタグ名(refs/tags/タグ名の形式)の2つを出力します。

c472d3f1ae20db91f24190b84bad1c43e069d72b    refs/tags/v0.0.0
b8bf98df470d0ea4a982176711798e3b833ee628    refs/tags/v0.0.1
d6b2fbfa22e24eb9390bbea9ef8d9ac8965b5401    refs/tags/v0.0.2
90aaa9c4d34e855f79133e6671fde3bfb0554d6d    refs/tags/v0.0.3

...

1947ac8c43e10984ffe9d3002aa824914b5cc943    refs/tags/v0.0.9
9a3320acc941217734de78ea9211cdd4521a348b    refs/tags/v0.0.10

この中からタグ名だけを取得する場合、tail + headで1つ前のタグを取得するのに加えてcutによる抽出が必要となります。

ローカルリポジトリとリモートリポジトリ、どちらからタグを取得するのが良い?

コマンドの性能差だけで言いますと、リモートリポジトリからタグを取得するほうが良いと思います。v*.*.*のフィルター機能が便利で、誤ってセマンティックバージョニングに沿わなかったタグを除外することができますので。他に決め手があるとしたら、

タグ情報をfetchする速度が、どちらのコマンドでどれくらい時間かかるかに寄る

と思われます。たとえばGHAのContinuous deployment(以下CDと称します)環境上でGitからソースコードをチェックアウトしつつタグ情報を取得する場合、別途タグ情報をfetchしないとタグを参照できません。方法はいくつかありますが、

        uses: actions/checkout@v4
        with:
          fetch-depth: 0

のように、チェックアウト時にfetch-depth: 0を指定する方法があります。もしくはチェックアウト後にgit fetchするかです。CD環境上でタグ取得する場合、時間がかかる方法ですとデプロイにも時間をかけることになり望ましくありません。Gitリポジトリの状況を鑑みながら最適な選択をする必要があります。

参考

Gitの関連記事

まだ記事がありません