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:
<
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.)
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:
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
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.