git pullは、fetchしてmergeするのと同じなのか?

はじめまして、 はおりん と申します。
phpを食べたり、Androidを食べたり、サーバの面倒を見たり、ギルザットのフォレスドンを狩ったりして毎日を過ごしています。おおむねテンション高めですが、実は寝不足によるミッドナイトハイテンションなのかもしれません。
わたくし、新しい作業branchを作るときに、
git fetch origin
してから、
git checkout -b hogehoge origin/master
ってやってたんですけど、その後手元のmasterを最新にしたいときに git pull
してたんですね。
でも、ふと疑問に思ったんです。
すでにfetchしてあるんだから、手元のorigin/masterからmasterに反映って、できるんじゃね?と。
でもなんか普通に、 git checkout master; git merge origin/master
ってやったら、マージコミットが勝手に作られそうで、超面倒だなぁ、って思ってたんです。
で、ちょっと調べてみると、git pullは使わない方が良いとか、fetchとmergeをやっているだけだとか、いろいろネットでは書かれてるじゃないですかー。
でもどれも、git pullはfetchとmergeを両方やってるだけだよ、ほら、証拠はココに。ってがっつり書かれてるサイトって見つけられなかったんですね。
と、いうわけで、本日のエンジニアブログは、 git pullの伝説 です。
伝説を語るだけでなく、真実かを検証するのです。
(ディスカバリーチャンネルの「怪しい伝説」、だいすきです。)
git pullの正体
実は git pull
はC言語で実装されていません。git-pull.shというシェルスクリプトです。
CentOSであれば /usr/libexec/git-core/git-pull
に配置されています。
guthubでのソースは こちら
256行目まで
256行目までは、現在のhashを取得したり、work treeがcleanかどうかを調べたりしているだけですので、割愛します。
fetchを実行する
257行目で、皆様お待ちかねのfetchを実行しています。
と、ここで、どうやらfetchだけでも作業ツリーのHEADが変わってしまうことがあるようです。
その場合は、
Warning: fetch updated the current branch head. Warning: fast-forwarding your working tree from Warning: commit \$orig_head.
というエラーを表示した上で、
git update-index -q --refresh git read-tree -u -m "$orig_head" "$curr_head"
を実行しています。
このコマンドの内容については、また別の機会にしましょう。
fetchしてくる前のHEADが、無い?
305行目で、orig_headが空だった場合に、
git read-tree -m -u $empty_tree $merge_head && git update-ref -m "initial pull" HEAD $merge_head "$curr_head"
を実行してexitしています。
これは、空branchをpullしてきたときのパターンでしょうか・・・
mergeのお時間
さて、ようやくここでmergeのお時間です。
rebaseの場合
rebaseの場合は328行目の中に入っていきます。
git-rebase $diffstat $strategy_args $merge_args $rebase_args $verbosity $gpg_sign_args --onto $merge_head ${oldremoteref:-$merge_head}
というコマンドが実行されるようです。
git-rebaseとは、git rebase
を実行する、実体のシェルスクリプトです。rebaseもC言語ではなくシェスルクリプトで実装されているんですね。
mergeの場合
mergeの場合には333行目の中に入っていきます。
git-merge $diffstat $no_commit $verify_signatures $edit $squash $no_ff $ff_only $log_arg $strategy_args $merge_args $verbosity $progress $gpg_sign_args \"\$merge_name\" HEAD $merge_head
とまぁ、これまたなんとも長いコードが実行されているんですが、なにも引数を付けずにpullを実行すると、
git merge "コミットの文" HEAD 0123456789abcdef0123456789abcdef01234567
というコマンドが実行されるだけなので、シンプルなmergeだと思って良いです。
結論
git pullに、何も引数を付けずに実行した場合、git fetch
してgit merge
するのと同義であると確認できました。
伝説は本当でした。