Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GIT: How to unambiguously reference current HEAD when having a branch called HEAD?

Tags:

git

branch

Even though "HEAD" is definitely a poor choice for the name of a Git branch, it is still a valid branch name. If you happen to have branch named "HEAD", is there a way how to unambiguously refer to the actual HEAD symbolic reference?

The branch can be referenced as refs/heads/HEAD, but what about the HEAD itself?

Using just HEAD results in an refname 'HEAD' is ambiguous error anywhere where a <commit> is passed as an argument.

like image 683
Glutexo Avatar asked Jan 22 '15 08:01

Glutexo


People also ask

What is head in Git?

The Git HEAD is a Git reference object that represents the current branch. Checking out a branch will change the HEAD to point to the checked out branch. The HEAD will point to the tip of a branch by default, but can be set to point to any commit. When pointed at a specific commit the HEAD is considered to be in a ‘detached’ state.

How do I add a branch head reference in Git?

Figure 150. Git directory objects with branch head references included When you run commands like git branch <branch>, Git basically runs that update-ref command to add the SHA-1 of the last commit of the branch you’re on into whatever new reference you want to create.

How many branches can I check out in Git?

When working with Git, only one branch can be checked out at a time - and this is what's called the "HEAD" branch. Often, this is also referred to as the "active" or "current" branch. Git makes note of this current branch in a file located inside the Git repository, in.git/HEAD.

What is a branch in Git?

That’s basically what a branch in Git is: a simple pointer or reference to the head of a line of work. To create a branch back at the second commit, you can do this: Your branch will contain only work from that commit down: Now, your Git database conceptually looks something like this: Figure 150.


3 Answers

According to gitrevisions, if both HEAD and refs/heads/HEAD exist, the selected revision is HEAD (i.e., not the branch named HEAD).

This is in fact the correct answer for most situations, but git checkout prefers the branch-name to the revision, so git checkout HEAD resolves to the branch, rather than the current commit.

There are other commands that also choose the branch name, e.g., git branch -f HEAD newrev or git branch -D HEAD refers to the branch, but here there's no real room for ambiguity: git branch is obviously going to work on the branch.

Other handlers generally pass a branch name or a revision specifier to git rev-parse or git rev-list, and those behave as documented in gitrevisions.

Note that similar cases can occur with more realistic branch names. Just yesterday I created a branch for dealing with a certain ethernet items, and named the branch e1000 ... which looks like an abbreviated SHA-1. A branch named facade suffers the same fate.

like image 191
torek Avatar answered Oct 21 '22 01:10

torek


The good news is, with Git 2.16 (Q1 2018), that issue won't come up easily anymore, since "git branch" and "git checkout -b" are now forbidden from creating a branch whose name is "HEAD".

See commit 662a4c8 (14 Nov 2017) by Kaartic Sivaraam (sivaraam).
See commit a625b09 (14 Nov 2017), and commit bc1c9c0, commit 8280c4c (13 Oct 2017) by Junio C Hamano (gitster).
(Merged by Junio C Hamano -- gitster -- in commit 1616928, 28 Nov 2017)

branch: correctly reject refs/heads/{-dash,HEAD}

strbuf_check_branch_ref() is the central place where many codepaths see if a proposed name is suitable for the name of a branch.
It was designed to allow us to get stricter than the check_refname_format() check used for refnames in general, and we already use it to reject a branch whose name begins with a '-'.
The function gets a strbuf and a string "name", and returns non-zero if the name is not appropriate as the name for a branch.
When the name is good, it places the full refname for the branch with the proposed name in the strbuf before it returns.

However, it turns out that one caller looks at what is in the strbuf even when the function returns an error.
Make the function populate the strbuf even when it returns an error.
That way, when "-dash" is given as name, "refs/heads/-dash" is placed in the strbuf when returning an error to copy_or_rename_branch(), which notices that the user is trying to recover with "git branch -m -- -dash dash" to rename "-dash" to "dash".

While at it, use the same mechanism to also reject "HEAD" as a branch name.

like image 30
VonC Avatar answered Oct 20 '22 23:10

VonC


You can use use $(git rev-parse --quiet HEAD) whenever you need the commit (or $(git symbolic-ref HEAD) if you want to know where HEAD "points to").

According to git help rev-parse $GIT_DIR/<refname> takes precedence over refs/HEAD, refs/tags/HEAD, refs/heads/HEAD, etc., etc. and --quiet will silence the "refname 'HEAD' is ambiguous" warning.

Everywhere that takes a ref you should be able to use HEAD to refer to the currently checked out "thing" and refs/heads/HEAD to refer to a branch called HEAD. If you find a place in Git which takes a ref (not a branch) and HEAD doesn't work then you should report it as a bug.

like image 34
CB Bailey Avatar answered Oct 21 '22 00:10

CB Bailey