diff options
| author | Holger Weiss <holger@zedat.fu-berlin.de> | 2009-10-24 22:55:44 +0200 |
|---|---|---|
| committer | Holger Weiss <holger@zedat.fu-berlin.de> | 2009-10-24 22:55:44 +0200 |
| commit | af5e252846d08a579835e9b3bd0b004727252850 (patch) | |
| tree | 76c6731a465f5a87697096f7e95a58861f136da3 | |
| parent | a6b329689782ddd22b4ecd52d0b375e0841ca682 (diff) | |
| download | monitoring-plugins-af5e252846d08a579835e9b3bd0b004727252850.tar.gz | |
git-notify: Don't generate duplicate notifications
Never notify on a given commit more than once, even if it's referenced
via multiple branch heads. We make sure this won't happen simply by
maintaining a list of commits we notified about. The file path used for
saving this list can be specified using the new "-t" option. (The
contrib/hooks/post-receive-email script distributed with Git tries hard
to avoid such a list, but it doesn't get the necessary magic right.)
| -rwxr-xr-x | tools/git-notify | 61 |
1 files changed, 61 insertions, 0 deletions
diff --git a/tools/git-notify b/tools/git-notify index a158e872..a89104a6 100755 --- a/tools/git-notify +++ b/tools/git-notify | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | # Tool to send git commit notifications | 3 | # Tool to send git commit notifications |
| 4 | # | 4 | # |
| 5 | # Copyright 2005 Alexandre Julliard | 5 | # Copyright 2005 Alexandre Julliard |
| 6 | # Copyright 2009 Nagios Plugins Development Team | ||
| 6 | # | 7 | # |
| 7 | # This program is free software; you can redistribute it and/or | 8 | # This program is free software; you can redistribute it and/or |
| 8 | # modify it under the terms of the GNU General Public License as | 9 | # modify it under the terms of the GNU General Public License as |
| @@ -19,6 +20,7 @@ | |||
| 19 | # -n max Set max number of individual mails to send | 20 | # -n max Set max number of individual mails to send |
| 20 | # -r name Set the git repository name | 21 | # -r name Set the git repository name |
| 21 | # -s bytes Set the maximum diff size in bytes (-1 for no limit) | 22 | # -s bytes Set the maximum diff size in bytes (-1 for no limit) |
| 23 | # -t file Set the file to use for reading and saving state | ||
| 22 | # -u url Set the URL to the gitweb browser | 24 | # -u url Set the URL to the gitweb browser |
| 23 | # -i branch If at least one -i is given, report only for specified branches | 25 | # -i branch If at least one -i is given, report only for specified branches |
| 24 | # -x branch Exclude changes to the specified branch from reports | 26 | # -x branch Exclude changes to the specified branch from reports |
| @@ -27,6 +29,7 @@ | |||
| 27 | 29 | ||
| 28 | use strict; | 30 | use strict; |
| 29 | use open ':utf8'; | 31 | use open ':utf8'; |
| 32 | use Fcntl ':flock'; | ||
| 30 | use Encode 'encode'; | 33 | use Encode 'encode'; |
| 31 | use Cwd 'realpath'; | 34 | use Cwd 'realpath'; |
| 32 | 35 | ||
| @@ -76,6 +79,9 @@ my @include_list = split /\s+/, git_config( "notify.include" ) || ""; | |||
| 76 | # branches to exclude | 79 | # branches to exclude |
| 77 | my @exclude_list = split /\s+/, git_config( "notify.exclude" ) || ""; | 80 | my @exclude_list = split /\s+/, git_config( "notify.exclude" ) || ""; |
| 78 | 81 | ||
| 82 | # the state file we use (can be changed with the -t option) | ||
| 83 | my $state_file = git_config( "notify.statefile" ) || "/var/tmp/git-notify.state"; | ||
| 84 | |||
| 79 | # Extra options to git rev-list | 85 | # Extra options to git rev-list |
| 80 | my @revlist_options; | 86 | my @revlist_options; |
| 81 | 87 | ||
| @@ -87,6 +93,7 @@ sub usage() | |||
| 87 | print " -n max Set max number of individual mails to send\n"; | 93 | print " -n max Set max number of individual mails to send\n"; |
| 88 | print " -r name Set the git repository name\n"; | 94 | print " -r name Set the git repository name\n"; |
| 89 | print " -s bytes Set the maximum diff size in bytes (-1 for no limit)\n"; | 95 | print " -s bytes Set the maximum diff size in bytes (-1 for no limit)\n"; |
| 96 | print " -t file Set the file to use for reading and saving state\n"; | ||
| 90 | print " -u url Set the URL to the gitweb browser\n"; | 97 | print " -u url Set the URL to the gitweb browser\n"; |
| 91 | print " -i branch If at least one -i is given, report only for specified branches\n"; | 98 | print " -i branch If at least one -i is given, report only for specified branches\n"; |
| 92 | print " -x branch Exclude changes to the specified branch from reports\n"; | 99 | print " -x branch Exclude changes to the specified branch from reports\n"; |
| @@ -127,6 +134,59 @@ sub git_rev_list(@) | |||
| 127 | return $revlist; | 134 | return $revlist; |
| 128 | } | 135 | } |
| 129 | 136 | ||
| 137 | # append the given commit hashes to the state file | ||
| 138 | sub save_commits($) | ||
| 139 | { | ||
| 140 | my $commits = shift; | ||
| 141 | |||
| 142 | open STATE, ">>", $state_file or die "Cannot open $state_file: $!"; | ||
| 143 | flock STATE, LOCK_EX or die "Cannot lock $state_file"; | ||
| 144 | print STATE "$_\n" for @$commits; | ||
| 145 | flock STATE, LOCK_UN or die "Cannot unlock $state_file"; | ||
| 146 | close STATE or die "Cannot close $state_file: $!"; | ||
| 147 | } | ||
| 148 | |||
| 149 | # for the given range, return the new hashes and append them to the state file | ||
| 150 | sub get_new_commits($$) | ||
| 151 | { | ||
| 152 | my ($old_sha1, $new_sha1) = @_; | ||
| 153 | my ($seen, @args); | ||
| 154 | my $newrevs = []; | ||
| 155 | |||
| 156 | @args = ( "^$old_sha1" ) unless $old_sha1 eq '0' x 40; | ||
| 157 | push @args, $new_sha1, @exclude_list; | ||
| 158 | |||
| 159 | my $revlist = git_rev_list(@args); | ||
| 160 | |||
| 161 | if (not -e $state_file) # initialize the state file with all hashes | ||
| 162 | { | ||
| 163 | save_commits(git_rev_list("--all", "--full-history")); | ||
| 164 | return $revlist; | ||
| 165 | } | ||
| 166 | |||
| 167 | open STATE, $state_file or die "Cannot open $state_file: $!"; | ||
| 168 | flock STATE, LOCK_SH or die "Cannot lock $state_file"; | ||
| 169 | while (<STATE>) | ||
| 170 | { | ||
| 171 | chomp; | ||
| 172 | die "Invalid commit: $_" if not /^[0-9a-f]{40}$/; | ||
| 173 | $seen->{$_} = 1; | ||
| 174 | } | ||
| 175 | flock STATE, LOCK_UN or die "Cannot unlock $state_file"; | ||
| 176 | close STATE or die "Cannot close $state_file: $!"; | ||
| 177 | |||
| 178 | # FIXME: if another git-notify process reads the $state_file at *this* | ||
| 179 | # point, that process might generate duplicates of our notifications. | ||
| 180 | |||
| 181 | save_commits($revlist); | ||
| 182 | |||
| 183 | foreach my $commit (@$revlist) | ||
| 184 | { | ||
| 185 | push @$newrevs, $commit unless $seen->{$commit}; | ||
| 186 | } | ||
| 187 | return $newrevs; | ||
| 188 | } | ||
| 189 | |||
| 130 | # truncate the given string if it exceeds the specified number of characters | 190 | # truncate the given string if it exceeds the specified number of characters |
| 131 | sub truncate_str($$) | 191 | sub truncate_str($$) |
| 132 | { | 192 | { |
| @@ -217,6 +277,7 @@ sub parse_options() | |||
| 217 | elsif ($arg eq '-n') { $max_individual_notices = shift @ARGV; } | 277 | elsif ($arg eq '-n') { $max_individual_notices = shift @ARGV; } |
| 218 | elsif ($arg eq '-r') { $repos_name = shift @ARGV; } | 278 | elsif ($arg eq '-r') { $repos_name = shift @ARGV; } |
| 219 | elsif ($arg eq '-s') { $max_diff_size = shift @ARGV; } | 279 | elsif ($arg eq '-s') { $max_diff_size = shift @ARGV; } |
| 280 | elsif ($arg eq '-t') { $state_file = shift @ARGV; } | ||
| 220 | elsif ($arg eq '-u') { $gitweb_url = shift @ARGV; } | 281 | elsif ($arg eq '-u') { $gitweb_url = shift @ARGV; } |
| 221 | elsif ($arg eq '-i') { push @include_list, shift @ARGV; } | 282 | elsif ($arg eq '-i') { push @include_list, shift @ARGV; } |
| 222 | elsif ($arg eq '-x') { push @exclude_list, shift @ARGV; } | 283 | elsif ($arg eq '-x') { push @exclude_list, shift @ARGV; } |
