.. | .. |
---|
19 | 19 | use Getopt::Long qw(:config no_auto_abbrev); |
---|
20 | 20 | use Cwd; |
---|
21 | 21 | use File::Find; |
---|
| 22 | +use File::Spec::Functions; |
---|
22 | 23 | |
---|
23 | 24 | my $cur_path = fastgetcwd() . '/'; |
---|
24 | 25 | my $lk_path = "./"; |
---|
.. | .. |
---|
26 | 27 | my $email_usename = 1; |
---|
27 | 28 | my $email_maintainer = 1; |
---|
28 | 29 | my $email_reviewer = 1; |
---|
| 30 | +my $email_fixes = 1; |
---|
29 | 31 | my $email_list = 1; |
---|
| 32 | +my $email_moderated_list = 1; |
---|
30 | 33 | my $email_subscriber_list = 0; |
---|
31 | 34 | my $email_git_penguin_chiefs = 0; |
---|
32 | 35 | my $email_git = 0; |
---|
.. | .. |
---|
55 | 58 | my $letters = ""; |
---|
56 | 59 | my $keywords = 1; |
---|
57 | 60 | my $sections = 0; |
---|
58 | | -my $file_emails = 0; |
---|
| 61 | +my $email_file_emails = 0; |
---|
59 | 62 | my $from_filename = 0; |
---|
60 | 63 | my $pattern_depth = 0; |
---|
61 | 64 | my $self_test = undef; |
---|
.. | .. |
---|
66 | 69 | my $vcs_used = 0; |
---|
67 | 70 | |
---|
68 | 71 | my $exit = 0; |
---|
| 72 | + |
---|
| 73 | +my @files = (); |
---|
| 74 | +my @fixes = (); # If a patch description includes Fixes: lines |
---|
| 75 | +my @range = (); |
---|
| 76 | +my @keyword_tvi = (); |
---|
| 77 | +my @file_emails = (); |
---|
69 | 78 | |
---|
70 | 79 | my %commit_author_hash; |
---|
71 | 80 | my %commit_signer_hash; |
---|
.. | .. |
---|
248 | 257 | 'r!' => \$email_reviewer, |
---|
249 | 258 | 'n!' => \$email_usename, |
---|
250 | 259 | 'l!' => \$email_list, |
---|
| 260 | + 'fixes!' => \$email_fixes, |
---|
| 261 | + 'moderated!' => \$email_moderated_list, |
---|
251 | 262 | 's!' => \$email_subscriber_list, |
---|
252 | 263 | 'multiline!' => \$output_multiline, |
---|
253 | 264 | 'roles!' => \$output_roles, |
---|
.. | .. |
---|
262 | 273 | 'pattern-depth=i' => \$pattern_depth, |
---|
263 | 274 | 'k|keywords!' => \$keywords, |
---|
264 | 275 | 'sections!' => \$sections, |
---|
265 | | - 'fe|file-emails!' => \$file_emails, |
---|
| 276 | + 'fe|file-emails!' => \$email_file_emails, |
---|
266 | 277 | 'f|file' => \$from_filename, |
---|
267 | 278 | 'find-maintainer-files' => \$find_maintainer_files, |
---|
268 | 279 | 'mpath|maintainer-path=s' => \$maintainer_path, |
---|
.. | .. |
---|
420 | 431 | } |
---|
421 | 432 | } |
---|
422 | 433 | |
---|
| 434 | +sub maintainers_in_file { |
---|
| 435 | + my ($file) = @_; |
---|
| 436 | + |
---|
| 437 | + return if ($file =~ m@\bMAINTAINERS$@); |
---|
| 438 | + |
---|
| 439 | + if (-f $file && ($email_file_emails || $file =~ /\.yaml$/)) { |
---|
| 440 | + open(my $f, '<', $file) |
---|
| 441 | + or die "$P: Can't open $file: $!\n"; |
---|
| 442 | + my $text = do { local($/) ; <$f> }; |
---|
| 443 | + close($f); |
---|
| 444 | + |
---|
| 445 | + my @poss_addr = $text =~ m$[A-Za-zÀ-ÿ\"\' \,\.\+-]*\s*[\,]*\s*[\(\<\{]{0,1}[A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+\.[A-Za-z0-9]+[\)\>\}]{0,1}$g; |
---|
| 446 | + push(@file_emails, clean_file_emails(@poss_addr)); |
---|
| 447 | + } |
---|
| 448 | +} |
---|
| 449 | + |
---|
423 | 450 | # |
---|
424 | 451 | # Read mail address map |
---|
425 | 452 | # |
---|
.. | .. |
---|
500 | 527 | |
---|
501 | 528 | ## use the filenames on the command line or find the filenames in the patchfiles |
---|
502 | 529 | |
---|
503 | | -my @files = (); |
---|
504 | | -my @range = (); |
---|
505 | | -my @keyword_tvi = (); |
---|
506 | | -my @file_emails = (); |
---|
507 | | - |
---|
508 | 530 | if (!@ARGV) { |
---|
509 | 531 | push(@ARGV, "&STDIN"); |
---|
510 | 532 | } |
---|
511 | 533 | |
---|
512 | 534 | foreach my $file (@ARGV) { |
---|
513 | 535 | if ($file ne "&STDIN") { |
---|
| 536 | + $file = canonpath($file); |
---|
514 | 537 | ##if $file is a directory and it lacks a trailing slash, add one |
---|
515 | 538 | if ((-d $file)) { |
---|
516 | 539 | $file =~ s@([^/])$@$1/@; |
---|
.. | .. |
---|
518 | 541 | die "$P: file '${file}' not found\n"; |
---|
519 | 542 | } |
---|
520 | 543 | } |
---|
| 544 | + if ($from_filename && (vcs_exists() && !vcs_file_exists($file))) { |
---|
| 545 | + warn "$P: file '$file' not found in version control $!\n"; |
---|
| 546 | + } |
---|
521 | 547 | if ($from_filename || ($file ne "&STDIN" && vcs_file_exists($file))) { |
---|
522 | 548 | $file =~ s/^\Q${cur_path}\E//; #strip any absolute path |
---|
523 | 549 | $file =~ s/^\Q${lk_path}\E//; #or the path to the lk tree |
---|
524 | 550 | push(@files, $file); |
---|
525 | | - if ($file ne "MAINTAINERS" && -f $file && ($keywords || $file_emails)) { |
---|
| 551 | + if ($file ne "MAINTAINERS" && -f $file && $keywords) { |
---|
526 | 552 | open(my $f, '<', $file) |
---|
527 | 553 | or die "$P: Can't open $file: $!\n"; |
---|
528 | 554 | my $text = do { local($/) ; <$f> }; |
---|
.. | .. |
---|
533 | 559 | push(@keyword_tvi, $line); |
---|
534 | 560 | } |
---|
535 | 561 | } |
---|
536 | | - } |
---|
537 | | - if ($file_emails) { |
---|
538 | | - my @poss_addr = $text =~ m$[A-Za-zÀ-ÿ\"\' \,\.\+-]*\s*[\,]*\s*[\(\<\{]{0,1}[A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+\.[A-Za-z0-9]+[\)\>\}]{0,1}$g; |
---|
539 | | - push(@file_emails, clean_file_emails(@poss_addr)); |
---|
540 | 562 | } |
---|
541 | 563 | } |
---|
542 | 564 | } else { |
---|
.. | .. |
---|
566 | 588 | my $filename2 = $2; |
---|
567 | 589 | push(@files, $filename1); |
---|
568 | 590 | push(@files, $filename2); |
---|
| 591 | + } elsif (m/^Fixes:\s+([0-9a-fA-F]{6,40})/) { |
---|
| 592 | + push(@fixes, $1) if ($email_fixes); |
---|
569 | 593 | } elsif (m/^\+\+\+\s+(\S+)/ or m/^---\s+(\S+)/) { |
---|
570 | 594 | my $filename = $1; |
---|
571 | 595 | $filename =~ s@^[^/]*/@@; |
---|
.. | .. |
---|
596 | 620 | } |
---|
597 | 621 | |
---|
598 | 622 | @file_emails = uniq(@file_emails); |
---|
| 623 | +@fixes = uniq(@fixes); |
---|
599 | 624 | |
---|
600 | 625 | my %email_hash_name; |
---|
601 | 626 | my %email_hash_address; |
---|
.. | .. |
---|
610 | 635 | my %deduplicate_address_hash = (); |
---|
611 | 636 | |
---|
612 | 637 | my @maintainers = get_maintainers(); |
---|
613 | | - |
---|
614 | 638 | if (@maintainers) { |
---|
615 | 639 | @maintainers = merge_email(@maintainers); |
---|
616 | 640 | output(@maintainers); |
---|
.. | .. |
---|
916 | 940 | print("\n"); |
---|
917 | 941 | } |
---|
918 | 942 | } |
---|
| 943 | + |
---|
| 944 | + maintainers_in_file($file); |
---|
919 | 945 | } |
---|
920 | 946 | |
---|
921 | 947 | if ($keywords) { |
---|
.. | .. |
---|
931 | 957 | |
---|
932 | 958 | foreach my $file (@files) { |
---|
933 | 959 | if ($email && |
---|
934 | | - ($email_git || ($email_git_fallback && |
---|
935 | | - !$exact_pattern_match_hash{$file}))) { |
---|
| 960 | + ($email_git || |
---|
| 961 | + ($email_git_fallback && |
---|
| 962 | + $file !~ /MAINTAINERS$/ && |
---|
| 963 | + !$exact_pattern_match_hash{$file}))) { |
---|
936 | 964 | vcs_file_signoffs($file); |
---|
937 | 965 | } |
---|
938 | 966 | if ($email && $email_git_blame) { |
---|
.. | .. |
---|
961 | 989 | push_email_address($tmp_email, ''); |
---|
962 | 990 | add_role($tmp_email, 'in file'); |
---|
963 | 991 | } |
---|
| 992 | + } |
---|
| 993 | + |
---|
| 994 | + foreach my $fix (@fixes) { |
---|
| 995 | + vcs_add_commit_signers($fix, "blamed_fixes"); |
---|
964 | 996 | } |
---|
965 | 997 | |
---|
966 | 998 | my @to = (); |
---|
.. | .. |
---|
1023 | 1055 | --r => include reviewer(s) if any |
---|
1024 | 1056 | --n => include name 'Full Name <addr\@domain.tld>' |
---|
1025 | 1057 | --l => include list(s) if any |
---|
1026 | | - --s => include subscriber only list(s) if any |
---|
| 1058 | + --moderated => include moderated lists(s) if any (default: true) |
---|
| 1059 | + --s => include subscriber only list(s) if any (default: false) |
---|
1027 | 1060 | --remove-duplicates => minimize duplicate email names/addresses |
---|
1028 | 1061 | --roles => show roles (status:subsystem, git-signer, list, etc...) |
---|
1029 | 1062 | --rolestats => show roles and statistics (commits/total_commits, %) |
---|
1030 | 1063 | --file-emails => add email addresses found in -f file (default: 0 (off)) |
---|
| 1064 | + --fixes => for patches, add signatures of commits with 'Fixes: <commit>' (default: 1 (on)) |
---|
1031 | 1065 | --scm => print SCM tree(s) if any |
---|
1032 | 1066 | --status => print status if any |
---|
1033 | 1067 | --subsystem => print subsystem name if any |
---|
.. | .. |
---|
1313 | 1347 | } else { |
---|
1314 | 1348 | if ($email_list) { |
---|
1315 | 1349 | if (!$hash_list_to{lc($list_address)}) { |
---|
1316 | | - $hash_list_to{lc($list_address)} = 1; |
---|
1317 | 1350 | if ($list_additional =~ m/moderated/) { |
---|
1318 | | - push(@list_to, [$list_address, |
---|
1319 | | - "moderated list${list_role}"]); |
---|
| 1351 | + if ($email_moderated_list) { |
---|
| 1352 | + $hash_list_to{lc($list_address)} = 1; |
---|
| 1353 | + push(@list_to, [$list_address, |
---|
| 1354 | + "moderated list${list_role}"]); |
---|
| 1355 | + } |
---|
1320 | 1356 | } else { |
---|
| 1357 | + $hash_list_to{lc($list_address)} = 1; |
---|
1321 | 1358 | push(@list_to, [$list_address, |
---|
1322 | 1359 | "open list${list_role}"]); |
---|
1323 | 1360 | } |
---|
.. | .. |
---|
1325 | 1362 | } |
---|
1326 | 1363 | } |
---|
1327 | 1364 | } elsif ($ptype eq "M") { |
---|
1328 | | - my ($name, $address) = parse_email($pvalue); |
---|
1329 | | - if ($name eq "") { |
---|
1330 | | - if ($i > 0) { |
---|
1331 | | - my $tv = $typevalue[$i - 1]; |
---|
1332 | | - if ($tv =~ m/^([A-Z]):\s*(.*)/) { |
---|
1333 | | - if ($1 eq "P") { |
---|
1334 | | - $name = $2; |
---|
1335 | | - $pvalue = format_email($name, $address, $email_usename); |
---|
1336 | | - } |
---|
1337 | | - } |
---|
1338 | | - } |
---|
1339 | | - } |
---|
1340 | 1365 | if ($email_maintainer) { |
---|
1341 | 1366 | my $role = get_maintainer_role($i); |
---|
1342 | 1367 | push_email_addresses($pvalue, $role); |
---|
1343 | 1368 | } |
---|
1344 | 1369 | } elsif ($ptype eq "R") { |
---|
1345 | | - my ($name, $address) = parse_email($pvalue); |
---|
1346 | | - if ($name eq "") { |
---|
1347 | | - if ($i > 0) { |
---|
1348 | | - my $tv = $typevalue[$i - 1]; |
---|
1349 | | - if ($tv =~ m/^([A-Z]):\s*(.*)/) { |
---|
1350 | | - if ($1 eq "P") { |
---|
1351 | | - $name = $2; |
---|
1352 | | - $pvalue = format_email($name, $address, $email_usename); |
---|
1353 | | - } |
---|
1354 | | - } |
---|
1355 | | - } |
---|
1356 | | - } |
---|
1357 | 1370 | if ($email_reviewer) { |
---|
1358 | 1371 | my $subsystem = get_subsystem_name($i); |
---|
1359 | 1372 | push_email_addresses($pvalue, "reviewer:$subsystem"); |
---|
.. | .. |
---|
1724 | 1737 | return $vcs_used == 2; |
---|
1725 | 1738 | } |
---|
1726 | 1739 | |
---|
| 1740 | +sub vcs_add_commit_signers { |
---|
| 1741 | + return if (!vcs_exists()); |
---|
| 1742 | + |
---|
| 1743 | + my ($commit, $desc) = @_; |
---|
| 1744 | + my $commit_count = 0; |
---|
| 1745 | + my $commit_authors_ref; |
---|
| 1746 | + my $commit_signers_ref; |
---|
| 1747 | + my $stats_ref; |
---|
| 1748 | + my @commit_authors = (); |
---|
| 1749 | + my @commit_signers = (); |
---|
| 1750 | + my $cmd; |
---|
| 1751 | + |
---|
| 1752 | + $cmd = $VCS_cmds{"find_commit_signers_cmd"}; |
---|
| 1753 | + $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd |
---|
| 1754 | + |
---|
| 1755 | + ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, ""); |
---|
| 1756 | + @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref; |
---|
| 1757 | + @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref; |
---|
| 1758 | + |
---|
| 1759 | + foreach my $signer (@commit_signers) { |
---|
| 1760 | + $signer = deduplicate_email($signer); |
---|
| 1761 | + } |
---|
| 1762 | + |
---|
| 1763 | + vcs_assign($desc, 1, @commit_signers); |
---|
| 1764 | +} |
---|
| 1765 | + |
---|
1727 | 1766 | sub interactive_get_maintainers { |
---|
1728 | 1767 | my ($list_ref) = @_; |
---|
1729 | 1768 | my @list = @$list_ref; |
---|
.. | .. |
---|
1817 | 1856 | tg toggle git entries |
---|
1818 | 1857 | tl toggle open list entries |
---|
1819 | 1858 | ts toggle subscriber list entries |
---|
1820 | | -f emails in file [$file_emails] |
---|
| 1859 | +f emails in file [$email_file_emails] |
---|
1821 | 1860 | k keywords in file [$keywords] |
---|
1822 | 1861 | r remove duplicates [$email_remove_duplicates] |
---|
1823 | 1862 | p# pattern match depth [$pattern_depth] |
---|
.. | .. |
---|
1942 | 1981 | bool_invert(\$email_git_all_signature_types); |
---|
1943 | 1982 | $rerun = 1; |
---|
1944 | 1983 | } elsif ($sel eq "f") { |
---|
1945 | | - bool_invert(\$file_emails); |
---|
| 1984 | + bool_invert(\$email_file_emails); |
---|
1946 | 1985 | $rerun = 1; |
---|
1947 | 1986 | } elsif ($sel eq "r") { |
---|
1948 | 1987 | bool_invert(\$email_remove_duplicates); |
---|