Tilde and Caret in Git¶
I run into ~1
and ^1
commands used interchangeably in Git commands but I never really understood
the difference between them. Such as git reset HEAD~1
and git reset HEAD^1
usages. As it turns
out, even though the output is the same, they are not the same thing.
TLDR; Tilde points the first parent commit, while caret points the specified parent of a commit. See Example Visual Git Graph
Tilde (~)¶
Tilde (~
) is used to point the first parent commit. A commit can have multiple parents in
case of a merge commit, such as a4079a6
in the example below. Tilde follows the first parent.
Example:
$ git log --graph --oneline --date-order
* a4079a6 (HEAD -> main) Merge branch 'feature-1'
|\
* | 878f26d add readme
| * c482dd1 (feature-1) add tests
| * 9b5e39e wip 2
| * b23144e wip 1
|/
* 9786a71 initial commit
$ git show -s --oneline HEAD
a4079a6 (HEAD -> main) Merge branch 'feature-1'
$ git show -s --oneline HEAD~
878f26d add readme
$ git show -s --oneline HEAD~2
9786a71 initial commit
You can use multiple consecutive tildes to follow the first parent multiple times. Or you can use
HEAD~n
to follow the first parent n
times. (HEAD~
= HEAD~1
, HEAD~~
= HEAD~2
, etc.)
Caret (^)¶
Caret (^
) is used to point a specific parent of a commit. With tilde you can
only point the first parent, but with caret you choose which parent to follow.
By default, HEAD^
means the first parent which is the same as HEAD~
. HEAD^2
means the second
parent, HEAD^3
means the third parent, and so on.
Example:
$ git log --graph --oneline --date-order
* a4079a6 (HEAD -> main) Merge branch 'feature-1'
|\
* | 878f26d add readme
| * c482dd1 (feature-1) add tests
| * 9b5e39e wip 2
| * b23144e wip 1
|/
* 9786a71 initial commit
$ git show -s --oneline HEAD^
878f26d add readme
$ git show -s --oneline HEAD^2
c482dd1 (feature-1) add tests
$ git show -s --oneline HEAD^2~
9b5e39e wip 2
$ git show -s --oneline HEAD^2~2
b23144e wip 1
Be aware in the caret usage, HEAD^2
is not the same thing as HEAD^^
. HEAD^2
means the second
parent of the commit, while HEAD^^
means the first parent of the first parent of the commit.
Even though in practice you can, I prefer not use HEAD^
instead of HEAD~
to avoid confusion. And
I suggest you to do the same. Only use caret when you need to specify a parent other than the first
one.
I only gave examples relative to HEAD
but you can use these notations with commit hashes. For
example, $ git show c482dd1~
will show the first parent of the commit c482dd1
.
Example Over a Visual Git Graph¶
Here is an example Git graph to demonstrate the usage of tilde and carets for specifying commits relative to HEAD
:
Mermaid source code for the graph:
gitGraph
commit id: "initial commit"
commit id: "add readme"
branch ci-cd
checkout main
commit id: "add license"
checkout ci-cd
commit id: "wip 1"
branch fix-ci-cd
checkout ci-cd
commit id: "wip 2"
checkout fix-ci-cd
commit id: "fix workflow"
checkout ci-cd
merge fix-ci-cd id: "merge branch fix-ci-cd"
checkout main
merge ci-cd id: "merge branch ci-cd"
commit id: "update readme"
Create this history in Git:
$ git init
$ git commit --allow-empty -m "initial commit"
[main (root-commit) 06cd678] initial commit
$ git commit --allow-empty -m "add readme"
[main 4a84c04] add readme
$ git checkout -b ci-cd
Switched to a new branch 'ci-cd'
$ git checkout main
Switched to branch 'main'
$ git commit --allow-empty -m "add license"
[main 60f2398] add license
$ git checkout ci-cd
Switched to branch 'ci-cd'
$ git commit --allow-empty -m "wip 1"
[ci-cd 3986280] wip 1
$ git checkout -b fix-ci-cd
Switched to a new branch 'fix-ci-cd'
$ git checkout ci-cd
Switched to branch 'ci-cd'
$ git commit --allow-empty -m "wip 2"
[ci-cd 705e155] wip 2
$ git checkout fix-ci-cd
Switched to branch 'fix-ci-cd'
$ git commit --allow-empty -m "fix-workflow"
[fix-ci-cd 89403bd] fix-workflow
$ git checkout ci-cd
Switched to branch 'ci-cd'
$ git merge fix-ci-cd
Merge made by the 'ort' strategy.
$ git checkout main
Switched to branch 'main'
$ git merge ci-cd
Merge made by the 'ort' strategy.
$ git commit --allow-empty -m "update readme"
[main ba3ce9c] update readme
$ git log --graph --oneline --date-order
* ba3ce9c (HEAD -> main) update readme
* 699b2d3 merge branch 'ci-cd'
|\
| * 1d86cc1 (ci-cd) merge branch 'fix-ci-cd'
| |\
| | * 89403bd (fix-ci-cd) fix-workflow
| * | 705e155 wip 2
| |/
| * 3986280 wip 1
* | 60f2398 add license
|/
* 4a84c04 add readme
* 06cd678 initial commit