gitgit-branchgit-bashgit-detached-head

Git started detaching head when viewing any previous commits on a branch?


I'm a newer git user so this maybe a dumb question, but all the sudden whenever I checkout any previous commit with something like git checkout 050aa9f in my Development branch, git immediately detaches the head:

You are in 'detached HEAD' state. 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.

If you want to create a new branch to retain commits you create, you may do so (now or later) by using -b with the checkout command again. Example:

git checkout -b

HEAD is now at 050aa9f [Code commit title here]

But when I checkout a commit from another branch such as master it doesn't detach the head.

Have I done something to corrupt my tree in some way? How can I find where this started, and how do I fix it?


Solution

  • whenever I [use] ... git checkout 050aa9f ... git immediately detaches the head

    That's because this kind of git checkout is specifically a request to detach HEAD.

    Any time you use something that is not a branch name, but can be resolved to a commit hash ID, git checkout will put you in detached-HEAD mode. But any time you use something that is a branch name, git checkout will put you in the normal mode. (Git does not call this "attached-HEAD" mode but that's the obvious right name for the mode.)

    There are several tricky bits here, some of which are partly helped out by using the new (in Git 2.23 and later) git switch command as VonC recommends. I'll go through them here, but remember that some of this is Advanced Git and you're not expected to know all of it right away. 😀


    It's worth describing exactly what goes wrong with the old git checkout here, that doesn't with the new git switch / git restore split. (And, as I mentioned, git checkout itself has been made smarter so that it doesn't just blindly do the wrong thing now—but with Git versions older than 2.23, watch out!) The two "kinds" of git checkout are:

    This means one kind of git checkout is entirely safe: it never wrecks in-progress work. The other kind of git checkout is quite dangerous: you're telling Git please wipe out my in-progress work, irretrievably.

    This is the danger I mentioned above. Suppose you have a bunch of files in a folder named dev, and a remote-tracking name origin/dev, but you do not yet have a branch named dev. If you run:

    git checkout dev
    

    expecting Git to create a branch named dev now based on origin/dev, you get a nasty surprise: Git (before 2.23) wipes out any work you've done on the dev/* files instead.


    (There are even more things that git checkout can do, all of which are now part of the split-out commands. I've left these out to keep this answer short. Well, shorter, anyway.)