Gitで過去のコミットやタグをcheckoutすると、git statusに次のような警告が出ることがあります。
$ git status HEAD detached at a1b2c3d 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.
このdetached HEAD(分離HEAD)状態は「HEADがどのブランチにも属していない」ことを意味し、放置してブランチを切り替えるとこの状態で積んだコミットが迷子になる危険があります。幸い、慌てなければgit switchやgit reflogで簡単に復帰できます。
この記事では、detached HEADの正体と7つの発生シナリオ、状況別の復帰手順、迷子コミットの救済方法、そしてそもそも陥らないための予防策まで実務で役立つ形でまとめます。bisect・worktree・submoduleといったdetached HEADが正常動作の一部である特殊ケースも扱います。
この記事で学べること
- detached HEADが発生する7つの代表シナリオ
git status+git reflogによる状況診断- 作業がない場合の復帰(
git switch -・ブランチ指定) - detached中のコミットを救済する手順(
git switch -cでブランチ化) - bisect・worktree・submoduleでの想定外の挙動
- 迷子コミットを
reflogから復元する方法 - そもそもdetachedにならないための運用(
git switch -c徹底)
detached HEADの正体
GitのHEADは「現在作業中のコミット」を指すポインタです。通常はrefs/heads/mainのようなブランチ名を指しています。detached HEAD状態では、ブランチ名を経由せずにコミットSHAを直接指している状態になります。
# 通常状態:HEAD はブランチを指す cat .git/HEAD # → ref: refs/heads/main # detached 状態:HEAD はコミットSHAを直接指す cat .git/HEAD # → a1b2c3d4e5f6... # 一発で判定 git symbolic-ref -q HEAD # 通常: refs/heads/main # detached: 何も表示されず終了コード1
detachedでも「悪い状態」ではない
- 過去の状態を確認するだけ/古いタグを試すには普通の操作
- コミットを積まずに元ブランチへ戻れば失うものはない
- bisectやworktreeでは意図的にdetachedにする
- 危険なのは「detached中に作業してブランチ切替してしまう」こと
ポイント:「detached HEADになった」だけで慌てる必要はありません。警告メッセージ自体は情報提供が目的です。作業していなければgit switch -で即復帰できるので、「発生=事故」ではなく「作業したときだけ注意」と考えましょう。
detached HEADが発生する7つのシナリオ
意図的・意図せず問わず、detached HEADに入るタイミングは複数あります。自分の状況を特定すると対処が決めやすくなります。
意図的 vs 意図しないdetached
- 意図的(bisect・worktree・submodule等):普通に使うものなので心配不要
- 意図しない(SHA/タグ/リモート直接checkout):警告を読んで対処
- 多くの「困った」ケースは①〜④の意図しないdetached
STEP 0:現在の状態を診断する
# detached かどうか
git status
# "HEAD detached at XXX" が出れば detached
# HEADの生情報
git symbolic-ref -q HEAD
# 何も表示されなければ detached
# どこから来たか(直前のブランチ)
git reflog -10
# 出力例:
# a1b2c3d HEAD@{0}: checkout: moving from main to a1b2c3d
# e4f5g6a HEAD@{1}: commit: 前のcommit
# i7j8k9l HEAD@{2}: checkout: moving from feature to main
# detached中にコミットしているかチェック
git log --oneline HEAD ^$(git branch --format='%(refname:short)' | tr '\n' ' ')
# 出力があれば、どのブランチにも紐づかないコミット
診断チェックリスト
git statusで”HEAD detached”表示 → detached確定git reflogで元のブランチを特定- detached以降にコミットがあるか確認
- 無ければ単純に戻るだけ、あれば保全してから戻る
復帰①:作業していない場合は1コマンドで戻る
detached HEADの状態でコミットを一切作っていなければ、ブランチに戻るだけで終わります。迷ったらgit switch -で直前ブランチに戻るのが最速です。
# 直前のブランチに戻る(もっとも簡単) git switch - # または git checkout - # ブランチ名を指定して戻る git switch main git switch feature/login # 戻った後の状態確認 git status # → On branch main
ポイント:git switch -はシェルのcd -のように「1つ前のブランチ」に戻ります。detached HEAD状態のように「ブランチから来た」場合は便利。ただしcheckout履歴が長くなっていると正確に動かないので、確実に戻るならブランチ名を明示しましょう。
復帰②:detached中にコミットした場合は救済が必要
detached HEAD中にgit commitを実行していた場合、そのコミットはどのブランチにも紐づいていない状態です。そのまま他のブランチに切り替えると、コミットSHAが参照を失い、最終的にGCで消滅します。先にブランチ化して保全しましょう。
# 現在位置(コミット積んだ先)を新ブランチとして固定 git switch -c rescue/my-work # これでdetachedコミットはrescue/my-workブランチの履歴になった git log --oneline -5 # 必要に応じてpushで保全 git push -u origin rescue/my-work # その後、元のブランチへ安心して戻れる git switch main
禁止:detached中にコミットしたままgit switch main等で別ブランチに切り替えると、Gitは警告を出しますが無視するとコミットが迷子状態になります。必ずgit switch -c rescue/xxxでブランチ化してから移動してください。
警告を見逃して切り替えてしまった場合
# 過去のHEAD履歴からSHAを探す git reflog -20 # detached時のコミットSHAを特定したら git switch -c rescue/lost-work <SHA> # 直後なら簡単だが、reflog期限(90日)を超えると救済不可
迷子ブランチ/commit全般の救済は誤ってmaster/mainを削除したときの復旧方法も参考になります。
特殊ケース:意図的detachedとその抜け方
bisect中のdetached HEAD
git bisectでバグ発生コミットを探す時、Gitは自動でdetached HEADにします。これは正常な動作で、bisect終了時に元のブランチに戻ります。
# bisect 開始 git bisect start git bisect bad HEAD git bisect good v1.0.0 # → detachedで中間コミットにcheckoutされる # 動作確認して good/bad を伝える git bisect good # または git bisect bad # bisect 終了して元ブランチに戻る git bisect reset
bisectの詳細はbisectでバグを仕込んだコミットを特定する方法を参照。
worktreeのdetached HEAD
# 指定コミットでworktreeを作る(detached) git worktree add --detach ../review-pr a1b2c3d # そのworktreeに移動 cd ../review-pr git status # HEAD detached at a1b2c3d # そのまま参照用に使うならそれで良い # 作業するならブランチを切る git switch -c scratch/review
submodule初期化時
# submoduleを初期化すると各submoduleがdetachedになる git submodule update --init --recursive # submodule内に入って作業するなら cd path/to/submodule git switch main # またはsubmoduleの開発ブランチに # submoduleは各々独立したリポジトリ
意図的detachedの特徴
- bisect:終了時に
bisect resetで自動復帰 - worktree:新ブランチを切るかworktree削除で対処
- submodule:内部で作業する前にブランチ切替
- これらは「正常動作」なのでエラーと混同しない
そもそもdetached HEADに陥らないために
detachedに入る最大の経路は「過去コミット/タグをcheckout」です。最初からブランチ化しながら移動すれば、detached状態を経由せずに済みます。
# 過去コミットから作業するなら最初からブランチ化 git switch -c scratch/old-check a1b2c3d # タグから始める場合も同じ git switch -c hotfix/v1.2.1 v1.2.0 # リモート追跡ブランチから git switch -c review/pr-42 origin/feature/pr-42 # 単に履歴を見るだけなら switch せずに show / log でOK git show a1b2c3d git log -p a1b2c3d git log --graph --all --oneline
ポイント:「見るだけ」ならgit show/git log、「作業するかも」ならgit switch -cでブランチ化、が黄金ルール。git checkoutでSHAやタグを指定して移動する習慣は事故の温床なので、今からswitch -c中心に切り替えましょう。
過去コミット起点のブランチ作成の詳細は過去のコミットから新しいブランチを作り作業をやり直す方法を参照してください。
便利なエイリアス
# 検証用scratch branchをSHAから切る
git config --global alias.scratch '!f() { git switch -c "scratch/$(date +%Y%m%d-%H%M)" "$1"; }; f'
# 使用例
git scratch a1b2c3d
# → scratch/20260101-1530 という使い捨てブランチを自動で作る
実践シナリオ
シナリオ① タグをcheckoutしたら警告が出た
git checkout v1.2.0 # HEAD detached at v1.2.0 # 中身を見るだけで作業しないなら直接戻る git switch - # または git switch main
シナリオ② detached中に2〜3コミット積んでしまった
# 現在位置確認 git log --oneline -5 # 救済ブランチを切って保全 git switch -c rescue/my-experiments # pushで保全(お好みで) git push -u origin rescue/my-experiments # 必要なコミットをmainへ移す場合 git switch main git cherry-pick <SHA>
シナリオ③ 切り替えてしまった後でdetachedコミットを思い出した
git reflog --all -30
# 出力からdetached時のSHAを特定
# a1b2c3d HEAD@{3}: commit: 消したくないコミット
# SHAからブランチ化
git switch -c rescue/recovered a1b2c3d
シナリオ④ bisect中に誤ってgit switchしてしまった
# まず元のブランチに戻る git bisect reset # → bisect開始前のブランチに自動で戻る # bisect情報を確認 git bisect log
やってはいけない落とし穴
警告を無視してブランチ切替
detached状態でコミットを積んだ後にgit switch mainすると、GitはWarning: you are leaving N commits behind, not connected to any of your branchesと警告を出します。無視すると迷子コミットになり、最終的にGCで削除されます。必ずgit switch -c rescue/xxxで保全してから移動。
reflogを当てにしすぎる
reflogは既定90日で自動clean upされます。長期間経って気付いたdetachedコミットは復元できないことがあります。大事な作業は即座にブランチ化&pushで保全するのが鉄則です。
bisect中に自分でcheckoutする
bisect中はGitが自動でHEADを動かしています。途中で手動git checkout/git switchするとbisectのプロセスが壊れます。終わらせたいならgit bisect resetを使ってください。
submodule内でdetached中に開発して親リポを参照する
submodule内でdetached状態のままcommit&pushすると、親リポは「detached時点のSHA」を参照したままになります。submodule内で作業する前に必ずブランチに切替え、作業後は親リポでgit add <submodule>+commitで参照SHA更新を。
worktreeのdetachedを消さずに放置
git worktree add --detachで作った検証用worktreeを使い終わった後、削除せずに放置すると、.gitにworktree情報が溜まります。使い終わったらgit worktree remove <path>で明示的に削除しましょう。
よくある質問
git switch -ですぐ戻れます。git reflogで履歴を見ればcheckout: moving from X to Yという行がHEADの移動を示しています。そこからブランチ名が特定できます。git switch -(直前ブランチに戻る)も有効です。git switch -c rescue/xxx、その後本線へマージまたはcherry-pickで必要なコミットを取り込みます。detached状態で直接mergeすることはできません。git switch -cでブランチ化してからgit push -u origin <branch>します。git push origin HEAD:refs/heads/new-branchで直接pushすることも技術的に可能ですが、ブランチ化してからpushする方が分かりやすく事故も防げます。git bisect resetを実行してください。開始前のブランチに自動的に戻ります。実行し忘れると元のブランチに戻れない状態になります。-は@{-1}のショートカットで、「1つ前のブランチ参照」を意味します。git switch @{-2}のようにさらに前に戻ることもできます。git statusで確認する習慣を付けましょう。関連記事
- Gitで過去のコミットから新しいブランチを作り作業をやり直す方法 — detached回避の派生ブランチ
- 【Git】新しいブランチを作成する基本的な手順 — switch -c の基礎
- 【Git】特定のコミットまで戻す方法 — switch –detach で安全に覗く
- 【Git】ローカル動作確認の一時コミット&巻き戻し方法 — scratch branchやworktreeの検証活用
- 【Git】bisectでバグを仕込んだコミットを特定する方法 — 意図的detachedの代表例
- 【Git】誤ってmaster/mainを削除したときの復旧方法 — 迷子コミットの大規模救済
- 【Git】ブランチを削除する方法 — rescueブランチの片付け
- 【Git】よく使うgitコマンドまとめ — 日常コマンドの早見表
まとめ
- detached HEADはHEADがブランチではなくコミットSHAを直接指している状態
- 発生は7パターン:コミットSHA/タグ/
--detach/リモート直指定/bisect/worktree/submodule等 - 作業していなければ
git switch -で即復帰 - コミットを積んでいたら
git switch -c rescue/xxxで必ずブランチ化 - 切替後に気付いたら
git reflogから90日以内に救済可 - bisect/worktree/submoduleは意図的detachedなので通常の操作で終了させる
- 予防:過去コミット起点の作業は最初から
git switch -c scratch/xxx <SHA>
detached HEADは「怖いエラー」ではなく、Gitが「特別な状態にいます」と教えてくれる親切な警告です。見るだけなら即戻り、作業してしまったらブランチ化で保全——この2つを覚えておけば事故は起きません。さらに「過去コミットに移動する時は最初からgit switch -c」という習慣を付ければ、そもそもdetachedに入る回数が激減します。reflogという強力な救済手段もあるので、落ち着いて手順を踏めば必ずリカバーできます。

