2011-01-31 12:43:03 -06:00
|
|
|
#!/usr/bin/perl
|
|
|
|
eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
|
|
|
|
if $running_under_some_shell;
|
|
|
|
#!/usr/bin/perl
|
|
|
|
|
|
|
|
use strict;
|
|
|
|
|
|
|
|
my $main_repo="bootstrap";
|
|
|
|
my @pieces=("artwork", "base", "calc", "components",
|
|
|
|
"extensions", "extras", "filters", "help", "impress",
|
|
|
|
"libs-core", "libs-extern", "libs-extern-sys", "libs-gui",
|
2011-04-04 08:12:33 -05:00
|
|
|
"translations", "postprocess", "sdk", "testing", "ure", "writer");
|
2011-01-31 12:43:03 -06:00
|
|
|
|
|
|
|
sub search_bugs($$$$)
|
|
|
|
{
|
|
|
|
my ($pdata, $piece, $commit_id, $line) = @_;
|
|
|
|
|
|
|
|
my $bug = "";
|
|
|
|
my $bug_orig;
|
|
|
|
while (defined $bug) {
|
2011-03-01 14:16:48 -06:00
|
|
|
|
2011-01-31 12:43:03 -06:00
|
|
|
# match fdo#123, rhz#123, i#123
|
|
|
|
if ( $line =~ m/(\w*\#+\d+)/ ) {
|
|
|
|
$bug_orig = $1;
|
|
|
|
$bug = $1;
|
2011-02-02 10:46:51 -06:00
|
|
|
# match #i123#
|
2011-01-31 12:43:03 -06:00
|
|
|
} elsif ( $line =~ m/(\#i)(\d+)(\#)/ ) {
|
|
|
|
$bug_orig = $1 . $2 . $3;
|
2011-02-09 05:37:47 -06:00
|
|
|
$bug = "i#$2";
|
2011-01-31 12:43:03 -06:00
|
|
|
} else {
|
|
|
|
$bug = undef;
|
|
|
|
next;
|
|
|
|
}
|
2011-03-01 14:16:48 -06:00
|
|
|
|
2011-02-09 05:37:47 -06:00
|
|
|
# print " found $bug\n";
|
2011-03-01 14:16:48 -06:00
|
|
|
# remove bug number from the comment; it will be added later a standardized way
|
2011-01-31 12:43:03 -06:00
|
|
|
$bug_orig =~ s/\#/\\#/;
|
|
|
|
$line =~ s/[Rr]esolves:\s*$bug_orig\s*//;
|
|
|
|
$line =~ s/\s*-\s*$bug_orig\s*//;
|
2011-02-02 10:46:51 -06:00
|
|
|
$line =~ s/\(?$bug_orig\)?[:,]?\s*//;
|
2011-03-01 14:16:48 -06:00
|
|
|
|
|
|
|
# bnc# is prefered over n# for novell bugs
|
|
|
|
$bug =~ s/^n\#/bnc#/;
|
|
|
|
# save the bug number
|
2011-01-31 12:43:03 -06:00
|
|
|
%{$pdata->{$piece}{$commit_id}{'bugs'}} = () if (! defined %{$pdata->{$piece}{$commit_id}{'bugs'}});
|
|
|
|
$pdata->{$piece}{$commit_id}{'bugs'}{$bug} = 1;
|
2011-02-14 08:26:38 -06:00
|
|
|
$pdata->{$piece}{$commit_id}{'flags'}{'bug'} = 1;
|
2011-01-31 12:43:03 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
return $line;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub standardize_summary($)
|
|
|
|
{
|
|
|
|
my $line = shift;
|
|
|
|
|
|
|
|
$line =~ s/^\s*//;
|
|
|
|
$line =~ s/\s*$//;
|
|
|
|
|
|
|
|
# lower first letter
|
|
|
|
$line =~ m/(^.)/;
|
|
|
|
my $first_char = lc($1);
|
|
|
|
$line =~ s/^./$first_char/;
|
|
|
|
|
|
|
|
# FIXME: remove do at the end of line
|
|
|
|
# remove bug numbers
|
|
|
|
return $line;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub load_git_log($$$$)
|
|
|
|
{
|
|
|
|
my ($pdata, $repo_dir, $piece, $pgit_args) = @_;
|
|
|
|
|
2011-04-11 10:09:34 -05:00
|
|
|
my $cmd = "cd $repo_dir && git rev-list --pretty=medium " . join ' ', @{$pgit_args};
|
2011-01-31 12:43:03 -06:00
|
|
|
my $commit_id;
|
|
|
|
my $summary;
|
|
|
|
|
2011-02-14 08:26:38 -06:00
|
|
|
print STDERR "Analyzing log from the git repo: $piece...\n";
|
2011-01-31 12:43:03 -06:00
|
|
|
|
|
|
|
open (GIT, "$cmd 2>&1|") || die "Can't run $cmd: $!";
|
|
|
|
%{$pdata->{$piece}} = ();
|
|
|
|
|
|
|
|
while (my $line = <GIT>) {
|
|
|
|
chomp $line;
|
|
|
|
|
2011-02-02 10:46:51 -06:00
|
|
|
if ( $line =~ m/^commit ([0-9a-z]{20})/ ) {
|
2011-01-31 12:43:03 -06:00
|
|
|
$commit_id = "$1";
|
|
|
|
$summary=undef;
|
|
|
|
%{$pdata->{$piece}{"$commit_id"}} = ();
|
2011-02-14 08:26:38 -06:00
|
|
|
%{$pdata->{$piece}{"$commit_id"}{'flags'}} = ();
|
2011-01-31 12:43:03 -06:00
|
|
|
next;
|
|
|
|
}
|
|
|
|
|
2011-02-02 10:46:51 -06:00
|
|
|
if ( $line =~ /^Author:\s*([^\<]*)\<([^\>]*)>/ ) {
|
2011-01-31 12:43:03 -06:00
|
|
|
# get rid of extra empty spaces;
|
|
|
|
my $name = "$1";
|
|
|
|
$name =~ s/\s+$//;
|
|
|
|
die "Error: Author already defined for the commit {$commit_id}\n" if defined ($pdata->{$piece}{$commit_id}{'author'});
|
|
|
|
%{$pdata->{$piece}{$commit_id}{'author'}} = ();
|
|
|
|
$pdata->{$piece}{$commit_id}{'author'}{'name'} = "$name";
|
|
|
|
$pdata->{$piece}{$commit_id}{'author'}{'email'} = "$2";
|
|
|
|
next;
|
|
|
|
}
|
2011-02-02 10:46:51 -06:00
|
|
|
|
|
|
|
if ( $line =~ /^Date:\s+/ ) {
|
2011-01-31 12:43:03 -06:00
|
|
|
# ignore date line
|
|
|
|
next;
|
|
|
|
}
|
2011-02-02 10:46:51 -06:00
|
|
|
|
2011-01-31 12:43:03 -06:00
|
|
|
if ( $line =~ /^\s*$/ ) {
|
|
|
|
# ignore empty line
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
|
|
|
|
$line = search_bugs($pdata, $piece, $commit_id, $line);
|
|
|
|
# FIXME: need to be implemeted
|
|
|
|
# search_keywords($pdata, $line);
|
|
|
|
|
|
|
|
unless (defined $pdata->{$piece}{$commit_id}{'summary'}) {
|
|
|
|
$summary = standardize_summary($line);
|
|
|
|
$pdata->{$piece}{$commit_id}{'summary'} = $summary;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
close GIT;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub get_repo_name($)
|
|
|
|
{
|
|
|
|
my $repo_dir = shift;
|
|
|
|
|
|
|
|
open (GIT_CONFIG, "$repo_dir/.git/config") ||
|
|
|
|
die "can't open \"$$repo_dir/.git/config\" for reading: $!\n";
|
|
|
|
|
|
|
|
while (my $line = <GIT_CONFIG>) {
|
|
|
|
chomp $line;
|
|
|
|
|
|
|
|
if ( $line =~ /^\s*url\s*=\s*(\S+)$/ ) {
|
|
|
|
my $repo_name = "$1";
|
|
|
|
$repo_name = s/.*\///g;
|
|
|
|
return "$repo_name";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
die "Error: can't find repo name in \"$$repo_dir/.git/config\"\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
sub load_data($$$$)
|
|
|
|
{
|
|
|
|
my ($pdata, $top_dir, $piece, $pgit_args) = @_;
|
|
|
|
|
|
|
|
if (defined $piece) {
|
|
|
|
my $piece_dir;
|
|
|
|
if ("$piece" eq "$main_repo") {
|
|
|
|
$piece_dir = "$top_dir";
|
|
|
|
} else {
|
|
|
|
$piece_dir = "$top_dir/clone/$piece";
|
|
|
|
}
|
|
|
|
load_git_log($pdata, $piece_dir, $piece, $pgit_args);
|
|
|
|
} else {
|
|
|
|
load_git_log($pdata, $top_dir, $main_repo, $pgit_args);
|
|
|
|
foreach my $piece (@pieces) {
|
|
|
|
load_git_log($pdata, "$top_dir/clone/$piece", $piece, $pgit_args);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-28 08:51:47 -05:00
|
|
|
sub get_branch_name($)
|
2011-01-31 12:43:03 -06:00
|
|
|
{
|
2011-03-28 08:51:47 -05:00
|
|
|
my ($top_dir) = @_;
|
|
|
|
|
|
|
|
my $branch;
|
|
|
|
my $cmd = "cd $top_dir && git branch";
|
|
|
|
|
|
|
|
print STDERR "Analyzing git branch: ";
|
|
|
|
|
|
|
|
open (GIT, "$cmd 2>&1|") || die "Can't run $cmd: $!";
|
|
|
|
|
|
|
|
while (my $line = <GIT>) {
|
|
|
|
chomp $line;
|
|
|
|
|
|
|
|
if ( $line =~ m/^\*\s*(\S+)/ ) {
|
|
|
|
$branch = "$1";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
close GIT;
|
|
|
|
|
|
|
|
die "Error: did not detect git branch name\n" unless defined ($branch);
|
|
|
|
|
|
|
|
print STDERR "$branch\n";
|
|
|
|
|
|
|
|
return $branch;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub open_log_file($$$)
|
|
|
|
{
|
|
|
|
my ($log_prefix, $log_suffix, $top_dir) = @_;
|
|
|
|
|
|
|
|
my $branch_name = get_branch_name($top_dir);
|
|
|
|
my $logfilename = "$log_prefix-$branch_name-$log_suffix.log";
|
|
|
|
|
|
|
|
if (-f $logfilename) {
|
|
|
|
print "WARNING: The log file already exists: $logfilename\n";
|
|
|
|
print "Do you want to ovewrite it? (Y/n)?\n";
|
|
|
|
my $answer = <STDIN>;
|
|
|
|
chomp $answer;
|
|
|
|
$answer = "y" unless ($answer);
|
|
|
|
die "Please, rename the file or choose another log suffix\n" if ( lc($answer) ne "y" );
|
|
|
|
}
|
|
|
|
|
|
|
|
my $log;
|
|
|
|
open($log, '>', $logfilename) || die "Can't open \"$logfilename\" for writing: $!\n";
|
|
|
|
|
|
|
|
return $log;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub print_summary_in_stat($$$$$$$$)
|
|
|
|
{
|
|
|
|
my ($summary, $pprint_filters, $ppiece_title, $pflags, $pbugs, $pauthors, $prefix, $log) = @_;
|
2011-02-02 10:46:51 -06:00
|
|
|
|
2011-01-31 12:43:03 -06:00
|
|
|
return if ( $summary eq "" );
|
2011-02-14 08:26:38 -06:00
|
|
|
|
|
|
|
# do we want to print this summary at all?
|
|
|
|
my $print;
|
|
|
|
if (%{$pprint_filters}) {
|
|
|
|
foreach my $flag (keys %{$pprint_filters}) {
|
|
|
|
$print = 1 if (defined $pflags->{$flag});
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$print = 1;
|
|
|
|
}
|
|
|
|
return unless (defined $print);
|
2011-01-31 12:43:03 -06:00
|
|
|
|
2011-02-14 08:26:38 -06:00
|
|
|
# print piece title if not done yet
|
|
|
|
if (defined ${$ppiece_title}) {
|
2011-03-28 08:51:47 -05:00
|
|
|
printf $log "${$ppiece_title}\n";
|
2011-02-14 08:26:38 -06:00
|
|
|
${$ppiece_title} = undef;
|
|
|
|
}
|
|
|
|
|
|
|
|
# finally print the summary line
|
2011-01-31 12:43:03 -06:00
|
|
|
my $bugs = "";
|
|
|
|
if ( %{$pbugs} ) {
|
|
|
|
$bugs = " (" . join (", ", keys %{$pbugs}) . ")";
|
|
|
|
}
|
|
|
|
|
|
|
|
my $authors = "";
|
|
|
|
if ( %{$pauthors} ) {
|
|
|
|
$authors = " [" . join (", ", keys %{$pauthors}) . "]";
|
|
|
|
}
|
|
|
|
|
2011-03-28 08:51:47 -05:00
|
|
|
printf $log $prefix . $summary . $bugs . $authors . "\n";
|
2011-01-31 12:43:03 -06:00
|
|
|
}
|
|
|
|
|
2011-03-28 08:51:47 -05:00
|
|
|
sub print_stat($$$)
|
2011-01-31 12:43:03 -06:00
|
|
|
{
|
2011-03-28 08:51:47 -05:00
|
|
|
my ($pdata, $pprint_filters, $log) = @_;
|
2011-01-31 12:43:03 -06:00
|
|
|
|
2011-02-09 05:48:29 -06:00
|
|
|
foreach my $piece ( sort { $a cmp $b } keys %{$pdata}) {
|
2011-01-31 12:43:03 -06:00
|
|
|
# check if this peice has any entries at all
|
2011-02-14 08:26:38 -06:00
|
|
|
my $piece_title = "+ $piece";
|
2011-01-31 12:43:03 -06:00
|
|
|
if ( %{$pdata->{$piece}} ) {
|
|
|
|
my $old_summary="";
|
|
|
|
my %authors = ();
|
|
|
|
my %bugs = ();
|
2011-02-14 08:26:38 -06:00
|
|
|
my %flags = ();
|
2011-01-31 12:43:03 -06:00
|
|
|
foreach my $id ( sort { $pdata->{$piece}{$a}{'summary'} cmp $pdata->{$piece}{$b}{'summary'} } keys %{$pdata->{$piece}}) {
|
|
|
|
my $summary = $pdata->{$piece}{$id}{'summary'};
|
|
|
|
if ($summary ne $old_summary) {
|
2011-03-28 08:51:47 -05:00
|
|
|
print_summary_in_stat($old_summary, $pprint_filters, \$piece_title, \%flags, \%bugs, \%authors, " + ", $log);
|
2011-01-31 12:43:03 -06:00
|
|
|
$old_summary = $summary;
|
|
|
|
%authors = ();
|
|
|
|
%bugs = ();
|
2011-02-14 08:26:38 -06:00
|
|
|
%flags = ();
|
2011-01-31 12:43:03 -06:00
|
|
|
}
|
2011-02-14 08:26:38 -06:00
|
|
|
# collect bug numbers
|
2011-01-31 12:43:03 -06:00
|
|
|
if (defined $pdata->{$piece}{$id}{'bugs'}) {
|
|
|
|
foreach my $bug (keys %{$pdata->{$piece}{$id}{'bugs'}}) {
|
|
|
|
$bugs{$bug} = 1;
|
|
|
|
}
|
|
|
|
}
|
2011-02-14 08:26:38 -06:00
|
|
|
# collect author names
|
2011-01-31 12:43:03 -06:00
|
|
|
my $author = $pdata->{$piece}{$id}{'author'}{'name'};
|
|
|
|
$authors{$author} = 1;
|
2011-02-14 08:26:38 -06:00
|
|
|
# collect flags
|
|
|
|
foreach my $flag ( keys %{$pdata->{$piece}{$id}{'flags'}} ) {
|
|
|
|
$flags{$flag} = 1;
|
|
|
|
}
|
2011-01-31 12:43:03 -06:00
|
|
|
}
|
2011-03-28 08:51:47 -05:00
|
|
|
print_summary_in_stat($old_summary, $pprint_filters, \$piece_title, \%flags, \%bugs, \%authors, " + ", $log);
|
2011-01-31 12:43:03 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
########################################################################
|
|
|
|
# help
|
|
|
|
|
|
|
|
sub usage()
|
|
|
|
{
|
|
|
|
print "This script generates LO git commit summary\n\n" .
|
|
|
|
|
2011-03-28 08:51:47 -05:00
|
|
|
"Usage: lo-commit-stat [--help] [--no-pieces] [--piece=<piece>] --log-suffix=<string> topdir [git_log_param...]\n\n" .
|
2011-01-31 12:43:03 -06:00
|
|
|
|
|
|
|
"Options:\n" .
|
|
|
|
" --help print this help\n" .
|
|
|
|
" --no-pieces read changes just from the main repository, ignore other cloned repos\n" .
|
|
|
|
" --piece=<piece> summarize just chnages from the given piece\n" .
|
2011-03-28 08:51:47 -05:00
|
|
|
" --log-suffix=<string> suffix of the log file name; the result will be\n" .
|
|
|
|
" commit-log-<branch>-<log-name-suffix>.log; the branch name\n" .
|
|
|
|
" is detected autoamtically\n" .
|
2011-02-14 08:26:38 -06:00
|
|
|
" --bugs print just bug fixes\n" .
|
2011-01-31 12:43:03 -06:00
|
|
|
" topdir directory with the libreoffice/bootstrap clone; the piece repos\n" .
|
|
|
|
" must be cloned in the main-repo-root/clone/<piece> subdirectories\n" .
|
|
|
|
" git_log_param extra parameters passed to the git log command to define\n" .
|
|
|
|
" the area of interest , e.g. --after=\"2010-09-27\" or\n" .
|
2011-04-11 10:09:34 -05:00
|
|
|
" TAG..HEAD or origin/master ^origin/libreoffice-3-3";
|
2011-01-31 12:43:03 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#######################################################################
|
|
|
|
#######################################################################
|
|
|
|
# MAIN
|
|
|
|
#######################################################################
|
|
|
|
#######################################################################
|
|
|
|
|
|
|
|
|
|
|
|
my $piece;
|
|
|
|
my $top_dir;
|
2011-03-28 08:51:47 -05:00
|
|
|
my $log_prefix = "commit-log";
|
|
|
|
my $log_suffix;
|
|
|
|
my $log;
|
2011-01-31 12:43:03 -06:00
|
|
|
my @git_args;
|
|
|
|
my %data;
|
2011-02-14 08:26:38 -06:00
|
|
|
my %print_filters = ();
|
2011-01-31 12:43:03 -06:00
|
|
|
|
|
|
|
foreach my $arg (@ARGV) {
|
|
|
|
if ($arg eq '--help') {
|
|
|
|
usage();
|
|
|
|
exit;
|
|
|
|
} elsif ($arg eq '--no-pieces') {
|
|
|
|
$piece = "bootstrap";
|
|
|
|
} elsif ($arg =~ m/--piece=(.*)/) {
|
|
|
|
$piece = $1;
|
2011-03-28 08:51:47 -05:00
|
|
|
} elsif ($arg =~ m/--log-suffix=(.*)/) {
|
|
|
|
$log_suffix = "$1";
|
2011-02-14 08:26:38 -06:00
|
|
|
} elsif ($arg eq '--bugs') {
|
|
|
|
$print_filters{'bug'} = 1;
|
2011-03-28 08:51:47 -05:00
|
|
|
$log_prefix = "bugfixes"
|
2011-01-31 12:43:03 -06:00
|
|
|
} else {
|
|
|
|
if (! defined $top_dir) {
|
|
|
|
$top_dir=$arg;
|
|
|
|
} else {
|
|
|
|
push @git_args, $arg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
(defined $top_dir) || die "Error: top direcotry is not defined\n";
|
|
|
|
(-d "$top_dir") || die "Error: not a directory: $top_dir\n";
|
|
|
|
(-f "$top_dir/.git/config") || die "Error: can't find $top_dir/.git/config\n";
|
|
|
|
|
2011-03-28 08:51:47 -05:00
|
|
|
(defined $log_suffix) || die "Error: define log suffix using --log-suffix=<string>\n";
|
|
|
|
|
2011-01-31 12:43:03 -06:00
|
|
|
load_data(\%data, $top_dir,$piece, \@git_args);
|
2011-03-28 08:51:47 -05:00
|
|
|
|
|
|
|
$log = open_log_file($log_prefix, $log_suffix, $top_dir);
|
|
|
|
print_stat(\%data, \%print_filters, $log);
|
|
|
|
close $log;
|