aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xscripts/get_maintainer.pl194
1 files changed, 176 insertions, 18 deletions
diff --git a/scripts/get_maintainer.pl b/scripts/get_maintainer.pl
index 81a67a458e78..4e11c271e613 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.21'; 16my $V = '0.22';
17 17
18use Getopt::Long qw(:config no_auto_abbrev); 18use Getopt::Long qw(:config no_auto_abbrev);
19 19
@@ -33,6 +33,8 @@ my $email_git_blame = 0;
33my $email_remove_duplicates = 1; 33my $email_remove_duplicates = 1;
34my $output_multiline = 1; 34my $output_multiline = 1;
35my $output_separator = ", "; 35my $output_separator = ", ";
36my $output_roles = 0;
37my $output_rolestats = 0;
36my $scm = 0; 38my $scm = 0;
37my $web = 0; 39my $web = 0;
38my $subsystem = 0; 40my $subsystem = 0;
@@ -79,6 +81,8 @@ if (!GetOptions(
79 'l!' => \$email_list, 81 'l!' => \$email_list,
80 's!' => \$email_subscriber_list, 82 's!' => \$email_subscriber_list,
81 'multiline!' => \$output_multiline, 83 'multiline!' => \$output_multiline,
84 'roles!' => \$output_roles,
85 'rolestats!' => \$output_rolestats,
82 'separator=s' => \$output_separator, 86 'separator=s' => \$output_separator,
83 'subsystem!' => \$subsystem, 87 'subsystem!' => \$subsystem,
84 'status!' => \$status, 88 'status!' => \$status,
@@ -90,8 +94,7 @@ if (!GetOptions(
90 'v|version' => \$version, 94 'v|version' => \$version,
91 'h|help' => \$help, 95 'h|help' => \$help,
92 )) { 96 )) {
93 usage(); 97 die "$P: invalid argument - use --help if necessary\n";
94 die "$P: invalid argument\n";
95} 98}
96 99
97if ($help != 0) { 100if ($help != 0) {
@@ -113,6 +116,10 @@ if ($output_separator ne ", ") {
113 $output_multiline = 0; 116 $output_multiline = 0;
114} 117}
115 118
119if ($output_rolestats) {
120 $output_roles = 1;
121}
122
116my $selections = $email + $scm + $status + $subsystem + $web; 123my $selections = $email + $scm + $status + $subsystem + $web;
117if ($selections == 0) { 124if ($selections == 0) {
118 usage(); 125 usage();
@@ -326,9 +333,9 @@ if ($email) {
326 333
327 $email_address = format_email($1, $2); 334 $email_address = format_email($1, $2);
328 if ($email_git_penguin_chiefs) { 335 if ($email_git_penguin_chiefs) {
329 push(@email_to, $email_address); 336 push(@email_to, [$email_address, 'chief penguin']);
330 } else { 337 } else {
331 @email_to = grep(!/${email_address}/, @email_to); 338 @email_to = grep($_->[0] !~ /${email_address}/, @email_to);
332 } 339 }
333 } 340 }
334 } 341 }
@@ -342,7 +349,7 @@ if ($email || $email_list) {
342 if ($email_list) { 349 if ($email_list) {
343 @to = (@to, @list_to); 350 @to = (@to, @list_to);
344 } 351 }
345 output(uniq(@to)); 352 output(merge_email(@to));
346} 353}
347 354
348if ($scm) { 355if ($scm) {
@@ -405,6 +412,8 @@ MAINTAINER field selection options:
405 --l => include list(s) if any 412 --l => include list(s) if any
406 --s => include subscriber only list(s) if any 413 --s => include subscriber only list(s) if any
407 --remove-duplicates => minimize duplicate email names/addresses 414 --remove-duplicates => minimize duplicate email names/addresses
415 --roles => show roles (status:subsystem, git-signer, list, etc...)
416 --rolestats => show roles and statistics (commits/total_commits, %)
408 --scm => print SCM tree(s) if any 417 --scm => print SCM tree(s) if any
409 --status => print status if any 418 --status => print status if any
410 --subsystem => print subsystem name if any 419 --subsystem => print subsystem name if any
@@ -435,6 +444,13 @@ Notes:
435 Used with "--git-blame", does not iterate all files in directory 444 Used with "--git-blame", does not iterate all files in directory
436 Using "--git-blame" is slow and may add old committers and authors 445 Using "--git-blame" is slow and may add old committers and authors
437 that are no longer active maintainers to the output. 446 that are no longer active maintainers to the output.
447 Using "--roles" or "--rolestats" with git send-email --cc-cmd or any
448 other automated tools that expect only ["name"] <email address>
449 may not work because of additional output after <email address>.
450 Using "--rolestats" and "--git-blame" shows the #/total=% commits,
451 not the percentage of the entire file authored. # of commits is
452 not a good measure of amount of code authored. 1 major commit may
453 contain a thousand lines, 5 trivial commits may modify a single line.
438EOT 454EOT
439} 455}
440 456
@@ -547,6 +563,71 @@ sub find_ending_index {
547 return $index; 563 return $index;
548} 564}
549 565
566sub get_maintainer_role {
567 my ($index) = @_;
568
569 my $i;
570 my $start = find_starting_index($index);
571 my $end = find_ending_index($index);
572
573 my $role;
574 my $subsystem = $typevalue[$start];
575 if (length($subsystem) > 20) {
576 $subsystem = substr($subsystem, 0, 17);
577 $subsystem =~ s/\s*$//;
578 $subsystem = $subsystem . "...";
579 }
580
581 for ($i = $start + 1; $i < $end; $i++) {
582 my $tv = $typevalue[$i];
583 if ($tv =~ m/^(\C):\s*(.*)/) {
584 my $ptype = $1;
585 my $pvalue = $2;
586 if ($ptype eq "S") {
587 $role = $pvalue;
588 }
589 }
590 }
591
592 $role = lc($role);
593 if ($role eq "supported") {
594 $role = "supporter";
595 } elsif ($role eq "maintained") {
596 $role = "maintainer";
597 } elsif ($role eq "odd fixes") {
598 $role = "odd fixer";
599 } elsif ($role eq "orphan") {
600 $role = "orphan minder";
601 } elsif ($role eq "obsolete") {
602 $role = "obsolete minder";
603 } elsif ($role eq "buried alive in reporters") {
604 $role = "chief penguin";
605 }
606
607 return $role . ":" . $subsystem;
608}
609
610sub get_list_role {
611 my ($index) = @_;
612
613 my $i;
614 my $start = find_starting_index($index);
615 my $end = find_ending_index($index);
616
617 my $subsystem = $typevalue[$start];
618 if (length($subsystem) > 20) {
619 $subsystem = substr($subsystem, 0, 17);
620 $subsystem =~ s/\s*$//;
621 $subsystem = $subsystem . "...";
622 }
623
624 if ($subsystem eq "THE REST") {
625 $subsystem = "";
626 }
627
628 return $subsystem;
629}
630
550sub add_categories { 631sub add_categories {
551 my ($index) = @_; 632 my ($index) = @_;
552 633
@@ -564,17 +645,22 @@ sub add_categories {
564 if ($ptype eq "L") { 645 if ($ptype eq "L") {
565 my $list_address = $pvalue; 646 my $list_address = $pvalue;
566 my $list_additional = ""; 647 my $list_additional = "";
648 my $list_role = get_list_role($i);
649
650 if ($list_role ne "") {
651 $list_role = ":" . $list_role;
652 }
567 if ($list_address =~ m/([^\s]+)\s+(.*)$/) { 653 if ($list_address =~ m/([^\s]+)\s+(.*)$/) {
568 $list_address = $1; 654 $list_address = $1;
569 $list_additional = $2; 655 $list_additional = $2;
570 } 656 }
571 if ($list_additional =~ m/subscribers-only/) { 657 if ($list_additional =~ m/subscribers-only/) {
572 if ($email_subscriber_list) { 658 if ($email_subscriber_list) {
573 push(@list_to, $list_address); 659 push(@list_to, [$list_address, "subscriber list${list_role}"]);
574 } 660 }
575 } else { 661 } else {
576 if ($email_list) { 662 if ($email_list) {
577 push(@list_to, $list_address); 663 push(@list_to, [$list_address, "open list${list_role}"]);
578 } 664 }
579 } 665 }
580 } elsif ($ptype eq "M") { 666 } elsif ($ptype eq "M") {
@@ -591,7 +677,8 @@ sub add_categories {
591 } 677 }
592 } 678 }
593 if ($email_maintainer) { 679 if ($email_maintainer) {
594 push_email_addresses($pvalue); 680 my $role = get_maintainer_role($i);
681 push_email_addresses($pvalue, $role);
595 } 682 }
596 } elsif ($ptype eq "T") { 683 } elsif ($ptype eq "T") {
597 push(@scm, $pvalue); 684 push(@scm, $pvalue);
@@ -618,7 +705,7 @@ sub email_inuse {
618} 705}
619 706
620sub push_email_address { 707sub push_email_address {
621 my ($line) = @_; 708 my ($line, $role) = @_;
622 709
623 my ($name, $address) = parse_email($line); 710 my ($name, $address) = parse_email($line);
624 711
@@ -627,9 +714,9 @@ sub push_email_address {
627 } 714 }
628 715
629 if (!$email_remove_duplicates) { 716 if (!$email_remove_duplicates) {
630 push(@email_to, format_email($name, $address)); 717 push(@email_to, [format_email($name, $address), $role]);
631 } elsif (!email_inuse($name, $address)) { 718 } elsif (!email_inuse($name, $address)) {
632 push(@email_to, format_email($name, $address)); 719 push(@email_to, [format_email($name, $address), $role]);
633 $email_hash_name{$name}++; 720 $email_hash_name{$name}++;
634 $email_hash_address{$address}++; 721 $email_hash_address{$address}++;
635 } 722 }
@@ -638,24 +725,52 @@ sub push_email_address {
638} 725}
639 726
640sub push_email_addresses { 727sub push_email_addresses {
641 my ($address) = @_; 728 my ($address, $role) = @_;
642 729
643 my @address_list = (); 730 my @address_list = ();
644 731
645 if (rfc822_valid($address)) { 732 if (rfc822_valid($address)) {
646 push_email_address($address); 733 push_email_address($address, $role);
647 } elsif (@address_list = rfc822_validlist($address)) { 734 } elsif (@address_list = rfc822_validlist($address)) {
648 my $array_count = shift(@address_list); 735 my $array_count = shift(@address_list);
649 while (my $entry = shift(@address_list)) { 736 while (my $entry = shift(@address_list)) {
650 push_email_address($entry); 737 push_email_address($entry, $role);
651 } 738 }
652 } else { 739 } else {
653 if (!push_email_address($address)) { 740 if (!push_email_address($address, $role)) {
654 warn("Invalid MAINTAINERS address: '" . $address . "'\n"); 741 warn("Invalid MAINTAINERS address: '" . $address . "'\n");
655 } 742 }
656 } 743 }
657} 744}
658 745
746sub add_role {
747 my ($line, $role) = @_;
748
749 my ($name, $address) = parse_email($line);
750 my $email = format_email($name, $address);
751
752 foreach my $entry (@email_to) {
753 if ($email_remove_duplicates) {
754 my ($entry_name, $entry_address) = parse_email($entry->[0]);
755 if ($name eq $entry_name || $address eq $entry_address) {
756 if ($entry->[1] eq "") {
757 $entry->[1] = "$role";
758 } else {
759 $entry->[1] = "$entry->[1],$role";
760 }
761 }
762 } else {
763 if ($email eq $entry->[0]) {
764 if ($entry->[1] eq "") {
765 $entry->[1] = "$role";
766 } else {
767 $entry->[1] = "$entry->[1],$role";
768 }
769 }
770 }
771 }
772}
773
659sub which { 774sub which {
660 my ($bin) = @_; 775 my ($bin) = @_;
661 776
@@ -730,6 +845,10 @@ sub recent_git_signoffs {
730 s/.*:\s*(.+)\s*/$1/ for (@lines); 845 s/.*:\s*(.+)\s*/$1/ for (@lines);
731 846
732 $total_sign_offs = @lines; 847 $total_sign_offs = @lines;
848 foreach my $line (@lines) {
849 my ($name, $address) = parse_email($line);
850 $line = format_email($name, $address);
851 }
733 852
734 if ($email_remove_duplicates) { 853 if ($email_remove_duplicates) {
735 @lines = mailmap(@lines); 854 @lines = mailmap(@lines);
@@ -743,11 +862,19 @@ sub recent_git_signoffs {
743 # sort -rn 862 # sort -rn
744 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) { 863 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
745 my $sign_offs = $hash{$line}; 864 my $sign_offs = $hash{$line};
865 my $role;
866
746 $count++; 867 $count++;
747 last if ($sign_offs < $email_git_min_signatures || 868 last if ($sign_offs < $email_git_min_signatures ||
748 $count > $email_git_max_maintainers || 869 $count > $email_git_max_maintainers ||
749 $sign_offs * 100 / $total_sign_offs < $email_git_min_percent); 870 $sign_offs * 100 / $total_sign_offs < $email_git_min_percent);
750 push_email_address($line); 871 push_email_address($line, '');
872 $role = "git-signer";
873 if ($output_rolestats) {
874 my $percent = sprintf("%.0f", $sign_offs * 100 / $total_sign_offs);
875 $role = "$role:$sign_offs/$total_sign_offs=$percent%";
876 }
877 add_role($line, $role);
751 } 878 }
752} 879}
753 880
@@ -824,11 +951,23 @@ sub git_assign_blame {
824 $count = 0; 951 $count = 0;
825 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) { 952 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
826 my $sign_offs = $hash{$line}; 953 my $sign_offs = $hash{$line};
954 my $role;
955
827 $count++; 956 $count++;
828 last if ($sign_offs < $email_git_min_signatures || 957 last if ($sign_offs < $email_git_min_signatures ||
829 $count > $email_git_max_maintainers || 958 $count > $email_git_max_maintainers ||
830 $sign_offs * 100 / $total_sign_offs < $email_git_min_percent); 959 $sign_offs * 100 / $total_sign_offs < $email_git_min_percent);
831 push_email_address($line); 960 push_email_address($line, '');
961 if ($from_filename) {
962 $role = "commits";
963 } else {
964 $role = "modified commits";
965 }
966 if ($output_rolestats) {
967 my $percent = sprintf("%.0f", $sign_offs * 100 / $total_sign_offs);
968 $role = "$role:$sign_offs/$total_sign_offs=$percent%";
969 }
970 add_role($line, $role);
832 } 971 }
833} 972}
834 973
@@ -849,6 +988,25 @@ sub sort_and_uniq {
849 return @parms; 988 return @parms;
850} 989}
851 990
991sub merge_email {
992 my @lines;
993 my %saw;
994
995 for (@_) {
996 my ($address, $role) = @$_;
997 if (!$saw{$address}) {
998 if ($output_roles) {
999 push @lines, "$address ($role)";
1000 } else {
1001 push @lines, $address;
1002 }
1003 $saw{$address} = 1;
1004 }
1005 }
1006
1007 return @lines;
1008}
1009
852sub output { 1010sub output {
853 my @parms = @_; 1011 my @parms = @_;
854 1012