Commit
Commits are snapshots for each phase of the repository, which allowing you to rollback to any instance of that commit. Git is able to compare base on two commits(usually the current and last commit), which is the diff.
- Each commit stores a snapshot of current repository with a unique hash to be identified.
- A commit can have one or more parent from which it was derived.
- Each snapshot file or folder has a unique hash, and can inspect their content in some way.
- the folder snapshot is called
tree
- the file snapshot is
blob
- a commit always shows the snapshot of root of the repository as entry of a commit
- the folder snapshot is called
NOTE
Revision is another synonym of Commit in git.
IMPORTANT
Commit is the atomic identifier of how git manage the versions, it's the true and real concept while branch and merge are just fake concepts upon it.
Commit Identifier
Each commit is identified by the uniquely generated SHA hash, you can reference the commit using the full hash or the leading 7 digit hash for short. Each snapshot instance of that particular commit could be found on .git/objects/<first-2digits-of-hash>/<restof-hash>
NOTE
Those identifier of commits are called refs in git, you could find their representations in .git/refs
- commit hash: the most explicit identification of a commit.
- branch name: points to latest commit of the branch.
- tag name: points to the commit of that tag.
HEAD
or@
: points to the current commit you're at.- special identifiers like
HEAD
is independent from any branch, its a primitive and dynamic pointer for your current worktree. - for some other special identifier names, see documentation.
- special identifiers like
-
or@{-1}
: previous commit you're at.-
is not a valid identifier in some scenarios, but you can use it in likegit checkout -
orgit merge -
.
- commit expression: see next section.
Commit Expression
Commit expression is special syntax to reference commits backward from a given point. Three special symbols for traversing back in git:
@
: reference backward through reflog, you can see such symbol likeHEAD@{1}
ingit reflog
output.@{-<n>}
: go to nth previously checked out commit bygit checkout
<commit_identifier>@{<n>}
: go to previous nth commit@{<n>}
is equivalent to<current_branch>@{<n>}
, this is only valid when you're on a branch(not detached).
~
: trace back to previous commits through the first parent only.<identifier>~3
: points to previous 3 commit from current branch.
^
: trace back to previous commits through a specified parent by its index.<identifier>^2
: points to previous commit through second parent of<identifier>
.<identifier>^101
: if you're wild enough to merge from 100 branches, you might need to point to previous commit through the 101-th parent.
TIP
~1
and ^1
are always equivalent since they would pick the same first parent of the commit.
NOTE
See git help revisions
for more details
NOTE
When saying HEAD
we're referring to the head of worktree which might walk across different branches as you checkout. When a branch name is explicitly specified or elided(representing the current branch), the reflog can differ from HEAD
's since it's branch specific
Identifier Expansion
You can expand any commit identifier to full commit hash using git rev-parse <identifier>
$ git rev-parse @~1
42804017200559f28d12757e729f95dbd18f4998
$ git rev-parse main
e7bbe687097f56d152c2291190b67d91eed4cd57
2
3
4
Commit Inspection
Commit inspection generally requires git cat-file -p <hash>
, see git help cat-file
.
A commit can contain elements:
tree
: the structure of current snapshot, including files and folders.parent
: the parent commit it was derived from, a commit can have multiple parents.author
: who did the changes.committer
: who made the commit, may differ fromauthor
when rebasing.- and more...
$ git cat-file -p 5eb5a747cfd9b17603b79d7ab64fc4ecee1751e3
tree e060913d1a04d39082981dc116c5a5cd35ba8e52
parent 02348ec70c2c901e9ecc4be5860330e6dcf911ff
author sharpchen <rui.chen.sharp@gmail.com> 1749697268 +0800
committer sharpchen <rui.chen.sharp@gmail.com> 1749697268 +0800
2
3
4
5
6
TIP
It's more general to use git show <commit_identifier>
to inspect a commit.
Tree & Blob Inspection
Tree in the inspected commit is a recursive structure and also has a hash that can be inspected by git cat-file -p <hash>
. A blob
represents a real file, a tree
represents a folder as a container. So the tree printed from the commit is actually the root folder of your repository.
$ git cat-file -p e060913d1a04d39082981dc116c5a5cd35ba8e52
100644 blob 397b4a7624e35fa60563a9c03b1213d93f7b6546 .gitignore
100644 blob 67f24d19be001eebf7bcdde488d8ab594bcd969d PSScriptAnalyzerSettings.psd1
100644 blob 4bd5bb6fcff0b514d0160f3cb8ff01b5de46eeb4 README.md
100644 blob 207846dc4b8f93edd892df9b6058ded48f5355b8 dotfiles.ps1
100755 blob fa9fe71978d17992a3f27eaf78f2619a25a1966d dotfiles.sh
040000 tree 0199c6476a45a6dadadaf7baf70a136d7f8f217f dotfiles
100644 blob b6e1531298fed5dc7a019f6c01018b5c1add3c99 flake.lock
100644 blob 92d9549668557adb021f959f8b898ad96ee84efb flake.nix
100644 blob bb166d071cb8541aac7df35b7c1f5dbf420dd70c home.nix
100644 blob baa979aff962214ddb5e23502acdb211d8d32064 install.ps1
040000 tree c1aef838a425cd6f209ecc01f28bef2208fd2d26 packages
2
3
4
5
6
7
8
9
10
11
12
13
You can either go deeper to the nested tree
or inspect the snapshot content of a blob
using the same way.
$ git cat-file -p 67f24d19be001eebf7bcdde488d8ab594bcd969d
@{
IncludeRules = @('PSAvoidTrailingWhitespace')
}
2
3
4
5