summaryrefslogtreecommitdiff
path: root/bin/lo-commit-stat
diff options
context:
space:
mode:
authorPetr Mladek <pmladek@suse.cz>2011-01-31 19:43:03 +0100
committerPetr Mladek <pmladek@suse.cz>2011-01-31 19:43:03 +0100
commit3a0ec7548fddb0c474211c60d53259ce526b7c92 (patch)
tree1369ece0ee83b3e5d180196d7fe0f35af0cc775d /bin/lo-commit-stat
parentae6acbd210d30f0c94f3d930b83aa6a9e9e589b8 (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-xbin/lo-commit-stat269
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);