git rebase 〜初級編〜 rebaseとは？

# はじめに
Gitを使っている人の多くは、「rebase」という言葉は聞いたことがあると思いますが、「rebase」とはどういうことなのか、どうやって使うのかがよく分からない人も多くいます。また、なんとなく危険のある操作だとイメージを持っていて、使うのを戸惑う人もいるでしょう。

ここでは、gitのrebaseという機能の説明と使い方を紹介したいと思います。

# rebaseとは
rebaseを理解するには、まずは簡単な例を見ていきましょう。

![Git1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553103/f4e39b4f-b96f-b652-afdc-936e83841c99.png)

上の図のように、masterからブランチを切ってそのブランチに対していくつかコミットをしています。コミット1はブランチを切った段階でmasterの最新のコミットで、これを「merge base」と言います。（切ったブランチをmasterにマージする時にベースとなるコミットなのでmerge baseです。）

※ master以外のブランチから切った場合も同じです。merge baseのコミットが入っているブランチは一般的にbase branchの呼びます。

「rebase」はその名の通り、merge baseを設定し直す。上の図の状態からmasterブランチに新しいコミットが入った場合を考えましょう。

![Git2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553103/05ceeb76-b7ff-1eaf-84d7-aaa5b1aa41ed.png)

自分のブランチはコミット1から切られていましたが、この状態でmasterブランチにrebaseすれば、コミット5から切られたようになります。

![Git3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553103/0588f349-3e4a-ceec-b2eb-17eb9715a2ac.png)

# rebaseはいつ使うのか
「masterの状態を自分のブランチに反映したい時に使う」というふうに元々理解していた人が多いかもしれませんが、上の内容を踏まえて考えればその理屈が明確になるでしょう。

ブランチを切った時にまだなかったコミットは、rebaseによって自分のブランチの履歴に反映されます。

とはいえ、base branchの状態が進んでいるからって、必ずrebaseする必要があるというわけではありません。普段の場合、mergeだけでbase branchの最新状態と自分のコミットをそれぞれ反映してくれます。rebaseが必要になるのは、主に2つのパターンがあります。

1. base branchの最新コミットと自分のブランチのコミットがコンフリクトしている場合
2. base branchの最新コミットの内容に依存している場合

### base branchの最新コミットと自分のブランチのコミットがコンフリクトしている場合

コンフリクトしているとマージはできないので、コンフリクトを解消するためにrebaseを使います。rebaseを使わなくてもコンフリクト解消ようのコミットを打つこともできますが、rebaseを使うとコミットをし直すのできれいなコミット履歴を保てます。

### base branchの最新コミットの内容に依存している場合

もうひとつのパターンとしては、git上ではコンフリクトこそ発生しませんが、base branchの最新の内容が自分のブランチの開発で必要になる場合です。例えば新しく開発された機能を自分のブランチで使う必要がある、もしくは新しい修正を入れないと自分が開発している機能が意図通り動作しない、細かい状況は様々です。

rebaseをしなくても自分の開発を進められる、テストが通る、マージもできる、という状況であれば多分rebaseをする必要はないでしょう。

# rebaseをする方法

今までrebaseが何をしているか、どのような状況で使えばいいか、を説明してきましたので、最後に実際にrebaseする方法を紹介したいと思います。

SourceTreeやGitHub DesktopなどのGUIで簡単にrebaseできるようになっていると思いますが、コマンドラインを使う場合も特に難しいことはありません。rebaseしたいブランチをcheckoutした上で、以下のコマンドを打つだけです。

```
git rebase [new merge base]
```

`new merge base`は、コミット番号でもブランチ名でも構いません。

```
git rebase f4ba680921aa
git rebase other-branch
git rebase origin/other-branch
```

※ ブランチ名を使う場合、そのブランチの最新コミットが新しいmerge baseになります。`origin/`を指定しなければローカルにある最新コミットになりますので、rebaseする前にpullするか、安全のために`origin/`をつけるのどちらかが必要になります。

### rebaseのコンフリクト解消

何も問題なければ。上のコマンドを実行するだけでmerge baseが変更されます。

```
% git rebase master
First, rewinding head to replay your work on top of it...
Applying: Expand from 6 to 10 characters
%
```

ただ、rebaseの途中でコンフルエントが発生すると、以下のようなメッセージが出ます

```
% git rebase master
First, rewinding head to replay your work on top of it...
Applying: Expand from 6 to 10 characters
Using index info to reconstruct a base tree...
M	a.txt
Falling back to patching base and 3-way merge...
Auto-merging a.txt
CONFLICT (content): Merge conflict in a.txt
error: Failed to merge in the changes.
Patch failed at 0001 Expand from 6 to 10 characters
hint: Use 'git am --show-current-patch' to see the failed patch
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
%
```

`git status`を打つと、どのファイルがコンフリクトしているかが確認できます。

```
% git status
rebase in progress; onto 0e14386
You are currently rebasing branch 'branchA' on '0e14386'.
  (fix conflicts and then run "git rebase --continue")
  (use "git rebase --skip" to skip this patch)
  (use "git rebase --abort" to check out the original branch)

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	new file:   b.txt

Unmerged paths:
  (use "git restore --staged <file>..." to unstage)
  (use "git add <file>..." to mark resolution)
	both modified:   a.txt
```

コンフリクト解消の仕方はgitの基本なのでここで触れませんが、解消できたらrebaseを続けます

```
git rebase --continue
```

なお、なにかの理由でrebaseを途中でやめたい場合はabortできます。これで`git rebase`を打つ前の状態に戻ります。

```
git rebase --abort
```

rebase中の使えるコマンドは他にもありますが、興味があればdocsをみてください。

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

### rebaseの注意点
多くの人が「rebaseが危ない」というイメージを持っている理由としては、rebaseする時に履歴を上書きしているからでしょう。

例えば、上の例でコンフリクトが発生した場合、コンフリクト解消した内容で新しいコミットが作成されます。（前と違うコミット番号になります。）そうなると、単純にpushできなくなります。慣れていればforce pushを使ってもいいですが、不安であればrebase前の状態をバックアップするのがおすすめです。

https://qiita.com/ktroutner/items/39afd3aa2aa409362ac8

remoteでは履歴が上書きされていますが、ローカルにrebase前の状態が残っていますので、なにか問題があればロールバックできます。

### まとめ
この記事ではgit rebaseを簡単に紹介しました。rebaseを使えば、自分のブランチが派生しているコミットを違うコミットに変更することができます。実際には、コンフリクトや依存のために元々切ったブランチの最新コミットを自分のブランチに反映したい時に使います。

そして、`git rebase`コマンドの簡単な使い方も説明しました。

rebaseは奥が深いので、基本をまず覚えると色んなことが可能になります。
今度の記事では以下のトピックスについても紹介する予定ですのでご確認ください。

- git rebase 〜中級編〜 チーム開発でrebase -iを使って事故を防ぐ
- git rebase 〜上級編〜 autosquashを活用してきれいなgit logを作りましょう