First, once you have a detached HEAD, it just keeps being a detached HEAD regardless of any git reset
operations. Using git reset
, you don't reconnect it, you simply move it about, when it's in this mode.
You may use git checkout branch
to attach HEAD to the given branch
name at the same time as also checking out the tip commit of branch
. Of course, this has no effect on which commit hash ID that particular branch-name names.
It's not clear how you got into detached HEAD mode in the first place, as git merge
does not detach HEAD from its current branch if it is on one, nor attach HEAD to a branch if it's detached. The git checkout
command will go into detached HEAD mode in many cases though:
- if you use
--detach
to force that, or
- if you use a tag name to specify a commit, or
- if you use a remote-tracking name like
origin/master
to specify a commit, or
- if you use a raw commit hash ID to specify a commit
for instance. (This is not a complete list, it's just a representative sample of things that result in detached HEAD mode.)
If you want to create a new branch name, and make it point to some existing commit, you can:
- use
git branch name hash
to create the branch name, pointing to the given commit, but not actually run git checkout
on the new name, or
- use
git checkout -b name hash
to create the branch name, pointing to the given commit, and check it out and switch to that branch, all in one go.
Again, this is not meant as a complete list of ways to do this, but it is a good representative sample. Note that in all of these cases, we are creating a new name for some existing commit. (Use git log
, perhaps with --all --decorate --oneline --graph
for instance, to see a list of available commits and their corresponding hash IDs.) There is no requirement that any given commit have a branch name—many commits have no name, just a hash ID—but at the same time, one commit can have dozens of branch names, if you want. The names are really very unimportant to Git: what matters are the hash IDs. Software that demands a name, and won't accept a hash ID, is generally being rather unkind and non-Git-like.1
If you want to force some existing branch name to identify some commit other than the one that it currently identifies, you can:
- use
git branch -f
, if it's not the current branch name, or
- use
git reset
(with any of --soft
, --mixed
, or --hard
) if it is the current branch name, or
- use
git checkout -B name [hash]
to make it the current name (attaching HEAD if needed) and also move the name to either the given hash
, or to the current (HEAD
) commit if you don't supply a hash ID.
Again, these are representative sample commands, not a full list of every possible one. These are all slightly (or more than slightly) more dangerous than just creating a new name that points to some existing commit. In particular:
Forcing a branch name to "forget" some previous commit, and remember some new one, may, depending on a lot of stuff we haven't really covered here, cause your Git to lose easy access to some commit(s) that it previously found through the previous hash ID stored in the name. They can be gotten back for a while (via reflog searching), but not forever.
git reset
with --mixed
or --hard
overwrites the index and (with --hard
) work-tree contents, which are not frozen-for-as-long-as-you-can-find-the-commit and therefore may be impossible to get back,
1There is a good excuse to demand a name if the software is going to use git fetch
and/or git push
, or create new commits. That's because git fetch
and git push
have your Git call up another Git, and exchange commits and names, so these two commands may depend on having a name. The git commit
command, when it creates a new commit, writes the new commit's hash ID into the current branch name, or directly into HEAD
in the detached HEAD mode, so again there's a reason to want—but not require—a branch name.