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?
In Git, a head is a ref that points to the tip (latest commit) of a branch. You can view your repository's heads in the path . git/refs/heads/ . In this path you will find one file for each branch, and the content in each file will be the commit ID of the tip (most recent commit) of that branch.
When working with Git, only one branch can be checked out at a time - and this is what's called the "HEAD" branch. Often, this is also referred to as the "active" or "current" branch. Git makes note of this current branch in a file located inside the Git repository, in . git/HEAD .
HEAD~1 refers to the commit's first parent. HEAD~2 refers to the first parent of the commit's first parent. ^<n> refers to the the <n>th parent. HEAD^1 refers to the commit's first parent. HEAD^2 refers to the commit's second parent.
The simple answer is that HEAD is a pointer/label to the most recent commit of the branch you are currently on. master is the default branch created when you initialized a git repository (e.g. git init ). You can delete the master branch (e.g. git branch -D master ). You cannot delete the HEAD pointer.
~
most of the time — to go back a number of generations, usually what you want^
on merge commits — because they have two or more (immediate) parentsMnemonics:
~
is almost linear in appearance and wants to go backward in a straight line^
suggests an interesting segment of a tree or a fork in the roadThe “Specifying Revisions” section of the git rev-parse
documentation defines ~
as
<rev>~<n>
, e.g.master~3
A suffix~<n>
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,<rev>~3
is equivalent to<rev>^^^
which is equivalent to<rev>^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.
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
<rev>^
, e.g.HEAD^
,v1.5.1^0
A suffix^
to a revision parameter means the first parent of that commit object.^<n>
means the nth parent ([e.g.]<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.
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.
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 <[email protected]> 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
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With