なぜrebase後に強制プッシュをする必要があるのか


rebase後の強制プッシュ

以下のようにfeatureブランチをmasterブランチにrebaseして最新のmasterを取り込むことはよくあると思います。

$ git pull --rebase origin master

で、それをリモートにpushしようとすると、fast-forwardできないからpushできないよ!と怒られます。

$ git push origin feature1
To github.com:hitochan777/test.git
 ! [rejected]        feature1 -> feature1 (non-fast-forward)
error: failed to push some refs to '[email protected]:hitochan777/test.git'
(以下略)

これを解決するためには強制プッシュをするとうまくいきます。

git push origin feature1 -f

なぜ強制プッシュする必要があるのか

ポイントはpushはリモートブランチがfast-forwardできることを想定しているということです。
どういうことか、具体的に例を考えてみましょう。

origin/feature1がC3を、feature1がC5を指している状態を考えます。

C1--C2--C3          <- origin/feature1 (C5までfast-forwardできる)
         \        
          C4--C5    <- feature1

この場合、origin/feature1はC5までポインターを進められる(fast-forward)ので、
強制プッシュをしなくてもプッシュは成功します。

ではfeature1ブランチをorigin/masterブランチにrebaseしている場合はどうでしょうか。

C1--C2--C3--C6--C7  <- origin/master
         \        
          C4--C5    <- feature1, origin/feature1

rebase後は次のようになります。origin/masterにrebaseをするとfeature1origin/masterの共通の祖先からorigin/masterが指すコミットまでのコミットのコピーが作られます。下図ではC4', C5'としています。

C1--C2--C3-------C6--C7--C4'--C5'   <- feature1
         \  
          C4--C5                    <- origin/feature1

この場合origin/feature1のポインターを前に進めて(fast-forwardして)feature1と
同じコミットを指すようにすることはできないので、エラーが出てしまいます。

おまけ

feature1を複数人で共有している場合を考えます。
他のメンバーによってorigin/feature1がアップデートされたとしましょう。(C8の追加)
ここで、このことに気づかずfeature1を強制プッシュしてしまうと、リモートブランチは上書きされ、C8は消えてしまいます。

C1--C2--C3-------C6--C7--C4'--C5'   <- feature1
         \  
          C4--C5--C8                <- origin/feature1

こういったことが起きないように --force-with-leaseというオプションがあります。