Deep in Deep

プログラミング勉強中。遠回りですが、ゆっくり深く学んでいきたい。

コンフリクトの解消

コンフリクト?

チームで開発しているリポジトリにプルリクエストを投げたら、コンフリクトが出ているからMergeできないよ!という旨のメッセージが出ることがある。

コンフリクト(conflict)というと、日本語で「対立」とか「紛争」っていう意味があるみたい。

その意味の通り、これまでにmerge済みの最新バージョンのコードと今回プルリクエストを投げている部分のコードに対立が生じているという意味。

実際にコードを見たほうがわかりやすいかもしれない。

↓ こちらがmainブランチのコード

  6 import { Game } from "./components/Game";
  7 import { app } from './reducers';
  8 import { GameContainer } from './containers'
  9 import './index.css';
 10 // コンフリクトのテスト1

↓ そして、今回mainブランチに向けてPRを飛ばそうと思ったconflict_testブランチのコードがこちら

  6 import { Game } from "./components/Game";
  7 import { app } from './reducers';
  8 import { GameContainer } from './containers'
  9 import './index.css';
 10 // コンフリクトのテスト2

二つのコードを見比べてもらうとわかるかと思うが、同じ10行目に、「// コンフリクトのテスト1」と「// コンフリクトのテスト2」という、それぞれ異なるコメントが入っている。

こうなってしまうと、それぞれのコードがぶつかってしまい、どちらを優先してくっつけたらいいんだい?という状態になってしまう。

これは至極単純な例ではあるが、コンフリクトはこんな感じにして起こる。

Ruby on Railsなどでは新しくモデルにカラムを追加するなどして、マイグレーションを行うとdb/schema.rbが自動で更新されるが、このような自動で更新されてしまうファイルなどでもコンフリクトは起きやすく、また、自動生成されるファイルであるため手動で書き直したりすることは避けたい。

そういった場合はやや方法が複雑になる(コンフリクトしたschema.rbをきれいにマージする手順←チェリー本の著者、伊藤先生がとってもわかりやすい記事を書いてくださっていました!)が、今回は上記のような、至極単純なコンフリクトが起こった際にどのようにしてコンフリクトを解消するのかをメモしておこうと思う。

コンフリクト、どう直す?

ここでは上で例として示したコンフリクトを解消する方法について書いていく。

登場するブランチ

  • main
  • conflict_test(現在こちらのブランチにて作業中。以下、作業ブランチとして説明する)

作業しているファイル

  • src/index.js

PR先のブランチを最新の状態にする

git checkout mainでmainブランチに切り替える。

続いて、git pull origin mainでリモートリポジトリの最新の更新分をローカルリポジトリにpullする

これで、mainブランチは最新の状態となった。

作業ブランチに戻る

git checkout conflict_testで作業ブランチに戻る。

mainブランチの内容を作業ブランチにmerge

git branchコマンド等で、現在いるブランチが作業ブランチであることが確認する。

git merge mainとすると、mainブランチの内容を作業ブランチにmergeできる。

しかし、今回はコンフリクトが発生しているため、「はい、mergeできましたよ」とはならない。

Auto-merging src/index.js
CONFLICT (content): Merge conflict in src/index.js
Automatic merge failed; fix conflicts and then commit the result.

このようなエラー分がでるはず。簡単に訳すと、「コンフリクトが起きているから、自動mergeできなかったよ」という感じか。

テキストエディタで当該ファイルを開くと

  6 import { Game } from "./components/Game";
  7 import { app } from './reducers';
  8 import { GameContainer } from './containers'
  9 import './index.css';
 10 <<<<<<< HEAD
 11  // コンフリクトのテスト2
 12 =======
 13  // コンフリクトのテスト1
 14 >>>>>>> conflict

という風に表示されている。

コンフリクトしている部分を正しい内容に修正してあげる

先ほどのコンフリクト部分は以下のように修正した。

  6 import { Game } from "./components/Game";
  7 import { app } from './reducers';
  8 import { GameContainer } from './containers'
  9 import './index.css';
 10  // コンフリクトのテスト1
 11  // コンフリクトのテスト2

修正したらgit add src/index.jsでステージにあげる。

この段階で、git statusをしてみると

On branch conflict_test
All conflicts fixed but you are still merging.
  (use "git commit" to conclude merge)

Changes to be committed:
        modified:   src/index.js

無事、コンフリクトは解消されたようです。まだmergeされていないので、commitせよ、と言われているので、

git commit -m 'コンフリクトを解消'というようにして、commitし、リモートリポジトリにpushする。これでPRのページを見てもらうと、おそらくコンフリクトは解消されており、 mergeできるようになっているはず。

さいごに

以上、簡単なコンフリクトを解消する方法でした。

実際に困るのは途中でも書いたような、フレームワークなどによって自動生成される部分のコンフリクトかもしれない。上記で紹介した、伊藤先生の記事が本当にわかりやすく、自分もかなり助けられました。

また、特定のコミットを引っこ抜いてくる、cherry-pickという技もあるので、こちらも併せて読んでおくといいかもしれません!

【Git】cherry-pickで解決🍒