During development, many times you want to select a single revision in the history of a project, to examine it, or to compare with the current version. The ability to a select revision is also the basis for selecting a revision range, for example a subsection of history to examine.
Many Git commands take revision parameters as arguments, which is typically denoted by <rev>
in Git reference documentation. Git allows you to specify specific commits or a range of commits in several ways.
Most, but not all, Git commands that require the revision parameter, default to using HEAD
. For example, git log
and git log HEAD
will show the same information.
The HEAD
denotes the current branch, or in other words the commit that was checked out into the working directory, and forms a base of a current work.
There are a few other references which are similar to HEAD
:
FETCH_HEAD
: This records the information about the remote branches that were fetched from a remote repository with your last git fetch
or git pull
invocation. It is very useful for one-off fetch, with the repository to fetch from given by a URL, unlike when fetching from a named repository such as origin
, where we can use the remote-tracking branch instead, for example, origin/master
. Moreover, with named repositories, we can use the reflog for remote-tracking branch, for example, origin/master@{1}
, to get the position before the fetch. Note that FETCH_HEAD
is overwritten by each fetch from any repository.ORIG_HEAD
: This records the previous position of the current branch; this reference is created by commands that move the current branch in a drastic way (creating a new commit doesn't set ORIG_HEAD
) to record the position of the HEAD
command before the operation. It is very useful if you want to undo or abort such operations; though nowadays the same can be done using reflogs, which store additional information that can be examined in their use.You can also stumble upon the short-lived temporary references used during specific operations:
MERGE_HEAD
records the commit(s) that you are merging into your branch. It vanishes after creating a merge commit.CHERRY_PICK_HEAD
records the commit that you have selected for cherry-picking.The most straightforward and commonly used way to specify a revision is to use symbolic names: branches, naming the line of development, pointing to the tip of said line, and tags, naming specific revision. This way of specifying revisions can be used to view the history of a line of development, examine the most current revision (current work) on a given branch, or compare the branch or tag with the current work.
You can use any refs (external references to the DAG of revisions) to select a commit. You can use a branch name, tag name, and remote-tracking branch in any Git command that requires revision as a parameter.
Usually, it is enough to give a short name to a branch or tag, for example, git log master
, to view the history of a master
branch, or git log v1.3-rc3
to see how version v1.3-rc1
came about. It can, however, happen that there are different types of refs with the same name, for example, both branch and tag named dev
(though it is recommended to avoid such situations). Or, you could have created (usually by accident) the local branch, origin/master
, when there was a remote-tracking branch with the short name, origin/master
, tracking where the master
branch was in the remote repository, origin
.
In such a situation, when ref name is ambiguous, it is disambiguated by taking the first match in the following rules (this is a shortened and simplified version; for the full list, see the gitrevisions(7) manpage):
HEAD
.refs/tags/
namespace).refs/heads/
namespace).refs/remotes/
namespace).refs/remotes/origin/HEAD
for origin
as a parameter).In Git, each revision is given a unique identifier (object name), which is a SHA-1 hash function, based on the contents of the revision. You can select a commit by using its SHA-1 identifier as a 40-character long hexadecimal number (160 bits). Git shows full SHA-1 identifiers in many places, for example, you can find them in the full git log
output:
$ git log commit 50f84e34a1b0bb893327043cb0c491e02ced9ff5 Author: Junio C Hamano <[email protected]> Date: Mon Jun 9 11:39:43 2014 -0700 Update draft release notes to 2.1 Signed-off-by: Junio C Hamano <[email protected]> commit 07768e03b5a5efc9d768d6afc6246d2ec345cace Merge: 251cb96 eb07774 Author: Junio C Hamano <[email protected]> Date: Mon Jun 9 11:30:12 2014 -0700 Merge branch 'jc/shortlog-ref-exclude'
It is not necessary to give a full 40 characters of the SHA-1 identifier. Git is smart enough to figure out what you meant if you provide it with the first few characters of the SHA-1 revision identifier, as long as the partial SHA-1 is at least four characters long. To be able to use a shortened SHA-1 to select revision, it must be long enough to be unambiguous, that is, there is one and only one commit object which SHA-1 identifier begins with given characters.
For example, both dae86e1950b1277e545cee180551750029cfe735
and dae86e
name the same commit object, assuming, of course, that there is no other object in your repository whose object name starts with dae86e
.
In many places, Git shows unambiguous shortened SHA-1 identifiers in its command output. For example, in the preceding example of the git log
output, we can see the shortened SHA-1 identifiers in the Merge:
line.
You can also request that Git use the shortened SHA-1 in place of the full SHA-1 revision identifiers with the --abbrev-commit
option. By default, Git will use at least seven characters for the shortened SHA-1; you can change it with the optional parameter, for example, --abbrev-commit=12
.
Note that Git would use as many characters as required for the shortened SHA-1 to be unique at the time the command was issued. The parameter --abbrev-commit
(and the similar --abbrev
option) is the minimal length.
HA short note about the shortened SHA-1:
Generally, 8 to 10 characters are more than enough to be unique within a project. One of the largest Git projects, the Linux kernel, is beginning to need 12 characters out of the possible 40 to stay unique. While a hash collision, which means having two revisions (two objects) that have the same full SHA-1 identifier, is extremely unlikely (with 1/2^80 ≈ 1/1.2×10^24 probability), it is possible that formerly unique shortened SHA-1 identifier will stop to be unique due to the repository growth.
The SHA-1 and the shortened SHA-1 are most often copied from the command output and pasted as a revision parameter in another command. They can also be used to communicate between developers in case of doubt or ambiguity, as SHA-1 identifiers are the same in any clone of the repository. Fig 2 uses a five-character shortened SHA-1 to identify revisions in the DAG.
The other main way to specify a revision is via its ancestry. One can specify a commit by starting from some child of it (for example from the current commit i.e. HEAD, a branch head, or a tag), and then follow through parent relationships to the commit in question. There is a special suffix syntax to specify such ancestry paths.
If you place ^
at the end of a revision name, Git resolves it to mean a (first) parent of that revision. For example, HEAD^
means the parent of the HEAD
, that is, the previous commit.
This is actually a shortcut syntax. For merge commits, which have more than one parent, you might want to follow any of the parents. To select a parent, put its number after the ^
character; using the ^<n>
suffix means the nth parent of a revision. We can see that ^
is actually a short version of ^1
.
As a special case, ^0
means the commit itself; it is important only when a command behaves differently when using the branch name as a parameter and when using other revision specifier. It can be also used to get the commit an annotated (or a signed) tag points to; compare git show v0.9
and git show v0.9^0
.
This suffix syntax is composable. You can use HEAD^^
to mean grandparent of HEAD
, and parent of HEAD^
.
There is another shortcut syntax for specifying a chain of first parents. Instead of writing n times the ^
suffix, that is, ^^…^
or ^1^1…^1
, you can simply use ~<n>
. As a special case, ~
is equivalent to ~1
, so, for example, HEAD~
and HEAD^
are equivalent. And, HEAD~2
means the first parent of the first parent, or the grandparent, and is equivalent to HEAD^^
.
You can also combine it all together, for example, you can get the second parent of the great grandparent of HEAD
(assuming it was a merge commit) by using HEAD~3^2
and so on. You can use git name-rev
or git describe --contains
to find out how a revision is related to local refs, for example, via:
$ git log | git name-rev --stdin
The ancestry reference describes how a historic version relates to the current branches and tags. It depends on the position of the starting revision. For example, HEAD^
would usually mean completely different commit next month.
Sometimes, we want to describe how the current version relates to prior named version. For example, we might want to have a human-readable name of the current version to store in the generated binary application. And, we want this name to refer to the same revision for everybody. This is the task of git describe
.
The git describe
finds the most recent tag that is reachable from a given revision (by default, HEAD
) and uses it to describe that version. If the found tag points to the given commit, then (by default) only the tag is shown. Otherwise, git describe
suffixes the tag name with the number of additional commits on top of the tagged object, and the abbreviated SHA-1 identifier of the given revision. For example, v1.0.4-14-g2414721
means that the current commit was based on named (tagged) version v1.0.4
, which was 14 commits ago, and that it has 2414721
as a shortened SHA-1.
Git understands this output format as a revision specifier.
To help you recover from some of types of mistakes, and to be able to undo changes (to go back to the state before the change), Git keeps a reflog—a temporary log of where your HEAD
and branch references have been for the last few months, and how they got there. The default is to keep reflog entries up to 90 days, 30 days for revisions which are reachable only through reflog (for example, amended commits). This can be, of course, configured, even on a ref-by-ref basis.
You can examine and manipulate your reflog with the git reflog
command and its subcommands. You can also display reflog
like a history with git log -g
(or git log --walk-reflog
):
$ git reflog ba5807e HEAD@{0}: pull: Merge made by the 'recursive' strategy. 3b16f17 HEAD@{1}: reset: moving to HEAD@{2} 2b953b4 HEAD@{2}: reset: moving to HEAD^ 69e0d3d HEAD@{3}: reset: moving to HEAD^^ 3b16f17 HEAD@{4}: commit: random.c was too long to type
Every time your HEAD
and your branch head are updated for any reason, Git stores that information for you in this local temporary log of ref history. The data from reflog
can be used to specify references (and therefore, to specify revisions):
HEAD
in your local repository, you can use the HEAD@{n}
notation that you see in the git reflog
output. It's same with the nth prior value of the given branch, for example, master@{n}
. The special syntax, @{n}
, means the nth prior value of the current branch, which can be different from HEAD@{n}
.master
branch was yesterday in your local repository, you can use master@{yesterday}
.@{-n}
syntax to refer to the nth branch checked out (used) before the current one. In some places, you can use -
in place of @{-1}
, for example, git checkout -
will switch to the previous branch.The local repository which you use to work on a project does not usually live in the isolation. It interacts with other repositories, usually at least with the origin
repository it was cloned from. For these remote repositories with which you interact often, Git will track where their branches were at the time of last contact.
To follow the movement of branches in the remote repository, Git uses remote-tracking branches. You cannot create new commits on remote-tracking branches as they would be overwritten on the next contact with remote. If you want to create your own work based on some branch in remote repository, you need to create a local branch based on the respective remote-tracking branch.
For example, when working on a line of development that is to be ultimately published to the next
branch in the origin
repository, which is tracked by the remote-tracking branch, origin/next
, one would create a local next
branch. We say that origin/next
is upstream of the next
branch and we can refer to it as next@{upstream}
.
The suffix, @{upstream}
(short form <refname>@{u}
), which can be applied only to a local branch name, selects the branch that the ref is set to build on top of. A missing ref defaults to the current branch, that is, @{u}
is upstream for the current branch.
You can find more about remote repositories, the concept of the upstream, and remote tracking branches in Chapter 5, Collaborative Development with Git and Chapter 6, Advanced Branching Techniques.
You can specify the revision a by matching its commit message with a regular expression. The :/<pattern>
notation (for example, :/^Bugfix
) specifies the youngest matching commit, which is reachable from any ref, while <rev>^{/<pattern>}
(for example, next^{/fix bug}
) specifies the youngest matching commit which is reachable from <rev>
:
$ git log 'origin/pu^{/^Merge branch .rs/ref-transactions}'
This revision specifier gives similar results to the --grep=<pattern>
option to git log
, but is composable. On the other hand, it returns the first (youngest) matching revision, while the --grep
option returns all matching revisions.