git commit解説

以下は社内向け勉強会のLT枠で話した内容をベースにして編集増補したものである。増補分があるので、この内容を5分で話したわけではない。

## git commitコマンドの概要

git commitはインデックスにあるファイルの変更を、ローカルリポジトリに記録するコマンドである。つまり、リポジトリへ記録することをコミットと呼ぶわけである。

## git commitの対象範囲

`git commit [--] <ファイル名>`で指定したインデックス内にある最新コミットとは変更があるファイルを対象としてコミットする。

ファイル名は複数指定、ワイルドカード、「.」（そのディレクトリとサブディレクトリにあるすべてのファイル、の意味）も指定可能である。「.」を使って「dir/.」のような指定も可能であり、この場合dirとそのサブディレクトリのすべてのファイルが対象になる。

ファイル名の直前の「--」はこの後の引数をオプションとして使用しない、という意味であり、ファイルパスがマイナスで始まるものを指定する場合に有用であるが、特にそういうのがなければ省略しても良い。習慣として付ける人もいるし、バッチの中で不特定のファイルを処理する場合は付けるべきだろう。

[git add](https://qiita.com/yunano/items/bb2420006b9ba51dc866#ファイル名を一覧で指定)と同じように、「--pathspec-from-file <ファイル名>」でファイルの中に書かれたファイル名の一覧を対象にしたり、「--pathspec-from-file -」として対象にするファイル名の一覧を標準入力から渡したり、--pathspec-file-nulオプションも指定して渡すファイル名の区切り文字をNULにしたりできる。

またこちらも[git add](https://qiita.com/yunano/items/bb2420006b9ba51dc866#ファイルの一部だけ追加する)と同じように-p（あるいは--patch）オプションもあるのでインデックスのファイルの一部だけコミットすることもできる。git addにある、エディタを立ち上げてファイル内の対象範囲を指定する-eオプションはgit commitにおいてはコミットメッセージの編集に用いる別のオプションである。これは後述する。

ファイル名を省略するとすべてのインデックス内にある最新コミットとは変更があるファイルが対象となる。大抵はこのファイル名を省略した形で実行されていると思われる。

## コミットメッセージの指定

コミットを行う時には--allow-empty-messageオプションを指定した場合を除き、何らかのコミットメッセージを必要とする。特にオプションを指定しなかった場合、コミットメッセージはgit commitコマンドを実行した後にエディタが立ち上がり入力を行う。

-m（あるいは--message）オプションを使用した場合、オプションの後に指定した文字列をコミットメッセージとする。この場合エディタは立ち上がらない。コマンドラインシェルのお約束通りコミットメッセージに「 」を含ませる場合はクォートで囲む必要がある。改行も（少なくともbashでは）以下のようにすれば入力できる（「$」や「>」は自分では入力しない）。

```bash
$ git commit --amend -m 'a
> b'
```

-F（あるいは--file）オプションを使用した場合、オプションの後に指定したファイルの内容をコミットメッセージとする。この場合エディタは立ち上がらない。ファイル名の代わりに「-」を指定した場合は標準入力の内容をコミットメッセージとする。

-C（あるいは--reuse-message）オプションを使用した場合、オプションの後に指定した[commit-ish](https://qiita.com/yunano/items/f3133ea64efed762df2f#commit-ish)と同じコミットメッセージにする。この場合エディタは立ち上がらないが、-Cの代わりに-c（あるいは--reedit-message）にすれば更なる編集のためにエディタが立ち上がる。なお、-Cあるいは-cオプションにおいて、コミットメッセージだけでなくAuthorDateと呼ばれるコミット時刻の1つもコピーされる。AuthorDateについては次の[コミット情報](#コミット情報)の項で説明する。

これらのオプションとともに-e（あるいは--edit）オプションも指定すると強制的に更なる編集のためにエディタが立ち上がるようにすることができる。逆に--no-editオプションも指定すると強制的にエディタは立ち上がらないようにすることができる。

### コミット情報

`git log --pretty=fuller`や`git show --pretty=fuller`を実行すると、コミットがAuthorとCommitという2つの作成者およびAuthorDateとCommitDateという2つの日時を持っていることがわかる。

これらの2つの違いは何だろうか。git commitの--amendオプションは新規のコミットを作る代わりに、最新コミットとマージして置き換える。この場合に最初にコミットした時をAuthorやAuthorDateとして、--amendで作られたコミットの時の記録をCommitやCommitDateとして残しているのである。

git commitにおいて--authorオプションあるいはなければ環境変数GIT_AUTHOR_NAMEとGIT_AUTHOR_EMAILでAuthorを、--dateオプションあるいはなければ環境変数GIT_AUTHOR_DATEでAuthorDateを上書きできる。

CommitやCommitDateはオプションで上書きはできない。Commitは環境変数GIT_COMMITTER_NAMEとGIT_COMMITTER_EMAILとで、CommitDateは環境変数GIT_COMMITTER_DATEで上書きすることはできる。

各種日時で与える日時は"Unixタイムスタンプ タイムゾーン"（タイムゾーンは「+0900」のような形式とする）や「Thu, 07 Apr 2005 22:13:13 +0900」のようなRFC 2822形式、「2005-04-07T22:13:13+0900」のようなISO 8601形式を指定する。`man git-commit`には書かれていないが`date`の出力も理解する。

--authorオプションに与える値は"名前 <メールアドレス>"である。

`git log`（オプションなし）で表示されるのはAuthorやAuthorDateの方だが、GitHubのようなCommitDateをコミット時刻として扱うサービスもある。

### コミットメッセージ編集時の下部のコメント行の制御

コミットメッセージを編集する際に、特にオプションや設定がなければメッセージ入力部の下に`git status`の出力を「#」でコメントアウトしたものが出力される。これは--statusオプションで強制的に出力されるように、--no-statusオプションで強制的に出力されないようにすることができる。これらのオプションがない場合git configで設定可能なcommit.status設定の値(true/false)で制御できる。commit.status設定のデフォルト値はtrueなのでどれもなければ出力されるわけである。

出力が行われる場合、以下のようなオプションを使って出力内容の調整が可能である。

* -u（あるいは--untracked-files）

`git status`の代わりに`git status -u`を出力する。つまり、「Untracked files:」行の後は通常git管理外の新規追加ファイル、ただし新規追加ディレクトリはディレクトリが表示されて中の新規追加ファイルは表示されないが、-uオプションを指定すると新規追加ディレクトリの代わりに中の新規追加ファイルが表示される。git statusの-uオプションと同様、-uの後、スペースなしで（--untracked-filesの場合は「=」に続けて）以下の値を指定することでこの内容の制御を行うこともできる。

all: 値を指定しない場合と同じく、新規追加ディレクトリの代わりに中の新規追加ファイルが表示される。

normal: -uオプションを指定しない場合と同じく、新規追加ディレクトリが表示され中の新規追加ファイルは表示されない。

no: すべての新規追加ファイルやディレクトリは表示されない。代わりに「Untracked files not listed」と出力される。

* -v（あるいは--verbose）

git statusの出力の下にさらにコミットされるファイルの差分の内容を表示させる。差分の内容を見ながらコミットメッセージを決めたい時に便利である。2回このオプションが指定された場合はインデックスとワークツリーとの差分も追加で表示される。

### Gitが推奨するコミットメッセージの書き方

`man git-commit`で読むことができる、コミットメッセージの推奨の書き方というのがある。曰く、

* 1行目は50文字以内の要約とする
* それ以上の詳細な説明が必要な場合、要約の後に1行空行を開け、その後の行に記述する

とある。これを守った場合、`git log --oneline`という最初の空行の前だけをタイトルとしてコミット履歴を表示させるコマンドですっきりとした表示になることが利点である。この利点を考えると、「50文字以内」というのは日本語のいわゆる「全角文字」だと文字表示幅の関係で25文字までであるのが良いだろう。

また、コミットメッセージはUTF-8で書くことも推奨されている。

## git commitの便利なオプション

ここまで挙がっていないオプションの中で良く使われるのを見掛けるものを紹介する。

* -a（あるいは--all）

インデックスに上がっていない更新あるいは削除ファイルもコミットの対象にする。このオプションを指定する場合、対象ファイル名の指定をすることはできずエラーになる。

* --allow-empty

ファイルの変更差分がない、いわゆる空のコミットの作成を許容する。空とは言っても一緒に--allow-empty-messageオプションも指定していなければコミットメッセージは設定しなければならない。良く使われる用途としては、git init直後の全くコミットがない状態から空の最初のコミットを作るために使用する。Gitにおいてはコミットとコミットとの間で何かをするコマンドは多いが、最初のコミットを行う前とで何かをするのは割と特別な操作の扱いである。そのため、最初のコミットをまず空で作っておくことには意義がある。