91

Currently for switching to another git commit (on the same branch...actually, on the master branch!), I'm executing the command

git checkout ea3d5ed039edd6d4a07cc41bd09eb58edd1f2b3a

Now, every time I do this git tells me that I'm now with a detached head. How do I go to an older commit and still maintain the head on the same branch?

Jo Liss
  • 30,333
  • 19
  • 121
  • 170
devoured elysium
  • 101,373
  • 131
  • 340
  • 557
  • 3
    After you execute that command, your HEAD ref changes to *that* commit. It doesn't make sense to want the HEAD also pointing somewhere else. – Greg Hewgill Apr 14 '11 at 04:01
  • From what I understand from git's message, it is pointing *nowhere*, which is undesirable. – devoured elysium Apr 14 '11 at 04:19
  • 2
    The message Git shows when you check out a specific commit like that says "HEAD is now at ea3d5ed...", which tells you that HEAD *is* pointing somewhere. It's just pointing somewhere that doesn't have any other name *except* HEAD (at the moment, since a `git checkout` to another commit or branch name will move HEAD to that new place). – Greg Hewgill Apr 14 '11 at 05:05
  • Do the answers given adequately explain this, or is there something we could be more clear on? I'd be happy to clarify my answer for you if it doesn't answer your question. – Brian Campbell Apr 14 '11 at 18:07
  • If you came here looking to for a way to check out another commit while keeping the HEAD completely unchanged (for example in order to revert to an older commit): `git revert --no-commit 0766c053..HEAD` will do this, where `0766c053` is the commit you want to check out. This is from http://stackoverflow.com/a/21718540/525872. – Jo Liss Mar 06 '17 at 20:06

6 Answers6

198

Most of the time when I do this I checkout to a temp branch:

git checkout -b temp-branch-name ea3d5ed039edd6d4a07cc41bd09eb58edd1f2b3a

Then after I'm done i just delete the branch

Nick Canzoneri
  • 3,774
  • 1
  • 23
  • 17
83

It depends on what you want to do when you checkout that commit. If all you're doing is checking it out so you can build or test that revision, then there's nothing wrong with working with a detached head. Just remember to check out an actual branch before you make any commits (git checkout master, for example), so that you don't create commits that are not included in any branch.

If, however, you want to make more commits starting from that point, you should create a branch. If you make commits that are not referenced by a branch, they can easily get lost, and will eventually be cleaned up by git's garbage collector, as nothing refers to them. You can create a new branch by running:

git checkout -b newbranch ea3d5ed

To help visualize, here's are some diagrams demonstrating how working on a detached head differs from working on a branch.

Let's start out with 3 commits on master, A, B, and C. master is the current branch, so HEAD points to master, which points to commit C.

A  B  C
*--*--* <-- master <-- HEAD

Now if we commit, git will create a commit that has C as a parent (because that's the current commit, pointed to from HEAD via master), and will update master to point to that new commit. All of our commits are now in master, and HEAD points to the new commit through master.

A  B  C  D
*--*--*--* <-- master <-- HEAD

Now let's check out B, giving us a detached HEAD.

A  B  C  D
*--*--*--* <-- master
   ^
    \-- HEAD

Everything works fine here; we can look at all of the files, build our program, test it, etc. We can even create new commits; but if we do so, there's no branch that we're on, so we can't point any branch at that new commit. The only thing pointing at it is HEAD:

A  B  C  D
*--*--*--* <-- master
    \
     * <-- HEAD
     E

If we later decide to check out master again, there will be nothing referring to E.

A  B  C  D
*--*--*--* <-- master <-- HEAD
    \
     *
     E

Since there's nothing referring to it, it can be hard to find, and git considers commits with no references to be abandoned (they happen quite commonly if you rebase, or squash patches in, or do other fun history manipulation; they usually represent abandoned patches that you no longer care about). After a certain amount of time, git will consider it garbage, to be discarded the next time garbage collection runs.

So, instead of checking out a bare revision and getting a detached head, if you feel like you are going to make more commits, you should use git checkout -b branch B to create a branch and check it out. Now your commits won't be lost, as they will be included in a branch, that you can easily refer to, and merge later on.

A  B  C  D
*--*--*--* <-- master
   ^
    \-- branch <-- HEAD

If you forget to do this, and create commits off a branch, there's no need to worry. You can create a branch referring to the head revision with git checkout -b branch. If you have already switched back to the master branch, and realize that you forgot a stray commit, you can find it using git reflog, which will show you a history of what commits HEAD has pointed to over the last few days. Anything that's still in the reflog will not be garbage collected, and generally references are kept in the reflog for at least 30 days.

Brian Campbell
  • 322,767
  • 57
  • 360
  • 340
  • It's not entirely clear to me why when you checkout an old commit the head gets *detached*. Maybe the problem is on what a *detached* head means? On the other hand, if checking out an old commit with a detached head will only get it lost, why would anyone do it? Just to mess around and try things up? Why does git allow it, in the first place? – devoured elysium Apr 14 '11 at 18:27
  • 6
    @devoured elysium "detached head" means that you have a `HEAD` ref that is pointing directly at a SHA-1 of a commit, rather than pointing at a branch which in turn points at a commit. Because your head does not reference a branch, Git does not know what branch to update when you add new commits. As I explained at the beginning of my answer, it's perfectly fine to have a detached head if you're going back to an old version just to build or test the code; you can always get back to a branch with `git checkout master` or the like. It's only a problem if you commit while you have a detached head. – Brian Campbell Apr 14 '11 at 18:37
  • @BrianCampbell - After making commits on branch (where your head is currently), do you then merge branch into B and merge B into master. What should you do next? – amey1908 Mar 27 '14 at 20:23
  • Your explanation made a bunch of other things 'click' for me. Thank you. I might now finally have understood git... – Joe Jun 15 '16 at 14:39
8

If you simply want to return to an earlier commit to play with it without making any changes, you can do

git co <previous-commit-id>

you'll will be on a branch called "(no branch)" after this command.

Confirm this by

git br

After you've played with this previously committed code, you can switch to the branch you were on by

git co <the-branch-you-were-on>

The "(no branch)" will be deleted automatically. This way you don't need to create a temporary branch.

Zack Xu
  • 11,505
  • 9
  • 70
  • 78
6

Git’s HEAD is simply a pointer that says what’s in the working directory. If you want to check out a commit which is not the head of a branch, you simply have to redirect your HEAD to point at that commit. There’s no way around it. You can create a temporary branch at that commit, but HEAD will be directed away from master nonetheless.

That’s the short explanation. The verbosity below will hopefully aid in understanding how HEAD and master are different:

Normally, things look like this:

C ← refs/heads/master ← HEAD 
↓
B
↓
A

Which is to say: “The parent of C is B, and the parent of B is A. The branch master is pointing to C, and I currently have checked out the contents of master. In addition, when I commit, master shall be updated.”

Some assumptions are implicit in this which are necessary for a thorough understanding of the commit graph. Namely, commits refer only to their parents, and the contents of a branch are those commits (and only those commits) which can be reached by following the parent links. The (unmodified) contents of the working tree and the index must correspond to the commit named by HEAD, either indirectly (“symbolic”) or directly (“detached”).

Thus, if you want to check out an old commit, the HEAD must be updated to point to the desired commit. git-checkout does just that:

C ← refs/heads/master 
↓
B ← HEAD
↓
A

Now, you’ve left your branch behind you, since you’re looking at something old. That’s perfectly OK, as the “detached head” advice tell you calmly (emphasis mine):

You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by performing another checkout.

On the other hand, while resetting your branch also gets HEAD where it needs to be, it would have a very different effect!

C
↓
B ← refs/heads/master ← HEAD
↓
A

The commit C will become garbage, since you have declared that you do not wish for it to be part of the master branch anymore.

In short, all you have to do is understand what git means by “HEAD” — it’s where you are, not where any given branch is. And if where you are is not the same as where a branch is, there is no choice but to use a detached HEAD.

(Perhaps also look into GitHub, gitk, or gitweb to browse the commit history, if derailing your HEAD continues to irk you.)

Josh Lee
  • 171,072
  • 38
  • 269
  • 275
2

The question is a bit vague, but if you want to just change files in your working tree, you can simply do this:

git checkout [commit|branch] -- .

You can then stage the changes and create a new commit if you wish. This is pretty useful sometimes.

Lari Hotari
  • 5,190
  • 1
  • 36
  • 43
  • That's what I came here for. The question is answered also here: https://stackoverflow.com/questions/49571915/checkout-a-commit-without-updating-head – Cornelius Roemer Jul 14 '23 at 12:23
0

I think I understand your questions. Here is what I found to solve it. and there is no GUI solution of it, you can only use command to solve it, and it's really simple.

step 1: creat a tag of the old commit which you want to go back.

like tag v2.0

step 2: git checkout v2.0

here it is, now your HEAD is pointing at 'v2.0' commit, but master is still pointing at last commit.

C:\Program Files\Git\doc\git\html\git-checkout.html this document may help you

or type git help < checkout >

Radim Köhler
  • 122,561
  • 47
  • 239
  • 335
Lion Lai
  • 1,862
  • 2
  • 20
  • 41