When using relative references to identify git commits, there is a difference between HEAD~1 and HEAD^1. The ~ looks back linearly at commits from earlier generations of a single branch, while the ^ walks through all immediate parents for a single commit. These may seem similar, but that's only the case until a fork is introduced into the repository. Once someone creates a merge commit, these two symbols become very different.

An example

To illustrate different commit references, we will use the example created by Jon Loeliger. Here's his git repository structure:

         A
        / \
       /   \
      B     C
     /|\    |
    / | \   |
   /  |  \ /
  D   E   F
 / \     / \
G   H   I   J
         

Any git commits near the bottom-left of the visualization are older, while the ones towards the top or right sides are newer. One implementation might look like this:

$ git log --graph --oneline
*   7522b1c (HEAD -> main) Commit A
|\
| * 8ae601a Commit C
| |
|  \
*-. | 59eb30f Commit B
|\ \|
| | *   6421881 Commit F
| | |\
| | | * 75fe470 Commit J
| | * bb7eb7c Commit I
| |
| * 3fd5db5 Commit E
|
*   bac00e3 Commit D
|\
| * d8daf15 Commit H
| |
* eba4957 Commit G

For demonstration purposes, I find this visualization easiest:

A git log-like graph of Jon Loeliger's example git repository.

The tilde

Using commit A as a starting point, we use tildes to walk vertically down the left-most branch, i.e. B = A~1, D = A~2, etc. There is no way to reference commits in other branches using the tilde.

Tilde references scan chronologically back along one branch's ancestry line.

The caret

Using commit A as a starting point, we use carets to walk horizontally forward through all parent objects, i.e. B = A^1 and C = A^2. Parent references are 1-indexed (and ^ is specially reserved to reference the starting commit).

It's common to use ^ as shorthand for ^1, so B = A^ too. However, even though ^1^1 shortens to ^^, this isn't the same thing as ^2. A^1^1 is commit D, while A^2 is commit C.

Caret references scan chronologically forward among all parents in the same generation at a fork.

Putting it together

Here are ways to reference other commits in this repository:

A =      = A^0
B = A^   = A^1     = A~1
C =      = A^2
D = A^^  = A^1^1   = A~2
E = B^2  = A^^2
F = B^3  = A^^3
G = A^^^ = A^1^1^1 = A~3
H = D^2  = B^^2    = A^^^2  = A~2^2
I = F^   = B^3^    = A^^3^
J = F^2  = B^3^2   = A^^3^2

It's also possible to mix-and-match tildes and carets. For example, F = A~1^3. Order matters!