git pullの–rebase違いって?図を交えて説明します!
origin: git pull と git pull –rebase の違いって?図を交えて説明します! – KRAY Inc.
はじめに
こんにちは、クレイの亀井です。ここ最近一気に気温が上がりましたね。顔に重点的に汗をかくタイプの私には憂鬱な季節がやってまいりましたさて、今月正式リリースしました(!) DocBase プロジェクトではクレイ外部のデザイナーの方と一緒に開発しています。SourceTree で Git を使っている方で、軽いデザイン修正などは弊社の Rails プロジェクトに直接手を加えてプルリクエストを送ってくれます。
こちらのデザイナーさんに「プルリクエストを送る際は、作業ブランチで git pull –rebase origin master してから送ってもらえますか?」とお願いすると「pull はわかるんですけど、この –rebase ってなんですか?これつけると何が変わるんですか?」と質問がきたのです。
作業ブランチで git pull –rebase origin master してほしい理由はなるべく最新の master に近い状態でプルリクエストを送ってほしいからなのですが、デザイナーさんは作業ブランチに最新の master を取り込むのに git pull origin master を使っていました。
なぜ git pull –rebase する必要があるのか、改めて説明しようとするととても難しく、口頭では説明しきれなくて自分自身の力不足を感じました。。
もっと段階を踏んで理解する必要がある
業務の中で Git のことが理解できるようになってからわかったことですが、git pull –rebase を理解するには、なんとfetch、merge、rebase の3つの Git コマンドをちゃんと理解する必要があります。ヒャー!
この記事では図を交えながら、感覚的に理解できることを目指して説明していきたいと思います。
長くなりますが、ファイトです!
さっそく説明といきたいところですが!今後の説明に使うので、まずはローカルリポジトリとリモートリポジトリについてさらっとおさらいします。
ローカルリポジトリとリモートリポジトリ
Git にはローカルリポジトリとリモートリポジトリという2種類のリポジトリがあります。ローカルリポジトリは作業者のPCにあるリポジトリ、リモートリポジトリは共用のサーバに設置されているリポジトリです。
ローカルリポジトリで作業して、適宜リモートリポジトリにプッシュするというのがGitを使った開発の大まかな流れになります。
Git のローカルリポジトリには
* ワーキングツリー
* インデックス
* ローカルブランチ
* リモート追跡ブランチ
があります。
ローカルリポジトリの中に「リモート追跡ブランチ」というのがいますが、これはリモートリポジトリのコピーです。これだけ言われてもなんのこっちゃと思う方もいらっしゃるかもしれませんが、これからの説明の肝になるので注意しておいてください!
ではではざっくり Git の構造を図にしたところで、fetch、merge、rebase、pull & pull –rebase の説明をしていきたいと思います。
git fetch とは
git fetch とは「リモートのコピーをローカルにダウンロードする」コマンドです。ローカルのリモート追跡ブランチ(リモートのコピー置き場)にコピーしてくるだけなのでワーキングツリーには何も影響を及ぼしません。
git fetch
上記のコマンドを実行した場合の動きを、図に表してみます。
- リモートのコピーちょうだいって言う
- ローカルとの差分を見つつ、リモートリポジトリが変更や新規ブランチをリモート追跡ブランチに渡す
- リモート追跡ブランチが更新される
ちなみにこの「リモート追跡ブランチ」とはあくまでリモートの状態を示すもので、ユーザー自身がこのブランチの内容を変更することはできません。(内容を見たりとかはできます)また、リモートの状態は自動的に反映されるわけではなく、git fetchコマンドで明示的に行う必要があります。※SourceTreeには自動フェッチ機能があるそうです。(使ったことないのであまりよくは知らないです、すみません)
このリモート追跡ブランチの内容を見たり、ローカルブランチにマージしたりするときは
git merge origin/master
というふうに、origin/ブランチ名 の形でアクセスできます。
git merge とは
git merge とは、今いるブランチに別のブランチの内容を結合させるコマンドです。リモートリポジトリにあるブランチや、ローカルの別のブランチをマージしたいときに使います。
ローカルブランチがこんなかんじであるとしまして(master や hige はブランチ名です)
hige ブランチに master ブランチの内容をマージしたいときは、以下のコマンドを使います。
git checkout hige // 現在higeブランチにいるなら不要 git merge master
これで hige ブランチの中に master ブランチの内容が取り込まれます。また、マージした場合は「マージコミット」というコミットが積まれます。
また、マージには Fast-Forword と Non-Fast-Forword という2種類のマージがありますが、ここでは割愛します。わかりやすく解説してくださっている方がいるので、そちらを参照してください。
【git】分かりやすく!mergeは「合流」、rebaseは「付け替え」!
リモートリポジトリのブランチをマージする場合
ちなみに、上記のコマンドはローカルリポジトリの master ブランチをマージするものなので、リモートの master ブランチをマージするには以下のコマンドを実行します。
git fetch // 先にリモートの状態をリモート追跡ブランチにコピー git merge origin/master // リモートのコピーからマージ
まず git fetch でリモートにリモート追跡ブランチを合わせたあと、
git merge origin/master で master に origin/master の差分が取り込まれます。
origin/ブランチ名 はリモート追跡ブランチのことでしたね。(fetch のセクション参照)git merge コマンドでリモートのブランチをマージしたい場合は、いったんリモートのコピーをとってきて、そのコピーをマージするという手順になります。
git rebase とは
git rebase とは、merge と同じく今いるブランチに別のブランチの内容を取り込むコマンドですが、merge とは動きが違います。
前提は、git merge の時と同じこちらの状態です。
この状態で、hige ブランチを master にリベースしたい場合、以下のようになります。
git checkout hige // higeブランチにいるなら不要 git rebase master
まず、hige ブランチにあるふたつのコミットを一時的に保存、hige ブランチをリベース先のブランチに git reset –hard します。
リセット後の hige ブランチの上に一時保存していたコミットを乗せます。
hige ブランチの猫には口がなかったのに、リベース後は口がついてますね。差分がコンフリクトしていなければ Git がうまいことくっつけてくれます。(コンフリクトしたときはエディタを開いて手動で直しましょう。。。)
ただ、マージと違ってリベースの場合はコミットを新しく作りなおすので、リベース前とはコミット自身の id と親の id が変わります。master の変更を取り込んで別物になったので当然ですね。
親のidが変わると、すでにリモートリポジトリにブランチをプッシュしていた場合、リモートにあるブランチの親の id が一致せずプッシュできなくなり(rejectされ)ます。(git push -f で強制的にプッシュできますが、よくわかっていないうちは使わないほうがいいでしょう。)
マージは現在のブランチの上に他のブランチの内容を取り込むというかんじですが、リベースは取り込みたいブランチの上に今のブランチの内容を乗せるといったかんじですね。
git pull とは
git pull とは、さきほど説明したgit fetch と git merge をいっぺんに実行するコマンドのことです。
git merge のセクションで言っていた以下のコマンド。
git fetch // 先にリモートの状態をリモート追跡ブランチにコピー git merge origin/master // リモートのコピーからマージ
こちらのコマンドは、
git pull origin master
とイコールです。フェッチしてからマージするのMENDOIと思うようになったら使いどきです。
git pull –rebase とは
ついにお待ちかねの git pull –rebase です。
–rebase は git pull コマンドのオプションです。git pull は fetch + merge ですが、–rebase オプションをつけると fetch + rebaseとして実行します。
git pull --rebase origin master
git pull と git pull –rebase の違い
冒頭で以下の様なやりとりをデザイナーさんとしていますが、
「ブランチを git pull –rebase origin master してからプルリクエストを送ってもらえますか?」
なぜこのようなお願いをしたかといいますと、–rebase オプションをつけてプルしたほうがマージコミットが作られない&履歴が綺麗になるからです。
このときに –rebase オプションをつけなくても「作業ブランチに最新の master を取り込む」という目的は達成できるのですが、上記の理由があるため作業ブランチに master を取り込むときは git pull –rebase でお願いしていました。
ただこちらはいろいろ議論がありまして、(「ログが綺麗になるとしても履歴を書き換えるべきではない」など)いきなりリベースをするとチームの規約に反する場合もありますので、そのあたりは事前にチームの人に確認してください。
また、マージするのに比べてリベースするほうが差分がコンフリクトしたときに修正するのが面倒なときがあったりするので、あまり Git に慣れていないうちは git merge でリモートリポジトリの差分を取り込むほうがいいかもしれません。
最後に
git pull と git pull –rebase の違いを説明するだけなのに、なんだかとても長くなってしまいました。そして、書くのに時間がかかりすぎて冒頭で質問してくれたデザイナーさんはすでに DocBase チームから離れていました。。。Kさん、見ていらっしゃいますでしょうか…?
Git はコミットさえしていれば間違えてコミットやブランチを消してしまっても元に戻せる方法があるので、テスト用のブランチを切って試してみるのもいいかもしれません元に戻しやすいのは Git のいいところのひとつですね!
ここまで読んでくださった方、ありがとうございました!この記事を読んで Git の理解が少しでも進めば幸いです。