Re: Git push failure with update hook success

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wednesday 2007 March 07 17:25, Bill Lear wrote:

> Ok, thank you for fixing this.  I guess the call run_update_hook()
> should not in fact come after the write_ref_sha1() block, and just
> before the 'return 0' line, because you don't want to run the
> write_ref_sha1() at all if the update hook complains.

Yep.  The problem is definitely real, it's a bodge to have to do the email 
generation in the update hook.  When I started writing a new update hook, I 
wanted to put it in post-update but found I couldn't.

Here's my assumption for an email generator: you want every change of revision 
to generate an email; you want only the revisions added to make that change 
to appear on an email; you want every new revision to appear on /only/ one 
email.

A lot of the magic for making emails with useful information comes from the 
fact that the update hook is given three pieces of information:
 - ref being changed
 - current revision of ref
 - potential new revision of ref

The first job of the update hook is to see if this update is allowed.  The 
sample update hook defaults to rejecting un-annotated tags - generally 
un-annotated tags are ones that you wouldn't want to distribute to the whole 
project.  It also rejects updates to remote tracking branches - obviously a 
remote shouldn't be allowed to update your local tracking branches - that's 
done by your own git-fetch; not someone elses git-push (in fact a central 
repository probably shouldn't have tracking branches at all).  This is the 
bit that in an ideal world stay in the pre-update hook.

The second job (which is the one that really should go into post-update but 
can't) is to build a useful email.  The ref parameter tells us what sort of 
ref we're updating; branch updates (i.e. refs/heads/*) and annotated tags 
(i.e. refs/tags/*) updates are the most interesting.

So; you want to send an email summarising the changes that are being added to 
the repository.  You don't want to send the entire log for every change to 
the branch in question since the beginning of time, because that would just 
make ever increasing emails.  Therefore you need the old revision. e.g.

 * -- * --- O (oldrev)
             \
              N -- N -- N (newrev)

In this case, the new revisions are found with "git-rev-list $oldrev..
$newrev".  Easy.

More difficult is when you want to be able to cope when the ref update 
was --forced.  e.g.

  * -- * -- B -- O -- O -- O (oldrev)
             \
              N -- N -- N (newrev)

In this example, the ref update was not a fast forward, newrev points at the 
tip of a branch which does not contain oldrev.  Oh dear, "git-rev-list 
$oldrev..$newrev" wouldn't return just the "N" revisions.  To get around 
this, the hook makes the assumption that emails for oldrev will already have 
been issued (otherwise how did oldrev get into the repository :-)) and finds 
the base revision of the old and new, B.  Then the new commits are given 
by "git-rev-list $baserev..$newrev".

The final tricky thing to get around is the killer for why this email 
generation has to be done in the pre-update hook.  Merges.

 * -- O (master) -- A -- A -- A -- M
       \                          /
        B --- B ---- B --- B --- B (maint)

Let's imagine that the master branch is what is being pushed, so it is about 
to be updated from O to M.  Sometime earlier, the "B" branch referenced by 
maint has received updates as normal.

The new revisions that need an email are all of the "A" nodes and the "M" 
node.  We can't simply use "git rev-list O..M" as we have before because that 
would include all of the "B" branch.  We've already had emails for those 
revisions so we'd simply be making noise.  The solution then is to cheat a 
little and say "all the revisions from O to M, but not any that reachable by 
refs already in the repository".  In git-speak:

  git-rev-list $oldrev..$newrev --not --all

Or, close to how it's written in the update-hook

 git-rev-list $newrev --not $oldrev --all

The "--all" means all of "refs/", and the revisions reachable by them.  Now 
here's the killer.  If we did that in the post-update hook (even if all the 
other information were available), this wouldn't work because the master ref 
would have been updated and would already point at "M".  In that 
case "--not --all" would include the new revisions.

Without adding some nasty switches to git-rev-list 
(like --all-except-this-branch), I can't see how the post-update hook could 
ever send emails with the necessary amount of detail.


Andy
-- 
Dr Andy Parkins, M Eng (hons), MIET
andyparkins@xxxxxxxxx
-
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]