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.
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:
- Updates your working directory to match the snapshot in that commit, overwriting current files as needed.
- Moves the
HEAD
ref to that commit, marking it as the current state you’re viewing.
→
[check out commit C2
...]
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.
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 taggedv1.0
git checkout 0023cdd
: checks out the commit with the hash0023cdd
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.