10

I have a Git repository with 'master' branch. Some time ago (few months), we stopped using master, and created a new branch that all work is being done on.

I am now setting up source indexing with git, and for some reason i am seeing weird stuff with the new branch:

  1. Running git log fails:

    fatal: bad default revision 'HEAD'

  2. Running git fsck results in this:

    notice: HEAD points to an unborn branch (master) notice: No default references dangling commit 81f11e0b99ad38ecc8502bbed171d2bdfcaa6476

I think that something is not right with this repository/branch which is causing problems with the source indexing scripts.

Any ideas? (Note that the REAL issue here is that the source indexing script fails to get the object id it is trying to lookup using git show, it says that no such object exists).

kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
lysergic-acid
  • 19,570
  • 21
  • 109
  • 218
  • Try and `git log 81f11e0b99ad38ecc8502bbed171d2bdfcaa6476`, is that your branch? – fge Dec 18 '11 at 12:22
  • No, that's the master branch. i am using another branch not this one. Can you explain what do you think is going on? – lysergic-acid Dec 18 '11 at 12:31
  • I'm struggling with this particular output situation where I know where the gpg files are, but Git can't find them. When trying to git fetch or git push/pull, I get this message after executing git commit -sam "Make remote and local branches current. 8/7/2020 13:44 GMT" error: cannot spawn gpg2: No such file or directory error: gpg failed to sign the data fatal: failed to write commit object – Angelfirenze Aug 07 '20 at 17:45

3 Answers3

15

You don't have to have a master branch, but you do have to have a "default" branch in any git repository. In a non-bare repository, this is the checked-out branch; in a bare repository, it just means it's the default branch checked out for clones.

This default branch is called HEAD, and must always exist in a valid git repository. If you've removed the branch that HEAD was pointing at, you can reset it to a valid branch with:

git symbolic-ref HEAD refs/heads/new-main-branch
NSGod
  • 22,699
  • 3
  • 58
  • 66
CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • 1
    Is this a workaround or a proper way to fix this? Also, this has to be done on my main repository, or on any other cloned ones? (can i push this change into the main one as well?) – lysergic-acid Dec 18 '11 at 12:36
  • This is the proper fix, not having a `HEAD` is a genuine problem. This needs to be done on your main repository; your clones will have valid `HEAD`s so long as you have _something_ checked out. – CB Bailey Dec 18 '11 at 12:38
  • 1
    OK. After doing this, will cloned repositories suffer from this again? i am wondering why it happened in the first place. – lysergic-acid Dec 18 '11 at 12:42
  • @liortal: Perhaps I've misunderstood. I thought the problem was with your central repository. What issue are your cloned repositories suffering from? – CB Bailey Dec 18 '11 at 12:45
  • 1
    This message is seen on cloned repositories. The way Source Server works as i've seen, it clones the bare central repository and attempts to get the correct object (file) from it. This fails with the message i've posted in my original question. – lysergic-acid Dec 18 '11 at 12:46
  • @liortal: OK, if your central repository lacks a valid HEAD then this will affect new clones, your existing clones will be unaffected but I assume that they are working OK in any case. Fixing the central repository should fix this error if you are getting the error during or after making a fresh clone. Perhaps you could update your question with a more explicit test case. – CB Bailey Dec 18 '11 at 12:49
  • All works fine now after adding the symbolic-ref to the actual branch. – lysergic-acid Dec 19 '11 at 06:34
1

This happened to me due to some corruption, and the answer from @CBBailey didn't work at first.

To fix the previous corruption I had pushed from my working clone like:

git push origin :refs/heads/master

This caused the refs/heads/ directory of the bare repo to be empty, hence why I encountered this problem of HEAD pointing to an "unborn branch". In my case this was resolved by just doing another:

git push --all

This put refs/heads/master back in place, so HEAD worked again.

Warbo
  • 2,611
  • 1
  • 29
  • 23
  • winner winner chicken dinner, thanks - i ended up in a similar spot whilst deep in a frenzy of errant SO git commands - didn't think I was ever going to surface – MCGRAW Jun 16 '21 at 02:55
1

In a non-bare repository this the checked out branch, in a bare repository it just means it's the default branch checked out for clones.

Git will now be aware If that default branch does not exist yet.

With Git 2.31 (Q1 2021), "git clone"(man) tries to locally check out the branch pointed at by HEAD of the remote repository after it is done, but the protocol did not convey the information necessary to do so when copying an empty repository.
The protocol v2 learned how to do so.

See commit 4f37d45, commit 3983540, commit 59e1205 (05 Feb 2021) by Jonathan Tan (jhowtan).
(Merged by Junio C Hamano -- gitster -- in commit 69571df, 17 Feb 2021)

ls-refs: report unborn targets of symrefs

Signed-off-by: Jonathan Tan

When cloning, we choose the default branch based on the remote HEAD.
But if there is no remote HEAD reported (which could happen if the target of the remote HEAD is unborn), we'll fall back to using our local init.defaultBranch.

(This is what CB Bailey's answer mentioned)

Traditionally this hasn't been a big deal, because most repos used "master" as the default.

But these days it is likely to cause confusion if the server and client implementations choose different values (e.g., if the remote started with "main", we may choose "master" locally, create commits there, and then the user is surprised when they push to "master" and not "main").

To solve this, the remote needs to communicate the target of the HEAD symref, even if it is unborn, and "git clone"(man) needs to use this information.

Currently, symrefs that have unborn targets (such as in this case) are not communicated by the protocol.

Teach Git to advertise and support the "unborn" feature in "ls-refs" (by default, this is advertised, but server administrators may turn this off through the lsrefs.unborn config).
This feature indicates that "ls-refs" supports the "unborn" argument; when it is specified, "ls-refs" will send the HEAD symref with the name of its unborn target.

This change is only for protocol v2.
A similar change for protocol v0 would require independent protocol design (there being no analogous position to signal support for "unborn") and client-side plumbing of the data required, so the scope of this patch set is limited to protocol v2.

The client side will be updated to use this in a subsequent commit.

git config now includes in its man page:

lsrefs.unborn

May be "advertise" (the default), "allow", or "ignore".
If "advertise", the server will respond to the client sending "unborn" (as described in protocol-v2.txt) and will advertise support for this feature during the protocol v2 capability advertisement.
"allow" is the same as "advertise" except that the server will not advertise support for this feature; this is useful for load-balanced servers that cannot be updated atomically (for example), since the administrator could configure "allow", then after a delay, configure "advertise".

technical/protocol-v2 now includes in its man page:

If the 'unborn' feature is advertised the following argument can be included in the client's request.

unborn
The server will send information about HEAD even if it is a symref pointing to an unborn branch in the form "unborn HEAD symref-target:<target>".

technical/protocol-v2 now includes in its man page:

obj-id-or-unborn = (obj-id | "unborn")
ref = PKT-LINE(obj-id-or-unborn SP refname *(SP ref-attribute) LF)


Again, this feature is available only when using protocol v2, as illustrated in Git 2.31.1 (Q1 2021):

See commit 5f70859 (17 Mar 2021) by Jonathan Tan (jhowtan).
(Merged by Junio C Hamano -- gitster -- in commit cc930b7, 19 Mar 2021)

t5606: run clone branch name test with protocol v2

Signed-off-by: Jonathan Tan

4f37d45 ("clone: respect remote unborn HEAD", 2021-02-05, Git v2.31.0-rc0 -- merge listed in batch #9) introduces a new feature (if the remote has an unborn HEAD, e.g. when the remote repository is empty, use it as the name of the branch) that only works in protocol v2, but did not ensure that one of its tests always uses protocol v2, and thus that test would fail if GIT_TEST_PROTOCOL_VERSION=0 (or 1) is used.
Therefore, add "-c protocol.version=2" to the appropriate test.

Example:

git -c init.defaultBranch=foo init --bare empty
git -C empty config lsrefs.unborn advertise
git -c init.defaultBranch=up -c protocol.version=2 clone empty whats-up

The default branch name for the local cloned repository (git -C whats-up symbolic-ref HEAD) won't be up (even though init.defaultBranch was explicitely set to 'up'), but whatever the default name was for the remote (empty) repository: in this case 'foo' (because the remote repository has set lsrefs.unborn to advertise)


Before Git 2.34 (Q4 2021), "git clone"(man) from a repository whose HEAD is unborn into a bare repository didn't follow the branch name the other side used, which is corrected.

See commit 6b58df5 (20 Sep 2021) by Jeff King (peff).
(Merged by Junio C Hamano -- gitster -- in commit ac162a6, 03 Oct 2021)

clone: handle unborn branch in bare repos

Signed-off-by: Jeff King

When cloning a repository with an unborn HEAD, we'll set the local HEAD to match it only if the local repository is non-bare.
This is inconsistent with all other combinations:

remote HEAD       | local repo | local HEAD
-----------------------------------------------
points to commit  | non-bare   | same as remote
points to commit  | bare       | same as remote
unborn            | non-bare   | same as remote
unborn            | bare       | local default

So I don't think this is some clever or subtle behavior, but just a bug in 4f37d45 ("clone: respect remote unborn HEAD", 2021-02-05, Git v2.31.0-rc0 -- merge listed in batch #9).
And it's easy to see how we ended up there.
Before that commit, the code to set up the HEAD for an empty repo was guarded by "if (!option_bare)".
That's because the only thing it did was call install_branch_config(), and we don't want to do so for a bare repository (unborn HEAD or not).

That commit put the handling of unborn HEADs into the same block, since those also need to call install_branch_config().
But the unborn case has an additional side effect of calling create_symref(), and we want that to happen whether we are bare or not.

This patch just pulls all of the "figure out the default branch" code out of the "!option_bare" block.
Only the actual config installation is kept there.

Note that this does mean we might allocate "ref" and not use it (if the remote is empty but did not advertise an unborn HEAD).
But that's not really a big deal since this isn't a hot code path, and it keeps the code simple.
The alternative would be handling unborn_head_target separately, but that gets confusing since its memory ownership is tangled up with the "ref" variable.

There's just one new test, for the case we're fixing.
The other ones in the table are handled elsewhere (the unborn non-bare case just above, and the actually-born cases in t5601, t5606, and t5609, as they do not require v2's "unborn" protocol extension).


"git clone"(man) from a repository with some ref whose HEAD is unborn did not set the HEAD in the resulting repository correctly, which has been corrected with Git 2.38 (Q3 2022).

See commit daf7898 (11 Jul 2022), and commit cc8fcd1, commit 3d8314f, commit f77710c (07 Jul 2022) by Jeff King (peff).
(Merged by Junio C Hamano -- gitster -- in commit cf92cb2, 19 Jul 2022)

clone: move unborn head creation to update_head()

Signed-off-by: Jeff King

Prior to 4f37d45 ("clone: respect remote unborn HEAD", 2021-02-05, Git v2.31.0-rc0 -- merge listed in batch #9), creation of the local HEAD was always done in update_head().
That commit added code to handle an unborn head in an empty repository, and just did all symref creation and config setup there.

This makes the code flow a little bit confusing, especially as new corner cases have been covered (like the previous commit to match our default branch name to a non-HEAD remote branch).

Let's move the creation of the unborn symref into update_head().
This matches the other HEAD-creation cases, and now the logic is consistently separated: the main cmd_clone() function only examines the situation and sets variables based on what it finds, and update_head() actually performs the update.

See also "How to clone a bare git repo with no commits and get the correct HEAD ref during the clone?".

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250