diff options
author | Petr Mladek <pmladek@suse.cz> | 2011-01-31 19:43:03 +0100 |
---|---|---|
committer | Petr Mladek <pmladek@suse.cz> | 2011-01-31 19:43:03 +0100 |
commit | 3a0ec7548fddb0c474211c60d53259ce526b7c92 (patch) | |
tree | 1369ece0ee83b3e5d180196d7fe0f35af0cc775d /bin/lo-commit-stat | |
parent | ae6acbd210d30f0c94f3d930b83aa6a9e9e589b8 (diff) |
lo-commit-stat: new script for analyzing commit logs
this script can be used to generate the weekly news, release news;
it might be extended to do even more commit statistics in the future;
Diffstat (limited to 'bin/lo-commit-stat')
-rwxr-xr-x | bin/lo-commit-stat | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/bin/lo-commit-stat b/bin/lo-commit-stat new file mode 100755 index 000000000000..aae7ce5cfac2 --- /dev/null +++ b/bin/lo-commit-stat @@ -0,0 +1,269 @@ +#!/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", + "l10n", "postprocess", "sdk", "testing", "ure", "writer"); + +sub search_bugs($$$$) +{ + my ($pdata, $piece, $commit_id, $line) = @_; + + my $bug = ""; + my $bug_orig; + %{$pdata->{$piece}{$commit_id}{'bugs'}} = (); + while (defined $bug) { + # match fdo#123, rhz#123, i#123 + if ( $line =~ m/(\w*\#+\d+)/ ) { + $bug_orig = $1; + $bug = $1; + # match #i123# + } elsif ( $line =~ m/(\#i)(\d+)(\#)/ ) { + $bug_orig = $1 . $2 . $3; + $bug = $2; + } else { + $bug = undef; + next; + } + $bug_orig =~ s/\#/\\#/; + $line =~ s/[Rr]esolves:\s*$bug_orig\s*//; + $line =~ s/\s*-\s*$bug_orig\s*//; + $line =~ s/$bug_orig[:,]?\s*//; + %{$pdata->{$piece}{$commit_id}{'bugs'}} = () if (! defined %{$pdata->{$piece}{$commit_id}{'bugs'}}); + $pdata->{$piece}{$commit_id}{'bugs'}{$bug} = 1; + $bug = undef; + return $line; + } + + 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) = @_; + + my $cmd = "cd $repo_dir && git log " . join ' ', @{$pgit_args}; + my $commit_id; + my $summary; + + print "Analyzing log from the git repo: $piece...\n"; + + open (GIT, "$cmd 2>&1|") || die "Can't run $cmd: $!"; + %{$pdata->{$piece}} = (); + + while (my $line = <GIT>) { + chomp $line; + + if ( $line =~ m/commit ([0-9a-z]{20})/ ) { + $commit_id = "$1"; + $summary=undef; + %{$pdata->{$piece}{"$commit_id"}} = (); + next; + } + + if ( $line =~ /Author:\s*([^\<]*)\<([^\>]*)>/ ) { + # 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; + } + + if ( $line =~ /Date:\s+/ ) { + # ignore date line + next; + } + + 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); + } + } +} + +sub print_summary_in_stat($$$$) +{ + my ($summary, $pbugs, $pauthors, $prefix) = @_; + + return if ( $summary eq "" ); + + my $bugs = ""; + if ( %{$pbugs} ) { + $bugs = " (" . join (", ", keys %{$pbugs}) . ")"; + } + + my $authors = ""; + if ( %{$pauthors} ) { + $authors = " [" . join (", ", keys %{$pauthors}) . "]"; + } + +# print only entries with bug numbers +# print $prefix . $summary . $bugs . $authors . "\n" if ($bugs ne ""); + print $prefix . $summary . $bugs . $authors . "\n"; +} + +sub print_weekly_stat($) +{ + my $pdata = shift; + + foreach my $piece (keys %{$pdata}) { + # check if this peice has any entries at all + if ( %{$pdata->{$piece}} ) { + print "+ $piece\n"; + my $old_summary=""; + my %authors = (); + my %bugs = (); + 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) { + print_summary_in_stat($old_summary, \%bugs, \%authors, " + "); + $old_summary = $summary; + %authors = (); + %bugs = (); + } + if (defined $pdata->{$piece}{$id}{'bugs'}) { + foreach my $bug (keys %{$pdata->{$piece}{$id}{'bugs'}}) { + $bugs{$bug} = 1; + } + } + my $author = $pdata->{$piece}{$id}{'author'}{'name'}; + $authors{$author} = 1; + } + print_summary_in_stat($old_summary, \%bugs, \%authors, " + "); + } + } +} + +######################################################################## +# help + +sub usage() +{ + print "This script generates LO git commit summary\n\n" . + + "Usage: lo-commit-stat [--help] [--no-pieces] [--piece=<piece>] topdir [git_log_param...]\n\n" . + + "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" . + " 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" . + " TAG..HEAD"; +} + + +####################################################################### +####################################################################### +# MAIN +####################################################################### +####################################################################### + + +my $piece; +my $top_dir; +my @git_args; +my %data; + +foreach my $arg (@ARGV) { + if ($arg eq '--help') { + usage(); + exit; + } elsif ($arg eq '--no-pieces') { + $piece = "bootstrap"; + } elsif ($arg =~ m/--piece=(.*)/) { + $piece = $1; + } 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"; + +load_data(\%data, $top_dir,$piece, \@git_args); +print_weekly_stat(\%data); |