Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

Welcome to Software Development on Codidact!

Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.

How can I git checkout the previous HEAD?

+5
−0

After switching to a different branch, git checkout - can move me back to the branch I came from. This is handy for times when I wonder "wait, what was that last branch again?"

But this does not work for everything situation when you switch from commit to another. It works for branches, but when you do git pull and receive some new commits, git checkout won't switch you back to the previous one.

Is there a command for "switch back to the previous commit I was on" that works in every situation that changes HEAD?

History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.
Why should this post be closed?

2 comment threads

I would normally use 'git reflog' to see the branch name, then check out the branch by name. (1 comment)
Related comment (1 comment)

1 answer

+6
−1

There are many ways to get the previous states of HEAD, and which one to use will depend on each situation.


Git keeps reference logs (also called "reflogs"), that "record when the tips of branches and other references were updated in the local repository".

To provide a simple example, let's start an empty repository and add some commits to it (for the sake of brevity, some outputs were ommited):

$ mkdir project
$ cd project/
$ git init

# create first and second commits
$ echo first >> file.txt && git add . && git commit -m"first"
$ echo second >> file.txt && git commit -am"second"

# see commit history
$ git log --oneline
899ed6c (HEAD -> master) second
d142056 first

Now we can run git reflog to see the reference logs:

$ git reflog
899ed6c (HEAD -> master) HEAD@{0}: commit: second
d142056 HEAD@{1}: commit (initial): first

Basically, this is the history of how HEAD has changed (most recent first). We can also note the notation HEAD@{N}, that specifies the Nth prior value of HEAD.

Therefore, we can use this in any command that expects a commit/reference, such as git log HEAD@{1}, git diff HEAD@{1}, git checkout HEAD@{1} and so on. HEAD@{N} will use the reflogs to search for the commit that corresponds to the Nth prior value of HEAD, and all will work as if you provided the commit hash/branch name directly.

Therefore, HEAD@{0} is the same as HEAD.

The reflogs also record when you switch to another branch:

# create a branch and switch to it
$ git checkout -b somebranch # or "git switch -c somebranch" for Git version >= 2.23.0
$ git reflog
899ed6c (HEAD -> somebranch, master) HEAD@{0}: checkout: moving from master to somebranch
899ed6c (HEAD -> somebranch, master) HEAD@{1}: commit: second
d142056 HEAD@{2}: commit (initial): first

# add a commit to the branch
$ echo branch >> file.txt && git commit -am"branch"
$ git reflog
d0e4769 (HEAD -> somebranch) HEAD@{0}: commit: branch
899ed6c (master) HEAD@{1}: checkout: moving from master to somebranch
899ed6c (master) HEAD@{2}: commit: second
d142056 HEAD@{3}: commit (initial): first

$ git log --oneline 
d0e4769 (HEAD -> somebranch) branch
899ed6c second
d142056 first

Note how it records the branch switching from master to somebranch. It's also noteworthy that all the HEAD@{N} references had also changed (ex: HEAD@{0} was pointing to commit 899ed6c, now it's d0e4769). The reflogs numbers are updated everytime there's a new record in it, with {0} being the most recent one.

Just to add one more example, let's say someone else updated the remote repository and I want to get those updates:

$ git checkout master  # or "git switch master" for Git version >= 2.23.0
$ git pull origin master # get updates from the remote
$ git reflog
0ff62c8 (HEAD -> master, origin/master) HEAD@{0}: pull origin master: Fast-forward
899ed6c HEAD@{1}: checkout: moving from somebranch to master
d0e4769 (origin/somebranch, somebranch) HEAD@{2}: commit: branch
899ed6c HEAD@{3}: checkout: moving from master to somebranch
899ed6c HEAD@{4}: commit: second
d142056 HEAD@{5}: commit (initial): first

PS: I skipped the parts where I configured the remote and added commit 0ff62c8 to it.

git pull also updates HEAD, hence this change is recorded in the reflogs as well. As you can see, the reflog is a general way to know how HEAD has changed in a particular repository.


But git pull also created two additional references:

$ git log FETCH_HEAD --oneline -1
0ff62c8 (HEAD -> master, origin/master) another

$ git log ORIG_HEAD --oneline -1
899ed6c second

Those are described in the documentation:

  • FETCH_HEAD: records the branch which you fetched from a remote repository with your last git fetch invocation. Note: git pull calls git fetch, that's why this was created.
  • ORIG_HEAD: is created by commands that move your HEAD in a drastic way (git am, git merge, git rebase, git reset), to record the position of HEAD before their operation, so that you can easily change the tip of the branch back to the state before you ran them. Note: after fetching, git pull also does a merge or a rebase (depending on the configuration or command line options), that's why this was created.

So FETCH_HEAD refers to the remote branch that was fetched from the remote repository (in this case, origin/master, which corresponds to commit 0ff62c8), and ORIG_HEAD points to where HEAD was before merging it to 0ff62c8. We can see those in the commit history:

$ git log --oneline
0ff62c8 (HEAD -> master, origin/master) another
899ed6c second
d142056 first

In the same documentation you'll also find some other special references, such as MERGE_HEAD, that records the commit(s) which you are merging into your branch. Usually it exists only during the merge, and it's deleted after it finishes (so you can check it when there's a conflict, as it's deleted only after you solve or abort it).

Those might be helpful if you want more details about the status of HEAD during a specific operation. But as general solution, you can always check the reflogs.


The documentation also describes some other ways to get previous states of HEAD in a particular point in time, such as HEAD@{yesterday}, HEAD@{5.minutes.ago} or HEAD@{2024-08-21T08:30:00}. You can also use spaces, but in this case the reference must be quoted: git log "HEAD@{5 minutes ago}" works, git log HEAD@{5 minutes ago} doesn't.

And remind that this notation works not only with HEAD, but with any other branch. So you can use things like master@{yesterday} (where the master branch was pointing to yesterday) or somebranch@{2} (the second previous value of somebranch).

And if you omit the reference, it'll refer to the current branch. So if your current branch is master, then @{yesterday} will be equivalent to master@{yesterday}. But note that it might not be the same as HEAD@{yesterday}, because HEAD could be pointing to a different branch at that time.

As a curiosity, @ is a shortcut for HEAD.

Beware: if you use negative numbers, it's a totally different thing. @{-N} means "the Nth branch/commit checked out before the current one". And you can't put a reference before it, so HEAD@{-1} doesn't work. Negative numbers always refer to commits that were checked out before. As a curiosity, the git checkout - command you mentioned is a shortcut for git checkout @{-1} (since version 1.6.2).


Finally, it's important to remember that the reflog is stored locally, and it's not shared with the remote repositories when you fetch, pull or push.

Even if you clone the same remote repository in another folder of the same machine, the new clone will have its own separate reflog (which means that HEAD@{N} might not be the same in every clone).

And if you're using git-worktree, each worktree will also have its own separate reflog.

History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.

0 comment threads

Sign up to answer this question »