Git - remote: fatal: You are on a branch yet to be born

前端 未结 1 1796
一生所求
一生所求 2021-02-03 22:58

Im trying to setup a hook to push from my desktop to my server. This has worked countless times in the past but now I\'m getting an error upon setting up a new site:

<         


        
相关标签:
1条回答
  • 2021-02-03 23:13

    Edit, September 2020: This is becoming more common because people are renaming existing master branches to main or similar in various server-side bare repositories. For a TL;DR, if you've done this in a server-side bare repository—or made a repository with git init --bare without also updating the initial master part—you will want to adjust the HEAD setting in this bare repository. Jump down to the So what to do? section for instructions.


    It is, as you suspected, because you're pushing a branch named master_prefix rather than master. As for what to do, well, that depends on what you want to happen. Skip to the end if you want to see several options.

    First, though, let's break this down a bit.

    Any message that begins with:

    remote: ...
    

    is actually coming from the "other guy". When you do a push (or fetch, for that matter), your Git calls up another Git over the Internet-phone or equivalent. They exchange information using a protocol that helps them identify when they're talking to each other directly, and when your Git is getting stuff from their end that's not from their Git, but rather from something their Git is using.

    In this case, their Git (on the server) is running their Git's hooks. There's just the one hook—you created it, so we could call it "your" hook, but your computer running your Git has no idea that the stuff on the server is authored by you: it doesn't know, doesn't need to know, and doesn't care; it just delivers messages. So we'll call it "their" hook.

    Their hook says:

    fatal: You are on a branch yet to be born
    

    and you see it on your end prefixed with remote: to let you know it's not your Git that's saying things, it's something on their end.


    At this point, the best thing to do is to change perspectives, by pretending "you" are now the server. On "your" end, your Git fires up and receives stuff (successfully, and puts in the requested branch, master_prefix), then runs the one hook. That hook fires up another, separate, git checkout command:

    git --work-tree=/home/username/public_html/example.com --git-dir=/home/username/example.git checkout -f
    

    This is pretty long so let's shorten it by ignoring the options for a moment. Aside from setting the work and git directories, it's just git checkout -f.

    If you ran this command by itself somewhere else, what branch will it check out? This isn't a rhetorical question, and the answer is in the documentation, although it may be unclear and even misleading:

    You could omit branch, in which case the command degenerates to "check out the current branch", which is a glorified no-op with a rather expensive side-effects to show only the tracking information, if exists, for the current branch.

    Because of the --work-dir and --git-dir options and the fact that the (bare) repository might be changed, it's not "a glorified no-op" after all, but it does use:

    the current branch

    That's the key, right there: the current branch. What is the "current branch" of this bare repository?

    The answer is the same as for any repo (bare or not): the current branch is the one named in the HEAD file. If you poke around in this bare repository, you'll find that file; examine it and it will say:

    $ cat HEAD
    ref: refs/heads/master
    $ 
    

    In other words, HEAD names the current branch, which—since the git init set it that way and nothing since then has changed it—is master.

    So your git checkout -f command is attempting to check out branch master.

    What branches actually exist? You can find out, by going into the bare repository and running git branch:

    $ git branch
      master_prefix
    $ 
    

    I got this with git version 2.3.0: note that there's no * master output. Other (future, really) versions of Git might show you * master since that is the branch you're on—even though it does not exist yet!

    What's going on? The answer is that any time you create a new branch that's not connected to any existing revision—which is always true for the master branch in a newly created repository—Git handles this by writing the branch name into HEAD, but not writing any revision ID into the appropriate file(s) for that branch. This is how Git records the idea that the named branch has not yet been created, but will be once you provide the first commit(s) for that branch.

    (If you use git checkout -b newbranch --orphan you get in this same "yet to be born" state for the new branch. It's most common for master of course, since that's how any brand-new, empty repository starts out.)


    So what to do?

    As I noted before, this really depends on what you want to have happen.

    You have a new (initially empty) bare repository with no master branch (but a post-receive hook that tries to export the current branch, which is still master). Then you supply, from another system, a new branch, but it's not master. I see two obvious possible "want"s although perhaps you want something fancier than either of these:

    1. You want to export nothing since there's no master to export: modify your hook to check whether the current branch exists:

      current_branch=$(git symbolic-ref HEAD) || exit 1
      sha1=$(git rev-parse -q --verify $current_branch) || exit 0
      # ok, the current branch exists; deploy it as usual
      git --work-tree=... --git-dir=... checkout -f
      
    2. You want to export something other than the current (master) branch. Decide whether that means "forever" or "until master appears" or whatever; modify your deployment script if needed or desired, or just change Git's idea of the current-branch.

    Let's say you want master_prefix deployed now and forever. Normally, you'd switch the bare repo to master_prefix with a simple git checkout, but you can't because (1) it's a --bare repo and (2) there is no master_prefix yet (unless you're doing this post-push as a fixup step).

    There are two easy ways, on the server, to update its own idea of the current branch, even if the new one does not yet exist:

        $ echo ref: refs/heads/master_prefix > HEAD
    

    does the trick by (c)rudely bypassing Git entirely, or:

        $ git symbolic-ref HEAD refs/heads/master_prefix
    

    does the same thing using Git.

    Alternatively, you can specify the precise branch that the post-receive script should check out:

        $ git --work-tree=... --git-dir=... checkout -f master_prefix
    

    Note that this will cause Git to change the current branch (in the bare repository) to master_prefix on each push.

    Since your hook does not look at which branch(es) have been updated if any, there's no way to tell which branch(es) to deploy, other than to use whatever is the default (in HEAD) or explicitly deploy a particular branch (add an argument).

    It's also worth noting a subtle tricky bit: the index file in the bare repository will record what's been checked-out to the specified work-tree. As long as you have a single deployment location and/or a single branch deployed, this will be fine. If you start getting fancy (e.g., deploying master to the regular server, but test to a test service on the same server), you may want to modify the deployment script, either to clean out and rebuild the target or to use multiple index files.

    Most of the above doesn't matter until you start getting fancy. The main thing is that you must decide what you want deployed, and maybe create a master branch.

    0 讨论(0)
提交回复
热议问题