public release
[git-grep-footer.git] / git-grep-footer
index a868319653246030ea7f83e7540b0e78a7ee2e48..97fbca7748b374a295e1758f43887ce6f14de8ef 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
 use 5.010;
 use strict;
 use warnings;
 use 5.010;
 use strict;
 use warnings;
@@ -7,6 +7,8 @@ use Encode 'decode';
 use Data::Dump 'pp';
 use Getopt::Long qw(:config bundling);
 
 use Data::Dump 'pp';
 use Getopt::Long qw(:config bundling);
 
+our $VERSION = '1.00';
+
 GetOptions(\my %opt,
        'debug!',
        '',  # stdin
 GetOptions(\my %opt,
        'debug!',
        '',  # stdin
@@ -17,6 +19,7 @@ GetOptions(\my %opt,
        'grep|S=s',
        'min|min-count|unique|u:i',
        'max|max-count|show|n:i',
        'grep|S=s',
        'min|min-count|unique|u:i',
        'max|max-count|show|n:i',
+       'hash|H!',
        'version|V'  => sub { Getopt::Long::VersionMessage() },
        'usage|h'    => sub { Getopt::Long::HelpMessage() },
        'help|man|?' => sub { Getopt::Long::HelpMessage(-verbose => 2) },
        'version|V'  => sub { Getopt::Long::VersionMessage() },
        'usage|h'    => sub { Getopt::Long::HelpMessage() },
        'help|man|?' => sub { Getopt::Long::HelpMessage(-verbose => 2) },
@@ -24,30 +27,28 @@ GetOptions(\my %opt,
 
 my $inputstream = $opt{''} ? \*ARGV : eval {
        require Git;
 
 my $inputstream = $opt{''} ? \*ARGV : eval {
        require Git;
-       Git::command_output_pipe('log', '-z', '--pretty=format:%b', @ARGV);
+       Git::command_output_pipe('log', '-z', '--pretty=format:%h%n%b', @ARGV);
 } || die "Automatic git log failed: $@";
 
 local $| = 1;
 local $/ = "\0";
 
 } || die "Automatic git log failed: $@";
 
 local $| = 1;
 local $/ = "\0";
 
-my $HEADERMATCH = qr/ [a-z]+ (?: (?:-\w+)+ | \ by ) | cc | reference /ix;
+my $HEADERMATCH = qr/ [a-z]+ (?: (?:-\w+)+ | \ by ) | cc | reference /imsx;
 
 my (%headercount, @headercache);
 
 while (readline $inputstream) {
 
 my (%headercount, @headercache);
 
 while (readline $inputstream) {
-       s/^([0-9a-f]{4,40})\n//m and
-       my $hash = $1;
+       s/^ ([0-9a-f]{4,40}) \n//msx;
+       my $hash = $opt{hash} ? $1 : undef;
 
        # strip commit seperator
        chomp;
        # skip expensive checks without potential identifier
        m/:/ or next;
        # try to parse as UTF-8
 
        # strip commit seperator
        chomp;
        # skip expensive checks without potential identifier
        m/:/ or next;
        # try to parse as UTF-8
-       eval { $_ = decode(utf8   => $_, Encode::FB_CROAK()) };
+       eval { $_ = decode(utf8   => $_, Encode::FB_CROAK()); return 1 }
        # if invalid, assume it's latin1
        # if invalid, assume it's latin1
-              $_ = decode(cp1252 => $_) if $@;
-
-       my %attr;
+           or $_ = decode(cp1252 => $_);
 
        BLOCK:
        for (reverse split /\n\n/) {
 
        BLOCK:
        for (reverse split /\n\n/) {
@@ -56,14 +57,14 @@ while (readline $inputstream) {
 
                LINE:
                for (split /\n/) {
 
                LINE:
                for (split /\n/) {
-                       next if not /\S/;
+                       next if not m/\S/;
                        my @header = m{
                                ^
                                (?<key> $HEADERMATCH)
                                : \s*
                        my @header = m{
                                ^
                                (?<key> $HEADERMATCH)
                                : \s*
-                               (?<val> \S .+)
+                               (?<val> \S [^\n]+)
                                $
                                $
-                       }imx or do {
+                       }imsx or do {
                                $prefix++;
                                next LINE;
                        };
                                $prefix++;
                                next LINE;
                        };
@@ -74,16 +75,16 @@ while (readline $inputstream) {
                                for ($header[0]) {
                                        tr/ _/-/;
 
                                for ($header[0]) {
                                        tr/ _/-/;
 
-                                       state $BY = qr{ (?: -? b[yu] )? \Z }ix;
-                                       s{^ si (?:ge?n|n?g) (?:e?[dt])? -? (?:of+)? $BY}{Signed-off-by}ix;
-                                       s{^ ack (?:ed|de)?  $BY}{Acked-by}ix;
-                                       s{^ review (?:e?d)? $BY}{Reviewed-by}ix;
-                                       s{^ teste[dt]       $BY}{Tested-by}ix;
+                                       state $BY = qr{ (?: -? b[yu] )? \Z }imsx;
+                                       s{\A si (?:ge?n|n?g) (?:e?[dt])? -? (?:of+)? $BY}{Signed-off-by}imsx;
+                                       s{\A ack (?:ed|de)?  $BY}{Acked-by}imsx;
+                                       s{\A review (?:e?d)? $BY}{Reviewed-by}imsx;
+                                       s{\A teste[dt]       $BY}{Tested-by}imsx;
                                }
                        }
 
                        if (defined $opt{grep}) {
                                }
                        }
 
                        if (defined $opt{grep}) {
-                               $_ ~~ qr/$opt{grep}/i or next LINE;
+                               $_ ~~ qr/$opt{grep}/im or next LINE;
                        }
 
                        given ($opt{simplify} // 'none') {
                        }
 
                        given ($opt{simplify} // 'none') {
@@ -96,7 +97,7 @@ while (readline $inputstream) {
                                        }{<...>}imsx;
                                }
                                when (['var', 'vars', '']) {
                                        }{<...>}imsx;
                                }
                                when (['var', 'vars', '']) {
-                                       when ($header[0] =~ /[ _-] (?: by | to ) $ | ^cc$/imsx) {
+                                       when ($header[0] =~ m/[ _-] (?: by | to ) $ | ^cc$/imsx) {
                                                $header[1] = undef;
                                        }
                                        for ($header[1]) {
                                                $header[1] = undef;
                                        }
                                        for ($header[1]) {
@@ -134,11 +135,11 @@ while (readline $inputstream) {
 
                for (@headers) {
                        my $line = $_->[2] // join(': ', @$_);
 
                for (@headers) {
                        my $line = $_->[2] // join(': ', @$_);
-                       $line =~ s/^/$hash / if defined $hash;
+                       $line =~ s/\A/$hash /msx if defined $hash;
 
                        if (defined $opt{min} or $opt{max} or $opt{count}) {
                                my $counter = \$headercount{ $_->[0] }->{ $_->[1] // '' };
 
                        if (defined $opt{min} or $opt{max} or $opt{count}) {
                                my $counter = \$headercount{ $_->[0] }->{ $_->[1] // '' };
-                               my $excess = $$counter++ - ($opt{min} // 0);
+                               my $excess = ${$counter}++ - ($opt{min} // 0);
                                next if $excess >= ($opt{max} || 1);
                                next if $excess <  0;
                                if ($opt{count}) {
                                next if $excess >= ($opt{max} || 1);
                                next if $excess <  0;
                                if ($opt{count}) {
@@ -234,6 +235,10 @@ Additional samples are optionally given upto the given maximum.
 Prefixes (unique) lines by the number of occurrences.
 Causes output to be buffered until all input has been read (obviously).
 
 Prefixes (unique) lines by the number of occurrences.
 Causes output to be buffered until all input has been read (obviously).
 
+=item -H, --hash
+
+Prefixes the SHA1 hash of the (or a) matching commit.
+
 =back
 
 =head1 EXAMPLES
 =back
 
 =head1 EXAMPLES
@@ -257,7 +262,7 @@ Compare various capitalisations and (mis)spellings of signoffs.
 
 List the ten most frequently used attribute names.
 
 
 List the ten most frequently used attribute names.
 
-=item git-grep-footer -n2 -i -s -- --reverse
+=item git-grep-footer -n2 -i -s --hash -- --reverse
 
 The earliest two usages of each distinct identifier.
 
 
 The earliest two usages of each distinct identifier.