要約
- git rebase時には
git push -f
することが(頻繁に)ある。- -fは怖いオプションなので一体何のためにそれが必要なのか理解して使いたい。
-
git push
はリモートがローカルをfast-forwardマージで取り込めるという前提がある。rebaseではそれを満たさなくなることが多いため、-fが必要になるになる - 「リモートがローカルをfast-forwardマージで取り込めるという前提がある。rebaseではそれを満たさなくなることが多い」を理解するためには、rebaseの仕組みとfast-forwardマージの意味を抑えると良い。
なお、この記事ではgit rebaseについては詳しく説明せず、fast-forwardについてまとめた。
rebase
A--B--C------F--G (master)
\
D--E (feature)
この状態で(= featureブランチを切ったあとにmasterに変更が加えられている状態)
$ gco feature
# git rebase master
をすると、
A--B--C------F--G (master)
\
D'--E' (feature)
# なお、rebaseをすると従来のD, Eとは異なるコミットIDが振られ直される。
# 従来のD, Eは新たにD’, E'に生まれ変わっており、そのコミットIDはもうない。
こうなる。
(ここまでの理解が怪しい場合はrebase = 付け替えということが理解できるまでggるとよい。)
この時点で重要な点は、今ローカルのfeatureブランチはリモートのfeatureブランチとは異なるログを持つということ。
A--B--C------D--E (remote/feature)
A--B--C------F--G--D'--E' (local/feature)
今この状態でローカルからリモートにgit push
すると怒られる。
なぜならば git pushはリモートがローカルをfast-forwardマージとして取り込める前提にたっているから。
結論としては、git push -f
すれば良いのだけど、理解せずに-fするのは怖いのでfast-forwardをここで理解する
fast-forwardマージ
ブランチXと、そこから切ったブランチYがあるとする。
YがXでの変更をすべて含むときに行われるマージをfast-forward(早送り)マージという。
つまり、分岐後に、元ブランチXにおいて変更がないときに行われるマージのこと
A--B--C (X)
\
D--E (Y)
このようなとき、
$ gco X
$ git merge Y
をすると、するするとブランチXの先頭がブランチYの先頭に移動する。 = 合流が行われない。
このようなマージをfast-forwardマージといい、git pushにおいてはこれができることが前提となっている
つまり
featureブランチで作業しているとmasterと差分が出るのはよくある話である。
したがって、rebaseをするとremote/localで上記のように異なるログを持つので、そのままではgit push
できない。
この場合は気にせずgit push -f
してよい。
単にremote/featureのブランチがローカルのそれによって強制上書きされるだけであるため。
安心。
ref
Git push rejected after feature branch rebase - Stack Overflow
http://stackoverflow.com/questions/8939977/git-push-rejected-after-feature-branch-rebase
ブランチの統合【ブランチ】 | サルでもわかるGit入門 〜バージョン管理を使いこなそう〜 | どこでもプロジェクト管理バックログ
http://www.backlog.jp/git-guide/stepup/stepup1_4.html