How can I move HEAD back to a previous location? (Detached head) & Undo commits
Solution 1
Before answering, let's add some background, explaining what this HEAD
is.
First of all what is HEAD?
HEAD
is simply a reference to the current commit (latest) on the current branch.
There can only be a single HEAD
at any given time (excluding git worktree
).
The content of HEAD
is stored inside .git/HEAD
and it contains the 40 bytes SHA-1 of the current commit.
detached HEAD
If you are not on the latest commit - meaning that HEAD
is pointing to a prior commit in history it's called detached HEAD
.
On the command line, it will look like this - SHA-1 instead of the branch name since the HEAD
is not pointing to the tip of the current branch:
A few options on how to recover from a detached HEAD:
git checkout
git checkout <commit_id>
git checkout -b <new branch> <commit_id>
git checkout HEAD~X // x is the number of commits to go back
This will checkout the new branch pointing to the desired commit.
This command will checkout to a given commit.
At this point, you can create a branch and start to work from this point on.
# Checkout a given commit.
# Doing so will result in a `detached HEAD` which mean that the `HEAD`
# is not pointing to the latest so you will need to checkout branch
# in order to be able to update the code.
git checkout <commit-id>
# Create a new branch forked to the given commit
git checkout -b <branch name>
git reflog
You can always use the reflog
as well.
git reflog
will display any change which updated the HEAD
and checking out the desired reflog entry will set the HEAD
back to this commit.
Every time the HEAD is modified there will be a new entry in the reflog
git reflog
git checkout HEAD@{...}
This will get you back to your desired commit
git reset --hard <commit_id>
"Move" your HEAD back to the desired commit.
# This will destroy any local modifications.
# Don't do it if you have uncommitted work you want to keep.
git reset --hard 0d1d7fc32
# Alternatively, if there's work to keep:
git stash
git reset --hard 0d1d7fc32
git stash pop
# This saves the modifications, then reapplies that patch after resetting.
# You could get merge conflicts if you've modified things which were
# changed since the commit you reset to.
- Note: (Since Git 2.7) you can also use the
git rebase --no-autostash
as well.
git revert <sha-1>
"Undo" the given commit or commit range.
The reset command will "undo" any changes made in the given commit.
A new commit with the undo patch will be committed while the original commit will remain in history as well.
# Add a new commit with the undo of the original one.
# The <sha-1> can be any commit(s) or commit range
git revert <sha-1>
This schema illustrates which command does what.
As you can see there, reset && checkout
modify the HEAD
.
Solution 2
First reset
locally:
git reset 23b6772
To see if you're on the right position, verify with:
git status
You will see something like:
On branch master Your branch is behind 'origin/master' by 17 commits, and can be fast-forwarded.
Then rewrite history on your remote tracking branch to reflect the change:
git push --force-with-lease // a useful command @oktober mentions in comments
Using --force-with-lease
instead of --force
will raise an error if others have meanwhile committed to the remote branch, in which case you should fetch first. More info in this article.
Solution 3
Quickest possible solution (just 1 step)
Use git checkout -
You will see Switched to branch <branch_name>
. Confirm it's the branch you want.
Brief explanation: this command will move HEAD back to its last position. See note on outcomes at the end of this answer.
Mnemonic: this approach is a lot like using cd -
to return to your previously visited directory. Syntax and the applicable cases are a pretty good match (e.g. it's useful when you actually want HEAD to return to where it was).
More methodical solution (2-steps, but memorable)
The quick approach solves the OP's question. But what if your situation is slightly different: say you have restarted Bash then found yourself with HEAD detached. In that case, here are 2 simple, easily remembered steps.
1. Pick the branch you need
Use git branch -v
You see a list of existing local branches. Grab the branch name that suits your needs.
2. Move HEAD to it
Use git checkout <branch_name>
You will see Switched to branch <branch_name>
. Success!
Outcomes
With either method, you can now continue adding and committing your work as before: your next changes will be tracked on <branch_name>
.
Note that both git checkout -
and git checkout <branch_name>
will give additional instructions if you have committed changes while HEAD was detached.
Solution 4
The question can be read as:
I was in detached-state with HEAD
at 23b6772
and typed git reset origin/master
(because I wanted to squash). Now I've changed my mind, how do I go back to HEAD
being at 23b6772
?
The straightforward answer being: git reset 23b6772
But I hit this question because I got sick of typing (copy & pasting) commit hashes or its abbreviation each time I wanted to reference the previous HEAD
and was Googling to see if there were any kind of shorthand.
It turns out there is!
git reset -
(or in my case git cherry-pick -
)
Which incidentally was the same as cd -
to return to the previous current directory in *nix! So hurrah, I learned two things with one stone.
Solution 5
When you run the command git checkout commit_id
then HEAD detached from 13ca5593d(say commit-id)
and branch will be on longer available.
Move back to previous location run the command step wise -
git pull origin branch_name
(say master)git checkout branch_name
git pull origin branch_name
You will be back to the previous location with an updated commit from the remote repository.
Related videos on Youtube
timpone
Updated on July 08, 2022Comments
-
timpone almost 2 years
In Git, I was trying to do a
squash commit
by merging in another branch and then resettingHEAD
to the previous place via:git reset origin/master
But I need to step out of this. How can I move HEAD back to the previous location?
I have the SHA-1 fragment (
23b6772
) of the commit that I need to move it to. How can I get back to this commit?-
Yaroslav Admin over 8 yearsHEAD is just a pointer to your current location (or revision to be precise).
git checkout 23b6772
should do. -
Andrew C over 8 yearsPossible duplicate of Revert Git repo to a previous commit
-
Romain Valeri about 5 years@YaroslavAdmin No it should not. Checking out a commit directly is the reason detached HEAD state happened (since remote-tracking branches can't be checked out themselves and automatically defer to the commit they point to when you try to do so like OP did) Also, sorry for the necromantic comment :-) I sort of hope the initial problem is solved already...
-
-
Tim about 8 yearsIf you are not on the latest commit - meaning that HEAD is pointing to a prior commit in history its called detached HEAD unless that prior commit in history is the tip of a different branch. In my experience, you could say that you are detached if HEAD is not pointing to a commit that is also pointed to by any branch. This does not apply for tags.
-
CodeWizard about 8 yearsYou can be in detached HEAD and on the same time have a branch with the same commit as the HEAD of this branch. I dont understand your comment
-
jub0bs over 7 yearsI have issues with your use of inline-code markup for headings :)
-
CodeWizard over 7 yearsCould not find any better way to emphasize it. feel free to edit. you are more than welcome
-
amuliar over 5 yearsThis is not working because if I do (assuming that 8acc968 is HEAD~2)
git checkout 8acc968
thengit branch -v
hasMyBranch
in the list below ...but thengit checkout MyBranch
deletes my comments. -
Kay V over 5 yearsHi @amuliar -
git checkout 8acc968
will check out a commit, not a branch. IfMyBranch
has the commits you want, trygit checkout MyBranch
. If it does not contain the changes in commit 8acc968, you would need to merge those changes after checking out the branch. -
zyy about 4 yearsThanks for the answer! I did
git checkout
to see a previous commit and wanted to get back to the latest commit. But without the latest commit hash, I was pretty much lost. This solution is perfect for my situation! -
Kay V about 4 yearsBe EXCEEDINGLY CAUTIOUS with
git push --force
. In many situations it will make you the least popular person on the team for a little while.... -
Kay V about 4 yearsto add to the above note, I just came across this quote at about.gitlab.com/blog/2014/11/26/keeping-your-code-protected and had to add it: "A single git push --force command can easily ruin the day for a lot of people: [186 Jenkins] repositories have their branch heads rewinded to point to older commits, and in effect the newer commits were misplaced after the bad git-push." --very unpopular developer....
-
oktober about 4 years@KayV take a look at
git push --force-with-lease
(Thoughtbot article: thoughtbot.com/blog/git-push-force-with-lease) -
Kay V about 4 yearsUseful flag, @oktober, and a good article. Thanks for adding it here and pinging me about it.
-
Kay V about 4 yearsImportant to note that a key detail in this answer is incorrect; I've proposed an edit. In the meantime:
git push --force
affects your remote. It does not "Then fix the HEAD to current commit." -
fIwJlxSzApHEZIl about 4 yearsthank you! this helped me un-do a bad merge. since merges don't react the same way to
revert
as commits I found myself in an incredibly difficult situation.force-with-lease
gave me the confidence to rewrite the git history of the branch without affecting other people's work. bravo! -
mike rodent about 2 years
git checkout -
does not necessarily have the effect you say.