Software Engineering for Self-Directed Learners »

Tools → Git and GitHub →

Traversing to a Specific Commit


Another useful feature of revision control is to be able to view the working directory as it was at a specific point in history, by checking out a commit created at that point.

This lesson covers that part.

Suppose you added a new feature to a software product, and while testing it, you noticed that another feature added two commits ago doesn’t handle a certain edge case correctly. Now you’re wondering: did the new feature break the old one, or was it already broken? Can you go back to the moment you committed the old feature and test it in isolation, and come back to the present after you found the answer? With Git, you can.

To view the working directory at a specific point in history, you can check out a commit created at that point.

When you check out a commit, Git:

  1. Updates your working directory to match the snapshot in that commit, overwriting current files as needed.
  2. Moves the HEAD ref to that commit, marking it as the current state you’re viewing.
C3 masterHEAD
|
C2
|
C1


[check out commit C2...]

C3 master
|
C2HEAD detached head!
|
C1

Checking out a specific commit puts you in a "detached HEAD" state: i.e., the HEAD no longer points to a branch, but directly to a commit (see the above diagram for an example). This isn't a problem by itself, but any commits you make in this state can be lost, unless certain follow-up actions are taken. It is perfectly fine to be in a detached state if you are only examining the state of the working directory at that commit.

To get out of a "detached HEAD" state, you can simply check out a branch, which "re-attaches" HEAD to the branch you checked out.

HANDS-ON: Checking out some commits

Checkout a few commits in a local repo (e.g., the things repo), which examining the working directory to verify that it matches the state when you created the corresponding commit:

1 Examine the revision tree, to get your bearing first.

git log --oneline --decorate
e60deae (HEAD -> master, origin/master) Update fruits list
f761ea6 (tag: v1.0) Add colours.txt, shapes.txt
2bedace (tag: v0.9) Add figs to fruits.txt
d5f91de Add fruits.txt

2 Use the checkout <commit-identifier> command to check out a commit other than the one currently pointed by HEAD. You can use any of the following methods:

  • git checkout v1.0: checks out the commit tagged v1.0
  • git checkout 0023cdd: checks out the commit with the hash 0023cdd
  • git checkout HEAD~2: checks out the commit 2 commits behind the most recent commit.
git checkout HEAD~2
Note: switching to 'HEAD~2'.

You are in 'detached HEAD' state.
# rest of the warning about the detached head ...

HEAD is now at 2bedace Add figs to fruits.txt

3 Verify HEAD and the working directory have updated as expected.

  • HEAD should now be pointing at the target commit
  • The working directory should match the state it was in at that commit (e.g., files added after that commit -- such as shapes.txt should not be in the folder).
git log --one-line --decorate
2bedace (HEAD, tag: v0.9) Add figs to fruits.txt
d5f91de Add fruits.txt

HEAD is indeed pointing at the target commit.

But note how the output does not show commits you added after the checked-out commit.

The --all switch tells git log to show commits from all refs, not just those reachable from the current HEAD. This includes commits from other branches, tags, and remotes.

git log --one-line --decorate --all
e60deae (origin/master, master) Update fruits list
f761ea6 (tag: v1.0) Add colours.txt, shapes.txt
2bedace (HEAD, tag: v0.9) Add figs to fruits.txt
d5f91de Add fruits.txt

4 Go back to the latest commit by checking out the master branch again.

git checkout master

In the revision graph, double-click the commit you want to check out, or right-click on that commit and choose Checkout....

Click OK to the warning about ‘detached HEAD’ (similar to below).

The specified commit is now loaded onto the working folder, as indicated by the HEAD label.

To go back to the latest commit on the master branch, double-click the master branch.


If you check out a commit that comes before the commit in which you added a certain file (e.g., temp.txt) to the .gitignore file, and if the .gitignore file is version controlled as well, Git will now show it under ‘unstaged modifications’ because at Git hasn’t been told to ignore that file yet.

If there are uncommitted changes in the working directory, Git proceeds with a checkout only if it can preserve those changes.

  • Example 1: There is a new file in the working directory that is not committed yet.
    → Git will proceed with the checkout and will keep the uncommitted file as well.
  • Example 2: There is an uncommitted change to a file that conflicts with the version of that file in the commit you wish to check out.
    → Git will abort the checkout, and the repo will remain in the current commit.

DETOUR: Dealing with Uncommitted Confilicting Changes at a Checkout

To proceed with a checkout when there are conflicting uncommitted changes in the working directory, there are several options:

  • Commit the changes.
  • Stash the changes.
  • Discard the changes.