Permalink
Browse files

Add git email notification post-receive hook.

Import a modified version of the git-notify post-receive hook from the
Wine project. This hook can be configured using various notify.* config
settings. See the source for details.
  • Loading branch information...
1 parent be0cff3 commit 5d3a98d4c331f3a72b3bccf7ce1f2b58136a2af2 @bmeurer committed Nov 21, 2009
Showing with 297 additions and 0 deletions.
  1. +1 −0 Makefile
  2. +296 −0 post-receive.d/99gitnotify
View
@@ -25,6 +25,7 @@ install: all
done
install -d -m 0755 $(PREFIX)/share/git-generic-hooks/templates/info
install -c -m 0644 exclude $(PREFIX)/share/git-generic-hooks/templates/info/exclude
+ install -c -m 0755 post-receive.d/99gitnotify $(PREFIX)/share/git-generic-hooks/post-receive.d/99gitnotify
git-generic-hook: git-generic-hook.c
$(CC) $(CFLAGS) -o $@ $<
View
@@ -0,0 +1,296 @@
+#!/usr/bin/perl -w
+#
+# "post-receive" hook script to send git commit notifications
+#
+# Copyright (c) 2009 Benedikt Meurer <benedikt.meurer@googlemail.com>
+# Copyright (c) 2005 Alexandre Julliard
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+
+use Cwd 'realpath';
+use Encode 'encode';
+use open ':utf8';
+use strict;
+
+binmode STDIN, ':utf8';
+binmode STDOUT, ':utf8';
+
+sub git_config($);
+sub git_object_info($);
+sub git_repository();
+
+
+################
+### SETTINGS ###
+################
+
+# Path to sendmail
+my $sendmail = '/usr/sbin/sendmail';
+
+
+################################
+### REPOSITORY CONFIGURATION ###
+################################
+
+# cgit base URL
+my $cgit_baseurl = git_config("cgit.baseurl");
+
+# default repository name
+my $notify_repository = git_repository();
+
+# mailinglist to send notifications to
+my $notify_mailinglist = git_config("notify.mailinglist");
+
+# subject prefix for notification emails
+my $notify_emailprefix = git_config("notify.emailprefix") || "";
+
+# branches to include
+my @notify_include = split /\s+/, git_config("notify.include") || "";
+
+# branches to exclude
+my @notify_exclude = split /\s+/, git_config("notify.exclude") || "";
+
+
+#################
+### FUNCTIONS ###
+#################
+
+# fetch a parameter from the git config file
+sub git_config($)
+{
+ my ($key) = @_;
+
+ open GIT_CONFIG, "-|" or exec "git", "config", "$key";
+ my $value = <GIT_CONFIG>;
+ chomp $value if $value;
+ close GIT_CONFIG or $value = undef;
+ return $value;
+}
+
+# format an integer data + timezone as string (see git's date.c)
+sub git_format_date($$)
+{
+ my ($time,$tz) = @_;
+
+ if ($tz < 0)
+ {
+ my $minutes = (-$tz / 100) * 60 + (-$tz % 100);
+ $time -= $minutes * 60;
+ }
+ else
+ {
+ my $minutes = ($tz / 100) * 60 + ($tz % 100);
+ $time += $minutes * 60;
+ }
+ return gmtime($time) . sprintf " %+05d", $tz;
+}
+
+# extract the information from a commit or tag object and return a hash
+# containing the various fields
+sub git_object_info($)
+{
+ my ($object) = @_;
+ my %info = ();
+ my @log = ();
+ my $do_log = 0;
+
+ open GIT_CAT_FILE, "-|" or exec "git", "cat-file", "-t", $object
+ or die "Failed to execute git-cat-file";
+ my $type = <GIT_CAT_FILE>;
+ chomp $type if $type;
+ close GIT_CAT_FILE or $type = undef;
+
+ open GIT_CAT_FILE, "-|" or exec "git", "cat-file", $type, $object
+ or die "Failed to execute git-cat-file";
+ while (<GIT_CAT_FILE>)
+ {
+ chomp;
+ if ($do_log)
+ {
+ last if /^-----BEGIN PGP SIGNATURE-----/;
+ push @log, $_;
+ }
+ elsif (/^(author|committer|tagger) ((.*)(<.*>)) (\d+) ([+-]\d+)$/)
+ {
+ $info{$1} = $2;
+ $info{$1 . '_name'} = $3;
+ $info{$1 . '_email'} = $4;
+ $info{$1 . '_date'} = $5;
+ $info{$1 . '_tz'} = $6;
+ }
+ elsif (/^tag (.*)$/)
+ {
+ $info{'tag'} = $1;
+ }
+ elsif (/^$/)
+ {
+ $do_log = 1;
+ }
+ }
+ close GIT_CAT_FILE;
+
+ $info{'type'} = $type;
+ $info{'log'} = \@log;
+ return %info;
+}
+
+# get the default repository name
+sub git_repository()
+{
+ my $dir = `git rev-parse --git-dir`;
+ chomp $dir;
+ $dir = realpath($dir);
+ $dir =~ s/(.*?)((\.git\/)?\.git)$/$1/;
+ $dir =~ s/(.*)\/([^\/]+)\/?$/$2/;
+ return $dir;
+}
+
+# send a prepared commit notice to a mailinglist
+sub git_send_mail($@)
+{
+ my ($subject, @text) = @_;
+
+ $subject = encode("MIME-Q", $subject);
+ open SENDMAIL, "|$sendmail -t" or die "Failed to execute $sendmail";
+ print SENDMAIL <<EOF;
+To: $notify_mailinglist
+Subject: $notify_emailprefix$subject
+Sender: $notify_mailinglist
+Content-Type: text/plain; charset=UTF-8
+EOF
+ print SENDMAIL "\n";
+ print SENDMAIL join("\n", @text);
+ close SENDMAIL;
+}
+
+# send a commit notice to a mailinglist
+sub git_send_notice($$)
+{
+ my ($ref, $object) = @_;
+ my %info = git_object_info($object);
+ my @notice = ();
+ my $subject;
+
+ if ($info{'type'} eq 'tag')
+ {
+ push @notice,
+ "Module: $notify_repository",
+ "Branch: $ref",
+ "Tag: $object";
+ if ($cgit_baseurl)
+ {
+ push @notice,
+ "URL: "
+ . "$cgit_baseurl/$notify_repository/tag/?id="
+ . "$object";
+ }
+ push @notice,
+ "Tagger: " .
+ $info{'tagger'},
+ "Date: "
+ . git_format_date($info{'tagger_date'},$info{'tagger_tz'}),
+ '',
+ join "\n", @{$info{'log'}};
+
+ $subject = "<$notify_repository:$ref> "
+ . 'Tag ' . $info{'tag'} . ' : '
+ . ${$info{'log'}}[0];
+ }
+ else
+ {
+ push @notice,
+ "Module: $notify_repository",
+ "Branch: $ref",
+ "Commit: $object";
+ if ($cgit_baseurl)
+ {
+ push @notice,
+ "URL: "
+ . "$cgit_baseurl/$notify_repository/commit/?id="
+ . "$object";
+ }
+ push @notice,
+ "Author: " . $info{'author'},
+ "Date: "
+ . git_format_date($info{'author_date'},$info{'author_tz'}),
+ '',
+ ' ' . (join "\n ", @{$info{'log'}}),
+ '';
+
+ open GIT_DIFF_TREE, "-|"
+ or exec 'git', 'diff-tree', '--stat', '-M',
+ '--no-commit-id', $object
+ or die "Failed to execute git-diff-tree";
+ push @notice, join('', <GIT_DIFF_TREE>);
+ close GIT_DIFF_TREE;
+
+ open GIT_DIFF_TREE, "-|"
+ or exec 'git', 'diff-tree', '-p', '-M',
+ '--no-commit-id', $object,
+ or die "Failed to execute git-diff-tree";
+ push @notice, join('', <GIT_DIFF_TREE>);
+ close GIT_DIFF_TREE;
+
+ $subject = "<$notify_repository:$ref> "
+ . ${$info{'log'}}[0];
+ }
+
+ git_send_mail($subject, @notice);
+}
+
+# send all the notices to the mailinglist
+sub git_send_all_notices($$$)
+{
+ my ($old, $new, $ref) = @_;
+
+ $ref =~ s/^refs\/heads\///;
+ return if (@notify_include && !grep {$_ eq $ref} @notify_include);
+
+ # check if we have a new ref here
+ if ($old eq '0' x 40)
+ {
+ git_send_notice($ref, $new) if $notify_mailinglist;
+ return;
+ }
+
+ # collect commits
+ my @commits = ();
+ open GIT_REV_LIST, "-|"
+ or exec 'git', 'rev-list', "^$old", "$new", @notify_exclude
+ or die "Failed to execute git-rev-list";
+ while (<GIT_REV_LIST>)
+ {
+ chomp;
+ die "Invalid commit $_" unless /^[0-9a-f]{40}$/;
+ unshift @commits, $_;
+ }
+ close GIT_REV_LIST;
+
+ # send notifications for each commit
+ foreach my $commit (@commits)
+ {
+ git_send_notice($ref, $commit) if $notify_mailinglist;
+ }
+}
+
+if (@ARGV)
+{
+ git_send_all_notices($ARGV[0], $ARGV[1], $ARGV[2]);
+}
+else
+{
+ while (<>)
+ {
+ chomp;
+ if (/^([0-9a-f]{40}) ([0-9a-f]{40}) (.*)$/)
+ {
+ git_send_all_notices($1, $2, $3);
+ }
+ }
+}
+
+exit 0;

0 comments on commit 5d3a98d

Please sign in to comment.