PR

どうしてgit pull –forceは存在しないのか?

プログラミング
記事内に広告が含まれています。
スポンサーリンク
この記事はこんな方におすすめ
  • Githubを使ってコード管理している
  • ローカルブランチをリモートブランチの内容で上書きしたい
  • 普段git pullを魔法のように使っている

 先日、複数人で一つのブランチにコミットをしていて、ふと「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されていたときに、その情報をローカルにも持って行きたいときに使用します。

例えば、他の人が知らぬ間にプルリクをあるブランチAから出していて、その人のブランチAを使ってコードをビルドして動作確認してみたい時、いきなりローカルの環境で”git checkout ブランチA”としても、ブランチAは存在しないと怒られます。
なので、git fetchしてリモートリポジトリに存在するブランチAの情報をローカルに持ってきてからgit checkout ブランチAを実行する。そんな時に使うイメージです。

git fetchはただリモートの情報を持ってくるだけなので何か問題が起きるということはありません。なので強制的に実行するようなオプションは不必要です。

git merge

 git mergeは今いるブランチに、指定したブランチを統合するコマンドです。

 git pull の中ではgit merge FETCH_HEADが実行されています。

 つまり、git fetchで取得したブランチの先頭のコミットIDを現在のブランチにマージします。

 このgit mergeは実行時に問題が発生する場合があります。

 それはコンフリクトです。

コンフリクトとは簡単に言えば、同じファイルに対してAの変更を取り入れるのかBの変更を取り入れるべきか分からなくなる状態です。

 例えば、別なブランチを統合する際に異なるコミット間で同じファイルを変更していると、どちらのコミットの変更を採用するのかを選択する必要があります

 つまり、強制的に実行するというオプションがありません

 どちらの変更を採用するかの選択が必要です。

git push –forceについて

 git pull –forceは無いのか?と疑問に思うのは、

 その反対のようなイメージの“git push –force”が存在するからだと思います。

 git pushはローカルの変更をリモートリポジトリに反映するときに使います。

 更新のコマンドであり、オプションとして更新内容を通知するので、さきほどのgit pullとは異なっていて、更新内容が明確です。

 コンフリクトが起きていても、–forceオプションをつけることで、強制的に指定した更新内容で上書きできます。

git push –forceは通常、複数人で開発している時に、PRを出した後などに実行することはあまり推奨されていません。

 これは、コミット履歴が強制的に上書きされるので、以前のコミット履歴が参照できなくなるからです。

 他の人が参照していた場合は、変更内容が分からなくなるので、あまり推奨されていません。

 しかし、PRをドラフトの段階で行っている段階など、まだ他人に見せる想定でない段階では問題ないと思います。

 また、PRの後でもコミットをキレイにまとめるために、複数のコミットをfixupした後にforce pushされる事例もよく見るので、注意しながら使うというのが良いと思います。

なぜgit pull –forceはないのか

 ここまで書いた内容から分かるように、git pullはfetchとmergeという強制する意味の無いコマンドから成ります。

 –forceというオプションは、何か問題があっても「とにかくこうして下さい」という要求が明確でないとつけられません。

 なので、git pullは–forceオプションがつけられません。

force pushについてWebで調べると、避けるべきという話がよくあります。
確かにそれは私も納得できます。
皆が共有しているリモートリポジトリを強制的に上書きしてしまうと、他の方の作業を消してしまう危険もあります。

 以上の説明も十分納得できるのですが、

 なぜ、”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します。

 git resetのhardオプションは作業ディレクトリ、HEAD、インデックスを指定したコミットにリセットするので、fetchで取得してきたブランチのHEADの状態にすることになります。

もし作業ディレクトリに変更を加えていた場合は消されるので注意が必要です。

git resetはバージョン管理されているtrackedファイルに影響するので、管理されていないuntrackedなファイルには影響はありません。

なので、untrackedなファイルが、このresetコマンドで消えたりはしません。

まとめ

 最近気になった、gitのforceオプションについて書いてみました。

 gitのコマンドは普段何気なく使っていますが、考えてみると奥深いなと感じました。

 最後に書いたgit resetもきちんと理解しておく必要があるなと気づきました。。

 皆さんの参考になれば嬉しいです。

 以上、最後までお読みいただきありがとうございました。

コメント

タイトルとURLをコピーしました