Table of Contents
Difference between HEAD~
and HEAD^
git diff
やgit 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 EHEAD~2
: commit BHEAD~~
: commit BE~
: commit BE~2
: commit A
を指し示します.
Column: HEAD~
is different from ORIG_HEAD
ORIG_HEAD
はHEAD
の直前の状態を示すシンボル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 EHEAD^2
: 存在しない(fatal: invalid reference: HEAD^2
)HEAD^^
: commit B(=親の親なので)E^
: commit BE^^
: commit AE^2
: commit D(2人の親はmerge source branch)E^2~
: commit CE^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
- Git 公式ドキュメント > HEAD, ORIG_HEAD, MERGE_HEAD etc
- Git 公式ドキュメント > DETACHED HEAD
- stackoverflow > What’s the difference between HEAD^ and HEAD~ in Git?
(注意:GitHub Accountが必要となります)