Suggestions for improvements to post-receive hook

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

 



We have made a few changes to the post-receive hook for operation in
our company.  I wanted to pass them on in case they are suitable for
more general use.  Below I mix description of the change, a
justification of the change, along with a snippet of code and/or code
comments illustrating the change.  I'm sure there are more
sophisticated ways to do what we've done, but it seems to work for us.

1. Support per-branch mailing lists.  This allows me to collaborate
   with several different people on different mini-projects from the
   same "hub" repository without each of them getting mail they don't
   care about.

   Code:

# hooks.{branch}mailinglist
#   This is the list that all pushes to a given branch will go to;
#   leave it blank to not send emails for every ref update to this branch.
[...]
        if [ -z "$recipients" ]; then
                # Essentially an associative array,
                # hooks.${short_refname}mailinglist: allows a separate mailing
                # list for each branch/tag thingie.
                recipients=$(git-repo-config hooks.${short_refname}mailinglist)
        fi

        # One last try ...
        if [ -z "$recipients" ]; then
                recipients=$(git repo-config hooks.mailinglist)
        fi

        # Check if we've got anyone to send to
        if [ -z "$recipients" ]; then
                echo >&2 "*** hooks.recipients is not set so no email will be sent"
                echo >&2 "*** for $refname update $oldrev->$newrev"
                exit 0
        fi


2. Allow specification of a prefix for the subject line.  This allows
   us to better filter our email --- we use a mailing list for some
   projects and the mailing list prefixes the subject with its own
   string, for example.

   Code:

# hooks.prefix
#   Set the email subject prefix to "[$hooks.prefix]".  Defaults to empty string.
[...]
# Notes
# -----
# All emails have their subjects prefixed with "[$hooks.prefix]"
# to aid filtering, if hooks.prefix is set.  If it is not set, no
# prefix is used.
[...]
prefix=$(git-repo-config hooks.prefix)

if [ -n "$prefix" ]; then
        EMAILPREFIX="[${prefix}] "
else
        EMAILPREFIX=""
fi
[... elsewhere in the code]
generate_email_header()
{
        # --- Email (all stdout will be the email)
        # Generate header
        cat <<-EOF
        From: $committer
        To: $recipients
        Subject: ${EMAILPREFIX}$projectdesc $refname_type "$short_refname" ${change_type}d. $describe
        X-Git-Refname: $refname
        X-Git-Reftype: $refname_type
[...]

3.  Provide change content in email.  The current hook does not produce
    enough information for us --- we like to see the content of the change
    so we can code-review on-the-fly.  I hacked the code, in my ignorance,
    to use git-whatchanged, as it seemed to provide what we wanted.  I
    am not currently controlling this behavior with a config flag, though
    presumably it could be, easily.

    Code (simple diff):
408,411c399,400
<       echo "Diffstat:"
<       git-diff-tree --no-color --stat -M -C --find-copies-harder $newrev ^$oldrev
<       echo ""
<       git-whatchanged --no-color -p --find-copies-harder $oldrev..$newrev
---
>       echo "Summary of changes:"
>       git diff-tree --stat --summary --find-copies-harder $oldrev..$newrev

4 and 5.

    4: Log the content of the emails, if desired, to a log file.
    Our email archiving system here is between lame and nonexistent,
    so we like to be able to look back at the logs if a problem should
    occur.

    5: Allow sending only a certain number of lines maximum to email
    recipients.  Some diffs are huge, so we don't want to overwhelm
    recipients' inboxes.

    Note: currently, specifying the maximum number of email lines
    only works when you specify logging, though this is not necessary.
    Also: this might only be useful if you have a flag that controls
    the use of git-whatchanged (or whatever) to produce a fuller diff
    set in the email.

    Code:

# hooks.maxemaillines
#   Clip the email sent out to this number of lines.  If unset,
#   defaults to 500 lines.
[...]
# hooks.logfile
#   If set then the entire email content sent will be logged to this file.
#   If unset, no logging is done.
[...]
maxemaillines=$(git-repo-config --int hooks.maxemaillines)
logfile=$(git-repo-config hooks.logfile)
[...]
        # If a log file was specified, tee to the logfile, head, and then
        # sendmail, else just send the mail directly.
        if [ -n "$logfile" ]; then
                while read oldrev newrev refname
                do
                        generate_email $oldrev $newrev $refname |
                            /usr/bin/tee -a ${logfile} 2>/dev/null |
                            /usr/bin/head -${maxemaillines} |
                            /usr/sbin/sendmail -t $envelopesender
                done
        else
                while read oldrev newrev refname
                do
                        generate_email $oldrev $newrev $refname |
                            /usr/sbin/sendmail -t $envelopesender
                done
        fi

6.  Support an author filter.  I don't have this running currently,
    but I believe this feature was in the update script.  Basically
    (and here follows most likely confusion on my part) it allows you
    to filter your changes out of a change set so that when you
    push a change set that includes your changes to a hub, it only
    sends them once (here, I'm most certainly not expressing this
    correctly).

    Anyway, someone on the list suggested this, and it was in
    the update hook, like this:

authorfilter=$(git-repo-config hooks.authorfilter)
case "$refname_type" in
        "tracking branch"|branch)
                if expr "$oldrev" : '0*$' >/dev/null
                then
                        # If the old reference is "0000..0000" then this is a new branch
                        # and so oldrev is not valid
                        echo "  as a new  $refname_type"
                        echo "        to  $newrev ($newrev_type)"
                        echo ""
                        # This shows all log entries that are not already covered by
                        # another ref - i.e. commits that are now accessible from this
                        # ref that were previously not accessible
                        git-whatchanged -p ${authorfilter:+--author="$authorfilter"} --pretty $newref $(git-rev-parse --not --all)
                else
                        # oldrev is valid
                        oldrev_type=$(git-cat-file -t "$oldrev")

                        # Now the problem is for cases like this:
                        #   * --- * --- * --- * (oldrev)
                        #          \
                        #           * --- * --- * (newrev)
                        # i.e. there is no guarantee that newrev is a strict subset
                        # of oldrev - (would have required a force, but that's allowed).
                        # So, we can't simply say rev-list $oldrev..$newrev.  Instead
                        # we find the common base of the two revs and list from there
                        baserev=$(git-merge-base $oldrev $newrev)

                        # Commit with a parent
                        for rev in $(git-rev-list ${authorfilter:+--author="$authorfilter"} $newrev ^$baserev)
                        do
                                revtype=$(git-cat-file -t "$rev")
                                echo "       via  $rev ($revtype)"
                        done
                        if [ "$baserev" = "$oldrev" ]; then
                                echo "      from  $oldrev ($oldrev_type)"
                        else
                                echo "  based on  $baserev"
                                echo "      from  $oldrev ($oldrev_type)"
                                echo ""
                                echo "This ref update crossed a branch point; i.e. the old rev is not a strict subset"
                                echo "of the new rev.  This occurs, when you --force push a change in a situation"
                                echo "like this:"
                                echo ""
                                echo " * -- * -- B -- O -- O -- O ($oldrev)"
                                echo "            \\"
                                echo "             N -- N -- N ($newrev)"
                                echo ""
                                echo "Therefore, we assume that you've already had alert emails for all of the O"
                                echo "revisions, and now give you all the revisions in the N branch from the common"
                                echo "base, B ($baserev), up to the new revision."
                        fi
                        echo ""
                        echo "Diffstat:"
                        git-diff-tree --no-color --stat -M -C --find-copies-harder $newrev ^$baserev
                        echo ""
                        git-whatchanged -p ${authorfilter:+--author="$authorfilter"} --pretty $newrev ^$baserev
                fi
                ;;
        "annotated tag")

Ok, I think that's about it.  Andy, let me know if I can help
clarify anything.


Bill
-
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]

  Powered by Linux