git rebaseのちょっとしたまとめ
rebase
、分かったつもりになってまたわからないを繰り返しているので少しまとめておきます。
rebaseとは
- 現在いるブランチを、rebaseしてきたブランチの先頭にくっつけます。
- この時、現在いるブランチのコミットを一瞬退避(一時保存)し、rebaseしてきたブランチに現在いるブランチを
git reset --hard
して、保存していたコミットを上にのせる仕組みです。 - リベースを行うとコミットのidが変わり、別物として扱われます。これはコミットが新しく作られるためであり、リモートリポジトリに既にpushしていた場合は、ローカルと一致しなくなってしまいpushできなくなります。
- 上記のパターンでpushがrejectされた場合、
git push -f
で対応します。 - rebaseするとその際のコミットが作られません。(mergeするとマージコミットができちゃいます。)
git rebase
rebaseはこんな感じで使えます。これはgit checkout branchB
+ git rebase branchA
を組み合わせたものです。
# branchAをbranchBにリベース(Bが先行する) $ git rebase branchA branch B
git pullとpull --rebaseについて
git pull
は以下のように、fetch + mergeです。
# リモートBからリモート追跡Bにコピー $ git fetch # リモート追跡B(=リモートBのコピー)からローカルBにマージ $ git merge origin/master # 上記二つをいっぺんに $ git pull origin master
一方git pull --rebase(←オプション)
は、fetch + rebaseをします。
$ git pull --rebase origin master
git rebase -i
git rebase --interactive。過去のコミットを編集することができます。
git rebase -i <編集したいコミットID>~
と使用します。
ちなみに間違えて編集した場合は、git reset --hard <編集前のコミットID>
で修正できました。
間違えたパターンの再現
3つのcommitを用意しました。 (1・2はpush済みで3が未push状態でした。)
$ git log --oneline 058deaf (HEAD -> practise) commit3 b100354 (origin/practise) commit2 4ceebb5 commit1
push済みでもrebase -iで編集はできます。しかしこの時間違えてcommit2を、commit1にsquashしてしまいました。
pick 4ceebb5 commit1 s b100354 commit2 pick 058deaf commit3 # Rebase bf5a488..058deaf onto bf5a488 (3 commands) # # Commands: # p, pick <commit> = use commit # r, reword <commit> = use commit, but edit the commit message # e, edit <commit> = use commit, but stop for amending # s, squash <commit> = use commit, but meld into previous commit # f, fixup <commit> = like "squash", but discard this commit's log message # x, exec <command> = run command (the rest of the line) using shell # b, break = stop here (continue rebase later with 'git rebase --continue') # d, drop <commit> = remove commit # l, label <label> = label current HEAD with a name # t, reset <label> = reset HEAD to a label
その結果こうなります。
$ git log --oneline 75df8ce (HEAD -> practise) commit3 7d51144 commit1 $ git reflog 75df8ce (HEAD -> practise) HEAD@{0}: rebase (finish): returning to refs/heads/practise 75df8ce (HEAD -> practise) HEAD@{1}: rebase (pick): commit3 7d51144 HEAD@{2}: rebase (squash): commit1 4ceebb5 HEAD@{3}: rebase (start): checkout 4ceebb5~ 058deaf HEAD@{4}: commit: commit3 b100354 (origin/practise) HEAD@{5}: commit: commit2 4ceebb5 HEAD@{6}: commit: commit1
間違えた編集をした場合、git reset --hard <編集前のコミットID>
で対応できました。今回の場合は058deaf時点に戻しました。
reset --hardを使用することでコミットが抹消しないか心配だったのですが、問題なく意図通りrebase編集前状態に戻すことができました!
$ git reset --hard 058deaf HEAD is now at 058deaf commit3 $ git reflog 058deaf (HEAD -> practise) HEAD@{0}: reset: moving to 058deaf $ git log --oneline 058deaf (HEAD -> practise) commit3 b100354 (origin/practise) commit2 4ceebb5 commit1
そして本来やりたかった、commit3をcommit2にsquashしました。
※この場合であれば、$ git commit --amend
でもOKですが今回は敢えてrebase -i使っています
$ git rebase -i 4ceebb5~ pick 4ceebb5 commit1 pick b100354 commit2 s 058deaf commit3
大丈夫そうですね。
$ git log --oneline 2a18a66 (HEAD -> practise) commit2 4ceebb5 commit1 $ git reflog 2a18a66 (HEAD -> practise) HEAD@{0}: rebase (finish): returning to refs/heads/practise 2a18a66 (HEAD -> practise) HEAD@{1}: rebase (squash): commit2 b100354 (origin/practise) HEAD@{2}: rebase (start): checkout 4ceebb5~ 058deaf HEAD@{3}: reset: moving to 058deaf 75df8ce HEAD@{4}: rebase (finish): returning to refs/heads/practise 75df8ce HEAD@{5}: rebase (pick): commit3 7d51144 HEAD@{6}: rebase (squash): commit1 4ceebb5 HEAD@{7}: rebase (start): checkout 4ceebb5~
rebaseによってcommitIDが変更されてしまっているので、このままでは普通にpushできません。
なので以下のように強制pushします。--force-with-lease
を利用すれば、他の人のコミットは維持したまま上書きすることができます。
$ git push origin ブランチ --force-with-lease
Fjordのメンターodaillyさんのまとめがわかりやすかったです🙆♀️
- 参考:コミットを整理してみよう