standalone message retrieval unless - given
[git-grep-footer.git] / git-grep-footer
index c39222fd9d6a3280cd817d88d72e1d086bfc8a64..a868319653246030ea7f83e7540b0e78a7ee2e48 100755 (executable)
@@ -9,10 +9,12 @@ use Getopt::Long qw(:config bundling);
 
 GetOptions(\my %opt,
        'debug!',
 
 GetOptions(\my %opt,
        'debug!',
+       '',  # stdin
        'count|c!',
        'simplify|s:s',
        'ignore-case|i!',
        'fuzzy!',
        'count|c!',
        'simplify|s:s',
        'ignore-case|i!',
        'fuzzy!',
+       'grep|S=s',
        'min|min-count|unique|u:i',
        'max|max-count|show|n:i',
        'version|V'  => sub { Getopt::Long::VersionMessage() },
        'min|min-count|unique|u:i',
        'max|max-count|show|n:i',
        'version|V'  => sub { Getopt::Long::VersionMessage() },
@@ -20,14 +22,19 @@ GetOptions(\my %opt,
        'help|man|?' => sub { Getopt::Long::HelpMessage(-verbose => 2) },
 ) or exit 129;
 
        'help|man|?' => sub { Getopt::Long::HelpMessage(-verbose => 2) },
 ) or exit 129;
 
+my $inputstream = $opt{''} ? \*ARGV : eval {
+       require Git;
+       Git::command_output_pipe('log', '-z', '--pretty=format:%b', @ARGV);
+} || die "Automatic git log failed: $@";
+
 local $| = 1;
 local $/ = "\0";
 
 local $| = 1;
 local $/ = "\0";
 
-my $HEADERMATCH = qr/ [a-z]+ (?: (?:-\w+)+ | \ by ) /ix;
+my $HEADERMATCH = qr/ [a-z]+ (?: (?:-\w+)+ | \ by ) | cc | reference /ix;
 
 my (%headercount, @headercache);
 
 
 my (%headercount, @headercache);
 
-while (readline) {
+while (readline $inputstream) {
        s/^([0-9a-f]{4,40})\n//m and
        my $hash = $1;
 
        s/^([0-9a-f]{4,40})\n//m and
        my $hash = $1;
 
@@ -75,6 +82,10 @@ while (readline) {
                                }
                        }
 
                                }
                        }
 
+                       if (defined $opt{grep}) {
+                               $_ ~~ qr/$opt{grep}/i or next LINE;
+                       }
+
                        given ($opt{simplify} // 'none') {
                                when (['email', 'authors']) {
                                        $header[1] =~ s{
                        given ($opt{simplify} // 'none') {
                                when (['email', 'authors']) {
                                        $header[1] =~ s{
@@ -85,7 +96,7 @@ while (readline) {
                                        }{<...>}imsx;
                                }
                                when (['var', 'vars', '']) {
                                        }{<...>}imsx;
                                }
                                when (['var', 'vars', '']) {
-                                       when ($header[0] =~ /[ _-] (?: by | to ) $/imsx) {
+                                       when ($header[0] =~ /[ _-] (?: by | to ) $ | ^cc$/imsx) {
                                                $header[1] = undef;
                                        }
                                        for ($header[1]) {
                                                $header[1] = undef;
                                        }
                                        for ($header[1]) {
@@ -125,7 +136,7 @@ while (readline) {
                        my $line = $_->[2] // join(': ', @$_);
                        $line =~ s/^/$hash / if defined $hash;
 
                        my $line = $_->[2] // join(': ', @$_);
                        $line =~ s/^/$hash / if defined $hash;
 
-                       if (defined $opt{min} or $opt{max}) {
+                       if (defined $opt{min} or $opt{max} or $opt{count}) {
                                my $counter = \$headercount{ $_->[0] }->{ $_->[1] // '' };
                                my $excess = $$counter++ - ($opt{min} // 0);
                                next if $excess >= ($opt{max} || 1);
                                my $counter = \$headercount{ $_->[0] }->{ $_->[1] // '' };
                                my $excess = $$counter++ - ($opt{min} // 0);
                                next if $excess >= ($opt{max} || 1);
@@ -154,7 +165,9 @@ git-grep-footer - Find custom header lines in commit messages
 
 =head1 SYNOPSIS
 
 
 =head1 SYNOPSIS
 
-F<git> log --pretty=%b%x00 | F<git-grep-footer> [OPTIONS]
+F<git-grep-footer> [OPTIONS] [-- <git log options>]
+
+F<git> log -z --pretty=format:%b | F<git-grep-footer> [OPTIONS] -
 
 =head1 DESCRIPTION
 
 
 =head1 DESCRIPTION
 
@@ -163,6 +176,7 @@ a common convention to list custom metadata such as
 C<Signed-off-by> and C<Acked-by>.
 
 Sections are identified by at least one leading keyword containing a dash
 C<Signed-off-by> and C<Acked-by>.
 
 Sections are identified by at least one leading keyword containing a dash
+(or exceptionally recognised)
 followed by a colon.
 
 =head1 OPTIONS
 followed by a colon.
 
 =head1 OPTIONS
@@ -199,6 +213,11 @@ Values will be hidden entirely, so only attribute names remain.
 
 =back
 
 
 =back
 
+=item --grep=<pattern>
+
+Only include lines matching the specified regular expression.
+Case insensitivity can be disabled by prepending C<(?-i)>.
+
 =item -u, --unique[=<threshold>]
 
 Each match is only shown once,
 =item -u, --unique[=<threshold>]
 
 Each match is only shown once,
@@ -210,6 +229,38 @@ The original line is given for each match,
 but simplifications still apply for duplicate determination.
 Additional samples are optionally given upto the given maximum.
 
 but simplifications still apply for duplicate determination.
 Additional samples are optionally given upto the given maximum.
 
+=item -c, --count
+
+Prefixes (unique) lines by the number of occurrences.
+Causes output to be buffered until all input has been read (obviously).
+
+=back
+
+=head1 EXAMPLES
+
+=over
+
+=item git-grep-footer --grep=^ack v2.6.32..v2.6.33
+
+Search for I<Acked-by> lines for version I<v2.6.33>.
+Append C<-uin> to skip reoccurrences.
+
+=item git-grep-footer -u --grep=junio
+
+Show distinct lines mentioning a specific author.
+
+=item git-grep-footer -c --simplify --grep=^si
+
+Compare various capitalisations and (mis)spellings of signoffs.
+
+=item git-grep-footer -c --simplify=all -i | sort -n -r | head -n10
+
+List the ten most frequently used attribute names.
+
+=item git-grep-footer -n2 -i -s -- --reverse
+
+The earliest two usages of each distinct identifier.
+
 =back
 
 =head1 AUTHOR
 =back
 
 =head1 AUTHOR
@@ -218,5 +269,7 @@ Mischa POSLAWSKY <perl@shiar.org>
 
 =head1 LICENSE
 
 
 =head1 LICENSE
 
-Copyright. All rights reserved.
+This software is free software;
+you can redistribute and/or modify it under the terms of the GNU GPL
+version 2 or later.