diff options
author | Joe Perches <joe@perches.com> | 2009-09-21 20:04:13 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-22 10:17:46 -0400 |
commit | f5492666a3b62344de9026a960c11888160362c9 (patch) | |
tree | e9f491186ac1f778817c8b493857d9c84478cb85 /scripts/get_maintainer.pl | |
parent | b61d4a71e483fe1aa1c4b170c28d85be77edce4f (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-x | scripts/get_maintainer.pl | 138 |
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 @@ | |||
13 | use strict; | 13 | use strict; |
14 | 14 | ||
15 | my $P = $0; | 15 | my $P = $0; |
16 | my $V = '0.17'; | 16 | my $V = '0.18beta2'; |
17 | 17 | ||
18 | use Getopt::Long qw(:config no_auto_abbrev); | 18 | use Getopt::Long qw(:config no_auto_abbrev); |
19 | 19 | ||
@@ -29,6 +29,7 @@ my $email_git_min_signatures = 1; | |||
29 | my $email_git_max_maintainers = 5; | 29 | my $email_git_max_maintainers = 5; |
30 | my $email_git_min_percent = 5; | 30 | my $email_git_min_percent = 5; |
31 | my $email_git_since = "1-year-ago"; | 31 | my $email_git_since = "1-year-ago"; |
32 | my $email_git_blame = 0; | ||
32 | my $output_multiline = 1; | 33 | my $output_multiline = 1; |
33 | my $output_separator = ", "; | 34 | my $output_separator = ", "; |
34 | my $scm = 0; | 35 | my $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 | ||
110 | if ($email && ($email_maintainer + $email_list + $email_subscriber_list | 112 | if ($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 | ||
152 | my @files = (); | 155 | my @files = (); |
156 | my @range = (); | ||
153 | 157 | ||
154 | foreach my $file (@ARGV) { | 158 | foreach 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 | ||
231 | if ($email) { | 244 | if ($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 | ||
334 | Notes: | 348 | Notes: |
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. | ||
343 | EOT | 359 | EOT |
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)); | 573 | sub 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 | |||
589 | sub 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 | ||