kasei_sanのブログ

かせいさんのIT系のおぼえがきです。胡乱の方はnoteとtwitterへ

git rebase/merge をそろそろキチンと理解する

なんなのかと

rebase

品質を落とす、品のない振る舞いをする

git rebase

つかいみち : ブランチにmasterの変更を取り込む

例えば、こんな感じに、 master と、branch_b がある場合...

* ac7957d Add 'd' in a.txt
| * 9aa15f9 (refs/heads/branch_b) Add 'c' in b.txt
| * b129f6c Add b.txt
|/
* 3d940b6 Add 'b' in a.txt
* 26ba7e0 Add a.txt
git checkout branch_b
git rebase master

git rebase master を実行すると、masterHEAD の上に、 branch_b が乗っかる感じになる

  • ブランチ分岐後に追加された変更 ac7957d の上に、 branch_b の変更が乗っかる
* a27055b (HEAD -> refs/heads/branch_b) Add 'c' in b.txt
* 7d45f1d Add b.txt
* ac7957d (refs/heads/master) Add 'd' in a.txt
* 3d940b6 Add 'b' in a.txt
* 26ba7e0 Add a.txt

上のような状態を fast-forward な関係にある という

git rebase --interactive

つかいみち : コミットの履歴を編集する

* afe1290 (HEAD -> refs/heads/make_01_txt) Add 'aaa' on 01.txt
* cccfaee Add 01.txt
* 183848b (refs/heads/master) first commit
git rebase -i 183848b

commit番号より上のcommitについて、 git rebase --interactive を行う

pick cccfaee Add 01.txt
pick afe1290 Add 'aaa' on 01.txt

# Rebase 183848b..afe1290 onto 183848b (2 command(s))
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
  • pick commitを採用
  • reword 名前変更
  • squash 上のcommitと合体
  • fixup 上のcommitと合体(commitメッセージは捨てる)

こうする

reword cccfaee Add 01.txt
fixup afe1290 Add 'aaa' on 01.txt
  • この場合、 9f6ec01 は、上のcommitと合体
  • その上で、79f2ab0 のコミットメッセージを変更

すると、commitメッセージの変更画面が出るので変更する

* 4625879 (HEAD -> refs/heads/make_01_txt) Create 01.txt
* 183848b (refs/heads/master) first commit

すっきりした!

ちなみに

  • git rebase --interactive の時に、commitの順序の入れ替えもできるので便利です

git rebase のログを見る

git reflog
4625879 HEAD@{0}: rebase -i (finish): returning to refs/heads/make_01_txt
4625879 HEAD@{1}: rebase -i (fixup): Create 01.txt
4cdf15a HEAD@{2}: rebase -i (reword): Create 01.txt
cccfaee HEAD@{3}: cherry-pick: fast-forward
183848b HEAD@{4}: rebase -i (start): checkout 183848b
afe1290 HEAD@{5}: commit: Add 'aaa' on 01.txt
cccfaee HEAD@{6}: commit: Add 01.txt
183848b HEAD@{7}: checkout: moving from master to make_01_txt
183848b HEAD@{8}: commit (initial): first commit

git rebase を元に戻したい!!

HEAD@{0}〜HEAD@{4} までが、git rebase でやったこと

git reset --hard HEAD@{5}

直前の git rebase を戻したいのであれば

git reset --hard ORIG_HEAD

mergeやreset、rebase を行った直前の HEAD (べんり!!!!!!)

git merge

ブランチ make_01_txtmaster にマージする

* afe1290 (HEAD -> refs/heads/make_01_txt) Add 'aaa' on 01.txt
* cccfaee Add 01.txt
* 183848b (refs/heads/master) first commit

git merge --ff

fast-forward な関係ならば、マージコミット無しでマージする

git merge make_01_txt --ff
* afe1290 (HEAD -> refs/heads/master, refs/heads/make_01_txt) Add 'aaa' on 01.txt
* cccfaee Add 01.txt
* 183848b first commit

fast-forward な関係じゃない場合は?

* e838a79 (HEAD -> refs/heads/master) Add 02.txt
| * afe1290 (refs/heads/make_01_txt) Add 'aaa' on 01.txt
| * cccfaee Add 01.txt
|/
* 183848b first commit

マージコミットが作られる

*   33cd880 (HEAD -> refs/heads/master) Merge branch 'make_01_txt'
|\
| * afe1290 (refs/heads/make_01_txt) Add 'aaa' on 01.txt
| * cccfaee Add 01.txt
* | e838a79 Add 02.txt
|/
* 183848b first commit

git merge --no-ff

fast-forward な関係でも、マージコミットを作る

git merge make_01_txt --no-ff
*   4084ea5 (HEAD -> refs/heads/master) Merge branch 'make_01_txt'
|\
| * afe1290 (refs/heads/make_01_txt) Add 'aaa' on 01.txt
| * cccfaee Add 01.txt
|/
* 183848b first commit

それぞれどういう時に使うの?

この辺はプロジェクト毎のルール次第のような

  • ブランチをマージする時には、コミットを1個にsquashしてからマージするとか
  • 必ず、マージコミットを作る為に --no-ff をつけるとか

git cherry-pick

特定のcommitをカレントブランチの先頭に乗っける

こんな場合

* ac7957d (HEAD -> refs/heads/master) Add 'd' in a.txt
| * 9aa15f9 (refs/heads/branch_b) Add 'c' in b.txt
| * b129f6c Add b.txt
|/
* 3d940b6 Add 'b' in a.txt
* 26ba7e0 Add a.txt

branch_b9aa15f9 を、masterHEAD にのせる

git checkout master
git cherry-pick b129f6c
* f16c177 (HEAD -> refs/heads/master) Add b.txt
* ac7957d Add 'd' in a.txt
| * 9aa15f9 (refs/heads/branch_b) Add 'c' in b.txt
| * b129f6c Add b.txt
|/
* 3d940b6 Add 'b' in a.txt
* 26ba7e0 Add a.txt

参考

次回予告

git pull --rebase を理解する

(おまけ)

本記事の git log は以下のオプションで出力してます

git log --graph --oneline --branches --decorate=full

エイリアスにしておくと便利かも