GitのHEADとは?仕組みとデタッチドHEADの対処法を初心者向けに解説

GitのHEADの仕組みを初心者向けに図解で解説します。HEADが何を指しているのか、デタッチドHEAD状態とは何か、その対処法まで詳しく説明。HEADを理解してGitの操作に自信を持ちましょう。

GitのHEADとは

HEADは、Gitにおける「現在の作業位置」を示すポインタです。簡単に言えば、「今、どのコミットを見ているか」を表しています。

通常、HEADはブランチを指しており、そのブランチが指すコミットが現在の作業対象になります。コミットを作成すると、HEADが指すブランチが新しいコミットを指すように更新されます。

HEADの仕組み

HEADは通常、ブランチへの参照(間接参照)として機能します。

上の図では、HEADがmainブランチを指し、mainブランチはコミットC3を指しています。この状態で新しいコミットを作成すると、mainがC4を指すようになり、HEADはそのままmainを指し続けます。

HEADの実体

HEADの実体は.git/HEADファイルです。中身を確認してみましょう。

cat .git/HEAD
# ref: refs/heads/main

ref: refs/heads/mainという内容は、HEADがmainブランチを参照していることを示しています。

# mainブランチが指すコミットを確認
cat .git/refs/heads/main
# abc1234def5678...(コミットハッシュ)

HEADの参照方法

HEADを基準にして、相対的にコミットを参照できます。

記法意味
HEAD現在のコミット
HEAD^ または HEAD~11つ前のコミット
HEAD~22つ前のコミット
HEAD~nn個前のコミット
HEAD^2マージコミットの2番目の親

具体的な使用例

# 直前のコミットとの差分を確認
git diff HEAD~1

# 2つ前のコミットにリセット
git reset HEAD~2

# 直前のコミットを表示
git show HEAD^

^~の違い

^~は似ていますが、マージコミットがある場合に動作が異なります。

マージコミットMには2つの親があります。

記法参照先
M^1 または M^D(mainブランチ側の親)
M^2C(featureブランチ側の親)
M~1D(最初の親を1つ遡る)
M~2B(最初の親を2つ遡る)

通常のコミット(親が1つ)では^~の動作は同じです。マージコミットの2番目の親を参照したい場合のみ^2を使います。

デタッチドHEAD(Detached HEAD)

デタッチドHEADは、HEADがブランチではなく特定のコミットを直接指している状態です。

デタッチドHEADになるケース

操作
特定のコミットをチェックアウトgit checkout abc1234
タグをチェックアウトgit checkout v1.0.0
リモートブランチを直接チェックアウトgit checkout origin/main
git checkout abc1234
# Note: switching to 'abc1234'.
# You are in 'detached HEAD' state...

デタッチドHEADの警告メッセージ

デタッチドHEADになると、以下のような警告が表示されます。

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

この状態でも作業やコミットは可能ですが、ブランチを切り替えるとそのコミットは「迷子」になる可能性があります。

デタッチドHEADでコミットするとどうなるか

デタッチドHEAD状態で作成したコミットDは、どのブランチからも参照されていません。この状態でmainに戻ると、コミットDを参照する方法がなくなります(ただしreflogで復元可能です)。

デタッチドHEADの対処法

変更を保持したい場合:ブランチを作成する

# 現在のHEAD位置で新しいブランチを作成
git switch -c new-branch

# または
git checkout -b new-branch

これで新しいブランチがコミットを指すようになり、安全に保持されます。

既存のブランチに戻る

# mainブランチに戻る
git switch main

# または
git checkout main

デタッチドHEAD状態で作業中の変更(未コミット)がある場合は、stashで退避してからブランチを切り替えましょう。

git stash
git switch main
git stash pop

デタッチドHEADで作ったコミットを救出する

すでにブランチに戻った後でも、コミットハッシュがわかれば救出できます。

# reflogでコミットハッシュを確認
git reflog

# 出力例
# abc1234 HEAD@{0}: checkout: moving from abc1234 to main
# abc1234 HEAD@{1}: commit: デタッチドHEADで作成したコミット

# そのコミットからブランチを作成
git branch recovered-work abc1234

初心者が混乱しやすいポイント

HEADとブランチの違い

HEADは「現在位置」を示すポインタであり、ブランチとは異なります。

概念説明
HEAD現在の作業位置を示すポインタ(1つだけ存在)
ブランチコミットを指す名前付きポインタ(複数存在可能)

通常、HEADはブランチを指しています。git switchgit checkoutでブランチを切り替えると、HEADが別のブランチを指すようになります。

HEAD@は同じ

Gitでは@HEADのエイリアスです。短く書きたい場合に使えます。

# 以下は同じ意味
git show HEAD
git show @

git reset HEAD~1
git reset @~1

origin/mainへのcheckoutでデタッチドHEADになる理由

リモート追跡ブランチ(origin/mainなど)は「リモートの状態を追跡する」ものであり、直接作業するためのものではありません。そのため、チェックアウトするとデタッチドHEADになります。

# これはデタッチドHEADになる
git checkout origin/main

# ローカルブランチに切り替えるならこちら
git checkout main

リモートの最新状態をローカルで作業したい場合は、fetchした後にローカルブランチをマージまたはリベースしましょう。

git fetch origin
git merge origin/main
# または
git pull origin main

HEADを使った実践的な操作

直前のコミットを修正

# 直前のコミットのメッセージを修正
git commit --amend -m "新しいコミットメッセージ"

# 直前のコミットにファイルを追加
git add forgotten-file.js
git commit --amend --no-edit

特定のコミットの内容を確認

# 3つ前のコミットの内容を表示
git show HEAD~3

# 直前のコミットで変更されたファイル一覧
git diff --name-only HEAD~1 HEAD

コミットの取り消し

# 直前のコミットを取り消し(変更は残す)
git reset --soft HEAD~1

# 直前のコミットを完全に取り消し
git reset --hard HEAD~1

詳しくはGit revertとresetの違いを参照してください。

まとめ

HEADは、Gitにおける「現在の作業位置」を示すポインタです。

状態HEADの指す先
通常ブランチ(間接参照)
デタッチドHEADコミット(直接参照)
  • HEADは通常ブランチを指し、コミット作成時にブランチとともに移動する
  • HEAD~nで相対的にコミットを参照できる
  • 特定のコミットやタグをチェックアウトするとデタッチドHEADになる
  • デタッチドHEAD状態で作業を保持したい場合は、新しいブランチを作成する

HEADの仕組みを理解することで、reset、rebase、cherry-pickなどの操作がより明確に理解できるようになります。デタッチドHEADは怖いものではなく、過去のコミットを確認したり実験したりするのに便利な状態です。ブランチを作成すれば、いつでも安全に作業を保持できます。

編集部

編集部