1. 概要

gitの作業中に不思議な状態に遭遇することは、それほど珍しいことではありません。 しかし、ある日、「切り離されたHEAD」が表示される可能性が最も高くなります。

このチュートリアルでは、切り離されたHEADとは何か、およびそれがどのように機能するかについて説明します。 Gitで切り離されたHEADに出入りする方法について説明します。

2. GitのHEADとは

Gitは、コミットを作成するときに、リポジトリ内のすべてのファイルの状態の記録を保存します。 HEADはもう1つの重要なタイプの参照です。 HEADの目的は、Gitリポジトリの現在のポイントを追跡することです。 言い換えれば、HEADは「私は今どこにいますか?」という質問に答えます。

$git log --oneline
a795255 (HEAD -> master) create 2nd file
5282c7c appending more info
b0e1887 creating first file

たとえば、logコマンドを使用する場合、Gitはどのコミットから結果の表示を開始する必要があるかをどのように知るのでしょうか。 HEADが答えを提供します。 新しいコミットを作成すると、その親はHEADが現在指している場所によって示されます。

Gitにはこのような高度なバージョン追跡機能があるため、リポジトリ内の任意の時点に戻ってコンテンツを表示できます。

過去のコミットを確認できることで、リポジトリ、特定のファイル、またはファイルのセットが時間の経過とともにどのように進化したかを確認することもできます。 ブランチではないコミットをチェックアウトすると、「切り離されたHEAD状態」になります。これは、リポジトリ内の最新のコミットではないコミットを表示している場合を指します。

3. 切り離されたHEADの例

ほとんどの場合、HEADはブランチ名を指します。新しいコミットを追加すると、ブランチ参照はそれを指すように更新されますが、HEADは同じままです。 ブランチを変更すると、切り替えたブランチを指すようにHEADが更新されます。 つまり、上記のシナリオでは、HEADは「現在のブランチの最後のコミット」と同義です。 これは、HEADがブランチに接続されている通常の状態です。

ご覧のとおり、HEADは最後のコミットを指すマスターブランチを指しています。 すべてが完璧に見えます。 ただし、以下のコマンドを実行した後、リポジトリは切り離されたHEADにあります。

$ git checkout 5282c7c
Note: switching to '5282c7c'.

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 -

HEAD is now at 5282c7c appending more info

users@ubuntu01: MINGW64 ~/git/detached-head-demo ((5282c7c...))

以下は、現在のgitHEADのグラフィック表現です。 以前のコミットにチェックアウトしたので、HEADは 5282c7c commitを指しており、マスターブランチはまだ同じものを参照しています。

4. GitDetachedHEADの利点

特定の( 5282c7c)コミットをチェックアウトしてHEADを切り離した後、プロジェクトの履歴の前のポイントに戻ることができます。

先週の火曜日に特定のバグがすでに存在するかどうかを確認したいとします。 logコマンドを使用して、日付でフィルタリングし、関連するコミットハッシュを開始できます。 次に、コミットをチェックアウトし、手動または自動テストスイートを実行してアプリケーションをテストできます。

過去を見るだけでなく、それを変えることができたらどうでしょうか。 それは、切り離されたHEADが私たちにできることです。 以下のコマンドを使用してそれを行う方法を確認しましょう。

echo "understanding git detached head scenarios" > sample-file.txt
git add .
git commit -m "Create new sample file"
echo "Another line" >> sample-file.txt
git commit -a -m "Add a new line to the file"

これで、2番目のコミットから派生した2つの追加のコミットがあります。 git log –oneline を実行して、結果を確認してみましょう。

$ git log --oneline
7a367ef (HEAD) Add a new line to the file
d423c8c create new sample file
5282c7c appending more info
b0e1887 creating first file

HEADが指す前に 5282c7c コミットしてから、さらに2つのコミットを追加しました。 d423c8c 7a367ef。 以下は、HEAD上で実行されたコミットのグラフ表示です。 これは、HEADが最新のコミット7a367efを指していることを示しています。

これらの変更を保持するか、前の変更に戻したい場合はどうすればよいですか? 次のポイントで見ていきます。

5. シナリオ

5.1. 事故による

誤って切り離されたHEAD状態に達した場合、つまり、コミットをチェックアウトするつもりはなかった場合、戻るのは簡単です。 以下のコマンドを使用する前に、私たちがいたブランチをチェックしてください。

git switch <branch-name>   
git checkout <branch-name>

5.2. 実験的な変更を行いましたが、それらを破棄する必要があります

一部のシナリオでは、一部の機能をテストしたりバグを特定したりするためにHEADをデタッチした後に変更を加えたが、これらの変更を元のブランチにマージしたくない場合は、前のシナリオと同じコマンドを使用して単純に破棄し、元のブランチをバックアップします。

5.3. 実験的な変更を行いましたが、それらを維持する必要があります

切り離されたHEADで行われた変更を保持したい場合は、新しいブランチを作成してそれに切り替えます。 切り離されたHEADに到達した直後、または1つ以上のコミットを作成した後に作成できます。 結果は同じです。 唯一の制限は、通常のブランチに戻る前にそれを行う必要があるということです。 1つ以上のコミットを作成した後、以下のコマンドを使用してデモリポジトリでそれを実行しましょう。

git branch experimental
git checkout experimental

git log –oneline の結果が以前とまったく同じであることがわかりますが、最後のコミットで示されたブランチの名前だけが異なります。

$ git log --oneline
7a367ef (HEAD -> experimental) Add a new line to the file
d423c8c create new sample file
5282c7c appending more info
b0e1887 creating first file

6. 結論

この記事で見たように、HEADが切り離されているからといって、リポジトリに問題があるわけではありません。 切り離されたHEADは、リポジトリが存在する可能性のある、あまり一般的ではない状態です。 エラーではないことを除けば、実際には非常に便利であり、実験を実行して、保持するか破棄するかを選択できます。