William Vaughn

The gitrevisions manual review

Review

The gitrevisions manual describes all the ways you can reference objects, and ranges of objects, in git. If you have used git for a while and are comfortable using it, but don’t feel like a master of it; gitrevisions can and should be your next step. I believe this manual teaches the intermediate git user the language they need for describing the structure of their git repo. The git log --graph command shows a repository as a graph of objects, blobs(files) and trees(dirs), as it changes over time. It makes sense to me to think of revisions as coordinates in that graph. I enjoyed reading this manual, I hope you will learn something from my review of it.

Revisions

By commit with <SHA-1>

Each commit object has a single unique name within the repository, its SHA-1 hash e.g. 0bde21a, 0bde21a835dbb052b00895b69cf6e345a2406ed0. This hash is the ultimate identity of every revision, and can be used from anywhere. The rest of of the gitrevisions manual is about ways to target commit objects by names other than their 40-character hash, which would be difficult to work with.

Named commits with <refname>

It’s difficult to go far in git without using refnames. These are things like HEAD, master, or origin/master. Behind the scenes git treats these as aliases for refnames like ref/heads/master, ref/remotes/origin/master. Curious about these reference names, checkout the contents of your .git/refs directory.

There are these other special HEAD refnames which stay consistent in their meaning, even as you change or alter the state of your repository.

VARIANT DESCRIPTION
@ An alias for HEAD, when used on its own.
HEAD commit on which you based changes in the working tree
FETCH_HEAD head of last fetched branch using git fetch
ORIG_HEAD created internally by git when git is doing an operation that moves HEAD in a drastic way
MERGE_HEAD commit which you into your branch with git merge
CHERRY_PICK_HEAD commit you are cherry picking when you run git cherry-pick

Parent selection with <rev>^[<n>]

HEAD^ is the first parent of HEAD. Some commits, such as merge commits, have multiple parents. Using ^1 will select the first, and ^2 will select the second. What helps me is to think of ^1 as a name for “Mom” and ^2 as a name for “Dad”, and the way you express maternal-Grandma would ^1^1, parent 1 (Mom) of parent 1 (Mom). Similarly maternal Grandpa would be ^1^2. Maybe that’s a strange example but I hope it helps with understanding how the caret works in these revisions.

Generational ancestors with <rev>~[<n>]

HEAD~3 means go back three ancestors, and is equivalent to HEAD^^^ or HEAD^1^1^1. I find this is often a more intuitive way of targeting commits in the past than the ^ approach.

ranges

Commands like git log operate on a set of commits. When you enter a single commit to git log you’re telling it to show you the commit itself as well as any commits in its ancestry chain. Defining ranges of commits can be used to narrow and focus the information in these commands.

Exclusion

The first, most basic way to narrow a range is to specify a prefix caret, as in ^master. This means don’t show me anything which is an ancestor of master.

git log ^master HEAD

Shows the commits of your checked out branch since you diverged from master.

Dotted Ranges

The .. (two-dot) range is an alternative way of excluding commit ancestors. Running git log master..HEAD is the same as the example above using ^master HEAD.

The ... (three-dot) range defines a set of commits that are reachable from either, but not both of the commits.

git log master...HEAD

Shows what has happened on master since we diverged from it AND what happened on HEAD since we diverged from master. This is useful for showing two separate branches of changes I think.

The origin is implicitly inserted if you leave off one of the commits on either side of the range.

Simple Implied Meaning
..HEAD origin..HEAD What did I do since I diverged from the origin branch?
HEAD.. HEAD..origin What does the origin branch have that I don’t have since I diverged from it

History by date with <refname>@{<date>}

This tries to get the exact commit for that reference as it was at the specified time. For example:

What was in my local master branch 1 week ago?

git show 'master@{1 week ago}'=

Shortcut to the upstream branch with <branchname>@{upstream}

Using @{upstream} will reference the commit of the branch configured by branch.<name>.remote and branch.<name>.merge

Shortcut to the push branch with <branchname>@{push}

Using @{push} will reference the commit of the branch configured by push.default. The manual describes this as a reference to “where we would push to if git push were run while branchname was checked out.

Refer to a file or subtree of a revision with <rev>:<path>

Targets files or a directory at the given path. For instance:

git show origin/master:README.md

OR

git diff origin/master:blog.org

To show the difference between the blog.org file in the working tree, and the one on origin/master. That first argument origin/master is a revision. Git diff can take a path revision to show a diff between blog.org in my working tree, and the repositories README.md on origin. Why do this? I have no idea, but it works!

git diff origin/master:./README.md blog.org=

Now, throwing it all together! What’s the diff between origin master blog.org, and origin master blog.org from 1 week ago?

git diff origin/master:blog.org 'origin/master@{1 week ago}:blog.org'

Conclusion

After reading and reviewing this manual, I am planning to commands which leverage the power of revisions like git-log, git-rebase, and git-push. I think I’ll be coming back to this document pretty often as I dig into these other tools. As always, thank you so much for reading this manual review!