git rebase についてまとめてみた

# git rebaseについてまとめてみた

## はじめに

なんとなくでしかgit rebaseを使ってないなと思ったので、
git manualなどを読んで自分なりのメモを残すことにしました。
ここ(Qiita)に上げておけばきっと読み返す機会が多くなるはず。
そもそも、この記事の投稿時は仕事でGITを使っているわけではなく、個人で使うオプションも、ほぼ"--continue"と"--abort"だけなので、
この際、きちんと他のオプションも理解しておきたいと思いました。
もしも、記事の中で間違いなどありましたら、編集リクエストやコメントなどで教えていただければ幸いです。
※オプションも全て記載しようと思ったのですが、結構な量があったため断念
　今後、使用する機会、リクエストや学ぶ機会があったら追記もしくは別記事で投稿します。

## 注意事項

<font color="Red">**※多人数で開発している場合すでにプッシュしているコミットを改変すると大変カオスなことになるので、 絶対に使用しないでください（どうしても使用する場合は自己責任でお願いします）**</font>

## git rebase

### 基本情報

git rebaseは歴史の元を改変するコマンドです（と教わった）
具体的には下図のような歴史があったとして...(a)

```
(a)
      A---B---C topic
     /
D---E---F---G master
```

↑を下記のコマンド(のどちらか)を実施することで

```
git rebase master
git rebase master topic
```

こんな感じ↓に変更します。...(b)

```
(b)
              A'---B'---C' topic
             /
D---E---F---G master
```

もちろん、(a)の段階で両ブランチをプッシュしていた場合は、
コミットAに対しての親が変わるわけでpush時に--forceオプションが必要になるわけですが、、、
基本コミットのみの場合は問題ないと思います。
（もちろん場合によってはコンフリクトが発生する）

### アップストリームブランチに変更が含まれている場合

対象のコミットはスキップされます。
具体的には以下

```
(c)
      A---B---C topic
     /
D---E---A'---F master
```

↑(c)をrebaseすると↓(d)になる。

```
(d)
               B'---C' topic
              /
D---E---A'---F master
```

## git rebase オプション一覧

### git rebase -i (--interactive)

過去のコミットをまとめたり編集できたりします。

`git rebase -i HEAD~4`

上記コマンドを入力することで、HEADから4つまでのコミットが表示されます。下図(e)

```
(e)
PS C:\work\hogehoge> git rebase -i HEAD~4
pick 3ba3a2e commit comment 1
pick 6ef356e commit comment 2
pick d84a911 commit comment 3
pick 9a34966 commit comment 4

# Rebase f492a4e..9a34966 onto f492a4e (4 commands)
#
# Commands:
# Note that empty commits are commented out
#
# However, if you remove everything, the rebase will be aborted.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# d, drop = remove commit
# x, exec = run command (the rest of the line) using shell
# f, fixup = like "squash", but discard this commit's log message
# s, squash = use commit, but meld into previous commit
# e, edit = use commit, but stop for amending
# r, reword = use commit, but edit the commit message
# p, pick = use commit
```

では、以下より git rebase -i を行った後のオプションについてメモしていきます。
※再度書きますが、多人数で開発している場合すでにプッシュしているコミットを改変(git push --force)すると大変カオスなことになるので、 絶対に使用しないでください（どうしても使用する場合は自己責任でお願いします）

#### squash-過去のコミットをまとめる場合

今回は9a34966 とd84a911 の変更をまとめます。
(e)の最後にオプションが表示されているとおり、squash(もしくは"s")を使用します
1.(e)で表示された画面の上4行を下記のように修正する...(f)
※squash=sだけを記述してもOK

```
(f)
pick 3ba3a2e commit comment 1
pick 6ef356e commit comment 2
pick d84a911 commit comment 3
squash 9a34966 commit comment 4
```

2.(f)のように修正後保存してエディタを閉じると、コミットを統合するためのエディタが開きます...(g)

```
(g)
# This is a combination of 2 commits.
# The first commit's message is:

commit comment 3

# This is the commit message #2:

commit comment 4

# Please enter the commit message for your changes. Lines starting

# with '#' will be ignored, and an empty message aborts the commit.
#
# ....
```

3.(g)を下記のように変更し、保存して終了するとコミットがまとまります。...(h)

```
(h)
new commit message

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# ....
```

もし、上記の段階でコミット「d84a911 commit comment 3」をプッシュしていた場合は
新しくpushするときに-fオプションが必要になります。

#### fixup-過去のコミットをまとめる場合...その2

過去のコミットをまとめる場合で、コミットメッセージの変更が必要ない場合はfixupを使用します。
(e)を下記のように変更して...(i)、保存して終了すると、コミットがまとめられます。
※fixup=fのみでもOK

```
(i)
pick 3ba3a2e commit comment 1
pick 6ef356e commit comment 2
pick d84a911 commit comment 3
fixup 9a34966 commit comment 4
```

上記でfixupした場合のgit logは、以下のようになります...(j)

```
(j)
git log --oneline
3ba3a2e commit comment 1
6ef356e commit comment 2
32sedv1 commit comment 3
```

#### drop-過去のコミットを削除する場合

過去のコミットを削除する場合はdropを使います。
具体的には(e)を下記のように変更して...(k)、保存して終了すると、コミットが削除されます。
※drop=dのみでもOK

```
(k)
pick 3ba3a2e commit comment 1
pick 6ef356e commit comment 2
pick d84a911 commit comment 3
drop 9a34966 commit comment 4
```

#### exec

申し訳ありませんが、使ったこと無い上に参考記事を読んでもピンと来なかったため、
割愛させていただきます。
`run command (the rest of the line) using shell`
↑とありますので、shellを使ってGitの歴史を改変する模様？？

- 参考記事

  - [git-rebaseのexecを使ってstashした内容を履歴に埋め込む](http://qiita.com/yuku_t/items/fed530acb97385ecbeee)
  - [interactive-rebaseのexecを使ってコミット毎に単語を置換する](http://qiita.com/yuku_t/items/0e82d438375b0010f5ea)

#### edit-コミットを修正する(rebaseを一時停止する)

過去のコミットを修正する場合はeditを使います。
具体的には(e)を下記のように変更して...(l)、
保存して終了すると、対象の箇所でgitのrebaseが一時停止します...(m)
※edit=eのみでもOK

```
(l)
pick 3ba3a2e commit comment 1
pick 6ef356e commit comment 2
edit d84a911 commit comment 3
pick 9a34966 commit comment 4
```

```
(m)
warning: stopped at ff4f7f8... update schedule
You can amend the commit now, with

  git commit --amend

Once you are satisfied with your changes, run

  git rebase --continue
```

ここでgit statusを行うと下記のように表示されます。...(n)

```
(n)
PS C:\work\hogehoge> git status
interactive rebase in progress; onto f49e33e
Last commands done (3 commands done):
   pick 6ef356e commit comment 2
   edit d84a911 commit comment 3
  (see more in file .git/rebase-merge/done)
Next command to do (1 remaining command):
   pick 9a34966 commit comment 4
  (use "git rebase --edit-todo" to view and edit)
You are currently editing a commit while rebasing branch 'dev' on 'f49e33e'.
  (use "git commit --amend" to amend the current commit)
  (use "git rebase --continue" once you are satisfied with your changes)
```

ここで、add していないファイルをadd してコミットするなどしてコミットを改変した後に
`git rebase --continue`をしてあげるとeditが完了します。

※二つ前のコミットメッセージなどを編集する場合は(n)の段階で
`git commit --amend -m "new commit message"`
などここでamendしてあげるとOKです。

#### reword-コミットメッセージをまとめて変更する

過去のコミットのコミットメッセージを変更する場合はrewordを使用します
具体的には(e)を下記のように変更して...(o)、
保存して終了すると、対象の箇所でコミットメッセージが表示される...(p)ので変更して保存します。
※reward=rのみでもOK

```
(o)
pick 3ba3a2e commit comment 1
pick 6ef356e commit comment 2
edit d84a911 commit comment 3
reward 9a34966 commit comment 4
```

```
(p)
commit comment 4

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# ...
```

#### pick

(e)にもあるように初期表示されます。
`# p, pick = use commit`とあるように、このコミットを使います的な意味合いかと...

### git rebase --continue

rebaseを続行します。
もしrebase中で無い場合は、
`No rebase in progress?`のメッセージが表示されます。

### git rebase --skip

rebaseをスキップします。
主にコンフリクト解消時に、1つ前のコミットとの違いがなくなってしまった場合などに使用します。

### git rebase --abort

rebaseを中断します。
gitのリポジトリは`git rebase`を実行する前に戻ります。

### git rebase --edit-todo

rebaseのtodoリストを編集します。
使用したことが無いので、詳しくはわかりませんが、下記が参考になるかと。
<http://kledgeb.blogspot.jp/2015/09/ubuntu-git-376-todogit-rebase.html>

### git rebase --onto
ブランチをそのまま移動させます。
イメージは下記になります。(q)→(r)
※topicブランチをnextからmasterに移動させた場合

```
(q)
    o---o---o---o---o  master
         \
          o---o---o---o---o  next
                           \
                            o---o---o  topic
```

↑これ(q)が、↓こう(r)なる。

```
(r)
    o---o---o---o---o  master
        |            \
        |             o'--o'--o'  topic
         \
          o---o---o---o---o  next
```


## 参考サイト様

<https://git-scm.com/docs/git-rebase>
[rebase -i でコミットをまとめる](http://qiita.com/takke/items/3400b55becfd72769214)
<http://tkengo.github.io/blog/2013/05/16/git-rebase-reference/>
<http://sota1235.hatenablog.com/entry/2015/12/27/200739>
他、本文中に記載をしたサイト様