When I specify an ancestor commit object in Git, I'm confused between HEAD^
and HEAD~
.
Both have a "numbered" version like HEAD^3
and HEAD~2
.
They seem very similar or the same to me, but are there any differences between the tilde and the caret?
Rules of thumb
Use ~ most of the time — to go back a number of generations, usually what you want
Use ^ on merge commits — because they have two or more (immediate) parents
Mnemonics:
Tilde ~ is almost linear in appearance and wants to go backward in a straight line
Caret ^ suggests an interesting segment of a tree or a fork in the road
Tilde
The “Specifying Revisions” section of the git rev-parse
documentation defines ~
as
You can get to parents of any commit, not just HEAD
. You can also move back through generations: for example, master~2
means the grandparent of the tip of the master branch, favoring the first parent on merge commits.
Caret
Git history is nonlinear: a directed acyclic graph (DAG) or tree. For a commit with only one parent, rev~
and rev^
mean the same thing. The caret selector becomes useful with merge commits because each one is the child of two or more parents — and strains language borrowed from biology.
HEAD^
means the first immediate parent of the tip of the current branch. HEAD^
is short for HEAD^1
, and you can also address HEAD^2
and so on as appropriate. The same section of the git rev-parse
documentation defines it as
Examples
These specifiers or selectors can be chained arbitrarily, e.g., topic~3^2
in English is the second parent of the merge commit that is the great-grandparent (three generations back) of the current tip of the branch topic
.
The aforementioned section of the git rev-parse
documentation traces many paths through a notional git history. Time flows generally downward. Commits D, F, B, and A are merge commits.
Here is an illustration, by Jon Loeliger. Both commit nodes B and C are parents of commit node A. Parent commits are ordered left-to-right. (N.B. The git log --graph command displays history in the opposite order.) G H I J \ / \ / D E F \ | / \ \ | / | \|/ | B C \ / \ / A 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
Run the code below to create a git repository whose history matches the quoted illustration.
#! /usr/bin/env perl
use strict;
use warnings;
use subs qw/ postorder /;
use File::Temp qw/ mkdtemp /;
my %sha1;
my %parents = (
A => [ qw/ B C / ],
B => [ qw/ D E F / ],
C => [ qw/ F / ],
D => [ qw/ G H / ],
F => [ qw/ I J / ],
);
sub postorder {
my($root,$hash) = @_;
my @parents = @{ $parents{$root} || [] };
postorder($_, $hash) for @parents;
return if $sha1{$root};
@parents = map "-p $sha1{$_}", @parents;
chomp($sha1{$root} = `git commit-tree @parents -m "$root" $hash`);
die "$0: git commit-tree failed" if $?;
system("git tag -a -m '$sha1{$root}' '$root' '$sha1{$root}'") == 0 or die "$0: git tag failed";
}
$0 =~ s!^.*/!!; # / fix Stack Overflow highlighting
my $repo = mkdtemp "repoXXXXXXXX";
chdir $repo or die "$0: chdir: $!";
system("git init") == 0 or die "$0: git init failed";
chomp(my $tree = `git write-tree`); die "$0: git write-tree failed" if $?;
postorder 'A', $tree;
system "git update-ref HEAD $sha1{A}"; die "$0: git update-ref failed" if $?;
system "git update-ref master $sha1{A}"; die "$0: git update-ref failed" if $?;
# for browsing history - http://blog.kfish.org/2010/04/git-lola.html
system "git config alias.lol 'log --graph --decorate --pretty=oneline --abbrev-commit'";
system "git config alias.lola 'log --graph --decorate --pretty=oneline --abbrev-commit --all'";
It adds aliases in the new throwaway repo only for git lol
and git lola
so you can view history as in
$ git lol
* 29392c8 (HEAD -> master, tag: A) A
|\
| * a1ef6fd (tag: C) C
| |
| \
*-. \ 8ae20e9 (tag: B) B
|\ \ \
| | |/
| | * 03160db (tag: F) F
| | |\
| | | * 9df28cb (tag: J) J
| | * 2afd329 (tag: I) I
| * a77cb1f (tag: E) E
* cd75703 (tag: D) D
|\
| * 3043d25 (tag: H) H
* 4ab0473 (tag: G) G
Note that on your machine the SHA-1 object names will differ from those above, but the tags allow you to address commits by name and check your understanding.
$ git log -1 --format=%f $(git rev-parse A^)
B
$ git log -1 --format=%f $(git rev-parse A~^3~)
I
$ git log -1 --format=%f $(git rev-parse A^2~)
F
The “Specifying Revisions” in the git rev-parse
documentation is full of great information and is worth an in-depth read. See also Git Tools - Revision Selection from the book Pro Git.
Order of Parent Commits
The commit 89e4fcb0dd from git’s own history is a merge commit, as git show 89e4fcb0dd
indicates with the Merge header line that displays the immediate ancestors’ object names.
commit 89e4fcb0dd01b42e82b8f27f9a575111a26844df
Merge: c670b1f876 649bf3a42f b67d40adbb
Author: Junio C Hamano
We can confirm the ordering by asking git rev-parse
to show 89e4fcb0dd’s immediate parents in sequence.
$ git rev-parse 89e4fcb0dd^1 89e4fcb0dd^2 89e4fcb0dd^3
c670b1f876521c9f7cd40184bf7ed05aad843433
649bf3a42f344e71b1b5a7f562576f911a1f7423
b67d40adbbaf4f5c4898001bf062a9fd67e43368
Querying the non-existent fourth parent results in an error.
$ git rev-parse 89e4fcb0dd^4
89e4fcb0dd^4
fatal: ambiguous argument '89e4fcb0dd^4': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'
If you want to extract the parents only, use pretty format %P
for the full hashes
$ git log -1 --pretty=%P 89e4fcb0dd
c670b1f876521c9f7cd40184bf7ed05aad843433 649bf3a42f344e71b1b5a7f562576f911a1f7423 b67d40adbbaf4f5c4898001bf062a9fd67e43368
or %p
for abbreviated parents.
$ git log -1 --pretty=%p 89e4fcb0dd
c670b1f876 649bf3a42f b67d40adbb
The difference between HEAD^
and HEAD~
is well described by the illustration (by Jon Loeliger) found on http://www.kernel.org/pub/software/scm/git/docs/git-rev-parse.html.
This documentation can be a bit obscure to beginners so I've reproduced that illustration below:
G H I J
\ / \ /
D E F
\ | / \
\ | / |
\|/ |
B C
\ /
\ /
A
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
F = A^2^
.
^ == ^1 == LEFTMOST PARENT
, ^2 == SECOND LEFTMOST PARENT
and so forth. And ~ == ~1 == LEFTMOST PARENT
, ~2 == LEFTMOST PARENTS LEFTMOST PARENT == LEFTMOST GRANDPARENT
. By extension, ~2^2 == LEFTMOST GRANDPARENTS SECOND LEFTMOST PARENT
git merge B C
. That's a 3-way merge.
git merge E F
. The resulting commit was B.
Both ~
and ^
on their own refer to the parent of the commit (~~
and ^^
both refer to the grandparent commit, etc.) But they differ in meaning when they are used with numbers:
~2 means up two levels in the hierarchy, via the first parent if a commit has more than one parent
^2 means the second parent where a commit has more than one parent (i.e. because it's a merge)
These can be combined, so HEAD~2^3
means HEAD
's grandparent commit's third parent commit.
^^
was the same as ^2
but it's not .
branch^
and branch~
are the same thing. Well done for leading with that point - I came here wondering whether to type git reset HEAD^
or git reset HEAD~
. If I'd read this answer first, I could have left a lot sooner.
My two cents...
https://i.stack.imgur.com/pDAzG.png
H=A~2^2
not H=A~2^1
?
A
, B
, D
, G
are on the same branch and the commit D
is a merge of G
and H
, therefore having two parents. So the commit (H
) from other branch is reference by ^2
.
git cat-file -p D
and look at the list of parents?
Here's a very good explanation taken verbatim from http://www.paulboxley.com/blog/2011/06/git-caret-and-tilde :
ref~ is shorthand for ref~1 and means the commit's first parent. ref~2 means the commit's first parent's first parent. ref~3 means the commit's first parent's first parent's first parent. And so on. ref^ is shorthand for ref^1 and means the commit's first parent. But where the two differ is that ref^2 means the commit's second parent (remember, commits can have two parents when they are a merge). The ^ and ~ operators can be combined.
https://i.stack.imgur.com/4Bdtm.png
The ^<n>
format allows you to select the nth parent of the commit (relevant in merges). The ~<n>
format allows you to select the nth ancestor commit, always following the first parent. See git-rev-parse's documentation for some examples.
Simplistically:
~ specifies ancestors
^ specifies parents
You can specify one or more branches when merging. Then a commit has two or more parents and then ^
is useful to indicate parents.
Suppose you are on branch A and you have two more branches: B and C.
On each branch the three last commits are:
A: A1, A2, A3
B: B1, B2, B3
C: C1, C3, C3
If now on branch A you execute the command:
git merge B C
then you are combining three branches together (here your merge commit has three parents)
and
~
indicates the n'th ancestor in the first branch, so
HEAD~ indicates A3
HEAD~2 indicates A2
HEAD~3 indicates A1
^
indicates the n'th parent, so
HEAD^ indicates A3
HEAD^2 indicates B3
HEAD^3 indicates C3
The next use of ~
or ^
next to each other is in the context of the commit designated by previous characters.
Notice 1:
HEAD~3 is always equal to: HEAD~~~ and to: HEAD^^^ (every indicates A1),
and generally:
HEAD~n is always equal to: HEAD~...~ (n times ~) and to: HEAD^...^ (n times ^).
Notice 2:
HEAD^3 is not the same as HEAD^^^ (the first indicates C3 and the second indicates A1),
and generally:
HEAD^1 is the same as HEAD^,
but for n > 1: HEAD^n is always not the same as HEAD^...^ (n times ^).
It is worth noting that git also has a syntax for tracking "from-where-you-came"/"want-to-go-back-now" - for example, HEAD@{1}
will reference the place from where you jumped to new commit location.
Basically HEAD@{}
variables capture the history of HEAD movement, and you can decide to use a particular head by looking into reflogs of git using the command git reflog
.
Example:
0aee51f HEAD@{0}: reset: moving to HEAD@{5}
290e035 HEAD@{1}: reset: moving to HEAD@{7}
0aee51f HEAD@{2}: reset: moving to HEAD@{3}
290e035 HEAD@{3}: reset: moving to HEAD@{3}
9e77426 HEAD@{4}: reset: moving to HEAD@{3}
290e035 HEAD@{5}: reset: moving to HEAD@{3}
0aee51f HEAD@{6}: reset: moving to HEAD@{3}
290e035 HEAD@{7}: reset: moving to HEAD@{3}
9e77426 HEAD@{8}: reset: moving to HEAD@{3}
290e035 HEAD@{9}: reset: moving to HEAD@{1}
0aee51f HEAD@{10}: reset: moving to HEAD@{4}
290e035 HEAD@{11}: reset: moving to HEAD^
9e77426 HEAD@{12}: reset: moving to HEAD^
eb48179 HEAD@{13}: reset: moving to HEAD~
f916d93 HEAD@{14}: reset: moving to HEAD~
0aee51f HEAD@{15}: reset: moving to HEAD@{5}
f19fd9b HEAD@{16}: reset: moving to HEAD~1
290e035 HEAD@{17}: reset: moving to HEAD~2
eb48179 HEAD@{18}: reset: moving to HEAD~2
0aee51f HEAD@{19}: reset: moving to HEAD@{5}
eb48179 HEAD@{20}: reset: moving to HEAD~2
0aee51f HEAD@{21}: reset: moving to HEAD@{1}
f916d93 HEAD@{22}: reset: moving to HEAD@{1}
0aee51f HEAD@{23}: reset: moving to HEAD@{1}
f916d93 HEAD@{24}: reset: moving to HEAD^
0aee51f HEAD@{25}: commit (amend): 3rd commmit
35a7332 HEAD@{26}: checkout: moving from temp2_new_br to temp2_new_br
35a7332 HEAD@{27}: commit (amend): 3rd commmit
72c0be8 HEAD@{28}: commit (amend): 3rd commmit
An example could be that I did local-commits a->b->c->d and then I went back discarding 2 commits to check my code - git reset HEAD~2
- and then after that I want to move my HEAD back to d - git reset HEAD@{1}
.
HEAD@{n}
syntax. Any link to the document?
An actual example of the difference between HEAD~ and HEAD^:
https://i.stack.imgur.com/1qqSY.jpg
TLDR
~ is what you want most of the time, it references past commits to the current branch
^ references parents (git-merge creates a 2nd parent or more)
A~ is always the same as A^ A~~ is always the same as A^^, and so on A~2 is not the same as A^2 however, because ~2 is shorthand for ~~ while ^2 is not shorthand for anything, it means the 2nd parent
HEAD^^^ is the same as HEAD~3, selecting the third commit before HEAD
HEAD^2 specifies the second head in a merge commit
HEAD~ specifies the first parent on a "branch"
HEAD^ allows you to select a specific parent of the commit
An Example:
If you want to follow a side branch, you have to specify something like
master~209^2~15
^ BRANCH Selector git checkout HEAD^2 Selects the 2nd branch of a (merge) commit by moving onto the selected branch (one step backwards on the commit-tree)
~ COMMIT Selector git checkout HEAD~2 Moves 2 commits backwards on the default/selected branch
Defining both ~ and ^ relative refs as PARENT selectors is far the dominant definition published everywhere on the internet I have come across so far - including the official Git Book. Yes they are PARENT selectors, but the problem with this "explanation" is that it is completely against our goal: which is how to distinguish the two... :)
The other problem is when we are encouraged to use the ^ BRANCH selector for COMMIT selection (aka HEAD^ === HEAD~). Again, yes, you can use it this way, but this is not its designed purpose. The ^ BRANCH selector's backwards move behaviour is a side effect not its purpose.
At merged commits only, can a number be assigned to the ^ BRANCH selector. Thus its full capacity can only be utilised where there is a need for selecting among branches. And the most straightforward way to express a selection in a fork is by stepping onto the selected path / branch - that's for the one step backwards on the commit-tree. It is a side effect only, not its main purpose.
~
means parent.
^
if it has two or more parents, like a merge commit. We can select the second of the parent or another.
So if just one thing like (HEAD~ or HEAD^), it has the same results.
OP: When I specify an ancestor commit object in Git, I'm confused between HEAD^ and HEAD~.
What's the difference between HEAD^ and HEAD~ in Git?
The difference between HEAD^
(Caret) and HEAD~
(Tilde) is how they traverse history backwards from a specified starting point, in this particular case HEAD
.
Tilde ~
<rev>~[<n>]
= select <n>th
generation ancestor, following only first* parents
Caret ^
<rev>^[<n>]
= select <n>th
parent of first generation ancestors
*First parent is always the left hand side of the merge, e.g. the commit on the branch that got merged into.
Joining ~ and ^ together
As seen in the illustration below the two selectors ~ and ^ can be used in combination. Also note that instead of using HEAD
as a starting point, any regular reference can be used such as a branch, tag or even a commit hash.
Further more, depending on what ancestor is intended to be selected ^ and ~ can be used interchangeably as seen below in the table.
https://i.stack.imgur.com/Ye1H7.jpg
Source: A thorough rundown can be found in this blog post on the subject.
Simply put, for the first level of parentage (ancestry, inheritance, lineage, etc.) HEAD^ and HEAD~ both point to the same commit, which is (located) one parent above the HEAD (commit).
Furthermore, HEAD^ = HEAD^1 = HEAD~ = HEAD~1. But HEAD^^ != HEAD^2 != HEAD~2. Yet HEAD^^ = HEAD~2. Read on.
Beyond the first level of parentage, things get trickier, especially if the working branch/master branch has had merges (from other branches). There is also the matter of syntax with the caret, HEAD^^ = HEAD~2 (they're equivalent) BUT HEAD^^ != HEAD^2 (they're two different things entirely).
Each/the caret refers to the HEAD's first parent, which is why carets stringed together are equivalent to tilde expressions, because they refer to the first parent's (first parent's) first parents, etc., etc. based strictly on the number on connected carets or on the number following the tilde (either way, they both mean the same thing), i.e. stay with the first parent and go up x generations.
HEAD~2 (or HEAD^^) refers to the commit that is two levels of ancestry up/above the current commit (the HEAD) in the hierarchy, meaning the HEAD's grandparent commit.
HEAD^2, on the other hand, refers NOT to the first parent's second parent's commit, but simply to the second parent's commit. That is because the caret means the parent of the commit, and the number following signifies which/what parent commit is referred to (the first parent, in the case when the caret is not followed by a number [because it is shorthand for the number being 1, meaning the first parent]). Unlike the caret, the number that follows afterwards does not imply another level of hierarchy upwards, but rather it implies how many levels sideways, into the hierarchy, one needs to go find the correct parent (commit). Unlike the number in a tilde expression, it is only one parent up in the hierarchy, regardless of the number (immediately) proceeding the caret. Instead of upward, the caret's trailing number counts sideways for parents across the hierarchy [at a level of parents upwards that is equivalent to the number of consecutive carets].
So HEAD^3 is equal to the third parent of the HEAD commit (NOT the great-grandparent, which is what HEAD^^^ AND HEAD~3 would be...).
If you're wondering whether to type HEAD^
or HEAD~
in your command, just use either:
They're both names for the same commit - the first parent of the current commit.
Likewise with master~
and master^
- both names for the first parent of master.
In the same way as 2 + 2
and 2 x 2
are both 4
- they're different ways of getting there, but the answer is the same.
This answers the question: What's the difference between HEAD^ and HEAD~ in Git?
If you just did a merge (so your current commit has more than one parent), or you're still interested in how the caret and tilde work, see the other answers (which I won't duplicate here) for an in-depth explanation, as well as how to use them repeatedly (e.g.HEAD~~~
), or with numbers (e.g.HEAD^2
). Otherwise, I hope this answer saves you some time.
Success story sharing
^^^^^^^
instead of~7
, is it? That's why~
is useful