gitgit-detached-head

Detached HEAD after checking out a branch; how to `push`?


Here's what I did:

  1. Checked out a remote git repository.

  2. Added to the [remote "origin] section of .git/config:

fetch = +refs/heads/release/BranchName:refs/remotes/origin/release/BranchName

  1. Checked out the corresponding branch:

git checkout origin/release/BranchName

After that git status reported:

HEAD detached from origin/release/BranchName

  1. Added and checked in some modifications.

  2. Tries to git push. This resulted in the error message:

fatal: You are not currently on a branch. To push the history leading to the current (detached HEAD) state now, use

git push origin HEAD:<name-of-remote-branch>
  1. Then I followed the suggested command:

git push origin HEAD:origin/release/BranchName

and got the following:

error: unable to push to unqualified destination: origin/release/BranchName The destination refspec neither matches an existing ref on the remote nor begins with refs/, and we are unable to guess a prefix based on the source ref. error: failed to push some refs to 'RepositoryName`

Thus the questions: what did I do wrong? How to fix that and push the changes?


Solution

  • The string origin/release/BranchName contains both the name of the remote (origin) and the remote branch name (release/BranchName). The suggested command had these as separate arguments, so you should run:

    git push origin HEAD:release/BranchName
    

    To understand what went wrong, you have to understand that in git, branches don't really exist; a branch is just a convenient pointer to some commit. With a local branch, there are some convenient mechanics for moving that pointer when you commit, but with a remote branch, that doesn't happen (because you don't update the remote pointer until you run push).

    When you ran:

    git checkout origin/release/BranchName
    

    Git looked up a remote branch, found out what commit it pointed to, and checked out that commit. However, it didn't create or update any local branch, so when you committed, no new pointer was created, just a bunch of commits. That's what "detached HEAD" means - you have something checked out, but it's not "attached" to any branch.

    What you should instead have run was this:

    git checkout -t origin/release/BranchName
    

    Or this:

    git checkout release/BranchName
    

    (Or, in newer versions of git, replace git checkout with git switch - the functionality is the same in this case, but git switch has fewer confusing extra meanings.)

    In each case, assuming you don't already have a local branch called release/BranchName, git will work out that what you want is a new local branch which "tracks" (is associated with for push and pull commands) the remote branch of the same name.

    Then, when you commit, you will be committing to a normal branch, and won't get "detached head" errors.