gitのコミット指定方法(ハッシュ値、HEAD、HEAD~)を理解して、コマンドを有効活用しよう！

# はじめに

私はgitのコマンドオプションについて、例えばgit resetならHEAD~指定、git rebaseはハッシュ指定と決まった形で覚えていました。
しかし、[こちらの記事](https://qiita.com/tatetate55/items/7a822a3246df79d693b8#comment-bf5839cdafba13a109d4)で自分の知らない`git rebase`の使い方が紹介されており、その流れでコミット指定方法が理解でき、今まで不便に感じていたコマンドの使い方が解消されました。
最初にこの辺りの仕様を理解していればな～と思ったので、記事にまとめておこうと思います。

# gitのコミットを指定する方法は色々ある

先に結論から。以下のような方法でコミットを指定することが出来ます。

|指定方法|意味|備考|
|:----|:----|:---|
|ハッシュ値|対応するコミットを直接指定||
|HEAD|最新のコミット||
|HEAD~n|最新のコミットのn個前|HEAD~だと最新の1個前|
|ハッシュ値~n|対応するコミットのn個前|ハッシュ値~だと最新の1個前|

また、どれもその意味に該当するハッシュ値を指定しています。以下詳細です。

### **ハッシュ値を指定**

**コミット指定の基本はハッシュ値**で行います。
ハッシュ値は`git log`等で確認出来る`commit d7aab0dec8e026f64cb6fae75d7ca05b5ec50b05`のような長い16進数の値です。
この値はコミットを一意に表すものになっているため、この値を利用してコミットの指定が可能となります。

また、実際コマンドで指定する際は前の7桁だけ指定すれば十分だったりもします。(下記は指定したハッシュ値のコミット内容を表示する`git show `の例です)

```
$ git show d7aab0d
commit d7aab0dec8e026f64cb6fae75d7ca05b5ec50b05
Author: developer-kikikaikai <kikikaikaikai@gmail.com>
Date:   Sat Aug 11 03:33:18 2018 +0900

    <threadpool> Clean help option

diff --git a/Makefile.in b/Makefile.in
index 88f7cd2..93af2f8 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -170,8 +170,8 @@ DIST_SUBDIRS = design_pattern_util flyweight builder publisher \
        threadpool state chain_of_responsibility memorypool prototype \
...
```
### **HEAD(最新のコミット)を指定**

**最新コミットのハッシュ値は、別名としてHEADが設定されています。**

例えば最新のコミットが以下なら、HEAD=d7aab0dと同じ意味になります。

```
$git log --oneline
d7aab0d <threadpool> Clean help option
cebf87d <threadpool> Update readme to up fd size
5248829 <mement> Remove extra definition
...
```

実際にコマンドを実行してみても同じ。

```
$ git show HEAD
commit d7aab0dec8e026f64cb6fae75d7ca05b5ec50b05
Author: developer-kikikaikai <kikikaikaikai@gmail.com>
Date:   Sat Aug 11 03:33:18 2018 +0900
```

毎回ハッシュ値を調べるのは面倒なので便利ですね。

また、手元で確認したところ、どのコマンドも**コマンドオプションでハッシュを指定しない場合、基本デフォルトでHEADが指定される**ようです。
なのでgit logやgit showでは最新コミットの内容が確認出来るんですね。

### **HEAD~n: 最新のコミットのn個前を指定する**

HEADだけではなく、HEADからn個前のコミットを指定するという方法も用意されています。それが`HEAD~n`指定。
例えば先ほど`git log --oneline`で表示していたコミットの例だと、先頭の2個前は`5248829 <mement> Remove extra definition`になります。

```
$ git show HEAD~2
commit 52488293662d11e7d30ab445a68b17c344daba78
Author: developer-kikikaikai <kikikaikaikai@gmail.com>
Date:   Fri Aug 3 08:12:15 2018 +0900

    <mement> Remove extra definition
```

### ハッシュ値~n: 対応するコミットのn個前を指定する

HEADは最新コミットに対するハッシュ値の別名です。ということは、HEAD~2は最新ハッシュ値の2個前という意味。
なので、`5248829~2`のような**特定のコミットからn番目**という指定方法も可能だったりします。

# gitコマンドの用途に合わせて指定方法を活用しよう

指定の仕方が色々あるんですよ～、と書かれてもピンとこないと思うので、私自身不便に感じていたコマンドの利用ケースを例にあげて説明したいと思います。

## 例1: git reset: 指定したコミットの1つ前までのコミットを削除する

### 直近いくつかのコミットを削除したい⇒HEAD~指定が便利

やっちゃった、間違えて変なものをコミットしちゃった！消さないと！
こんなケースではHEADが便利ですね。例えば2個分のコミットを削除するなら以下のような指定。

```
git reset --hard HEAD~2
```

### コンフリクトが発生した履歴を削除したい⇒ハッシュ値~が便利

例えば以下のケースで`d86c069 <builder> Set stack size, fixed #48`がコンフリクトしたため、一旦コンフリクトした履歴まで削除したいとします。

```
$ git log --oneline
7ab9110 <mement> Add new library
e1bd739 <common> Add CHANGES to remember changing external spec
c8999f5 <Common> Remove sample code
7659ba1 <command> add sample...
ec6ae98 <common> Add extern C option for C++, fix #45
d86c069 <builder> Set stack size, fixed #48
cadff15 <builder> Change constructor interface to sync constructor, fix #47
890acaa <builder> Fix to fail to load lib, fix #46
```

ここでgit reset HEADの使い方だけしか知らないと、「コンフリクトする直前の`cadff15 `まであと何個だっけ？」なんて数えながら地道にコミットを削除することになります。
今までの私がそうでした(-_-;)

そんな時にはハッシュ値~ 指定で、コンフリクトしたコミットの1個前まで削除する指定方法を知っていれば1発です。

```
$ git reset --hard d86c069~
Unstaged changes after reset:
M       Makefile.am
M       Makefile.in
...

#削除されたかコミットを確認。ちゃんと1個前のcadff15が先頭に来ている!
$ git log --oneline
cadff15 <builder> Change constructor interface to sync constructor, fix #47
890acaa <builder> Fix to fail to load lib, fix #46
...
```

## 例2: git log -pでコミットの詳細を確認する。

### 最新のログが見たい⇒HEAD or 指定なしで十分

通常の使い方はこれで問題ないですね。

### とある修正より前のログが見たい⇒ハッシュ値指定が便利

例えば発生したバグの解析をする際に、問題発生時期したアプリケーションのバージョン的にこのコミットより前のどこかだ！その前のログが確認したい！
なんてケースがあったりします。
こちらも今までの私は愚直に`git log -p`の後ハッシュ値で検索なんてことをしていました。。。

こちらもこれで一発でしたね。

```
git log -p ハッシュ値
```

# コミット指定を利用するgitコマンド × コミット指定方法には種類がある = 用途に合わせた使い方が選べる

というわけで、いくつか例をあげてコミット指定方法について紹介しました。
gitには沢山のコマンドがあり([チートシート載せておきます](https://qiita.com/sutetotanuki/items/1700343852e863ba63a0))、その中にはコミットを指定するコマンドも多くあります。
このコマンドと指定方法とをかけ合わせれば、基本的な使い方だけではなく色々な工夫が出来るはず。

実際「コミット指定の仕組みを理解していれば、あの時やってたあれがすぐ終わったのに～」という小さな記憶がふつふつと湧きあがっています。
この記事がそんなちょっとした作業効率化につながれば幸いです。

## **参考**

[git rebase -iでgitのコミットをまとめる](https://qiita.com/tatetate55/items/7a822a3246df79d693b8#comment-bf5839cdafba13a109d4)
[GitのHEAD, ORIG_HEAD, FETCH_HEAD, MERGE_HEADとは？](https://qiita.com/t-mochizuki/items/347cba461fd570bca03c)
[gitコマンドチートシート](https://qiita.com/t-mochizuki/items/347cba461fd570bca03c)