初心者から一歩抜け出すためのGitの業 〜 git rebase -i

[前回](http://qiita.com/tbaba/items/68463451a4e060097dbb)とは記事を分けることにした。長くなるから。
今回は、だれでもやったことがあるであろう、「え、ちょ、3つ前のコミットにtypo見つけちゃったよ！！」に対応する。

やりかたはいくつかある。例えば、最初に思いつきそうなことが、

- そのコミットまで`git reset`を繰り返して、編集して、もう一回`git commit`していく

である。だけど、前の編集内容を覚えてなきゃいけないし、「Gitを使ってるくせに」的なアナログ感を感じざるをえない……ので、もっと上手い方法は無いのか。

ある。

## git rebase -i

`i`は`interactive`のことだ。多分。`man git-rebase`をちゃんと読めば書いてあるのかもしれない。英語だけど気になる人は読んでみて欲しい。

で、このコマンド、何ができるのか。試しにやってみよう。
`git log --oneline`したら、こんな感じのログがあった。

```
ee88db4 Add picture of '平塚の海岸線'
f182a59 Write the continuance of the diary
80dfd45 Add nikki
```

しかし、`git log -p`して詳細を眺めていくと、`80dfd45`の中に、重大なミスを発見してしまった。

**俺10時って書いてるけど、10時半だよこれ！！**

```diff
commit 80dfd45d305cb7b9807419e31115929781b1f94b
Author: Tatsuro Baba <harakirisoul@gmail.com>
Date:   Tue Oct 8 09:33:33 2013 +0900

    Add nikki

diff --git a/nikki b/nikki
new file mode 100644
index 0000000..0551e98
--- /dev/null
+++ b/nikki
@@ -0,0 +1,7 @@
+今、富士の某ホテルでこの日記を書いていている。
+
+朝8:40、自宅を出る。すでに出発予定時刻を40分過ぎている。普段の生活態度がうかがい知れる。
+ひたすら南に下って行く。途中自動車専用道があったので、回避したら、300m程度のとんでもない激坂があって、萎えかけた。
+そのあとはひたすら平坦な道を平塚に向かって進むだけだったので、楽しかった。時速で言うと30km/hくらいか。
+
+10時ごろに平塚に着いて、海に出てみる。
```

しかし、このコミット、もう3つも前のやつである。`git show HEAD~2`でようやく出てくるやつである。
ここまで根深いところにあると、`git commit --amend`では対処できない。ふつうに修正して`Typo`とか`fix forgotten time`とかでコミットするのも手だが、まだリモートにpushしていないところなので、（ダサいし）パパっと直してしまいたい。

というわけで前置きが長くなったが、`git rebase -i`の出番である。

おもむろにターミナルで、

```sh
$ git rebase -i HEAD~3
```

こうすることで、過去3つのコミットをrebaseの対象にできる。
前回の記事と同じく、エディタが立ち上がって、以下のように表示されるはずだ。

```vi
  1 pick 80dfd45 Add nikki
  2 pick f182a59 Write the continuance of the diary
  3 pick ee88db4 Add picture of '平塚の海岸線'
  4 
  5 # Rebase fa33ce4..ee88db4 onto fa33ce4
  6 #
  7 # Commands:
  8 #  p, pick = use commit
  9 #  r, reword = use commit, but edit the commit message
 10 #  e, edit = use commit, but stop for amending
 11 #  s, squash = use commit, but meld into previous commit
 12 #  f, fixup = like "squash", but discard this commit's log message
 13 #  x, exec = run command (the rest of the line) using shell
 14 #
 15 # These lines can be re-ordered; they are executed from top to bottom.
 16 #
 17 # If you remove a line here THAT COMMIT WILL BE LOST.
 18 #
 19 # However, if you remove everything, the rebase will be aborted.
 20 #
 21 # Note that empty commits are commented out
```

これの1行目、`pick`と書いてあるところを、おもむろに`edit`と書き換えよう。すると、

```sh
Stopped at 80dfd45... Add nikki
You can amend the commit now, with

	git commit --amend

Once you are satisfied with your changes, run

	git rebase --continue
```

と言われるはずだ。この時点でどうなっているかというと、 **次にある2つのコミットは一旦よそに置いといて、`80dfd45`だけがコミットされた状態**まで巻き戻っている、みたいな、だいたいそんな感じだ。現状はそんな程度の認識でも大丈夫。

ここまでくればあとは楽勝である。おもむろに修正したコミットを作り、

```diff
diff --git a/nikki b/nikki
index 6a66ce0..0551e98 100644
--- a/nikki
+++ b/nikki
@@ -4,4 +4,4 @@
 ひたすら南に下って行く。途中自動車専用道があったので、回避したら、300m程度のとんでもない激坂があって、萎えかけた。
 そのあとはひたすら平坦な道を平塚に向かって進むだけだったので、楽しかった。時速で言うと30km/hくらいか。
 
-10時ごろに平塚に着いて、海に出てみる。
+10時半ごろに平塚に着いて、海に出てみる。
```

さくっとamendしてやる。

```sh
$ g ad .
$ d --cached
diff --git a/nikki b/nikki
index 6a66ce0..0551e98 100644
--- a/nikki
+++ b/nikki
@@ -4,4 +4,4 @@
 ひたすら南に下って行く。途中自動車専用道があったので、回避したら、300m程度のとんでもない激坂があって、萎えかけた。
 そのあとはひたすら平坦な道を平塚に向かって進むだけだったので、楽しかった。時速で言うと30km/hくらいか。
 
-10時ごろに平塚に着いて、海に出てみる。
+10時半ごろに平塚に着いて、海に出てみる。
$ git commit --amend
```

直った！！

この後、まだやることはある。現状、2つくらいコミットがどこか俺の知らない所に旅行してしまっていて、行き先を知っているのはGitのみである。ので、Gitにコミットたちを呼び戻してもらう必要がある。
つまり、rebase中で止まっている状態なので、rebaseを継続するコマンドを打ってやればよい。

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

これで全工程は完了。ちゃんとログも、

```
f6dfa25 Add picture of '平塚の海岸線'
f1b9f54 Write the continuance of the diary
00ea658 Add nikki
```
と、今までどおりだ。

`git-rebase`というコマンド、他にも色々機能があって奥深い。黒魔術というほどでもないが、使えるようになって損はないと思う。
ちなみにこの業を何も知らないGit初心者の前でスラスラとやったりすると、目の前で何が起こっているのか分からずに戸惑うので、ちょっと楽しい。俺は楽しかった（でもちゃんと説明してあげよう）。

あ、あと最後に。
これ、すでにリモートにpushしちゃった後にやったりすると、チームメンバーにものすごい迷惑をかける可能性が高いので、気をつけよう。
つまり、リモートにあるやつと、あなたが今rebaseしたやつには、大きな乖離が生じていて、リモートを強制的に変更することも出来るけど、それをやってしまうと、すでにリモートをpullしていたチームメンバーからしてみたら、「俺のリポジトリとリモートが違う…」ということになってしまうからだ。
このへんのリスクとかは、別の記事にしようと思う。