rebase備忘録

## はじめに
備忘録も兼ねて、以下の場合に応じて用途をまとめる。
 - gitで親ブランチの変更を取り込むときの、mergeコミットをなくしたい
 - 誤コミットをrevertコミットで消すんじゃなくて、そもそもなかったことにしたい
 - コミットの中に誤修正が入っていたので、コミットは残しつつ誤修正だけなかったことにしたい
 - 過去のコミットコメントを修正したい
 - 複数に分けたコミットを1つにまとめたい
 - コミットの順番を変えたい（コミットをまとめるときに使いがち）
## 概要(rebaseとは)
「re(**再**)base(**基点**)」の名の通り、 再度基点を設定する。
変更の取り込みに使う場合があるため、mergeコマンドと比較されることが多い。
変更の取り込み以外にも、以下のようにコミット履歴の改変が可能なコマンド。
・コミットメッセージの改編
・コミットの削除　など

例) 以下のようなコミットがある場合
```
-----➀-----➁-----➂-----➃-----➄-----➅
```
➃から生えている➄をrebaseすると
```
-----➀-----➁-----➂-----➃
                    \
                     -----➄'-----➅'
```
➄以降が➂（先ほどの基点とは別のコミット）から生えるように変更が可能。

ここで注意すべきなのは、**➄～➅≠➄'～➅'** であること。
コミットをそのまま移動しているのではなく、同じような別コミットに作り替えられるイメージ。
```:1
-----➀-----➁-----➂-----➃-----➄-----➅
```
```:2
-----➀-----➁-----➂-----➃ //➄、➅を外す
```
```:3
-----➀-----➁-----➂-----➃
                    \
                     -----➄'-----➅' //➄、➅と同じ修正内容、同じコミットメッセージのコミットが新たに作られる
                                       コミットを作り直しているので、➄、➅とはコミットIDが異なる
```
### 親ブランチを取り込むときのmergeコミットをなくす
mergeではなくてrebaseで親ブランチの変更を取り込むとよい。
###### ターミナルを使用している場合
 - リモートの親ブランチを取り込む場合
```:
git pull --rebase origin xxxxxxx（取り込みたい親ブランチのブランチ名）
```
※プルリベースは、リモートから最新の状態を取ってくるプルと、リベースが合体したもの
 - ローカルの親ブランチを取り込む場合
```:
git rebase xxxxxxx（取り込みたい親ブランチのブランチ名）
```

###### SourceTreeを使用している場合
 - リモートの親ブランチを取り込む場合
1. 画面上部の「プル」を押下する
1. 「プルするリモートブランチ」で取り込みたい親ブランチを選択する
1. 「マージの代わりにリベースする」にチェックを付けて「プル」を押下する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2641495/eea25af8-19af-7b58-55bf-c100b8b3dda8.png)
 - ローカルの親ブランチを取り込む場合
1. 変更を取り込みたいブランチ（親ブランチ）を右クリックする
1. 「現在の変更を○○にリベース」を選択する
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2641495/d6d09b44-ed6d-cb96-ce4f-242a53dfecf5.png)

親ブランチの変更が入っていることを確認出来ればOK。

#### なぜrebaseで変更が取り込めるのか & mergeとの違い
例) ➀～➃が親ブランチのコミット、➊～➋が子ブランチのコミット
```
-----➀-----➁-----➂-----➃-----➊-----➋
```
↓ 親ブランチに追加の修正コミットが入る
```
-----➀-----➁-----➂-----➃--------------------➄-----➅
                           \
                            -----➊-----➋
```
##### mergeで変更を取り込む場合
```
-----➀-----➁-----➂-----➃--------------------➄-----➅
                           \
                            -----➊-----➋
```
```
-----➀-----➁-----➂-----➃--------------------➄-----➅
                           \                       　　　\
                            -----➊-----➋---------------➌ //mergeコミットとして変更がまとめて入る
```
- メリット
・１コミットでまとめて変更が入るので、コンフリクトが起きたときの解決が１回のみ
- デメリット
・親の変更をとりこむときにmergeコミットが積まれる

##### rebaseで変更を取り込む場合
```
-----➀-----➁-----➂-----➃--------------------➄-----➅
                           \
                            -----➊-----➋
```
```
-----➀-----➁-----➂-----➃--------------------➄-----➅
                                          
                                         //一度コミットを外す
```
```
-----➀-----➁-----➂-----➃-----➄-----➅-----➊'-----➋' //➅から生えるようにコミットを作り直す
```
親ブランチに新たに入ったコミットから、子ブランチが生え直すイメージ。
基点を変更するだけなので、mergeコミットが積まれない。
- メリット
・変更を取り込むときにmergeコミットが積まれない
- デメリット
・コンフリクトが起きたときに、解決を複数回行う場合がある
※あらかじめrebaseするブランチのコミットをrebaseでまとめておけば、解決は１回で済むので問題なし
・もしrebaseしたブランチから子ブランチが生えていた場合、
　rebaseによってコミットIDが変わるため、子ブランチがrebaseしたブランチを追跡出来なくなる。
※子ブランチをrebaseして新しいコミット（上の例だと➋'）から生えるようにすれば問題なし

→ **実質デメリットなし**

### 誤コミットをなかったことにしたい
###### ターミナルを使用している場合
```:コマンド
git rebase -i oooooooo（消したいコミットの親コミットID）
```
するとvim形式で編集が可能な画面が表示されるので、以下の手順で編集する。
1. 「i」キーを押下して挿入モードを開始
1. 消したいコミット行を削除（「d」（drop）オプションで消しても良い）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2641495/5ab0f1d1-c5cf-fd70-7626-b46e79dbc01c.png)
1. 「esc」キーを押下して挿入モードを終了
1. そのまま「:wq!」を入力してエンターキーを押下

###### SourceTreeを使用している場合
1. 消したいコミットの親コミット行を右クリック
1. 「○○の子とインタラクティブなリベースを…」を選択
1. 以下のような画面が出てくるので、コミット行を選択して「削除」を押下
![図6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2641495/47a86a63-8160-fe9b-7b7e-8975a5a8ac2c.png)
1. 「OK」を押下

どちらもローカルの履歴から該当コミットが消えていることを確認出来ればOK。
#### なぜrebaseでコミットを消せるのか
概要でも説明した通り、rebaseはコミットをそのまま移動するのではなく、作り直される。
なので同じコミットから生えるようにrebaseしたうえで、消したいコミットは作り直さなければ良い。
例) ➃コミットを消す場合
```: 1
-----➀-----➁-----➂-----➃-----➄-----➅
```
```: 2
-----➀-----➁-----➂ //➃以降を一回外す
```
```: 3
-----➀-----➁-----➂(-----➃')-----➄'-----➅' //➃'を作り直さない
```
```: 4
-----➀-----➁-----➂-----➄'-----➅'
```
### コミットの中の誤修正だけをなかったことにしたい
###### ターミナルを使用している場合
```:コマンド
git rebase -i oooooooo（リセットしたいコミットの親コミットID）
```
するとvim形式で編集が可能な画面が表示されるので、以下の手順で編集する。
１. 「i」キーを押下して挿入モードを開始する
２.  リセットしたいコミット行のオプションを「t」（reset）に変更する
３. 「esc」キーを押下して挿入モードを終了
３. そのまま「:wq!」を入力してエンターキーを押下
４. リセットしたいコミットがHEADコミットとして扱われているので、以下のコマンドを叩く
```:コマンド
git reset --soft HEAD^
```
５. 変更がコミットされていない状態になるため、誤修正だけ破棄して通常と同様にコミットする
６. 手元の変更が消えたら、以下のコマンドを実行する
```:コマンド
git rebase --continue
```

###### SourceTreeを使用している場合
1. リセットしたいコミットの親コミット行を右クリック
1. 「○○の子とインタラクティブなリベースを…」を選択
1. 以下のような画面が出てくるので、リセットしたいコミット行にチェックを入れて「OK」を押下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2641495/36f488ed-9592-92a5-8605-3c9eb976dc87.png)
1. リセットしたいコミットの親コミット行を右クリック
1. 「現在のブランチをこのコミットまでリセット」を選択
1. 以下のような画面が出てくるので、Softを選択して「OK」を押下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2641495/9785ad34-c7fb-1702-ca2e-f985d7970a62.png)
1. ファイルステータスにリセットしたコミットの変更があるため、任意の順でいつもどおりにコミット
1. 全てのファイルをコミットし終えたら、「プル」ボタンを押下
1. リベース続行確認ダイアログが表示されるので、続行を選択

どちらもローカルの該当コミットから誤修正が消えていることを確認出来ればOK。
#### なぜrebaseでコミットから誤修正を消せるのか
概要でも説明した通り、rebaseはコミットをそのまま移動するのではなく、作り直される。
なので同じコミットから生えるようにrebaseしたうえで、該当コミットにご修正を入れなければ良い。
例) ➃コミットから誤修正を消す場合
```: 1
-----➀-----➁-----➂-----➃-----➄-----➅
```
```: 2
-----➀-----➁-----➂ //➃以降を一回外す
```
```: 3
-----➀-----➁-----➂-----➃'(誤修正を除いたコミット)-----➄'-----➅' //➃'のコミット内容だけ変更する
```
```: 4
-----➀-----➁-----➂-----➃'(誤修正を除いたコミット)-----➄'-----➅'
```
### 過去のコミットメッセージを変更したい
###### ターミナルを使用している場合
```:コマンド
git rebase -i oooooooo（メッセージを変更したいコミットの親コミットID）
```
するとvim形式で編集が可能な画面が表示されるので、以下の手順で編集する。
1. 「i」キーを押下して挿入モードを開始する
1. コミットメッセージを変更したいコミット行のオプションを「r」（reword）に変更する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2641495/47891446-89d1-5449-72b6-d3b2f48e9dbe.png)
1. 「esc」キーを押下して挿入モードを終了
1. そのまま「:wq!」を入力してエンターキーを押下
1. コミットメッセージの変更画面が出てくるので、「i」キーを押下して挿入モードを開始する
1. そのままコミットメッセージを編集する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2641495/4aac269e-ba5e-204c-7b33-c4414837bf81.png)
1. 「esc」キーを押下して挿入モードを終了
1. そのまま「:wq!」を入力してエンターキーを押下

###### SourceTreeを使用している場合
1. メッセージを変更したいコミットの親コミット行を右クリック
1. 「○○の子とインタラクティブなリベースを…」を選択
1. 以下のような画面が出てくるので、コミット行を選択して「メッセージを編集」を押下
![図7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2641495/7ebae149-7b12-9981-2214-a630c11ce615.png)
1. メッセージ編集ダイアログが出てくるので、メッセージを編集する
1. 「OK」を押下

どちらもローカルの履歴にて該当コミットのメッセージが変更されていることを確認出来ればOK。
#### なぜrebaseでコミットメッセージを変更できるのか
概要でも説明した通り、rebaseはコミットをそのまま移動するのではなく、作り直される。
なので同じコミットから生えるようにrebaseしたうえで、コミットのコミットメッセージを変更すれば良い。
例) ➃コミットのコミットメッセージを変更する場合
```: 1
-----➀-----➁-----➂-----➃-----➄-----➅
```
```: 2
-----➀-----➁-----➂ //➃以降を一回外す
```
```: 3
-----➀-----➁-----➂-----➃'(メッセージ変更済)-----➄'-----➅' //➃'を作るときにコミットメッセージを変更する
```
```: 4
-----➀-----➁-----➂-----➃'(メッセージ変更済)-----➄'-----➅'
```
### 複数のコミットを１コミットにまとめたい
###### ターミナルを使用している場合
```:コマンド
git rebase -i oooooooo（まとめたいコミット群の親コミットID）
```
```:例) 以下の状況で➂、➃、➄を1つのコミットにまとめたい場合
-----➀-----➁-----➂-----➃-----➄-----➅
↓
-----➀-----➁-----➂'(➃、➄の内容もまとめて入っている)-----➅'

git rebase -i ➁のID（まとめたいコミット群の親コミットID）
```
するとvim形式で編集が可能な画面が表示されるので、以下の手順で編集する。
1. 「i」キーを押下して挿入モードを開始する
1. まとめたいコミット群のうち、最も過去コミット以外のコミット行のオプションを以下に変更する
a. まとめた後のコミットメッセージを編集したい場合
「s」（squash）に変更する
※「f」オプションと「r」オプションの併用でも可
b. まとめた後のコミットメッセージを編集したくない場合
「f」（fixup）に変更する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2641495/b62a93d2-6d6d-d65f-d030-7fd0bd459ff7.png)
1. 「esc」キーを押下して挿入モードを終了
1. そのまま「:wq!」を入力してエンターキーを押下
1. 「s」オプションを使用した場合は、そのままコミットメッセージの編集画面が出てくるので、vim形式で編集すれば良い
###### SourceTreeを使用している場合（squashのみ）
1. まとめたいコミット群の親コミット行を右クリック
1. 「○○の子とインタラクティブなリベースを…」を選択
1. 以下のような画面が出てくるので、コミット行を選択して「前のコミットとスカッシュ」を押下
![図7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2641495/7ebae149-7b12-9981-2214-a630c11ce615.png)
1. コミットがまとめられるので、コミットメッセージに不備がなければ「OK」を押下
※コミットメッセージを変更したい場合は、そのまま「メッセージを編集」を押下すれば良い

どちらもローカルの履歴にて該当コミットがまとめられていることを確認出来ればOK。
#### なぜrebaseでコミットがまとまるのか
概要でも説明した通り、rebaseはコミットをそのまま移動するのではなく、作り直される。
なので同じコミットから生えるようにrebaseしたうえで、作り直されるコミットの内容を１コミットにまとめてしまえば良い。
例) ➂、➃、➄コミットのコミットメッセージを変更する場合
```: 1
-----➀-----➁-----➂-----➃-----➄-----➅
```
```: 2
-----➀-----➁ //➂以降を一回外す
```
```: 3
-----➀-----➁-----➂'（(➃、➄の内容もまとめて入っている)-----➅' //変更をまとめたコミットを作成する
```
```: 4
-----➀-----➁-----➂'-----➅'
```
### コミットの順番を変えたい
###### ターミナルを使用している場合
```:コマンド
git rebase -i oooooooo（順番を変更したいコミット群の親コミットID）
```
```:例) 以下の状況で➂、➃、➄の順番を変更したい場合
-----➀-----➁-----➂-----➃-----➄-----➅
↓
-----➀-----➁-----➄'-----➂'-----➃'-----➅'

git rebase -i ➁のID（順番を変更したいコミット群の親コミットID）
```
するとvim形式で編集が可能な画面が表示されるので、以下の手順で編集する。
1. 「i」キーを押下して挿入モードを開始する
1. 「p」（pick）オプションはそのままで、順番を直接変更する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2641495/96576bd6-d34f-2f5c-05ea-e1a3a7503bde.png)
このままだとurlListの変更としてsquashで１コミットにまとめたいのに、errorMessageの修正が邪魔。。。
↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2641495/c2a88ee7-5c0a-61b2-ca25-9dfbbee5ea11.png)
※pickオプションではコミットメッセージが変更されることはないので、多少コピペをミスっても大丈夫です
1. 「esc」キーを押下して挿入モードを終了
1. そのまま「:wq!」を入力してエンターキーを押下
###### SourceTreeを使用している場合
1. まとめたいコミット群の親コミット行を右クリック
1. 「○○の子とインタラクティブなリベースを…」を選択
1. 以下のような画面が出てくるので、コミット行を選択して上下の「↑」もしくは「↓」を押下
![図7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2641495/7ebae149-7b12-9981-2214-a630c11ce615.png)
1. 選択したコミットが上下に移動するので、順番に不備がなければ「OK」を押下

どちらもローカルの履歴にてコミット順が変更されていることを確認出来ればOK。

###### 注意！
変更したコミット順によっては、エラーが発生することがある。
（ファイル追加コミットの前に、ファイル修正コミットを入れてしまった場合 等）
コミット順を変更してエラーが発生した場合は、コミット順が正しいかを見直す。
見直したうえで、もしどうしてもその順に並び変えたい場合は、
以下の手順のように一度コミットをリセットして、変更を再コミットし直すしかない。

1. 一度順番を変更したいコミット群をまとめる
1. 1でまとめたコミットをリセットする（手元に変更があって、未コミットの状態に戻す）
※もしコミットのリセット手順が分からない場合は、このページの「誤修正をコミットから取り除く」を参照
1. 手元の変更を任意の順でいつもどおりにコミット
1. 全てのファイルをコミットし終えたら、リベースを続行する

3でコミットした順にコミットが積まれていればOK。
#### なぜrebaseでコミット順が変更されるのか
概要でも説明した通り、rebaseはコミットをそのまま移動するのではなく、作り直される。
なので同じコミットから生えるようにrebaseしたうえで、順番を変更してコミットを作り直せば良い。
例) ➂、➃、➄コミットのコミット順を変更する場合
```: 1
-----➀-----➁-----➂-----➃-----➄-----➅
```
```: 2
-----➀-----➁ //➂以降を一回外す
```
```: 3
-----➀-----➁-----➄'-----➂'-----➃'-----➅' //順番を変えてコミットを作成する
```