Lesson 4

チームでの共同開発

Lesson 4 Chapter 1
main, developmentに直接pushしてはいけない理由

Lesson3では実際の業務で利用機会の多い実践的な各Gitコマンドを学んできました。さて、最後のレッスンであるLesson4では、並行して複数の機能を開発する際のブランチの運用方法や、実務でGitHubを利用したチーム開発をスムーズに進めるための方法について学んでいきましょう。

リモートリポジトリ上のmainブランチには直接pushしない

前回のLesson3では説明を分かりやすくするために、mainブランチ、developmentブランチのみを利用して作業を行ってきました。実際の開発現場では、新規機能開発やバグ修正の目的や用途に合わせて、ブランチをいくつかの種類に切り分ける必要があります。

例えば、リモートリポジトリ上のmainブランチは、「実際に公開中のサービス用にビルドしたソースコードの内容にしておく」といった目的があります。そのため、ソースコード内にバグが一切含まれていないような、安定した状態を常に保っておく必要があります。

ここで仮に、手元のマシンで行った編集内容をリモートリポジトリのmainブランチに直接pushした場合を考えてみましょう。編集した内容を取り込んだmainブランチが公開中のサービスにデプロイ(反映)された場合、編集した内容がそのまま公開中のサービス上に現れてしまうことになります。
万が一、編集した内容に意図しないバグが含まれていた場合に、公開中のサービスにて、そのバグが何かしらの問題を引き起こす可能性も考えられます。その結果、サービスが正常に稼働できない状態となり、サービス利用者ならびにサービスを公開している側にも多大な不利益をもたらしてしまう可能性に繋がる恐れがあります。

以上の理由から、リモートリポジトリのmainブランチには編集内容を直接pushされないようにする仕組み(運用方法)を採用している開発現場が多いです。

developmentブランチにも直接の反映は避ける

同様に、リモートリポジトリ上のdevelopmentブランチにも直接pushすることは好ましくありません。次の「git-flow」のチャプターでも説明しますが、developmentブランチはリリース前の状態、つまりmainブランチの1歩手前の状態のブランチであり、リリース準備が出来次第、developmentブランチの内容をmainブランチに反映します。
そのため、こちらも同様になるべく挙動が安定した状態を保っておく必要があります。

新規機能の開発や、バグの緊急修正などはdevelopmentブランチではなく、別のブランチで行います。そのため、developmentブランチに対しても、編集内容を直接反映するのではなく、新規機能の開発用やバグの緊急修正用のブランチからdevelopmentブランチに反映させる仕組みが「git-flow」(次のチャプターで説明)の考え方となります。

ブランチの統合(マージ)

ここで、次のチャプター「git-flow」に入る前に、ブランチの統合について説明を行いたいと思います。

上記でも説明しました通り、実際の開発現場では、開発の目的によって専用のブランチを作成します。
例えば、新規開発用のブランチ(featureブランチ)を作成し、そのブランチでの作業(コミット)が完了した後にdevelopmentブランチに統合し、最終的にmainブランチまでその内容を反映していく流れとなります。
このように、とあるブランチで行った変更内容(コミット)を別のブランチに統合(反映)する処理を「マージ」と言います。マージは英語で「merge」という単語であり、「統合する」、「合併させる」といった意味を持ちます。
マージは、Gitの仕組みの中でも、ブランチと同じレベルで重要な機能です。このマージ操作を行うことにより、手元で行った変更内容を、最終的にmainブランチにまで反映していくことになります。

マージ処理の仕組み

以下に、Gitにおけるマージ処理の仕組みのイメージ図を載せます。
最初の「コミット1」ができた段階ではmainブランチのみが存在している状態です。次に、mainブランチを起点にリリース前の状態用のdevelopmentブランチを作成し、developmentブランチを起点に新規開発用のfeatureブランチを作成します。

作業者は手元のマシンでfeatureブランチにて、「コミット2」と「コミット3」の作業を行い、featureブランチをリモートリポジトリにpushします。
その後、作業内容のチェックを行い、問題が無ければ、developmentブランチおよびmainブランチにマージされます。
このような手順を踏まえることで、mainブランチに「コミット2」、「コミット3」の内容が反映されることになります。

merge.png マージのイメージ

マージコミットについて

「コミット4」は、developmentブランチにfeatureブランチの内容をマージした際に自動で生成されるマージコミットです。「コミット5」も同様のマージコミットとなります。マージの内容や、Gitの設定によって、マージコミットが生成されない場合もあります。

Lesson 4 Chapter 2
git-flow

git-flowとは

このチャプターでは、git-flowについての説明をします。
git-flowとは、実際のチーム開発の現場でよく採用されている、有名なブランチの運用方法の1つです。git-flowはブランチを以下の5種類に分け、運用を行います。
①mainブランチ
②developmentブランチ
③hotfixブランチ
④releaseブランチ
⑤featureブランチ
以下のようなイメージとなります。(◯をコミットとみなしています。)

gitflow.png git-flowのイメージ

では、それぞれのブランチの役割について見ていきましょう。

main

mainブランチは、常にデプロイできる安定した状態に保っておく必要があります。言い換えると、リリース中のサービスとソースコードの状態を一致させておく必要があります。
原則、mainブランチ上では直接の作業(コミット)は行わず、developmentブランチやreleaseブランチ、hotfixブランチからのマージを待つ状態となります。マージ後はすぐにリリース(本番環境へのデプロイ)を実施します。
最新のコミットには、リリース中のバージョンをわかりやすくするためにタグ付けをすることがあります。

また、mainブランチに対し、直接のpushや意図しないブランチ削除といった事故を防ぐために原則保護(プロテクト)されていることが一般的です。リモートリポジトリに対して、編集権限を一定以上持っているような、管理者に相当するレベルのユーザのみがmainブランチの管理を行うことが多いです。

development

developmentブランチは、その名の通り開発用のブランチという意味を持ちます。(developブランチと付けられることもあります。)
コードレビューやテスト等のチェックを経て、ソースコードの状態が安定し、リリースの準備が出来次第、releaseブランチにマージします。
mainブランチ同様に、このブランチでは直接作業してコミットすることはなく、featureブランチからのマージを待つ状態となります。

hotfix

hotfixブランチは、リリース中のサービスで発生したバグなどを緊急修正するために利用するブランチです。
バグが発生した際、リリース中のサービスとmainブランチはソースコードの状態が一致しているため、mainブランチを起点にhotfixブランチを作成します。
緊急修正後、mainブランチへマージし、その後developmentブランチにもマージを行います。

release

releaseブランチは、リリースをする前の最終確認やバグの修正といった対応を行うブランチです。developmentブランチを起点に、releaseブランチを作成します。
releaseブランチでの作業後は、mainブランチへマージし、その後developmentブランチにもマージします。

注意

リリースのための準備をする目的を持つブランチである以上、releaseブランチから新規機能開発用のfeatureブランチが作成されることはありません。

feature

featureブランチは、新規機能、追加機能などの開発用のブランチであり、developmentブランチを起点に作成されます。「feature」は英語で、「機能」という意味を持ち、慣習として新規機能開発用のブランチに設定されやすいブランチ名です。

また、バグ修正を行うためにも利用されることがあります。
featureブランチでの作業が完了後は、developmentブランチへのマージを行います。

デメリット:複数案件平行した開発に弱い

git-flowの弱点として、「複数案件平行した開発に弱い」という点があります。
複数案件を同時進行で行う、つまり、新規機能や追加機能の開発が同時に複数存在している状態であり、その数だけfeatureブランチが作成されていることになります。
先述しましたが、featureブランチは、「developmentブランチを起点に作成され、作業完了後にdevelopmentブランチにマージする」という流れとなります。
ここで、featureブランチをdevelopmentブランチにマージする際に、「featureブランチで行った変更内容を素直にマージできないケースが発生しやすい」という問題があります。
この問題を、「コンフリクト(競合)」と言います。

コンフリクト(競合)が発生するケース

実際に、どのような状態の場合にコンフリクトの発生が考えられるかを説明したいと思います。

例えば、とある時点のdevelopmentブランチを起点に作成されたfeature1ブランチとfeature2ブランチが存在していると仮定します。当然、feature1ブランチとfeature2ブランチでは、それぞれ独自の変更をコミットし、developmentブランチにマージすることになります。

ここで、feature1ブランチとfeature2ブランチでは、たまたまfeature.htmlという同じファイルに対し変更を加えていたとします。さらに、同じ行に変更を加えていた場合、developmentブランチに取り込む際にどちらの変更内容を優先すべきでしょうか。

例を挙げると、以下のように、feature.htmlの1行目に、それぞれ以下のように独自の変更を加えていたとします。

feature1ブランチのfeature.html
feature1ブランチでコミットしたコードです。
feature2ブランチfeature.html
feature2ブランチでコミットしたコードです。

この時、Git側では、どちらの変更内容を優先し取り込むべきかを自動で判断することができません。この状態が、「コンフリクト(競合)」が発生している状態となります。
この場合は、どちらの変更内容を優先すべきかを、作業者のほうで判断し、決定する必要があります。
このように複数のfeatureブランチを同時に並行して開発する際は、上記のようなコンフリクトが発生しやすい、ということは認識しておいていただきたいと思います。

コンフリクト(競合)をなるべく起こさないために

また、可能な限り、コンフリクトが起きないようにする工夫もしておくと良いと思います。
なるべくコンフリクトを起こりにくくするためのひとつの方法として、リモートリポジトリのdevelopmentブランチを、ローカルリポジトリで作業進行中のfeatureブランチに適宜pullを行い、最新のdevelopmentブランチとの差分をできる限り少なくしておくことです。
都度、pullを行い、最新のdevelopmentブランチの変更内容をローカルのfeatureブランチに反映しておくことで、新たに変更が入った箇所を把握しやすくなり、同じ箇所への変更を回避しやすくなります。
逆に、自分自身のローカルリポジトリ内のfeatureブランチの内容を適宜リモートリポジトリへpushし、都度その変更内容をdevelopmentブランチへマージしてもらう機会を増やすことで、別の作業者が作業中のfeatureブランチに対しpullを実行した際に、その内容が反映されるため、チーム全体としてもコンフリクトの防止に役立つこととなるでしょう。

コンフリクトの解消の方法について

実際に、コンフリクトが発生してしまった場合のコンフリクトの解消の方法については、「Chapter5 コンフリクトの解消」にて詳しく説明したいと思います。

さいごに

このチャプターでは、git-flowについて学びました。git-flowを使いこなせるようになれば、チームでの開発作業もより効率的にすることができるようになりますので、それぞれのブランチの役割についてしっかりと理解しておきましょう。
また、「git-flow」以外にも、「GitHub-flow」や「GitLab-flow」といったブランチの運用方法も有名であり、それぞれ特徴にも違いがあります。興味がある方は一度調べてみていただければと思います。

Lesson 4 Chapter 3
プルリクエストを出す

Chapter 2ではgit-flowについて学んできました。これで、基本的なブランチの運用方法やマージによって、新規機能をmainブランチまで反映していく流れについては理解することができたと思います。

それでは、実際に自身のローカルリポジトリのfeatureブランチで行った作業を、リモートリポジトリのdevelopmentブランチにマージする部分について見ていきましょう。
自身の行った変更内容をマージするためには、リモートリポジトリ上で「プルリクエスト」という操作を行う必要があります。

プルリクエストとは

プルリクエストとは、自分がローカルリポジトリで行った作業内容を、リモートリポジトリの対象のブランチにマージしてもらうために依頼する機能のことです。
プルリクエストは、GitHubなどで管理しているリモートリポジトリ上で行う操作になります。

また、プルリクエストは、「pull」と「request」という単語からできており、依頼されたプルリクエストを承認することで、リモートリポジトリ上の対象のブランチの内容を、もう一つの比較対象のブランチがpullを行う、つまり、ブランチ同士をマージすることができます。

プルリクエストの必要性

「依頼をする機能」という部分がこの機能の重要なポイントです。ここでは、なぜマージを依頼をする機能が必要かについて説明したいと思います。

仮に、プルリクエストという仕組みが存在しない場合、ローカルリポジトリのfeatureブランチで行った作業内容をリモートリポジトリにpushし、その後そのまま自分自身の手でリモートリポジトリ上のpushしたfeatureブランチからdevelopmentブランチにマージすることができてしまいます。
この場合、万が一、作業を行ったfeatureブランチの実装内容にバグが含まれていた場合、他の開発者、リモートリポジトリの管理者がその状態を事前に把握することなく、バグがリモートリポジトリの重要なブランチに取り込まれてしまうことになります。

そのため、作業内容を取り込む前に、
・実装内容にバグが潜んでないかどうか
・コードの質に問題がないか
といった観点で、変更が加わった箇所の確認(コードレビュー)が必要になります。
プルリクエストを出すことで、マージを依頼すると同時に、変更内容を他者に確認をしてもらうステップを踏むことができます。

実際に作業を行うチームメンバーの開発スキルや習熟度はそれぞれ異なるため、技術力が高い人もいればそうではない人もいます。故に、とあるメンバーがバグを含むコードを書いてしまう可能性も考慮しておく必要があります。
そのために、プロジェクトチーム内にて一定以上高度なスキルを持った方がリモートリポジトリの管理役となり、開発メンバーが行った作業内容を都度レビューし、内容に問題がなければ既存のコードに取り込む(マージする)という手順を踏むことで、リモートリポジトリの内容を安定した状態に保つようにしています。
このように、単にマージするだけでなく、事前にコードレビュー等の確認の過程を経ることができる、これがプルリクエストが必要である大きな理由となります。

プルリクエストの手順

以下は、作業を開始し、プルリクエストを経て、変更内容のマージが完了するまでのおおまかな流れとなります。

まず、ローカルリポジトリで行う手順が以下となります。
①developmentブランチを最新の状態に更新(pull)する
②developmentブランチから新しいブランチを作成する
③作成したブランチで変更内容をコミットする
④リモートリポジトリ(GitHub)へpushする

ここからが、GitHub上のリモートリポジトリで行う作業です。
⑤pushしたユーザがプルリクエストを出す
⑥プルリクエストを受けた担当者がコードレビューを行う
⑦担当者がプルリクエストをマージする
⑧不要になったマージ済みのブランチを削除する

プルリクエストを出す

それでは、実際にプルリクエストを出してみましょう。今回は、GitHub上のリモートリポジトリ(test-remote-repositry)で試してみたいと思います。

①developmentブランチを最新の状態に更新(pull)する

まずは、最初のステップとして、リモートリポジトリのdevelopmentブランチをローカルリポジトリへpullし、ローカルリポジトリのdevelopmentブランチを最新の状態にしておく必要があります。
しかし、これまでのレッスンを進めてきた状態ですと、リモートリポジトリ「test-remote-repositry」にはmainブランチしか存在していないはずです。そのため、あくまでも今回は練習として、リモートリポジトリのmainブランチに対し、プルリクエストを行いたいと思います。
また、リモートリポジトリのmainブランチのコミット履歴も「ローカルリポジトリにて3行目に文章を追加」というコミットが最新の状態となっており、ローカルリポジトリのmainブランチのほうがコミット履歴のほうが進んでいる状態となっているため、今回はmainブランチに直接pushし、ローカルリポジトリの変更履歴と一致させておくことにします。

git push origin main

remote.png git push後のリモートリポジトリの状態

これで、リモートリポジトリとローカルリポジトリのmainブランチの状態が一致したため、①の状態を満たしたとみなし、次へ進みたいと思います。

②developmentブランチから新しいブランチを作成する

次に、ローカルリポジトリ側で、新規機能を追加する、という名目でfeatureブランチを作成します。①同様、本来はdevelopmentブランチからfeatureブランチを作成するほうが良いのですが、今回は、mainブランチからfeatureブランチを作成します。
作成したfeatureブランチの名前は「feature-pull_request」としました。

branch.png 新ブランチ「feature-pull_request」を作成

③作成したブランチで変更内容をコミットする

feature-pull_requestブランチに移動し、test.htmlの14行目に以下の文章を追記しました。この状態で、変更内容をコミットします。

test.html.png test.html

commit.png git commitで変更内容を記録

④リモートリポジトリ(GitHub)へpushする

それでは、上記のコミット内容でfeature-pull_requestブランチをリモートリポジトリへpushしましょう。
リモートリポジトリにはまだfeature-pull_requestブランチが存在しないため、新しく同じ名前のブランチが作成され、変更内容のpushが行われます。

push.png git pushの実行結果

pushが成功し、以下のようにGitHub上のリモートリポジトリにて、feature-pull_requestブランチが作成され、最新のコミット内容を確認することができました。

remote2.png git push後のリモートリポジトリ

⑤pushしたユーザがプルリクエストを出す

ここまでの作業で、プルリクエストを出すまでの準備が整いましたので、リモートリポジトリでプルリクエストを出してみたいと思います。以下の赤枠の部分の「Pull requests」という箇所をクリックします。

pullrequest.png プルリクエストへ進む

すると、以下のような画面が表示されるので、赤枠の緑色の「Compare & pull request」というボタンをクリックします。

pullrequest2.png 「Compare & pull request」ボタンをクリックする

そうすると、以下のような画面に進みます。
赤枠の「base」と「compare」という箇所にそれぞれ「main」と「feature-pull_request」と表示されています。「base」がマージ先のブランチ、「compare」が今回プルリクエストを出す側のブランチ、という意味です。
また、青枠の箇所はそれぞれ、プルリクエストのタイトルとコメントを記述することが可能です。今回はこのままにしておきます。
それでは、右下に表示されている緑色の「Create pull request」というボタンをクリックします。

pullrequest3.png プルリクエストを出す際の準備ページ

すると、以下の画面が表示されます。これで無事、正常にプルリクエストを発行することができました。

pullrequest4.png プルリクエスト発行完了

Lesson 4 Chapter 4
プルリクエストをマージする

上記のチャプターで、プルリクエストを出すところまで進みましたので、今回はその続きとして、プルリクエストをマージする作業から進めていきたいと思います。

残りの手順については以下となります。
⑥プルリクエストを受けた担当者がコードレビューを行う
⑦担当者がプルリクエストをマージする
⑧不要になったマージ済みのブランチを削除する

⑥プルリクエストを受けた担当者がコードレビューを行う

以前のチャプターでも説明しましたが、基本的に「プルリクエストを出す人」と「プルリクエストをマージする人」は異なります。今回は便宜上、自分自身がコードレビューおよびプルリクエストをマージする側の担当者になったつもりで進めてみたいと思います。

それでは、以下のように、リモートリポジトリにて、赤枠のところからタブの「Pull requests」をクリックします。
すると、すでに発行されているプルリクエストの一覧が確認できる画面を表示することができます。そのまま、先ほど発行したばかりである、青枠のプルリクエストをクリックします。

pullrequest5.png プルリクエスト画面へ進む

そうすると、以下のように、選択したプルリクエストの詳細画面へ遷移します。ここで、赤枠のタブ「Files changed」をクリックします。

pullrequest6.png プルリクエスト画面

以下のように、変更した内容を確認することができます。黄緑色に塗られている箇所のコード(13,14行目)が今回のプルリクエストに含まれているコミットの変更内容となります。
つまり、今回のプルリクエストを承認した場合、この箇所の変更内容がmainブランチにマージされることになります。

pullrequest7.png プルリクエストでマージしたい変更内容

対象のコードに対してコメントを残す

また、ファイルの行数表示の右横の黒い「+」で表示されている箇所にマウスをホバーすると、青色の「+」マークが表示されます。こちらをクリックすると、以下のようにコメントを記入する欄が出現します。
このように、変更内容を確認している最中に気になったコードがあれば、ピンポイントにコメントを差し込むことが可能です。

pullrequest8.png 気になったコードにコメントを追加する

コメントを入力すると、右下の「Add single comment」と「Start a review」という2つのボタンが押せるようになります。「こちらの行を修正してください。」というコメントを入力し終えたとして、今回は「Add single comment」をクリックします。

pullrequest9.png コメントを入力し、追加ボタンをクリック

すると、以下のようにコメントが追加されます。この時、プルリクエストを出した開発者宛に、コメントしたことがメールで通知されます。開発者は、このコメントを確認し、必要な修正を加えて、再度プルリクエストを行います。

pullrequest10.png コメントの追加が完了

このように、プルリクエストを承認するまで、コードレビューと修正のサイクルを繰り返しながら、作業を進めていく形となります。

⑦担当者がプルリクエストをマージする

コードレビューが済み、マージの準備が整った場合は、プルリクエストを承認してから、プルリクエストのマージを行います。

プルリクエストの承認

プルリクエストをマージする前に、レビュー担当者が「プルリクエストの承認」を行います。確認完了の合図のようなイメージです。
プルリクエストの画面の右側に「Review changes」という緑色のボタンがありますので、そちらをクリックすると、画像内の右下のようなポップアップが表示されます。
このポップアップ内の赤枠の「Approve」のラジオボタンにチェックを入れ、操作を進めることでプルリクエストの承認をすることができます。

「Approve」はレビュー担当者向けの操作項目

今回は自分自身でプルリクエストを発行しているため、「Approve」が選択できないようになっておりますが、レビュー担当者の方であれば、この先の操作が可能です。

pullrequest11.png プルリクエストの承認

プルリクエストのマージ

プルリクエストの承認が完了したと仮定し、プルリクエストのマージを行っていきたいと思います。
同じ画面の「Conversation」のタブを開き、画面の下のほうにスクロールすると以下のような表示がされていますので、赤枠の「Merge pull request」のボタンをクリックします。

pullrequest12.png プルリクエストのマージ

すると、以下のような表示に切り替わるので、「Confirm merge」ボタンをクリックして、マージを実行します。

pullrequest13.png プルリクエストのマージの最終確認

以下のような画面が表示されます。これで、プルリクエストのマージが完了しました。

pullrequest14.png プルリクエストのマージが完了

それでは、念のため、プルリクエストの内容がmainブランチにマージされているかを確認してみたいと思います。画面の上部の「Code」のタブをクリックします。

pullrequest15.png

すると、以下のように、mainブランチの最新コミットが、先ほどマージした内容となっていることを無事に確認することができました。

pullrequest16.png

⑧不要になったマージ済みのブランチを削除する

最後に、プルリクエストが完了して不要になったブランチはリモートリポジトリから削除しておきましょう。
以下の画面で、「branches」のタブをクリックします。

pullrequest17.png

すると、以下のブランチ一覧が表示される画面に切り替わりますので、「Your branches」もしくは「Active branches」に表示されている「feature-pull_request」ブランチの右端にある赤枠のゴミ箱のアイコンをクリックします。(クリックするのはどちらでも構いません。)

pullrequest18.png

すると、ブランチのところの表示が「Deleted~~」と変化します。
これで、ブランチの削除が完了しました。

pullrequest19.png

このように、マージが完了し、不要になったブランチは削除しておくようにしましょう。
また、ローカルリポジトリの該当のブランチも同様に削除しておくと尚良いです。

Lesson 4 Chapter 5
コンフリクトの解消

Chapter2 git-flowでは、コンフリクトが発生するケースについての説明を行いました。
このチャプターでは、先述したコンフリクトについて、実際に発生してしまった場合の解消の方法について説明したいと思います。ここでは、ローカルリポジトリで発生したコンフリクトの見方と解消の方法を、人気のエディタであるVSCodeを使って説明します。

VSCodeとは

VSCodeは、Microsoft社が提供している開発用のエディタです。正式名称は「Visual Studio Code」と言います。無料で利用できるエディタであり、各種プラグイン(拡張機能)を追加することも可能で、効率の良い開発を行いやすく、定番のエディタとなっています。
例えば、Gitの拡張機能を追加することで、VSCode上でGitの機能を扱うことも可能です。筆者も普段の開発にはVSCodeを使用しており、おすすめのエディタとなっております。

コンフリクトの発生

それでは、ローカルリポジトリでコンフリクトが発生した場合の解消の方法について学んでいきたいと思います。

今回は試しに、ローカルリポジトリ「test-remote-repositry」にて、feature系のブランチを2個作成し、それらのブランチで行った変更内容をdevelopmentブランチにマージする過程でコンフリクトを発生させてみたいと思います。

まず、ローカルリポジトリ内でdevelopmentブランチにチェックアウトします。

conflict.png developmentブランチへチェックアウト

次に、developmentブランチを起点に、新規開発の名目としてfeature系のブランチを2つ作成します。ここでは、「feature1」ブランチと「feature2」ブランチを作成しました。

conflict2.png feature系のブランチを2個作成

それでは、まず、feature1ブランチにチェックアウトし、test.htmlの14行目に「feature1ブランチでコミットしたコードです。」と記述し、コミットまでを行います。

conflict3.png feature1ブランチで14行目に1行追加

conflict4.png feature1ブランチで行った作業をリポジトリに記録

続いて、feature2ブランチにチェックアウトします。developmentブランチを起点にfeature2ブランチを作成しているため、当然feature1ブランチの変更内容は存在しておりません。
ここで、同様にtest.htmlの14行目に「feature2ブランチでコミットしたコードです。」と入力し、コミットまでを行います。

conflict5.png feature2ブランチで14行目に1行追加

conflict6.png feature2ブランチで行った作業をリポジトリに記録

これで、2つのブランチでの作業が完了し、developmentブランチへマージする準備が整いました。
それでは、feature1ブランチ、feature2ブランチの順にdevelopmentブランチへマージを実行したいと思います。

マージについて、これまではリモートリポジトリ上でプルリクエストを通じたマージの方法を説明していましたが、ローカルリポジトリ内のブランチ同士でもマージを行うことが可能です。
マージ先のブランチにチェックアウトした状態で、以下のようにコマンドを実行することで、マージを実行することができます。

git merge (マージ対象のブランチ)

つまり、今回はdevelopmentブランチにfeature1ブランチの内容を取り込みたいため、developmentブランチにチェックアウトした状態で、以下のように実行します。

git merge feature1

実際に、マージコマンドの実行結果が以下になります。

conflict7.png feature1ブランチをマージした際の実行結果

conflict8.png

このように、developmentブランチにfeature1ブランチのコミットをマージすることができました。test.htmlにも、マージした内容が反映されていることを確認することができました。

続いて、developmentブランチにfeature2ブランチの変更内容をマージしてみたいと思います。このまま、developmentブランチにいる状態で、以下のようにコマンドを実行します。

git merge feature2

すると、以下のように、先ほどとは異なる内容が出力されました。
出力内容は、「test.htmlに対し、CONFLICT(コンフリクト)が発生している」ということを伝えています。
原因は、test.htmlの同じ箇所に変更が加わっているところにあります。

conflict9.png git merge feature2の実行結果

また、この時のVSCode上のtest.htmlの表示は以下のようになっています。

conflict10.png VSCode上のtest.html

ハイライトなどの表示デザインについて

表示(緑色、青色のハイライトなど)のデザインについては、VSCodeに追加している拡張機能によって変わることがあります。

Chapter2 git-flowでも説明を行いました通り、この時、Git側では、どちらの変更内容を優先し取り込むべきかを自動で判断することができません。そのため、どちらの変更内容を優先すべきかを、作業者のほうで判断し、決定する必要があります。

コンフリクトを解消する

それでは、実際にVSCode上でコンフリクトの解消を行ってみたいと思います。
まず、以下の画面の表示内容について説明を行います。
14行目と15行目には、事前にfeature1ブランチから取り込んだ変更内容が表示されていることが分かります。つまり、現在の最新のdevelopmentブランチのtest.htmlの内容となります。
続いて、17行目と18行目には、今回のマージでfeature2ブランチから取り込もうとした内容が表示されています。

conflict10.png VSCode上のtest.html

では、今回は、後者のfeature2ブランチからマージしようとした内容(17〜18行目)のほうが必要であったと判断し、コンフリクトを解消したいと思います。
そこで、コンフリクト解消のために、この状態で、14行目から18行目までの5行に対し、手作業で変更を行う必要があります。

現在表示中のtest.htmlはそのままエディタ上で編集が可能であるため、以下のように
①14〜15行目の緑色のハイライトの表示
②16行目の=======の表示
③18行目の青色のハイライトの表示
の3箇所を削除し、14行目に「feature2ブランチでコミットしたコードです。」の内容が表示されるように調整を行いました。

conflict11.png 調整後のtest.html

ここで一度、git statusコマンドを実行し、状況を確認します。

conflict12.png git statusコマンドの実行結果

このように、「test.htmlのマージがまだ完了していない」という状況が出力されています。
一番下の「no changes added to commit 〜〜〜」という出力の通り、上記で行った手作業による変更内容の分をリポジトリに記録するために、git addおよびgit commitを実行する必要があります。

それでは、出力結果の指摘に従い、コミットまでを行います。

conflict13.png 手作業で行った変更内容をコミットする

ここでは、最後にgit statusコマンドを確認をし、無事に「nothing to commit, working tree clean」と出力されるようになっていることを確認しました。これで、コンフリクトの解消をすることができました。

最後に、念のため、developmentブランチのコミット履歴を確認してみたいと思います。
以下のようにgit logコマンドを実行します。(developmentブランチのみのログを確認したいため、--first-parent developmentという引数を追加しています。)

conflict14.png git logコマンドの実行結果

このように、最新のコミット履歴の2件が、古い方から順に
①最初にマージしたfeature1ブランチの変更内容のコミット
②先ほどのコンフリクト解消のために実行したコミット
の2つが存在していることが分かりました。

これで、コンフリクトの解消の説明は以上となります。
実際にコンフリクトが発生してしまった場合は、慌てずに上記で説明した通りの手順で解消作業を進めていただければと思います。

おわりに

この教材では、バージョン管理システムであるGitについて学んできました。Lesson1〜4を通して、Gitの概念、基本的な操作コマンド、ブランチ、マージ操作の方法を学び、さらには、チーム開発のためのGitHubを利用した実践的なGitの活用方法についても理解することができたと思います。
ただ、一度学習しただけでは、難しく感じた箇所もいくつかあると思います。繰り返し見返すことで、より深く理解のレベルを高めることができることができるようになるでしょう。
この教材で学んだ内容が、個人開発や実際のチーム開発の現場で活躍するための、みなさんの第一歩となれば幸いです。