git の tag コマンドの仕様を完全に理解する

# はじめに

git の tag コマンドについて勉強する。
確認に使った git のバージョンは 2.14.2 である。

テストに使ったリポジトリ: https://github.com/firedial/git_practice

# git tag コマンドについて

git tag には下記の 4 つの使い方がある。

* タグの追加
* タグの削除
* タグの正当性の確認
* タグの確認

## タグの追加

### 基本的な使い方

```
$ git tag hoge <commit>
```

とすることで \<commit> に hoge というタグをつけることができる。
\<commit> は省略可能で、省略すると HEAD にタグがつく。

また、 \<commit> ではなく \<object> を使うこともできる。そのためか、すでについているタグで指定することができる(\<object> について調べていると奥が深そうだったので深追いはあきらめた...)。
上記で hoge をつけた後に、下記コマンドを実行すると同じところに hogehoge というタグができる。

```
$ git tag hogehoge hoge
```

同じ名前のタグはつけれないので、上記コマンド実行後に、もう一度実行すると怒られる。

```
$ git tag hoge
fatal: tag 'hoge' already exists
```

強制的に別のコミットにつけるには、

```
$ git tag -f hoge
Updated tag 'hoge' (was bf09944)
```

と、 -f (--force) オプションで強制的に付け替えることができる。
元々ついていたコミット(例でいうと bf09944)についていた hoge のタグは消えている。

### 注釈をつける

タグには -a (--annotate) オプションで注釈をつけることができる。

```
$ git tag -a -m "huga ってなんだろう" huga
```

-m オプションは git commit するときの -m と同じものである。
省略すると外部エディタが開いてそこで記入できる。

-m オプションの代わりに -F オプションを使うことができる。
それは、注釈をファイルから読み込むという使い方である。

```
$ git tag -a -F tag.txt annotate_from_file
```

その場合 -a がなくてもよい。

```
git tag -F tag.txt annotate_from_file_without_a
```

### GPG 署名をつける

コミットする際、任意のユーザー名とメールアドレスでコミットできてしまう。タグはリリース時に用いられることが多く、偽装されると困ることがある。正当性を保証するために署名がつけることができる(と認識している)。

これに関しては、公開鍵の設定とかしていないので端折る。
完全理解？しらねぇ。

一応、軽く説明すると、

```
$ git tag -s hoge
```

```
$ git tag -u <keyid> hoge
```

で設定できるらしい。

### タグの追加のまとめ

man git-tag で出てくるコマンドの書式。

```
$ git tag [-a | -s | -u <keyid>] [-f] [-m <msg> | -F <file>] <tagname> [<commit> | <object>]
```

上記説明を完全に理解していたら解説するまでもなくわかるだろう。

## タグの削除

削除するときは -d オプションで削除したいタグ名を指定する。

```
$ git tag -d hoge
Deleted tag 'hoge' (was 1e98cbf)
```

複数指定してあげることも可能。

```
$ git tag -d past past_past
Deleted tag 'past' (was bf09944)
Deleted tag 'past_past' (was bf09944)
```

man git-tag の記述は下記の通り。

```
git tag -d <tagname>...
```

## タグの正当性の確認

さっき端折った GPG の話の続きである。
なのでここも端折る。
やっていることは、署名が正しいかの確認をしているのだろう。

かんぜんりかい、なにそれおいしいの？

man git-tag 記述は下記の通り。

```
git tag -v [--format=<format>] <tagname>...
```

## タグの確認

基本的な使い方

```
$ git tag
annotate_from_file
annotate_from_file_without_a
ano_ano
hogehoge
huga
tag
```

-l オプションをつけてもいい。

```
$ git tag -l
annotate_from_file
annotate_from_file_without_a
ano_ano
hogehoge
huga
tag
```

カラム表示したいときは --column オプションをつける。

```
$ git tag -l --column
annotate_from_file            hogehoge
annotate_from_file_without_a  huga
ano_ano                       tag
```

むしろカラム表示したくない場合は --no-column オプションをつける。

```
$ git tag -l --no-column
annotate_from_file
annotate_from_file_without_a
ano_ano
hogehoge
huga
tag
```

ある特定のコミットを含むタグ一覧を表示したいときは --contains オプションで検索できる。

```
$ git tag -l --contains 61f95ae35ab0b6190a24845f07e270fa3c7818a7
annotate_from_file
annotate_from_file_without_a
ano_ano
hogehoge
huga
tag
```

逆にある特定のコミットを含まない場合の検索は --no-contains オプションでできる。

```
$ git tag -l --no-contains 4dfdd0f5ea52bac918c26f42e7796f6ce626ebd0
hogehoge
huga
tag
```

ある特定の \<object> に対してついているタグを取得するには --points-at オプションを使う。

```
$ git tag -l --points-at 1e98cbfa0f5763ed79d7ed4fad566e333885ae3d
hogehoge
huga
tag
```

-n オプションを使うことで注釈の1行目を出すことができる。

```
$ git tag -l -n
annotate_from_file ファイルからタグの注釈をつける
annotate_from_file_without_a ファイルからタグの注釈をつける
ano_ano         ファイルからタグの注釈をつける
hogehoge        焼肉
huga            huga ってなんだろう
tag             huga ってなんだろう
```

-n を使うと暗黙的に -l がつけられていることになるので省略してよい。

```
$ git tag -n
annotate_from_file ファイルからタグの注釈をつける
annotate_from_file_without_a ファイルからタグの注釈をつける
ano_ano         ファイルからタグの注釈をつける
hogehoge        焼肉
huga            huga ってなんだろう
tag             huga ってなんだろう
```

注釈を \<num> 行表示したいときは -n\<num> みたいに指定する。

```
$ git tag -n2
annotate_from_file ファイルからタグの注釈をつける
annotate_from_file_without_a ファイルからタグの注釈をつける
ano_ano         ファイルからタグの注釈をつける
    複数行
hogehoge        焼肉
huga            huga ってなんだろう
tag             huga ってなんだろう
```

\<pattern> で表示するタグを指定できる。下記は a から始まるタグ名を表示。

```
$ git tag -l a*
annotate_from_file
annotate_from_file_without_a
ano_ano
```

--sort オプションで表示する順番が変えられて、 --[no-]merged オプションでコミットがマージ済みかどうかの指定ができて、 --format は......なんだろうねっ。

完全理解はまだまだ遠い、と思った27の夜。

man git-tag 記述は下記の通り。

```
git tag [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]
               [--points-at <object>] [--column[=<options>] | --no-column]
               [--create-reflog] [--sort=<key>] [--format=<format>]
               [--[no-]merged [<commit>]] [<pattern>...]
```

# tag 名に使えない文字列

ついでに、紹介しておく。

## /. が入っていないこと

```
$ git tag /.a
fatal: '/.a' is not a valid tag name.
```

怒られる。

## / や . で終わっていないこと

```
$ git tag hoge/
fatal: 'hoge/' is not a valid tag name.
$ git tag hoge.
fatal: 'hoge.' is not a valid tag name.
```

怒られる。

## .. が入っていないこと

```
$ git tag hoge..hoge
fatal: 'hoge..hoge' is not a valid tag name.
```

怒られる。

## ASCII 制御文字 (0x00-1F,7F) が入っていないこと

水平タブ (^I) を入れた。

```
$ git tag hoge       hoge
fatal: Failed to resolve 'hoge' as a valid ref.
```

怒られた。

## 半角スペースを含む特別な記号 ( ~^:?*[\\) が入っていないこと

```
$ git tag hoge:hoge
fatal: 'hoge:hoge' is not a valid tag name.
```

怒られた。

## .lock という文字列で終わっていないこと

```
$ git tag hoge.lock
fatal: 'hoge.lock' is not a valid tag name.
```

怒られた。

## @{ が入っていないこと

```
$ git tag hoge@{aaa}
fatal: 'hoge@{aaa}' is not a valid tag name.
```

怒られた。

ちなみに @ 自体は問題ない。

```
$ git tag yakiniku@
$ git tag -l y*
yakiniku@
```

怒られたかったのに...。

嘘です。

# なぜ tag について解説したのか

何かしらちゃんと調べて記事を書きたいと思っていたところ、これだといい感じの記事の長さになるなぁと思って書いてみた。実際いい長さになったが、想定よりも理解するのに時間がかかって大変だった。

今回、調べている最中に object という概念を知ったので、今度はこれを勉強して完全理解(笑)できたら記事にしたいと思う。