今までなんとなく、fetchしてmergeするのを一気にやってくれるのがpullという理解で、詳しく調べたことがありませんでした。
改めて調べてみたのでまとめます。
前提知識
fetch, merge, pullを理解する上で欠かせないのが以下の言葉です。
- リモートリポジトリ: GitHubなどリモート上に存在するリポジトリ
-
ローカルリポジトリ: PC上に存在するリポジトリ
-
リモート追跡ブランチ:
origin/mainなど、リモートリポジトリを追跡するブランチ - ローカルブランチ:
mainなど、ローカル上で実際に扱うブランチ
-
リモート追跡ブランチ:
すこしわかりにくいのがリモート追跡ブランチですね。
これはリモートブランチの状態をとりあえずローカルに持ってきている場所というような理解であっていると思います。
ここで大事なのは、リモート追跡ブランチはあくまでローカルにあるブランチであることです。
fetch
fetchとは、リモートリポジトリのブランチの状態をリモート追跡ブランチに反映させる操作です。
リモートの最新の状態をローカルでも見れるようにするイメージです。
Gitはリモートリポジトリとリアルタイムで同期するわけではないので、最新の状態を見るためには毎回git fetchをする必要があります。
fetchをしただけではローカルブランチ(mainなど)の状態は何も変わりません。
merge
mergeは異なるブランチの変更を自分のブランチに取り込む操作で、Pull requestを作ってマージする、あれと同じです。
リモート追跡ブランチとの関係に限って説明すると、リモート追跡ブランチをローカルブランチに取り込む操作です。
取り込み方で以下の2つの方法があります。
fast-forwardマージ
リモート追跡ブランチの状態をローカルブランチに取り込む場合、通常はfast-forward(マージコミットを作成しない)でマージします。
図示するとこのような感じです。

origin/mainの方がaとbというコミット分、ローカルのmainよりも進んでいるとき、fast-forwardマージではmainにaとbのコミットを取り込みます。
ここでマージコミットは作らないので、mainブランチは単にaとbのコミットを積み上げたようになります。
non-fast-forwardマージ
mainブランチは基本的にローカルで変更を加えることがないと思うので、fast-forwardでマージできます。
しかしfeatureブランチなどの作業ブランチで、複数人で一つのブランチを共有して開発などしている場合、origin/feature/hogeとfeature/hogeで異なる履歴(コミット)が積み上がってしまうことがあると思います。
この場合、コンフリクトが発生しなくとも、fast-forwardでマージすることはできません。

このように、マージコミットを作ってマージするのがnon-fast-forwardマージです。
pull
pullは冒頭で言ったようにfetch + mergeです。

もう少し詳しく見ると、mergeの方法でいくつか挙動が異なります。
- 通常(特に何も設定していない場合)
-
fast-forwardマージを試す - できなければ
non-fast-forwardマージをする
-
-
--ff-onlyオプションを付けた場合-
fast-forwardマージを試す - できなければマージしないでエラー
-
-
--no-ffオプションを付けた場合-
non-fast-forwardマージでマージする
-
おわりに
改めてGitでよく使うfetch, merge, pullについてまとめました。
私はGit初心者だった頃、git pullしたときにマージコンフリクトが発生してマージしようとし始めたらpullするのをやめてreset --hardで強制移動するなどしていたのですが、今回調べてみて、--ff-onlyオプションを付けていれば良かったんだなと学びがありました。(今はそもそもpullをほとんど使わないのですが…)
特にmerge部分の違いについて理解できていると、pullしたときの挙動で焦らないで済むと思います。




