Add an option to map names and emails to their canonical forms via a .mailmap file. This is enabled by default, consistent with the behavior of Git itself. Signed-off-by: Emma Brooks <me@xxxxxxxxxxx> --- Documentation/gitweb.conf.txt | 5 +++ gitweb/gitweb.perl | 81 +++++++++++++++++++++++++++++++++-- 2 files changed, 82 insertions(+), 4 deletions(-) diff --git a/Documentation/gitweb.conf.txt b/Documentation/gitweb.conf.txt index 7963a79ba9..2d7551a6a5 100644 --- a/Documentation/gitweb.conf.txt +++ b/Documentation/gitweb.conf.txt @@ -751,6 +751,11 @@ default font sizes or lineheights are changed (e.g. via adding extra CSS stylesheet in `@stylesheets`), it may be appropriate to change these values. +mailmap:: + Use mailmap to find the canonical name/email for + committers/authors (see linkgit:git-shortlog[1]). Enabled by + default. + highlight:: Server-side syntax highlight support in "blob" view. It requires `$highlight_bin` program to be available (see the description of diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 0959a782ec..1ca495b8b4 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -505,6 +505,12 @@ sub evaluate_uri { 'override' => 0, 'default' => ['']}, + # Enable reading mailmap to determine canonical author + # information. Enabled by default. + 'mailmap' => { + 'override' => 0, + 'default' => [1]}, + # Enable displaying how much time and how many git commands # it took to generate and display page. Disabled by default. # Project specific override is not supported. @@ -3490,6 +3496,63 @@ sub parse_tag { return %tag } +# Contents of mailmap stored as a referance to a hash with keys in the format +# of "name <email>" or "<email>", and values that are hashes containing a +# replacement "name" and/or "email". If set (even if empty) the mailmap has +# already been read. +my $mailmap; + +sub read_mailmap { + my %mailmap = (); + open my $fd, '-|', quote_command( + git_cmd(), 'cat-file', 'blob', 'HEAD:.mailmap') . ' 2> /dev/null' + or die_error(500, 'Failed to read mailmap'); + return \%mailmap if eof $fd; + foreach (split '\n', <$fd>) { + next if (/^#/); + if (/(.*)\s+ <([^<>]+)>\s+ ((?:.*\s+)? <[^<>]+>) (?:\s+\#)/x || + /(.*)\s+ <([^<>]+)>\s+ ((?:.*\s+)? <[^<>]+>)/x) { + # New Name <new@email> <old@email> + # New Name <new@email> Old Name <old@email> + $mailmap{$3} = (); + ($mailmap{$3}{name} = $1) =~ s/^\s+|\s+$//g; + $mailmap{$3}{email} = $2; + } elsif (/(?: <([^<>]+)>\s+ | (.+)\s+ ) (<[^<>]+>) (?:\s+\#)/x || + /(?: <([^<>]+)>\s+ | (.+)\s+ ) (<[^<>]+>)/x) { + # New Name <old@email> + # <new@email> <old@email> + $mailmap{$3} = (); + if ($1) { + $mailmap{$3}{email} = $1; + } else { + ($mailmap{$3}{name} = $2) =~ s/^\s+|\s+$//g; + } + } + } + return \%mailmap; +} + +# Map author name and email based on mailmap. A more specific match +# ("name <email>") is preferred to a less specific one ("<email>"). +sub map_author { + my $name = shift; + my $email = shift; + + if (!$mailmap) { + $mailmap = read_mailmap; + } + + if ($mailmap->{"$name <$email>"}) { + $name = $mailmap->{"$name <$email>"}{name} || $name; + $email = $mailmap->{"$name <$email>"}{email} || $email; + } elsif ($mailmap->{"<$email>"}) { + $name = $mailmap->{"<$email>"}{name} || $name; + $email = $mailmap->{"<$email>"}{email} || $email; + } + + return ($name, $email); +} + sub parse_commit_text { my ($commit_text, $withparents) = @_; my @commit_lines = split '\n', $commit_text; @@ -3517,8 +3580,13 @@ sub parse_commit_text { $co{'author_epoch'} = $2; $co{'author_tz'} = $3; if ($co{'author'} =~ m/^([^<]+) <([^>]*)>/) { - $co{'author_name'} = $1; - $co{'author_email'} = $2; + my ($name, $email) = @_; + if (gitweb_check_feature('mailmap')) { + ($name, $email) = map_author($1, $2); + $co{'author'} = "$name <$email>"; + } + $co{'author_name'} = $name; + $co{'author_email'} = $email; } else { $co{'author_name'} = $co{'author'}; } @@ -3527,8 +3595,13 @@ sub parse_commit_text { $co{'committer_epoch'} = $2; $co{'committer_tz'} = $3; if ($co{'committer'} =~ m/^([^<]+) <([^>]*)>/) { - $co{'committer_name'} = $1; - $co{'committer_email'} = $2; + my ($name, $email) = @_; + if (gitweb_check_feature('mailmap')) { + ($name, $email) = map_author($1, $2); + $co{'committer'} = "$name <$email>"; + } + $co{'committer_name'} = $name; + $co{'committer_email'} = $email; } else { $co{'committer_name'} = $co{'committer'}; }