|
@@ -1,4 +1,6 @@
|
|
|
#!/usr/bin/env perl
|
|
|
+# SPDX-License-Identifier: GPL-2.0
|
|
|
+#
|
|
|
# (c) 2007, Joe Perches <joe@perches.com>
|
|
|
# created from checkpatch.pl
|
|
|
#
|
|
@@ -7,8 +9,6 @@
|
|
|
#
|
|
|
# usage: perl scripts/get_maintainer.pl [OPTIONS] <patch>
|
|
|
# perl scripts/get_maintainer.pl [OPTIONS] -f <file>
|
|
|
-#
|
|
|
-# Licensed under the terms of the GNU GPL License version 2
|
|
|
|
|
|
use warnings;
|
|
|
use strict;
|
|
@@ -19,6 +19,7 @@ my $V = '0.26';
|
|
|
use Getopt::Long qw(:config no_auto_abbrev);
|
|
|
use Cwd;
|
|
|
use File::Find;
|
|
|
+use File::Spec::Functions;
|
|
|
|
|
|
my $cur_path = fastgetcwd() . '/';
|
|
|
my $lk_path = "./";
|
|
@@ -26,7 +27,9 @@ my $email = 1;
|
|
|
my $email_usename = 1;
|
|
|
my $email_maintainer = 1;
|
|
|
my $email_reviewer = 1;
|
|
|
+my $email_fixes = 1;
|
|
|
my $email_list = 1;
|
|
|
+my $email_moderated_list = 1;
|
|
|
my $email_subscriber_list = 0;
|
|
|
my $email_git_penguin_chiefs = 0;
|
|
|
my $email_git = 0;
|
|
@@ -48,24 +51,31 @@ my $output_roles = 0;
|
|
|
my $output_rolestats = 1;
|
|
|
my $output_section_maxlen = 50;
|
|
|
my $scm = 0;
|
|
|
+my $tree = 1;
|
|
|
my $web = 0;
|
|
|
my $subsystem = 0;
|
|
|
my $status = 0;
|
|
|
my $letters = "";
|
|
|
my $keywords = 1;
|
|
|
my $sections = 0;
|
|
|
-my $file_emails = 0;
|
|
|
+my $email_file_emails = 0;
|
|
|
my $from_filename = 0;
|
|
|
my $pattern_depth = 0;
|
|
|
my $self_test = undef;
|
|
|
my $version = 0;
|
|
|
my $help = 0;
|
|
|
-my $find_maintainer_files = 1;
|
|
|
-
|
|
|
+my $find_maintainer_files = 0;
|
|
|
+my $maintainer_path;
|
|
|
my $vcs_used = 0;
|
|
|
|
|
|
my $exit = 0;
|
|
|
|
|
|
+my @files = ();
|
|
|
+my @fixes = (); # If a patch description includes Fixes: lines
|
|
|
+my @range = ();
|
|
|
+my @keyword_tvi = ();
|
|
|
+my @file_emails = ();
|
|
|
+
|
|
|
my %commit_author_hash;
|
|
|
my %commit_signer_hash;
|
|
|
|
|
@@ -245,6 +255,8 @@ if (!GetOptions(
|
|
|
'r!' => \$email_reviewer,
|
|
|
'n!' => \$email_usename,
|
|
|
'l!' => \$email_list,
|
|
|
+ 'fixes!' => \$email_fixes,
|
|
|
+ 'moderated!' => \$email_moderated_list,
|
|
|
's!' => \$email_subscriber_list,
|
|
|
'multiline!' => \$output_multiline,
|
|
|
'roles!' => \$output_roles,
|
|
@@ -253,14 +265,16 @@ if (!GetOptions(
|
|
|
'subsystem!' => \$subsystem,
|
|
|
'status!' => \$status,
|
|
|
'scm!' => \$scm,
|
|
|
+ 'tree!' => \$tree,
|
|
|
'web!' => \$web,
|
|
|
'letters=s' => \$letters,
|
|
|
'pattern-depth=i' => \$pattern_depth,
|
|
|
'k|keywords!' => \$keywords,
|
|
|
'sections!' => \$sections,
|
|
|
- 'fe|file-emails!' => \$file_emails,
|
|
|
+ 'fe|file-emails!' => \$email_file_emails,
|
|
|
'f|file' => \$from_filename,
|
|
|
'find-maintainer-files' => \$find_maintainer_files,
|
|
|
+ 'mpath|maintainer-path=s' => \$maintainer_path,
|
|
|
'self-test:s' => \$self_test,
|
|
|
'v|version' => \$version,
|
|
|
'h|help|usage' => \$help,
|
|
@@ -317,7 +331,7 @@ if ($email &&
|
|
|
die "$P: Please select at least 1 email option\n";
|
|
|
}
|
|
|
|
|
|
-if (!top_of_kernel_tree($lk_path)) {
|
|
|
+if ($tree && !top_of_kernel_tree($lk_path)) {
|
|
|
die "$P: The current directory does not appear to be "
|
|
|
. "a U-Boot source tree.\n";
|
|
|
}
|
|
@@ -382,26 +396,52 @@ sub find_ignore_git {
|
|
|
read_all_maintainer_files();
|
|
|
|
|
|
sub read_all_maintainer_files {
|
|
|
- if (-d "${lk_path}MAINTAINERS") {
|
|
|
- opendir(DIR, "${lk_path}MAINTAINERS") or die $!;
|
|
|
- my @files = readdir(DIR);
|
|
|
- closedir(DIR);
|
|
|
- foreach my $file (@files) {
|
|
|
- push(@mfiles, "${lk_path}MAINTAINERS/$file") if ($file !~ /^\./);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if ($find_maintainer_files) {
|
|
|
- find( { wanted => \&find_is_maintainer_file,
|
|
|
- preprocess => \&find_ignore_git,
|
|
|
- no_chdir => 1,
|
|
|
- }, "${lk_path}");
|
|
|
+ my $path = "${lk_path}MAINTAINERS";
|
|
|
+ if (defined $maintainer_path) {
|
|
|
+ $path = $maintainer_path;
|
|
|
+ # Perl Cookbook tilde expansion if necessary
|
|
|
+ $path =~ s@^~([^/]*)@ $1 ? (getpwnam($1))[7] : ( $ENV{HOME} || $ENV{LOGDIR} || (getpwuid($<))[7])@ex;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (-d $path) {
|
|
|
+ $path .= '/' if ($path !~ m@/$@);
|
|
|
+ if ($find_maintainer_files) {
|
|
|
+ find( { wanted => \&find_is_maintainer_file,
|
|
|
+ preprocess => \&find_ignore_git,
|
|
|
+ no_chdir => 1,
|
|
|
+ }, "$path");
|
|
|
+ } else {
|
|
|
+ opendir(DIR, "$path") or die $!;
|
|
|
+ my @files = readdir(DIR);
|
|
|
+ closedir(DIR);
|
|
|
+ foreach my $file (@files) {
|
|
|
+ push(@mfiles, "$path$file") if ($file !~ /^\./);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } elsif (-f "$path") {
|
|
|
+ push(@mfiles, "$path");
|
|
|
} else {
|
|
|
- push(@mfiles, "${lk_path}MAINTAINERS") if -f "${lk_path}MAINTAINERS";
|
|
|
+ die "$P: MAINTAINER file not found '$path'\n";
|
|
|
}
|
|
|
-
|
|
|
+ die "$P: No MAINTAINER files found in '$path'\n" if (scalar(@mfiles) == 0);
|
|
|
foreach my $file (@mfiles) {
|
|
|
- read_maintainer_file("$file");
|
|
|
+ read_maintainer_file("$file");
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+sub maintainers_in_file {
|
|
|
+ my ($file) = @_;
|
|
|
+
|
|
|
+ return if ($file =~ m@\bMAINTAINERS$@);
|
|
|
+
|
|
|
+ if (-f $file && ($email_file_emails || $file =~ /\.yaml$/)) {
|
|
|
+ open(my $f, '<', $file)
|
|
|
+ or die "$P: Can't open $file: $!\n";
|
|
|
+ my $text = do { local($/) ; <$f> };
|
|
|
+ close($f);
|
|
|
+
|
|
|
+ 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;
|
|
|
+ push(@file_emails, clean_file_emails(@poss_addr));
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -485,17 +525,13 @@ sub read_mailmap {
|
|
|
|
|
|
## use the filenames on the command line or find the filenames in the patchfiles
|
|
|
|
|
|
-my @files = ();
|
|
|
-my @range = ();
|
|
|
-my @keyword_tvi = ();
|
|
|
-my @file_emails = ();
|
|
|
-
|
|
|
if (!@ARGV) {
|
|
|
push(@ARGV, "&STDIN");
|
|
|
}
|
|
|
|
|
|
foreach my $file (@ARGV) {
|
|
|
if ($file ne "&STDIN") {
|
|
|
+ $file = canonpath($file);
|
|
|
##if $file is a directory and it lacks a trailing slash, add one
|
|
|
if ((-d $file)) {
|
|
|
$file =~ s@([^/])$@$1/@;
|
|
@@ -503,11 +539,14 @@ foreach my $file (@ARGV) {
|
|
|
die "$P: file '${file}' not found\n";
|
|
|
}
|
|
|
}
|
|
|
+ if ($from_filename && (vcs_exists() && !vcs_file_exists($file))) {
|
|
|
+ warn "$P: file '$file' not found in version control $!\n";
|
|
|
+ }
|
|
|
if ($from_filename || ($file ne "&STDIN" && vcs_file_exists($file))) {
|
|
|
$file =~ s/^\Q${cur_path}\E//; #strip any absolute path
|
|
|
$file =~ s/^\Q${lk_path}\E//; #or the path to the lk tree
|
|
|
push(@files, $file);
|
|
|
- if ($file ne "MAINTAINERS" && -f $file && ($keywords || $file_emails)) {
|
|
|
+ if ($file ne "MAINTAINERS" && -f $file && $keywords) {
|
|
|
open(my $f, '<', $file)
|
|
|
or die "$P: Can't open $file: $!\n";
|
|
|
my $text = do { local($/) ; <$f> };
|
|
@@ -519,10 +558,6 @@ foreach my $file (@ARGV) {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- if ($file_emails) {
|
|
|
- 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;
|
|
|
- push(@file_emails, clean_file_emails(@poss_addr));
|
|
|
- }
|
|
|
}
|
|
|
} else {
|
|
|
my $file_cnt = @files;
|
|
@@ -540,7 +575,20 @@ foreach my $file (@ARGV) {
|
|
|
|
|
|
while (<$patch>) {
|
|
|
my $patch_line = $_;
|
|
|
- if (m/^\+\+\+\s+(\S+)/ or m/^---\s+(\S+)/) {
|
|
|
+ if (m/^ mode change [0-7]+ => [0-7]+ (\S+)\s*$/) {
|
|
|
+ my $filename = $1;
|
|
|
+ push(@files, $filename);
|
|
|
+ } elsif (m/^rename (?:from|to) (\S+)\s*$/) {
|
|
|
+ my $filename = $1;
|
|
|
+ push(@files, $filename);
|
|
|
+ } elsif (m/^diff --git a\/(\S+) b\/(\S+)\s*$/) {
|
|
|
+ my $filename1 = $1;
|
|
|
+ my $filename2 = $2;
|
|
|
+ push(@files, $filename1);
|
|
|
+ push(@files, $filename2);
|
|
|
+ } elsif (m/^Fixes:\s+([0-9a-fA-F]{6,40})/) {
|
|
|
+ push(@fixes, $1) if ($email_fixes);
|
|
|
+ } elsif (m/^\+\+\+\s+(\S+)/ or m/^---\s+(\S+)/) {
|
|
|
my $filename = $1;
|
|
|
$filename =~ s@^[^/]*/@@;
|
|
|
$filename =~ s@\n@@;
|
|
@@ -570,6 +618,7 @@ foreach my $file (@ARGV) {
|
|
|
}
|
|
|
|
|
|
@file_emails = uniq(@file_emails);
|
|
|
+@fixes = uniq(@fixes);
|
|
|
|
|
|
my %email_hash_name;
|
|
|
my %email_hash_address;
|
|
@@ -584,7 +633,6 @@ my %deduplicate_name_hash = ();
|
|
|
my %deduplicate_address_hash = ();
|
|
|
|
|
|
my @maintainers = get_maintainers();
|
|
|
-
|
|
|
if (@maintainers) {
|
|
|
@maintainers = merge_email(@maintainers);
|
|
|
output(@maintainers);
|
|
@@ -890,6 +938,8 @@ sub get_maintainers {
|
|
|
print("\n");
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ maintainers_in_file($file);
|
|
|
}
|
|
|
|
|
|
if ($keywords) {
|
|
@@ -905,8 +955,10 @@ sub get_maintainers {
|
|
|
|
|
|
foreach my $file (@files) {
|
|
|
if ($email &&
|
|
|
- ($email_git || ($email_git_fallback &&
|
|
|
- !$exact_pattern_match_hash{$file}))) {
|
|
|
+ ($email_git ||
|
|
|
+ ($email_git_fallback &&
|
|
|
+ $file !~ /MAINTAINERS$/ &&
|
|
|
+ !$exact_pattern_match_hash{$file}))) {
|
|
|
vcs_file_signoffs($file);
|
|
|
}
|
|
|
if ($email && $email_git_blame) {
|
|
@@ -937,6 +989,10 @@ sub get_maintainers {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ foreach my $fix (@fixes) {
|
|
|
+ vcs_add_commit_signers($fix, "blamed_fixes");
|
|
|
+ }
|
|
|
+
|
|
|
my @to = ();
|
|
|
if ($email || $email_list) {
|
|
|
if ($email) {
|
|
@@ -997,11 +1053,13 @@ MAINTAINER field selection options:
|
|
|
--r => include reviewer(s) if any
|
|
|
--n => include name 'Full Name <addr\@domain.tld>'
|
|
|
--l => include list(s) if any
|
|
|
- --s => include subscriber only list(s) if any
|
|
|
+ --moderated => include moderated lists(s) if any (default: true)
|
|
|
+ --s => include subscriber only list(s) if any (default: false)
|
|
|
--remove-duplicates => minimize duplicate email names/addresses
|
|
|
--roles => show roles (status:subsystem, git-signer, list, etc...)
|
|
|
--rolestats => show roles and statistics (commits/total_commits, %)
|
|
|
--file-emails => add email addresses found in -f file (default: 0 (off))
|
|
|
+ --fixes => for patches, add signatures of commits with 'Fixes: <commit>' (default: 1 (on))
|
|
|
--scm => print SCM tree(s) if any
|
|
|
--status => print status if any
|
|
|
--subsystem => print subsystem name if any
|
|
@@ -1018,13 +1076,14 @@ Other options:
|
|
|
--sections => print all of the subsystem sections with pattern matches
|
|
|
--letters => print all matching 'letter' types from all matching sections
|
|
|
--mailmap => use .mailmap file (default: $email_use_mailmap)
|
|
|
+ --no-tree => run without a kernel tree
|
|
|
--self-test => show potential issues with MAINTAINERS file content
|
|
|
--version => show version
|
|
|
--help => show this help information
|
|
|
|
|
|
Default options:
|
|
|
- [--email --nogit --git-fallback --m --r --n --l --multiline --pattern-depth=0
|
|
|
- --remove-duplicates --rolestats]
|
|
|
+ [--email --tree --nogit --git-fallback --m --r --n --l --multiline
|
|
|
+ --pattern-depth=0 --remove-duplicates --rolestats]
|
|
|
|
|
|
Notes:
|
|
|
Using "-f directory" may give unexpected results:
|
|
@@ -1288,11 +1347,14 @@ sub add_categories {
|
|
|
} else {
|
|
|
if ($email_list) {
|
|
|
if (!$hash_list_to{lc($list_address)}) {
|
|
|
- $hash_list_to{lc($list_address)} = 1;
|
|
|
if ($list_additional =~ m/moderated/) {
|
|
|
- push(@list_to, [$list_address,
|
|
|
- "moderated list${list_role}"]);
|
|
|
+ if ($email_moderated_list) {
|
|
|
+ $hash_list_to{lc($list_address)} = 1;
|
|
|
+ push(@list_to, [$list_address,
|
|
|
+ "moderated list${list_role}"]);
|
|
|
+ }
|
|
|
} else {
|
|
|
+ $hash_list_to{lc($list_address)} = 1;
|
|
|
push(@list_to, [$list_address,
|
|
|
"open list${list_role}"]);
|
|
|
}
|
|
@@ -1300,35 +1362,11 @@ sub add_categories {
|
|
|
}
|
|
|
}
|
|
|
} elsif ($ptype eq "M") {
|
|
|
- my ($name, $address) = parse_email($pvalue);
|
|
|
- if ($name eq "") {
|
|
|
- if ($i > 0) {
|
|
|
- my $tv = $typevalue[$i - 1];
|
|
|
- if ($tv =~ m/^([A-Z]):\s*(.*)/) {
|
|
|
- if ($1 eq "P") {
|
|
|
- $name = $2;
|
|
|
- $pvalue = format_email($name, $address, $email_usename);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
if ($email_maintainer) {
|
|
|
my $role = get_maintainer_role($i);
|
|
|
push_email_addresses($pvalue, $role);
|
|
|
}
|
|
|
} elsif ($ptype eq "R") {
|
|
|
- my ($name, $address) = parse_email($pvalue);
|
|
|
- if ($name eq "") {
|
|
|
- if ($i > 0) {
|
|
|
- my $tv = $typevalue[$i - 1];
|
|
|
- if ($tv =~ m/^([A-Z]):\s*(.*)/) {
|
|
|
- if ($1 eq "P") {
|
|
|
- $name = $2;
|
|
|
- $pvalue = format_email($name, $address, $email_usename);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
if ($email_reviewer) {
|
|
|
my $subsystem = get_subsystem_name($i);
|
|
|
push_email_addresses($pvalue, "reviewer:$subsystem");
|
|
@@ -1699,6 +1737,32 @@ sub vcs_is_hg {
|
|
|
return $vcs_used == 2;
|
|
|
}
|
|
|
|
|
|
+sub vcs_add_commit_signers {
|
|
|
+ return if (!vcs_exists());
|
|
|
+
|
|
|
+ my ($commit, $desc) = @_;
|
|
|
+ my $commit_count = 0;
|
|
|
+ my $commit_authors_ref;
|
|
|
+ my $commit_signers_ref;
|
|
|
+ my $stats_ref;
|
|
|
+ my @commit_authors = ();
|
|
|
+ my @commit_signers = ();
|
|
|
+ my $cmd;
|
|
|
+
|
|
|
+ $cmd = $VCS_cmds{"find_commit_signers_cmd"};
|
|
|
+ $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
|
|
|
+
|
|
|
+ ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, "");
|
|
|
+ @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
|
|
|
+ @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
|
|
|
+
|
|
|
+ foreach my $signer (@commit_signers) {
|
|
|
+ $signer = deduplicate_email($signer);
|
|
|
+ }
|
|
|
+
|
|
|
+ vcs_assign($desc, 1, @commit_signers);
|
|
|
+}
|
|
|
+
|
|
|
sub interactive_get_maintainers {
|
|
|
my ($list_ref) = @_;
|
|
|
my @list = @$list_ref;
|
|
@@ -1792,7 +1856,7 @@ tm toggle maintainers
|
|
|
tg toggle git entries
|
|
|
tl toggle open list entries
|
|
|
ts toggle subscriber list entries
|
|
|
-f emails in file [$file_emails]
|
|
|
+f emails in file [$email_file_emails]
|
|
|
k keywords in file [$keywords]
|
|
|
r remove duplicates [$email_remove_duplicates]
|
|
|
p# pattern match depth [$pattern_depth]
|
|
@@ -1917,7 +1981,7 @@ EOT
|
|
|
bool_invert(\$email_git_all_signature_types);
|
|
|
$rerun = 1;
|
|
|
} elsif ($sel eq "f") {
|
|
|
- bool_invert(\$file_emails);
|
|
|
+ bool_invert(\$email_file_emails);
|
|
|
$rerun = 1;
|
|
|
} elsif ($sel eq "r") {
|
|
|
bool_invert(\$email_remove_duplicates);
|