Skip to content

Instantly share code, notes, and snippets.

@ribasushi
Last active December 7, 2016 18:16
Show Gist options
  • Save ribasushi/600bcbc0c84f5825b1236fb02c57dda3 to your computer and use it in GitHub Desktop.
Save ribasushi/600bcbc0c84f5825b1236fb02c57dda3 to your computer and use it in GitHub Desktop.
Simple perl-centric git blame agregator
#!/usr/bin/env perl
use warnings;
use strict;
use File::Find qw/find/;
use Data::Dumper::Concise;
my $author_stats;
die "Need a list of paths as arguments" unless @ARGV;
find({
wanted => \&stat_file,
follow => 1,
follow_skip => 0,
no_chdir => 1,
}, @ARGV);
my $total;
for my $author_counts (values %$author_stats) {
$total->{$_} += $author_counts->{$_}
for keys %$author_counts;
}
chomp( my $head = `git rev-parse --short HEAD` );
$author_stats->{"As of $head GRAND_TOTAL"} = $total;
print ( "${_} stats => " . Dumper $author_stats->{$_}) for ( (sort {
(
$author_stats->{$b}{code_lines}
||
$author_stats->{$b}{total_lines})
<=>
(
$author_stats->{$a}{code_lines}
||
$author_stats->{$a}{total_lines}
)
} keys %$author_stats)[0..7]);
print "...\n\n";
sub stat_file {
my $fn = $_;
return unless -f $fn;
return unless -s $fn;
return if $fn =~ m|\Q/.git/|;
return unless $fn =~ / \. (?: pl | pm | t | c | h | xs | pod )? $ /xi;
my $line_author;
for (split /\n/, `git blame -C -C -M -w -e $fn`) {
($line_author->{scalar keys %$line_author}) = $_ =~ /\<(.+?)\>/;
}
die "$fn not in git: perhaps you need to \`git [ stash | commit -a | clean -fxd ]\` ?\n"
unless $line_author;
my @file_content = do { local ($/, @ARGV) = ("\n", $fn); <> };
chomp $_ for @file_content;
my ($in_pod, $past_end);
for my $ln_num (0 .. $#file_content) {
my $ln_text = $file_content[$ln_num];
my $is_pod;
if ($ln_text eq '__END__' or $ln_text eq '__DATA__') {
$past_end = 1;
}
elsif (
$ln_text =~ /^\=cut/
and
$ln_num < $#file_content
and
$file_content[$ln_num+1] eq ''
) {
$is_pod = 1;
$in_pod = 0;
}
elsif (
$ln_text =~ /^\=\w/
and
$ln_num < $#file_content
and
$file_content[$ln_num+1] eq ''
) {
$is_pod = $in_pod = 1;
}
else {
$is_pod = $in_pod;
}
my $type =
( $ln_text =~ /^ \s* $/x ) ? "blank"
: $is_pod ? "pod"
: $past_end ? "misc"
: ( $ln_text =~ /^ \s* \#/x ) ? "comment"
: "code"
;
my $as = $author_stats->{$line_author->{$ln_num}} ||= {};
$as->{"${type}_lines"}++;
$as->{total_lines}++;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment