【Git】 ブランチの役割と作り方~マージまで

ブランチを用いることでそれぞれの作業を分離し、チームでの開発を大幅にスムーズにすることができます。

また、単に各人向けのブランチを作るだけでなく、環境ごと (本番、HotFix、ステージングなど) に分ける使い方もあります。

ブランチとは

ブランチはその名の通り枝です。

Aの機能、Bの修正、Cの機能、というのを普通に共同編集してしまうと、別の機能が中途半端なせいで動作確認ができない、変なバグがでてうまくテストできない、編集場所がかぶってうまく編集できないなど、様々な問題が発生します。

ブランチを分けることで、それぞれの環境で別々のソースコードになるため、互いの作業がぶつかり合うことが大幅に減少します。

ブランチを作ると、他のブランチに影響せず作業できる

また、ブランチで行った変更をまとめて別のブランチに適用できます。これをマージ(merge)と言います。

mergeすることでブランチにした作業をまとめられる

これにより、メインブランチに中途半端な状態を入れずに済むため、常に問題なく動くブランチというものを作ることができます。

ブランチの作成

ブランチはbranchコマンドで作成できます。

git branch <ブランチ名>

例えば、test1ブランチを作成したい場合は、git branch test1とします。

ブランチは、今いるブランチから派生します。例えば、masterブランチにいる状態なら、新しく作成したブランチはmasterブランチ (派生元ブランチ) と同じ状態から始まります。

作業中のブランチを変更

開発したいブランチを変更するには、checkoutします。

git checkout <ブランチ名>

test1ブランチに変更したい場合は、git checkout test1となります。

ちなみに、コミットしていない変更がある場合、ブランチの変更ができない場合があります。

> git checkout test2
error: Your local changes to the following files would be overwritten by checkout:
        hoge.txt
Please commit your changes or stash them before you switch branches.
Aborting

この場合、コミットするか、スタッシュに入れるか、変更をキャンセルする(git checkout .)かなどして、変更が無い状態にする必要があります。

追跡対象にしていないファイルの変更は影響しません。

ブランチ一覧を表示

git branchします。

$ git branch
  master
* test1
  test2

ブランチ作成時の注意点

ブランチを作る際は、注意すべき点があります。

  • 必ずブランチを作る前に今いるブランチが正しいかチェックする
  • 追跡対象外のファイルを除き、全ての変更をコミットしておく

特に1個目は重要です。途中まで作業したけど派生元のブランチが間違っていました、となると頑張ってコミットの内容を移動する必要があり、かなり面倒です。気をつけましょう。

マージ

ブランチをマージすることで、2つのブランチに加えられた変更を1つにまとめることができます。

マージには方向があり、例えばtest1ブランチからmainブランチにマージした場合、test1ブランチには何も起こらず、一般的に不要なブランチになります。マージされたmainブランチは、test1ブランチに加えた変更が丸々反映されます。

試す

早速マージを試してみましょう。まずはmasterブランチに適当にコミットしておきます。

ファイル作成をterminal上で行っていますが、環境によってはUTF-16LE等になり、バイナリファイル扱いされたりするので、普通にIDE上で作成したほうが良いです。

バイナリファイルになると、部分的な変更をうまく検出できず、ファイル全体で変わったか変わってないかの判定しかできなくなります。

# 適当にファイル作成
echo "# README" > readme.md

git add .
git commit -m "initial commit"

したら、ブランチを作成して切り替え、ファイルを更新してコミットします。

# ブランチ作成+切り替え
git branch test1
git checkout test1

# 適当にreadme.mdファイルを変更
echo "hogehoge" >> readme.md

# 適当にファイルを追加
echo "fuga" > fuga.txt

# コミット
git add .
git commit -m "update test files"

これでtest1ブランチにコミットできました。次はこのブランチをmasterブランチにマージしてみましょう。

マージは、マージされる側にブランチに移動し、git merge <マージしたいブランチ>とします。

# checkoutすると、masterブランチの状態に戻る
git checkout master

# test1ブランチの変更を適用
git merge test1

完了です! test1ブランチに行った変更が、全てmasterブランチに反映されているはずです。

どんな挙動をしているのか

マージする際は、test1ブランチで上書きしたのではなく、test1ブランチに加えられた変更をmasterブランチにも行った、というものです。

マージの例

つまり、「readme.mdの2行目にhogehogeを追加した」、「fuga.txtを追加した」というものをmasterブランチに行ったということです。

コンフリクト

コンフリクトは、つまり競合したということです。

例えば、

  • test1ブランチでreadme.mdの2行目に”hogehoge”を追加した
  • test2ブランチでreadme.mdの2行目に”fugafuga”を追加した

という作業をしたとします。この時、test1ブランチをmasterにマージ後、test2ブランチをmasterにマージしようとすると、同じ2行目に変更を加えているため、どちらを反映したらよいかをコンピュータでは判別できなくなります。これをコンフリクトといいます。

そもそもコンフリクトを起こさないように作業することが大事ですが、被るときは被るのでどうしたらよいか見ていきましょう。

まずは上の例の通りの作業をしてみます。

git init

# ファイルを作成してコミット
echo "# README" > readme.md
git add .
git commit -m "initial commit"

# test1ブランチで作業する
git branch test1
git checkout test1
# readme.mdの2行目に追加
echo "hogehoge" >> readme.md
# 反映
git commit -a -m "update readme.md"

# masterに戻って、test2ブランチを作成して作業
git checkout master
git branch test2
git checkout test2
# readme.mdの2行目に追加
echo "fugafuga" >> readme.md
# 反映
git commit -a -m "update readme.md"

これで、masterから派生したtest1ブランチとtest2ブランチに、同じ場所に異なる変更を加えました。

まずはtest1をmasterにマージします。

git checkout master

# test1ブランチを現在のブランチにmerge
git merge test1

次に、test2ブランチをmasterにマージしてみます。

$ git merge test2
Auto-merging readme.md
CONFLICT (content): Merge conflict in readme.md
Automatic merge failed; fix conflicts and then commit the result.

エラーのようなものが出ました。CONFLICTという文字が見えます。

それと同時に、conflictを起こしたreadme.mdのファイルの中身が、この様になっていると思います。

# README
<<<<<<< HEAD
hogehoge
=======
fugafuga
>>>>>>> test2

これは、現在のブランチの最新の内容(HEAD)はhogehogeで、mergeしたいtest2ブランチの内容はfugafugaで、どっちを適用したらよいか判別できない、という状態です。

この場合は、人がどちらをどう適用するか判別する必要があります。正しい状態になるように編集するということです。多くの場合は、どちらか片方を全部適用、または両方を全部適用だと思います。

VSCodeの場合は次のような表示になり、手でコピペしたり以外にもボタンで作業を選択できます。

merge時の挙動を1ボタンで選べる

どのように適用するかを選んだら、addしてコミットしてコンフリクトの解消完了です。

$ git add <ファイル>
$ git commit -m "コメント"

git merge --abortでマージをキャンセルできます。

コンフリクトが起きる条件

コンフリクトは、作業場所が重複した場合に発生します。ファイルが重複ではありません。

例えば、

  • test1ブランチで、readme.mdの10行目に5行追加した
  • test2ブランチで、readme.meの5~20行目を削除した

これは改修場所が被っているのでコンフリクトします。しかし、

  • test1ブランチで、readme.mdの10行目に5行追加した
  • test2ブランチで、readme.meの30~40行目を削除した

この場合、ファイルは同じですが改修場所は被っていないため、コンフリクトしません。

リモートリポジトリと連携

アップロード (push)

ブランチをリモートリポジトリに上げるには、普通にpushするだけです。

git push -u origin HEAD
# または
git push -u origin <ブランチ名>

-uは、今のブランチに対する上流ブランチを設定するものです。上流ブランチは、要はリモートリポジトリのブランチです。

-uをするとローカルブランチとリモートのブランチが紐づき、次回からはgit pushだけでpushできるようになります。

これは、各ブランチの最初のpushだけで十分です。

ブランチの内容を更新 (pull)

リモートリポジトリにあるブランチを手元に持ってきて作業したい場合、pullのみでokです。

git pull

ブランチを取得

リモートにはあってローカルには無いブランチで、リモートからブランチを取得するにはfetchします。

# ブランチ取得
git fetch origin <ブランチ名>

# 切り替え
git checkout <ブランチ名>

まとめ

ブランチを活用することで、各作業を分離することができます。ブランチを作る際は、今いるブランチに注意を払って作成するべきです。

マージする際にもしコンフリクトが発生した場合、手動で解決する必要があるため、できるだけコンフリクトを起こさないように作業を分担しましょう。

git branch thumb

役に立ったらシェアしよう!