簡単な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
です。
コミットにはAuthorDate
とCommitDate
が管理されています。
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でいうdevelop
やmaster
ブランチ(複数人のコミットが入ったブランチ)で同様なことを行うと、他のメンバーのコミットをいつの間にか変えてしまうかも知れません。
なぜrebaseするの?
複数人で開発する際はこのような事象が発生すると思います。
例: rebaseしない場合
- Aさんは13時に実装を終えてコミット、プルリクエストを出した。
- Bさんは14時に実装を終えてコミット、プルリクエストを出した。
- Bさんは指摘箇所なしで、当日の夕方にマージされた。
- Aさんは指摘された箇所が多すぎて、マージできたのは翌日の朝でした。
rebaseをしないと以下のようなログの順序になります。
* Aさんのマージコミット 9:00
* Aさんの指摘取り込み用コミット(その3) 21:00
* Bさんのマージコミット 18:00
* Aさんの指摘取り込み用コミット(その2) 17:00
* Aさんの指摘取り込み用コミット(その1) 16:00
* Bさんの実装コミット 14:00
* Aさんの実装コミット 13:00
もうすでに複雑ですよね...
あとで見返した時にどこを確認すれば良いかわからなくなってしまいます。
また、指摘取り込み用コミットがいくつも入っています。
このコミットのせいでAさんのどんな実装をしたか一目ではわかりません。
例: rebaseを使った場合
- Aさんは13時に実装を終えてコミット、プルリクエストを出した。
- Bさんは14時に実装を終えてコミット、プルリクエストを出した。
- Bさんは指摘箇所なしで、当日の夕方にマージされた。
- Aさんは指摘された箇所が多すぎて、LGTMされたのは翌日の朝でした。
- Aさんはrebaseしてコミットを1つにしてから、マージしてもらった。
rebaseを使うと以下のようなログの順序になります。
* Aさんのマージコミット 9:00
* Aさんの実装コミット 13:00
* Bさんのマージコミット 18:00
* Bさんの実装コミット 14:00
綺麗になりました。
これなら、Aさんがどんな実装をしたかはAさんの実装コミット 14:00
を確認すれば良いと一目でわかります。
rebaseにはこのようなメリットがあります。
安全にrebaseを使うには
プロテクトブランチを活用しよう
GitHubやGitLabにはプロテクトブランチという機能があります。
develop
やmaster
ブランチではプッシュを許容しないように設定すれば、rebaseして壊れたブランチを間違ってプッシュしたとしてもリジェクトされるので保護されます。
自分のコミットだけが枝分かれしているときだけrebaseしよう
基本、featureブランチを作成し開発してコミットしますよね?
そうであれば、枝分かれしたコミットは自分のものだけなハズです。
こういう時だけrebaseを使うように心がけましょう。
それでも不安な場合はバックアップ用のブランチを作成してチャレンジしましょう。
sandboxなリポジトリをつかってrebaseを試して見よう
重要なリポジトリで初めてrebaseするのはやはりリスクがあります。
汚してもいいリポジトリを作成してrebaseを試してみましょう。
Git職人に付いてもらってrebaseしてみよう
隣にGit職人がいれば安心です。
まとめ
今回rebaseのメリットについてあまり触れませんでしたが、もちろんログが綺麗になるという大きなメリットがあります。
個人的には
- リモートリポジトリではmerge(プルリク)
- ローカルリポジトリではrebase(最新情報を取得)
なイメージを持っています。
ログを綺麗に保つことは後々ログを見返すと気に役立ちますので、
今後意識してGit使ってもらえれば....と思います。