git HEAD~ vs HEAD^ vs HEAD@{} Explained with Examples


GIT

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 as HEAD^, similarly HEAD~~ is always the same as HEAD^^, 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 like HEAD@{1} and master@{1}, is just a synonym/alias/shortcut for the special Git reference 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 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:
git HEAD~ vs HEAD^ vs HEAD@{} Explained [Easy Examples]

 

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:

git HEAD~ vs HEAD^ vs HEAD@{} Explained [Easy Examples]

 

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:

git HEAD~ vs HEAD^ vs HEAD@{} Explained [Easy Examples]

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:

git HEAD~ vs HEAD^ vs HEAD@{} Explained [Easy Examples]

 

List of all commits using at-sign (@)

Here is a representation of all the commits using at-sign(@{}):

$ git reflog

Sample Output:

git HEAD~ vs HEAD^ vs HEAD@{} Explained [Easy Examples]

 

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

Deepak Prasad

Deepak Prasad

He is the founder of GoLinuxCloud and brings over a decade of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels in various domains, from development to DevOps, Networking, and Security, ensuring robust and efficient solutions for diverse projects. You can connect with him on his LinkedIn profile.

Can't find what you're searching for? Let us assist you.

Enter your query below, and we'll provide instant results tailored to your needs.

If my articles on GoLinuxCloud has helped you, kindly consider buying me a coffee as a token of appreciation.

Buy GoLinuxCloud a Coffee

For any other feedbacks or questions you can send mail to admin@golinuxcloud.com

Thank You for your support!!

Leave a Comment