Chiroru's Diary

日々の学びをちょこちょこメモしていきます

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さんのまとめがわかりやすかったです🙆‍♀️