diff options
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/Makefile.modpost | 12 | ||||
-rwxr-xr-x | scripts/checkpatch.pl | 106 | ||||
-rwxr-xr-x | scripts/get_maintainer.pl | 421 |
3 files changed, 416 insertions, 123 deletions
diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost index f4053dc7b5d6..8f14c81abbc7 100644 --- a/scripts/Makefile.modpost +++ b/scripts/Makefile.modpost | |||
@@ -13,7 +13,6 @@ | |||
13 | # 2) modpost is then used to | 13 | # 2) modpost is then used to |
14 | # 3) create one <module>.mod.c file pr. module | 14 | # 3) create one <module>.mod.c file pr. module |
15 | # 4) create one Module.symvers file with CRC for all exported symbols | 15 | # 4) create one Module.symvers file with CRC for all exported symbols |
16 | # 4a) [CONFIG_MARKERS] create one Module.markers file listing defined markers | ||
17 | # 5) compile all <module>.mod.c files | 16 | # 5) compile all <module>.mod.c files |
18 | # 6) final link of the module to a <module.ko> file | 17 | # 6) final link of the module to a <module.ko> file |
19 | 18 | ||
@@ -59,10 +58,6 @@ include scripts/Makefile.lib | |||
59 | 58 | ||
60 | kernelsymfile := $(objtree)/Module.symvers | 59 | kernelsymfile := $(objtree)/Module.symvers |
61 | modulesymfile := $(firstword $(KBUILD_EXTMOD))/Module.symvers | 60 | modulesymfile := $(firstword $(KBUILD_EXTMOD))/Module.symvers |
62 | kernelmarkersfile := $(objtree)/Module.markers | ||
63 | modulemarkersfile := $(firstword $(KBUILD_EXTMOD))/Module.markers | ||
64 | |||
65 | markersfile = $(if $(KBUILD_EXTMOD),$(modulemarkersfile),$(kernelmarkersfile)) | ||
66 | 61 | ||
67 | # Step 1), find all modules listed in $(MODVERDIR)/ | 62 | # Step 1), find all modules listed in $(MODVERDIR)/ |
68 | __modules := $(sort $(shell grep -h '\.ko' /dev/null $(wildcard $(MODVERDIR)/*.mod))) | 63 | __modules := $(sort $(shell grep -h '\.ko' /dev/null $(wildcard $(MODVERDIR)/*.mod))) |
@@ -85,8 +80,6 @@ modpost = scripts/mod/modpost \ | |||
85 | $(if $(KBUILD_EXTRA_SYMBOLS), $(patsubst %, -e %,$(KBUILD_EXTRA_SYMBOLS))) \ | 80 | $(if $(KBUILD_EXTRA_SYMBOLS), $(patsubst %, -e %,$(KBUILD_EXTRA_SYMBOLS))) \ |
86 | $(if $(KBUILD_EXTMOD),-o $(modulesymfile)) \ | 81 | $(if $(KBUILD_EXTMOD),-o $(modulesymfile)) \ |
87 | $(if $(CONFIG_DEBUG_SECTION_MISMATCH),,-S) \ | 82 | $(if $(CONFIG_DEBUG_SECTION_MISMATCH),,-S) \ |
88 | $(if $(CONFIG_MARKERS),-K $(kernelmarkersfile)) \ | ||
89 | $(if $(CONFIG_MARKERS),-M $(markersfile)) \ | ||
90 | $(if $(KBUILD_EXTMOD)$(KBUILD_MODPOST_WARN),-w) \ | 83 | $(if $(KBUILD_EXTMOD)$(KBUILD_MODPOST_WARN),-w) \ |
91 | $(if $(cross_build),-c) | 84 | $(if $(cross_build),-c) |
92 | 85 | ||
@@ -101,17 +94,12 @@ quiet_cmd_kernel-mod = MODPOST $@ | |||
101 | cmd_kernel-mod = $(modpost) $@ | 94 | cmd_kernel-mod = $(modpost) $@ |
102 | 95 | ||
103 | vmlinux.o: FORCE | 96 | vmlinux.o: FORCE |
104 | @rm -fr $(kernelmarkersfile) | ||
105 | $(call cmd,kernel-mod) | 97 | $(call cmd,kernel-mod) |
106 | 98 | ||
107 | # Declare generated files as targets for modpost | 99 | # Declare generated files as targets for modpost |
108 | $(symverfile): __modpost ; | 100 | $(symverfile): __modpost ; |
109 | $(modules:.ko=.mod.c): __modpost ; | 101 | $(modules:.ko=.mod.c): __modpost ; |
110 | 102 | ||
111 | ifdef CONFIG_MARKERS | ||
112 | $(markersfile): __modpost ; | ||
113 | endif | ||
114 | |||
115 | 103 | ||
116 | # Step 5), compile all *.mod.c files | 104 | # Step 5), compile all *.mod.c files |
117 | 105 | ||
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 2d5ece798c4c..87bbb8bce9bf 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl | |||
@@ -10,7 +10,7 @@ use strict; | |||
10 | my $P = $0; | 10 | my $P = $0; |
11 | $P =~ s@.*/@@g; | 11 | $P =~ s@.*/@@g; |
12 | 12 | ||
13 | my $V = '0.28'; | 13 | my $V = '0.29'; |
14 | 14 | ||
15 | use Getopt::Long qw(:config no_auto_abbrev); | 15 | use Getopt::Long qw(:config no_auto_abbrev); |
16 | 16 | ||
@@ -28,6 +28,41 @@ my $mailback = 0; | |||
28 | my $summary_file = 0; | 28 | my $summary_file = 0; |
29 | my $root; | 29 | my $root; |
30 | my %debug; | 30 | my %debug; |
31 | my $help = 0; | ||
32 | |||
33 | sub help { | ||
34 | my ($exitcode) = @_; | ||
35 | |||
36 | print << "EOM"; | ||
37 | Usage: $P [OPTION]... [FILE]... | ||
38 | Version: $V | ||
39 | |||
40 | Options: | ||
41 | -q, --quiet quiet | ||
42 | --no-tree run without a kernel tree | ||
43 | --no-signoff do not check for 'Signed-off-by' line | ||
44 | --patch treat FILE as patchfile (default) | ||
45 | --emacs emacs compile window format | ||
46 | --terse one line per report | ||
47 | -f, --file treat FILE as regular source file | ||
48 | --subjective, --strict enable more subjective tests | ||
49 | --root=PATH PATH to the kernel tree root | ||
50 | --no-summary suppress the per-file summary | ||
51 | --mailback only produce a report in case of warnings/errors | ||
52 | --summary-file include the filename in summary | ||
53 | --debug KEY=[0|1] turn on/off debugging of KEY, where KEY is one of | ||
54 | 'values', 'possible', 'type', and 'attr' (default | ||
55 | is all off) | ||
56 | --test-only=WORD report only warnings/errors containing WORD | ||
57 | literally | ||
58 | -h, --help, --version display this help and exit | ||
59 | |||
60 | When FILE is - read standard input. | ||
61 | EOM | ||
62 | |||
63 | exit($exitcode); | ||
64 | } | ||
65 | |||
31 | GetOptions( | 66 | GetOptions( |
32 | 'q|quiet+' => \$quiet, | 67 | 'q|quiet+' => \$quiet, |
33 | 'tree!' => \$tree, | 68 | 'tree!' => \$tree, |
@@ -35,7 +70,7 @@ GetOptions( | |||
35 | 'patch!' => \$chk_patch, | 70 | 'patch!' => \$chk_patch, |
36 | 'emacs!' => \$emacs, | 71 | 'emacs!' => \$emacs, |
37 | 'terse!' => \$terse, | 72 | 'terse!' => \$terse, |
38 | 'file!' => \$file, | 73 | 'f|file!' => \$file, |
39 | 'subjective!' => \$check, | 74 | 'subjective!' => \$check, |
40 | 'strict!' => \$check, | 75 | 'strict!' => \$check, |
41 | 'root=s' => \$root, | 76 | 'root=s' => \$root, |
@@ -45,22 +80,16 @@ GetOptions( | |||
45 | 80 | ||
46 | 'debug=s' => \%debug, | 81 | 'debug=s' => \%debug, |
47 | 'test-only=s' => \$tst_only, | 82 | 'test-only=s' => \$tst_only, |
48 | ) or exit; | 83 | 'h|help' => \$help, |
84 | 'version' => \$help | ||
85 | ) or help(1); | ||
86 | |||
87 | help(0) if ($help); | ||
49 | 88 | ||
50 | my $exit = 0; | 89 | my $exit = 0; |
51 | 90 | ||
52 | if ($#ARGV < 0) { | 91 | if ($#ARGV < 0) { |
53 | print "usage: $P [options] patchfile\n"; | 92 | print "$P: no input files\n"; |
54 | print "version: $V\n"; | ||
55 | print "options: -q => quiet\n"; | ||
56 | print " --no-tree => run without a kernel tree\n"; | ||
57 | print " --terse => one line per report\n"; | ||
58 | print " --emacs => emacs compile window format\n"; | ||
59 | print " --file => check a source file\n"; | ||
60 | print " --strict => enable more subjective tests\n"; | ||
61 | print " --root => path to the kernel tree root\n"; | ||
62 | print " --no-summary => suppress the per-file summary\n"; | ||
63 | print " --summary-file => include the filename in summary\n"; | ||
64 | exit(1); | 93 | exit(1); |
65 | } | 94 | } |
66 | 95 | ||
@@ -153,7 +182,7 @@ our $UTF8 = qr { | |||
153 | }x; | 182 | }x; |
154 | 183 | ||
155 | our $typeTypedefs = qr{(?x: | 184 | our $typeTypedefs = qr{(?x: |
156 | (?:__)?(?:u|s|be|le)(?:\d|\d\d)| | 185 | (?:__)?(?:u|s|be|le)(?:8|16|32|64)| |
157 | atomic_t | 186 | atomic_t |
158 | )}; | 187 | )}; |
159 | 188 | ||
@@ -356,6 +385,13 @@ sub sanitise_line { | |||
356 | $off++; | 385 | $off++; |
357 | next; | 386 | next; |
358 | } | 387 | } |
388 | if ($sanitise_quote eq '' && substr($line, $off, 2) eq '//') { | ||
389 | $sanitise_quote = '//'; | ||
390 | |||
391 | substr($res, $off, 2, $sanitise_quote); | ||
392 | $off++; | ||
393 | next; | ||
394 | } | ||
359 | 395 | ||
360 | # A \ in a string means ignore the next character. | 396 | # A \ in a string means ignore the next character. |
361 | if (($sanitise_quote eq "'" || $sanitise_quote eq '"') && | 397 | if (($sanitise_quote eq "'" || $sanitise_quote eq '"') && |
@@ -379,6 +415,8 @@ sub sanitise_line { | |||
379 | #print "c<$c> SQ<$sanitise_quote>\n"; | 415 | #print "c<$c> SQ<$sanitise_quote>\n"; |
380 | if ($off != 0 && $sanitise_quote eq '*/' && $c ne "\t") { | 416 | if ($off != 0 && $sanitise_quote eq '*/' && $c ne "\t") { |
381 | substr($res, $off, 1, $;); | 417 | substr($res, $off, 1, $;); |
418 | } elsif ($off != 0 && $sanitise_quote eq '//' && $c ne "\t") { | ||
419 | substr($res, $off, 1, $;); | ||
382 | } elsif ($off != 0 && $sanitise_quote && $c ne "\t") { | 420 | } elsif ($off != 0 && $sanitise_quote && $c ne "\t") { |
383 | substr($res, $off, 1, 'X'); | 421 | substr($res, $off, 1, 'X'); |
384 | } else { | 422 | } else { |
@@ -386,6 +424,10 @@ sub sanitise_line { | |||
386 | } | 424 | } |
387 | } | 425 | } |
388 | 426 | ||
427 | if ($sanitise_quote eq '//') { | ||
428 | $sanitise_quote = ''; | ||
429 | } | ||
430 | |||
389 | # The pathname on a #include may be surrounded by '<' and '>'. | 431 | # The pathname on a #include may be surrounded by '<' and '>'. |
390 | if ($res =~ /^.\s*\#\s*include\s+\<(.*)\>/) { | 432 | if ($res =~ /^.\s*\#\s*include\s+\<(.*)\>/) { |
391 | my $clean = 'X' x length($1); | 433 | my $clean = 'X' x length($1); |
@@ -1336,6 +1378,18 @@ sub process { | |||
1336 | WARN("adding a line without newline at end of file\n" . $herecurr); | 1378 | WARN("adding a line without newline at end of file\n" . $herecurr); |
1337 | } | 1379 | } |
1338 | 1380 | ||
1381 | # Blackfin: use hi/lo macros | ||
1382 | if ($realfile =~ m@arch/blackfin/.*\.S$@) { | ||
1383 | if ($line =~ /\.[lL][[:space:]]*=.*&[[:space:]]*0x[fF][fF][fF][fF]/) { | ||
1384 | my $herevet = "$here\n" . cat_vet($line) . "\n"; | ||
1385 | ERROR("use the LO() macro, not (... & 0xFFFF)\n" . $herevet); | ||
1386 | } | ||
1387 | if ($line =~ /\.[hH][[:space:]]*=.*>>[[:space:]]*16/) { | ||
1388 | my $herevet = "$here\n" . cat_vet($line) . "\n"; | ||
1389 | ERROR("use the HI() macro, not (... >> 16)\n" . $herevet); | ||
1390 | } | ||
1391 | } | ||
1392 | |||
1339 | # check we are in a valid source file C or perl if not then ignore this hunk | 1393 | # check we are in a valid source file C or perl if not then ignore this hunk |
1340 | next if ($realfile !~ /\.(h|c|pl)$/); | 1394 | next if ($realfile !~ /\.(h|c|pl)$/); |
1341 | 1395 | ||
@@ -1355,6 +1409,16 @@ sub process { | |||
1355 | WARN("CVS style keyword markers, these will _not_ be updated\n". $herecurr); | 1409 | WARN("CVS style keyword markers, these will _not_ be updated\n". $herecurr); |
1356 | } | 1410 | } |
1357 | 1411 | ||
1412 | # Blackfin: don't use __builtin_bfin_[cs]sync | ||
1413 | if ($line =~ /__builtin_bfin_csync/) { | ||
1414 | my $herevet = "$here\n" . cat_vet($line) . "\n"; | ||
1415 | ERROR("use the CSYNC() macro in asm/blackfin.h\n" . $herevet); | ||
1416 | } | ||
1417 | if ($line =~ /__builtin_bfin_ssync/) { | ||
1418 | my $herevet = "$here\n" . cat_vet($line) . "\n"; | ||
1419 | ERROR("use the SSYNC() macro in asm/blackfin.h\n" . $herevet); | ||
1420 | } | ||
1421 | |||
1358 | # Check for potential 'bare' types | 1422 | # Check for potential 'bare' types |
1359 | my ($stat, $cond, $line_nr_next, $remain_next, $off_next); | 1423 | my ($stat, $cond, $line_nr_next, $remain_next, $off_next); |
1360 | if ($realcnt && $line =~ /.\s*\S/) { | 1424 | if ($realcnt && $line =~ /.\s*\S/) { |
@@ -1372,6 +1436,8 @@ sub process { | |||
1372 | # Ignore functions being called | 1436 | # Ignore functions being called |
1373 | } elsif ($s =~ /^.\s*$Ident\s*\(/s) { | 1437 | } elsif ($s =~ /^.\s*$Ident\s*\(/s) { |
1374 | 1438 | ||
1439 | } elsif ($s =~ /^.\s*else\b/s) { | ||
1440 | |||
1375 | # declarations always start with types | 1441 | # declarations always start with types |
1376 | } elsif ($prev_values eq 'E' && $s =~ /^.\s*(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?((?:\s*$Ident)+?)\b(?:\s+$Sparse)?\s*\**\s*(?:$Ident|\(\*[^\)]*\))(?:\s*$Modifier)?\s*(?:;|=|,|\()/s) { | 1442 | } elsif ($prev_values eq 'E' && $s =~ /^.\s*(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?((?:\s*$Ident)+?)\b(?:\s+$Sparse)?\s*\**\s*(?:$Ident|\(\*[^\)]*\))(?:\s*$Modifier)?\s*(?:;|=|,|\()/s) { |
1377 | my $type = $1; | 1443 | my $type = $1; |
@@ -1532,8 +1598,9 @@ sub process { | |||
1532 | $s =~ /^\s*#\s*?/ || | 1598 | $s =~ /^\s*#\s*?/ || |
1533 | $s =~ /^\s*$Ident\s*:/) { | 1599 | $s =~ /^\s*$Ident\s*:/) { |
1534 | $continuation = ($s =~ /^.*?\\\n/) ? 1 : 0; | 1600 | $continuation = ($s =~ /^.*?\\\n/) ? 1 : 0; |
1535 | $s =~ s/^.*?\n//; | 1601 | if ($s =~ s/^.*?\n//) { |
1536 | $cond_lines++; | 1602 | $cond_lines++; |
1603 | } | ||
1537 | } | 1604 | } |
1538 | } | 1605 | } |
1539 | 1606 | ||
@@ -1891,7 +1958,7 @@ sub process { | |||
1891 | # A unary '*' may be const | 1958 | # A unary '*' may be const |
1892 | 1959 | ||
1893 | } elsif ($ctx =~ /.xW/) { | 1960 | } elsif ($ctx =~ /.xW/) { |
1894 | ERROR("Aspace prohibited after that '$op' $at\n" . $hereptr); | 1961 | ERROR("space prohibited after that '$op' $at\n" . $hereptr); |
1895 | } | 1962 | } |
1896 | 1963 | ||
1897 | # unary ++ and unary -- are allowed no space on one side. | 1964 | # unary ++ and unary -- are allowed no space on one side. |
@@ -2243,7 +2310,8 @@ sub process { | |||
2243 | DECLARE_PER_CPU| | 2310 | DECLARE_PER_CPU| |
2244 | DEFINE_PER_CPU| | 2311 | DEFINE_PER_CPU| |
2245 | __typeof__\(| | 2312 | __typeof__\(| |
2246 | \.$Ident\s*=\s* | 2313 | \.$Ident\s*=\s*| |
2314 | ^\"|\"$ | ||
2247 | }x; | 2315 | }x; |
2248 | #print "REST<$rest> dstat<$dstat>\n"; | 2316 | #print "REST<$rest> dstat<$dstat>\n"; |
2249 | if ($rest ne '') { | 2317 | if ($rest ne '') { |
diff --git a/scripts/get_maintainer.pl b/scripts/get_maintainer.pl index 278a45bd45a5..cdb44b63342e 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.20'; |
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,8 @@ 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; | ||
33 | my $email_remove_duplicates = 1; | ||
32 | my $output_multiline = 1; | 34 | my $output_multiline = 1; |
33 | my $output_separator = ", "; | 35 | my $output_separator = ", "; |
34 | my $scm = 0; | 36 | my $scm = 0; |
@@ -36,6 +38,7 @@ my $web = 0; | |||
36 | my $subsystem = 0; | 38 | my $subsystem = 0; |
37 | my $status = 0; | 39 | my $status = 0; |
38 | my $from_filename = 0; | 40 | my $from_filename = 0; |
41 | my $pattern_depth = 0; | ||
39 | my $version = 0; | 42 | my $version = 0; |
40 | my $help = 0; | 43 | my $help = 0; |
41 | 44 | ||
@@ -68,6 +71,8 @@ if (!GetOptions( | |||
68 | 'git-max-maintainers=i' => \$email_git_max_maintainers, | 71 | 'git-max-maintainers=i' => \$email_git_max_maintainers, |
69 | 'git-min-percent=i' => \$email_git_min_percent, | 72 | 'git-min-percent=i' => \$email_git_min_percent, |
70 | 'git-since=s' => \$email_git_since, | 73 | 'git-since=s' => \$email_git_since, |
74 | 'git-blame!' => \$email_git_blame, | ||
75 | 'remove-duplicates!' => \$email_remove_duplicates, | ||
71 | 'm!' => \$email_maintainer, | 76 | 'm!' => \$email_maintainer, |
72 | 'n!' => \$email_usename, | 77 | 'n!' => \$email_usename, |
73 | 'l!' => \$email_list, | 78 | 'l!' => \$email_list, |
@@ -78,6 +83,7 @@ if (!GetOptions( | |||
78 | 'status!' => \$status, | 83 | 'status!' => \$status, |
79 | 'scm!' => \$scm, | 84 | 'scm!' => \$scm, |
80 | 'web!' => \$web, | 85 | 'web!' => \$web, |
86 | 'pattern-depth=i' => \$pattern_depth, | ||
81 | 'f|file' => \$from_filename, | 87 | 'f|file' => \$from_filename, |
82 | 'v|version' => \$version, | 88 | 'v|version' => \$version, |
83 | 'h|help' => \$help, | 89 | 'h|help' => \$help, |
@@ -101,14 +107,19 @@ if ($#ARGV < 0) { | |||
101 | die "$P: argument missing: patchfile or -f file please\n"; | 107 | die "$P: argument missing: patchfile or -f file please\n"; |
102 | } | 108 | } |
103 | 109 | ||
110 | if ($output_separator ne ", ") { | ||
111 | $output_multiline = 0; | ||
112 | } | ||
113 | |||
104 | my $selections = $email + $scm + $status + $subsystem + $web; | 114 | my $selections = $email + $scm + $status + $subsystem + $web; |
105 | if ($selections == 0) { | 115 | if ($selections == 0) { |
106 | usage(); | 116 | usage(); |
107 | die "$P: Missing required option: email, scm, status, subsystem or web\n"; | 117 | die "$P: Missing required option: email, scm, status, subsystem or web\n"; |
108 | } | 118 | } |
109 | 119 | ||
110 | if ($email && ($email_maintainer + $email_list + $email_subscriber_list | 120 | if ($email && |
111 | + $email_git + $email_git_penguin_chiefs) == 0) { | 121 | ($email_maintainer + $email_list + $email_subscriber_list + |
122 | $email_git + $email_git_penguin_chiefs + $email_git_blame) == 0) { | ||
112 | usage(); | 123 | usage(); |
113 | die "$P: Please select at least 1 email option\n"; | 124 | die "$P: Please select at least 1 email option\n"; |
114 | } | 125 | } |
@@ -147,9 +158,36 @@ while (<MAINT>) { | |||
147 | } | 158 | } |
148 | close(MAINT); | 159 | close(MAINT); |
149 | 160 | ||
161 | my %mailmap; | ||
162 | |||
163 | if ($email_remove_duplicates) { | ||
164 | open(MAILMAP, "<${lk_path}.mailmap") || warn "$P: Can't open .mailmap\n"; | ||
165 | while (<MAILMAP>) { | ||
166 | my $line = $_; | ||
167 | |||
168 | next if ($line =~ m/^\s*#/); | ||
169 | next if ($line =~ m/^\s*$/); | ||
170 | |||
171 | my ($name, $address) = parse_email($line); | ||
172 | $line = format_email($name, $address); | ||
173 | |||
174 | next if ($line =~ m/^\s*$/); | ||
175 | |||
176 | if (exists($mailmap{$name})) { | ||
177 | my $obj = $mailmap{$name}; | ||
178 | push(@$obj, $address); | ||
179 | } else { | ||
180 | my @arr = ($address); | ||
181 | $mailmap{$name} = \@arr; | ||
182 | } | ||
183 | } | ||
184 | close(MAILMAP); | ||
185 | } | ||
186 | |||
150 | ## use the filenames on the command line or find the filenames in the patchfiles | 187 | ## use the filenames on the command line or find the filenames in the patchfiles |
151 | 188 | ||
152 | my @files = (); | 189 | my @files = (); |
190 | my @range = (); | ||
153 | 191 | ||
154 | foreach my $file (@ARGV) { | 192 | foreach my $file (@ARGV) { |
155 | ##if $file is a directory and it lacks a trailing slash, add one | 193 | ##if $file is a directory and it lacks a trailing slash, add one |
@@ -162,13 +200,19 @@ foreach my $file (@ARGV) { | |||
162 | push(@files, $file); | 200 | push(@files, $file); |
163 | } else { | 201 | } else { |
164 | my $file_cnt = @files; | 202 | my $file_cnt = @files; |
203 | my $lastfile; | ||
165 | open(PATCH, "<$file") or die "$P: Can't open ${file}\n"; | 204 | open(PATCH, "<$file") or die "$P: Can't open ${file}\n"; |
166 | while (<PATCH>) { | 205 | while (<PATCH>) { |
167 | if (m/^\+\+\+\s+(\S+)/) { | 206 | if (m/^\+\+\+\s+(\S+)/) { |
168 | my $filename = $1; | 207 | my $filename = $1; |
169 | $filename =~ s@^[^/]*/@@; | 208 | $filename =~ s@^[^/]*/@@; |
170 | $filename =~ s@\n@@; | 209 | $filename =~ s@\n@@; |
210 | $lastfile = $filename; | ||
171 | push(@files, $filename); | 211 | push(@files, $filename); |
212 | } elsif (m/^\@\@ -(\d+),(\d+)/) { | ||
213 | if ($email_git_blame) { | ||
214 | push(@range, "$lastfile:$1:$2"); | ||
215 | } | ||
172 | } | 216 | } |
173 | } | 217 | } |
174 | close(PATCH); | 218 | close(PATCH); |
@@ -201,6 +245,7 @@ foreach my $file (@files) { | |||
201 | if ($type eq 'X') { | 245 | if ($type eq 'X') { |
202 | if (file_match_pattern($file, $value)) { | 246 | if (file_match_pattern($file, $value)) { |
203 | $exclude = 1; | 247 | $exclude = 1; |
248 | last; | ||
204 | } | 249 | } |
205 | } | 250 | } |
206 | } | 251 | } |
@@ -208,35 +253,45 @@ foreach my $file (@files) { | |||
208 | 253 | ||
209 | if (!$exclude) { | 254 | if (!$exclude) { |
210 | my $tvi = 0; | 255 | my $tvi = 0; |
256 | my %hash; | ||
211 | foreach my $line (@typevalue) { | 257 | foreach my $line (@typevalue) { |
212 | if ($line =~ m/^(\C):\s*(.*)/) { | 258 | if ($line =~ m/^(\C):\s*(.*)/) { |
213 | my $type = $1; | 259 | my $type = $1; |
214 | my $value = $2; | 260 | my $value = $2; |
215 | if ($type eq 'F') { | 261 | if ($type eq 'F') { |
216 | if (file_match_pattern($file, $value)) { | 262 | if (file_match_pattern($file, $value)) { |
217 | add_categories($tvi); | 263 | my $value_pd = ($value =~ tr@/@@); |
264 | my $file_pd = ($file =~ tr@/@@); | ||
265 | $value_pd++ if (substr($value,-1,1) ne "/"); | ||
266 | if ($pattern_depth == 0 || | ||
267 | (($file_pd - $value_pd) < $pattern_depth)) { | ||
268 | $hash{$tvi} = $value_pd; | ||
269 | } | ||
218 | } | 270 | } |
219 | } | 271 | } |
220 | } | 272 | } |
221 | $tvi++; | 273 | $tvi++; |
222 | } | 274 | } |
275 | foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) { | ||
276 | add_categories($line); | ||
277 | } | ||
223 | } | 278 | } |
224 | 279 | ||
225 | if ($email && $email_git) { | 280 | if ($email && $email_git) { |
226 | recent_git_signoffs($file); | 281 | recent_git_signoffs($file); |
227 | } | 282 | } |
228 | 283 | ||
284 | if ($email && $email_git_blame) { | ||
285 | git_assign_blame($file); | ||
286 | } | ||
229 | } | 287 | } |
230 | 288 | ||
231 | if ($email) { | 289 | if ($email) { |
232 | foreach my $chief (@penguin_chief) { | 290 | foreach my $chief (@penguin_chief) { |
233 | if ($chief =~ m/^(.*):(.*)/) { | 291 | if ($chief =~ m/^(.*):(.*)/) { |
234 | my $email_address; | 292 | my $email_address; |
235 | if ($email_usename) { | 293 | |
236 | $email_address = format_email($1, $2); | 294 | $email_address = format_email($1, $2); |
237 | } else { | ||
238 | $email_address = $2; | ||
239 | } | ||
240 | if ($email_git_penguin_chiefs) { | 295 | if ($email_git_penguin_chiefs) { |
241 | push(@email_to, $email_address); | 296 | push(@email_to, $email_address); |
242 | } else { | 297 | } else { |
@@ -258,22 +313,22 @@ if ($email || $email_list) { | |||
258 | } | 313 | } |
259 | 314 | ||
260 | if ($scm) { | 315 | if ($scm) { |
261 | @scm = sort_and_uniq(@scm); | 316 | @scm = uniq(@scm); |
262 | output(@scm); | 317 | output(@scm); |
263 | } | 318 | } |
264 | 319 | ||
265 | if ($status) { | 320 | if ($status) { |
266 | @status = sort_and_uniq(@status); | 321 | @status = uniq(@status); |
267 | output(@status); | 322 | output(@status); |
268 | } | 323 | } |
269 | 324 | ||
270 | if ($subsystem) { | 325 | if ($subsystem) { |
271 | @subsystem = sort_and_uniq(@subsystem); | 326 | @subsystem = uniq(@subsystem); |
272 | output(@subsystem); | 327 | output(@subsystem); |
273 | } | 328 | } |
274 | 329 | ||
275 | if ($web) { | 330 | if ($web) { |
276 | @web = sort_and_uniq(@web); | 331 | @web = uniq(@web); |
277 | output(@web); | 332 | output(@web); |
278 | } | 333 | } |
279 | 334 | ||
@@ -311,10 +366,12 @@ MAINTAINER field selection options: | |||
311 | --git-max-maintainers => maximum maintainers to add (default: 5) | 366 | --git-max-maintainers => maximum maintainers to add (default: 5) |
312 | --git-min-percent => minimum percentage of commits required (default: 5) | 367 | --git-min-percent => minimum percentage of commits required (default: 5) |
313 | --git-since => git history to use (default: 1-year-ago) | 368 | --git-since => git history to use (default: 1-year-ago) |
369 | --git-blame => use git blame to find modified commits for patch or file | ||
314 | --m => include maintainer(s) if any | 370 | --m => include maintainer(s) if any |
315 | --n => include name 'Full Name <addr\@domain.tld>' | 371 | --n => include name 'Full Name <addr\@domain.tld>' |
316 | --l => include list(s) if any | 372 | --l => include list(s) if any |
317 | --s => include subscriber only list(s) if any | 373 | --s => include subscriber only list(s) if any |
374 | --remove-duplicates => minimize duplicate email names/addresses | ||
318 | --scm => print SCM tree(s) if any | 375 | --scm => print SCM tree(s) if any |
319 | --status => print status if any | 376 | --status => print status if any |
320 | --subsystem => print subsystem name if any | 377 | --subsystem => print subsystem name if any |
@@ -322,24 +379,28 @@ MAINTAINER field selection options: | |||
322 | 379 | ||
323 | Output type options: | 380 | Output type options: |
324 | --separator [, ] => separator for multiple entries on 1 line | 381 | --separator [, ] => separator for multiple entries on 1 line |
382 | using --separator also sets --nomultiline if --separator is not [, ] | ||
325 | --multiline => print 1 entry per line | 383 | --multiline => print 1 entry per line |
326 | 384 | ||
327 | Default options: | ||
328 | [--email --git --m --n --l --multiline] | ||
329 | |||
330 | Other options: | 385 | Other options: |
386 | --pattern-depth => Number of pattern directory traversals (default: 0 (all)) | ||
331 | --version => show version | 387 | --version => show version |
332 | --help => show this help information | 388 | --help => show this help information |
333 | 389 | ||
390 | Default options: | ||
391 | [--email --git --m --n --l --multiline --pattern-depth=0 --remove-duplicates] | ||
392 | |||
334 | Notes: | 393 | Notes: |
335 | Using "-f directory" may give unexpected results: | 394 | Using "-f directory" may give unexpected results: |
336 | 395 | Used with "--git", git signators for _all_ files in and below | |
337 | Used with "--git", git signators for _all_ files in and below | 396 | directory are examined as git recurses directories. |
338 | directory are examined as git recurses directories. | 397 | Any specified X: (exclude) pattern matches are _not_ ignored. |
339 | Any specified X: (exclude) pattern matches are _not_ ignored. | 398 | Used with "--nogit", directory is used as a pattern match, |
340 | Used with "--nogit", directory is used as a pattern match, | 399 | no individual file within the directory or subdirectory |
341 | no individual file within the directory or subdirectory | 400 | is matched. |
342 | is matched. | 401 | Used with "--git-blame", does not iterate all files in directory |
402 | Using "--git-blame" is slow and may add old committers and authors | ||
403 | that are no longer active maintainers to the output. | ||
343 | EOT | 404 | EOT |
344 | } | 405 | } |
345 | 406 | ||
@@ -370,30 +431,100 @@ sub top_of_kernel_tree { | |||
370 | return 0; | 431 | return 0; |
371 | } | 432 | } |
372 | 433 | ||
373 | sub format_email { | 434 | sub parse_email { |
374 | my ($name, $email) = @_; | 435 | my ($formatted_email) = @_; |
436 | |||
437 | my $name = ""; | ||
438 | my $address = ""; | ||
439 | |||
440 | if ($formatted_email =~ /^([^<]+)<(.+\@.*)>.*$/) { | ||
441 | $name = $1; | ||
442 | $address = $2; | ||
443 | } elsif ($formatted_email =~ /^\s*<(.+\@\S*)>.*$/) { | ||
444 | $address = $1; | ||
445 | } elsif ($formatted_email =~ /^(.+\@\S*).*$/) { | ||
446 | $address = $1; | ||
447 | } | ||
375 | 448 | ||
376 | $name =~ s/^\s+|\s+$//g; | 449 | $name =~ s/^\s+|\s+$//g; |
377 | $name =~ s/^\"|\"$//g; | 450 | $name =~ s/^\"|\"$//g; |
378 | $email =~ s/^\s+|\s+$//g; | 451 | $address =~ s/^\s+|\s+$//g; |
452 | |||
453 | if ($name =~ /[^a-z0-9 \.\-]/i) { ##has "must quote" chars | ||
454 | $name =~ s/(?<!\\)"/\\"/g; ##escape quotes | ||
455 | $name = "\"$name\""; | ||
456 | } | ||
457 | |||
458 | return ($name, $address); | ||
459 | } | ||
460 | |||
461 | sub format_email { | ||
462 | my ($name, $address) = @_; | ||
379 | 463 | ||
380 | my $formatted_email = ""; | 464 | my $formatted_email; |
465 | |||
466 | $name =~ s/^\s+|\s+$//g; | ||
467 | $name =~ s/^\"|\"$//g; | ||
468 | $address =~ s/^\s+|\s+$//g; | ||
381 | 469 | ||
382 | if ($name =~ /[^a-z0-9 \.\-]/i) { ##has "must quote" chars | 470 | if ($name =~ /[^a-z0-9 \.\-]/i) { ##has "must quote" chars |
383 | $name =~ s/(?<!\\)"/\\"/g; ##escape quotes | 471 | $name =~ s/(?<!\\)"/\\"/g; ##escape quotes |
384 | $formatted_email = "\"${name}\"\ \<${email}\>"; | 472 | $name = "\"$name\""; |
473 | } | ||
474 | |||
475 | if ($email_usename) { | ||
476 | if ("$name" eq "") { | ||
477 | $formatted_email = "$address"; | ||
478 | } else { | ||
479 | $formatted_email = "$name <${address}>"; | ||
480 | } | ||
385 | } else { | 481 | } else { |
386 | $formatted_email = "${name} \<${email}\>"; | 482 | $formatted_email = $address; |
387 | } | 483 | } |
484 | |||
388 | return $formatted_email; | 485 | return $formatted_email; |
389 | } | 486 | } |
390 | 487 | ||
391 | sub add_categories { | 488 | sub find_starting_index { |
489 | |||
392 | my ($index) = @_; | 490 | my ($index) = @_; |
393 | 491 | ||
394 | $index = $index - 1; | 492 | while ($index > 0) { |
395 | while ($index >= 0) { | ||
396 | my $tv = $typevalue[$index]; | 493 | my $tv = $typevalue[$index]; |
494 | if (!($tv =~ m/^(\C):\s*(.*)/)) { | ||
495 | last; | ||
496 | } | ||
497 | $index--; | ||
498 | } | ||
499 | |||
500 | return $index; | ||
501 | } | ||
502 | |||
503 | sub find_ending_index { | ||
504 | my ($index) = @_; | ||
505 | |||
506 | while ($index < @typevalue) { | ||
507 | my $tv = $typevalue[$index]; | ||
508 | if (!($tv =~ m/^(\C):\s*(.*)/)) { | ||
509 | last; | ||
510 | } | ||
511 | $index++; | ||
512 | } | ||
513 | |||
514 | return $index; | ||
515 | } | ||
516 | |||
517 | sub add_categories { | ||
518 | my ($index) = @_; | ||
519 | |||
520 | my $i; | ||
521 | my $start = find_starting_index($index); | ||
522 | my $end = find_ending_index($index); | ||
523 | |||
524 | push(@subsystem, $typevalue[$start]); | ||
525 | |||
526 | for ($i = $start + 1; $i < $end; $i++) { | ||
527 | my $tv = $typevalue[$i]; | ||
397 | if ($tv =~ m/^(\C):\s*(.*)/) { | 528 | if ($tv =~ m/^(\C):\s*(.*)/) { |
398 | my $ptype = $1; | 529 | my $ptype = $1; |
399 | my $pvalue = $2; | 530 | my $pvalue = $2; |
@@ -414,19 +545,19 @@ sub add_categories { | |||
414 | } | 545 | } |
415 | } | 546 | } |
416 | } elsif ($ptype eq "M") { | 547 | } elsif ($ptype eq "M") { |
417 | my $p_used = 0; | 548 | my ($name, $address) = parse_email($pvalue); |
418 | if ($index >= 0) { | 549 | if ($name eq "") { |
419 | my $tv = $typevalue[$index - 1]; | 550 | if ($i > 0) { |
420 | if ($tv =~ m/^(\C):\s*(.*)/) { | 551 | my $tv = $typevalue[$i - 1]; |
421 | if ($1 eq "P") { | 552 | if ($tv =~ m/^(\C):\s*(.*)/) { |
422 | if ($email_usename) { | 553 | if ($1 eq "P") { |
423 | push_email_address(format_email($2, $pvalue)); | 554 | $name = $2; |
424 | $p_used = 1; | 555 | $pvalue = format_email($name, $address); |
425 | } | 556 | } |
426 | } | 557 | } |
427 | } | 558 | } |
428 | } | 559 | } |
429 | if (!$p_used) { | 560 | if ($email_maintainer) { |
430 | push_email_addresses($pvalue); | 561 | push_email_addresses($pvalue); |
431 | } | 562 | } |
432 | } elsif ($ptype eq "T") { | 563 | } elsif ($ptype eq "T") { |
@@ -436,31 +567,41 @@ sub add_categories { | |||
436 | } elsif ($ptype eq "S") { | 567 | } elsif ($ptype eq "S") { |
437 | push(@status, $pvalue); | 568 | push(@status, $pvalue); |
438 | } | 569 | } |
439 | |||
440 | $index--; | ||
441 | } else { | ||
442 | push(@subsystem,$tv); | ||
443 | $index = -1; | ||
444 | } | 570 | } |
445 | } | 571 | } |
446 | } | 572 | } |
447 | 573 | ||
574 | my %email_hash_name; | ||
575 | my %email_hash_address; | ||
576 | |||
577 | sub email_inuse { | ||
578 | my ($name, $address) = @_; | ||
579 | |||
580 | return 1 if (($name eq "") && ($address eq "")); | ||
581 | return 1 if (($name ne "") && exists($email_hash_name{$name})); | ||
582 | return 1 if (($address ne "") && exists($email_hash_address{$address})); | ||
583 | |||
584 | return 0; | ||
585 | } | ||
586 | |||
448 | sub push_email_address { | 587 | sub push_email_address { |
449 | my ($email_address) = @_; | 588 | my ($line) = @_; |
589 | |||
590 | my ($name, $address) = parse_email($line); | ||
450 | 591 | ||
451 | my $email_name = ""; | 592 | if ($address eq "") { |
452 | if ($email_address =~ m/([^<]+)<(.*\@.*)>$/) { | 593 | return 0; |
453 | $email_name = $1; | ||
454 | $email_address = $2; | ||
455 | } | 594 | } |
456 | 595 | ||
457 | if ($email_maintainer) { | 596 | if (!$email_remove_duplicates) { |
458 | if ($email_usename && $email_name) { | 597 | push(@email_to, format_email($name, $address)); |
459 | push(@email_to, format_email($email_name, $email_address)); | 598 | } elsif (!email_inuse($name, $address)) { |
460 | } else { | 599 | push(@email_to, format_email($name, $address)); |
461 | push(@email_to, $email_address); | 600 | $email_hash_name{$name}++; |
462 | } | 601 | $email_hash_address{$address}++; |
463 | } | 602 | } |
603 | |||
604 | return 1; | ||
464 | } | 605 | } |
465 | 606 | ||
466 | sub push_email_addresses { | 607 | sub push_email_addresses { |
@@ -476,7 +617,9 @@ sub push_email_addresses { | |||
476 | push_email_address($entry); | 617 | push_email_address($entry); |
477 | } | 618 | } |
478 | } else { | 619 | } else { |
479 | warn("Invalid MAINTAINERS address: '" . $address . "'\n"); | 620 | if (!push_email_address($address)) { |
621 | warn("Invalid MAINTAINERS address: '" . $address . "'\n"); | ||
622 | } | ||
480 | } | 623 | } |
481 | } | 624 | } |
482 | 625 | ||
@@ -492,6 +635,32 @@ sub which { | |||
492 | return ""; | 635 | return ""; |
493 | } | 636 | } |
494 | 637 | ||
638 | sub mailmap { | ||
639 | my @lines = @_; | ||
640 | my %hash; | ||
641 | |||
642 | foreach my $line (@lines) { | ||
643 | my ($name, $address) = parse_email($line); | ||
644 | if (!exists($hash{$name})) { | ||
645 | $hash{$name} = $address; | ||
646 | } elsif ($address ne $hash{$name}) { | ||
647 | $address = $hash{$name}; | ||
648 | $line = format_email($name, $address); | ||
649 | } | ||
650 | if (exists($mailmap{$name})) { | ||
651 | my $obj = $mailmap{$name}; | ||
652 | foreach my $map_address (@$obj) { | ||
653 | if (($map_address eq $address) && | ||
654 | ($map_address ne $hash{$name})) { | ||
655 | $line = format_email($name, $hash{$name}); | ||
656 | } | ||
657 | } | ||
658 | } | ||
659 | } | ||
660 | |||
661 | return @lines; | ||
662 | } | ||
663 | |||
495 | sub recent_git_signoffs { | 664 | sub recent_git_signoffs { |
496 | my ($file) = @_; | 665 | my ($file) = @_; |
497 | 666 | ||
@@ -500,6 +669,7 @@ sub recent_git_signoffs { | |||
500 | my $output = ""; | 669 | my $output = ""; |
501 | my $count = 0; | 670 | my $count = 0; |
502 | my @lines = (); | 671 | my @lines = (); |
672 | my %hash; | ||
503 | my $total_sign_offs; | 673 | my $total_sign_offs; |
504 | 674 | ||
505 | if (which("git") eq "") { | 675 | if (which("git") eq "") { |
@@ -513,52 +683,119 @@ sub recent_git_signoffs { | |||
513 | } | 683 | } |
514 | 684 | ||
515 | $cmd = "git log --since=${email_git_since} -- ${file}"; | 685 | $cmd = "git log --since=${email_git_since} -- ${file}"; |
516 | $cmd .= " | grep -Ei \"^[-_ a-z]+by:.*\\\@.*\$\""; | ||
517 | if (!$email_git_penguin_chiefs) { | ||
518 | $cmd .= " | grep -Ev \"${penguin_chiefs}\""; | ||
519 | } | ||
520 | $cmd .= " | cut -f2- -d\":\""; | ||
521 | $cmd .= " | sort | uniq -c | sort -rn"; | ||
522 | 686 | ||
523 | $output = `${cmd}`; | 687 | $output = `${cmd}`; |
524 | $output =~ s/^\s*//gm; | 688 | $output =~ s/^\s*//gm; |
525 | 689 | ||
526 | @lines = split("\n", $output); | 690 | @lines = split("\n", $output); |
527 | 691 | ||
528 | $total_sign_offs = 0; | 692 | @lines = grep(/^[-_ a-z]+by:.*\@.*$/i, @lines); |
693 | if (!$email_git_penguin_chiefs) { | ||
694 | @lines = grep(!/${penguin_chiefs}/i, @lines); | ||
695 | } | ||
696 | # cut -f2- -d":" | ||
697 | s/.*:\s*(.+)\s*/$1/ for (@lines); | ||
698 | |||
699 | $total_sign_offs = @lines; | ||
700 | |||
701 | if ($email_remove_duplicates) { | ||
702 | @lines = mailmap(@lines); | ||
703 | } | ||
704 | |||
705 | @lines = sort(@lines); | ||
706 | |||
707 | # uniq -c | ||
708 | $hash{$_}++ for @lines; | ||
709 | |||
710 | # sort -rn | ||
711 | foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) { | ||
712 | my $sign_offs = $hash{$line}; | ||
713 | $count++; | ||
714 | last if ($sign_offs < $email_git_min_signatures || | ||
715 | $count > $email_git_max_maintainers || | ||
716 | $sign_offs * 100 / $total_sign_offs < $email_git_min_percent); | ||
717 | push_email_address($line); | ||
718 | } | ||
719 | } | ||
720 | |||
721 | sub save_commits { | ||
722 | my ($cmd, @commits) = @_; | ||
723 | my $output; | ||
724 | my @lines = (); | ||
725 | |||
726 | $output = `${cmd}`; | ||
727 | |||
728 | @lines = split("\n", $output); | ||
529 | foreach my $line (@lines) { | 729 | foreach my $line (@lines) { |
530 | if ($line =~ m/([0-9]+)\s+(.*)/) { | 730 | if ($line =~ m/^(\w+) /) { |
531 | $total_sign_offs += $1; | 731 | push (@commits, $1); |
532 | } else { | ||
533 | die("$P: Unexpected git output: ${line}\n"); | ||
534 | } | 732 | } |
535 | } | 733 | } |
734 | return @commits; | ||
735 | } | ||
536 | 736 | ||
537 | foreach my $line (@lines) { | 737 | sub git_assign_blame { |
538 | if ($line =~ m/([0-9]+)\s+(.*)/) { | 738 | my ($file) = @_; |
539 | my $sign_offs = $1; | 739 | |
540 | $line = $2; | 740 | my @lines = (); |
541 | $count++; | 741 | my @commits = (); |
542 | if ($sign_offs < $email_git_min_signatures || | 742 | my $cmd; |
543 | $count > $email_git_max_maintainers || | 743 | my $output; |
544 | $sign_offs * 100 / $total_sign_offs < $email_git_min_percent) { | 744 | my %hash; |
545 | last; | 745 | my $total_sign_offs; |
546 | } | 746 | my $count; |
747 | |||
748 | if (@range) { | ||
749 | foreach my $file_range_diff (@range) { | ||
750 | next if (!($file_range_diff =~ m/(.+):(.+):(.+)/)); | ||
751 | my $diff_file = $1; | ||
752 | my $diff_start = $2; | ||
753 | my $diff_length = $3; | ||
754 | next if (!("$file" eq "$diff_file")); | ||
755 | $cmd = "git blame -l -L $diff_start,+$diff_length $file"; | ||
756 | @commits = save_commits($cmd, @commits); | ||
547 | } | 757 | } |
548 | if ($line =~ m/(.+)<(.+)>/) { | 758 | } else { |
549 | my $git_name = $1; | 759 | if (-f $file) { |
550 | my $git_addr = $2; | 760 | $cmd = "git blame -l $file"; |
551 | if ($email_usename) { | 761 | @commits = save_commits($cmd, @commits); |
552 | push(@email_to, format_email($git_name, $git_addr)); | 762 | } |
553 | } else { | 763 | } |
554 | push(@email_to, $git_addr); | 764 | |
555 | } | 765 | $total_sign_offs = 0; |
556 | } elsif ($line =~ m/<(.+)>/) { | 766 | @commits = uniq(@commits); |
557 | my $git_addr = $1; | 767 | foreach my $commit (@commits) { |
558 | push(@email_to, $git_addr); | 768 | $cmd = "git log -1 ${commit}"; |
559 | } else { | 769 | |
560 | push(@email_to, $line); | 770 | $output = `${cmd}`; |
771 | $output =~ s/^\s*//gm; | ||
772 | @lines = split("\n", $output); | ||
773 | |||
774 | @lines = grep(/^[-_ a-z]+by:.*\@.*$/i, @lines); | ||
775 | if (!$email_git_penguin_chiefs) { | ||
776 | @lines = grep(!/${penguin_chiefs}/i, @lines); | ||
777 | } | ||
778 | |||
779 | # cut -f2- -d":" | ||
780 | s/.*:\s*(.+)\s*/$1/ for (@lines); | ||
781 | |||
782 | $total_sign_offs += @lines; | ||
783 | |||
784 | if ($email_remove_duplicates) { | ||
785 | @lines = mailmap(@lines); | ||
561 | } | 786 | } |
787 | |||
788 | $hash{$_}++ for @lines; | ||
789 | } | ||
790 | |||
791 | $count = 0; | ||
792 | foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) { | ||
793 | my $sign_offs = $hash{$line}; | ||
794 | $count++; | ||
795 | last if ($sign_offs < $email_git_min_signatures || | ||
796 | $count > $email_git_max_maintainers || | ||
797 | $sign_offs * 100 / $total_sign_offs < $email_git_min_percent); | ||
798 | push_email_address($line); | ||
562 | } | 799 | } |
563 | } | 800 | } |
564 | 801 | ||