I am using nodegit to checkout from clone and Open it to do something. My code like this:
//repo is a Repository from Clone() or Open()
//branchName is your branch name, of course
repo.getBranch('refs/remotes/origin/' + branchName)
.then(function(reference) {
//checkout branch
return repo.checkoutRef(reference);
});
But after that I cd in to branch directory and typing the command like this
git status
or git branch
It's show a red line like this
HEAD detached at origin/branchname
How can I solved it? Thanks
In general,1
origin/somebranchis not a branch name, and thereforegit checkout origin/somebranchresults in a detached HEAD, exactly as you are seeing.Branch names, in Git, don't really do any good, to a first approximation.2 So there's no need to use them. To understand how and why this is the case, let's note that in Git, there are many kinds of names.
A branch name is simply a name which, when spelled out in full, begins with
refs/heads/. The branch namesmasterormain, for instance, are reallyrefs/heads/masterandrefs/heads/main, correspondingly.A tag name is a name which, when spelled out in full, begins with
refs/tags/. Sov2.1is reallyrefs/tags/v2.1.Git calls these things—these names in general, before they're split into some particular classification—refs or references. Each reference holds one (1) hash ID. The hash ID is what really matters to Git. That's what Git needs. That's what
git checkoutrequires: a hash ID. You can give it any valid commit hash ID, and it will check out that commit.Hash IDs, though, are big and ugly and look random (though they're not). They are thoroughly unmemorable. What commit is
225365fb5195e804274ab569ac3cc4919451dc7fanyway? If I sayv2.31.0-rc0, that probably means something—or at least, seems suggestive of something—to you; but if I say2253blahyou have probably forgotten the2253part long before I get to thedc7fpart. So refs are for humans. They're not for Git, which only really cares about the hash IDs.You only need a ref—such as a branch name—if you're a human. If you're a build system, a hash ID is fine. If you're writing part of a build system, just use a hash ID. If you're writing something for a human to use ... well, humans are hard.
Git has a special thing it calls "DWIM", or Do What I Mean, mode. If you run:
and there is no branch named
foobranchright now, Git will assume that you mean: Find me a name that resemblesfoobranch, such asorigin/foobranch. Then use that name to make a branch name for me. You can disable this withgit checkout --no-guessand sometimes that's a good idea. But sometimes this DWIM mode is exactly what you want.Note, however, that if the pesky human went and made his own
foobranchearlier, not related toorigin/foobranch, thisgit checkout foobranchwill get hisfoobranch, not related toorigin/foobranch. So be careful: humans are tricky and weird. They do illogical, unexpected things.Now, there's a reason humans often want to use a branch name, rather than any other name that results in the detached-HEAD mode. The primary reason they like this is because then, if they make a new commit, Git will change the hash ID stored in the branch name. The new commit will automatically link, backwards, to whatever commit was the one stored in the reference. Then Git will update the branch name so that it now stores the hash ID of the new commit.
This feature is exclusive to branch names. No other kind of name has this special feature. You can select detached-HEAD mode when using a branch name, by running
git checkout --detach foobranchfor instance. But the default is that when you usegit checkoutwith a branch name—even one that DWIM mode is going to have to create—Git will instead go into attached HEAD mode.3 So that's why humans like branch names, and that's what Git does with them, that it doesn't do with any other reference name.If you need to accommodate humans, you can do that by letting the DWIM mode do its thing here. That won't satisfy all humans, so watch out. It also won't work in some cases, so watch out. The detached-HEAD mode always works, though.
In NodeGit specifically, you have
Branch.createas an async class method ofBranch.. You also haveBranch.lookup. You could use this to look up a remote-tracking name, likeorigin/branchname, and use that to create a new local branch, if that's your goal. But as before, watch out for all these various edge cases.1It is possible to make a (local) branch named
origin/somebranch, so that it is a branch name. The result is very confusing unless you carefully call out all names using their full spellings. Don't do this!2Of course, they do do some good, so the first approximation is pretty rough.
3Git does not call it this, but what else could the right phrase for "opposite of detached HEAD" mode be?