git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: Deleting of the specified ref during the post-receive hook

From
Shawn O. Pearce <spearce@spearce.org>
Date
Sep 21, 2010, 14:50 UTC
Message-ID
<20100921145014.GH32601@spearce.org>
In-Reply-To
<4C98C3F2.9050006@workspacewhiz.com>
Joshua Jensen <jjensen@workspacewhiz.com> wrote:
Show 21 quoted lines
>  I've recently asked whether there was a public script to act as an  
> automatic 'maintainer', something akin to Gerrit's push always  
> succeeding without having to pull first when using Git as a central  
> repository.  I received a number of suggestions, and I have begun trial  
> implementations.
>
> My current line of thought has an auto-merging script that monitors the  
> refs/for/ namespace (similar to Gerrit) and then applies --no-ff merges  
> to the appropriate branch.  For instance, when the user pushes to  
> refs/for/master, the post-receive hook creates a secondary ref called  
> refs/for/master-SHA1-timestamp and then deletes the refs/for/master ref:
>
> #!bin/sh
> # post-receive hook
> while read oldrev newrev ref
> do
>     case $ref in
>         refs/for/*)
>             timestamp=`date +%s`
>             `git update-ref $ref-$newrev-$timestamp $newrev`
>             `git update-ref -d $ref`

FWIW, you don't need `` around the git update-ref calls, because you aren't using the output of the command as input to another command.

Show 5 quoted lines
> If you'll pardon my lacking shell script skills (I'm open to learn!), my  
> primary question concerns safety.  When receiving a ref via an SSH-based  
> server (which happens to be Gitolite, but I don't think that is relevant  
> here), is the post-receive hook guaranteed to be run in a lockstep  
> manner?

No. If there are two concurrent pushes occurring, the script may execute in parallel.

But you'll actually get something much worse. receive-pack will create refs/for/master for the first user... and then might be put to sleep while another user's receive-pack starts. That second user will see refs/for/master existing, and will fail their push because their concept of $newrev doesn't match what is currently at refs/for/master. Then the first user wakes up, runs your post-receive, and the ref is cleared.

The better strategy would be to use an update hook that refuses to permit the creation of refs/for/master:

  #!/bin/sh
  ref=$1
  old=$2
  new=$3
  case $ref in
  refs/for/*)
    timestamp=`date +%s`
    git update-ref $ref-$new-$timestamp $new
    echo "Created $ref-$new-$timestamp"
    exit 1
    ;;
  *)
    exit 0
    ;;
  esac

By exit 1 here we refuse the push attempt, so receive-pack won't create refs/for/master, and another user pushing won't see that false failure. However, unlike with Gerrit, every user is now going to receive a push failure message because the hook has appeared to reject the value, even though it accepted it.

> That is, if two people push to 'refs/for/master' at the same  
> time, is there a lock to process one user and then the other user?

There is, but only for the duration of the update hook. So if you used an update hook instead, one user would succeed with the push and the other would get a lock failure, and would need to retry.

> Before I go too much deeper down this path, am I way off base here?

I'd have to ask why you are using gitolite and trying to abuse git-receive-pack to do something that Gerrit does out of the box...

-- 
Shawn.
Previous: Joshua JensenNext: Joshua Jensen
Message 2 of 4 in “Deleting of the specified ref during the post-receive hook”
  1. Joshua JensenSep 21, 2010
  2. Shawn O. PearceSep 21, 2010
  3. Joshua JensenSep 21, 2010
  4. Shawn O. PearceSep 21, 2010

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.