どぼじょのIT学習ブログ

高専卒土木女子がIT業界を目指してお勉強。

Git入門 part3 / ブランチ

サルでもわかるGit入門の発展編に突入です!
入門編の記事は、part1part2があります😊

目次

1.発展編

1-1. ブランチ

ブランチとは 

Git入門 part2で少しだけ登場したブランチというものを学習します。
ブランチ(branch)は英語のとおり、履歴の流れを枝のように分岐して記録していくためのものです🌲
ブランチは他のブランチの影響を受けないため、同じリポジトリ中で複数の変更を並行して進めることができます。
また、ブランチは他のブランチと合流(マージ)し、ひとつのブランチとなることもできます🤝
そのためソフトウェア開発において、各担当者が別々のブランチでそれぞれ作業を行い、最後にマージしてひとつのソフトウェアとすることが可能になります✨
リポジトリに最初のコミットを行うときはmasterという名前のブランチが作成されます。
それ以降のコミットは、ブランチを切り替えるまでmasterブランチに追加されていきます。

ブランチとの運用方法

ブランチの運用方法についてはおそらくチームごとに決めるのですが、今回は統合ブランチトピックブランチの2つを使った運用方法について学習します💡

統合ブランチは、リリース版をいつでも作成可能な状態にしておくためのブランチです。
通常、masterブランチを統合ブランチとして使用するようです😗
トピックブランチは、機能追加やバグ修正など、ある課題に関する作業を行うためのブランチです。
統合ブランチから課題に関するトピックブランチを作成し、トピックブランチでの作業が完了したら、統合ブランチに取り込むという使い方をします。
芯となる統合ブランチがあることで、情報がごちゃごちゃしなくて良いですね😊

ブランチの切り替え

作業するブランチを切り替える操作をチェックアウトと言います。
また、現在使用しているブランチの先頭のことをHEADと表します。このHEADが移動することで、作業するブランチが変更される仕組みです💡

コミットを指定するときに、この時にHEADと後述の記号を組み合わせて、あるコミットからの相対位置で指定することもできます。

記号 読み方 用途
~ チルダ 何世代前の親かを指定する
^ キャレット 何番目の親かを指定する

使い方のイメージは以下のような感じです😊凡例を書き忘れてしまったのですが、葉っぱはコミットです。
f:id:mistyrinth:20181224124710p:plain

変更内容をコミットしていないままチェックアウトを行うと、その変更内容はワークツリーやインデックスに残ったまま、移動先のブランチに移ります。
ただし、移動先のブランチで既にそのファイルに変更が行われている場合、チェックアウトに失敗してしまいます💦
このような場合に、一時的に変更内容を退避させてからチェックアウトする方法があります。
stashという、ファイルの変更内容を一時的に記録しておく領域を使い、ワークツリーとインデックス内にあるコミットされていない変更を、一時的に退避させることができます!
また、退避させた変更は後から取り出して、元のブランチや別のブランチに反映させることができます👏

ブランチの統合

トピックブランチでの作業が完了したら、最終的に統合ブランチに統合されます。
ブランチを統合する方法として、mergeを使う方法と、rebaseを使う方法の2種類があります。
どちらを使うかによって、統合後のブランチの履歴が大きく異なるそうなので、しっかり学習しておきます😎

方法 特徴
merge ・各々の変更履歴を統合する
・変更内容の履歴がそのまま残る
・履歴が複雑になる
rebase ・それぞれのコミットで競合を解消し、履歴を一本化する
・元のコミットの変更履歴が変わる
・履歴は単純になる

図で表すと以下のようになります。
f:id:mistyrinth:20181226171143p:plain

うーん分かりにくい…😶笑
チュートリアルに進んでみましょう!!

1-2. チュートリアル1 ブランチを使ってみよう

早速チュートリアルに沿って進めていきます🚗

前準備

まずはtutorialディレクトリを作って、空のリポジトリを作成します。

$ mkdir tutorial
$ cd tutorial
$ git init

次にmyfile.txtというファイルを作成し、サルでもわかるGitコマンドというテキストを入力してコミットします。

今回からコンソールの入力/出力の表示を変えました〜!
今までは枠で囲ってたのですが、沢山続くと見にくいのでマーカーにしました😊
f:id:mistyrinth:20181226175741p:plain

ブランチを作成する

早速、branchコマンドを使ってissue1という名前のブランチを作成します。

$ git branch ブランチ名

また、branchコマンドで引数を指定しない場合、ブランチ一覧を表示します。
*が付いているのが、現在のブランチです🌲

f:id:mistyrinth:20181226180718p:plain
今はmasterブランチにいますが、他にissue1ブランチがあることが確認できますね✨

ブランチを切り替える

issue1ブランチにコミットを追加していくには、checkoutコマンドを使ってissue1ブランチをチェックアウトします😊
宿泊施設のチェックアウトとは違って指定したブランチを出るのではなく、指定したブランチに切り替えるコマンドです!

$ git checkout ブランチ名

ここではブランチ名はissue1になります👆
f:id:mistyrinth:20181226183838p:plain

ちなみに、以下のようにcheckoutコマンドに-bオプションを付けると、ブランチの新規作成とチェックアウトを同時に行えます✨

$ git checkout -b ブランチ名

続いて、myfile.txtの中身に2行目を追加してコミットします🙂

f:id:mistyrinth:20181226184228p:plain

ブランチをマージする

先程issue1で行った変更をmasterブランチに merge します。
まずはcheckoutコマンドでmasterブランチに移動します。

$ git checkout master

f:id:mistyrinth:20190107160646p:plain

myfile.txtの編集はissue1ブランチ上で行ったため、masterブランチのほうでは内容は変更されていないことが確認できます。

ブランチのmerge はmergeコマンドで実行できます😊

$ git merge 取り込みたいブランチ名

このコマンドを実行すると、指定したブランチがHEADの指しているブランチに取り込まれます!

f:id:mistyrinth:20190107161431p:plain

masterブランチのコミットがissue1と同じ位置(HEAD)に移動しました👌
このマージはfast-forward(早送り)マージと呼ばれます。 遅れているmasterブランチのコミットをさくっとissue1と同じコミットの位置まで持ってくることができるので、早送りと言われるんでしょうね😶

ブランチを削除する

issue1ブランチの内容はmasterブランチに無事統合されたため不要になりました。
issue1ブランチを削除します!
ブランチの削除は、branchコマンドに-dオプションを指定して実行します。

$ git branch -d ブランチ名

f:id:mistyrinth:20190107162252p:plain
branchコマンドでブランチの一覧を確認すると、issue1が削除されているのがわかります🎉

並行で作業する

今度は、2つのブランチで並行で作業してみます!
まずissue2ブランチとissue3ブランチを作成し、issue2ブランチをチェックアウトします。

f:id:mistyrinth:20190107162911p:plain

まずはissue2ブランチ側の作業です!
myfile.txtcommitコマンドの説明を追加してコミットします。
f:id:mistyrinth:20190107163334p:plain

一方、issue3ブランチのmyfile.txtにはpullコマンドの説明を追加します👀
issue3ブランチをチェックアウトし、myfile.txtpullコマンドの説明を追加してコミットします。

f:id:mistyrinth:20190107164028p:plain
これでissue2issue3masterに統合しようとすると、衝突が起きますね…!💥

マージでの衝突を解決する

いよいよ、issue2ブランチとissue3ブランチそれぞれでの変更をmasterブランチに統合します💪
まず、masterブランチをチェックアウトして、issue2ブランチを merge します。

f:id:mistyrinth:20190107164912p:plain

ここでは何の問題も無くfast-forwardマージが行われますね🙂
続いて、issue3ブランチをmasterブランチにマージします。

f:id:mistyrinth:20190107165408p:plain

自動マージに失敗し、myfile.txtには競合箇所が示されています。
前回のおさらいですが、競合箇所の読み方は以下のとおりです!

<<<<<<<
現在チェックアウトしている側の内容(ここではmasterブランチ)
=======
競合相手の内容(ここではissue3ブランチ)
>>>>>>>

これを受け、myfile.txtを以下のように修正します✍️

サルでもわかるGitコマンド
add 変更をインデックスに登録する
commit インデックスの状態を記録する
pull リモートリポジトリの内容を取得する

修正したら、改めてコミットします。
f:id:mistyrinth:20190107180928p:plain
出てくる文字が少ないですが、エラーではないので、たぶんOKだと思います!笑

今回の merge では、競合箇所を修正して、その変更を記録するマージコミットが作成されました。そこにmasterの先頭が移動していることになります🚗
このようにfast-forwardではないマージは、non fast-forwardマージと呼ばれます😂

rebaseでマージする

先程issue3ブランチをマージする際にissue3ブランチを rebase したら、履歴を一本にすることができました💡
rebase のチュートリアルをやってみます🙌
まずは一旦、resetコマンドの--hardオプションで先程行ったマージを取り消します!
resetコマンドはそれだけで結構書ける内容なので、ここでは解説を割愛して以下のコマンドをそのまま実行します。

$ git reset --hard HEAD~

resetコマンドについて詳しくは以下のブログを拝見しました!わかりやすかったです😊

git reset コマンドの使い方と、主要オプションまとめ | WWWクリエイターズ

実行するとこんな感じになります。
f:id:mistyrinth:20190107184018p:plain

これで現状issue2masterブランチにマージ済みで、これからissue3ブランチをマージするところまで戻りました!
ここでissue3ブランチをチェックアウトし、masterブランチに対して rebase を実行します!

f:id:mistyrinth:20190107184349p:plain

やはりmyfile.txtで競合が発生するため、mergeの時と同じmyfile.txtを修正します。
myfile.txtの中身は先程と全く同じなので、修正も割愛します〜👣

競合箇所を修正したら、rebase の場合にはコミットではなく、rebaseコマンドに--continueオプションを指定して実行します。
これにより、masterブランチはissue3ブランチをfast-forwardマージできるようになります✨
masterブランチをチェックアウトしてマージを実行します。

f:id:mistyrinth:20190107185913p:plain
mergeと比べて履歴(経緯)は異なりますが、myfile.txtの最終的な内容は同じです🤝

1-3. リモートリポジトリ

リモートリポジトリに対する動作として、pushやpullを学びました。
参考記事:Git入門 part1 / Gitの基本

おさらいを兼ねて、Fetchも合わせて意味を確認します!

用語 意味
Push ローカルリポジトリの履歴をリモートリポジトリにアップロードすること
Pull リモートリポジトリの履歴をローカルリポジトリにダウンロードすること
自動的にマージコミットが作成されるが、競合があれば手動で解決する
内部的に Fetch + merge をしている
Fetch マージせずにリモートリポジトリの履歴を取得すること
取得したコミットは名前の無いブランチとして作成される
(FETCH_HEADという名前でチェックアウトできる)

本日は以上です🙂