git log 1a410e
のように実行すれば、すべての歴史に目を通すことができます。しかし、歴史を辿ってすべてのオブジェクトを探しだすには、 1a410e
が最後のコミットであることを覚えていなければならないのは変わりません。
SHA-1ハッシュ値をシンプルな名前で保存できれば、生のSHA-1ハッシュ値ではなく、その名前をポインタとして使用できます。
Gitでは、これは 参照'' ないしは
refs'' と呼ばれます。 .git/refs
ディレクトリを見ると、SHA-1ハッシュ値を含むファイルがあることが分かります。
現在のプロジェクトでは、このディレクトリにファイルはありませんが、シンプルな構成のディレクトリがあります。
$ find .git/refs
.git/refs
.git/refs/heads
.git/refs/tags
$ find .git/refs -type f
参照を新しく作成して、最後のコミットがどこかを覚えやすくします。技術的には、以下のように簡単に行えます。
$ echo "1a410efbd13591db07496601ebc7a059dd55cfe9" > .git/refs/heads/master
これで、Gitコマンドで、SHA-1ハッシュ値の代わりに、たった今作成したhead参照(ブランチ)を使えるようになりました。
$ git log --pretty=oneline master
1a410efbd13591db07496601ebc7a059dd55cfe9 third commit
cac0cab538b970a37ea1e769cbbde608743bc96d second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit
参照ファイルを直接変更するのは推奨されません。
その代わり、参照をより安全に更新するためのコマンド update-ref
が、Gitには用意されています。
$ git update-ref refs/heads/master 1a410efbd13591db07496601ebc7a059dd55cfe9
これは、Gitにおいて、基本的にブランチとは一連の作業の先頭を指す単純なポインタや参照であるということを表しています。 2つ目のコミットが先頭になるブランチを作るには、次のようにします。
$ git update-ref refs/heads/test cac0ca
作成されたブランチは、さきほど指定したコミット以前の作業のみを含むことになります。
$ git log --pretty=oneline test
cac0cab538b970a37ea1e769cbbde608743bc96d second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit
この時点で、Gitのデータベースは概念的には以下の図のように見えます。
git branch (ブランチ名)
のようなコマンドを実行すると、あなたが作りたいと思っている新しい参照が何であれ、基本的にGitは update-ref
コマンドを実行して、いま自分がいるブランチ上の最後のコミットのSHA-1ハッシュをその参照に追加します。
では、git branch (ブランチ名)
を実行したときに、Gitはどうやって最後のコミットのSHA-1ハッシュを知るのでしょうか?
答えは、HEADファイルです。
HEADファイルは、現在作業中のブランチに対するシンボリック参照です。 通常の参照と区別する意図でシンボリック参照と呼びますが、これには、一般的にSHA-1ハッシュ値ではなく他の参照へのポインタが格納されています。 ファイルの中身を見ると、通常は以下のようになっています。
$ cat .git/HEAD
ref: refs/heads/master
git checkout test
を実行すると、Git はこのようにファイルを更新します。
$ cat .git/HEAD
ref: refs/heads/test
git commit
を実行するとコミットオブジェクトが作られますが、そのときコミットオブジェクトの親として、HEADが指し示す参照先のSHA-1ハッシュ値が指定されます。
このファイルを直に編集することもできますが、symbolic-ref
と呼ばれる、編集を安全に行うためのコマンドが存在します。
このコマンドを使ってHEADの値を読み取ることができます。
$ git symbolic-ref HEAD
refs/heads/master
HEADの値を設定することもできます。
$ git symbolic-ref HEAD refs/heads/test
$ cat .git/HEAD
ref: refs/heads/test
refs
の形式以外では、シンボリック参照を設定することはできません。
$ git symbolic-ref HEAD test
fatal: Refusing to point HEAD outside of refs/
これで Git の主要な三つのオブジェクトを見終わったわけですが、タグという4つ目のオブジェクトがあります。 タグオブジェクトは、コミットオブジェクトに非常によく似ており、タガー、日付、メッセージ、ポインタを格納しています。 主な違いは、タグオブジェクトは通常、ツリーではなくコミットを指しているということです。 タグオブジェクトはブランチに対する参照に似ていますが、決して変動しません – 常に同じコミットを指しており、より分かりやすい名前が与えられます。
ch02-git-basics.asc で述べましたが、タグには2つのタイプがあります。軽量 (lightweight) 版と注釈付き (annotated) 版です。 次のように実行すると、軽量版のタグを作成できます。
$ git update-ref refs/tags/v1.0 cac0cab538b970a37ea1e769cbbde608743bc96d
これが軽量版のタグのすべてです。つまり決して変動しない参照なのです。
一方、注釈付き版のタグはもっと複雑です。
注釈付き版のタグを作ろうとすると、Gitはタグオブジェクトを作った上で、コミットを直接指す参照ではなく、そのタグを指す参照を書き込みます。
注釈付き版のタグを作ると、これが分かります( -a
オプションで、注釈付き版のタグを作るよう指定しています)。
$ git tag -a v1.1 1a410efbd13591db07496601ebc7a059dd55cfe9 -m 'test tag'
作られたオブジェクトのSHA-1ハッシュ値はこうなります。
$ cat .git/refs/tags/v1.1
9585191f37f7b0fb9444f35a9bf50de191beadc2
ここで、そのSHA-1ハッシュ値に対して cat-file
コマンドを実行します。
$ git cat-file -p 9585191f37f7b0fb9444f35a9bf50de191beadc2
object 1a410efbd13591db07496601ebc7a059dd55cfe9
type commit
tag v1.1
tagger Scott Chacon <[email protected]> Sat May 23 16:48:58 2009 -0700
test tag
object
の項目が、上でタグ付けしたコミットのSHA-1ハッシュ値を指していることに注意してください。
また、この項目が必ずしもコミットだけをポイントするものではないことも覚えておいてください。あらゆるGitオブジェクトに対してタグを付けることができます。
例えば Git のソースコードリポジトリでは、メンテナが自分のGPG公開鍵をブロブオブジェクトとして追加し、そのオブジェクトにタグを付けています。
Gitリポジトリのクローン上で、以下のコマンドを実行すると公開鍵を閲覧できます。
$ git cat-file blob junio-gpg-pub
Linux カーネルのリポジトリもまた、object
項目でコミット以外を指しているタグオブジェクトを持っています。
これは最初のタグオブジェクトであり、最初にソースコードをインポートしたときの初期ツリーオブジェクトを指しています。
これから見ていく3つ目の参照のタイプはリモート参照です。
リモートを追加してそこにプッシュすると、Gitはそのリモートへ最後にプッシュした値を、ブランチ毎に refs/remotes
へ格納します。
例えば、 origin
というリモートを追加して、そこに master
ブランチをプッシュしたとします。
$ git remote add origin [email protected]:schacon/simplegit-progit.git
$ git push origin master
Counting objects: 11, done.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (7/7), 716 bytes, done.
Total 7 (delta 2), reused 4 (delta 1)
To [email protected]:schacon/simplegit-progit.git
a11bef0..ca82a6d master -> master
ここで refs/remotes/origin/master
ファイルの中身を確認してみてください。最後にサーバーと通信したときに origin
リモートの master
ブランチが何であったかがわかるはずです。
$ cat .git/refs/remotes/origin/master
ca82a6dff817ec66f44342007202690a93763949
リモート参照は、特に読み取り専用とみなされる点において、ブランチ(refs/heads
にある参照)とは異なります。
リモート参照に対して git checkout
を行うことはできますが、GitはHEADの参照先をそのリモートにすることはなく、したがって commit
コマンドでリモートを更新することもできません。
Gitはリモート参照を一種のブックマークとして管理します。つまり、最後に通信したとき、向こうのサーバー上でリモートブランチが置かれていた状態を指し示すブックマークということです。