0

Given a ref to a commit such as name, do name~n and name@{n} always refer to the same commit? Can I use them interchangeably?

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
Tim
  • 1
  • 141
  • 372
  • 590

2 Answers2

4

No, they can be different.

  • name~n gives you the nth generation ancestor of that commit by following the parent pointers.
  • name@{n} gives you the nth entry in the reflog for that name.

For example, if I did 3 commits (A, B, and C) on master and then reset master to B:

A <-- B <-- C
^     ^     ^
|     |     | master@{1}
|     |
|     | master
|     | master@{0}
|     | master~0
|
| master~1
takteek
  • 7,020
  • 2
  • 39
  • 70
3

To quote the documentation:

<refname>@{<n>}, e.g. master@{1} A ref followed by the suffix @ with an ordinal specification enclosed in a brace pair (e.g. {1}, {15}) specifies the n-th prior value of that ref. For example master@{1} is the immediate prior value of master while master@{5} is the 5th prior value of master. This suffix may only be used immediately following a ref name and the ref must have an existing log ($GIT_DIR/logs/<refname>).

<rev>~<n>, e.g. master~3

A suffix ~<n> to a revision parameter means the commit object that is the <n>th generation ancestor of the named commit object, following only the first parents. I.e. <rev>~3 is equivalent to <rev>^^^ which is equivalent to <rev>^1^1^1. See below for an illustration of the usage of this form.

While these two descriptions sound similar, they actually mean different things. ~n moves up n parents from the specified revision within the history. If you look at your Git log, then this will just move up to the first parent n times.

@{n} however talks about the log of a ref. So this is something that moves on top of the reflog of a ref. To see the reflog for a ref, just run git reflog <ref>, e.g. git reflog master. The @{n} will reference the nth item from the top (zero indexed).

So while ~n is about the Git history, something that is globally valid for all cloned repositories and which will always yield the same result for a base revision, @{n} will depend on the ref history of the local repository. If you are committing or fetching, then you are effectively modifying the reflog. So this is not something that is universally useful.

poke
  • 369,085
  • 72
  • 557
  • 602