gitgit-branchgit-refspec

Git: what is the difference between dev and refs/heads/dev?


From recent work in devops, it seems as though there are "two ways" to check out a branch in git: using the plain branch name, e.g. dev, or using the branch name prefixed with refs/heads/, e.g. refs/heads/dev.

What is the difference between the two?

Certain HTTP POST content in the Bitbucket webhook to Jenkins gives the "refs/head" version of the branch. Other POST content gives the "basename" branch.
E.g. using (what I think is) Jenkins syntax to fetch POST content from a pull-request Bitbucket event, $.pullRequest.fromRef.id == /refs/heads/dev...
and $.pullRequest.fromRef.displayId == dev

In a cloned repository, checking out refs/heads/dev and dev resolves to the same SHAID, but their respective stdouts differ:

$ git checkout refs/heads/dev
Note: checking out 'refs/heads/dev'.

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 <new-branch-name>

HEAD is now at c320fd1... wip
$ git checkout dev
Switched to branch 'dev'
Your branch is up-to-date with 'origin/dev'.
$ git log --oneline -n 1
c320fd1 wip

The stdout of the former looks the same as when you check out a SHAID.


Solution

  • Names are resolved to revision id's early, according to the rules in the docs.

    When the ref you give is resolved by (just) adding a refs/heads/ prefix, Git's convenience machinery kicks in, git checkout will make HEAD be an alias for the branch tip, so all the convenience commands that rely on HEAD will be referring to and updating the branch tip ref.

    So when you say git checkout dev, you're using Git's convenience machinery and it does the convenience thing for you, it wires HEAD to be a symbolic reference to refs/heads/dev and git commit's HEAD update will be redirected to that branch tip.

    But if you bypass the convenience logic by taking explicit steps yourself, that doesn't happen. That's really the sole difference. If you want, you can

    git checkout refs/heads/dev 
    git symbolic-ref HEAD refs/heads/dev
    

    and now you've done Git's convenience processing yourself.