読者です 読者をやめる 読者になる 読者になる

DRYな備忘録

Don't Repeat Yourself.

git rebase -i はやっぱりイケてる件【git】【rebase 】【iオプション】

はじめに

※ 以前git rebase -i して何か失敗して痛い目にあったりした

※ これ。git rebase master に失敗した模様のとき【git】

※ でも最近ちゃんとgitを理解し出したので再挑戦したらgit rebase -i がやっぱりイケてる

前提

※ .zshrc (.bash_profile?) にて、gitのデフォルトエディタを設定しときます

.zshrc

export GIT_EDITOR=vim

※ あと、念のためローカルbranchは新しく切って試します

% git branch
* develop
  master

% git branch rebase_test
% git branch
  rebase_test
* develop
  master
% git checkout rebase_test

※ ぜんぜん関係無いけど、tigあるとちょっと幸せかも

そもそもgit rebase って何

そもそもgit rebase とは、今あるブランチの根元を設定しなおす、というコマンドっぽい。なるほど、baseを再構築するのでrebaseね。具体的には以下みたいな感じ。

たとえばcommit0までを反映しているブランチmasterから、ある時点でdevelopブランチ切って、開発を進めてcommit1, commit2, commit3, commit4を加えたとする。しかしその間誰かがmasterブランチにcommit5を加えていたら、masterブランチとdevelopブランチはお互いに知らないcommitを持っていて、コンフリクトがあり得、安易にmergeなど出来ない可能性がある。図1

f:id:otiai10:20121110001758p:plain

図1

 

なので「commit5まで知っているmasterブランチから切られたdevelopブランチ」にしたいことがある。これをやるコマンドが、

% git checkout develop #developブランチに移動

% git rebase master

これで、自分がいるdevelopブランチが、今参照したmasterブランチのHEADを根元としたブランチにrebaseされたということになる。図2

f:id:otiai10:20121110002042p:plain

図2

それってつまり、

% git branch
* develop
  master
  
% git log
COMMIT4
COMMIT3
COMMIT2
COMMIT1
COMMIT0

%git checkout master
% git log
COMMIT5
COMMIT0

% git branch devleop_new
% git checkout develop_new
% git branch
  develop
* develop_new
  master

% git log
COMMIT5
COMMIT0

% git cherry-pick COMMIT1
% git cherry-pick COMMIT2
% git cherry-pick COMMIT3
% git cherry-pick COMMIT4

% git branch
  develop
* develop_new
  master

% git log
COMMIT4
COMMIT3
COMMIT2
COMMIT1
COMMIT5
COMMIT0

ということになるのかな

で、-i って何

iオプションというのは、--interactiveの略で「対話的」という意味です。対話的に現在のブランチの元およびコミットログを構築し直してくれるです。「どこがどー対話的やねん」と思って分からなかったのですが、どこがどー対話的なのかが味噌だったようです。

どこがどー対話的かというと、、、

  1. git rebase -i HEAD~n というコマンドで「HEADからn個のコミットを構築し直すよ」と伝える
  2. git が、伝票を出してくるので、それを編集して終了(提出)する
  3. git が、提出された伝票を元に、ひとつひとつのコミットごとに「で、具体的に何すんの?」という態度で待ち構える
  4. コミットログを編集したりして rebase --continueする
  5. これ何回かやってrebase成功!
という感じ。まー雰囲気はこんな感じなので、以下で具体例

では、

ブランチに乗ってるcommitを確認します

% git branch
* develop
  master

% git log | vim -

その結果

f:id:otiai10:20121110004006p:plain

こんな感じ。これは別にtigで代用してもいいよ。

たとえばこれの、

「最近から三つのcommitをひとつにまとめてコミットログを変える」ことをしてみようと思ったら

1. git rebase -i HEAD~n というコマンドで「HEADからn個のコミットを構築し直すよ」と伝える

% git rebase -i HEAD~3

その結果

f:id:otiai10:20121110004253p:plain

これがその "伝票"

2. git が、伝票を出してくるので、それを編集して終了(提出)する

なので、vimで開かれてる伝票を編集する。2行目のコミットと3行目のコミットを1行目のコミットにまとめたいので、2行目と3行目に青字で書かれてる「pick」を「squash」に書き換える。書き換えたら、いつものvimのように、:wpで伝票を閉じましょう!

f:id:otiai10:20121110004709p:plain

3. git が、提出された伝票を元に、ひとつひとつのコミットごとに「で、具体的に何すんの?」という態度で待ち構える

f:id:otiai10:20121110004820p:plain

今回は三つのコミットをまとめてコミットログを書き換えたいので、

f:id:otiai10:20121110005241p:plain

として:wq

4. コミットログを編集したりして rebase --continueする

今回はsquashしたいだけなので、ここは無いのかな。

5. rebase成功!

今回はこれで成功

f:id:otiai10:20121110010055p:plain

よくやる使い方

よくやる、って言っても最近やっとちゃんと使いこなせるようになったのでアレですけど

  1. リモートのmasterをローカルのmasterにpull => ローカルのmasterを最新にする
  2. ローカルのmasterからhoge_tempブランチを切って開発進める
  3. 進めてるうちにmasterに変更があったりするので、その気付いたらローカルのmasterに反映させて、hoge_tempをrebase masterする => hoge_tempブランチの根元が常に最新のmasterになる
  4. 何度もhoge_tempにローカルcommitを重ねる
  5. 完成したら、もっかいhoge_tempをrebaseする
  6. 最後に、hoge_tempに溜まったコミットを rebase -i で改ざんする

するとどうなるかってーと

githubのネットワーク図でこんな感じになる

f:id:otiai10:20121110012624p:plain

これってつまり

「すげーこのひとサクッとブランチ切ってバチッと開発してシュッとマージしてプロジェクトcloseさせてるじゃーん」って見た目。

でもじつはこんな感じ。

f:id:otiai10:20121110013549p:plain

じつは結構前からブランチ切ってたし、コミット単位もめちゃくちゃで汚いんだけど、rebaseして最新のmasterがが根元かのように改ざんして、ローカルコミットもrebase -i  でsquashとかしちゃって綺麗にしてるというワケさ。

雑感

この方が安全だし、コミットログも見やすいし、万が一不具合発覚して切り戻しが必要になったときもやり易いし。まーあと、他人とリモートでコンフリクトしないように。

 

 

Gitがこわい

Gitがこわい

 
入門git

入門git