summaryrefslogtreecommitdiffstats
path: root/crawl-ref/git-hooks
diff options
context:
space:
mode:
authorMatthew Cline <zelgadis@sourceforge.net>2009-11-01 20:24:41 -0800
committerMatthew Cline <zelgadis@sourceforge.net>2009-11-01 20:24:41 -0800
commit9683d9d03bd07ca4fdf46549fc662c06870c4227 (patch)
treed0acf0148322d5af877d72e8a55a10d4b0634a37 /crawl-ref/git-hooks
parent60df59bb213b7c2c3c6bc69ac9c87d28378db1ff (diff)
downloadcrawl-ref-9683d9d03bd07ca4fdf46549fc662c06870c4227.tar.gz
crawl-ref-9683d9d03bd07ca4fdf46549fc662c06870c4227.zip
Information on our git-hooks
Local copies of the git hooks, plus intructions on how to change them.
Diffstat (limited to 'crawl-ref/git-hooks')
-rw-r--r--crawl-ref/git-hooks/README.txt8
-rwxr-xr-xcrawl-ref/git-hooks/crawl-ref-cia289
-rwxr-xr-xcrawl-ref/git-hooks/crawl-ref-email677
-rwxr-xr-xcrawl-ref/git-hooks/git_buildbot.py311
-rwxr-xr-xcrawl-ref/git-hooks/post-receive13
-rwxr-xr-xcrawl-ref/git-hooks/update19
6 files changed, 1317 insertions, 0 deletions
diff --git a/crawl-ref/git-hooks/README.txt b/crawl-ref/git-hooks/README.txt
new file mode 100644
index 0000000000..a189aae7f0
--- /dev/null
+++ b/crawl-ref/git-hooks/README.txt
@@ -0,0 +1,8 @@
+This directory contains copies of the git hook scripts. The actual hooks can
+be edited by getting an SHH shell on the sourceforge machine:
+
+ ssh -t USER,crawl-ref@shell.sourceforge.net create
+
+See http://sourceforge.net/apps/trac/sourceforge/wiki/Shell%20service
+
+Once there, cd to "/home/scm_git/c/cr/crawl-ref/crawl-ref/hooks"
diff --git a/crawl-ref/git-hooks/crawl-ref-cia b/crawl-ref/git-hooks/crawl-ref-cia
new file mode 100755
index 0000000000..fd542653f4
--- /dev/null
+++ b/crawl-ref/git-hooks/crawl-ref-cia
@@ -0,0 +1,289 @@
+#!/usr/bin/perl -w
+#
+# ciabot -- Mail a git log message to a given address, for the purposes of CIA
+#
+# Loosely based on cvslog by Russ Allbery <rra@stanford.edu>
+# Copyright 1998 Board of Trustees, Leland Stanford Jr. University
+#
+# Copyright 2001, 2003, 2004, 2005 Petr Baudis <pasky@ucw.cz>
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License version 2, as published by the
+# Free Software Foundation.
+#
+# The master location of this file is in the Cogito repository
+# (see http://www.kernel.org/git/).
+#
+# This program is designed to run as the .git/hooks/post-commit hook. It takes
+# the commit information, massages it and mails it to the address given below.
+#
+# The calling convention of the post-commit hook is:
+#
+# .git/hooks/post-commit $commit_sha1 $branch_name
+#
+# If it does not work, try to disable $xml_rpc in the configuration section
+# below. Also, remember to make the hook file executable.
+#
+#
+# Note that you can (and it might be actually more desirable) also use this
+# script as the GIT update hook:
+#
+# refname=${1#refs/heads/}
+# [ "$refname" = "master" ] && refname=
+# oldhead=$2
+# newhead=$3
+# for merged in $(git-rev-list $newhead ^$oldhead | tac); do
+# /path/to/ciabot.pl $merged $refname
+# done
+#
+# This is useful when you use a remote repository that you only push to. The
+# update hook will be triggered each time you push into that repository, and
+# the pushed commits will be reported through CIA.
+
+use strict;
+use vars qw ($project $from_email $dest_email $noisy $rpc_uri $sendmail
+ $xml_rpc $ignore_regexp $alt_local_message_target);
+
+
+
+
+### Configuration
+
+# Project name (as known to CIA).
+$project = 'crawl-ref';
+
+# The from address in generated mails.
+$from_email = 'dshaligram@users.sourceforge.net';
+
+# Mail all reports to this address.
+$dest_email = 'cia@cia.vc';
+
+# If using XML-RPC, connect to this URI.
+$rpc_uri = 'http://cia.vc/RPC2';
+
+# Path to your USCD sendmail compatible binary (your mailer daemon created this
+# program somewhere).
+$sendmail = '/usr/sbin/sendmail';
+
+# If set, the script will send CIA the full commit message. If unset, only the
+# first line of the commit message will be sent.
+$noisy = 0;
+
+# This script can communicate with CIA either by mail or by an XML-RPC
+# interface. The XML-RPC interface is faster and more efficient, however you
+# need to have RPC::XML perl module installed, and some large CVS hosting sites
+# (like Savannah or Sourceforge) might not allow outgoing HTTP connections
+# while they allow outgoing mail. Also, this script will hang and eventually
+# not deliver the event at all if CIA server happens to be down, which is
+# unfortunately not an uncommon condition.
+$xml_rpc = 0;
+
+# This variable should contain a regexp, against which each file will be
+# checked, and if the regexp is matched, the file is ignored. This can be
+# useful if you do not want auto-updated files, such as e.g. ChangeLog, to
+# appear via CIA.
+#
+# The following example will make the script ignore all changes in two specific
+# files in two different modules, and everything concerning module 'admin':
+#
+# $ignore_regexp = "^(gentoo/Manifest|elinks/src/bfu/inphist.c|admin/)";
+$ignore_regexp = "";
+
+# It can be useful to also grab the generated XML message by some other
+# programs and e.g. autogenerate some content based on it. Here you can specify
+# a file to which it will be appended.
+$alt_local_message_target = "";
+
+
+
+
+### The code itself
+
+use vars qw ($commit $tree @parent $author $committer);
+use vars qw ($user $branch $rev @files $logmsg $message);
+my $line;
+
+
+
+### Input data loading
+
+
+# The commit stuff
+$commit = $ARGV[0];
+$branch = $ARGV[1];
+
+open COMMIT, "git-cat-file commit $commit|" or die "git-cat-file commit $commit: $!";
+my $state = 0;
+$logmsg = '';
+while (defined ($line = <COMMIT>)) {
+ if ($state == 1) {
+ $logmsg .= $line;
+ $noisy or $state++;
+ next;
+ } elsif ($state > 1) {
+ next;
+ }
+
+ chomp $line;
+ unless ($line) {
+ $state = 1;
+ next;
+ }
+
+ my ($key, $value) = split(/ /, $line, 2);
+ if ($key eq 'tree') {
+ $tree = $value;
+ } elsif ($key eq 'parent') {
+ push(@parent, $value);
+ } elsif ($key eq 'author') {
+ $author = $value;
+ } elsif ($key eq 'committer') {
+ $committer = $value;
+ }
+}
+close COMMIT;
+
+
+open DIFF, "git-diff-tree -r $parent[0] $tree|" or die "git-diff-tree $parent[0] $tree: $!";
+while (defined ($line = <DIFF>)) {
+ chomp $line;
+ my @f;
+ (undef, @f) = split(/\t/, $line, 2);
+ push (@files, @f);
+}
+close DIFF;
+
+
+# Figure out who is doing the update.
+# XXX: Too trivial this way?
+($user) = $author =~ /<(.*?)@/;
+
+
+$rev = substr($commit, 0, 12);
+
+
+
+
+### Remove to-be-ignored files
+
+@files = grep { $_ !~ m/$ignore_regexp/; } @files
+ if ($ignore_regexp);
+exit unless @files;
+
+
+
+### Compose the mail message
+
+
+my ($VERSION) = '1.0';
+my $ts = time;
+
+$message = <<EM
+<message>
+ <generator>
+ <name>CIA Perl client for Git</name>
+ <version>$VERSION</version>
+ </generator>
+ <source>
+ <project>$project</project>
+EM
+;
+$message .= " <branch>$branch</branch>" if ($branch);
+$message .= <<EM
+ </source>
+ <timestamp>
+ $ts
+ </timestamp>
+ <body>
+ <commit>
+ <author>$user</author>
+ <revision>$rev</revision>
+ <files>
+EM
+;
+
+foreach (@files) {
+ s/&/&amp;/g;
+ s/</&lt;/g;
+ s/>/&gt;/g;
+ $message .= " <file>$_</file>\n";
+}
+
+$logmsg =~ s/&/&amp;/g;
+$logmsg =~ s/</&lt;/g;
+$logmsg =~ s/>/&gt;/g;
+
+$message .= <<EM
+ </files>
+ <log>
+$logmsg
+ </log>
+ </commit>
+ </body>
+</message>
+EM
+;
+
+
+
+### Write the message to an alt-target
+
+if ($alt_local_message_target and open (ALT, ">>$alt_local_message_target")) {
+ print ALT $message;
+ close ALT;
+}
+
+
+
+### Send out the XML-RPC message
+
+
+if ($xml_rpc) {
+ # We gotta be careful from now on. We silence all the warnings because
+ # RPC::XML code is crappy and works with undefs etc.
+ $^W = 0;
+ $RPC::XML::ERROR if (0); # silence perl's compile-time warning
+
+ require RPC::XML;
+ require RPC::XML::Client;
+
+ my $rpc_client = new RPC::XML::Client $rpc_uri;
+ my $rpc_request = RPC::XML::request->new('hub.deliver', $message);
+ my $rpc_response = $rpc_client->send_request($rpc_request);
+
+ unless (ref $rpc_response) {
+ die "XML-RPC Error: $RPC::XML::ERROR\n";
+ }
+ exit;
+}
+
+
+
+### Send out the mail
+
+
+# Open our mail program
+
+open (MAIL, "| $sendmail -t -oi -oem") or die "Cannot execute $sendmail : " . ($?>>8);
+
+
+# The mail header
+
+print MAIL <<EOM;
+From: $from_email
+To: $dest_email
+Content-type: text/xml
+Subject: DeliverXML
+
+EOM
+
+print MAIL $message;
+
+
+# Close the mail
+
+close MAIL;
+die "$0: sendmail exit status " . ($? >> 8) . "\n" unless ($? == 0);
+
+# vi: set sw=2:
+
diff --git a/crawl-ref/git-hooks/crawl-ref-email b/crawl-ref/git-hooks/crawl-ref-email
new file mode 100755
index 0000000000..31577275a0
--- /dev/null
+++ b/crawl-ref/git-hooks/crawl-ref-email
@@ -0,0 +1,677 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Andy Parkins
+#
+# An example hook script to mail out commit update information. This hook
+# sends emails listing new revisions to the repository introduced by the
+# change being reported. The rule is that (for branch updates) each commit
+# will appear on one email and one email only.
+#
+# This hook is stored in the contrib/hooks directory. Your distribution
+# will have put this somewhere standard. You should make this script
+# executable then link to it in the repository you would like to use it in.
+# For example, on debian the hook is stored in
+# /usr/share/doc/git-core/contrib/hooks/post-receive-email:
+#
+# chmod a+x post-receive-email
+# cd /path/to/your/repository.git
+# ln -sf /usr/share/doc/git-core/contrib/hooks/post-receive-email hooks/post-receive
+#
+# This hook script assumes it is enabled on the central repository of a
+# project, with all users pushing only to it and not between each other. It
+# will still work if you don't operate in that style, but it would become
+# possible for the email to be from someone other than the person doing the
+# push.
+#
+# Config
+# ------
+# hooks.mailinglist
+# This is the list that all pushes will go to; leave it blank to not send
+# emails for every ref update.
+# hooks.announcelist
+# This is the list that all pushes of annotated tags will go to. Leave it
+# blank to default to the mailinglist field. The announce emails lists
+# the short log summary of the changes since the last annotated tag.
+# hooks.envelopesender
+# If set then the -f option is passed to sendmail to allow the envelope
+# sender address to be set
+# hooks.emailprefix
+# All emails have their subjects prefixed with this prefix, or "[SCM]"
+# if emailprefix is unset, to aid filtering
+# hooks.showrev
+# The shell command used to format each revision in the email, with
+# "%s" replaced with the commit id. Defaults to "git rev-list -1
+# --pretty %s", displaying the commit id, author, date and log
+# message. To list full patches separated by a blank line, you
+# could set this to "git show -C %s; echo".
+# To list a gitweb/cgit URL *and* a full patch for each change set, use this:
+# "t=%s; printf 'http://.../?id=%%s' \$t; echo;echo; git show -C \$t; echo"
+# Be careful if "..." contains things that will be expanded by shell "eval"
+# or printf.
+#
+# Notes
+# -----
+# All emails include the headers "X-Git-Refname", "X-Git-Oldrev",
+# "X-Git-Newrev", and "X-Git-Reftype" to enable fine tuned filtering and
+# give information for debugging.
+#
+
+# ---------------------------- Functions
+
+#
+# Top level email generation function. This decides what type of update
+# this is and calls the appropriate body-generation routine after outputting
+# the common header
+#
+# Note this function doesn't actually generate any email output, that is
+# taken care of by the functions it calls:
+# - generate_email_header
+# - generate_create_XXXX_email
+# - generate_update_XXXX_email
+# - generate_delete_XXXX_email
+# - generate_email_footer
+#
+generate_email()
+{
+ # --- Arguments
+ oldrev=$(git rev-parse $1)
+ newrev=$(git rev-parse $2)
+ refname="$3"
+
+ # --- Interpret
+ # 0000->1234 (create)
+ # 1234->2345 (update)
+ # 2345->0000 (delete)
+ if expr "$oldrev" : '0*$' >/dev/null
+ then
+ change_type="create"
+ else
+ if expr "$newrev" : '0*$' >/dev/null
+ then
+ change_type="delete"
+ else
+ change_type="update"
+ fi
+ fi
+
+ # --- Get the revision types
+ newrev_type=$(git cat-file -t $newrev 2> /dev/null)
+ oldrev_type=$(git cat-file -t "$oldrev" 2> /dev/null)
+ case "$change_type" in
+ create|update)
+ rev="$newrev"
+ rev_type="$newrev_type"
+ ;;
+ delete)
+ rev="$oldrev"
+ rev_type="$oldrev_type"
+ ;;
+ esac
+
+ # The revision type tells us what type the commit is, combined with
+ # the location of the ref we can decide between
+ # - working branch
+ # - tracking branch
+ # - unannoted tag
+ # - annotated tag
+ case "$refname","$rev_type" in
+ refs/tags/*,commit)
+ # un-annotated tag
+ refname_type="tag"
+ short_refname=${refname##refs/tags/}
+ ;;
+ refs/tags/*,tag)
+ # annotated tag
+ refname_type="annotated tag"
+ short_refname=${refname##refs/tags/}
+ # change recipients
+ if [ -n "$announcerecipients" ]; then
+ recipients="$announcerecipients"
+ fi
+ ;;
+ refs/heads/*,commit)
+ # branch
+ refname_type="branch"
+ short_refname=${refname##refs/heads/}
+ ;;
+ refs/remotes/*,commit)
+ # tracking branch
+ refname_type="tracking branch"
+ short_refname=${refname##refs/remotes/}
+ echo >&2 "*** Push-update of tracking branch, $refname"
+ echo >&2 "*** - no email generated."
+ exit 0
+ ;;
+ *)
+ # Anything else (is there anything else?)
+ echo >&2 "*** Unknown type of update to $refname ($rev_type)"
+ echo >&2 "*** - no email generated"
+ exit 1
+ ;;
+ esac
+
+ # Check if we've got anyone to send to
+ if [ -z "$recipients" ]; then
+ case "$refname_type" in
+ "annotated tag")
+ config_name="hooks.announcelist"
+ ;;
+ *)
+ config_name="hooks.mailinglist"
+ ;;
+ esac
+ echo >&2 "*** $config_name is not set so no email will be sent"
+ echo >&2 "*** for $refname update $oldrev->$newrev"
+ exit 0
+ fi
+
+ # Email parameters
+ # The email subject will contain the best description of the ref
+ # that we can build from the parameters
+ describe=$(git describe $rev 2>/dev/null)
+ if [ -z "$describe" ]; then
+ describe=$rev
+ fi
+
+ generate_email_header
+
+ # Call the correct body generation function
+ fn_name=general
+ case "$refname_type" in
+ "tracking branch"|branch)
+ fn_name=branch
+ ;;
+ "annotated tag")
+ fn_name=atag
+ ;;
+ esac
+ generate_${change_type}_${fn_name}_email
+
+ generate_email_footer
+}
+
+generate_email_header()
+{
+ # --- Email (all stdout will be the email)
+ # Generate header
+ cat <<-EOF
+ To: $recipients
+ Subject: ${emailprefix} ($refname_type: $short_refname) ${change_type}d. $describe
+ X-Git-Refname: $refname
+ X-Git-Reftype: $refname_type
+ X-Git-Oldrev: $oldrev
+ X-Git-Newrev: $newrev
+
+ EOF
+}
+
+generate_email_footer()
+{
+ SPACE=" "
+ cat <<-EOF
+
+ --${SPACE}
+ $projectdesc
+ EOF
+}
+
+# --------------- Branches
+
+#
+# Called for the creation of a branch
+#
+generate_create_branch_email()
+{
+ # This is a new branch and so oldrev is not valid
+ echo " at $newrev ($newrev_type)"
+ echo ""
+
+ echo $LOGBEGIN
+ show_new_revisions
+ echo $LOGEND
+}
+
+#
+# Called for the change of a pre-existing branch
+#
+generate_update_branch_email()
+{
+ # Consider this:
+ # 1 --- 2 --- O --- X --- 3 --- 4 --- N
+ #
+ # O is $oldrev for $refname
+ # N is $newrev for $refname
+ # X is a revision pointed to by some other ref, for which we may
+ # assume that an email has already been generated.
+ # In this case we want to issue an email containing only revisions
+ # 3, 4, and N. Given (almost) by
+ #
+ # git rev-list N ^O --not --all
+ #
+ # The reason for the "almost", is that the "--not --all" will take
+ # precedence over the "N", and effectively will translate to
+ #
+ # git rev-list N ^O ^X ^N
+ #
+ # So, we need to build up the list more carefully. git rev-parse
+ # will generate a list of revs that may be fed into git rev-list.
+ # We can get it to make the "--not --all" part and then filter out
+ # the "^N" with:
+ #
+ # git rev-parse --not --all | grep -v N
+ #
+ # Then, using the --stdin switch to git rev-list we have effectively
+ # manufactured
+ #
+ # git rev-list N ^O ^X
+ #
+ # This leaves a problem when someone else updates the repository
+ # while this script is running. Their new value of the ref we're
+ # working on would be included in the "--not --all" output; and as
+ # our $newrev would be an ancestor of that commit, it would exclude
+ # all of our commits. What we really want is to exclude the current
+ # value of $refname from the --not list, rather than N itself. So:
+ #
+ # git rev-parse --not --all | grep -v $(git rev-parse $refname)
+ #
+ # Get's us to something pretty safe (apart from the small time
+ # between refname being read, and git rev-parse running - for that,
+ # I give up)
+ #
+ #
+ # Next problem, consider this:
+ # * --- B --- * --- O ($oldrev)
+ # \
+ # * --- X --- * --- N ($newrev)
+ #
+ # That is to say, there is no guarantee that oldrev is a strict
+ # subset of newrev (it 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.
+ #
+ # As above, we need to take into account the presence of X; if
+ # another branch is already in the repository and points at some of
+ # the revisions that we are about to output - we don't want them.
+ # The solution is as before: git rev-parse output filtered.
+ #
+ # Finally, tags: 1 --- 2 --- O --- T --- 3 --- 4 --- N
+ #
+ # Tags pushed into the repository generate nice shortlog emails that
+ # summarise the commits between them and the previous tag. However,
+ # those emails don't include the full commit messages that we output
+ # for a branch update. Therefore we still want to output revisions
+ # that have been output on a tag email.
+ #
+ # Luckily, git rev-parse includes just the tool. Instead of using
+ # "--all" we use "--branches"; this has the added benefit that
+ # "remotes/" will be ignored as well.
+
+ # List all of the revisions that were removed by this update, in a
+ # fast forward update, this list will be empty, because rev-list O
+ # ^N is empty. For a non fast forward, O ^N is the list of removed
+ # revisions
+ fast_forward=""
+ rev=""
+ for rev in $(git rev-list $newrev..$oldrev)
+ do
+ revtype=$(git cat-file -t "$rev")
+ echo " discards $rev ($revtype)"
+ done
+ if [ -z "$rev" ]; then
+ fast_forward=1
+ fi
+
+ # List all the revisions from baserev to newrev in a kind of
+ # "table-of-contents"; note this list can include revisions that
+ # have already had notification emails and is present to show the
+ # full detail of the change from rolling back the old revision to
+ # the base revision and then forward to the new revision
+ for rev in $(git rev-list $oldrev..$newrev)
+ do
+ revtype=$(git cat-file -t "$rev")
+ echo " via $rev ($revtype)"
+ done
+
+ if [ "$fast_forward" ]; then
+ echo " from $oldrev ($oldrev_type)"
+ else
+ # 1. Existing revisions were removed. In this case newrev
+ # is a subset of oldrev - this is the reverse of a
+ # fast-forward, a rewind
+ # 2. New revisions were added on top of an old revision,
+ # this is a rewind and addition.
+
+ # (1) certainly happened, (2) possibly. When (2) hasn't
+ # happened, we set a flag to indicate that no log printout
+ # is required.
+
+ echo ""
+
+ # Find the common ancestor of the old and new revisions and
+ # compare it with newrev
+ baserev=$(git merge-base $oldrev $newrev)
+ rewind_only=""
+ if [ "$baserev" = "$newrev" ]; then
+ echo "This update discarded existing revisions and left the branch pointing at"
+ echo "a previous point in the repository history."
+ echo ""
+ echo " * -- * -- N ($newrev)"
+ echo " \\"
+ echo " O -- O -- O ($oldrev)"
+ echo ""
+ echo "The removed revisions are not necessarilly gone - if another reference"
+ echo "still refers to them they will stay in the repository."
+ rewind_only=1
+ else
+ echo "This update added new revisions after undoing existing revisions. That is"
+ echo "to say, the old revision is not a strict subset of the new revision. This"
+ echo "situation occurs when you --force push a change and generate a repository"
+ echo "containing something like this:"
+ echo ""
+ echo " * -- * -- B -- O -- O -- O ($oldrev)"
+ echo " \\"
+ echo " N -- N -- N ($newrev)"
+ echo ""
+ echo "When this happens we assume that you've already had alert emails for all"
+ echo "of the O revisions, and so we here report only the revisions in the N"
+ echo "branch from the common base, B."
+ fi
+ fi
+
+ echo ""
+ if [ -z "$rewind_only" ]; then
+ echo $LOGBEGIN
+ show_new_revisions
+
+ # XXX: Need a way of detecting whether git rev-list actually
+ # outputted anything, so that we can issue a "no new
+ # revisions added by this update" message
+
+ echo $LOGEND
+ else
+ echo "No new revisions were added by this update."
+ fi
+
+ # The diffstat is shown from the old revision to the new revision.
+ # This is to show the truth of what happened in this change.
+ # There's no point showing the stat from the base to the new
+ # revision because the base is effectively a random revision at this
+ # point - the user will be interested in what this revision changed
+ # - including the undoing of previous revisions in the case of
+ # non-fast forward updates.
+ echo ""
+ echo "Summary of changes:"
+ git diff-tree -u --stat --summary --find-copies-harder $oldrev..$newrev
+}
+
+#
+# Called for the deletion of a branch
+#
+generate_delete_branch_email()
+{
+ echo " was $oldrev"
+ echo ""
+ echo $LOGEND
+ git show -s --pretty=oneline $oldrev
+ echo $LOGEND
+}
+
+# --------------- Annotated tags
+
+#
+# Called for the creation of an annotated tag
+#
+generate_create_atag_email()
+{
+ echo " at $newrev ($newrev_type)"
+
+ generate_atag_email
+}
+
+#
+# Called for the update of an annotated tag (this is probably a rare event
+# and may not even be allowed)
+#
+generate_update_atag_email()
+{
+ echo " to $newrev ($newrev_type)"
+ echo " from $oldrev (which is now obsolete)"
+
+ generate_atag_email
+}
+
+#
+# Called when an annotated tag is created or changed
+#
+generate_atag_email()
+{
+ # Use git for-each-ref to pull out the individual fields from the
+ # tag
+ eval $(git for-each-ref --shell --format='
+ tagobject=%(*objectname)
+ tagtype=%(*objecttype)
+ tagger=%(taggername)
+ tagged=%(taggerdate)' $refname
+ )
+
+ echo " tagging $tagobject ($tagtype)"
+ case "$tagtype" in
+ commit)
+
+ # If the tagged object is a commit, then we assume this is a
+ # release, and so we calculate which tag this tag is
+ # replacing
+ prevtag=$(git describe --abbrev=0 $newrev^ 2>/dev/null)
+
+ if [ -n "$prevtag" ]; then
+ echo " replaces $prevtag"
+ fi
+ ;;
+ *)
+ echo " length $(git cat-file -s $tagobject) bytes"
+ ;;
+ esac
+ echo " tagged by $tagger"
+ echo " on $tagged"
+
+ echo ""
+ echo $LOGBEGIN
+
+ # Show the content of the tag message; this might contain a change
+ # log or release notes so is worth displaying.
+ git cat-file tag $newrev | sed -e '1,/^$/d'
+
+ echo ""
+ case "$tagtype" in
+ commit)
+ # Only commit tags make sense to have rev-list operations
+ # performed on them
+ if [ -n "$prevtag" ]; then
+ # Show changes since the previous release
+ git rev-list --pretty=short "$prevtag..$newrev" | git shortlog
+ else
+ # No previous tag, show all the changes since time
+ # began
+ git rev-list --pretty=short $newrev | git shortlog
+ fi
+ ;;
+ *)
+ # XXX: Is there anything useful we can do for non-commit
+ # objects?
+ ;;
+ esac
+
+ echo $LOGEND
+}
+
+#
+# Called for the deletion of an annotated tag
+#
+generate_delete_atag_email()
+{
+ echo " was $oldrev"
+ echo ""
+ echo $LOGEND
+ git show -s --pretty=oneline $oldrev
+ echo $LOGEND
+}
+
+# --------------- General references
+
+#
+# Called when any other type of reference is created (most likely a
+# non-annotated tag)
+#
+generate_create_general_email()
+{
+ echo " at $newrev ($newrev_type)"
+
+ generate_general_email
+}
+
+#
+# Called when any other type of reference is updated (most likely a
+# non-annotated tag)
+#
+generate_update_general_email()
+{
+ echo " to $newrev ($newrev_type)"
+ echo " from $oldrev"
+
+ generate_general_email
+}
+
+#
+# Called for creation or update of any other type of reference
+#
+generate_general_email()
+{
+ # Unannotated tags are more about marking a point than releasing a
+ # version; therefore we don't do the shortlog summary that we do for
+ # annotated tags above - we simply show that the point has been
+ # marked, and print the log message for the marked point for
+ # reference purposes
+ #
+ # Note this section also catches any other reference type (although
+ # there aren't any) and deals with them in the same way.
+
+ echo ""
+ if [ "$newrev_type" = "commit" ]; then
+ echo $LOGBEGIN
+ git show --no-color --root -s --pretty=medium $newrev
+ echo $LOGEND
+ else
+ # What can we do here? The tag marks an object that is not
+ # a commit, so there is no log for us to display. It's
+ # probably not wise to output git cat-file as it could be a
+ # binary blob. We'll just say how big it is
+ echo "$newrev is a $newrev_type, and is $(git cat-file -s $newrev) bytes long."
+ fi
+}
+
+#
+# Called for the deletion of any other type of reference
+#
+generate_delete_general_email()
+{
+ echo " was $oldrev"
+ echo ""
+ echo $LOGEND
+ git show -s --pretty=oneline $oldrev
+ echo $LOGEND
+}
+
+
+# --------------- Miscellaneous utilities
+
+#
+# Show new revisions as the user would like to see them in the email.
+#
+show_new_revisions()
+{
+ # 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
+ # (see generate_update_branch_email for the explanation of this
+ # command)
+
+ # Revision range passed to rev-list differs for new vs. updated
+ # branches.
+ if [ "$change_type" = create ]
+ then
+ # Show all revisions exclusive to this (new) branch.
+ revspec=$newrev
+ else
+ # Branch update; show revisions not part of $oldrev.
+ revspec=$oldrev..$newrev
+ fi
+
+ other_branches=$(git for-each-ref --format='%(refname)' refs/heads/ |
+ grep -F -v $refname)
+ git rev-parse --not $other_branches |
+ if [ -z "$custom_showrev" ]
+ then
+ git rev-list --pretty --stdin $revspec
+ else
+ git rev-list --stdin $revspec |
+ while read onerev
+ do
+ eval $(printf "$custom_showrev" $onerev)
+ done
+ fi
+}
+
+
+send_mail()
+{
+ if [ -n "$envelopesender" ]; then
+ /usr/sbin/sendmail -t -f "$envelopesender"
+ else
+ /usr/sbin/sendmail -t
+ fi
+}
+
+# ---------------------------- main()
+
+# --- Constants
+LOGBEGIN="-----------------------------------------------------------------------"
+LOGEND="-----------------------------------------------------------------------"
+
+# --- Config
+# Set GIT_DIR either from the working directory, or from the environment
+# variable.
+GIT_DIR=$(git rev-parse --git-dir 2>/dev/null)
+if [ -z "$GIT_DIR" ]; then
+ echo >&2 "fatal: post-receive: GIT_DIR not set"
+ exit 1
+fi
+
+projectdesc=$(sed -ne '1p' "$GIT_DIR/description")
+# Check if the description is unchanged from it's default, and shorten it to
+# a more manageable length if it is
+if expr "$projectdesc" : "Unnamed repository.*$" >/dev/null
+then
+ projectdesc="UNNAMED PROJECT"
+fi
+
+recipients=$(git config hooks.mailinglist)
+announcerecipients=$(git config hooks.announcelist)
+envelopesender=$(git config hooks.envelopesender)
+emailprefix=$(git config hooks.emailprefix || echo '[SCM] ')
+custom_showrev=$(git config hooks.showrev)
+
+# --- Main loop
+# Allow dual mode: run from the command line just like the update hook, or
+# if no arguments are given then run as a hook script
+if [ -n "$1" -a -n "$2" -a -n "$3" ]; then
+ # Output to the terminal in command line mode - if someone wanted to
+ # resend an email; they could redirect the output to sendmail
+ # themselves
+ PAGER= generate_email $2 $3 $1
+else
+ while read oldrev newrev refname
+ do
+ generate_email $oldrev $newrev $refname | send_mail
+ done
+fi
diff --git a/crawl-ref/git-hooks/git_buildbot.py b/crawl-ref/git-hooks/git_buildbot.py
new file mode 100755
index 0000000000..50d07f45d7
--- /dev/null
+++ b/crawl-ref/git-hooks/git_buildbot.py
@@ -0,0 +1,311 @@
+#! /usr/bin/env python
+
+# This script expects one line for each new revision on the form
+# <oldrev> <newrev> <refname>
+#
+# For example:
+# aa453216d1b3e49e7f6f98441fa56946ddcd6a20
+# 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master
+#
+# Each of these changes will be passed to the buildbot server along
+# with any other change information we manage to extract from the
+# repository.
+#
+# This script is meant to be run from hooks/post-receive in the git
+# repository. It can also be run at client side with hooks/post-merge
+# after using this wrapper:
+
+#!/bin/sh
+# PRE=$(git rev-parse 'HEAD@{1}')
+# POST=$(git rev-parse HEAD)
+# SYMNAME=$(git rev-parse --symbolic-full-name HEAD)
+# echo "$PRE $POST $SYMNAME" | git_buildbot.py
+#
+# Largely based on contrib/hooks/post-receive-email from git.
+
+import commands
+import logging
+import os
+import re
+import sys
+
+from twisted.spread import pb
+from twisted.cred import credentials
+from twisted.internet import reactor
+
+from buildbot.scripts import runner
+from optparse import OptionParser
+
+# Modify this to fit your setup, or pass in --master server:host on the
+# command line
+
+master = "xerxes.uplinklabs.net:9989"
+
+# When sending the notification, send this category iff
+# it's set (via --category)
+
+category = None
+
+
+# The GIT_DIR environment variable must have been set up so that any
+# git commands that are executed will operate on the repository we're
+# installed in.
+
+changes = []
+
+
+def connectFailed(error):
+ logging.error("Could not connect to %s: %s"
+ % (master, error.getErrorMessage()))
+ return error
+
+
+def addChange(dummy, remote, changei):
+ logging.debug("addChange %s, %s" % (repr(remote), repr(changei)))
+ try:
+ c = changei.next()
+ except StopIteration:
+ remote.broker.transport.loseConnection()
+ return None
+
+ logging.info("New revision: %s" % c['revision'][:8])
+ for key, value in c.iteritems():
+ logging.debug(" %s: %s" % (key, value))
+
+ d = remote.callRemote('addChange', c)
+ d.addCallback(addChange, remote, changei)
+ return d
+
+
+def connected(remote):
+ return addChange(None, remote, changes.__iter__())
+
+
+def grab_commit_info(c, rev):
+ # Extract information about committer and files using git show
+ f = os.popen("git show --raw --pretty=full %s" % rev, 'r')
+
+ files = []
+
+ while True:
+ line = f.readline()
+ if not line:
+ break
+
+ m = re.match(r"^:.*[MAD]\s+(.+)$", line)
+ if m:
+ logging.debug("Got file: %s" % m.group(1))
+ files.append(m.group(1))
+ continue
+
+ m = re.match(r"^Author:\s+(.+)$", line)
+ if m:
+ logging.debug("Got author: %s" % m.group(1))
+ c['who'] = m.group(1)
+
+ if re.match(r"^Merge: .*$", line):
+ files.append('merge')
+
+ c['files'] = files
+ status = f.close()
+ if status:
+ logging.warning("git show exited with status %d" % status)
+
+
+def gen_changes(input, branch):
+ while True:
+ line = input.readline()
+ if not line:
+ break
+
+ logging.debug("Change: %s" % line)
+
+ m = re.match(r"^([0-9a-f]+) (.*)$", line.strip())
+ c = {'revision': m.group(1),
+ 'comments': m.group(2),
+ 'branch': branch,
+ }
+ if category:
+ c['category'] = category
+ grab_commit_info(c, m.group(1))
+ changes.append(c)
+
+
+def gen_create_branch_changes(newrev, refname, branch):
+ # A new branch has been created. Generate changes for everything
+ # up to `newrev' which does not exist in any branch but `refname'.
+ #
+ # Note that this may be inaccurate if two new branches are created
+ # at the same time, pointing to the same commit, or if there are
+ # commits that only exists in a common subset of the new branches.
+
+ logging.info("Branch `%s' created" % branch)
+
+ f = os.popen("git rev-parse --not --branches"
+ + "| grep -v $(git rev-parse %s)" % refname
+ + "| git rev-list --reverse --pretty=oneline --stdin %s" % newrev,
+ 'r')
+
+ gen_changes(f, branch)
+
+ status = f.close()
+ if status:
+ logging.warning("git rev-list exited with status %d" % status)
+
+
+def gen_update_branch_changes(oldrev, newrev, refname, branch):
+ # A branch has been updated. If it was a fast-forward update,
+ # generate Change events for everything between oldrev and newrev.
+ #
+ # In case of a forced update, first generate a "fake" Change event
+ # rewinding the branch to the common ancestor of oldrev and
+ # newrev. Then, generate Change events for each commit between the
+ # common ancestor and newrev.
+
+ logging.info("Branch `%s' updated %s .. %s"
+ % (branch, oldrev[:8], newrev[:8]))
+
+ baserev = commands.getoutput("git merge-base %s %s" % (oldrev, newrev))
+ logging.debug("oldrev=%s newrev=%s baserev=%s" % (oldrev, newrev, baserev))
+ if baserev != oldrev:
+ c = {'revision': baserev,
+ 'comments': "Rewind branch",
+ 'branch': branch,
+ 'who': "dummy",
+ }
+ logging.info("Branch %s was rewound to %s" % (branch, baserev[:8]))
+ files = []
+ f = os.popen("git diff --raw %s..%s" % (oldrev, baserev), 'r')
+ while True:
+ line = f.readline()
+ if not line:
+ break
+
+ file = re.match(r"^:.*[MAD]\s*(.+)$", line).group(1)
+ logging.debug(" Rewound file: %s" % file)
+ files.append(file)
+
+ status = f.close()
+ if status:
+ logging.warning("git diff exited with status %d" % status)
+
+ if category:
+ c['category'] = category
+
+ if files:
+ c['files'] = files
+ changes.append(c)
+
+ if newrev != baserev:
+ # Not a pure rewind
+ f = os.popen("git rev-list --reverse --pretty=oneline %s..%s"
+ % (baserev, newrev), 'r')
+ gen_changes(f, branch)
+
+ status = f.close()
+ if status:
+ logging.warning("git rev-list exited with status %d" % status)
+
+
+def cleanup(res):
+ reactor.stop()
+
+
+def process_changes():
+ # Read branch updates from stdin and generate Change events
+ while True:
+ line = sys.stdin.readline()
+ if not line:
+ break
+
+ [oldrev, newrev, refname] = line.split(None, 2)
+
+ # We only care about regular heads, i.e. branches
+ m = re.match(r"^refs\/heads\/(.+)$", refname)
+ if not m:
+ logging.info("Ignoring refname `%s': Not a branch" % refname)
+ continue
+
+ branch = m.group(1)
+
+ # Find out if the branch was created, deleted or updated. Branches
+ # being deleted aren't really interesting.
+ if re.match(r"^0*$", newrev):
+ logging.info("Branch `%s' deleted, ignoring" % branch)
+ continue
+ elif re.match(r"^0*$", oldrev):
+ gen_create_branch_changes(newrev, refname, branch)
+ else:
+ gen_update_branch_changes(oldrev, newrev, refname, branch)
+
+ # Submit the changes, if any
+ if not changes:
+ logging.warning("No changes found")
+ return
+
+ host, port = master.split(':')
+ port = int(port)
+
+ f = pb.PBClientFactory()
+ d = f.login(credentials.UsernamePassword("change", "changepw"))
+ reactor.connectTCP(host, port, f)
+
+ d.addErrback(connectFailed)
+ d.addCallback(connected)
+ d.addBoth(cleanup)
+
+ reactor.run()
+
+
+def parse_options():
+ parser = OptionParser()
+ parser.add_option("-l", "--logfile", action="store", type="string",
+ help="Log to the specified file")
+ parser.add_option("-v", "--verbose", action="count",
+ help="Be more verbose. Ignored if -l is not specified.")
+ master_help = ("Build master to push to. Default is %(master)s" %
+ { 'master' : master })
+ parser.add_option("-m", "--master", action="store", type="string",
+ help=master_help)
+ parser.add_option("-c", "--category", action="store",
+ type="string", help="Scheduler category to notify.")
+ options, args = parser.parse_args()
+ return options
+
+
+# Log errors and critical messages to stderr. Optionally log
+# information to a file as well (we'll set that up later.)
+stderr = logging.StreamHandler(sys.stderr)
+fmt = logging.Formatter("git_buildbot: %(levelname)s: %(message)s")
+stderr.setLevel(logging.ERROR)
+stderr.setFormatter(fmt)
+logging.getLogger().addHandler(stderr)
+logging.getLogger().setLevel(logging.DEBUG)
+
+try:
+ options = parse_options()
+ level = logging.WARNING
+ if options.verbose:
+ level -= 10 * options.verbose
+ if level < 0:
+ level = 0
+
+ if options.logfile:
+ logfile = logging.FileHandler(options.logfile)
+ logfile.setLevel(level)
+ fmt = logging.Formatter("%(asctime)s %(levelname)s: %(message)s")
+ logfile.setFormatter(fmt)
+ logging.getLogger().addHandler(logfile)
+
+ if options.master:
+ master=options.master
+
+ if options.category:
+ category = options.category
+
+ process_changes()
+except SystemExit:
+ pass
+except:
+ logging.exception("Unhandled exception")
+ sys.exit(1)
diff --git a/crawl-ref/git-hooks/post-receive b/crawl-ref/git-hooks/post-receive
new file mode 100755
index 0000000000..5125e73991
--- /dev/null
+++ b/crawl-ref/git-hooks/post-receive
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+GIT_DIR=$(git rev-parse --git-dir 2>/dev/null)
+if [ -z "$GIT_DIR" ]; then
+ echo >&2 "fatal: post-receive: GIT_DIR not set"
+ exit 1
+fi
+
+$GIT_DIR/hooks/crawl-ref-email "$@"
+#$GIT_DIR/hooks/crawl-ref-cia "$@"
+
+# BuildBot
+#$GIT_DIR/hooks/git_buildbot.py "$@"
diff --git a/crawl-ref/git-hooks/update b/crawl-ref/git-hooks/update
new file mode 100755
index 0000000000..a38deb2499
--- /dev/null
+++ b/crawl-ref/git-hooks/update
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+# Note that you can (and it might be actually more desirable) also use this
+# script as the GIT update hook:
+#
+
+GIT_DIR=$(git rev-parse --git-dir 2>/dev/null)
+if [ -z "$GIT_DIR" ]; then
+ echo >&2 "fatal: post-receive: GIT_DIR not set"
+ exit 1
+fi
+
+refname=${1#refs/heads/}
+[ "$refname" = "master" ] && refname=
+oldhead=$2
+newhead=$3
+for merged in $(git-rev-list $newhead ^$oldhead | tac); do
+ $GIT_DIR/hooks/crawl-ref-cia $merged $refname
+done