Gitの参照についてまとめました。また別記事にてGitの内側について記載しています。
Gitの内側についておさらい
- 「git init」すると「.git」ディレクトリができるがその中についてがテーマ
- 普段は使わない配管コマンドと呼ばれる下位レベルのコマンドがある
- 4つのオブジェクトがある
- blobオブジェクト(ファイルに相当)
- treeオブジェクト(ディレクトリに相当)
- commitオブジェクト(commit情報を保持)
- tagオブジェクト(Tagを保持)
Gitの参照って?

ここで取り上げる Git の参照は「HEAD」ファイルと「.git/refs」配下のことを示します。「git init」した直後「HEAD」ファイルは「refs/heads/master」を参照し(初期化直後はなにも存在しない)、「.git/refs」配下には、heads と tags ディレクトリがあります。ブランチに commit すると heads 配下にブランチ名のファイルができ、HEAD ファイルは heads 配下のブランチ名を参照します。
# masterにcommit後
# HEADファイル
ref: refs/heads/master
# refs配下の構造
└── refs
├── heads
│ └── master
└── tags
Gitの参照の種類
Gitの参照の種類は次の通りです。
- heads:作業中ブランチの commit を参照
- tags:commit をわかりやすくする為の名前付け用
- remotes:リモートブランチの commit を参照
- HEAD:作業中のブランチに対するシンボリック参照。HEAD は基本的に heads を参照しているので、他の参照と区別する為シンボリック参照と呼ばれます
動作確認の為の事前準備
次のような3つの commit があるリポジトリを用意し remote へ push しておきます。
% git log --pretty=oneline master
1f36229bc26825222b191dcd63929392d9d8e2fd third commit
543ee947f5a865e1a998f793120667c3bf89fc80 second commit
23391397b4316d334e9293c4f7d1d601c1f24c4c first commit
% git remote add origin git@github.com:taisa007/internal-git.git
次からはこのリポジトリを利用します。クローンする場合はこちらを利用してください。
構成
構成は次の通りです。「.git/refs」ディレクトリ配下にそれぞれheads, remotes, tagsというディレクトリが作られています。
% tree .git
└── refs
├── heads
│ └── master
├── remotes
│ └── origin
│ └── master
└── tags
Gitの参照の解説
「refs」配下にあるファイルはそれぞれ最後の commit をファイルに記録しています。例えば現在master は「third commit」にいますが「git log –pretty=oneline master」とすると次のようなcommit 履歴が確認できます。
% git log --pretty=oneline master
1f36229bc26825222b191dcd63929392d9d8e2fd third commit
543ee947f5a865e1a998f793120667c3bf89fc80 second commit
23391397b4316d334e9293c4f7d1d601c1f24c4c first commit
# このコマンドでも上記と同じ結果となる
% git log 1f36229bc26825222b191dcd63929392d9d8e2fd
1f36229bc26825222b191dcd63929392d9d8e2fd third commit
543ee947f5a865e1a998f793120667c3bf89fc80 second commit
23391397b4316d334e9293c4f7d1d601c1f24c4c first commit
# masterファイルの中を確認
% cat .git/refs/heads/master
17fb14ac7c9493751cff838ba0c22897582406a5
# 現在の位置のハッシュ値が書かれている
これはハッシュを指定した次のコマンド「git log –pretty=oneline 17fb14ac7c9493751cff838ba0c22897582406a5」と同じ結果です。「.git/refs/heads/master」ファイルにcommit情報が記述されている為同じ結果となります。上記のことから「.git/refs/heads/master」に commit のハッシュ値を指定することで master の head が変更可能です。ただし直接操作は非推奨(通常操作することはありませんが)なので参照を変更する時は次のコマンドを使います。
# 参照を変更
% git update-ref refs/heads/master 543ee947f5a865e1a998f793120667c3bf89fc80
# headが変わった
% git log --pretty=oneline master
543ee947f5a865e1a998f793120667c3bf89fc80 second commit
23391397b4316d334e9293c4f7d1d601c1f24c4c first commit
% cat .git/refs/heads/master
543ee947f5a865e1a998f793120667c3bf89fc80
「second commit」に変わりました。また次のコマンドはそこからブランチを切ることができます。
% git update-ref refs/heads/test 543ee947f5a865e1a998f793120667c3bf89fc80
# testブランチができた
% git branch
master
test
「.git/refs」ディレクトリの構成を見ていきましたので、次は「HEAD」ファイルと「.git/refs」を構成する各参照について解説します。
HEAD
HEADファイルは現在作業中のブランチを参照しています。「git branch (ブランチ名)」を実行したときは、HEADからブランチが切られます。HEADファイルは 「.git」直下に配置されます。
.git
├── COMMIT_EDITMSG
├── FETCH_HEAD
├── HEAD ← ココ
├── ORIG_HEAD
├── config
├── description
├── hooks
│ ├── applypatch-msg.sample
│ ├── commit-msg.sample
│ ├── post-update.sample
│ ├── pre-applypatch.sample
│ ├── pre-commit.sample
│ ├── pre-push.sample
│ ├── pre-rebase.sample
│ ├── prepare-commit-msg.sample
│ └── update.sample
・
・
・
## HEADファイルの中を確認
% cat .git/HEAD
ref: refs/heads/master ← refs/heads配下を参照している
「git checkout test」を実行した内容は次の通りです。
% git checkout test
cat .git/HEAD
ref: refs/heads/master
この HEAD ファイルを確認したり変更するには次のコマンドを使います。
# 確認
% git symbolic-ref HEAD
refs/heads/test
# 変更
% git symbolic-ref HEAD refs/heads/master
% git symbolic-ref HEAD
refs/heads/master
tags
tagは Git のオブジェクトでもあります。tag オブジェクトは commit オブジェクトに似ていますが、tree ではなく commit を指し示します。また、tag には軽量 (lightweight) 版と注釈付き (annotated) 版の2つのタイプがあります。
軽量 (lightweight) 版
- tag をつけられる
- commit オブジェクトに tag がつく
- tag オブジェクトではない
# tagを付ける
% git update-ref refs/tags/v1.0 543ee947f5a865e1a998f793120667c3bf89fc80
# 確認
% git tag -n
v1.0 second commit
注釈付き (annotated)版
- tagとコメントがつけられる
- tagオブジェクトになる
# tagとコメント付き
% git tag -a v1.1 23391397b4316d334e9293c4f7d1d601c1f24c4c -m 'test tag'
% git tag -n
v1.0 second commit
v1.1 test tag ← コメントが付く
% cat .git/refs/tags/v1.0
543ee947f5a865e1a998f793120667c3bf89fc80 ← second commitのハッシュ
% cat .git/refs/tags/v1.1
0d69685716366ce02ea4452c8e47c64e779a40d8 ← 新しいハッシュ
% git cat-file -p d4f4075dfccb64a4c1b1966b6c7aa853f526c33a
object 23391397b4316d334e9293c4f7d1d601c1f24c4c ← first commitのハッシュ
type commit
tag v1.1
tagger sato.masaki <sato.masaki@aainc.co.jp> 1435421112 +0900
test tag
軽量版は「second commit」のハッシュ値になのに対し、注釈付きは別のハッシュの値となっています。これは commit に対する直接的な参照ではなく、tag オブジェクトがつくられた為です。また、refs の中は下記の通りです。
└── refs
├── heads
│ ├── master
│ └── test
├── remotes
│ └── origin
│ └── master
└── tags
├── v1.0 ← タグ追加
└── v1.1 ← タグ追加
remotes
remote もほぼ上記内容と同様の仕組みとなっています。では試しに test ブランチを remote にプッシュしてみます。するとさっきまでなかった「.git/refs/remotes/origin/test」が追加されます。またその中身は test ブランチのHEADのハッシュです。これはつまり remote を確認することで最後に origin と通信したときの情報を取得できるということです。
└── refs
├── heads
│ ├── master
│ └── test
├── remotes
│ └── origin
│ ├── master
│ └── test ← 追加
└── tags
├── v1.0
└── v1.1
% cat .git/refs/remotes/origin/test
543ee947f5a865e1a998f793120667c3bf89fc80
% cat .git/refs/heads/test
543ee947f5a865e1a998f793120667c3bf89fc80
# 同じハッシュとなる
まとめ
- 参照には head と remote と tag がある
- HEAD ファイルは参照を参照し現在のブランチのシンボリックリンクを参照している
- tag には軽量版と注釈付き版があり注釈付きの方が tag オブジェクトとなる
- remote は最後にサーバと通信したときのハッシュを保持している
Git の参照と参照系の下位コマンドが確認できました。普段何気なく使っているコマンドの内側で何が行なわれてるかが分かっておもしろいですね。
参考
- 10.3 Git Internals – Git References
- 10.3 Gitの内側 – Gitの参照日本語翻訳 (v2の方も翻訳が進んでました)
- こせきの技術日記