Gitのデータの持ち方(復習)
今回はブランチの仕組みについて解説します。
ブランチの理解において、Gitのデータの持ち方というのが非常に重要です。
ですので、まずブランチの話に入る前に、Gitのデータの持ち方について思い出してみましょう。 phoeducation.work
Gitはリポジトリの中に圧縮ファイルとツリーファイル、コミットファイルという3つのファイルでデータを保存しています。
例えば、ワークツリーの方に「index.html」というファイルがあった場合、「git add」コマンドで「圧縮ファイルA」が作成されます。
「圧縮ファイルA」というのは「index.html」のファイルの中身を圧縮したものです。
その後は「git commit」コマンドでコミットしていきます。
コミットすると何ができるかというと、「ツリー1」と「コミット1」というファイルができます。
「ツリー1」は「index.html」というファイル名のファイルの中身は「圧縮ファイルA」だよというのを記録しています。
つまりツリーというのは、コミットした時のスナップショットを表しています。
ツリーができたら次にコミットファイルを作っていきます。
「コミット1」というコミットファイルでは、ツリーは「ツリー1」だよということであったり、作成者の名前、日付、コミットメッセージといったものが記録されています。
ここでコミットファイルをより詳しく見ていくと、実際は図のような形のファイルの中身になっています。
一部省略していますが、だいたいこのような形です。
ファイルの中身として、まず「tree」と書いてあり、その横にツリー名、今回だと「ツリー1」と書いてあります。
次に「author」と書いてあって、今回だと「Norris」という名前が書いてあり、その横にgit commitした時の日付が書いてあります。
そして、改行されて、さらに1行空いて、その下にコミットメッセージです。
今回ですと「最初のコミット」というコミットメッセージが記載されています。
これがコミットファイルの中身になります。
もう一つ見て欲しいのが、コミットファイルのファイル名です。
ファイル名が今「051d8」というような英数字になっています。
コミットファイルの実際のファイル名は、このコミットファイルの中身をハッシュ関数というものをかけて、40文字ほどの英数字にしたハッシュ値というものでファイル名はできています。
このハッシュ関数やハッシュ値というのは何かというと、ファイルの中身を40文字の英数字に変換してしまう、それをハッシュ関数と言っています。
そうすることでファイルの中身に応じて一意の英数字ができるので、ファイルの中身が変わればファイル名も変わるし、ファイルの中身が一緒であれば同じファイル名という仕組みになっています。
この図では実際には40文字ほどのファイル名なのですが、先頭5文字を切り出して書いています。
ここで重要なことは、コミットというのはスナップショットで、それが時系列順に連なっているということでした。
「コミット1」をした時、その「コミット1」で「スナップショット1」を記録しているということになります。
ツリーにスナップショットの情報が記載されていて、コミットはそのツリーを指定することで、スナップショットを記録していることと同じことになります。
この「コミット1」の後に、「コミット2」をしたとします。
そうすると「コミット2」のファイル名は例えば「f57e2」というようになっていたとします。
これは先ほど同じように、コミットのファイルの内容にハッシュ関数をかけて40文字ほどの絵数字に置き換えたものから先頭5文字を今回は抜粋して書いています。
この「コミット2」で注目して欲しいのは、親のコミットの情報として「parent」というものを持っているということです。
parentに「コミット1」のファイル名「051d8」というのが指定されています。
次にこの「コミット2」の後に「コミット3」をしたら、その「コミット3」にも「parent」があって、その「parent」は「コミット2」のファイル名である「f57e2」というのを指しています。
gitではこのようにコミットファイルに「parent」というのを指定することで、前のコミットをたどることができるようになっています。
つまり、gitではコミットでスナップショットを記録していて、parentでそのコミットを時系列でたどれるようにしていることで、そのコミットした瞬間のスナップショットを時系列順で記録していて、それをたどることができるという仕組になっています。
ブランチとは
ブランチというのはコミットを指し示したポインタになります。
今状況として先ほどの状態を切り抜いたものです。
コミットを3つしていて、それを切り抜いたものだとします。
その中の「コミット3」のファイルが最新のコミットです。
最新のコミットである「コミット3」の「a6923」というコミットファイルを、mainというブランチが指している状況だとします。
mainというのはgitのデフォルトのブランチ名のことです。
その状況においてfeatureというブランチを新しく作ったとします。
そうするとこのfeatureというブランチも「コミット3」の「a6923」という最新のコミットファイルを指しています。
このようにブランチというのはコミットファイルを指し示したポインタシムリンクに過ぎません。
そのことをイメージとして押さえておいてください。
「HEAD」とは
もう一つ「HEAD」というものについても押さえておきましょう。
これはgitで作業をしているとちょくちょく出てくる単語なので、是非覚えておいてください。
このHEADは何かというと、今自分が作業しているブランチを指し示したポインタになります。
今回の状況ですと、自分はmainブランチで作業しているのでHEADはmainブランチを指し示しています。
このmainブランチというのが「コミット3」のコミットファイルを指し示しているので、今の状況としてはmainブランチで作業していて、そのmainブランチの最新のコミットというのが「コミット3」だよと言うのが分かるようになっています。
ブランチの仕組み
mainブランチとfeatureブランチが「コミット3」のコミットファイルを指し示している状況で、新しくコミットしたらどうなるのか。
コミットした場合にブランチというのはどのように変わっていくのかについて見ていきます。
自分はmainブランチにいるとして、何かファイルを変更して新しくコミットします。
すると、「コミット4」という新しいコミットファイル「47f32」というファイルができます。
ここで確認して欲しいのは、mainブランチが「コミット4」を指し示すように変わっているということです。
これは何が起きているのかというと、コミットしたらブランチが指し示すコミットファイルが、そのコミットした最新のコミットファイルの方を指し示すように変わったということを表しています。
ブランチというのは、あくまでコミットファイルを指し示すポインタに過ぎなくて、コミットするとその指し示しているポイントが最新のコミットの方へ移動するという仕組みになっています。
これで何が嬉しいのかというと、このようにすることで、いま自分が作業しているものの最新のコミットはどれかというのが分かるようになっています。
ではここで、なにか緊急のバグが発見された、そして、それの開発をしなくてはいけないという状況だとしましょう。
そのバグの内容は、「コミット3」の方のファイル内容でそのバグが発見されました。
ですので、まずfeatureブランチの方に今自分が作業するブランチを切り替えます。
そして、何らかの修正をして、コミットしたとします。
すると「コミット4’」という「21aq2」というコミットファイルが作成されました。
ここでも何が起きるかというと、featureブランチが指し示しているブランチが「21aq2」のコミットファイルの方を指し示すように変更されました。
ここでさらに変更を加えて、新しいコミットをしたとします。
すると「コミット5’」というコミットで、「b65bc」というコミットファイルが作成されました。
そしてまたfeatureブランチも「コミット5’」を指し示すように移動しています。
では、ここでもう一度HEADについて見ていきます。
HEADは今自分が作業しているブランチを指し示しているので、今はfeatureブランチを指し示しています。
このように、HEADがfeatureブランチを指し示していて、featureブランチが「b65bc」というコミットファイルを指し示しているので、今作業しているものの最新のコミットというのは「b65bc」というファイルだということが分かります。
この状況、このファイルの全体図を一度改めて振り返って見てください。
すると何が分かるかというと、mainブランチとfeatureブランチという2つのブランチがあることによって、それぞれ別の開発が平行してできていると言うことがわかります。
mainブランチの方では「コミット4」という流れの開発をしていて、featureブランチの方では「コミット4’」、「コミット5’」という別の流れの開発をしています。
このように開発を分岐させることができるのです。
ブランチを使って開発を分岐させることで、Gitでは複数人で複数の機能を平行して開発することをできるようにしています。
これがブランチの一番主要な役割です。
ブランチとHEADの中身
ブランチとHEADの中身について、もう少しだけ詳しく見ていきましょう。
では先ほどの一番最後の状況を切り出してみます。
「コミット4」があって、mainブランチは「コミット4」を指していました。
さらに「コミット5’」があって、featureブランチは「コミット5’」を指していました。
mainブランチとfeatureブランチの中身を確認してみましょう。
mainブランチはmainというファイルがあって、そのファイルの中身は「47f32」という「コミット4」のコミットファイル名が記載されています。
featureブランチも同じようにfeatureというファイルがあって、「b65bc」という「コミット5’」のコミットファイル名が記載されています。
つまりブランチというのは、コミットファイルのファイル名(コミットID)を記録した単なるポインタなのです。
このようにブランチというのは、複雑なことをしているのではなく、本当にシンプルな仕組みなのです。
ではHEADの方はどのようになっているのかというと、HEADというファイルがあって、そのファイルの中身は「ref:feature」というように書いてあります。
この「ref」というのは何かというと、referenceの略で、参照という意味になります。
featureブランチを参照しているということです。
このようにHEADも非常にシンプルな構造をしていて、現在作業中のブランチのファイル名が書いてあるブランチへのポインタなのです。
ちなみにこのmainやfeature、HEADというファイルは、リポジトリの中に記録されています。
もう少し具体的に言うと、HEADに関しては「.git/HEAD」、mainやfeatureブランチに関しては「.git/refs/」の下に記録されています。
具体的な保存場所は特に覚えておく必要はないのですが、「.git/」の下(リポジトリの中)にブランチやHEADの情報が保存されているんだなと言うことは頭の中に入れておいてください。
まとめ
- 分岐することで複数の機能を同時並行で開発するための仕組みがブランチです。
- ブランチとはコミットを指すポインタです。
この「コミットを指す」という特徴と、コミットがスナップショットを記録しているという特徴が相まって
- ブランチの作成や切り替え、マージが他のバージョン管理ツールより高速です。
- 結果、Gitは大規模開発において最も使われるツールとなり、普及しました。
参考図書
独学で挫折しそうになったら、オンラインプログラミングスクール