Why is there a difference between `master` and `heads/master`?

。_饼干妹妹 提交于 2020-01-05 23:44:27

问题


I'm writing a shell script which switches to a specified commit, makes a copy of the index in /tmp, resets HEAD to original position, then runs a command in the temporary directory:

orig_head=$(git rev-parse -q HEAD)  # "refs/heads/master"
git checkout "$1"
# copy index to /tmp/...
git checkout "$orig_head"
# run command in /tmp/...

However, this script gives me the same "'detached HEAD' state" message as when I run git checkout refs/heads/master:

Note: checking out 'refs/heads/master'.

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 c82ad67... Use vector and binary search for dictionary

How can I store and restore the position of HEAD properly?

I would prefer to avoid using reset HEAD@{1}, as that seems messy.


回答1:


Most git commands don't really care whether a name is a branch name. The obvious exceptions (there may be more, these are off the top of my head) are git branch and, of course, git checkout.

For the non-exceptions, the rules for resolving branch names to SHA-1s are listed in gitrevisions. For git branch it's easier: some arguments are obviously branch-names, e.g., in git branch new/branch/name, new/branch/name is a branch name, even though it presumably doesn't exist yet.

The checkout command can't do that: given git checkout xyz, xyz might be a branch name, or a tag name, or any of the other forms in gitrevisions. Of course, to be one of the funny syntax forms like HEAD~5 it has to have the special ~ character in it, but even an unadorned name might be a branch name, or might not. (If it follows -b, as in git checkout -b new/branch then is is definitely a branch-name, but that's not the case here.)

Anyway, the short answer is that git checkout has its own special rules, different from those listed in gitrevisions: a name is a branch name if adding refs/heads/ in front works to turn it into an existing branch name.

This fails with refs/heads/master since refs/heads/refs/heads/master is not an existing branch name.1 Hence you need to strip off the refs/heads/ part yourself.

You could do this after-the-fact, but there's an easier version: git symbolic-ref has --short to leave the refs/heads/ part off. Since HEAD should only be a symbolic reference to a branch (never a symbolic ref to a tag for instance), and you know you're inquiring about HEAD, just do:

orig_head=$(git symbolic-ref -q --short HEAD)

You do need one more bit, which is to remember whether the system was in detached HEAD state to start with, and if so, what SHA-1 that goes with. So:

sha1=$(git rev-parse HEAD) || exit 1 # should never fail
orig_head=$(git symbolic-ref -q --short HEAD) && symbolic=true || symbolic=false

or something along those lines.


1You can create such a branch. Don't. :-)



来源:https://stackoverflow.com/questions/30006786/why-is-there-a-difference-between-master-and-heads-master

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!