Re: git as an sfc member project
From: Brandon Casey <hidden>
Date: 2016-06-15 22:49:51
I was curious about something like this earlier this week,
and I wrote this little hack.
Got anything better?
---- >8 ----
#!/usr/bin/perl -w
use lib (split(/:/, $ENV{GITPERLLIB} || "/your/git/installation/lib/perl5/site_perl/5.8.8"));
use strict;
use Git;
use Getopt::Std;
$Getopt::Std::STANDARD_HELP_VERSION = 1;
$main::VERSION = 0.1;
sub usage {
my $name;
eval {
require File::Basename;
$name = File::Basename::basename($0);
} or do {
$name = substr $0, rindex($0, '/') + 1;
};
print "Usage: $name [--help] [-flstv] <tree-ish> [paths...]\n";
}
sub main::HELP_MESSAGE {
usage;
print "\nOPTIONS\n\n",
" -f use file centric view\n",
" -l use longer format\n",
" -s use short format\n",
" Both -l and -s produce an even longer format\n",
" -t print total lines at the end\n",
" -v print more information while processing\n",
"\n";
}
sub new_parser {
my $fh = shift;
my $ctx = shift;
return { FH => $fh, CTX => $ctx, buf => ''};
}
sub get_next_line {
my $obj = shift;
my $fh = $obj->{'FH'};
defined($obj->{'buf'} = <$fh>);
}
sub parse_blame_entry {
my $obj = shift;
return () unless get_next_line $obj;
$_ = $obj->{'buf'};
chomp;
my ($sha1, $sourceline, $resultline, $num_lines) = split ' ', $_, 4;
return () unless defined $num_lines;
my %h = ( sha1 => $sha1, sourceline => $sourceline,
resultline => $resultline, lines => $num_lines );
while (get_next_line $obj) {
$_ = $obj->{'buf'};
chomp;
my ($key, $val) = split ' ', $_, 2;
$h{$key} = $val;
last if m/^filename /;
}
return %h;
}
sub parse_blame {
my $obj = shift;
my $authors = shift;
my %commits;
while (my %h = parse_blame_entry $obj) {
if (! exists $commits{$h{'sha1'}}) {
if (! exists $authors->{$h{'author'}}->{$obj->{'filename'}}) {
$authors->{$h{'author'}}->{$obj->{'filename'}} = 0;
}
$commits{$h{'sha1'}} =
\$authors->{$h{'author'}}->{$obj->{'filename'}};
}
${$commits{$h{'sha1'}}} += $h{'lines'};
}
}
sub count_total_lines {
my $authors = shift;
my $lines = 0;
for (values %{$authors}) {
for (values %{$_}) { $lines += $_; }
}
return $lines;
}
sub count_author_lines {
my $authors = shift;
my %alines;
foreach my $author (keys %{$authors}) {
my $lines = 0;
for (values %{$authors->{$author}}) { $lines += $_; }
$alines{$author} = $lines;
}
return %alines;
}
sub count_file_lines {
my $authors = shift;
my %flines;
for (values %{$authors}) {
foreach my $file (keys %{$_}) {
$flines{$file} += $_->{$file};
}
}
return %flines;
}
sub print_short {
my $authors = shift;
my %alines = count_author_lines $authors;
foreach my $author (sort {$alines{$b} <=> $alines{$a}} keys %alines) {
printf "%6d %s\n", $alines{$author}, $author;
}
}
sub print_long {
my $authors = shift;
my %alines = count_author_lines $authors;
foreach my $author (sort {$alines{$b} <=> $alines{$a}} keys %alines) {
print "$author (", $alines{$author}, "):\n";
foreach my $file (sort
{$authors->{$author}->{$b} <=> $authors->{$author}->{$a}}
keys %{$authors->{$author}}) {
printf " %10d %s\n", $authors->{$author}->{$file},
$file;
}
}
}
sub print_longer {
my $authors = shift;
my %alines = count_author_lines $authors;
my $total_lines = count_total_lines $authors;
foreach my $author (sort {$alines{$b} <=> $alines{$a}} keys %alines) {
printf "%s (%d, %.2f%%):\n", $author, $alines{$author},
100. * $alines{$author} / $total_lines;
foreach my $file (sort
{$authors->{$author}->{$b} <=> $authors->{$author}->{$a}}
keys %{$authors->{$author}}) {
printf " %10d (%5.2f%%) %s\n",
$authors->{$author}->{$file},
100. *
$authors->{$author}->{$file} / $alines{$author},
$file;
}
}
}
sub print_with_file_percentage {
my $authors = shift;
my %alines = count_author_lines $authors;
my %flines = count_file_lines $authors;
my $total_lines = count_total_lines $authors;
my $total_files = scalar(keys %flines);
foreach my $author (sort {$alines{$b} <=> $alines{$a}} keys %alines) {
printf "%s (%d lines in %d files, " .
"%.2f%% of all lines, %.2f%% of all files):\n",
$author, $alines{$author},
scalar(keys %{$authors->{$author}}),
100. * $alines{$author} / $total_lines,
100. * scalar(keys %{$authors->{$author}})/$total_files;
foreach my $file (sort
{$authors->{$author}->{$b} <=> $authors->{$author}->{$a}}
keys %{$authors->{$author}}) {
printf " %10d (%6.2f%%) of %6d (%6.2f%%) %s\n",
$authors->{$author}->{$file},
100. *
$authors->{$author}->{$file} / $flines{$file},
$flines{$file},
100. *
$authors->{$author}->{$file} / $alines{$author},
$file;
}
}
}
sub print_with_file_perspective {
my $authors = shift;
my %flines = count_file_lines $authors;
foreach my $file (sort keys %flines) {
my @auths = grep {exists $authors->{$_}->{$file}}
keys %{$authors};
print "$file ($flines{$file}):\n";
foreach my $author (sort
{$authors->{$b}->{$file} <=> $authors->{$a}->{$file}}
@auths) {
printf " %10d %s\n", $authors->{$author}->{$file},
$author;
}
}
}
my $verbose = 0;
my $output_format = 0;
my $show_total = 0;
our ($opt_f, $opt_l, $opt_s, $opt_t, $opt_v);
getopts("flstv") or die "Invalid options specified";
if ($opt_f) {
$output_format = 4;
} elsif ($opt_l && $opt_s) {
$output_format = 3;
} elsif ($opt_l) {
$output_format = 2;
} elsif ($opt_s) {
$output_format = 1;
}
if ($opt_t) {
$show_total = 1;
}
if ($opt_v) {
$verbose = 1;
}
eval {select STDERR; usage; exit 1;} unless $#ARGV >= 0;
my %authors;
my $repo = Git->repository();
my ($fh, $ctx) = $repo->command_output_pipe('ls-tree', '-r', '--name-only', '--', @ARGV);
my $ls_tree = new_parser $fh, $ctx;
while (get_next_line $ls_tree) {
chomp $ls_tree->{'buf'};
my $is_text = `file "$ls_tree->{'buf'}"` =~ m/text/;
if ($is_text) {
if ($verbose) {
print STDERR "Processing file: ", $ls_tree->{'buf'},
"\n";
}
} else {
if ($verbose) {
print STDERR "Skipping non-text file: ",
$ls_tree->{'buf'}, "\n";
}
next;
}
($fh, $ctx) =
$repo->command_output_pipe('blame', '-C', '-C', '-w', '--incremental',
'--', $ls_tree->{'buf'});
my $blame_obj = new_parser $fh, $ctx;
$blame_obj->{'filename'} = $ls_tree->{'buf'};
parse_blame $blame_obj, \%authors;
$repo->command_close_pipe($blame_obj->{'FH'}, $blame_obj->{'CTX'});
}
$repo->command_close_pipe($ls_tree->{'FH'}, $ls_tree->{'CTX'});
if ($output_format == 0) {
print_long \%authors;
} elsif ($output_format == 1) {
print_short \%authors;
} elsif ($output_format == 2) {
print_longer \%authors;
} elsif ($output_format == 3) {
print_with_file_percentage \%authors;
} elsif ($output_format == 4) {
print_with_file_perspective \%authors;
}
print count_total_lines(\%authors), " total lines.\n" if ($show_total);
exit;