箱のプログラミング日記。

渋谷の自社開発企業でRails書いてます。

コンフリクト解消した後にpush/pullができない【Git】

f:id:y_hakoiri:20191102121842j:plain

GitのCUI操作もだいぶ慣れてきたけどコンフリクトが絡むとお手上げ〜な人向けにまとめる。

そもそもどうやってコンフリクトを解消したか

コンフリクトは解消できたけど、その後に色々やろうとしたらつまづいたってこと多々あるはず。

まずはじめに大前提として、コンフリクトを解消するにはリモートで解消する方法とローカルで解消する方法がある。

リモートで解消

こちらは割と簡単。

GitHub上でいつも見るプルリクのマージのところが

f:id:y_hakoiri:20200128224315p:plain

グレーっぽくなって押せなくなっているので、「Resolve conflicts」を押してブラウザ上で展開されるエディタの中で修正する。

こうすると結構楽にコンフリクトを解消できるので、そのまますぐにレビューをお願いしたりすることができる。

ローカルで修正を加えてもプッシュできない

で、無事にブラウザのGitHub上でコンフリクトを解消できたのでレビューに出したところ、指摘された箇所がいくつかあったので、再度ローカルで修正を加え、コミットして、リモートにプッシュ!!、、、ができない。

コンフリクトはさっき解消したのになぁ〜。何故なのか。

原因:リモートとローカルの歴史(HEAD)が違うから

先ほどブラウザ上でコンフリクトを解消したことで、リモートブランチには新しく「コンフリクトを解消したコミット」が発生した。

ブラウザでコンフリクトを解消するということはつまり、リモートのみコミットを進めることと同じ

結果、リモートブランチとHEADが異なっているローカルブランチで新しいコミットを作ってしまうと、それをリモートにプッシュしようとしても当然「歴史が違いますよー」と怒られてしまう。

解決:コンフリクトを解消したらリモートをpull

ブラウザ上でコンフリクトを解消した場合は、リモートブランチのみコミットが一つ進んだ状態になっているので、再度ブランチに修正を加える場合は先にリモートの内容をローカルにpullしてくる必要がある。

つまり、リモートでローカルを上書きするために強制プルを行う。

一気に強制pullはできないので、とりあえず変更をfetchする

git fetch origin ブランチ名

その後強制pull

git reset --hard origin/ブランチ名

これでしっかりローカルブランチも「コンフリクト解消済みのリモートブランチ」と同じ状態になったので、あとはいつも通りローカルで修正を加えて、コミット、プッシュするだけ。

ローカルで解消

次にローカルで解消する方法。

先ほどはGitHubのブラウザ上で直接コンフリクトを解消したが、それはせずにローカルの自分が使っているエディタ内で行う。

ローカルでもコンフリクトを発生させる

ローカルでコンフリクトを解消するためには、ローカルブランチでもリモートブランチと同じような状況(コンフリクト状態)を作ってあげる必要がある。

ここで一旦今の状況を整理

  • リモートで「これをmasterにpushするにはコンフリクトがあるからできないよー」となっている

  • 「これ」(=作業ブランチ)の状態は、リモートもローカルも一緒

(ああ、図で説明したい)

上の状況を考えると、①リモートのmasterの最新をpullしてきて、②そのローカルmasterをローカルの作業ブランチにマージしようとする と、ローカルでもコンフリクトを発生させることができるはずだ!

リモートのmasterをpull

これは特に難しいことはなく、一旦masterに移動して

git checkout master

ローカルのmasterにリモートの最新のmasterをpull

git pull

ローカルmasterをローカルの作業ブランチにマージ

で、また作業ブランチに戻って

git checkout hoge_branch

作業ブランチに、さっき最新をpullしてきたローカルのmasterをマージ

git merge master

でコンフリクト発生。(スクショなくてごめんね)

あとは自分が使ってるエディタでコンフリクトを解消して、"コンフリクト解消"みたいな感じのコミットメッセージにしてリモートにpushすればOK。

この時、作業ブランチには最新のmasterがすでに取り込まれた状態になっているけど、GitHub上で見てもmasterとの差分のみが確認できるので非常に分かりやすい。

どっちが良いのか

リモートで直接解消するのと、ローカルに取り込んでから解消するのどっちが良いのかは好みによるだろうけど、個人的にはローカルで解消する方がやりやすいなと思った。

ブラウザ上だと自分が使い慣れているエディタではないし、該当ファイルだけじゃなく全体像を見たい時もローカルの方が他のところを参照しやすいので。

ちょっとした修正ならリモートでチョチョっと直しちゃう方が楽かもしれないけど、それなりに粒の大きなコンフリクトはローカルでやるべきかなと思いました。

まとめ

  • コンフリクト解消法はリモートでやるかローカルでやるかの二択
  • リモートで解消した後に再度ブランチに修正を加える場合はローカルに強制プッシュしてから
  • ローカルでコンフリクトを解消させる場合はリモートのmasterをマージする

参考

git pull を強制し、リモートでローカルを上書きする方法 | WWWクリエイターズ