Understanding git HEAD~ vs HEAD^ vs HEAD@{} - Brief comparison
- The tilde (
~
), caret(^
) and at-sign(@
) are reference suffixes used in GIT - The tilde(
~
) sign refers to the first parent in the commit history.HEAD~
is always the same asHEAD^
, similarlyHEAD~~
is always the same asHEAD^^
, and so on. - The caret(
^
) sign refer to the parent of that particular commit. So, if you place a^
(caret) at the end of a commit reference, Git resolves it to mean the parent of that commit. - The at-sign (
@
), without a leading branch/reference name and ordinal{n}
suffix likeHEAD@{1}
andmaster@{1}
, is just a synonym/alias/shortcut for the special Git referenceHEAD
- 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 and is used when<rev>
is the object name of a tag object that refers to a commit object. - A suffix
^
followed by an at-sign (@
) is the same as listing all parents of<rev>
(meaning, include anything reachable from its parents, but not the commit itself).
You may not be able to understand the comparison completely, so let's take multiple examples to understand and compare HEAD^
vs HEAD~
and HEAD@{}
.
Display commit graph in git
Here is a graphical representation of the commit history on all the branches from our repository.
deepak@ubuntu:~/git_examples$ git log --graph --decorate --oneline
Sample Output:
How to get all the parent commit ID from commit history in Git
Since we will be dealing with parents when using tilde (~
) or caret (^
) sign, so we should also be familiar with the ways to check parent of any commit.
Based on our commit history, to list the available parents we can use:
deepak@ubuntu:~/git_examples$ git log --no-walk HEAD^@
commit 6850575b9e6e19d7144b4dec70b29efbcf7c4777 Author: Deepak Prasad <admin@golinuxcloud.com> Date: Tue Sep 21 08:33:04 2021 +0530 main-commit-4 commit 93adddfc946b331daf983be6d14eab7f421053d6 (prod) Author: Deepak Prasad <admin@golinuxcloud.com> Date: Tue Sep 21 08:33:04 2021 +0530 prod-commit-2
Here, we used ^@
to get all parents and the --no-walk
option to show only the parents and not their ancestors. So in our commit history we have two parent commit IDs i.e.:
commit 6850575b9e6e19d7144b4dec70b29efbcf7c4777
commit 93adddfc946b331daf983be6d14eab7f421053d6 (prod)
How to get the parent details of any commit ID in Git
We can use following command to get the parent ID of individual commits in Git:
To get the parent for 6850575
and f5aaccb
commit ID respectively:
deepak@ubuntu:~/git_examples$ git log --pretty=%P -n 1 6850575 28a60d8fffbe3a613ba8b3da501f37d215c177c2 deepak@ubuntu:~/git_examples$ git log --pretty=%P -n 1 f5aaccb e37fd194328c1c9b0ac062017ad0e68700a09552
Now that we are familiar with the commands to get the parent of any commit, lets start using HEAD^, HEAD~ in our examples to explain the difference.
Compare HEAD^ vs HEAD~ vs HEAD@{n}
The special characters i.e. tilde(~
), caret(^
) and at-sign(@
) are to be used with a particular commit ID to get respective details. We are using HEAD so you have to understand that position of HEAD can be different for individual branch.
Comparison Criteria | HEAD | HEAD^ | HEAD~ | HEAD@{n} |
---|---|---|---|---|
Reference to | Current branch’s latest commit | Parent of the latest commit | Ancestor of the latest commit | Specific reference in the reflog |
Usage | Identifies the latest commit | Refers to the immediate parent of the latest commit | Specifies the first parent commit recursively | Navigates through the reflog entries |
Flexibility | Fixed to the latest commit | Can specify which parent with HEAD^2 , HEAD^3 , etc. |
Can define ancestor level, e.g., HEAD~2 , HEAD~3 |
Access previous states of a branch by index |
Common Use Case | Checking out, diffing, logging | Viewing changes introduced in the last commit | Traversing parent commits | Recovering lost commits, checking previous states of the repository |
Practical Example | git show HEAD |
git diff HEAD^ |
git log HEAD~3 |
git checkout HEAD@{1} |
Explanation:
- HEAD: Always pointing at the most recent commit of the current branch.
- HEAD^: Useful to refer to the parent commit, handy for understanding recent changes.
- HEAD~: Used for navigating through ancestors, great for traversing commits.
- HEAD@{n}: Allows accessing various states in the repository’s history, essential for recovery and historical checks.
Let me explain using some dummy commit IDs
Here is a mapping of the commit IDs with the individual HEAD references based on a hypothetical scenario:
- A (oldest)
- B
- C
- D (most recent)
Mapping with HEAD references:
HEAD -> D (Current commit) HEAD^ -> C (Parent of the current commit) HEAD^^ -> B (Parent of the parent of the current commit) HEAD~2 -> B (Grandparent of the current commit) HEAD@{1} -> C (Previous state in the reflog, assuming a checkout or other operation hasn’t altered the position)
List all commits using HEAD with tilde(~) sign
Here is a representation of all the commits in my repository using HEAD with tilde(~
) sign:
List of all commits using both caret(^) when combined with tilde(~) sign
Here is a representation of all the commits using both tilde(~
) and caret(^
) sign for HEAD^1
:
$ git log --oneline --graph HEAD^1
Sample Output:
Here is a representation of all the commits using both tilde(~
) and caret(^
) sign for HEAD^2
:
$ git log --oneline --graph HEAD^2
Sample Output:
List of all commits using at-sign (@)
Here is a representation of all the commits using at-sign(@{}
):
$ git reflog
Sample Output:
Summary
In this tutorial we learned the difference between the usage of tilde(~
), caret(^
) and at-sign(@{}
) used in git. These suffix can be preferred in use cases where we don't have to deal with multiple parents in the commit history. You can always first locate the respective parent commit id and then use both tilde and caret sign together to refer individual commits part of the respective parent. Ideally if HEAD was a merge, then first parent is the branch into which we merged and second parent is the branch we merged.
Further Reading Resources
Git Tools - Revision Selection
HEAD~ vs HEAD^ vs HEAD@{} also known as tilde vs caret vs at sign