2020年8月4日火曜日

今更ながら git rebase をちゃんと使えるようにする

概要

git rebase はマージとは違いヘッドの位置を移動することであたかも別ブランチの修正をマージしたかのように見せます
rebase を使ったほうがコミットログがキレイになる場合があります

しかしなぜか rebase 後は force push しなければならなかったり結局マージと同じことをしている場合がありうまく使えていないことがありました
なので今更ながらちゃんと使い方を把握しておこうと思います

準備

まずは動作確認するための環境を作成します
リモートリポジトリは Github を使います

master ブランチ

  • touch memo
  • vim memo
a
  • git add .
  • git commit -m "first commit"
  • git push -u origin master

b1 ブランチの作成

master ブランチから作成します

  • git branch b1
  • git checkout b1
  • vim memo
a
b
  • git add .
  • git commit -m "second commit from b1"
  • git push -u origin b2

b2 ブランチの作成

このブランチは b1 ブランチから更に生やします

  • git branch b2
  • git checkout b2
  • vim memo
a
b
c
  • git add .
  • git commit -m "third commit from b2"
  • git push -u origin b2

でここまでのブランチごとのコミットの状況は以下のようになります

なのこの図は Github の「Insights」->「Network」から確認できます

b1 ブランチで修正が入る

  • git checkout b1
  • vim memo
a
b
d
  • git add .
  • git commit -m "thrid commit from b1"

b2 ブランチを b1 ブランチに rebase してみる (force push)

やりたいことは先程の b1 ブランチの修正を b2 ブランチにマージではなく rebase することでコミットログをキレイにします

  • git checkout b2
  • git rebase b1

何も考えずに上記を行うと当然ですがコンフリクトして rebase できません
エラーの内容は以下の感じです

Auto-merging memo CONFLICT (content): Merge conflict in memo error: could not apply a811605… third commit from b2 Resolve all conflicts manually, mark them as resolved with "git add/rm <conflicted_files>", then run "git rebase –continue". You can instead skip this commit: run "git rebase –skip". To abort and get back to the state before "git rebase", run "git rebase –abort". Could not apply a811605… third commit from b2

ファイルにはコンフリクト部分が記載されています

a
b
<<<<<<< HEAD
d
=======
c
>>>>>>> a811605... third commit from b2

このコンフリクトを解消した上で rebase してみます

  • vim memo
a
b
c
d
  • git add .
  • git commit
  • git rebase --continue
  • git push -u origin b1

しかし以下のようにエラーが発生します

To https://github.com/hawksnowlog/test.git ! [rejected] b2 -> b2 (non-fast-forward) error: failed to push some refs to 'https://github.com/hawksnowlog/test.git' hint: Updates were rejected because the tip of your current branch is behind hint: its remote counterpart. Integrate the remote changes (e.g. hint: 'git pull …') before pushing again. hint: See the 'Note about fast-forwards' in 'git push –help' for details.

理由は単純でリモート側のコミットログとローカルで rebase したコミットログが異なるため push できず force push を余儀なくされている感じです
ではどうするかいうと結局 force push するしかなくなります

  • git push -f origin b2

一応これでコミットログ的には b1 の続きとして b2 が作成されていることになるのでキレイにはなります
しかし force push しなければならないので権限がない場合などこれだと対応できません

force push しないようにするには

ではコンフリクトありの rebase 時に force push しないようにするにはどうすれば良いか考えます
最初はコンフリクトしない rebase なら大丈夫かなと思ったんですが結局ローカル側のコミットID が変わってしまうようなので force push が必要になります
いろいろ考えたのですが rebase -> push を force push なしでやる場合にはリモート側にまだ push していないコミットがある場合にだけ使うしかないと思います
すでにリモート側に push しているコミットがあるブランチで rebase -> push する場合には force push は必須にするしかないと思います

Tips: やり直したい場合は

リモート側に hard reset しましょう

  • git reset --hard origin/b2

まとめ

merge と rebase の使い分けは以下の通りかなと思います

  • 基本は merge
  • リモート側にまだ push していないコミットがある場合に限り rebase
  • どうしても rebase したい場合は force push を使う

0 件のコメント:

コメントを投稿