HEAD とは何か・detached HEAD
このページで分かること
- HEAD が「今いる場所」を指す名前だと分かる
- HEAD → ブランチ → コミットという2段の指し方を説明できる
- detached HEAD がどういう状態かが分かり、慌てずに戻れる
- detached HEAD で作ったコミットを失わずに済む方法が分かる
このページの目次8 items
第2部の14では、HEAD を「今いる位置、つまり最新のコミットを指す名前」として使いました。このページでは、その正体を開けて確かめます。HEAD が分かると、第4部のブランチの操作が「名前を付け替えているだけ」だと見えてきます。
HEAD はファイル1つ#
HEAD の実体は、.git の中にある HEAD という名前のテキストファイルです。
cd ~/git-practice
cat .git/HEAD
# 出力:
# ref: refs/heads/main中身は1行だけで、「refs/heads/main を見よ」と書いてあります。refs/heads/main は main ブランチのことで、その正体は第3部の19で開けます。ここでは「HEAD はコミットを直接指しているのではなく、ブランチを指している」ことだけ押さえます。

上段がふだんの状態です。HEAD は main を指し、main が最新のコミットを指します。HEAD と main のどちらで尋ねても、行き着くコミットは同じです。
git rev-parse HEAD
git rev-parse main
# 出力(2つとも同じ):
# b505e8e8f92abb8142a6ca486600de9100f98e42git rev-parse は、名前がどのコミットを指しているかを ID で答えるコマンドです。
コミットすると何が動くのか#
この2段の指し方には意味があります。コミットすると、Git はHEAD が指しているブランチを、新しいコミットへ進めます。HEAD のファイルの中身は ref: refs/heads/main のまま変わりません。HEAD は main を指し続け、main だけが1つ先へ動きます。
「今どのブランチで作業しているか」を HEAD が覚えていて、記録はそのブランチに積み上がる、という仕組みです。第4部の22でブランチを切り替えるときに動くのは、この HEAD の中身です。
detached HEAD:HEAD がコミットを直接指す状態#
過去のコミットの状態を見たいとき、そのコミットに移動できます。
git switch --detach HEAD~2
# 出力:
# HEAD is now at 35b12cd やることに郵便局を追加git switch は今いる場所を切り替えるコマンドで、--detach は「ブランチを通さず、コミットに直接移る」という指定です。HEAD の中身を見ると、変わっています。
cat .git/HEAD
# 出力:
# 35b12cd6e2f8655e722088f0cc635c3686230db0ref: … ではなく、コミットの ID が直接書かれています。これが detached HEAD(切り離された HEAD)です。図の下段の状態で、HEAD がブランチから切り離されて、コミットを直接指しています。
git status
# 出力:
# HEAD detached at 35b12cd
# nothing to commit, working tree cleanワークツリーのファイルは、そのコミットの時点の内容に変わっています。todo.txt を開くと「郵便局に行く」の行があり、recipes フォルダはありません。main ブランチのコミットは何も変わっていません。 見る場所を移しただけです。
元に戻る#
見終わったら、ブランチに戻ります。
git switch main
cat .git/HEAD
# 出力:
# ref: refs/heads/mainHEAD が再び main を指し、ワークツリーも main の最新のコミットの状態に戻りました。detached HEAD はエラーではなく、ただの状態です。慌てる必要はありません。
注意:detached HEAD でコミットすると#
detached HEAD のままでもコミットはできます。ただ、そのコミットを指すブランチがありません。
git switch --detach HEAD~2
echo "あとで消える行" >> todo.txt
git commit -a -m "迷子のコミット"
git switch main
# 出力:
# Warning: you are leaving 1 commit behind, not connected to
# any of your branches:
#
# 4d25345 迷子のコミット
# (以下略)main に戻るとき、Git は「どのブランチにもつながっていないコミットを1つ置いていく」と警告します。git log には出てこなくなり、第2部の15で見たとおり、reflog からしかたどれなくなります。
残しておきたい場合は、detached HEAD のうちに、そのコミットにブランチの名前を付けます。
git switch -c rescuegit switch -c 名前 は、今いるコミットに新しいブランチを作って、そこに移るコマンドです。ブランチの作り方は第4部の22で扱います。もう戻ってしまったあとでも、警告に表示された ID(ここでは 4d25345)や reflog を使って、git switch -c rescue 4d25345 のように救えます。
今回の「迷子のコミット」は練習なので、救わずにそのままにしておいて構いません。
よくある疑問#
Q. HEAD~1 と HEAD@{1} は何が違いますか?
A. HEAD~1 は「今のコミットの1つ前(親)」、HEAD@{1} は「HEAD が1回前にいた場所」です。前者は履歴のつながりを、後者は第2部の15の reflog をたどります。reset や switch のあとでは、2つが別のコミットを指すことがあります。
Q. detached HEAD は、どんなときに使うのですか?
A. 過去のある時点のファイルを丸ごと確かめたいときです。第5部の32の bisect(不具合が入ったコミットを探す機能)でも、Git が自動で detached HEAD を使います。
まとめ#
- HEAD は
.git/HEADというファイルで、ふだんはブランチの名前を指している - コミットすると、HEAD が指しているブランチが新しいコミットへ進む
- detached HEAD は、HEAD がブランチを通さずコミットを直接指している状態
git switch ブランチ名で戻れる。detached HEAD はエラーではない- detached HEAD で作ったコミットは、
git switch -cでブランチを付けて残す
次のページ#
19. 参照(refs)とブランチの実体 ── HEAD が指していた refs/heads/main を開けて、ブランチの正体が41バイトのファイルであることを確かめます。