超初心者向け解説Gitとは何か?
Gitとは何かを説明してみよう。ウェブ検索すればいくらでも説明が見つかる。。。と思ったのだが、難しい言葉だらけでさっぱり要領を得ないことがわかったので、思い切り簡単に説明してみることにする。
ここでは、ほとんど何も知らなくとも、gitがわかったような気になることを目指す。したがって、細かい部分は無視している。
そもそも何を解決したかったのか?
そもそもは、プログラム作りで遭遇する問題を解決したかったのである。それは、
- 極めて複雑であり、
- しょっちゅう、あちこちが変更され、
- 大勢で一斉になされる。
- それらの人々が地理的に離れている。
この場合に何が問題になるかと言えば、
- 間違った変更をしてしまう可能性がある。
- 誰がそんな変更をしたのか知りたい。
- 間違う以前に戻したいかもしれない。
- 矛盾した変更をしてしまうかもしれない。
- 過去のリリース時点でのコードがどうなっていたか知りたい。
と言ったところだ。「矛盾した変更」とは、例えば、Aがこの機能は不要と思い削除するが、Bはそれを改善強化した場合などだ。
上は多人数での開発の場合だが、たとえ一人であっても、こういった機能が必要なことはわかるだろう。
gitが何をしてくれるのか
で、gitが何をしてくれるのかと言えば、上の解決である。
- 任意の時点の状態を見ることができる。
- その時点の状態に戻ることができる。
- 誰がそんなことをしたのかがわかる。
- 例えば、現時点と任意の時点を比較して何が変更されたのかわかる。
- ブランチを作ることができる。
ブランチとは、うまく行くかどうかわからないため、いったんすべてのコピーを作り、そこで好き放題に変更してみて、うまく行けば皆に公開するといったものと考えれば良い。必ずしも公開するわけではないため、失敗を恐れずに試してみることができる。詳細は後述する。
gitのフォルダ構成
基本的な構成としては以下のようなものだ。
あるプロジェクトがあるとして、その共有リポジトリが一つだけ存在する。各個人はそれをコピーして自分の作業を開始する。これをクローンと言うが、最初は「クローン」として全く同じ物を作る。
開始時点で、個人リポジトリは、共有リポジトリと全く同じ物になる(差異もあるが後述)
個人Xは自身のリポジトリの上で作業し、満足の行く作業ができたら、それらの変更を共有リポジトリに書き加えてやる。これをプッシュ(押し込む)という。
そのままでは、個人YにはXの作業した分が見えないので、適宜共有リポジトリからプル(引っ張る)を行う。すると、YにもXの作業分が見えるようになる。
変更点をプッシュで共有リポジトリに加え、他の者はプルで最新状態にする
個人リポジトリの構成
個人リポジトリのファイル構成としては、例えば以下のようなものだ。実際には「リポジトリ」と「作業領域(ワーキングツリー)」というものがセットになっている。
sample
+-- .gitフォルダ
+-- Sample.java
上は、作業領域(ワーキングツリー)の中にSample.javaというプログラムファイル一つだけを格納した例だ。つまり、
- .gitフォルダ以下が実際のリポジトリ
- 他は作業領域内の編集対象ファイル
になっている。.gitフォルダ以下は、いわばデータベースであり、作業領域内での変更状況を記録しており、ここは直接いじってはいけない。必ずgitの機能を介してワーキングツリーのやりとりを行わせる。例えば、
- ある時点での状態を作業領域として出現させる。
- 作業領域で行った変更をデータベースに記録する。
などだ。もちろんこれらに加え、
- 共有リポジトリでの変更をプルして、データベースに格納すると共に作業領域にも反映させる。
- データベースに記録した変更を共有リポジトリにプッシュする。
等などの操作がある。
個人リポジトリは、すべての状態を格納するデータベースと作業領域がセットになったものである
共有リポジトリの構成
共有リポジトリも全く同じ形をしているのだが、ただし、共有リポジトリで直接編集する者はいないため、ここには上で説明した.gitフォルダ部分のみがある。これをBareリポジトリという。拡張子を.gitとするのが慣例である。
sample.gitフォルダ(上に述べた.gitフォルダ部分)
共有リポジトリには作業領域は無い(必要無い)、データベースのみがある
gitのコマンドはgitデータベースを扱うためのコマンド
上に述べたことを図にすると、以下のようになる。
共有部及び各個人がそれぞれデータベースを持っているのだが、gitのコマンドが何をするものかと言えば、
- これらのデータベース間のやりとりを行う。変更をプッシュして加えたり、プルでその変更を得る。
- 作業領域に施した変更を、自身のデータベースに記録する。
- データベースのある時点の履歴で作業領域を置き換える。
などなどだ。
作業領域は「ある時点の状態」しか表現しないのだが、データベースにはあらゆる時点の状態が格納されている。そこには、
- いつどんな変更を行ったか
- 誰がその変更を行ったか
がある。これを履歴機能で見ることもできるし、ある時点の状態を作業領域に再現することもできる。
コミットとは?
データベースの間の同期をとるために、PushやPullを行うのだが、それ以前に「個人が作業領域に加えた変更をデータベースに反映させる」ことが必要だ。
作業領域で何らかの変更を行っても、それがデータベースに自動的に反映されるわけではない。作業者が「これでOKだ」と思った段階で初めてデータベースに反映させるわけである。これをcommitと呼ぶ。
このコミットが何度も行われた後に、その成果を皆に公開すべくプッシュが行われる、といった具合だ。
コミットの際は必ずコメントの入力が求められる。そして、いつ誰がどのような理由で変更を行ったかがコミット履歴として記録されていく。
さらに、コミットした内容を共有データベースに反映させるにはプッシュを行わねばならない。つまり、以下のようなイメージだ。
ブランチとは?
さて、複数のデータベースのやりとりで一つのプロジェクトを作っていければ良いのだが、ここで問題になるのが例えば以下だ(これはあくまでも例である)。
- このプロジェクトにとって、かなり斬新なこと、既存の構造を大幅に変更するような新しいことをやろうとした場合、その途中経過が現在の安定リリースに影響を与えてはいけない。
例えば、プロジェクトを1.0としてリリースした後のこと、バグ修正やら何やらで1.0.1、1.0.2等をリリースするかもしれないが、その間に将来の2.0の作業もしたい。しかし、2.0には根本的な変更があり、安定するまではこのコードを1.0系列には混ぜたく無いものとする。
例えばこの場合にブランチを使う。次のように本流から枝分かれさせることができる。
そして、1.0のメンテナーは1.0系列のコードにのみ集中し、2.0開発者は2.0の新たなコードに取り組め、基本的にはお互いに影響を及ぼすことがなく作業を進められるという具合だ。
データベースの中のブランチとブランチ名称
さて、個人リポジトリには、データベースと作業領域が含まれると書いた。今、ある人が2.0の作業を開始したとすると、以下のようなイメージになる。
この人が、自身のデータベースの中で2.0用のブランチを作成し、1.0を基盤として、2.0用の変更をどんどんと作業領域で進め、その変更をデータベースに書いていく。
実は、ブランチには必ず名前が必要で、作業はどのブランチに対して行うのかを明確にしなければならない。
そして、以前に1.0の作業を開始した時点でも、それは「ブランチ」に対して行われ、その名前は慣例では「master」になっている。つまり、デフォルトブランチはmasterということだ。
2.0を開始する際には、新たな名前をつけなければならないが、好きな名前で良い。とりあえずsecondという名前にしておく。
2.0を開始した人が、このsecondというブランチを作成する。この時点での各データベースは以下の状態だ。以下ではデータベース部分のみを図示している。
ブランチをどうするか?
こういった状態なので、2.0を開始した人は以下ができる。
- 1.0系がすべて格納されているので、履歴等を参照することができる。
- しかし、1.0系に影響を与えず、好きなことができる。
- 気に入らなければブランチをまるまる消してしまってもよい。
実際には、「2.0系」といった大掛かりなものを想定しなくとも、ブランチは個人が好き勝手にいつでも作成できるのである。何かしらを試してみて、うまく行けばよし、うまく行かなければまるごと削除してしまってよい。「本流」には何の影響も与えずに何でも試すことができる。
ただし、他にも2.0の作業をする者がいるなら、これも共有データベースに入れることもできる。
マージとは?
2.0を最終的にリリースすることにするなら、これを本流に混ぜ合わせる。これをマージという。
上はあくまでも、これ以降1.0系列をメンテナンスしないという前提である。1.0をそのまま維持し、2.0はsecondというブランチでリリースを行っても良い。
この例では、1.0、2.0のような大きなリリースを考えたのだが、実際には、「細かい修正」にブランチを使って構わない。しかも、共有リポジトリに入れなければ良いことなので、個人がいくらでブランチを作ることができる。
この場合、常に「本流」にマージすることになるが、マージした後はブランチを削除してしまってよい。そうでないと、個人データベースの中に無数にブランチができてしまい、どれが何の修正なのかわからなくなる。
ブランチをマージした後であれば、そのコミット履歴はマージ先に残っている。つまり、どのような変更履歴であったかもマージされる。