aboutsummaryrefslogtreecommitdiffstats
path: root/scripts/get_maintainer.pl
diff options
context:
space:
mode:
authorJoe Perches <joe@perches.com>2009-09-21 20:04:13 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2009-09-22 10:17:46 -0400
commitf5492666a3b62344de9026a960c11888160362c9 (patch)
treee9f491186ac1f778817c8b493857d9c84478cb85 /scripts/get_maintainer.pl
parentb61d4a71e483fe1aa1c4b170c28d85be77edce4f (diff)
scripts/get_maintainer.pl: add --git-blame
Julia Lawall suggested that get_maintainers.pl should have the ability to include signatories of commits that are modified by a particular patch. Vegard Nossum did something similar once. http://lkml.org/lkml/2008/5/29/449 The modified script looks the commits for all lines in the patch, and includes the "-by:" signatories for those commits. It uses the same git-min-percent, git-max-maintainers, and git-min-signatures options. git-since is ignored. It can be used independently from the --git default, so ./scripts/get_maintainers.pl --nogit --git-blame <patch> or ./scripts/get_maintainers.pl --nogit --git-blame -f <file> is acceptable. If used with -f <file>, all lines/commits for the file are checked. --git-blame can be slow if used with -f <file> --git-blame does not work with -f <directory> Signed-off-by: Joe Perches <joe@perches.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'scripts/get_maintainer.pl')
-rwxr-xr-xscripts/get_maintainer.pl138
1 files changed, 109 insertions, 29 deletions
diff --git a/scripts/get_maintainer.pl b/scripts/get_maintainer.pl
index 278a45bd45a5..35781e0d43e6 100755
--- a/scripts/get_maintainer.pl
+++ b/scripts/get_maintainer.pl
@@ -13,7 +13,7 @@
13use strict; 13use strict;
14 14
15my $P = $0; 15my $P = $0;
16my $V = '0.17'; 16my $V = '0.18beta2';
17 17
18use Getopt::Long qw(:config no_auto_abbrev); 18use Getopt::Long qw(:config no_auto_abbrev);
19 19
@@ -29,6 +29,7 @@ my $email_git_min_signatures = 1;
29my $email_git_max_maintainers = 5; 29my $email_git_max_maintainers = 5;
30my $email_git_min_percent = 5; 30my $email_git_min_percent = 5;
31my $email_git_since = "1-year-ago"; 31my $email_git_since = "1-year-ago";
32my $email_git_blame = 0;
32my $output_multiline = 1; 33my $output_multiline = 1;
33my $output_separator = ", "; 34my $output_separator = ", ";
34my $scm = 0; 35my $scm = 0;
@@ -68,6 +69,7 @@ if (!GetOptions(
68 'git-max-maintainers=i' => \$email_git_max_maintainers, 69 'git-max-maintainers=i' => \$email_git_max_maintainers,
69 'git-min-percent=i' => \$email_git_min_percent, 70 'git-min-percent=i' => \$email_git_min_percent,
70 'git-since=s' => \$email_git_since, 71 'git-since=s' => \$email_git_since,
72 'git-blame!' => \$email_git_blame,
71 'm!' => \$email_maintainer, 73 'm!' => \$email_maintainer,
72 'n!' => \$email_usename, 74 'n!' => \$email_usename,
73 'l!' => \$email_list, 75 'l!' => \$email_list,
@@ -107,8 +109,9 @@ if ($selections == 0) {
107 die "$P: Missing required option: email, scm, status, subsystem or web\n"; 109 die "$P: Missing required option: email, scm, status, subsystem or web\n";
108} 110}
109 111
110if ($email && ($email_maintainer + $email_list + $email_subscriber_list 112if ($email &&
111 + $email_git + $email_git_penguin_chiefs) == 0) { 113 ($email_maintainer + $email_list + $email_subscriber_list +
114 $email_git + $email_git_penguin_chiefs + $email_git_blame) == 0) {
112 usage(); 115 usage();
113 die "$P: Please select at least 1 email option\n"; 116 die "$P: Please select at least 1 email option\n";
114} 117}
@@ -150,6 +153,7 @@ close(MAINT);
150## use the filenames on the command line or find the filenames in the patchfiles 153## use the filenames on the command line or find the filenames in the patchfiles
151 154
152my @files = (); 155my @files = ();
156my @range = ();
153 157
154foreach my $file (@ARGV) { 158foreach my $file (@ARGV) {
155 ##if $file is a directory and it lacks a trailing slash, add one 159 ##if $file is a directory and it lacks a trailing slash, add one
@@ -162,13 +166,19 @@ foreach my $file (@ARGV) {
162 push(@files, $file); 166 push(@files, $file);
163 } else { 167 } else {
164 my $file_cnt = @files; 168 my $file_cnt = @files;
169 my $lastfile;
165 open(PATCH, "<$file") or die "$P: Can't open ${file}\n"; 170 open(PATCH, "<$file") or die "$P: Can't open ${file}\n";
166 while (<PATCH>) { 171 while (<PATCH>) {
167 if (m/^\+\+\+\s+(\S+)/) { 172 if (m/^\+\+\+\s+(\S+)/) {
168 my $filename = $1; 173 my $filename = $1;
169 $filename =~ s@^[^/]*/@@; 174 $filename =~ s@^[^/]*/@@;
170 $filename =~ s@\n@@; 175 $filename =~ s@\n@@;
176 $lastfile = $filename;
171 push(@files, $filename); 177 push(@files, $filename);
178 } elsif (m/^\@\@ -(\d+),(\d+)/) {
179 if ($email_git_blame) {
180 push(@range, "$lastfile:$1:$2");
181 }
172 } 182 }
173 } 183 }
174 close(PATCH); 184 close(PATCH);
@@ -226,6 +236,9 @@ foreach my $file (@files) {
226 recent_git_signoffs($file); 236 recent_git_signoffs($file);
227 } 237 }
228 238
239 if ($email && $email_git_blame) {
240 git_assign_blame($file);
241 }
229} 242}
230 243
231if ($email) { 244if ($email) {
@@ -311,6 +324,7 @@ MAINTAINER field selection options:
311 --git-max-maintainers => maximum maintainers to add (default: 5) 324 --git-max-maintainers => maximum maintainers to add (default: 5)
312 --git-min-percent => minimum percentage of commits required (default: 5) 325 --git-min-percent => minimum percentage of commits required (default: 5)
313 --git-since => git history to use (default: 1-year-ago) 326 --git-since => git history to use (default: 1-year-ago)
327 --git-blame => use git blame to find modified commits for patch or file
314 --m => include maintainer(s) if any 328 --m => include maintainer(s) if any
315 --n => include name 'Full Name <addr\@domain.tld>' 329 --n => include name 'Full Name <addr\@domain.tld>'
316 --l => include list(s) if any 330 --l => include list(s) if any
@@ -333,13 +347,15 @@ Other options:
333 347
334Notes: 348Notes:
335 Using "-f directory" may give unexpected results: 349 Using "-f directory" may give unexpected results:
336 350 Used with "--git", git signators for _all_ files in and below
337 Used with "--git", git signators for _all_ files in and below 351 directory are examined as git recurses directories.
338 directory are examined as git recurses directories. 352 Any specified X: (exclude) pattern matches are _not_ ignored.
339 Any specified X: (exclude) pattern matches are _not_ ignored. 353 Used with "--nogit", directory is used as a pattern match,
340 Used with "--nogit", directory is used as a pattern match, 354 no individual file within the directory or subdirectory
341 no individual file within the directory or subdirectory 355 is matched.
342 is matched. 356 Used with "--git-blame", does not iterate all files in directory
357 Using "--git-blame" is slow and may add old committers and authors
358 that are no longer active maintainers to the output.
343EOT 359EOT
344} 360}
345 361
@@ -449,14 +465,19 @@ sub push_email_address {
449 my ($email_address) = @_; 465 my ($email_address) = @_;
450 466
451 my $email_name = ""; 467 my $email_name = "";
452 if ($email_address =~ m/([^<]+)<(.*\@.*)>$/) {
453 $email_name = $1;
454 $email_address = $2;
455 }
456 468
457 if ($email_maintainer) { 469 if ($email_maintainer) {
458 if ($email_usename && $email_name) { 470 if ($email_address =~ m/([^<]+)<(.*\@.*)>$/) {
459 push(@email_to, format_email($email_name, $email_address)); 471 $email_name = $1;
472 $email_address = $2;
473 if ($email_usename) {
474 push(@email_to, format_email($email_name, $email_address));
475 } else {
476 push(@email_to, $email_address);
477 }
478 } elsif ($email_address =~ m/<(.+)>/) {
479 $email_address = $1;
480 push(@email_to, $email_address);
460 } else { 481 } else {
461 push(@email_to, $email_address); 482 push(@email_to, $email_address);
462 } 483 }
@@ -545,20 +566,79 @@ sub recent_git_signoffs {
545 last; 566 last;
546 } 567 }
547 } 568 }
548 if ($line =~ m/(.+)<(.+)>/) { 569 push_email_address($line);
549 my $git_name = $1; 570 }
550 my $git_addr = $2; 571}
551 if ($email_usename) { 572
552 push(@email_to, format_email($git_name, $git_addr)); 573sub save_commits {
553 } else { 574 my ($cmd, @commits) = @_;
554 push(@email_to, $git_addr); 575 my $output;
555 } 576 my @lines = ();
556 } elsif ($line =~ m/<(.+)>/) { 577
557 my $git_addr = $1; 578 $output = `${cmd}`;
558 push(@email_to, $git_addr); 579
559 } else { 580 @lines = split("\n", $output);
560 push(@email_to, $line); 581 foreach my $line (@lines) {
582 if ($line =~ m/^(\w+) /) {
583 push (@commits, $1);
584 }
585 }
586 return @commits;
587}
588
589sub git_assign_blame {
590 my ($file) = @_;
591
592 my @lines = ();
593 my @commits = ();
594 my $cmd;
595 my $output;
596 my %hash;
597 my $total_sign_offs;
598 my $count;
599
600 if (@range) {
601 foreach my $file_range_diff (@range) {
602 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
603 my $diff_file = $1;
604 my $diff_start = $2;
605 my $diff_length = $3;
606 next if (!("$file" eq "$diff_file"));
607 $cmd = "git blame -l -L $diff_start,+$diff_length $file\n";
608 @commits = save_commits($cmd, @commits);
561 } 609 }
610 } else {
611 if (-f $file) {
612 $cmd = "git blame -l $file\n";
613 @commits = save_commits($cmd, @commits);
614 }
615 }
616
617 $total_sign_offs = 0;
618 @commits = uniq(@commits);
619 foreach my $commit (@commits) {
620 $cmd = "git log -1 ${commit}";
621 $cmd .= " | grep -Ei \"^[-_ a-z]+by:.*\\\@.*\$\"";
622 if (!$email_git_penguin_chiefs) {
623 $cmd .= " | grep -Ev \"${penguin_chiefs}\"";
624 }
625 $cmd .= " | cut -f2- -d\":\"";
626
627 $output = `${cmd}`;
628 $output =~ s/^\s*//gm;
629 @lines = split("\n", $output);
630 $hash{$_}++ for @lines;
631 $total_sign_offs += @lines;
632 }
633
634 $count = 0;
635 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
636 my $sign_offs = $hash{$line};
637 $count++;
638 last if ($sign_offs < $email_git_min_signatures ||
639 $count > $email_git_max_maintainers ||
640 $sign_offs * 100 / $total_sign_offs < $email_git_min_percent);
641 push_email_address($line);
562 } 642 }
563} 643}
564 644