フライさん(loxygen)です。限界開発鯖でのだのだ言っています。フライさんにおまかせなのだ!
説明はあまり得意ではありません。
※この記事は茨城高専生向けに書かれたものです。
お説明
ここからちょっと難しくなってきます。対戦よろしくおねがいします。
今回は「ブランチ」について解説していきます。このお話は、プログラマーを世界を操る神として例えるとしやすい(当社比)ので、神になりきって読んでくださると読みやすいかと思います。
ブランチってどんなの?
ブランチというのは、履歴を分岐して記録していくための機能です。噛み砕くと並行世界のようなものでしょうか。
ブランチを新しく作成することを「ブランチを切る」といいます。
「ブランチを切る」のは「並行世界を新しく創る」と同義ですね。
どういうタイミングで切っていくんや?
大体は、一つの機能ごと、または機能の小さな集まりごとにブランチを切っていきます。テキストエディタを例に取るなら、
- テキストを編集する機能を実装するブランチ
- ファイルを読み込み・保存する機能を実装するブランチ
もちろん他にもいろいろなやり方があります。
masterブランチ
リポジトリを作成すると、必ず一つ、master
という名前がついたブランチが生成されています。いわゆる現実世界です。
masterブランチがメインのブランチになります。プログラムの完成品を置いていく場所ですね。
マージ
並行世界でたくさん変更を加えたあとは、まとめて現実世界にその変更を反映させます。並行世界だけに変更を加えても現実世界は何の変化もないですからね。
この「まとめて現実世界にその変更を反映させる」ことを**マージ(merge)**と言います。
ブランチの存在意義
変更履歴が必然的に美しくなる
ブランチを切らないで、何も考えずに実装していったとしましょう。
以下の図はコミット履歴を表した図です。
一つの○が一つのコミットで、線がコミット同士のつながりです。同じ色のコミットは同じ機能を実装しています。
複数の機能の実装が入り組んでしまっていて、履歴がわかりにくくなっています。
今は問題ないかも知れませんが、後から見たり、また他の人が見たりしたときに「」となる可能性は十分あります。
ここでブランチを使用してみましょう。
一つ、または機能のまとまりごとにブランチを切っていくと説明しましたね。機能(=同じ色)ごとにブランチを切ってみましょう。
具体的には、こんな感じになります:
masterブランチの編集履歴が単純化されていますね。複数の機能が混じることなく、機能実装ごとにコミットが分かれています。
上の図と比べたら大分キレイになっていますね。
あまり詳しく考えずに「嗚呼確かにそうかもな」ってだけ思って頂ければ幸いです。
品質を保証できる
ブランチがあることのもうひとつの利点は、master
ブランチ上のプログラムの品質を保証できることです。
現実世界でバグが起こったら嫌ですよね。SCP1どころの騒ぎじゃなくなるかもしれません。
そこで、各々のブランチ(=並行世界)でバグを潰したあとにmaster
ブランチにマージします。
するとどうなるでしょうか。master
ブランチ(=現実世界)のバグはかなり抑えられた状態になります。つまりこれは、master
ブランチ上のプログラムの品質が保証されているということになりますね。
これがもう一つの利点です。
キーボードかたかたしよう
そろそろ用語の解説に飽きてきたので、ブランチ切ってコミットしてマージしてみましょう。
作業したいフォルダでGit Bashを開いておいてくださいね。
リポジトリを生成してInitial Commitする
リポジトリがないとマジで何も始まらないので作ります。
以下のコマンドを入力してEnterを押してください。
こるくも言っていましたが、$
は入力しないでくださいね。
$ git init (任意のリポジトリ名)
「(任意のリポジトリ名)
」は、お好きな名前にしてください。
Enterを押すと
Initialized empty Git repository in /path/to/your/selection/(任意のリポジトリ名)/.git/
と表示されます。新しいリポジトリが生成されました。
リポジトリに移動しましょう。
$ cd (任意のリポジトリ名)
ブランチはコミットを単位に切っていきます。つまりコミットがないとブランチを切ることができません。というわけでInitial Commitをしちゃいましょう。
リポジトリにhello.txt
という名前で以下のファイルを生成して保存してください。
I am the smartest person.
そうしたらInitial commitします。
Add
を忘れないでくださいね。筆者は忘れてしまいました。
$ git add --all
git status
を確認する癖をつけましょう。つけて困ることはありません。
$ git status
すると以下の出力を得ることができます。
On branch add-new-func
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: hello.txt
「緑色でnew file: hello.txt
」 …「次のコミットで hello.txt
が新しいファイルとして登録されるで!」と出力されていますね。新しくhello.txt
というファイルを作ったので正常です。
もしhello.txt
が赤く表示されていれば**add
し忘れています**。しましょう。
このままコミットして問題なさそうなのでコミットしちゃいましょう。
$ git commit -m "Initial Commit"
以下のように表示されます。
[master (root-commit) 1234567] Initial Commit
1 file changed, 1 insertion(+)
create mode 100644 hello.txt
これでこのリポジトリのコミット履歴はこうなりました:
Initial Commitだけがある状態です。HEAD(≒現在位置)もInitial Commitにいます。
ブランチを切る
これまで入力と出力を分けて書いていましたが、ここから一緒に書いてしまいます。
入力する際は、$
から行末までを入力してください。
ここで並行世界を創世しましょう。新しいコマンドのお出ましです。
以下のコマンドを入力してEnterを押してください。
$ git branch add-new-func
このコマンドを叩くと、add-new-func
というブランチが切られます。新しく並行世界を創世するわけです。
並行世界を創世しただけじゃ何も始まらないので、創世した並行世界に移動しましょう。
$ git checkout add-new-func
Switched to branch 'add-new-func'
このコマンドを叩くと**add-new-func
ブランチに移動します**。
ちなみに以下のコマンドを打つと上記の2つの操作を同時に行うことができます。
$ git checkout -b add-new-func
Switched to a new branch 'add-new-func'
通常はこちらを使います。一行で済んで楽ですからね。
試しにgit status
を叩いてみましょう。
$ git status
On branch add-new-func
nothing to commit, working tree clean
On branch add-new-func
、つまり「add-new-func
ブランチにいますよ」と表示されていますね。
さて、一連のコマンドを打った後、コミット履歴はこうなりました。
**・・・?**なんも変わってないやん。
なんかadd-new-funcブランチ
って文字列は認識できるけどそれ以外はなんも変わってないやん。
この時点ではまだコミット履歴上の変化はありません。それではここにコミットをしてみましょう。
並行世界に歴史を刻む
先程作ったファイルを以下のように変更してください。
I am the kindest human.
そしてコミットします。コミットメッセージは何でも構いません。
$ git add --all
$ git status
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: hello.txt
$ git commit -m "Edit hello.txt"
[add-new-func c48284a] Edit hello.txt
1 file changed, 1 insertion(+), 1 deletion(-)
あっ!?なんか縦にズレとる!
そうです。並行世界で変更を加えたのでちょっとズレました。
現実世界はどうなってるん?
並行世界(=master
から切ったブランチ)で変更を加えましたが、現実世界(=master
ブランチ)はどうなってるんでしょうか。一旦master
に戻ってどんなもんか見てみましょう。
$ git checkout master
Switched to brach 'master'
このコマンドで現実世界に帰ることができます。-b
はいらないので注意してください。
ちなみに、ブランチ間を移動することを「チェックアウト」と呼びます。
ここで、hello.txt
の中身を見てみましょう。「cat
」というコマンドを使用してみます。
cat
コマンドは、ファイルの中身を見るコマンドです。
全てGit Bashで完結させたかったので使っていますが、もちろんお好みのエディタで大丈夫です。以降についても同様です。
$ cat hello.txt
I am the smartest person.
cat
のあとにファイル名を入力してEnter
すると、そのファイルの中身が表示されます。
お気づきかもしれませんが、並行世界で加えた変更がありません。
でも消し飛ばされたわけではありません。並行世界に帰ってみましょうか。
$ git checkout add-new-func
Switched to branch 'add-new-func'
同じコマンドを打ってhello.txt
を見てみます。
$ cat hello.txt
I am the kindest person.
変更が帰ってきました。
そうです。並行世界で変更を加えても、現実世界には反映されないようになってるんです。
そりゃそうだろと言えばそりゃそうなんですが、なんだかすごいですよね。本当に世界を行き来してるみたいじゃありませんか?
お練習
一旦masterブランチに帰りましょう。
$ git checkout master
Switched to branch 'master'.
そうしたら、以下の操作をしてみてください。
・add-new-func-2ブランチを切る。
・hello.txtの中身を以下のように変更する:
I am the greatest people.
・自由なコミットメッセージでコミットする。
第1回 インストール編
第2回 設定編
第3回 実際に使ってみよう-基本技能編
以下に答えを書いておきます。行き詰まったり、正解を確認したいときに見てくださいね。
お答え
Answer
まずブランチを切りましょう。
$ git checkout -b add-new-func-2
Switched to a new branch 'add-new-func-2'.
そしたらファイルの中身を指示されたとおりに変更します。お好みの方法でどうぞ。
保存したらコミットします。
$ git add --all
$ git status
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: hello.txt
$ git commit -m "(何かしらメッセージをどうぞ)"
[add-new-func d2ad39f] (入力したメッセージ)
1 file changed, 1 insertion(+), 1 deletion(-)
これで完成です!
このルーチンは生涯で$N$回 ($N \geqq 任意の数字$)回やるので、なんとしてでも身につけておきましょう。
マージする
並行世界に変更を加えただけでは現実世界(master
ブランチ)に変更は反映されません。というわけでマージもしてみましょう。
マージの流れ
- マージしたい先のブランチにチェックアウトします。
-
git merge <マージするブランチ>
をします。 - 上手く行けばマージ完了です!
この流れでマージします。上手く行かなかった場合については、次回説明します。
実際にマージしてみる。
今のところ、コミット履歴は以下のようになっているはずです:
master
ブランチからadd-new-func
ブランチとadd-new-func-2
ブランチが生えている状態です。HEAD
は動かすのでどこでも大丈夫です。
それでは並行世界の歴史を現実世界に反映していきましょう。
まず、マージする先のブランチにチェックアウトします。
今回はmasterブランチにMergeしたいので、masterブランチにチェックアウトしましょう。
$ git checkout master
Switched to branch 'master'
一応ここでhello.txt
を見ておきます。
$ cat hello.txt
I am the smartest person.
並行世界上では変わっていますが、現実世界上では変わっていませんね。smartest personのままです。
そうしたら実際にマージしてみます。
$ git merge add-new-func
Updating 1234567..890abcd
Fast-forward
hello.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
なんかいろいろ出てきましたね。エラーは見た感じ出てないみたいです。
hello.txt
を見てみましょう。
$ cat hello.txt
I am the kindest person.
…おっ?中身が変わりましたね。smartest personがkindest personになっています。中身がadd-new-func
ブランチのものと同一**になりました。
ここで、コミット履歴は以下のようになっています。
ァ!なんかくっついてる!2
はい。mergeをしたことによって**add-new-func
ブランチの歴史がmaster
ブランチの歴史に併合しました**。
こうして並行世界の変更が現実世界にも適用されました。
コンフリクト
第5回で詳しく説明しますが、add-new-func-2
ブランチは「コンフリクト」というエラーが発生してマージすることができません。
マージしないまま第5回に進んでくださいね。
マージする前の確認作業
上記のエラーを防ぐために、マージ作業を執り行う前にある確認作業をするとお作法がいいです。3これも一緒に第5回で説明しますので、ぜひ頭に置いておいてください。
おまとめ
- ブランチは並行世界のようなもの
- masterブランチは現実世界のようなもの
- ブランチの利点は:
- 変更履歴が必然的にキレイになる
- masterブランチの品質を保証できる
- ブランチの操作には以下のコマンドを使用する:
-
git checkout -b <ブランチ名>
…ブランチを切る
中身はgit branch <ブランチ名>
→git checkout <ブランチ名>
-
git checkout <ブランチ名>
…ブランチ間を移動(チェックアウト) -
git merge <ブランチ名>
…ブランチをマージする
-
ここまでお疲れ様でした。**が、**ブランチの話にはあと少しだけ続きがあります。
第4回はかなり膨れてしまったのでここで切り上げますが、第5回で同じリポジトリを使用するので取っておいてくださいね。
おリンク
第1回 インストール編
第2回 設定編
第3回 実際に使ってみよう-基本技能編
第4回 いまここ
第5回 ブランチ(平行世界)編-2
第6回 GitHub入門編
第7回 fetch/pull編
第8回 issue/Pull Request編