git

git HEAD: the difference between HEAD~ and HEAD^

How to use git command 9/N

公開日: 2021-01-10
更新日: 2023-06-24

  Table of Contents

Difference between HEAD~ and HEAD^

git diffgit resetのとき直面するHEAD^(caret)とHEAD~(tilde)はどのような違いがあるのか? という疑問にぶち当たったのでまとめてみました. サマリーは以下のテーブルとなります:

表記 意味
~N N-th前のcommit
^N N番目のparent commit

What is Git HEAD?

Git 公式ドキュメントを見てみると, HEADについて以下のような説明がされています:

  • HEAD names the commit on which you based the changes in the working tree.

単純化してしまうと, HEADは現在のworking areaのベースとなる場所を指し示すポインターのことです. HEADというポインターがどこを示しているかは, repositoryの中の .git/HEAD に格納されています.

    gitGraph
        commit id:"A0001"
        commit id:"B0002"
        branch develop
        commit id: "C0003"
        commit id: "D0004 [HEAD]"
        checkout main
        commit id:"E0005"
        commit id:"F0006"

上記の流れで開発が進行した状況を考えます. そして現在develop branchにいるとします. まずこのときのHEADを以下のコマンドで確認してみます.

1
2
3
4
% cat .git/HEAD
ref: refs/heads/develop
% cat .git/refs/heads/develop
D0004

このようにdevelopにいるときのHEADはdevelop branch自体を指しており, develop branchはD0004という最新のcommitを指しています.

次に, mainへswitchしてからHEADを確認してみます:

1
2
3
4
5
% git switch main
% cat .git/HEAD
ref: refs/heads/main
% cat .git/refs/heads/develop
F0006

mainにいるときのHEADはmain branch自体を指しており, main branchはF0006という最新のcommitを指しています. これはGitの内部挙動的には, branchがcommit-hashをreferenceしているに過ぎないものというのに関係しています.

最後に, 任意の過去のcommit-hashを指定してcheckoutした時のHEADを確認してみます.

1
2
3
% git switch --detach B0002
% cat .git/HEAD
B0002

このとき, HEADは直接commit-hashを参照しています. このようにbranchを介さずに直接commit-hashを参照している HEAD のことを特に DETACHED HEAD と呼びます.

DETACHED HEADのときは, branchがない状態と理解しても大丈夫です.

Column: DEATCHED HEADはいつ使うのか?

特定のコミット時点でのコード全体をreview, testをする際にDETACHED HEADを使います.

DETACHED HEAD状態から新たに開発の必要性が出た場合は,

1
% git switch -c <new-branch-name>

でbranchを新たに作成してから開発する必要があります. これを未実行のまま, 開発を進めcommitを作成しても一度ほかのbranchへswitchした瞬間にそれまでの作業記録がなくなってしまいます. (逆に, 他のbranchに移る前にbranchを新たに作成すれば作業変更記録がlogとして残ります)

What is HEAD~?

公式ドキュメントの定義を確認します:

A suffix ~ to a revision parameter means the first parent of that commit object. A suffix ~<n> to a revision parameter means the commit object that is the <n>th generation ancestor of the named commit object, following only the first parents.

単純化すると, Gitの文脈における ~N は, 指定したcommitから数えて同じbranchのN-th step前のcommit-hashを指し示すシンボルです.

    gitGraph
        commit id:"A"
        commit id:"B"
        branch develop
        commit id:"C"
        commit id:"D"
        checkout main
        merge develop id:"E"
        commit id: "F[HEAD]"

上記の例の場合,

  • HEAD~: commit E
  • HEAD~2: commit B
  • HEAD~~: commit B
  • E~: commit B
  • E~2: commit A

を指し示します.

Column: HEAD~ is different from ORIG_HEAD

  • ORIG_HEADHEADの直前の状態を示すシンボル
  • git reflogコマンドで確認できるHEAD@{1}に相当

What is HEAD^?

公式ドキュメントの定義を確認します:

A suffix ^ to a revision parameter means the first parent of that commit object. ^<n> means the <n>th parent (i.e. <rev>^ is equivalent to <rev>^1). As a special rule, <rev>^0 means the commit itself

HEADに対して, N番目のparent commitを指し示すのが HEAD^Nとなります. 基本的には余り使わないほうが良いと思います.

    gitGraph
        commit id:"A"
        commit id:"B"
        branch develop
        commit id:"C"
        commit id:"D"
        checkout main
        merge develop id:"E"
        commit id: "F[HEAD]"

上記の例の場合,

  • HEAD^: commit E
  • HEAD^2: 存在しない(fatal: invalid reference: HEAD^2)
  • HEAD^^: commit B(=親の親なので)
  • E^: commit B
  • E^^: commit A
  • E^2: commit D(2人の親はmerge source branch)
  • E^2~: commit C
  • E^2~3: commit A

を指し示します.

How to Use HEAD~ and HEAD^ in Git?

Checking the diff between HEAD and the last commit by using git difftool

Editor経由で直前のcommitとの(またはN-th前の)差分をgit difftoolとの組合せで 簡単に確認することができます.

1
2
3
4
# 直前のcommitとの差分を確認したい場合
% git difftool HEAD~  
# 特定のファイルについて, 直前のcommitとの差分を確認したい場合
% git difftool HEAD~ <file-name>  

merge commitの直後に, mergeによる変更前のconflictを含む差分は以下のコマンドで確認することが出来ます

1
% git difftool HEAD~ HEAD^2

References



Share Buttons
Share on:

Feature Tags
Leave a Comment
(注意:GitHub Accountが必要となります)