参照(refs)とブランチの実体

《 Git & GitHub 完全ガイド 目次へ 》

このページで分かること

  • ブランチの実体が、コミットの ID を書いた41バイトのファイルだと分かる
  • ブランチを作る・進めるときに、ファイルの上で何が起きているか説明できる
  • HEAD・ブランチ・タグが、どう指し合っているかが分かる
  • refs のファイルを手で書き換えてはいけない理由が分かる

第3部の18で、HEAD の中身が ref: refs/heads/main だと見ました。このページでは、その先にある refs/heads/main を開けます。ここが分かると、第4部の21でブランチを「コミットを指す名前付きのポインタ」と説明する意味が、そのまま見えるようになります。


参照(refs)とは

コミットの ID は40桁あり、人が覚えて使うには向きません。そこで Git は、ID に名前を付けて呼べるようにしています。この名前の仕組みを参照(reference、略して ref)と呼び、.git/refs フォルダにまとめて置いています。

.git フォルダの中に HEAD ファイルと refs フォルダがあり、refs/heads/main のファイルがコミット b505e8e の ID を持ち、refs/tags/v1.0 のファイルが tag オブジェクトの ID を持つ。HEAD は main のファイルを指す
.git フォルダの中に HEAD ファイルと refs フォルダがあり、refs/heads/main のファイルがコミット b505e8e の ID を持ち、refs/tags/v1.0 のファイルが tag オブジェクトの ID を持つ。HEAD は main のファイルを指す

refs/heads/ の下にあるのがブランチ、refs/tags/ の下にあるのがタグです。どちらも、中身はIDを1行書いただけのファイルです。


ブランチは41バイトのファイル

cd ~/git-practice
cat .git/refs/heads/main

# 出力:
# b505e8e8f92abb8142a6ca486600de9100f98e42

wc -c .git/refs/heads/main

# 出力:
#       41 .git/refs/heads/main

wc -c はファイルの大きさをバイト数で数えるコマンドです。40桁の ID と、行の終わりを表す改行1文字で、ちょうど41バイト。main ブランチの正体は、これで全部です。

ブランチの中にファイルや履歴が入っているわけではありません。ブランチは「最新のコミットはこれ」と1つの ID を指しているだけで、それより前の履歴は、第3部の17で見たとおりコミットどうしが parent でつながっています。

ブランチを作る = ファイルを1つ作る

試しにブランチを1つ作ってみます(ブランチの操作は第4部の22で詳しく扱います)。

git branch experiment
ls .git/refs/heads

# 出力:
# experiment
# main

cat .git/refs/heads/experiment

# 出力:
# b505e8e8f92abb8142a6ca486600de9100f98e42

experiment という41バイトのファイルが1つ増え、中身は main と同じ ID です。ファイルは1つもコピーされていません。 ブランチをいくつ作っても一瞬で終わり、容量もほとんど増えないのはこのためです。

確かめたら消しておきます。

git branch -d experiment

# 出力:
# Deleted branch experiment (was b505e8e).

ブランチが進む = ファイルの中身が書き換わる

コミットすると、HEAD が指すブランチのファイルが、新しいコミットの ID に書き換わります。その記録は .git/logs/ に残っていて、第2部の15の reflog はここを読んでいます。

tail -n 1 .git/logs/refs/heads/main

# 出力:
# 29c43994091deca933abfcfca9795596789675ee b505e8e8f92abb8142a6ca486600de9100f98e42 Taro Yamada <[email protected]> 1792630800 +0900 commit: ケーキのレシピを追加

左が書き換える前の ID(第2部の15の Revert)、その次が書き換えたあとの ID(第3部の17のケーキのレシピ)です。「main の中身を 29c4399… から b505e8e… に書き換えた」という1行が、コミットの正体の半分です。残りの半分は、第3部の17で見たオブジェクトを保存することです。


タグも同じ仕組み

cat .git/refs/tags/v1.0

# 出力:
# 98206d50345d4cba6f0ef0f0908a213aa1f6a61f

git cat-file -t 98206d5

# 出力:
# tag

第3部の17で作ったタグ v1.0 のファイルは、tag オブジェクトの ID を持っています。tag オブジェクトがさらにコミットを指す、という2段です。

ブランチとタグの違いは、動くかどうかです。ブランチはコミットのたびに先へ進みますが、タグは作ったときのコミットを指したまま動きません。だからタグは「v1.0 を出したときの状態」のような目印に使われます(第5部の30)。


一覧で確かめる:git show-ref

git show-ref

# 出力:
# b505e8e8f92abb8142a6ca486600de9100f98e42 refs/heads/main
# 98206d50345d4cba6f0ef0f0908a213aa1f6a61f refs/tags/v1.0

すべての参照と、それぞれが指す ID が一覧で表示されます。第6部では、ここに refs/remotes/ で始まる参照が加わります。


手で書き換えてはいけない理由

仕組みが分かると、ファイルを直接書き換えたくなるかもしれません。ですが、refs のファイルは Git のコマンドで操作してください。

  • ファイルがない場合がある:参照が増えると、Git は .git/packed-refs という1つのファイルにまとめて保存します(第3部の20)。そのとき refs/heads/main のファイルは消えますが、main ブランチはなくなっていません
  • 記録が残らない:手で書き換えると .git/logs/ に記録されず、reflog で戻れなくなります
  • 保存の形式が変わる予定がある:将来の Git 3.0 では、新しく作るリポジトリの参照の保存形式が reftable という別の形式に変わる予定です。その場合、ここで見たような1つずつのファイルにはなりません(出典:Git の BreakingChanges)

保存の形が変わっても、「参照は ID に付けた名前で、ブランチは最新のコミットを指す名前」という考え方は変わりません。このページで確かめたのは、その考え方のほうです。


よくある疑問

Q. refs/heads/feature/login のように、スラッシュを含むブランチ名はどう保存されますか?
A. フォルダとして保存されます。.git/refs/heads/feature/ フォルダの中に login というファイルができます。ブランチ名をスラッシュで分類できるのは、この仕組みのおかげです。


まとめ

  • 参照(ref)は、コミットの ID に付けた名前。.git/refs に置かれる
  • ブランチの実体は、コミットの ID を1行書いた41バイトのファイル
  • ブランチを作るとファイルが1つ増え、コミットするとその中身が書き換わる
  • タグは作ったときのコミットを指したまま動かない。ブランチは進む
  • 参照は保存の形式が変わることがあるので、ファイルを手で触らずコマンドで操作する

次のページ

20. .git フォルダの中身ツアー ── ここまでに開けた HEAD・objects・refs 以外も含めて、.git の中を一通り見て回ります。

→ ガイドの目次に戻る

この記事の最終確認日: 2026年10月

タイトルとURLをコピーしました