先日、複数人で一つのブランチにコミットをしていて、ふと「git pull –forceってないのか」と疑問に思いました。
git pullはあまりGithubでのコード管理について理解していない段階では、

なんとなくリモートにある情報を
ローカルに持ってくるコマンド
という風に理解されている方もいらっしゃると思います。
実際、git pullを実行すると、リモートリポジトリにしか存在しないブランチの情報がローカルに反映されたりします。
しかし正確には、git pullはgit fetchとgit mergeをまとめたコマンドです。
今回の記事では、このことと関連させて、
なぜ強制的にpullを実行させるイメージの“git pull –force”というコマンドがないのか
について考えてみたいと思います。
git pullはfetchとmerge
git pullというコマンドは、git fetchとgit mergeをまとめたコマンドです。
git fetch
git fetchは、簡単に言えばリモートリポジトリにある情報をローカルに持ってくるコマンドです。
作業ディレクトリやローカルブランチには変更を加えず、ただローカルに持っているリモートリポジトリの情報を更新するだけです。
なので、リモートリポジトリ上で新しいブランチが作成された時や、
新しいコミットがPushされていたときに、その情報をローカルにも持って行きたいときに使用します。
git merge
git mergeは今いるブランチに、指定したブランチを統合するコマンドです。
git pull の中ではgit merge FETCH_HEADが実行されています。
つまり、git fetchで取得したブランチの先頭のコミットIDを現在のブランチにマージします。
このgit mergeは実行時に問題が発生する場合があります。
それはコンフリクトです。
例えば、別なブランチを統合する際に異なるコミット間で同じファイルを変更していると、どちらのコミットの変更を採用するのかを選択する必要があります。
つまり、強制的に実行するというオプションがありません。
どちらの変更を採用するかの選択が必要です。
git push –forceについて
git pull –forceは無いのか?と疑問に思うのは、
その反対のようなイメージの“git push –force”が存在するからだと思います。
git pushはローカルの変更をリモートリポジトリに反映するときに使います。
更新のコマンドであり、オプションとして更新内容を通知するので、さきほどのgit pullとは異なっていて、更新内容が明確です。
コンフリクトが起きていても、–forceオプションをつけることで、強制的に指定した更新内容で上書きできます。
これは、コミット履歴が強制的に上書きされるので、以前のコミット履歴が参照できなくなるからです。
他の人が参照していた場合は、変更内容が分からなくなるので、あまり推奨されていません。
しかし、PRをドラフトの段階で行っている段階など、まだ他人に見せる想定でない段階では問題ないと思います。
また、PRの後でもコミットをキレイにまとめるために、複数のコミットをfixupした後にforce pushされる事例もよく見るので、注意しながら使うというのが良いと思います。
なぜgit pull –forceはないのか
ここまで書いた内容から分かるように、git pullはfetchとmergeという強制する意味の無いコマンドから成ります。
–forceというオプションは、何か問題があっても「とにかくこうして下さい」という要求が明確でないとつけられません。
なので、git pullは–forceオプションがつけられません。
以上の説明も十分納得できるのですが、
なぜ、”git pull –force”的な自分だけに影響のありそうなコマンドが存在しないのでしょうか?
しかし、それでもデフォルトではコマンドが存在しないのには何か訳がありそうです。
以下に、私の考えを書きます。
私のGithubのコード管理のイメージですが、次のようなイメージです。

Remoteには皆が参照するoriginという領域があり、Localにはそのコピーのoriginの領域と作業をするLocalの領域があります。
LocalのLocal領域にはコードを編集したりステージングする環境があり、変更をリモートに反映する(pushする)ときは直接Remoteのorigin領域に行きます。
pushする際、RemoteのoriginとLocalの間は不整合が起きる可能性があるので、forceオプションが必要です。
一方、その反対のイメージのコマンドはfetchですが、fetchが更新しに行く、ローカルのorigin領域はRemoteからしか更新されないので、常にforceオプションがつけられているようなイメージです。

Localにあるoriginの領域が挟まっていることによって、”git pull –force”的なコマンドが存在しないというのが私の理解です。
ローカルのブランチをリモートで上書きたいときどうするか
では、ローカルブランチ(branch_A)の内容をリモートリポジトリにある内容で上書きするにはどうすればいいのかですが、次のようにコマンドを実行します。
$ git fetch
$ git reset --hard origin/branch_A
git fetchでリモートの情報を取ってきた後、リモートのブランチの情報でgit resetします。
git resetのhardオプションは作業ディレクトリ、HEAD、インデックスを指定したコミットにリセットするので、fetchで取得してきたブランチのHEADの状態にすることになります。
なので、untrackedなファイルが、このresetコマンドで消えたりはしません。
まとめ
最近気になった、gitのforceオプションについて書いてみました。
gitのコマンドは普段何気なく使っていますが、考えてみると奥深いなと感じました。
最後に書いたgit resetもきちんと理解しておく必要があるなと気づきました。。
皆さんの参考になれば嬉しいです。
以上、最後までお読みいただきありがとうございました。
コメント