git 履歴を変えるコマンドたち

#まえがき
バージョン管理システムであるgitにとって履歴は重要です。「間違ってコミットしてしまった」というときにはその履歴を変更したいものです。そのようなときに使うgitのコマンドを紹介します。

#前提知識
gitのaddやcommit、branchの概念は知っている前提で話を進めます。
以下のコマンドが使えることが目安となります。
git add
git commit 
git status
git branch
git checkout
git merge

また、リモートレポジトリの概念も知っておいたほうが理解しやすいです。
git push
git pull

#紹介するコマンド
git log
git commit "" --ammend
git reset --soft
git reset
git reset --hard
git revert
git reset --hard ORIG_HEAD
git reflog
git cherry-pick
git rebase
git clean
git rm
git add -A

#コマンドたち
##git log

履歴を変えるためには履歴を知らなければなりません。まずは履歴の一覧を見るためのコマンドです。
一つのコミットに対して4つの情報が表示されます。

```
コミット名
作成者
日付
コミットメッセージ
```
が見れます。

```
$ git log                                                                                                                                       
commit c0d3f0dad42f0d573708eaa5b5b926b0fbbfd38c
Author: test <test@gmail.com>
Date:   Fri Jul 31 16:22:33 2015 +0900

    add link for all

commit 8867ba4eeb4c22e2540ecff1f2849078cee0987d
Author: test <test@gmail.com>
Date:   Fri Jul 31 16:21:37 2015 +0900

    add link to show

commit 8f72515bddc5a96cd0e0233492abbb8314641df5
Author: test <test@gmail.com>
Date:   Fri Jul 31 16:19:53 2015 +0900

    first commit
```

###git log -(数字)
また、表示するコミットの数も指定出来ます。
以下、表示するコミットの数を2つに指定します。

```
$ git log -2                                                                                                                                    
commit c0d3f0dad42f0d573708eaa5b5b926b0fbbfd38c
Author: test <test@gmail.com>
Date:   Fri Jul 31 16:22:33 2015 +0900

    add link for all

commit 8867ba4eeb4c22e2540ecff1f2849078cee0987d
Author: test <test@gmail.com>
Date:   Fri Jul 31 16:21:37 2015 +0900

    add link to show
```

「git log -」のあとに表示したいコミットの数を指定出来ます。「git log -5」なら表示するコミットの数は5個です。

###git log --pretty=oneline
「-pretty=oneline」のオプションを付けるとコミット名とコミットメッセージのみで一行で表示してくれます。

```
$ g log -2 --pretty=oneline                                                                                                                  
59c006fb4999a0eb2a4bdf896819ac7de265579d add link to all
374b3594079db67d4aa36791076d1a6686353576 add link to show
```

##git commit "" --amend
コミットした直後に「コミットメッセージ間違えた><」なんてことありますよね。そんなときに直前のコミットメッセージを変更するコマンドです。
下記では「add link for all」というコミット名を「add link to all」に変えています。

```
$ g log -1 -pretty=oneline                                                                                                                                  
a7ad58a6e5d9efb3cc6024135509befe43c38842 add link for all

$ git commit -m "add link to all" --amend                                                                                                     
[master 59c006f] add link to all
 1 file changed, 1 insertion(+)

$ git log -1                                                                                                                                  
59c006fb4999a0eb2a4bdf896819ac7de265579d add link to all
```

##git reset
いよいよ、履歴を変えていきます。「git reset」は履歴を削除するコマンドです。
ただしこのコマンドを見ていく前に確認です。「HEAD」とは何かを確認しておきましょう。
###HEAD
HEADは、「今、自分がどの場所で作業しているポインタ」です。これまでの例で見てみると3つのコミットの内3番目に生成されたコミットの位置（最新のコミットの位置）にHEADがあります。以下の画像でなんとなく理解出来ると思います。（Cはコミットのこと。C1、C2、C3がそれぞれ1、2、3番目のコミットを示している。）


![HEAD1.png](https://qiita-image-store.s3.amazonaws.com/0/40796/f04176f0-a8dc-1aa5-d7cb-7129f98abf8e.png)

また「^」というものを使ってコミットの位置を指定出来ます。「^」は「-1」を表していて「HEAD^」とすると上記のC2を示すことが出来ます「HEAD^^」はC1を示します。
さらに、「~」というものもあってこれは「HEAD~2」のように数字で指定でき、「HEAD^^」のことを指定出来ます。（「HEAD~1」が「HEAD^」です）
今後、「HEAD」の位置、つまり自分のいる位置を移動させていくことをコマンドでやっていきます。

###ワーキングツリー、インデックス、リポジトリ
念のための確認です。
下記のようにワーキングツリーをaddしてインデックスへ、インデックスにあるものをコミットしてリポジトリへ移動させます。
また、ワーキングツリーは作業ツリー、インデックスはステージングとも言いますが、ここでは下記の画像のように呼びます。


![commit_stream.png](https://qiita-image-store.s3.amazonaws.com/0/40796/7b510f66-06f8-ff39-6bf8-d9373d6dbc88.png)


##git reset

それでは、git resetについて説明します。
resetは
git reset --soft
git reset(git reset --mixed)
git reset --hard
の3つを見ていきます。

注意が必要なのは履歴をなかったことにするコマンドなので既にリモートにpushしたコミットに対して行うのは危険です。ローカル内のコミットだとしても注意して使いましょう。（自己責任でお願いします）

###git reset --soft (指定位置)
HEADの位置を指定した位置に変更する。これだけでは分かりにくいので例を見て下さい。

例

```
$ git reset --soft HEAD
``` 
HEADの位置をHEADにするだけなので何も変わりません。

```
$ git reset --soft HEAD^
```
HEADの位置がHEAD^に移動するので、直前のコミットのみ取り消されます。

###git reset (指定位置) または git reset --mixed (指定位置)
HEADとインデックスの位置を指定した位置に変更する。
また、「git reset」 と「git reset --mixed」は同じ意味です。

例

```
$ git reset HEAD
```
HEADの位置とインデックスの位置がHEADに移動するため、addしているファイル(commitしていないもの)がaddしていない状態になる。

```
$ git reset HEAD^
```
HEADの位置とインデックスの位置がHEADに移動するため、直前のコミットが取り消され、addされていない状態まで戻る。


##git reset --hard
HEADとインデクッス、作業ツリーの全てを指定した位置に変更する。

例

```
$ git reset --hard HEAD
```
HEADとインデックス、作業ツリーを全てHEADの位置に移動するので、addしているファイル(commitしていないもの)と作業ツリーの編集ファイルがなかったことになる。

```
$ git reset --hard HEAD^
```
HEADとインデックス、作業ツリーを全てHEADの位置に移動するので、直前のコミットが取り消され、そのコミットの内容はまるまる消える。

以上がresetの使い方ですが、ピント来なかった場合は以下のサイトで一度理解してから読み直して下さい。図を交えた説明で分かりやすいです。
[git reset についてもまとめてみる](http://d.hatena.ne.jp/murank/20110327/1301224770)

## git revert

```
$ git revert HEAD
```
でHEADのコミットを取り消すようなコミットを作成してくます。git resetは履歴を削除しましたが、git revertは履歴を戻すようなコミットを新たに作成します。
コミットメッセージを変更する画面が出てくるので変更する場合は変更して、後は勝手にコミットまでやってくれます。

--no-editのオプションをつけることでデフォルトのコミットメッセージでコミットまでやってくれます。

```
$ git revert HEAD --no-edit
```

-nのオプションをつけることでコミットされない状態にすることも出来ます。(addされた状態)

```
$ git revert HEAD -n
``` 

git resetを使うときは履歴を消してしまうので作業のデータが消えてしまうことがあるので注意が必要です。ただし、git resetで消したコミットは復活させることが出来ます。

##git reset --hard ORIG_HEAD
このコマンドを利用すると直前のreset前の状態に戻すことが出来ます。

```
$ git reset --hard ORIG_HEAD
```

##git reflog
git reflogを使うと過去のあらゆるコミット履歴が見ることが出来ます。

```
$ git reflog
c0d3f0d HEAD@{0}: commit: add link for all
8867ba4 HEAD@{1}: commit: add link for show
8f72515 HEAD@{2}: commit (initial): first commit
```
のような形で履歴が見れるので下記コマンドのようにして任意の位置まで戻ることができます。

```
$ git reset --hard HEAD@{2}
```

注意点としては、コミットは遡れてもコミットし忘れればもう戻れないことです。
git reset --hardは作業ツリーのデータまで消すので使用には注意が必要です。

##git cherry-pick
git cherry-pickは別ブランチから任意のコミットをコピーして持ってきます。

```
$ git cherry-pick (コミット名)
```

間違って別ブランチにコミットしちゃった…　というときに使えます。
例えば、
developブランチにコミットしたかったのに、masterブランチにコミットしてしまったというときには、以下のように対処出来ます。

```
$ git branch
* master
develop
$ git checkout develop
$ git cherry-pick 8431e94b6c5e3f371e8b813f39fcf3b34761ac1a
$ git checkout master
$ git reset --hard HEAD^
```

##git rebase

ブランチの統合としてgit rebaseを使います。利点としては異なるブランチでの作業が一本化されて履歴が単純になることです。以下のサイト内で詳しく説明があるので参照下さい。

[ブランチの統合](http://www.backlog.jp/git-guide/stepup/stepup1_4.html)
上記サイト内で、rebaseした後にmasterブランチをHEADに移動していることに注意して下さい。
ちなみに、developブランチをmasterにrebaseするときは以下のように書きます。

```
$ git branch
  master
* develop

$ git rebase master
$ git branch -f master HEAD
```
とやるとmasterをHEADに移動出来ます。

ブランチの統合に関しましては上記サイトを参照してもらうとして、ここでは特に-iのオプションを使うことによってインタラクティブモードを使い

* コミット名の編集
* コミットの順番を変える
* コミットの編集
* コミットの圧縮
* コミットの分割

を行う方法を見て行きたいと思います。

###コミット名の編集
直前のコミット名を編集するときは皆さんご存知「git commit -m "" --amend」を使いますが、それ以前のコミットメッセージを変更したいと思います。「add link to all」を「add link to all information page」に変更します。

```
$ g log --pretty=oneline                                                                                                                      
825789c001c02dce729f977bee34167f555d73d2 add new message
59c006fb4999a0eb2a4bdf896819ac7de265579d add link to all #ここのコミットメッセージを変更したい
374b3594079db67d4aa36791076d1a6686353576 add link to show
8f72515bddc5a96cd0e0233492abbb8314641df5 first commit

$ git rebase -i HEAD~3
#エディタが開く
---編集前
pick 374b359 add link to show
pick 59c006f add link to all
pick 825789c add new
---
---編集後
pick 374b359 add link to show
edit 59c006f add link to all
pick 825789c add new
---閉じる
$ g log --pretty=oneline
5d995ee266a9af1234f691a309eccb2445b7ea34 add link to all
374b3594079db67d4aa36791076d1a6686353576 add link to show
8f72515bddc5a96cd0e0233492abbb8314641df5 first commit

$ g commit -m "add link to all information page" --amend

$ g log --pretty=oneline
5d995ee266a9af1234f691a309eccb2445b7ea34 add link to all information page #変わった！
374b3594079db67d4aa36791076d1a6686353576 add link to show
8f72515bddc5a96cd0e0233492abbb8314641df5 first commit

$ g rebase --continue
Successfully rebased and updated refs/heads/master.
```

##コミットの順番を変える
上記のエディタ編集部分でpickの順番を任意の順番に入れ替えれば出来ます。

```
#エディタが開く
---編集前
pick 374b359 add link to show
pick 59c006f add link to all
pick 825789c add new #順番を変える
---
---編集後
pick 825789c add new
pick 374b359 add link to show
pick 59c006f add link to all
---閉じる
```

##コミットの編集

```
---編集前
pick 374b359 add link to show
pick 59c006f add link to all
pick 825789c add new
---
---編集後
pick 374b359 add link to show
edit 59c006f add link to all
pick 825789c add new
---閉じる
```
のように編集後エディタを閉じて

```
$ git reset HEAD^
```
とすればeditのコミットがaddする前の状態になるのでそれを再編集しコミットし直せば完了。commit後は

```
$ git rebase --continue
```
を忘れずに。

##コミットの分割
上記のコミットの編集で

```
$ git reset HEAD^
```
後にcommitを分割すれば（複数回に分けてコミットする）、任意のコミットの分割が出来る。


##コミットの圧縮
エディタが開かれたときに統合したいコミットのpickをsquashに変えます。そうすればその前のコミットに統合されます。

```
$ g log --pretty=oneline                                                                                                                      
825789c001c02dce729f977bee34167f555d73d2 add new message
59c006fb4999a0eb2a4bdf896819ac7de265579d add link to all
374b3594079db67d4aa36791076d1a6686353576 add link to show
8f72515bddc5a96cd0e0233492abbb8314641df5 first commit

$ git rebase -i HEAD~3
#エディタが開く
---編集前
pick 374b359 add link to show
pick 59c006f add link to all
pick 825789c add new
---
---編集後
pick 374b359 add link to show
squash 59c006f add link to all
pick 825789c add new
---閉じる

$ g log --pretty=oneline
c601843d5fbd1efdc58b8e6880ad221a3093932a add new message
5d995ee266a9af1234f691a309eccb2445b7ea34 add link to all #「add link to show」に統合された！
8f72515bddc5a96cd0e0233492abbb8314641df5 first commit
```

##git clean
git cleanは追跡していないファイルを一気に削除します。追跡していないファイルとは新規作成ファイルでまだ一度もaddしていないファイルです。git status時に示されます。この追跡されていないファイルは「git reset --hard」を使っても消えないので注意です。

```
$ git clean -f
```
これで実行出来ます。
また、

```
$ git clean -n
```
で予行でどのファイルが消えるか確認もできるので、確認してから実行しましょう。

## git rm
gitからファイルを削除します。

--cachedオプションを付けるとファイル自体は残ります。
つまり、追跡していないファイルとしては残ります。

##git add -A
全ての変更をaddします。
「git add.」+ 「git add -u」の役割です。
「git add .」- 新規作成ファイルと変更ファイル をaddする。
「git add -u」-変更部分のみadd。新規ファイルはaddされない。

参考 [git add -A と git add . と git add -u の違い](http://nekova.hatenablog.com/entry/2013/02/20/013909)

#参考サイト
[サルでもわかるGit入門](http://www.backlog.jp/git-guide/stepup/stepup1_1.html)
図での解説がわかりやすいです。
「発展編チュートリアル3」が本記事と似通っていることに後々気づきました。復習も兼ねて「発展編チュートリアル3」だけでもみることをおすすめします。

[LearnGitBranching](http://k.swd.cc/learnGitBranching-ja/)
実際に手を動かしながら画面上で何が起きているか確認出来るチュートリアルです。かなり勉強になります。そして面白いです。

[git reset についてもまとめてみる](http://d.hatena.ne.jp/murank/20110327/1301224770)
git resetについて最も納得出来た記事です。図による解説で分かりやすいです。

[Gitでやらかした時に使える19個の奥義](http://qiita.com/muran001/items/dea2bbbaea1260098051)
シチュエーションごとのgit操作が書かれています。本記事を読んだ後に見てみるとかなり頭に入ると思います。

[入門git ](http://www.amazon.co.jp/%E5%85%A5%E9%96%80git-Travis-Swicegood/dp/427406767X)
gitのオススメ書籍です。本記事に関連するのは第6章あたりです。