近藤です。こんにちは。Gitは様々な利用の仕方ができますが、その基盤となるモデルは8個だけの簡単なモデルです。これらのモデルを理解していない状態でGitを利用すると、あたかもリポジトリが壊れたように見えてしまいます。Gitは難しいと言われますが、そういう感想を持つ人はGitのモデルを理解していない事が多いようです。
今回はGitを構成する中心モデルと、基本的なコマンドを実行した時のオブジェクト関係を解説します。
基本概念
Gitの基本概念は大きく2つにわかれます。
- GitObject
- Reference
GitObjectはGitで管理するオブジェクトです。CommitなどがGitObjectです。Gitリポジトリである.gitを開くとobjects配下にあるファイルがGitObjectです。GitObjectはそのコンテンツをハッシュ化した文字列を元に、先頭2文字で配置フォルダ、残りの文字列でファイル名です。
ReferenceはCommitを参照しています。BranchなどがReferenceです。Gitリポジトリである.gitを開くと、refs配下にあるファイルがReferenceです。ReferenceのコンテンツはCommitのIDです。
それぞれの概念をまとめたのが下記のモデル図です。それぞれの概念について解説します。
- Commit – コミットグラフを表現する概念です。
Commitのコンテンツはcommentだけでなく、1つ前のCommit、ファイルツリーを示すTree、作成者と編集者を示すUser、作成日時、編集日時があります。rebaseやcherry-pick、commit --amendを行うとCommitのIDが変更になりますが、それは1つ前のCommitが変更になったり、編集日時が変更になるためです。 - User – コミットを扱った人を表現する概念です。GitHub等ではユーザーアイコンを表示します。これは
emailを使って表示しています。 - Tree – フォルダツリーを表現する概念です。Treeは、子フォルダを示すTreeと、ファイルを示すBlobを持ちます。Treeへの参照、Blobへの参照共にnameがあります。
- Blob – ファイルを表現する概念です。ワーキングツリーのファイルそのものがコピーされます。
.gitの中には、これまで編集が少しでもあったファイルが全て入っています。
Blobオブジェクトがファイルそのものであるため、.git/objectsの容量は大きくなりがちです。少しでも抑えるため、全く同一のコンテンツであれば新たにファイルを作らなくても済むませるためのハッシュ文字列のファイル名だったり、ファイルの圧縮だったりします。 - Branch – ブランチそのものです。配置フォルダにより、ローカルリポジトリのブランチだったり、リモートリポジトリのブランチだったりします。
.git/refs/heads配下にあるReferenceがローカルリポジトリのブランチです。.git/refs/remotes配下にあるReferenceがリモートリポジトリのブランチです。.git/HEADに書かれたrefsが現在チェックアウト中のブランチを示します。ブランチをチェックアウトしている場合、コミットがあると、コミットに追従しますが、.git/HEADから参照されたブランチのコンテンツを更新しているだけです。 - Tag – タグそのものです。
.git/refs/tags配下にあるReferenceです。
Gitは様々な機能を提供していますが、基本となるモデルはこれだけです。これらの概念を元に作成されたオブジェクトが下記のように関連しあっているのです。
コマンドで起きること
Gitはファイル名と配置フォルダにより、それぞれのオブジェクトを強く意味付けしています。Gitでよく使うclone、fetch、push,pullの4つのコマンドで起きることをモデル図にまとめてみました。
clone
cloneを実行すると指定したリモートリポジトリからGitObjectとReferenceを取得し、masterブランチをローカルブランチに作成します。
fetch
fetchを実行すると、指定したリモートリポジトリからGitObjectとReferenceを取得します。ローカルリポジトリのリモートブランチが更新されるだけです。ローカルブランチは更新されません。
push
pushを実行すると、指定したリモートリポジトリにGitObjectとReferenceを送信します。リモートリポジトリのブランチとローカルブランチがFast Forwardではなかった場合、エラーが返ります。push -fはFast Forwardチェックを飛ばしているだけです。 ブランチを指定している場合は、そのブランチと、ブランチに紐づくGitObjectだけ送信します。
pull
pullを実行すると、指定したリモートリポジトリからGitObjectとReferenceを取得し、ローカルブランチにリモートブランチをマージします。
リポジトリの設定
リポジトリの設定は.git/configにかかれています。開いてみると下記のようなファイルになっているでしょう。
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = false
[remote "origin"]
url = git@github.com:ChangeVision/astah.git
fetch = +refs/heads/*:refs/remotes/origin/*
[ branch "master" ]
remote = origin
merge = refs/heads/master
この例では下記の内容が記述されています。
リポジトリ自体の設定
[core]の辺りです。共用のリポジトリではないのでbare = falseとなっている、等がわかります。
リモートリポジトリの名前とURL
[remote "origin"]の辺りです。originという名前のリモートリポジトリのURLはgit@github.com:ChangeVision/astah.git、ブランチのfetch対象は+refs/heads/*:refs/remotes/origin/*と書かれてます。
ローカルブランチとリモートブランチをひもづけるUpstreamブランチ
[ branch "master"]の辺りです。masterブランチのUpstreamはoriginのrefs/heads/masterと設定されています。
Gitの基本的な概念と設定についてざっくりと解説しました。案外単純だったのではないでしょうか?
こういった感じでモデル化してみると、文章だけでは見えなかった概念のつながりが見えるので、よりわかりやすかったのではないでしょうか?こういう感じで、利用しているツールの概念をモデル化してみると、よりよい使い方がわかるのでオススメです。
参考文献
ちなみに、モデルは、astah*を使って描きました。
2 thoughts on “Gitのデータモデル”