LoginSignup
47
40

More than 5 years have passed since last update.

git rebase して git push -f すると何が起こるか

Last updated at Posted at 2014-05-26

git rebase して git push -f すると何が起こるか

禁じ手の git push -f だけど、
「コミット履歴がなんかぶっ壊れて直せなくなるから駄目」くらいの認識しかしてなかったので、
rebaseからの流れで中身がどう変化するのかちょっと調べてみた。

検証用にリポジトリを準備

bash
cd /tmp
(mkdir repos.git && cd repos.git && git init --bare)

git clone repos.git work && cd work

ブランチ作ってみる

bash
# masterブランチ
git commit --allow-empty -m "init"
git commit --allow-empty -m "first"
git commit --allow-empty -m "second"
git push origin master

# ブランチb1を分岐
git checkout -b b1
git commit --allow-empty -m "third"
git push origin b1

echo "(1)--------------------------------------------"
git show-branch -a --sha1-name
git merge-base master b1
echo "--------------------------------------------(1)"

みたいにやると、ブランチの履歴はこう。

output
(1)--------------------------------------------
* [b1] third
 ! [master] second
  ! [origin/b1] third
   ! [origin/master] second
----
* +  [0ed34a9] third
*+++ [b06e7f4] second
b06e7f45ea0aed4650b763d7c88d6e57d1966967
--------------------------------------------(1)

b06e7f4のコミットから分岐してるのがわかる。

親ブランチを壊す

とりあえず履歴を壊せればいいので、
紙面の都合上ここではgit rebaseじゃなくてgit resetで。

bash
git checkout master
git reset --hard HEAD^^
git commit --allow-empty -m "force1"
git commit --allow-empty -m "force2"
git push -f origin master

echo "(2)--------------------------------------------"
git show-branch -a --sha1-name
git merge-base master b1
echo "--------------------------------------------(2)"

すると、

output
(2)--------------------------------------------
! [b1] third
 * [master] force2
  ! [origin/b1] third
   ! [origin/master] force2
----
 * + [5031219] force2
 * + [0b24ee6] force1
+ +  [0ed34a9] third
+ +  [b06e7f4] second
+ +  [246fe02] first
+*++ [07fe638] init
07fe6387710019bcbd90b8a3c458f6a218522ede
--------------------------------------------(2)

b1ブランチの分岐元が 07fe638まで遡る。

まぁ、そりゃそうだ。
元々のb1の分岐元のb06e7f4がmasterブランチ上から消えてしまったのだから。

この状態で子ブランチでrebaseすると

bash
git checkout b1
git rebase origin/master

echo "(3)--------------------------------------------"
git show-branch -a --sha1-name
git merge-base master b1
echo "--------------------------------------------(3)"
output
(3)--------------------------------------------
* [b1] force2
 ! [master] force2
  ! [origin/b1] third
   ! [origin/master] force2
----
  +  [0ed34a9] third
  +  [b06e7f4] second
  +  [246fe02] first
*+ + [5031219] force2
*+ + [0b24ee6] force1
*+++ [07fe638] init
50312197fff7a0cd77809b56772ff66e2b14f46a
--------------------------------------------(3)

分岐元ごと壊した親の方に追従してしまう。

壊す前の親ブランチからの、一連のコミットは全て無かったことになる。
246fe02 b06e7f4 はもとより、b1ブランチでコミットした 0ed34a9 まで消えてしまう。

じゃあmergeだとどうなるか

bash
git reset --hard ORIG_HEAD  # rebase前に戻す
git merge origin/master

echo "(4)--------------------------------------------"
git show-branch -a --sha1-name
git merge-base master b1
echo "--------------------------------------------(4)"

とすると、

output
(4)--------------------------------------------
* [b1] Merge branch 'master' into b1
 ! [master] force2
  ! [origin/b1] third
   ! [origin/master] force2
----
-    [6b02716] Merge branch 'master' into b1
*+ + [5031219] force2
*+ + [0b24ee6] force1
* +  [0ed34a9] third
* +  [b06e7f4] second
* +  [246fe02] first
*+++ [07fe638] init
50312197fff7a0cd77809b56772ff66e2b14f46a
--------------------------------------------(4)

今度は消えたはずの246fe02b06e7f4が復活してしまっている。
0ed34a9246fe02b06e7f4からの流れのコミットなので、
mergeで0ed34a9だけ取り出す、みたいなことはできない。

どうしてもやるなら、git rebase origin/masterで(3)の状態にしてから、
git cherry-pick 0ed34a9みたいにやらないとならない。

結局

たいていこういうケースって、
「親ブランチが更新されたので、追従しておこうかな」なパターンで

bash
git checkout 子ブランチ
git rebase 親ブランチ

とかすると思う。
で、親ブランチが壊されてた時に悲惨なことになる。

本質的には、

公開済みのブランチにforce pushするのがまずい

というより、

公開済みのブランチに対してコミット履歴書き換えることがまずい

ってこと。

まぁ、rebase した時点で fast foward じゃなくなるから、
結局 force じゃないと push できないんだけど。

47
40
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
47
40