my post-receive, a git hook, with two commands doesn't work

git githooks git-post-receive post-receive-email

197 观看


I have three git repositories:

  1. a bare repository on my git server
  2. a local repository(A) on my laptop (I develop web pages on this machine.)
  3. a local repository(B) on a hosting server (I use it as a http server.)

I set a post-receive, a server side hook, on the bare repository to have the repository(B) pull from the bare and have the git server send a notification email when I commit and push from the repository(A) to the bare repository. The post-receive file I made on the bare repository is as follows:

ssh -l MYUSER -i PRIVKEY "cd GITDIRECTORY;git pull"
. $(dirname $0)/post-receive-email

And I also set environmental variables on the bare repository:

git config hooks.mailinglist "MyMailAddress"
git config hooks.announcelist "MyMailAddress"
git config hooks.emailprefix "FooBar"
echo FooBar > description

I expected the repository(B) pulled and the git server sent a notification email by post-receive-email when I commit and push to the bare repository but the post-receive didn't work as I hoped. It had the repository(B) pull but didn't have the git server send any email. I didn't see any email error in logs on the git server.

When I commented out the ssh part of the post-receive, I got an email. When I commented out the post-receive-email part, the repository(B) pulled correctly. When both the ssh part and the post-receive-email part are active, the post-receive only have the repository(B) pull and didn't send any email.

I don't know why the post-receive doesn't work as I expect when both the ssh part and the post-receive-email part exist at the same time.

Any hint and comment is appreciated. Thank you in advance.

作者: Taiki Bessho 的来源 发布者: 2019 年 11 月 20 日

回应 (1)



TL;DR: ssh -n.

Background facts:

  1. The post-receive hook gets input on its standard input. Reading stdin, you will get one line per updated reference, in the form described in the githooks documentation.

  2. All of the Git hooks that receive input on stdin do so on a pipe. (I contend that this is a bad idea—that they should receive their input as a file descriptor open on a temporary file—but that's a separate battle. Even if that were not the case you would still need another trick.)

  3. Reading the input available on a pipe consumes that input. Once it's read, it's gone.

    (This, by the way, is why the read built-in in shells is so inefficient: it's necessary to read the input one byte at a time, so that only as much as read is going to read is consumed.)

With that in mind, consider the ssh command, and this excerpt from its documentation:

-n     Redirects stdin from /dev/null (actually, prevents reading from stdin).

So, what happens if you run ssh without -n? It will, of course, read stdin. But ... how much will it read? Well, strictly speaking, that depends: ssh runs several processes and/or threads, and there is a race if there is a great deal of stdin input and the command run on the remote end executes very quickly, but in general, the amount that ssh reads is all of stdin.

Now consult background fact #3, and answer the question: what's left on stdin, when ssh finishes?

Now, think about what your post-receive hook that sends email does. It must, necessarily, read its stdin to see which references were updated. What data will it get on its stdin?

If you prevent ssh from reading (and thereby consuming) all the input, the post-receive hook will be able to read (and consume, but now it doesn't matter any more) all the input, and therefore function correctly.

作者: torek 发布者: 23.12.2017 08:02