Git操作をしていると時折、You are in 'detached HEAD' state.
と表示されることがあります。デタッチド ヘッド という状態です。
このdetached HEAD
がどういう状態で、どうすれば元に戻せるのか?や、どうやって活用するのか?について解説しています。
detached HEADとは?
detached HEADとは、HEAD(ヘッド)がどのブランチも指していない状態です。
detachedとは「切り離された」という意味です。つまり、HEADがブランチから切り離された状態です。
HEADとは?
HEADとは現在自分がいるブランチを指すポインターです。
例えば自分がmasterブランチにいる場合、git log
で確認するとHEADはmasterを指しています。
$ git log
commit 66483ae81ca288f9cffdeb2859606bec5ec0363b (HEAD -> master, origin/master, default)
Author: prograshi
Date: Thu Jul 15 13:20:09 2021 +0900
[A]SplitChunnk設定
#onelineオプションを指定した場合
s01386$ git log --oneline
66483ae (HEAD -> master, origin/master, default) [A]SplitChunnk設定
イメージで見るdetached HEAD
detached HEAD
の例を図で確認してみます。
例えば、最新のコミットがdのmasterブランチに自分がいる場合、HEADはmasterを指しています。
(a,b,c,dはコミット番号です)
HEAD
|
master
a --- b --- c --- d
ファイルを過去のコミットの状態にしたい場合に、昔のコミット(ここではb)にHEADを移動させたとします。
↓ git checkout b
すると、HEADはコミットbを指します。ですが、ここにはどのブランチも存在しません。これがHEADがどのブランチも指していない状態、すなわちdetached HEAD
です。
HEAD
| master
a --- b --- c --- d
detached HEADを元に戻す方法
detached HEADを元に戻すのは簡単です。git checkout <ブランチ名>
でHEADが特定のブランチを指すようにすれば表示されなくなります。
実例
#detached 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がどこを指しているか)
$ git branch
* (HEAD detached at HEAD)
master
#detached HEADを直す
$ git checkout master
Previous HEAD position was d53f0fe 444
Switched to branch 'master'
#現在のブランチを確認(HEADがどこを指しているか)
$ git branch
* master
detached HEADの状態でコミットしてしまった場合
detached HEAD
の状態でファイルを編集してコミットをした後に、git chekoutでブランチを指定して移動しようとすると次のような警告がでます。
$ git checkout master
Warning: you are leaving 1 commit behind, not connected to
any of your branches:
d792323 xxx
If you want to keep it by creating a new branch, this may be a good time
to do so with:
git branch <new-branch-name> d792323
detached HEADで行ったコミットはどこのブランチとも紐付いてませんよ、という警告です。
コミットを行ったdetached HEADの状態から他のブランチに戻ると、同じdetached HEADの状態に戻ることはできません。
ですが、その変更内容自体を表すコミット番号が記録されています。
変更内容が不要な場合
detached HEADで行ったコミットが不要な場合は、この警告を無視して問題ありません。detached HEADで行ったコミットはどこにも引き継がれません。
コミット前の変更(ステージ前やステージ後)の場合は、そのファイルの変更内容が移動後のブランチに引き継がれてしまいます。
detached HEADの変更内容を破棄するには、次のコマンドを実行します。
git reset --hard @
git reset --hard <コミット>
として使い、指定したコミットに戻るコマンドです。ファイルの変更内容は保持せず、完全に指定したコミットと同じ状態になります。
@は最新のコミットを指します。HEADと同じです(エイリアスです)。
変更内容を引き継ぎたい場合
detached HEADで行ったコミットを引き継ぎたい場合は、ブランチを移動後に、対象のコミット番号を指定してmergeします。
#特定のブランチにて(masterブランチなど)
git merge <コミット番号>
これで指定したコミットが、現在のブランチのコミットの中に取り込まれます。
実例
#detached HEADからmasterブランチに移動
$ git checkout master
Warning: you are leaving 1 commit behind, not connected to
any of your branches:
d792323 xxx
If you want to keep it by creating a new branch, this may be a good time
to do so with:
git branch <new-branch-name> d792323
#detached HEADのコミットを取り込む
$ git merge d792323
Updating 66483ae..d792323
Fast-forward
config/routes.rb | 2 ++
1 file changed, 2 insertions(+)
#ログの確認
$ git log --oneline
d792323 (HEAD -> master) xxx
66483ae (origin/master, default) [A]SplitChunnk設定
masterブランチに、detached HEADで行ったコミット「xxx」を取り込むことができました。
変更内容で新しブランチを作成したい場合
変更内容で新しいブランチを作成したい場合は、コミット番号を指定して新しいブランチを作成します。
git checkout -b <新しいブランチ名> <コミット番号>
detached HEADの状態でも、ブランチを移動してしまった後でもやることは同じです。
実例
例として、ブランチを移動した後に、detached HEADで行ったコミットを、新しいブランチ「newBranch」に引き継ぎます。
#detached HEADから他のブランチに移動した場合
$ git checkout master
Warning: you are leaving 1 commit behind, not connected to
any of your branches:
d792323 xxx
If you want to keep it by creating a new branch, this may be a good time
to do so with:
git branch <new-branch-name> d792323
#新しいブランチを作成して移動
$ git checkout -b newBranch d792323
Switched to branch 'newBranch'
#ログを確認
$ git log --oneline
d792323 (HEAD -> newBranch, master) xxx
66483ae (origin/master, default) [A]SplitChunnk設定
detached HEADを発生させる方法
detached HEADの状態はとても簡単に作り出すことができます。
git checkout
でコミットのみ指定して、ブランチやファイルパスを指定しなければdetached HEAD
になります。
git checkout <コミット>
実例
現在のコミットを指す「@」を指定して、checkoutを実行します。
$ git checkout @
Note: switching to '@'.
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.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
Turn off this advice by setting config variable advice.detachedHead to false
HEAD is now at 66483ae [A]SplitChunnk設定
detached HEADの使い所
detached HEADは混乱させるために存在しているわけではありません。きちんと役立つ使い道があります。
主な使い道は次の2つです。
過去のコミットの状態を再現する
detached HEADでできることは、一時的に過去のファイルの状態に戻すことができます。
git checkout <戻りたいコミット>
コミットの指定は、コミット番号以外に、@、HEADで指定することができます。
例
例えば、最新のコミットがdのmasterブランチに自分がいる状態で、一時的に過去のコミットbの状態を確認したい場合に使えます。
HEAD
|
master
a --- b --- c --- d
↓ git checkout b
これで自分がdetached HEADの状態になります。この時、最新のコミットはbになっています。ファイルの内容もbの状態になっています。
HEAD
| master
a --- b --- c --- d
確認が終わって、元のブランチに戻りたい時は、git checkout master
をするだけです。
実験的に変更を加える
更に、detached HEADで過去のコミットに遡った状態から変更を加えることもできます。
上のdetached HEADの状態で、ファイルを変更し、コミットeを行うと次のようになります。
HEAD
|
e
/ master
a --- b --- c --- d
更にコミットfを加えると次のようになります。
HEAD
|
e --- f
/ master
a --- b --- c --- d
確認が終わって、元のブランチに戻りたい時は、git checkout master
をするだけです。
もし、この実験状態を新しいブランチにしたい場合は、
git checkout -b <新しいブランチ名>
とすれば、この状態を維持したブランチを作成することができます。
例えば、newBranchを作成すると次のようになります。
HEAD
|
newBranch
e --- f
/ master
a --- b --- c --- d
こうしておけば、masterブランチに移動した後も、またnewBranchに戻ることができます。
参考リンク
git checkoutを使うとステージ前の変更を取り消すこともできます。
【Git】ステージ前の変更を取り消す方法。ファイルの指定と全てのファイル(Changes not staged for commit:の一覧から外す)