4

Now, I don't know if a ref is considered to be a branch only if it's inside refs/heads, and indeed the question was previously titled How to checkout a branch stored outside refs/heads?. So I'm not sure if refs stored outside refs/heads can still be called "proper branches" or not, but the point is:

Let's say I have a ref to a commit as all normal branches are, but stored outside refs/heads; for example, the notes added with git-notes are stored this way, by default in the ref refs/notes/commits.

Can it still, in some way, be checked-out as if it were a normal branch (not doing a detached-head checkout), and thereafter be worked-on with the other git commands (rebase, cherry-pick etc.), as if it were a normal refs/heads branch?

I know that a normal git checkout will only check it out as a commit, placing the repository in detached-head state.

I did find out a way that seems to work, that is:

git symbolic-ref HEAD refs/MyUnusualRefPath/MyUnusualRef
git checkout -f HEAD

And I was able to do what this time I had to do, apparently, but I wanted to know if in general this is a supported, or sort-of-supported, or at least "right now it works well", operation, and if I can rely on it in the future and suggest others to utilize it.

I tried to look at the source for git checkout, but after a while it was clear that I'd better post a question in Stack Overflow (I searched on the web before, of course).
On hindsight it might have taken less to study the (whole) git source code.

I needed this thing indeed to solve problems with the sharing of (the horribly implemented / documented) notes between repositories, but please don't focus on this and just answer my above question, or else just ignore it and let others who are knowledgeable about it answer.

Zoe
  • 27,060
  • 21
  • 118
  • 148
gbr
  • 1,264
  • 8
  • 27
  • @MichaWiedenmann Read the last paragraph of the question – gbr Oct 22 '15 at 12:01
  • Your last sentence is not too clear, but do you mean fetching new work from a remote (i.e. not in local repo) and then checking it out? Would `git fetch && git checkout FETCH_HEAD` work for you? – code_dredd Oct 22 '15 at 12:05
  • @ray No, I don't mean that, anyhow my last sentence is just to give a context, what I'm really trying to solve is the following doubt: _Is there a supported, or sort-of-supported, way to checkout a branch whose head is stored outside refs/heads, and if so what that way is?_ Don't bother what was the problem that brought me to wonder about this, right now what I want to know is just this. – gbr Oct 22 '15 at 12:33
  • I'm not sure you are using the right terminology here. By definition, a git branch is a reference to a commit, and the place git stores the branch refs is in `refs/heads` (or `refs/remotes//`). If it's not stored under refs, then it isn't a branch in git. I am not clear what you are trying to do. – Dan Lowe Oct 22 '15 at 13:31
  • Regarding notes, which you talk about, they are not branches, they are commits. You can check one out and make it a branch, if you prefer... IMO that's kind of strange and I don't get it, but if you want to, just `git checkout -b `. – Dan Lowe Oct 22 '15 at 13:39
  • @DanLowe The notes issue is secondary, anyhow: 1. refs/notes/* are indeed references to commits. If there's some definition of branch that states explicitly that they are only those inside refs/heads, ok, my terminology is wrong, but that's not the point, the notes references are in all effects identical to branches expect for where their head is stored, and what I need to do is stuff that you do on branches (rebase, cherry-pick...) 2. If I make it a branch when I operate on that branch I change *that* new branch's history, not the one of the notes (refs/notes/commits does not get updated). – gbr Oct 22 '15 at 13:52
  • As far as I can tell, notes are *objects* in the git store, much like annotated tags or stashes. And you can check them out (detached head), or even base a new branch on them -- but they are not in their native form the same thing as a branch, so unless you `git checkout -b` to make a new branch *based on* the note, you will end up in detached head, just like if you check out a tag. I can't think of any way around that. Same situation for a random commit that is not at any branch tip; you can check it out but it will be a detached head. – Dan Lowe Oct 22 '15 at 14:03
  • @DanLowe I don't think there exists an official definition of git's `note` (if this think has a definite design behind there's an extremely wicked mind behind git's development), if we were to define it a single note is not a git object, it is a 'file' in the current notes' hierarchy (of a particular notes 'namespace' - of which I haven't found a definition yet, by the way) tip commit's tree, the name of which identifies the object being annotated and the contents of which identify the note's text (you do know the internal format of notes...?). (continues) – gbr Oct 22 '15 at 15:00
  • @DanLowe Anyway what matters (for this secondary issue) is that as far as I can tell the _only_ difference between normal branches and the notes references is that the notes are not stored under refs/heads. And I _did_ found a way around that, with `symbolic-ref` (that might be sufficient, but to make thinks cleaner it can be followed by `checkout-index -a -u` -which though leaves the old files in the working directory- or, as I found out then, simply by `checkout HEAD`, which seems to put the working directory exactly as a normal checkout does). (continues) – gbr Oct 22 '15 at 15:00
  • @DanLowe What I'm not sure is if this way is supported, or at least if I can expect it to work well or not (or if there is a better way). But I _did_ manage to do what I wanted, *apparently*. I would like to understand if this method can be trusted for the future, and if I can recommend it to others. (end - of this run of comments) – gbr Oct 22 '15 at 15:00

1 Answers1

3

A proper branch (meaning not a detached HEAD) is a commit stored in a refname directly referenced by HEAD.
That means HEAD is a symbolic ref to refname (which in turn contains the actual commit). See also "HEAD: current commit"

That is why git symbolic-ref HEAD refs/MyUnusualRefPath/MyUnusualRef works here.

You can store that ref anywhere in refs/, but:

Also by default, we sort by 'refname'.
Since 'HEAD' is alphabatically before 'refs/...' we end up with an array consisting of the 'HEAD' ref then the local branches and finally the remote-tracking branches.

The new branch.c code seems to filter only in refs/heads/... and refs/remotes/...


Note: only Git 2.11+ will detect and resist to a symbolic link with GIT_DIR/refs which would make a name resolution loop forever.

See commit e8c42cb, commit 3f7bd76 (06 Oct 2016) by Jeff King (peff).
(Merged by Junio C Hamano -- gitster -- in commit f7300cb, 17 Oct 2016)

Community
  • 1
  • 1
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • Thank you very much, at last a proper answer, I couldn't believe it when I saw it :D (was it you who deleted all those comments, by the way?) – gbr Oct 23 '15 at 18:29
  • That said: _A proper branch (meaning not a detached HEAD) is a commmit stored in a refname directly referenced by HEAD._ This does not seem right, I'm rather sure that branches stay branches even when they are not the _current branch_. Maybe you meant current branch indeed, or rather you were describing in what situation git thinks, or appears to be thinking, that you are in a 'proper branch' (not in detached HEAD status)...? (continues) – gbr Oct 23 '15 at 18:30
  • Anyway, I went on with my research (starting of course from the links you posted), and I found *a lot* of hints that only refs/heads/* are considered proper branches. In the documentation the most notable might be this, in [gitglossary](https://git-scm.com/docs/gitglossary): under [branch](https://git-scm.com/docs/gitglossary.html#def_branch): _The tip of the branch is referenced by a branch head_ , under [head](https://git-scm.com/docs/gitglossary.html#def_head): _Heads are stored in a file in $GIT_DIR/refs/heads/_ (continues) – gbr Oct 23 '15 at 18:30
  • Another almost authorative mention is in [this post](https://www.mail-archive.com/git%40vger.kernel.org/msg44987.html) by Junio C Hamano, the git maintainer, on march 2014: _By definition anything otuside refs/heads/ is not a branch_ . This almost settles it, but I'd still like to find something more official. I've been searching a little in the source code and the repository as well. I'll probably end up posting an answer of mine. (continues) – gbr Oct 23 '15 at 18:32
  • But if you, or others, already do know: 1. a better official definition of branch and 2. most of all, something that settles whether a non-refs/heads ref when stored in HEAD can be reliably operated on as if it were a proper branch - do post it, please, I'll accept that as the answer, I'm not craving to accept an answer of mine. – gbr Oct 23 '15 at 18:32
  • @gbr yes, I meant "current branch" as referenced by HEAD. HEAD can be a symbolic link to any refs/xxx, but, as you saw, only refs/heads and refs/remotes are looked up for branches. For now, what I see in branch.c is the closest of an "official" confirmation. – VonC Oct 23 '15 at 18:49
  • OK, I needed to know more in the question so I can't mark this answer as accepted. I'll probably have to end up asking in the git mailing list, it looks like it's a reasonably obscure issue. – gbr Oct 23 '15 at 19:05
  • @gbr no problem, I'll follow your question with interest. – VonC Oct 23 '15 at 19:07