【Git】HEAD の意味・使い方を初心者向けにわかりやすく解説

Web開発

はじめに

Gitを使うときに、よく耳にするのがHEADという言葉です。
これは作業中のブランチを指し示すポインタのような概念であり、コミットを参照するときの基準点にもなります。

しかし、初めてGitを触る人にとっては「HEADとは何だろう?」と感じるのではないでしょうか。
ブランチの切り替えをしたり、過去のコミットを参照したりする際に、このHEADの仕組みを知らないと混乱しがちです。

今回は、GitにおけるHEADの意味と使い方について解説します。
実際の現場に近い例を交えながら、何をするときにHEADが役立つのかもあわせて紹介します。

この記事を読むとわかること

  • GitのHEADとはどういう概念なのか
  • HEADを使ったコミット参照やブランチ操作の方法
  • 実務でどのようにHEADを活用する場面があるのか
  • エラーやトラブルを防ぐための注意点

HEADとは何か

Gitで作業するときには、必ずどこかのブランチ上でコミットを進めています。
そして、そのブランチの中の「現在チェックアウトしている最新コミット」を指し示しているのがHEADです。

もう少しわかりやすく言うと、「いまGitが把握している“あなたが作業中のポイント”」のようなイメージです。
HEADは常にただ一つのコミットを参照しており、新しいコミットを追加すると、自動的に最新のコミットを指すように動きます。

ブランチを切り替えれば、HEADも切り替わった先のブランチで最新のコミットに移動します。
この仕組みによって、現在どのブランチのどのコミットを操作しているかをGitが正確に把握できるわけです。

HEADが指すものを具体例で理解する

もう少しイメージを膨らませるために、実際のレポジトリ構造を考えてみましょう。

たとえば、下記のようにコミットが並んでいるとします。

A -- B -- C -- D

ここで、ブランチが main だとした場合に、HEADがDを指している状態であれば、次のようなイメージです。

(mainブランチ)
  ↓
D (HEAD)
↑
C
↑
B
↑
A

このときに、別のブランチ feature に切り替えたとしましょう。
feature ブランチの最新コミットが C だった場合、HEADは今度は C に移動します。
つまり常に「今操作している地点」を指してくれるわけです。

HEADを使って過去のコミットを参照する方法

Gitでは、HEADを基準にして過去のコミットを参照する方法があります。
たとえば、HEAD~1 は「HEADから1つ前のコミット」という意味です。
同様に、HEAD~2 は「HEADから2つ前のコミット」を指します。

よく使うコマンド例

# HEADの1つ前のコミットに切り替える
git checkout HEAD~1

# HEADの2つ前のコミットを確認する(チェックアウト)
git checkout HEAD~2

これによって、一時的に過去の状態を調べたり、コードを比較したりできるわけです。
ただし、過去のコミットに切り替えた状態でファイルを編集し、そのままコミットすると、detached HEAD(分岐先が特定のブランチに紐付いていない状態)になってしまうことがあります。

このdetached HEAD状態は、誤った操作をすると混乱を招きやすいです。
ブランチを作らずにコミットを追加すると、別の作業ラインができてしまうからです。

過去のコミットを調べるときは、detached HEADに注意してください。
必要があれば新しいブランチを切って作業を進める方が安全です。

HEADが参照先を更新する仕組み

HEADが参照先を更新するタイミングは主に2つです。

  1. ブランチの切り替え (checkout)
  2. 新しいコミットを作成

ブランチを切り替えると、HEADはそのブランチの最新コミットを指すようになります。
逆にいえば、常に「現在のブランチにおける最新コミット」を表すのがHEADです。

さらに、新しいコミットを追加すると、HEADは追加されたばかりの最新コミットに移動します。
このとき、ブランチの先端も同じように最新のコミットへと更新されます。

使いどころ:実務でのHEAD活用シーン

過去の状態を一時的にチェックするとき

コードの動作がおかしくなったときに、「以前のコミットでは正常に動いていたのに」となることがあります。
そんなときに、HEAD~n のようにして過去のコミットに一時的に切り替えて動作を確かめることができます。

もし「この過去の状態をもとに修正したい」という場合は、過去のコミットをチェックアウトした直後に新しいブランチを作ると良いでしょう。
そうすれば、誤って本流を壊すことなく調査が可能になります。

バグ修正やデバッグ作業

バグが発生したときに、どのコミットからおかしくなったのかを突き止めるのにHEADは便利です。
たとえば git bisect などの機能を使えば、特定の範囲のコミットを自動で二分探索しながらバグの原因となったコミットを発見します。
この内部でもHEADが指定したコミットを行き来して、どこでバグが入り込んだかを調査するわけです。

リリース前の調整や巻き戻し

何らかの理由で直前のコミットを取り消したい場合、HEAD~1 にリセットするコマンドを使うことがあります。
git reset は、過去のコミットに戻して、その差分を作業ツリーに残すという使い方が可能です。

# 直前のコミットを取り消し、差分をワーキングツリーに残す
git reset HEAD~1

この作業でHEADは、1つ前のコミットを参照するようになります。
その後、修正や調整が終わったら改めてコミットし直すことで、不要なコミットを履歴から排除できます。

ただし、この操作は他のメンバーと共有していないローカルブランチでやるのが基本です。
すでに共有されている履歴を上書きすると、他の人のリポジトリと齟齬が起きるからです。

Detached HEADについてもう少し詳しく

先ほど少し触れた通り、Gitにはdetached HEADという状態があります。
これは「特定のブランチに紐付いておらず、単一のコミットを直接参照している」状態です。

通常、HEADは何らかのブランチとつながっています。
しかし、過去のコミットをチェックアウトすると、そのコミット自体を直接指すことになります。
これがdetached HEADです。

たとえば以下のようにして過去コミットにチェックアウトすると、detached HEADになります。

git checkout HEAD~2

この状態で新しいコミットを作ると、コミットそのものは作成されますが、ブランチの先端が更新されるわけではありません。
もし、そのままブランチの切り替えをしてしまうと、新規作成したコミットがどこにも紐付かず、実質的に「行方不明」になってしまうことがあります。

対処法

detached HEAD状態のままで何か新しい修正を加えたい場合は、ブランチを作るのが良いです。
過去コミットにチェックアウトした直後に、以下のようにしてブランチ作成すると安心です。

# feature_old という名前のブランチを作成し、そこに切り替える
git checkout -b feature_old

これで、HEADは新しいブランチに紐付くので、コミットが行方不明になる危険性がなくなります。

過去のコミットを参照するときはdetached HEADに陥りやすいです。
新たに修正を行う場合は、ブランチを作ってから作業を続けるようにしましょう。

HEADを活用するメリット

状態管理が明確

HEADのおかげで、「今どのブランチのどの時点にいるか」をGitが管理してくれます。
これにより、過去のバージョンを調べる作業や、ブランチを切り替えての開発がスムーズに行えるでしょう。

コミット履歴を安全に遡れる

HEAD~n のような指定で過去のコミットに切り替えても、ブランチさえしっかり作れば今の作業には影響を与えません。
過去の状態を安全にチェックでき、履歴の差分を比較することも容易です。

複数人での開発に強い

チームで共同開発していると、ブランチのマージやリベースなど複雑な操作が発生します。
しかし、どの操作でも「最終的にHEADがどのコミットを指すか」が非常に重要です。
これを理解していないと、予期せぬコミット上書きや競合が起きたときに正しい対処が難しくなります。

実務と結びつけた具体的なフロー

たとえば、ウェブアプリケーションの開発をしているとしましょう。
メインとなる main ブランチがあり、そこから新機能を開発するために feature/login-page ブランチを切りました。

  1. feature/login-page 上でログインページの実装を進めてコミットを積み重ねる
  2. 動作に不具合が見つかったので、以前のコミット状態との比較をしたい
  3. 一時的に git checkout HEAD~1 で、直前のコミットに戻って挙動を確認する
  4. 必要があれば、別のブランチを切って修正を進める
  5. 不具合が解決したら、main ブランチにマージして完了

このとき、HEADは状況に応じてブランチ先端や過去コミットを行き来します。
しかし、操作のたびにHEADがどこを指しているのかを確認しておけば、作業中の混乱を最小限に抑えられます。

HEADにまつわるよくあるトラブルと対処法

コミットを失ったように見える

過去のコミットを直接チェックアウトし、そのままコミットを追加してブランチを切り替えた場合、コミットが見えなくなることがあります。
これはdetached HEAD状態で作成したコミットがどのブランチにも所属していないからです。

対処法としては、失われたコミットを探してブランチを作成してあげることです。
git reflog を使えば、HEADが過去に指し示したコミットの履歴が一覧で見られます。
そこからコミットハッシュを探し、git checkout <コミットハッシュ> して git checkout -b でブランチを切れば復帰できます。

HEADの指すブランチと別のブランチが混在

マージの際に誤って操作をしてしまい、「今自分はどのブランチにいるのか」がわからなくなるケースもあります。
慌てず git statusgit branch でブランチ名を確かめ、必要に応じて git checkout <ブランチ名> で明示的に切り替えると良いでしょう。

まとめ

GitのHEADは、現在作業中のコミットを指し示すポインタのような概念です。
HEADを中心にブランチがあり、そこにコミットを追加していくという構造を理解すれば、トラブルを避けながら開発作業を進められます。

HEADを使って過去のコミットを参照すれば、すばやく状態を切り替えて不具合の原因を調査することができます。
一方で、detached HEADのようにブランチから切り離された状態には注意が必要です。
新しい作業を行うときは、あらかじめブランチを作っておけば安全に進められます。

ブランチの切り替えやコミットの取り消しなど、Gitを活用するうえでHEADという仕組みは不可欠です。
初心者のうちは難しく感じるかもしれませんが、ここで紹介したポイントを押さえておけば、実務でも安心してコードを管理できるでしょう。

Gitをマスターしよう

この記事で学んだGitの知識をさらに伸ばしませんか?
Udemyには、現場ですぐ使えるスキルを身につけられる実践的な講座が揃っています。