diff --git a/src/lib/Hydra.pm b/src/lib/Hydra.pm
index 56165418..42da5f65 100644
--- a/src/lib/Hydra.pm
+++ b/src/lib/Hydra.pm
@@ -92,7 +92,7 @@ has 'hydra_plugins' => (
 
 after setup_finalize => sub {
     my $class = shift;
-    $plugins = [Hydra::Plugin->plugins(db => $class->model('DB'), config => $class->config)];
+    $plugins = [Hydra::Plugin->instantiate(db => $class->model('DB'), config => $class->config)];
 };
 
 __PACKAGE__->setup();
diff --git a/src/lib/Hydra/Plugin.pm b/src/lib/Hydra/Plugin.pm
index 47a5faeb..8b3df782 100644
--- a/src/lib/Hydra/Plugin.pm
+++ b/src/lib/Hydra/Plugin.pm
@@ -7,11 +7,19 @@ use Module::Pluggable
 
 sub new {
     my ($class, %args) = @_;
-    my $self = { db => $args{db}, config => $args{config} };
+    my $self = { db => $args{db}, config => $args{config}, plugins => $args{plugins} };
     bless $self, $class;
     return $self;
 }
 
+sub instantiate {
+    my ($class, %args) = @_;
+    my $plugins = [];
+    $args{plugins} = $plugins;
+    push @$plugins, $class->plugins(%args);
+    return @$plugins;
+}
+
 # Called when build $build has finished.  If the build failed, then
 # $dependents is an array ref to a list of builds that have also
 # failed as a result (i.e. because they depend on $build or a failed
@@ -34,4 +42,12 @@ sub fetchInput {
     return undef;
 }
 
+# Get the commits to repository ‘$value’ between revisions ‘$rev1’ and
+# ‘$rev2’.  Each commit should be a hash ‘{ revision = "..."; author =
+# "..."; email = "..."; }’.
+sub getCommits {
+    my ($self, $type, $value, $rev1, $rev2) = @_;
+    return [];
+}
+
 1;
diff --git a/src/lib/Hydra/Plugin/GitInput.pm b/src/lib/Hydra/Plugin/GitInput.pm
index 98c4c24a..fc267e70 100644
--- a/src/lib/Hydra/Plugin/GitInput.pm
+++ b/src/lib/Hydra/Plugin/GitInput.pm
@@ -12,17 +12,9 @@ sub supportedInputTypes {
     $inputTypes->{'git'} = 'Git checkout';
 }
 
-sub fetchInput {
-    my ($self, $type, $name, $value) = @_;
-
-    return undef if $type ne "git";
-
-    (my $uri, my $branch, my $deepClone) = split ' ', $value;
-    $branch = defined $branch ? $branch : "master";
-
-    my $timestamp = time;
-    my $sha256;
-    my $storePath;
+# Clone or update a branch of a repository into our SCM cache.
+sub _cloneRepo {
+    my ($self, $uri, $branch, $deepClone) = @_;
 
     my $cacheDir = getSCMCacheDir . "/git";
     mkpath($cacheDir);
@@ -38,8 +30,8 @@ sub fetchInput {
 
     chdir $clonePath or die $!; # !!! urgh, shouldn't do a chdir
 
-    # This command force the update of the local branch to be in the same as
-    # the remote branch for whatever the repository state is.  This command mirror
+    # This command forces the update of the local branch to be in the same as
+    # the remote branch for whatever the repository state is.  This command mirrors
     # only one branch of the remote repository.
     ($res, $stdout, $stderr) = captureStdoutStderr(600,
         "git", "fetch", "-fu", "origin", "+$branch:$branch");
@@ -47,16 +39,6 @@ sub fetchInput {
         "git", "fetch", "-fu", "origin") if $res;
     die "error fetching latest change from git repo at `$uri':\n$stderr" if $res;
 
-    ($res, $stdout, $stderr) = captureStdoutStderr(600,
-        ("git", "rev-parse", "$branch"));
-    die "error getting revision number of Git branch '$branch' at `$uri':\n$stderr" if $res;
-
-    my ($revision) = split /\n/, $stdout;
-    die "error getting a well-formated revision number of Git branch '$branch' at `$uri':\n$stdout"
-        unless $revision =~ /^[0-9a-fA-F]+$/;
-
-    my $ref = "refs/heads/$branch";
-
     # If deepClone is defined, then we look at the content of the repository
     # to determine if this is a top-git branch.
     if (defined $deepClone) {
@@ -74,6 +56,40 @@ sub fetchInput {
         }
     }
 
+    return $clonePath;
+}
+
+sub _parseValue {
+    my ($value) = @_;
+    (my $uri, my $branch, my $deepClone) = split ' ', $value;
+    $branch = defined $branch ? $branch : "master";
+    return ($uri, $branch, $deepClone);
+
+}
+
+sub fetchInput {
+    my ($self, $type, $name, $value) = @_;
+
+    return undef if $type ne "git";
+
+    my ($uri, $branch, $deepClone) = _parseValue($value);
+
+    my $clonePath = $self->_cloneRepo($uri, $branch, $deepClone);
+
+    my $timestamp = time;
+    my $sha256;
+    my $storePath;
+
+    my ($res, $stdout, $stderr) = captureStdoutStderr(600,
+        ("git", "rev-parse", "$branch"));
+    die "error getting revision number of Git branch '$branch' at `$uri':\n$stderr" if $res;
+
+    my ($revision) = split /\n/, $stdout;
+    die "error getting a well-formated revision number of Git branch '$branch' at `$uri':\n$stdout"
+        unless $revision =~ /^[0-9a-fA-F]+$/;
+
+    my $ref = "refs/heads/$branch";
+
     # Some simple caching: don't check a uri/branch/revision more than once.
     # TODO: Fix case where the branch is reset to a previous commit.
     my $cachedInput ;
@@ -145,4 +161,28 @@ sub fetchInput {
         };
 }
 
+sub getCommits {
+    my ($self, $type, $value, $rev1, $rev2) = @_;
+    return [] if $type ne "git";
+
+    return [] unless $rev1 =~ /^[0-9a-f]+$/;
+    return [] unless $rev2 =~ /^[0-9a-f]+$/;
+
+    my ($uri, $branch, $deepClone) = _parseValue($value);
+
+    my $clonePath = $self->_cloneRepo($uri, $branch, $deepClone);
+
+    my $out;
+    IPC::Run::run(["git", "log", "--pretty=format:%H%x09%an%x09%ae%x09%at", "$rev1..$rev2"], \undef, \$out)
+        or die "cannot get git logs: $?";
+
+    my $res = [];
+    foreach my $line (split /\n/, $out) {
+        my ($revision, $author, $email, $date) = split "\t", $line;
+        push @$res, { revision => $revision, author => $author, email => $email };
+    }
+
+    return $res;
+}
+
 1;
diff --git a/src/lib/Hydra/Plugin/HipChatNotification.pm b/src/lib/Hydra/Plugin/HipChatNotification.pm
index 13ef187f..c7b11352 100644
--- a/src/lib/Hydra/Plugin/HipChatNotification.pm
+++ b/src/lib/Hydra/Plugin/HipChatNotification.pm
@@ -35,6 +35,36 @@ sub buildFinished {
         }
     }
 
+    return if scalar keys %rooms == 0;
+
+    # Determine who broke/fixed the build.
+    my $prevBuild = getPreviousBuild($build);
+
+    my $nrCommits = 0;
+    my %authors;
+
+    if ($prevBuild) {
+        foreach my $curInput ($build->buildinputs_builds) {
+            next unless $curInput->type eq "git";
+            my $prevInput = $prevBuild->buildinputs_builds->find({ name => $curInput->name });
+            next unless defined $prevInput;
+
+            next if $curInput->type ne $prevInput->type;
+            next if $curInput->uri ne $prevInput->uri;
+
+            my @commits;
+            foreach my $plugin (@{$self->{plugins}}) {
+                push @commits, @{$plugin->getCommits($curInput->type, $curInput->uri, $prevInput->revision, $curInput->revision)};
+            }
+
+            foreach my $commit (@commits) {
+                print STDERR "$commit->{revision} by $commit->{author}\n";
+                $authors{$commit->{author}} = $commit->{email};
+                $nrCommits++;
+            }
+        }
+    }
+
     # Send a message to each room.
     foreach my $roomId (keys %rooms) {
         my $room = $rooms{$roomId};
@@ -53,7 +83,16 @@ sub buildFinished {
         $msg .= " (and ${\scalar @deps} others)" if scalar @deps > 0;
         $msg .= ": <a href='$baseurl/build/${\$build->id}'>" . showStatus($build) . "</a>";
 
+        if (scalar keys %authors > 0) {
+            # FIXME: HTML escaping
+            my @x = map { "<a href='mailto:$authors{$_}'>$_</a>" } (sort keys %authors);
+            $msg .= ", likely due to ";
+            $msg .= "$nrCommits commits by " if $nrCommits > 1;
+            $msg .= join(" or ", scalar @x > 1 ? join(", ", @x[0..scalar @x - 2]) : (), $x[-1]);
+        }
+
         print STDERR "sending hipchat notification to room $roomId: $msg\n";
+        next;
 
         my $ua = LWP::UserAgent->new();
         my $resp = $ua->post('https://api.hipchat.com/v1/rooms/message?format=json&auth_token=' . $room->{room}->{token}, {
diff --git a/src/script/hydra-build b/src/script/hydra-build
index 902b5bff..ab2650db 100755
--- a/src/script/hydra-build
+++ b/src/script/hydra-build
@@ -17,7 +17,7 @@ my $db = Hydra::Model::DB->new();
 
 my $config = getHydraConfig();
 
-my @plugins = Hydra::Plugin->plugins(db => $db, config => $config);
+my @plugins = Hydra::Plugin->instantiate(db => $db, config => $config);
 
 
 sub addBuildStepOutputs {
diff --git a/src/script/hydra-evaluator b/src/script/hydra-evaluator
index dd6ffa7e..50e0e448 100755
--- a/src/script/hydra-evaluator
+++ b/src/script/hydra-evaluator
@@ -21,7 +21,7 @@ STDOUT->autoflush();
 my $db = Hydra::Model::DB->new();
 my $config = getHydraConfig();
 
-my $plugins = [Hydra::Plugin->plugins(db => $db, config => $config)];
+my $plugins = [Hydra::Plugin->instantiate(db => $db, config => $config)];
 
 # Don't check a jobset more than once every five minutes.
 my $minCheckInterval = 5 * 60;