Subject: Doubts about hot to properly write post-receive hook to send an email with a newly added patch

From: Mauro Carvalho Chehab <[email-masked]>
Date: Sat, 06 Mar 2010 12:55:35 -0300

I'm needing a post-receive hook that would send a copy to the patch author,
to the C/C recipients and to the ones that signed/acked/tested a patch.

The idea is to announce to the patch author and to the relevant parts that
a new patch were accepted on my -git tree, showing the patch/comments
after its addition at the tree.

So, I wrote a perl script for this (see enclosed) for it.

Unfortunately, I just found a serious bug: when I merge back from upstream, 
it sends a message also for all patch authors from upstream!

What's the better way to identify what patches came from a merge tree,
in order to remove them from the mailbomb queue?

Cheers,
Mauro

---

#!/usr/bin/perl
#
# License: GPLv2
#
use warnings;
use strict;
use Sys::Hostname;

$ENV{PATH} = '/usr/local/bin:/usr/bin:/bin';

my $debug = 0;
my $mail_cmd = "/usr/sbin/sendmail";
#
# Retrieve all info from git config
#
my $project_name = qx(git config mailnotify.project) || "untitled";
my $smtp_server  = qx(git config mailnotify.smtpserver) || "localhost";
my $from         = qx(git config mailnotify.from) || sprintf('%s@%s', $project_name, hostname());
my $to           = qx(git config mailnotify.to) || die("No mail To:");
my $cfgcc        = qx(git config mailnotify.cc) || "";
my $replyto      = qx(git config mailnotify.replyto) || "";
my $maxsize      = qx(git config mailnotify.maxsize) || "";
my $url          = qx(git config mailnotify.url) || "";

$project_name =~ s/\s+$//;
$smtp_server =~ s/\s+$//;
$from =~ s/\s+$//;
$to =~ s/\s+$//;
$cfgcc =~ s/\s+$//;
$replyto =~ s/\s+$//;
$maxsize =~ s/\s+$//;
$url =~ s/\s+$//;

#
# Get old revision, new revision and branch/tag name
#
my ($oldrev, $newrev, $refname);
if (scalar(@ARGV)) {
	($oldrev, $newrev, $refname) = @ARGV[ 0 .. 2 ];
} else {
	my $args = <STDIN>;
	($oldrev, $newrev, $refname) = split(" ", $args);
}
printf(STDERR "args:%s %s %s\n", $oldrev, $newrev, $refname) if ($debug);

#
# Get the complete revision name
#
$oldrev = qx(git rev-parse $oldrev);
$newrev = qx(git rev-parse $newrev);

chomp($oldrev);
chomp($newrev);
chomp($refname);

if ($debug) {
    printf(STDERR "oldrev:%s\n", $oldrev);
    printf(STDERR "newrev:%s\n", $newrev);
    printf(STDERR "refname:%s\n", $refname);
}

#
# Get branch name
#
my $branch = (split("/", $refname))[-1];

#
# get revisions
#
open REF, "git rev-list $oldrev..$newrev|";
while (<REF>) {
	my $ref= $_;

	my $author = qx(git log -1 --pretty=format:"%aN" $ref);
	my $data = qx(git log -1 --pretty=format:"%aD" $ref);
	my $subject = qx(git log -1 --pretty=format:"%s" $ref);
	$subject = sprintf("[git:%s/%s] %s", $project_name, $branch, $subject);

	my %copy;
	my $log = "";
	open IN, "git log -1 --pretty=email --stat $ref|";

	# Discard initial From line
	my $dumb=<IN>;

	while (<IN>) {
		next if (m/^Subject: /);
		next if (m/^Date: /);
		$log .= $_;

		if (m/(signed-off-by|author|from|accepted-by|tested-by|thanks-to|reviewed-by|cc|acked-by):\s*(.*)\s*\n$/i) {
			my $sob=$2;
			if ($sob =~ m/\s*(.*)\s*<(.*)>/) {
				my $name = $1;
				my $email = $2;
				$name =~ s/^\s+//;
				$name =~ s/\s+$//;
				$name =~ s/^\"(.*)\"$/$1/;
				$name="" if ($name =~ m/\@/);
				$copy{"$email"} = $name;
			} elsif ($sob =~ m/([^\s]+\@[^\s+]+)/) {
				my $email = $1;
				$copy{"$email"} = "" if (!exists($copy{"$email"}));
			}
		}
	}
	close IN;

	my $cc = $cfgcc;
	while (my ($key,$value) = each(%copy) ) {
		next if ($key =~ m/stable\@kernel.org/);
		if ($value ne "") {
			$cc .= ", $value <$key>";
		} else {
			$cc .= ", $key";
		}
	}
	$cc =~ s/^, //;
	$cc =~ s/\s+/ /g;

	my $diff = qx(git show --pretty=format:"" $ref);

	my $header = "Subject: $subject\nFrom: Patch from $author <$from>\nTo: $to\nData: $data\n";
	$header .= "Cc: $cc\n" if ($cc);

	if ($replyto) {
		$header .= "Mail-followup-to: $replyto\n";
		$header .= "Forward-to: $replyto\n";
		$header .= "Reply-to: $replyto\n";
	}

	my $email = "$header\n$log\n---\n\n";
	$email   .= "$url?a=commitdiff;h=$ref" if ($url);
	if ($maxsize && length($diff) > $maxsize) {
		$diff = "<diff discarded since it is too big>\n"
	}
	$email .= $diff;

	if (!$debug) {
		open(MAIL, "| $mail_cmd -t");
		print MAIL $email;
		close MAIL;
	} else {
		print $email;
	}
}

close REF;