LoginSignup
1
0

More than 1 year has passed since last update.

初めて git rebase してみた

Last updated at Posted at 2022-05-01

はじめに

こちらの記事では、今まで GitHub Desktop に頼り切っており、最近漸く Git のコマンドに手を出した私が、rebase について勉強したこと・実際に rebase を実施した結果をメモとして残します。

rebase とは

Git の歴史を改変するコマンド

  • 改変例
    • コミットメッセージを変更(reword)
    • コミットを修正(edit)
    • コミットを前のコミットとまとめて、コミットメッセージを変更(squash)

メリット

rebase コマンドのオプションの一例

$ rebase -i

  • rebase を開始
  • 各コミットに対して、どのコマンド(pick、reword、edit、squash など)を実行するか選択

$ rebase --continue

  • rebase を継続
  • 使用例
    1. rebase 時にコンフリクトが発生し、rebase が中断
    2. コンフリクトを解決
    3. add と commit を実行し、次に作るべきコミットを生成
    4. $ rebase --continue を実行し、rebase を再開

$ rebase --abort

  • rebase を中止
  • $ rebase -i の実行前に戻る

rebase するときの注意点

push 済みのブランチをリベースしない(特に、複数人で1つのブランチを共有して開発を進めている場合)

試しに rebase してみる

こちらの記事がとても腹落ちしたので、記事の内容を自分で再現してみます。

rabase するための準備

準備の完成形

ex1-1.png

各ブランチとコミットの作成

main ブランチ

  • root コミットを作成
    • $ echo "# rebase_test" > README.md
  • push する

main から feat/1/機能A ブランチを作成

  • c_1 コミットを作成
    • $ echo "func_A_1" > feat_A.md
  • c_2 コミットを作成
    • $ echo "call feat_A" > call.md
  • push する

main から feat/2/機能B ブランチを作成

  • c_3 コミットを作成
    • $ echo "func_B_1" > feat_B.md
    • $ echo "call feat_B" > call.md
  • push する

feat/1/機能Amain にマージ

  • c_4 コミットを作成
    • feat/1/機能A からプルリクエストを作成し、main にマージ

rabase してみる

  1. feat/2/機能B に移動し、$ git fetch$ git rebase -i main を実行
    • 今回は pick を選択して終了
  2. HEADfeat/2/機能B でコンフリクトが発生
    • call.md を修正
    • 今回は call feat_Acall feat_B の両方を残す
  3. c_5 コミットを作成
    • ここまでで、何かに失敗してしまっていたら、$ git rebase --abort を実行して1に戻る
  4. git rebase --continue を実行して rebase を再開し、rebase を完了させる
  5. 1 ~ 4 の結果を確認
    ex1-2.png

テスト(今回はスキップ)

実際の開発では、機能Aが存在する状態で機能Bが正常に動作するか、確認する必要があると思います。

完成した feat/2/機能B をマージしてもらう

push で失敗してみる

feat/2/機能B を push したいところですが、実際にはエラーが発生すると思います。
これは、push 済みのブランチをリベースしているからです。
このエラーが発生する原因について、もう少し説明します。

まず、現状では、下の画像のようにリモートの feat/2/機能B は、ローカルの origin/feat/2/機能B と同じコミットを指しています。
また、push では、リモートの feat/2/機能B を fast-forward merge で更新します。
しかし、下の画像から分かるように、リモートの feat/2/機能B が指すコミット(今回は、ローカルの origin/feat/2/機能B が指すコミットと同義)から、新しく追加したい feat/2/機能B が指すコミットまでは、親→子の順で辿ることができません。そのため、push に失敗します。
このような状況では、force push を実行する必要があります。
ex1-2.png

force push してみる

push に --force-with-lease を追加して実行します。
下の画像のように、ローカルの feat/2/機能B と ローカルの origin/feat/2/機能B が指すコミットが一致すると思います。
ex1-3.png

feat/2/機能B からプルリクエストを作成してマージしてもらう

c_6 コミットを作成すると、下の画像のような結果になると思います。
確かに歴史が綺麗。
ex1-4.png

実際に rebase するときに気になったこと

rebase により発生したコンフリクトを解決した後の $ git diff の表示結果

予想していた表示結果

  • リベース先のブランチのインデックスと、コンフリクト解決後のワーキングツリーの差分を表示
  • diff --git a/XXXXX.xxx b/XXXXX.xxx

実際の表示結果

  • コンフリクトを解決するために適用した全ての修正を表示
  • diff --cc XXXXX.xxx
    • -cc とは?

調べて分かったこと

  • rebase によるマージを実行したとき、このような表示になる
    • 参考:Combined diff format
      • -cc$ git diff でマージを表示するときのデフォルトのオプション
      • 結合された差分を生成
  • リベース先のブランチの最新のコミット(インデックス)と、コンフリクト解決後の結果(ワーキングツリー)の差分を表示

    • 上を実現するためには、$ git diff HEAD^ HEAD を実行

おわりに

この記事では、最近漸く Git のコマンドに手を出した私が、rebase について勉強したこと・実際に rebase を実施した結果をメモとして残しました。
間違い等があれば、ご指摘いだけると助かります。
初めて git rebase してみる人の参考になればと思います。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0