git rebase -i を使用する目的とその結果、方法について

2021/04/21
コメントを頂き、内容を修正しました。
この記事は`git rebase`の中でも、`git rebase -i`について紹介していきます。

## 0. はじめに

この記事では`git rebase -i`の目的とその結果、方法を記述していきます。また、その中でも特に`edit`に関する内容に触れていきます。

この記事は、私がリベースを行った際に、その結果がイメージできなかったのでまとめておこうと思いました。なので`git rebase -i`の結果について、__どう変化するのか__、を伝えられればいいなと思います。

## 1. "git rebase -i"を行う目的

`git rebase -i`の目的は**過去の改竄**を行うことです。

私が`git rebase -i`を行う必要性が生じたのは、「ファイル内にEメールアドレスを直接記述」し、その内容をコミットしたからです。そのミスには暫くしてから気づき、過去の改竄が必要になりました。

ちなみに、変更の差分は`git diff`を用いて確認できます。それにより、過去の変更履歴も確認が可能です。それゆえ「ファイル内にEメールアドレスを直接記述」していない過去に変更する必要があり、`git rebase -i`を行いました。

## 2. "git rebase -i"の結果

まず初めに、`git rebase`で何が起こるか確認します。`git rebase`後でも、改竄を加えた部分以外は同じです。しかし、**コミット履歴**が変更されます。

以下に`git rebase -i`の前後のログの例を示します。

下の例では、新しく'test_branch'を用意し、commit[5555555]の内容を`edit`しています。

`before`

```bash:ターミナル
git log —-oneline

7777777 (HEAD -> develop_origin, origin/develop_origin)This is commit E
6666666 This is commit D
5555555 (third-branch)This is commit C
4444444 This is 3rd commit B
3333333 (second-branch)This is commit A
2222222 This is 1st commit
1111111 (origin/master, master) Initialize repository
```

`After`

```bash:ターミナル
git log —-oneline

ccccccc (HEAD -> test_branch)This is commit E
bbbbbbb This is commit D
aaaaaaa This is commit C rebased!!
4444444 This is 3rd commit B
3333333 (second-branch)This is commit A
2222222 This is 1st commit
1111111 (origin/master, master) Initialize repository
```

## 3. "git rebase -i"の方法

ここでは、リベースの中でも、`edit`の方法を解説します。ちなみに、私は以下の記事を参考にリベースさせて頂きました。とても分かりやすいので、おすすめです。

[Git のコミット履歴を大胆に書き換えるなら git rebase -i がオススメ](https://yu8mada.com/2018/08/23/if-you-drastically-rewrite-git-s-commit-history-git-rebase-i-is-recommended/)

リベースでeditするステップは以下の5つです。

1) リベースを行う ( `git rebase -i [コミット番号]`
2) 改竄したいコミット番号を指定する ( `pick` → `edit`
3) 過去を改竄する
4) コミットし直す ( `git commit —amend`
5) 現在に戻ってくる ( `git rebase continue`

以下、詳細の操作について説明していきます。ちなみに例では、2章で用いたコードを用います。

### 1) リベースを行う

まず、リベースのの基本的な使い方として `git rebase -i <after-this-commit>` という構文で行います。このコードをするとViエディタが起動します。

```bash:ターミナル
git rebase -i <after-this-commit>
# ex)
git rebase -i 4444444444444..
```

```:Viエディタ
pick 6666666 This is commit D
pick 5555555  This is commit C

# Rebase 4444444..5555555 onto 4444444 (2 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
```

`<after-this-commit>`には、コミット番号を指定します。このコミット番号によりリベースする範囲を決定します。具体的には、指定したコミット番号より一つ後ろのコミット番号から、HEADまでの間の内容をリベースすることが出来ます。

例では`4444444`のコミットを指定している為それ以降でHEADまでのコミットがリベース可能範囲です。
つまり、`6666666`、`5555555`がリベース可能になります。

### 2) 改竄したいコミット番号を指定する

次にViエディタで、改竄したいコミットを指定します。その方法は、改竄したいコミットの横にある`pick`を`edit`変更します。そしてViエディタを`:wq`で保存します。

```:Viエディタ
edit 5555555  This is commit C
pick 6666666 This is commit D

# Rebase 4444444..6666666 onto 4444444 (2 commands)
#
# Commands:
# .
#  省略
# .
# However, if you remove everything, the rebase will be aborted.
```

2個のコミットから1番上を選択すると以下のようなターミナルに変更されます。

![rebase.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/779676/e0b45182-fa48-6b42-226b-f78dfc5f77a5.png)


ちなみに、この時点でリベースをせずに中止するコマンドがあります。以下のコマンドです。

このコマンドを実行することで、リベースを行う前の状態へ戻す事が可能です

```bash
git rebase --abort
```

ちなみにここでは過去の改竄の為`edit`を用いていますが、リベースには他にもコマンドがあり、目的に応じたコマンドをここでは選択します。

### 3) 過去を改竄する

ここでは改竄したい過去の内容を変更します。自分の場合は、Eメールアドレスを直接記述していたので、その変更を削除します。

```bash:ターミナル
:~/myapp (test_branch|REBASE 1/2)$ git diff
diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb
--- a/config/initializers/devise.rb
+++ b/config/initializers/devise.rb
@@ -24,7 +24,7 @@ Devise.setup do |config|
   # with default "from" parameter.
-  config.mailer_sender = 'aiueo@abcde.com'
+  config.mailer_sender = Settings.google_account[:email]
 
   # Configure the class responsible to send e-mails.
```

### 4) コミットし直す

変更をステージングし、コミットし直します。
注意点として、ここではコミットのやり直しを意味する`—amend`オプションを付随させます。

```bash
git ci --amend
```

### 5) 現在に戻ってくる

目的通りに過去を改竄できたらリベースは終了です。リベース完了を意味する以下のコマンドを実行します。

```bash
git rebase --continue
```

先程の変更で、その後の変更にコンフリクトが生じていない場合は、リベース成功です。

コンフリクトが生じている場合には、コンフリクトを解消し、もう一度上記のコマンドを実行すればリベース完了です。リベース後はコミット履歴が変更されます。

```bash:ターミナル
git log —-oneline

ccccccc (HEAD -> test_branch)This is commit E
bbbbbbb This is commit D
aaaaaaa This is commit C rebased!!
4444444 This is 3rd commit B
3333333 (second-branch)This is commit A
2222222 This is 1st commit
1111111 (origin/master, master) Initialize repository
```

## 4. おわりに

最後まで、読んで頂きありがとうございました！

まだまだ学習中なので、間違っている部分などコメントなどして頂けると嬉しいです！

### 余談ですが...

私がリベースを行う必要があったのは、Githubでpublicにしたいリポジトリに裸のEメールアドレスをpushしたからです。セキュリティ的に脆弱になってしまう為、リベースを行いました。大変だったぜ。次から気をつけなければ、
~~リポジトリをpublicにする為には、リベースだけでなくリポジトリを一新する必要がありました。なぜかというと、リベースでは、改竄前のコミット履歴も残っている為、履歴を辿ればEメールアドレスを確認できてしまうからです。~~


### 参考

https://yu8mada.com/2018/08/23/if-you-drastically-rewrite-git-s-commit-history-git-rebase-i-is-recommended/

https://git-scm.com/docs/git-rebase