Newer
Older
my $start = find_starting_index($index);
my $end = find_ending_index($index);
push(@subsystem, $typevalue[$start]);
for ($i = $start + 1; $i < $end; $i++) {
my $tv = $typevalue[$i];

Joe Perches
committed
if ($tv =~ m/^(\C):\s*(.*)/) {
my $ptype = $1;
my $pvalue = $2;
if ($ptype eq "L") {

Joe Perches
committed
my $list_address = $pvalue;
my $list_additional = "";
my $list_role = get_list_role($i);
if ($list_role ne "") {
$list_role = ":" . $list_role;
}

Joe Perches
committed
if ($list_address =~ m/([^\s]+)\s+(.*)$/) {
$list_address = $1;
$list_additional = $2;
}
if ($list_additional =~ m/subscribers-only/) {
if (!$hash_list_to{lc($list_address)}) {
$hash_list_to{lc($list_address)} = 1;
push(@list_to, [$list_address,
"subscriber list${list_role}"]);
}
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}"]);
} else {
push(@list_to, [$list_address,
"open list${list_role}"]);
}

Joe Perches
committed
my ($name, $address) = parse_email($pvalue);
if ($name eq "") {
if ($i > 0) {
my $tv = $typevalue[$i - 1];

Joe Perches
committed
if ($tv =~ m/^(\C):\s*(.*)/) {
if ($1 eq "P") {
$name = $2;
$pvalue = format_email($name, $address, $email_usename);
}
}
}
}

Joe Perches
committed
if ($email_maintainer) {
my $role = get_maintainer_role($i);
push_email_addresses($pvalue, $role);
}
} elsif ($ptype eq "T") {
push(@scm, $pvalue);
} elsif ($ptype eq "W") {
push(@web, $pvalue);
} elsif ($ptype eq "S") {
push(@status, $pvalue);
}
}
}
}
sub email_inuse {
my ($name, $address) = @_;
return 1 if (($name eq "") && ($address eq ""));
return 1 if (($name ne "") && exists($email_hash_name{lc($name)}));
return 1 if (($address ne "") && exists($email_hash_address{lc($address)}));

Joe Perches
committed
return 0;
}

Joe Perches
committed
sub push_email_address {
my ($line, $role) = @_;

Joe Perches
committed

Joe Perches
committed
my ($name, $address) = parse_email($line);

Joe Perches
committed
if ($address eq "") {
return 0;
}
if (!$email_remove_duplicates) {
push(@email_to, [format_email($name, $address, $email_usename), $role]);
} elsif (!email_inuse($name, $address)) {
push(@email_to, [format_email($name, $address, $email_usename), $role]);

Joe Perches
committed
$email_hash_name{lc($name)}++ if ($name ne "");
$email_hash_address{lc($address)}++;

Joe Perches
committed
}
return 1;

Joe Perches
committed
}
sub push_email_addresses {
my ($address, $role) = @_;

Joe Perches
committed
my @address_list = ();
if (rfc822_valid($address)) {
push_email_address($address, $role);
} elsif (@address_list = rfc822_validlist($address)) {

Joe Perches
committed
my $array_count = shift(@address_list);
while (my $entry = shift(@address_list)) {
push_email_address($entry, $role);

Joe Perches
committed
}
} else {
if (!push_email_address($address, $role)) {
warn("Invalid MAINTAINERS address: '" . $address . "'\n");
}

Joe Perches
committed
}
}
sub add_role {
my ($line, $role) = @_;
my ($name, $address) = parse_email($line);
my $email = format_email($name, $address, $email_usename);
foreach my $entry (@email_to) {
if ($email_remove_duplicates) {
my ($entry_name, $entry_address) = parse_email($entry->[0]);
if (($name eq $entry_name || $address eq $entry_address)
&& ($role eq "" || !($entry->[1] =~ m/$role/))
) {
if ($entry->[1] eq "") {
$entry->[1] = "$role";
} else {
$entry->[1] = "$entry->[1],$role";
}
}
} else {
if ($email eq $entry->[0]
&& ($role eq "" || !($entry->[1] =~ m/$role/))
) {
if ($entry->[1] eq "") {
$entry->[1] = "$role";
} else {
$entry->[1] = "$entry->[1],$role";
}
}
}
}
}

Joe Perches
committed
foreach my $path (split(/:/, $ENV{PATH})) {
if (-e "$path/$bin") {
return "$path/$bin";
}
}
return "";
}

Joe Perches
committed
sub which_conf {
my ($conf) = @_;
foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) {
if (-e "$path/$conf") {
return "$path/$conf";
}
}
return "";
}

Joe Perches
committed
my ($line) = @_;
my ($name, $address) = parse_email($line);
my $email = format_email($name, $address, 1);
my $real_name = $name;
my $real_address = $address;
if (exists $mailmap->{names}->{$email} ||
exists $mailmap->{addresses}->{$email}) {
if (exists $mailmap->{names}->{$email}) {
$real_name = $mailmap->{names}->{$email};
}
if (exists $mailmap->{addresses}->{$email}) {
$real_address = $mailmap->{addresses}->{$email};
}
} else {
if (exists $mailmap->{names}->{$address}) {
$real_name = $mailmap->{names}->{$address};
}
if (exists $mailmap->{addresses}->{$address}) {
$real_address = $mailmap->{addresses}->{$address};
}
return format_email($real_name, $real_address, 1);
}
sub mailmap {
my (@addresses) = @_;

Joe Perches
committed
my @mapped_emails = ();
foreach my $line (@addresses) {

Joe Perches
committed
push(@mapped_emails, mailmap_email($line));

Joe Perches
committed
merge_by_realname(@mapped_emails) if ($email_use_mailmap);
return @mapped_emails;
}
sub merge_by_realname {
my %address_map;
my (@emails) = @_;

Joe Perches
committed
foreach my $email (@emails) {
my ($name, $address) = parse_email($email);

Joe Perches
committed
if (exists $address_map{$name}) {
$address = $address_map{$name};

Joe Perches
committed
$email = format_email($name, $address, 1);
} else {
$address_map{$name} = $address;
}
sub git_execute_cmd {
my ($cmd) = @_;
my @lines = ();
my $output = `$cmd`;
$output =~ s/^\s*//gm;
@lines = split("\n", $output);
return @lines;
}
sub hg_execute_cmd {
my ($cmd) = @_;
my @lines = ();
my $output = `$cmd`;
@lines = split("\n", $output);
return @lines;
}
sub extract_formatted_signatures {
my (@signature_lines) = @_;
my @type = @signature_lines;
s/\s*(.*):.*/$1/ for (@type);
# cut -f2- -d":"
s/\s*.*:\s*(.+)\s*/$1/ for (@signature_lines);
## Reformat email addresses (with names) to avoid badly written signatures
foreach my $signer (@signature_lines) {

Joe Perches
committed
$signer = deduplicate_email($signer);
}
return (\@type, \@signature_lines);
}
sub vcs_find_signers {
my ($cmd) = @_;
my $commits;
my @lines = ();
my @signatures = ();
@lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
my $pattern = $VCS_cmds{"commit_pattern"};
$commits = grep(/$pattern/, @lines); # of commits
@signatures = grep(/^[ \t]*${signature_pattern}.*\@.*$/, @lines);

Joe Perches
committed
return (0, @signatures) if !@signatures;

Joe Perches
committed
save_commits_by_author(@lines) if ($interactive);
save_commits_by_signer(@lines) if ($interactive);

Joe Perches
committed
if (!$email_git_penguin_chiefs) {
@signatures = grep(!/${penguin_chiefs}/i, @signatures);
}
my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
return ($commits, @$signers_ref);
}

Joe Perches
committed
sub vcs_find_author {
my ($cmd) = @_;
my @lines = ();
@lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
if (!$email_git_penguin_chiefs) {
@lines = grep(!/${penguin_chiefs}/i, @lines);
}
return @lines if !@lines;

Joe Perches
committed
foreach my $line (@lines) {
if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
my $author = $1;
my ($name, $address) = parse_email($author);
$author = format_email($name, $address, 1);
push(@authors, $author);
}

Joe Perches
committed
}
save_commits_by_author(@lines) if ($interactive);
save_commits_by_signer(@lines) if ($interactive);
return @authors;

Joe Perches
committed
}
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
sub vcs_save_commits {
my ($cmd) = @_;
my @lines = ();
my @commits = ();
@lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
foreach my $line (@lines) {
if ($line =~ m/$VCS_cmds{"blame_commit_pattern"}/) {
push(@commits, $1);
}
}
return @commits;
}
sub vcs_blame {
my ($file) = @_;
my $cmd;
my @commits = ();
return @commits if (!(-f $file));
if (@range && $VCS_cmds{"blame_range_cmd"} eq "") {
my @all_commits = ();
$cmd = $VCS_cmds{"blame_file_cmd"};
$cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
@all_commits = vcs_save_commits($cmd);
foreach my $file_range_diff (@range) {
next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
my $diff_file = $1;
my $diff_start = $2;
my $diff_length = $3;
next if ("$file" ne "$diff_file");
for (my $i = $diff_start; $i < $diff_start + $diff_length; $i++) {
push(@commits, $all_commits[$i]);
}
}
} elsif (@range) {
foreach my $file_range_diff (@range) {
next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
my $diff_file = $1;
my $diff_start = $2;
my $diff_length = $3;
next if ("$file" ne "$diff_file");
$cmd = $VCS_cmds{"blame_range_cmd"};
$cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
push(@commits, vcs_save_commits($cmd));
}
} else {
$cmd = $VCS_cmds{"blame_file_cmd"};
$cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
@commits = vcs_save_commits($cmd);
}

Joe Perches
committed
foreach my $commit (@commits) {
$commit =~ s/^\^//g;
}
return @commits;
}
my $printed_novcs = 0;
sub vcs_exists {
%VCS_cmds = %VCS_cmds_git;
return 1 if eval $VCS_cmds{"available"};
%VCS_cmds = %VCS_cmds_hg;
return 2 if eval $VCS_cmds{"available"};
%VCS_cmds = ();
if (!$printed_novcs) {
warn("$P: No supported VCS found. Add --nogit to options?\n");
warn("Using a git repository produces better results.\n");
warn("Try Linus Torvalds' latest git repository using:\n");
warn("git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git\n");
$printed_novcs = 1;
}
return 0;
}

Joe Perches
committed
vcs_exists();
return $vcs_used == 1;
}
sub vcs_is_hg {
return $vcs_used == 2;
}
sub interactive_get_maintainers {
my @list = @$list_ref;
my %authored;
my %signed;
my $maintained = 0;
foreach my $entry (@list) {

Joe Perches
committed
$maintained = 1 if ($entry->[1] =~ /^(maintainer|supporter)/i);
$selected{$count} = 1;
$authored{$count} = 0;
$signed{$count} = 0;
$count++;
}
#menu loop
my $done = 0;
my $print_options = 0;
my $redraw = 1;
while (!$done) {
$count = 0;
if ($redraw) {
printf STDERR "\n%1s %2s %-65s",
"*", "#", "email/list and role:stats";
if ($email_git ||
($email_git_fallback && !$maintained) ||
$email_git_blame) {
print STDERR "auth sign";
}
print STDERR "\n";
foreach my $entry (@list) {
my $email = $entry->[0];
my $role = $entry->[1];
my $sel = "";
$sel = "*" if ($selected{$count});
my $commit_author = $commit_author_hash{$email};
my $commit_signer = $commit_signer_hash{$email};
my $authored = 0;
my $signed = 0;
$authored++ for (@{$commit_author});
$signed++ for (@{$commit_signer});
printf STDERR "%1s %2d %-65s", $sel, $count + 1, $email;
printf STDERR "%4d %4d", $authored, $signed
if ($authored > 0 || $signed > 0);
printf STDERR "\n %s\n", $role;
if ($authored{$count}) {
my $commit_author = $commit_author_hash{$email};
foreach my $ref (@{$commit_author}) {
print STDERR " Author: @{$ref}[1]\n";
if ($signed{$count}) {
my $commit_signer = $commit_signer_hash{$email};
foreach my $ref (@{$commit_signer}) {
print STDERR " @{$ref}[2]: @{$ref}[1]\n";
}
}
$count++;
}
}
my $date_ref = \$email_git_since;
$date_ref = \$email_hg_since if (vcs_is_hg());
if ($print_options) {
$print_options = 0;
if (vcs_exists()) {

Joe Perches
committed
print STDERR <<EOT
Version Control options:
g use git history [$email_git]
gf use git-fallback [$email_git_fallback]
b use git blame [$email_git_blame]
bs use blame signatures [$email_git_blame_signatures]
c# minimum commits [$email_git_min_signatures]
%# min percent [$email_git_min_percent]
d# history to use [$$date_ref]
x# max maintainers [$email_git_max_maintainers]
t all signature types [$email_git_all_signature_types]
m use .mailmap [$email_use_mailmap]
EOT

Joe Perches
committed
print STDERR <<EOT
Additional options:
0 toggle all
tm toggle maintainers
tg toggle git entries
tl toggle open list entries
ts toggle subscriber list entries
f emails in file [$file_emails]
k keywords in file [$keywords]
r remove duplicates [$email_remove_duplicates]
p# pattern match depth [$pattern_depth]
EOT
print STDERR
"\n#(toggle), A#(author), S#(signed) *(all), ^(none), O(options), Y(approve): ";
my $input = <STDIN>;
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
$redraw = 1;
my $rerun = 0;
my @wish = split(/[, ]+/, $input);
foreach my $nr (@wish) {
$nr = lc($nr);
my $sel = substr($nr, 0, 1);
my $str = substr($nr, 1);
my $val = 0;
$val = $1 if $str =~ /^(\d+)$/;
if ($sel eq "y") {
$interactive = 0;
$done = 1;
$output_rolestats = 0;
$output_roles = 0;
last;
} elsif ($nr =~ /^\d+$/ && $nr > 0 && $nr <= $count) {
$selected{$nr - 1} = !$selected{$nr - 1};
} elsif ($sel eq "*" || $sel eq '^') {
my $toggle = 0;
$toggle = 1 if ($sel eq '*');
for (my $i = 0; $i < $count; $i++) {
$selected{$i} = $toggle;
} elsif ($sel eq "0") {
for (my $i = 0; $i < $count; $i++) {
$selected{$i} = !$selected{$i};
}

Joe Perches
committed
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
} elsif ($sel eq "t") {
if (lc($str) eq "m") {
for (my $i = 0; $i < $count; $i++) {
$selected{$i} = !$selected{$i}
if ($list[$i]->[1] =~ /^(maintainer|supporter)/i);
}
} elsif (lc($str) eq "g") {
for (my $i = 0; $i < $count; $i++) {
$selected{$i} = !$selected{$i}
if ($list[$i]->[1] =~ /^(author|commit|signer)/i);
}
} elsif (lc($str) eq "l") {
for (my $i = 0; $i < $count; $i++) {
$selected{$i} = !$selected{$i}
if ($list[$i]->[1] =~ /^(open list)/i);
}
} elsif (lc($str) eq "s") {
for (my $i = 0; $i < $count; $i++) {
$selected{$i} = !$selected{$i}
if ($list[$i]->[1] =~ /^(subscriber list)/i);
}
}
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
} elsif ($sel eq "a") {
if ($val > 0 && $val <= $count) {
$authored{$val - 1} = !$authored{$val - 1};
} elsif ($str eq '*' || $str eq '^') {
my $toggle = 0;
$toggle = 1 if ($str eq '*');
for (my $i = 0; $i < $count; $i++) {
$authored{$i} = $toggle;
}
}
} elsif ($sel eq "s") {
if ($val > 0 && $val <= $count) {
$signed{$val - 1} = !$signed{$val - 1};
} elsif ($str eq '*' || $str eq '^') {
my $toggle = 0;
$toggle = 1 if ($str eq '*');
for (my $i = 0; $i < $count; $i++) {
$signed{$i} = $toggle;
}
}
} elsif ($sel eq "o") {
$print_options = 1;
$redraw = 1;
} elsif ($sel eq "g") {
if ($str eq "f") {
bool_invert(\$email_git_fallback);
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
bool_invert(\$email_git);
}
$rerun = 1;
} elsif ($sel eq "b") {
if ($str eq "s") {
bool_invert(\$email_git_blame_signatures);
} else {
bool_invert(\$email_git_blame);
}
$rerun = 1;
} elsif ($sel eq "c") {
if ($val > 0) {
$email_git_min_signatures = $val;
$rerun = 1;
}
} elsif ($sel eq "x") {
if ($val > 0) {
$email_git_max_maintainers = $val;
$rerun = 1;
}
} elsif ($sel eq "%") {
if ($str ne "" && $val >= 0) {
$email_git_min_percent = $val;
$rerun = 1;
} elsif ($sel eq "d") {
if (vcs_is_git()) {
$email_git_since = $str;
} elsif (vcs_is_hg()) {
$email_hg_since = $str;
}
$rerun = 1;
} elsif ($sel eq "t") {
bool_invert(\$email_git_all_signature_types);
$rerun = 1;
} elsif ($sel eq "f") {
bool_invert(\$file_emails);
$rerun = 1;
} elsif ($sel eq "r") {
bool_invert(\$email_remove_duplicates);
$rerun = 1;

Joe Perches
committed
} elsif ($sel eq "m") {
bool_invert(\$email_use_mailmap);
read_mailmap();
$rerun = 1;
} elsif ($sel eq "k") {
bool_invert(\$keywords);
$rerun = 1;
} elsif ($sel eq "p") {
if ($str ne "" && $val >= 0) {
$pattern_depth = $val;
$rerun = 1;
}
} elsif ($sel eq "h" || $sel eq "?") {
print STDERR <<EOT
Interactive mode allows you to select the various maintainers, submitters,
commit signers and mailing lists that could be CC'd on a patch.
Any *'d entry is selected.
If you have git or hg installed, you can choose to summarize the commit
history of files in the patch. Also, each line of the current file can
be matched to its commit author and that commits signers with blame.
Various knobs exist to control the length of time for active commit
tracking, the maximum number of commit authors and signers to add,
and such.
Enter selections at the prompt until you are satisfied that the selected
maintainers are appropriate. You may enter multiple selections separated
by either commas or spaces.
EOT
} else {
print STDERR "invalid option: '$nr'\n";
$redraw = 0;
}
}
if ($rerun) {
print STDERR "git-blame can be very slow, please have patience..."
if ($email_git_blame);
goto &get_maintainers;
#drop not selected entries
$count = 0;
my @new_emailto = ();
foreach my $entry (@list) {
if ($selected{$count}) {
push(@new_emailto, $list[$count]);
}
$count++;
}
sub bool_invert {
my ($bool_ref) = @_;
if ($$bool_ref) {
$$bool_ref = 0;
} else {
$$bool_ref = 1;
}

Joe Perches
committed
sub deduplicate_email {
my ($email) = @_;
my $matched = 0;
my ($name, $address) = parse_email($email);
$email = format_email($name, $address, 1);
$email = mailmap_email($email);
return $email if (!$email_remove_duplicates);
($name, $address) = parse_email($email);

Joe Perches
committed
if ($name ne "" && $deduplicate_name_hash{lc($name)}) {

Joe Perches
committed
$name = $deduplicate_name_hash{lc($name)}->[0];
$address = $deduplicate_name_hash{lc($name)}->[1];
$matched = 1;
} elsif ($deduplicate_address_hash{lc($address)}) {
$name = $deduplicate_address_hash{lc($address)}->[0];
$address = $deduplicate_address_hash{lc($address)}->[1];
$matched = 1;
}
if (!$matched) {
$deduplicate_name_hash{lc($name)} = [ $name, $address ];
$deduplicate_address_hash{lc($address)} = [ $name, $address ];
}
$email = format_email($name, $address, 1);
$email = mailmap_email($email);
return $email;
}
sub save_commits_by_author {
my (@lines) = @_;
my @authors = ();
my @commits = ();
my @subjects = ();
foreach my $line (@lines) {
if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
my $author = $1;

Joe Perches
committed
$author = deduplicate_email($author);
push(@authors, $author);
}
push(@commits, $1) if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
push(@subjects, $1) if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
}
for (my $i = 0; $i < @authors; $i++) {
my $exists = 0;
foreach my $ref(@{$commit_author_hash{$authors[$i]}}) {
if (@{$ref}[0] eq $commits[$i] &&
@{$ref}[1] eq $subjects[$i]) {
$exists = 1;
last;
}
}
if (!$exists) {
push(@{$commit_author_hash{$authors[$i]}},
[ ($commits[$i], $subjects[$i]) ]);
}
sub save_commits_by_signer {
my (@lines) = @_;
my $commit = "";
my $subject = "";
foreach my $line (@lines) {
$commit = $1 if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
$subject = $1 if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
if ($line =~ /^[ \t]*${signature_pattern}.*\@.*$/) {
my @signatures = ($line);
my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
my @types = @$types_ref;
my @signers = @$signers_ref;
my $type = $types[0];
my $signer = $signers[0];

Joe Perches
committed
$signer = deduplicate_email($signer);
my $exists = 0;
foreach my $ref(@{$commit_signer_hash{$signer}}) {
if (@{$ref}[0] eq $commit &&
@{$ref}[1] eq $subject &&
@{$ref}[2] eq $type) {
$exists = 1;
last;
}
}
if (!$exists) {
push(@{$commit_signer_hash{$signer}},
[ ($commit, $subject, $type) ]);
}
}
}
sub vcs_assign {
my ($role, $divisor, @lines) = @_;
my %hash;
my $count = 0;
return if (@lines <= 0);
if ($divisor <= 0) {
warn("Bad divisor in " . (caller(0))[3] . ": $divisor\n");
$divisor = 1;
@lines = mailmap(@lines);

Joe Perches
committed

Joe Perches
committed
return if (@lines <= 0);

Joe Perches
committed
@lines = sort(@lines);

Joe Perches
committed
# uniq -c
$hash{$_}++ for @lines;

Joe Perches
committed
# sort -rn
foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
my $sign_offs = $hash{$line};
my $percent = $sign_offs * 100 / $divisor;
$percent = 100 if ($percent > 100);
$count++;
last if ($sign_offs < $email_git_min_signatures ||
$count > $email_git_max_maintainers ||
$percent < $email_git_min_percent);
push_email_address($line, '');
if ($output_rolestats) {
my $fmt_percent = sprintf("%.0f", $percent);
add_role($line, "$role:$sign_offs/$divisor=$fmt_percent%");
} else {
add_role($line, $role);
sub vcs_file_signoffs {
my ($file) = @_;
my @signers = ();
my $commits;
$vcs_used = vcs_exists();
return if (!$vcs_used);
my $cmd = $VCS_cmds{"find_signers_cmd"};
$cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd
($commits, @signers) = vcs_find_signers($cmd);

Joe Perches
committed
foreach my $signer (@signers) {
$signer = deduplicate_email($signer);
}
vcs_assign("commit_signer", $commits, @signers);
sub vcs_file_blame {
my @signers = ();

Joe Perches
committed
my @all_commits = ();
my @commits = ();
my $total_commits;

Joe Perches
committed
my $total_lines;
$vcs_used = vcs_exists();
return if (!$vcs_used);

Joe Perches
committed
@all_commits = vcs_blame($file);
@commits = uniq(@all_commits);
$total_commits = @commits;

Joe Perches
committed
$total_lines = @all_commits;
if ($email_git_blame_signatures) {
if (vcs_is_hg()) {
my $commit_count;
my @commit_signers = ();
my $commit = join(" -r ", @commits);
my $cmd;
$cmd = $VCS_cmds{"find_commit_signers_cmd"};
$cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
($commit_count, @commit_signers) = vcs_find_signers($cmd);

Joe Perches
committed
push(@signers, @commit_signers);
} else {
foreach my $commit (@commits) {
my $commit_count;
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) = vcs_find_signers($cmd);
push(@signers, @commit_signers);
}
}
if ($from_filename) {

Joe Perches
committed
if ($output_rolestats) {
my @blame_signers;
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
if (vcs_is_hg()) {{ # Double brace for last exit
my $commit_count;
my @commit_signers = ();
@commits = uniq(@commits);
@commits = sort(@commits);
my $commit = join(" -r ", @commits);
my $cmd;
$cmd = $VCS_cmds{"find_commit_author_cmd"};
$cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
my @lines = ();
@lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
if (!$email_git_penguin_chiefs) {
@lines = grep(!/${penguin_chiefs}/i, @lines);
}
last if !@lines;
my @authors = ();
foreach my $line (@lines) {
if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
my $author = $1;

Joe Perches
committed
$author = deduplicate_email($author);
push(@authors, $author);
}
}
save_commits_by_author(@lines) if ($interactive);
save_commits_by_signer(@lines) if ($interactive);
push(@signers, @authors);
}}
else {
foreach my $commit (@commits) {
my $i;
my $cmd = $VCS_cmds{"find_commit_author_cmd"};
$cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
my @author = vcs_find_author($cmd);
next if !@author;

Joe Perches
committed
my $formatted_author = deduplicate_email($author[0]);
my $count = grep(/$commit/, @all_commits);
for ($i = 0; $i < $count ; $i++) {

Joe Perches
committed
push(@blame_signers, $formatted_author);

Joe Perches
committed
}
}
if (@blame_signers) {
vcs_assign("authored lines", $total_lines, @blame_signers);
}
}

Joe Perches
committed
foreach my $signer (@signers) {
$signer = deduplicate_email($signer);
}
vcs_assign("commits", $total_commits, @signers);
} else {

Joe Perches
committed
foreach my $signer (@signers) {
$signer = deduplicate_email($signer);
}
vcs_assign("modified commits", $total_commits, @signers);
my (@parms) = @_;
my %saw;
@parms = grep(!$saw{$_}++, @parms);
return @parms;
}
sub sort_and_uniq {
my (@parms) = @_;
my %saw;
@parms = sort @parms;
@parms = grep(!$saw{$_}++, @parms);
return @parms;