Help us understand the problem. What is going on with this article?

rebaseをちゃんと理解して使えるようになろう!

簡単なrebaseの説明

簡単なレベルあれば、サル先生のGit入門で十分なのでこちらを参照ください。
サル先生のGit入門: rebase

mergeとの違い

rebase、mergeはどちらもブランチを統合するコマンドになります。
実際の作業ベースではどんな違いがあるのでしょうか?

コミットが改変される?

merge

  • 単純な統合なので、基本はマージコミット1つ追加される
aブランチ
        * e750862 Dコミット
        * 2f6c625 Cコミット
        * cf890e0 Bコミット
        * 8721950 Aコミット
に
bブランチ
        * f23vd43 Eコミット
        * 2f6c625 Cコミット
        * cf890e0 Bコミット
        * 8721950 Aコミット
をマージすると

⬇️

aブランチ(fast-forward場合)
        * f23vd43 Eコミット
        * e750862 Dコミット
        * 2f6c625 Cコミット
        * cf890e0 Bコミット
        * 8721950 Aコミット
または
aブランチ(non-fast-forward場合)
        * fas34vs Fコミット(マージコミット)
        |\ 
        | * f23vd43 Eコミット
        |/ 
        * e750862 Dコミット
        * 2f6c625 Cコミット
        * cf890e0 Bコミット
        * 8721950 Aコミット

rebase

  • rebaseはコミットが改変されることがある。
bブランチ
        * f23vd43 Eコミット
        * 2f6c625 Cコミット
        * cf890e0 Bコミット
        * 8721950 Aコミット
を
aブランチ
        * e750862 Dコミット
        * 2f6c625 Cコミット
        * cf890e0 Bコミット
        * 8721950 Aコミット
でリベースすると

⬇️

bブランチ
        * 34jfil3 Eコミット
        * e750862 Dコミット
        * 2f6c625 Cコミット
        * cf890e0 Bコミット
        * 8721950 Aコミット

rebaseの場合はEコミットのハッシュ値が書き換わってしまいました。
コミットメッセージが一緒なだけで、別物のコミットと認識されてしまいます。

rebaseしたら何が変わる?

先ほどはハッシュ値が変わることを説明しました。
変わるのは他にもあります。

CommitDateです。
コミットにはAuthorDateCommitDateが管理されています。

git logで出力される方はAuthorDateになります。

rebaseのリスク

もとに戻せないかも

ブランチ統合しようとしたら、なんかよくわからない状況になってしまった。(初心者あるある)
mergeはコミットが追加されるだけなので、もし失敗した場合はresetを使って元に戻すことができます。
しかし、rebaseはコミットが改変されてしまうので、バックアップを取っておかないと、取り返しのつかないことになってしまうかも知れません...

これが、理由でrebaseを使わないGit初心者がちらほら...

rebaseした後は強制プッシュ必須

一度プッシュしたブランチでrebaseを行うとコミットが改変されるため、再度プッシュできなくなります。
rebase前のコミットとrebase後のコミットは異なるものと判断されるためnon-fast-forwardな状態になってしまいます。

git push -fは危険なのでgit push --force-with-leaseを使って再度プッシュする癖をつけましょう。

他の開発者のコミットも改変させてしまうかも

git flowでいうdevelopmasterブランチ(複数人のコミットが入ったブランチ)で同様なことを行うと、他のメンバーのコミットをいつの間にか変えてしまうかも知れません。

なぜrebaseするの?

複数人で開発する際はこのような事象が発生すると思います。
例: rebaseしない場合
1. Aさんは13時に実装を終えてコミット、プルリクエストを出した。
2. Bさんは14時に実装を終えてコミット、プルリクエストを出した。
3. Bさんは指摘箇所なしで、当日の夕方にマージされた。
4. Aさんは指摘された箇所が多すぎて、マージできたのは翌日の朝でした。

rebaseをしないと以下のようなログの順序になります。

* Aさんのマージコミット 9:00
* Aさんの指摘取り込み用コミット(その3) 21:00
* Bさんのマージコミット 18:00
* Aさんの指摘取り込み用コミット(その2) 17:00
* Aさんの指摘取り込み用コミット(その1) 16:00
* Bさんの実装コミット 15:00
* Aさんの実装コミット 14:00

もうすでに複雑ですよね...
あとで見返した時にどこを確認すれば良いかわからなくなってしまいます。
また、指摘取り込み用コミットがいくつも入っています。
このコミットのせいでAさんのどんな実装をしたか一目ではわかりません。

例: rebaseを使った場合
1. Aさんは13時に実装を終えてコミット、プルリクエストを出した。
2. Bさんは14時に実装を終えてコミット、プルリクエストを出した。
3. Bさんは指摘箇所なしで、当日の夕方にマージされた。
4. Aさんは指摘された箇所が多すぎて、LGTMされたのは翌日の朝でした。
5. Aさんはrebaseしてコミットを1つにしてから、マージしてもらった。

rebaseを使うと以下のようなログの順序になります。

* Aさんのマージコミット 9:00
* Aさんの実装コミット 14:00
* Bさんのマージコミット 18:00
* Bさんの実装コミット 15:00

綺麗になりました。
これなら、Aさんがどんな実装をしたかはAさんの実装コミット 14:00を確認すれば良いと一目でわかります。

rebaseにはこのようなメリットがあります。

安全にrebaseを使うには

プロテクトブランチを活用しよう

GitHubやGitLabにはプロテクトブランチという機能があります。
developmasterブランチではプッシュを許容しないように設定すれば、rebaseして壊れたブランチを間違ってプッシュしたとしてもリジェクトされるので保護されます。

自分のコミットだけが枝分かれしているときだけrebaseしよう

基本、featureブランチを作成し開発してコミットしますよね?
そうであれば、枝分かれしたコミットは自分のものだけなハズです。

こういう時だけrebaseを使うように心がけましょう。
それでも不安な場合はバックアップ用のブランチを作成してチャレンジしましょう。

sandboxなリポジトリをつかってrebaseを試して見よう

重要なリポジトリで初めてrebaseするのはやはりリスクがあります。
汚してもいいリポジトリを作成してrebaseを試してみましょう。

Git職人に付いてもらってrebaseしてみよう

隣にGit職人がいれば安心です。

まとめ

今回rebaseのメリットについてあまり触れませんでしたが、もちろんログが綺麗になるという大きなメリットがあります。

個人的には

  • リモートリポジトリではmerge(プルリク)
  • ローカルリポジトリではrebase(最新情報を取得)

なイメージを持っています。

ログを綺麗に保つことは後々ログを見返すと気に役立ちますので、
今後意識してGit使ってもらえれば....と思います。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away