Git講座 その3

3レッスンの0が完了(0%)。

ブランチ操作、マージ、コンフリクト解決

コンフリクトの解決

このレッスンへのアクセス権がありません

コース内容にアクセスするには、登録またはサインインしてください。

コンフリクトの解決

  • コンフリクトは直せる
    • 手動で編集する
    • VSCodeの機能を使う
    • SourceTreeの機能を使う

コンフリクトした状態でもコミットできてしまうので、コミットする際には十分注意してください。コンフリクトが起きたら、まずはプロジェクトのメンバーや上司に確認・相談しましょう。

Git のマージを行った際に起こるコンフリクトと、その解決についてご紹介します。ここは一番難しいかもしれません。是非チャレンジなさってみてください。画面を共有します。

はい、えーと、今こちらにまた master がいるんですけども、ここから新たにブランチとして testA ブランチと testB ブランチを作り、その中でわざと衝突するようにしてマージさせてみたいと思います。ちょっとやることは謎かなと思いますので、まずは一旦、作業をご覧下さい。

ブランチ同士で衝突させる

今、私は  master ブランチにいます。ここからブランチを作りますね。まず一つが testA とします。はい、ブランチを作りました。で、もう一つブランチを作ります。testB ですね。はい。testB として作ります。はい、作りました、という形になります。はい。

testA ブランチでコミットする

で、ひとまず testA ブランチ に切り替えました。今、testA ブランチ ですね。はい、その状態でファイルを操作します。ファイルを操作したいので…そうですね、今、ここ(testフォルダの中)に三つのファイルがあります。この中の test.txt について、1行目に追加しましょうか。1行目の修正Aです…これは何でしたっけ?うーんと、testA ですね。testA からの修正ですというふうにします。

test1です。一行目の修正testAからの修正

はい。保存します。

では、この状態で Uncommitted changes が出ますね。testA ブランチで、ここの1行目に文字列が追加されたという事をコミットします。はい、コミット。「testA におけるコミット」ということでコミットを行います。はい行いました。

そうすると、今、このように「master と testB  は置いていかれて、testA だけが前に進んだ状態」ということになります。

testB ブランチでコミットする

続きまして、testB ブランチ切り替えて…testB ブランチに切り替えました。切り替えてエディターに戻ると、当然元に戻ってる状態ですね。えーと、masterと同じ状態になっています。ここで testA にやったものと同じような修正を加えてみましょう。

ここ(text.txt の一行目)ですかね~。

test1です。一行目の修正testBからの修正

とします、はい。

これで「testA  ブランチと同じ所の修正を testB ブランチでも行った」という事になります。ただ、「今、現状はブランチが分かれているので問題ありません」という状態ですね。はい、では、こちらで今度はここですね。一行目が「一行目の修正testBからの修正」となっています。そして今、自分は testB ブランチにいるのを確認して、ここでコミット行います。testB からのコミットという形で行います。はい、コミットしました。

えーと、この時点で、既に masterから進んだ testA と testB は「同じ箇所、同じ行を変更したブランチ」という事になっています。この状態でマージするとどうなるか。これが「コンフリクトを起こす所」という事になります。ちょっとやってみましょう。

testB ブランチから testA ブランチをマージする

今、自分は testB ブランチにいます。testB ブランチから testA ブランチをマージしてみたいと思います。マージをクリックして、今、testB ブランチなので testA ブランチをマージします。これでOKを押します。すると、このように、はい、ここですね。「マージの競合、作業コピーにマージの競合があったため、続行するには競合を解決する必要があります」ということで、これで OK を一旦押してみます(※Windowsの方は図1を参照)。

すると、このようになります。ここ(ステージングに未登録のファイル)ですね。今、アイコンとしては、このびっくりマークの三角が出ていて、こちらには HEAD と testA という見慣れない、書いたことがない文字列が追加されているのが分かります。ここを詳しく見ていきます。

まず、この「衝突した / 競合したよ」っていう状態のことをコンフリクトといいます。これは、ブランチ間を合体しようとした際に、同箇所もしくは同ファイルが変更されてるが故に、機械的にはそのどちらかを選択することができず、どうしようもないので、その作業者の判断を仰ぐために、一旦待っている状態という事になります。

コンフリクトを解決する

具体的にどういう風になってるかエディターで見てみましょう。ここですね。今、「現在の変更」ってことは、今、私は何から何にしたか?っていうと、testBブランチだったので、testB ブランチでは、1行目は「testB からの修正」となってましたよと、で、ただ testA 側では、同じ箇所は「testA からの修正」になってましたよ、と。で、「どちらにしたいですか?」という事を選択する場面に、画面が切り替わっています。

実は、ここまでご紹介していませんが、VSCode(Visual Studio Code) には、元々 Git 機能がありまして、その内容を明確に明示してくれています。色がちゃんと変わってますね。で、(画面の上部で)現在の変更を取り込むのか、入力側の変更を取り込むのか、両方の変更を取り込むのか、ということを選ぶことができます。実はこれは、とても良い機能で、エディターに備わっている git の特別な機能ということになります。

手動でコンフリクトした部分を直す

で、今回は、まずはこのエディターの特別な機能は使わずに、本当に、手動で直す場合はどうするか?という事についてご紹介したいと思います。まあ、こんな重なる事がないと思うんですけども、今回は(本当の修正内容が)「testC だった」という事にしましょう、実は。1行目の修正が「testC からの修正」という風にしたかったとしましょう。「その前に、(本当の修正内容は)どちらでもないので」っていう事になって、その場合は、ここ全部、文字列になってますから、不要な場所をどんどん消して行きます。

まずは、こちら側の不要な場所を全部消して、ここあれですね。必ず、この以前の修正「自分の側」と、その中央の「セパレーター」と、その下の「ブランチとしてマージする側であったもの」の修正の内容が出てきますんで、これらを全部消していきます。消して、これも消して、ここに本来の修正の内容ですね。test …、実は C からだった、としたら、こうして、これで保存する、と。

こうすると、今までの全部の修正の内容が異なってきたので、ここの部分(一行目)がこの「C」になって、これでこれ(text.txt)を上にやってコミットしていく、という内容にする事が可能です。えーと…、ちょっと分かりにくいんで、もう一回やりますね。はい。

VSCodeの機能でコンフリクトした部分を直す

また、別の方法もあります。これを「現在の変更を取り込む」。要は、これは testB ブランチでやってる B の方が正しいと、A ブランチ の方は不要だったとしたら「現在の変更を取り込む」というのクリックすると、このように、自動的に testB 側のものだけを残して削除してくれるます。このやり方もありですね。逆に「入力側の変更を取り込む」とすると、今度はこっち(testB側の修正内容)が消えてこっち(testA側の修正内容)が優先されます。このようにしてくれる、ということもできますね。はい。これはエディター側の機能です。「両方の変更を取り込む」にすると両方の内容が入るという形ですね。ビジュアルコード、えらい!…えーと、VSCode(Visual Studio Code) 素晴らしいですね(笑)。

SourceTreeでコンフリクトした部分を直す

同じことがこちら(SourceTree)からもできます。この状態で「競合してますよー」ってなった時に、ここ(ステージングに未登録のファイルにある test.txt)ですね。ここ(test.txt を右クリックで表示されたメニューの)で「競合解決」。で、「自分を解決」とか「相手を解決」っていう事で、同じように、その取り込む変更の「どちらを取り込むか?」というのを選択することができます。はい。

今回は手動で解決

で、今回は、そうですね、えーと、どうしようかな…「両方を取り込む」が面白いですか、両方取り込みましょうか(笑)。まぁ、それ、でも、あまりないですね。入力側を取り込みましょう。というか、変えときましょう。(Aを)Cにします。はい、しました。

この辺りはね、本当はプログラムの正しい方を選んでいただく事になったり、納品物に正しいものを選びますが、ここでは「testC」にしておきましょう、はい。そして、これを上に上げてコミットを押します。

そうすると、今、こちらですね。自動的にコミットがコンフリクトした場合には、コミットのメッセージが追記されます。「Merge branch ‘testA’ into testB」、マージでブランチ testA を B にマージしようとしたら、コンフリクトで test.txt の中身がコンフリクトしたよ、と。これについて解決したよ、という内容になります。ここ(コミットメッセージ)で追記いただいても結構ですし、このままでも結構です。コミットいただくと、無事にコンフリクト解決された状態で、コミットが出来上がったということになります。

コンフリクトした状態でもコミットできてしまう

で、一番問題なのは、あの今の、先ほどの状態ですね。この状態でコミットしてしまうのが一番の問題です。というのが、これって結局、文字列なんですよね。なので、このままの状態でコミットすることも可能です。実は、はい。このような状態で「なんかコンフリクトした!分かんない!でもコミットできるから、えーい!ままよ!行っちゃえ!!」ってコミットしちゃった、としましょう(笑)。そうすると、こういうコミットになります。

そうすると、全くプログラムや文章として表示して欲しいものとは関係ないものが、色々と混ざり込むことになります。そして、これは後から戻すのが非常に大変ですし、おそらくプログラムであれば壊れます。

ですので、多々あるその入門の Git の本の中では「コンフリクトが起こった場合には、一旦手を止めて分かる人に聞いてください」っていうような本もあります。ここではコンフリクトの直し方をお伝えしましたが、コンフリクトが起きた場合には、やはり、多少なりとも問題がある状態ですので、より上級な方や上司、もしくはチームメイトの方と相談いただいて、その上で取り込むにしてもその作業を続けていただいた方がいいかなと思います。

ここでは、ひとまずコンフリクトの解決、ということでコンフリクトした場合には、この二つ(現在の変更と入力側)の内容が出ますよ、そしてどちらか一つを選ぶか、それとは全く異なる第三の選択肢を選ぶかしていただいてコミットすると、このマージした時にコンフリクトのちゃんと内容が出た状態で、記録した形でコンフリクトの解決ができますよ、という内容になります。是非この方法を覚えておいてください。


付録 : Windowsでの表示

Windows10 / SourceTree ver.3.3.8

図1