ChatGPT解决这个技术问题 Extra ChatGPT

What's the difference between HEAD^ and HEAD~ in Git?

git

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?

Links are especially bad when they are broken. That is the reason why it is safer to answer the question which help prevent this because of the ability to copy paste some explanations :)

M
Mark Lakata

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

~, e.g. master~3 A suffix ~ to a revision parameter means the commit object that is the nth generation ancestor of the named commit object, following only the first parents. For example, ~3 is equivalent to ^^^ which is equivalent to ^1^1^1 …

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

^, e.g. HEAD^, v1.5.1^0 A suffix ^ to a revision parameter means the first parent of that commit object. ^ means the nth parent ([e.g.] ^ is equivalent to ^1). As a special rule, ^0 means the commit itself and is used when is the object name of a tag object that refers to a commit object.

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 Date: Mon Oct 29 10:15:31 2018 +0900 Merge branches 'bp/reset-quiet' and 'js/mingw-http-ssl' into nd/config-split […]

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

it seems ^ can handle all the cases and one can wonder why ~ appeared in the first place. Why not remember only how ^ works ?
this is super confusing still... assuming G is HEAD, so if I do a HEAD^ that would be D... right?
@duckx the graph is actually going top-to-bottom, so A is the most recent commit, and G is one of the oldest. The path from G to D is forward, not backward, from what I can tell.
@SimonBudin I guess, it's not very convenient to use ^^^^^^^ instead of ~7, is it? That's why ~ is useful
If above was a little hard to understand (it was for me), then this article is a little more easy to understand: salferrarello.com/git-head-tilde-vs-head-caret
Y
YakovL

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

Just one question. How is it possible for a commit to have more than two parents? (See B - it's parents are D, E and F) I imagine that the only way a commit can have two parents is when it is a merge commit... but how can you merge 3 commits at the same time?
Additionally, F = A^2^.
So, ^ == ^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
@tsikov If you're on branch A, you can do git merge B C. That's a 3-way merge.
@tsikov @Julian A. Or in the case of this illustration, someone was on branch D, and ran git merge E F. The resulting commit was B.
M
Matthew Strawbridge

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.


Reading this followed by the picture from stackoverflow.com/questions/2221658/… made perfect sense.
This answer made me distinguish between caret/tilde without number and with number! I thought ^^ 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.
This should be the answer because this answer zeros in on the only meaningful difference between ~ and ^ while cutting out all the fluff (though the other answers have useful graphs where you can mentally practice understanding and so also provide value).
M
Mark Amery

My two cents...

https://i.stack.imgur.com/pDAzG.png


And how H=A~2^2 not H=A~2^1?
If I had figured it out correctly, the commits 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.
Thank you. How do I know whether G or H is the first or second immediate ancestor of D if I don't have a pretty graph? git cat-file -p D and look at the list of parents?
C
Community

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


@KirkBroadhurst I hate when people share hosted examples i.e. what's the point to copy-paste, unless that's all you can do to put on the right track . So agree with, although nearly 3 years later , fella !
j
jamessan

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.


s
simhumileco

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 ^).


A
Air

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}.


Thanks for this. Its basically a "goto" to the commit I was {N} times before. Very useful!
The HEAD@{N} notation doesn't seem to work on git 2.26, and I fail to find information as to why
Dont know why can't update previous comment,sorry for that. But above comment is true for tcsh, in case someone runs into same issue! On bash it works fine! On tcsh you need to enclose it in @{} in ""
Thanks this is super useful. There seems to be little resource about HEAD@{n} syntax. Any link to the document?
P
Peter Mortensen

An actual example of the difference between HEAD~ and HEAD^:

https://i.stack.imgur.com/1qqSY.jpg


W
WeakPointer

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


k
knittl

HEAD^^^ is the same as HEAD~3, selecting the third commit before HEAD

HEAD^2 specifies the second head in a merge commit


u
user229044

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

R
Rich

^ 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.


P
Peter Mortensen

~ 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.


A
Alexis Määttä Vinkler

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.


S
Sean Tank Garvey

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...).


M
M_M

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.