ブランチとマージ
最後に、Gitを使う上で重要な機能である、branchとmergeについて解説します。Gitに限らず、多くのVCSでは単に一つの時系列順に編集履歴を記録するだけでなく、別々の時間軸に分岐して履歴を記録していくことができます。Gitではブランチという機能により実現されています。
これまでの作業はすべてgit init時に作られるmainというブランチで行っていました。ここでは、分岐した新しいブランチで作業を行い、その作業履歴をmainブランチにマージする(取り込む)ところまでをやってみます。
まず、git checkout -bコマンドでwork/report2ブランチを作成しつつそのブランチに移動します。そこで作業を行い、コミットを二つ積みます。
$ git checkout -b work/report2 $ echo "Branch and merge are important features of Git." >> report_2.txt $ git add report_2.txt $ git commit -m "二つ目のレポートを追加" $ echo "writing report_2..." >> README.md $ git commit -am "READMEに作業状況を追記" # -aオプション: addの省略(変更のあるファイルはすべてaddされる)
図に表すと以下のような状態です。

mainブランチに戻り、work/report2ブランチでの作業内容をマージします。
$ git merge work/report2
マージされると、mainブランチの位置が変わります。

また、ファイルの内容も確認してもらうと、work/report2ブランチの作業内容も反映されていることがわかると思います。
$ cat README.md writing report_1... writing report_2...
ちなみに、今回はマージの際にマージ元に新しいコミットが積まれてないため、mainブランチの参照先を移すだけ(Fast-foward Mergeと言います)で済んでいましたが、マージ元でも作業が進み、コミットが積まれている場合は以下のように、マージ先ブランチの変更を取り込むためにマージコミットが作成されます。

こういったbranchとmergeの機能があることにより、開発の本流から分岐して、本流の開発を邪魔することなく作業を続けることができます。大規模なチーム開発を行う上でとても重要な機能となっています。
mergeの際の注意点:コンフリクト
git mergeを実行した際、通常であればGitはマージ先ブランチで作業した差分をマージ元ブランチに自動的に取り込んでくれます。ただし、マージ元とマージ先で同じ箇所を変更していた場合、どちらの変更を残すべきか機械的には判断できないという問題があります。これはコンフリクト(競合)と呼ばれる状態です。コンフリクトが起きた場合は、人間が手動で残す差分を選択し、コンフリクト解消しなければなりません。
それでは、まずはコンフリクトを起こしてみましょう。新しく分岐するupdate-report1とmainブランチでそれぞれreport_1.txtに変更を加えます。
$ echo "You can efficiently back up version of many file." >> report_1.txt $ git commit -am "report_1にVCSのメリットについて1文追加" $ git checkout -b update-report1 $ echo "You can also easily revert files to any version." >> report_1.txt $ git commit -am "report_1にVCSのメリットをもう1文追加"
ここで、mainブランチにコミットした文章の文法ミスに気づきます(複数形になるべき単語が単数形になっている箇所が2つあります)。それぞれのブランチの作業者が気づいた箇所を修正し、コミットを積んだとします。修正内容はgit diffコマンドで表示される通りです。
$ git diff diff --git a/report_1.txt b/report_1.txt index 91a7f50..f4464b1 100644 --- a/report_1.txt +++ b/report_1.txt @@ -1,3 +1,3 @@ Git is one of the version control systems. -You can efficiently back up version of many file. +You can efficiently back up version of many files. You can also easily revert files to any version. $ git commit -am "文法ミスを修正: many file -> many files"
mainに戻り、こちらでも別の箇所の文法ミスを修正します。
$ git checkout main $ git diff diff --git a/report_1.txt b/report_1.txt index f850c97..05bb1c2 100644 --- a/report_1.txt +++ b/report_1.txt @@ -1,2 +1,2 @@ Git is one of the version control systems. -You can efficiently back up version of many file. +You can efficiently back up versions of many file. $ git commit -am "文法ミスを修正: version -> versions"
この状態でマージを実行してみます。
$ git merge update-report1 Auto-merging report_1.txt CONFLICT (content): Merge conflict in report_1.txt Automatic merge failed; fix conflicts and then commit the result.
git mergeコマンドの実行で、report_1.txtでコンフリクトし、Automatic merge failedとエラーメッセージが表示されています。git statusコマンドで状況を確認します。

ここでもboth modifiedというメッセージと共にコンフリクトしているファイルが表示されます。fix conflicts and run "git commit"とあるので、指示に従ってコンフリクトを解消し、git commitを実行します。もしmergeを取り消す場合は、git merge --abortコマンドを実行してください。
それでは、まずコンフリクトを解消するためにreport_1.txtファイルの中身を確認します。

初見だと見にくいかもしれませんが、マージ元(現在のHEAD)とマージ先ブランチで行われた変更が=======で区切られて表示されます。上がマージ元(HEAD)で下がマージ先ブランチの変更内容です。
同じ行をそれぞれのブランチで変更しているのでコンフリクトが起きていますが、今回はどちらの文法ミスの修正も正しいため、両方の修正を反映させるようにします。report_1.txtを開いてそのように修正してください。修正後は以下のようになります。
$ cat report_1.txt Git is one of the version control systems. You can efficiently back up versions of many files. You can also easily revert files to any version.
ちなみにコンフリクト箇所を示していた<<<<<<の行も不要なので忘れずに消してください。修正が完了したらコミットします。
$ git commit -a
これがコンフリクト解消までの流れとなります。今回は単純な解消作業で済む例を紹介しましたが、実際の現場ではプログラミングコードでのコンフリクトを解消しなければなりません。そのため、それぞれのブランチでどのような変更が施されたか、その意図や内容を正確に把握した上で修正しないと思わぬ問題を起こしてしまう可能性があります。実際の開発の現場では、コンフリクト解消はできるだけ慎重に行うようにしてください。
まとめ
Gitによるバージョン管理の基本的な流れを解説しました。実際に手を動かしながらやると覚えやすいと思うので、ぜひ実践してみてください。また今回紹介したもの以外でも、Gitには便利なサブコマンドやオプションがたくさんあるので、興味がある方はgit help -aで確認してみてください。
次回はGit、およびGitHubを使ったチーム開発の進め方を解説します。
