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 | ||
