日々常々

ふつうのプログラマがあたりまえにしたいこと。

gitのrebaseとremoteとbranchと

gitでrebaseは呼吸するようにするものらしいですが、remote絡むと若干息苦しくなる。

$ git log --oneline --graph --decorate
* caec1ad (HEAD, origin/master, origin/HEAD, master) add e
* cb99644 add d
* 912a264 add c
* 14bb339 add b
* b33a46a add a
* 5fd8be9 init

push済みのこれをうっかりrebaseする。

$ git log --oneline --graph --decorate --all
* f853362 (HEAD, master) add d
* 4163b31 add e
| * caec1ad (origin/master, origin/HEAD) add e
| * cb99644 add d
|/  
* 912a264 add c
* 14bb339 add b
* b33a46a add a
* 5fd8be9 init

こうなるともう別歴史になってしまう……。
やらないのがいいんですが、やっちゃった場合はーどうするんが良いんでしょ?

  1. 素直に取り込む
  2. えいやっと上書きする

(1) 素直に取り込む
同じ歴史を持っていればpush出来ます。なのでmergeしてやりましょう。

$ git merge origin/master
Merge made by the 'recursive' strategy.
$ git log --oneline --graph --decorate --all
*   50b7f86 (HEAD, master) Merge remote-tracking branch 'origin/master'
|\  
| * caec1ad (origin/master, origin/HEAD) add e
| * cb99644 add d
* | f853362 add d
* | 4163b31 add e
|/  
* 912a264 add c
* 14bb339 add b
* b33a46a add a
* 5fd8be9 init

わあ素直。git賢いから特にファイルを追加したりせずに繋げてくれます。後は素直にpushするだけですね。
……でもこれはやりたいことじゃない。なんかごちゃごちゃするし、こんな歴史は誰得なんよ。と言うことで。

(2) えいやっと上書きする
push -f ですね。多人数開発では御法度。個人だとよくやる。複数人でもたまにやる。大丈夫ばれてない。

$ git push -f
(略)
 + caec1ad...f853362 master -> master (forced update)
$ git log --oneline --graph --decorate --all
* f853362 (HEAD, origin/master, origin/HEAD, master) add d
* 4163b31 add e
* 912a264 add c
| * bde3740 (origin/b1, b1) add y
| * 1403611 add x
|/  
* 14bb339 add b
* b33a46a add a
* 5fd8be9 init

ブランチでやってるとき

masterからbranch生やして追随するためにrebaseすることも多々あります。

$ git log --oneline --graph --all
* f853362 add d
* 4163b31 add e
* 912a264 add c
| * bde3740 add y
| * 1403611 add x
|/  
* 14bb339 add b
* b33a46a add a
* 5fd8be9 init

このx,yですね。rebaseしてx,yをdの後ろに続けるわけです。

$ git log --oneline --graph
* 918becd add y
* c2d3585 add x
* f853362 add d
* 4163b31 add e
* 912a264 add c
* 14bb339 add b
* b33a46a add a
* 5fd8be9 init

でもx,yのbranch(b1)をpushしちゃってたら?

$ git log --oneline --graph --decorate --all
* 918becd (HEAD, b1) add y
* c2d3585 add x
* f853362 (origin/master, origin/HEAD, master) add d
* 4163b31 add e
* 912a264 add c
| * bde3740 (origin/b1) add y
| * 1403611 add x
|/  
* 14bb339 add b
* b33a46a add a
* 5fd8be9 init

こうなっちゃう。もちろんpushしようとしたら蹴られる。

$ git push
To (remote)
 ! [rejected]        b1 -> b1 (non-fast-forward)
error: failed to push some refs to '(remote)'
To prevent you from losing history, non-fast-forward updates were rejected
Merge the remote changes (e.g. 'git pull') before pushing again.  See the
'Note about fast-forwards' section of 'git push --help' for details.

もうこれは push -f でいいと思う……。

rebaseする予定があるならpushしない。それかpush -fでやる?
push -fしても取り込む側は普通にpullできたりする。でもマージコミット出来るアレになる。実害はないからいいんだけど、気になるなら一旦手元のブランチ破棄して、リモートのをcheckoutし直したらいいと思う。


pullった側はこんななってるから……

$ git log --oneline --graph --all --decorate
*   bb69e06 (HEAD, b1) Merge branch 'b1' of (remote) into b1
|\  
| * 4b25a53 (origin/b1) add y
| * ca28378 add x
| * e9eab92 (origin/master, master) add g
* | a8b376c add y
* | e519e1b add x
|/  
* 6d01ded add f
* f853362 add d

b1 を branch -D で消して、再度 b1 を checkout -b とかで作る。

$ git checkout master
Switched to branch 'master'
$ git branch -D b1
Deleted branch b1 (was bb69e06).
$ git checkout b1
Branch b1 set up to track remote branch b1 from origin.
Switched to a new branch 'b1'
$ git log --oneline --graph --all --decorate
* 4b25a53 (HEAD, origin/b1, b1) add y
* ca28378 add x
* e9eab92 (origin/master, master) add g
* 6d01ded add f
* f853362 add d

これで世界は平和になった。

まとめ

コミットグラフのいい見せ方がわかりません。絵描くのも面倒ですし。