From ae7d8379a560631f3bdd01624b027d6c65361a4c Mon Sep 17 00:00:00 2001 From: Martin Roth Date: Sat, 10 Dec 2022 22:00:40 -0700 Subject: [PATCH] util/release: Update gerrit_stats script to latest version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This updates a number of things: - Move the cache directory under the .local directory - Reformat & clean up with perltidy. Add the perltidy command line. - Add username and email aliases to clean up duplicates and unknown email addresses. - Use full length commit IDs - Collect patch commenters - Check variables before using them as key values - Ignore patch submit time, just collect the date - Get stats about large patches - Format the output better Example output: Statistics from commit c35f2819348501579a32e870e21f5f1b062d26e8 to commit f8fbf0917c722378454b07c2e8ec1a3f87b324ae Patch, Date, Owner, Author, Submitter, Inserted lines, Deleted lines, Subject, Reviewers, Commenters "f8fbf0917c722378454b07c2e8ec1a3f87b324ae", 2022/12/10, Frank Chu, Frank Chu, Martin Roth, 22, 1, "mb/google/brya/var/marasov: Change FSP board type to Type3", "Frank Chu, Eric Lai" , "-" "5778e06771627a5541ca2b137e783f47257f05ec", 2022/12/10, Dinesh Gehlot, Dinesh Gehlot, Subrata Banik, 30, 1, "soc/intel/meteorlake: Drop casts around `soc_read_pmc_base()`", "Kapil Porwal, Elyes Haouas" , "Subrata Banik" "ed8bdefcdf6c19258febb9931d1e8eb12b958bcc", 2022/12/10, Jamie Ryu, Jamie Ryu, Felix Held, 76, 3, "mb/intel/mtlrvp: Add MTL-P RVP board ids", "Usha P, Sridhar Siricilla, Eric Lai, Subrata Banik" , "Eric Lai, Subrata Banik, Harsha B R, Angel Pons" - Total Commits: 985 - Average Commits per day: 17.85 - Total lines added: 61475 - Average lines added per commit: 62.41 - Number of patches adding more than 100 lines: 49 - Average lines added per small commit: 37.82 - Total lines removed: 758022 - Average lines removed per commit: 769.57 - Total difference between added and removed: -696547 === Authors - Number of commits === Author ,Ptchs ,Revws , Cmnts , Sbmts , Email , Prcnt, Last commit , Earliest_commit Elyes Haouas , 126 , 90 , 28 , 0 , ehaouas@noos.fr ,12.79%, 2022/12/10 , 2022/10/17 Arthur Heymans , 107 , 99 , 28 , 40 , arthur@aheymans.xyz ,10.86%, 2022/12/10 , 2022/10/17 === Authors - Lines added === Martin Roth , 10103, 16.434% Kyösti Mälkki , 6044, 9.832% Arthur Heymans , 3314, 5.391% === Authors - Lines removed === Arthur Heymans , -741944, 97.879% Felix Held , -3031, 0.400% Kyösti Mälkki , -1680, 0.222% === Reviewers - Number of patches reviewed === Angel Pons , 272, 27.614% Eric Lai , 201, 20.406% Felix Held , 106, 10.761% === Submitters - Number of patches submitted === Name , #, total%, Own, own%, Other, other% Felix Held , 482, 48.934%, 56, 11.62%, 426, 88.38% Martin Roth , 179, 18.173%, 42, 23.46%, 137, 76.54% Subrata Banik , 54, 5.482%, 31, 57.41%, 23, 42.59% Signed-off-by: Martin Roth Change-Id: Ie1694116ab36ca4db25d13935adadca10e50068f Reviewed-on: https://review.coreboot.org/c/coreboot/+/70572 Tested-by: build bot (Jenkins) Reviewed-by: Angel Pons Reviewed-by: Felix Singer --- util/release/gerrit_stats.pl | 521 ++++++++++++++++++++++++++--------- 1 file changed, 390 insertions(+), 131 deletions(-) diff --git a/util/release/gerrit_stats.pl b/util/release/gerrit_stats.pl index 94681579e1..789cb051d7 100755 --- a/util/release/gerrit_stats.pl +++ b/util/release/gerrit_stats.pl @@ -8,6 +8,8 @@ package gerrit_stats; # sudo cpan App::cpanminus # sudo /usr/local/bin/cpanm JSON::Util Net::OpenSSH DateTime Devel::Size +# perltidy -l=200 -bt=2 -ce + use strict; use warnings; use English qw( -no_match_vars ); @@ -23,7 +25,7 @@ use Devel::Size qw(size total_size); my $old_version; my $new_version; -my $infodir="$ENV{'HOME'}/.commit_info/" . `git config -l | grep remote.origin.url | sed 's|.*@||' | sed 's|:.*||'`; +my $infodir = "$ENV{'HOME'}/.local/commit_info/" . `git config -l | grep remote.origin.url | sed 's|.*@||' | sed 's|:.*||'`; chomp($infodir); my $URL_WITH_USER; my $SKIP_GERRIT_CHECK; @@ -41,106 +43,275 @@ Main(); sub Main { check_arguments(); - my %submitters = (); - my %authors = (); - my %owners = (); - my %reviewers = (); - my %author_added = (); - my %author_removed = (); - my $total_added = 0; - my $total_removed = 0; - my $number_of_commits = 0; - my $number_of_submitters = 0; - my $submit_epoch = ""; - my $first_submit_epoch = ""; - if (!$URL_WITH_USER) { - get_user() + my %submitters = (); + my %authors = (); + my %owners = (); + my %reviewers = (); + my %commenters = (); + my %author_added = (); + my %author_removed = (); + my $total_added = 0; + my $total_removed = 0; + my $number_of_commits = 0; + my $number_of_submitters = 0; + my $submit_epoch = ""; + my $first_submit_epoch = ""; + my $patches_over_100_lines = 0; + my $total_lines_large_patches = 0; + my %email_addresses = ( + 'Kacper Stojek' => 'kacper.stojek@3mdeb.com', + 'Damien Zammit' => 'damien@zamaudio.com', + 'Pavel Sayekat' => 'pavelsayekat@gmail.com', + 'Lance Zhao' => 'lance.zhao@gmail.com', + ); + + my %aliases = ( + ' Felix Singer' => 'Felix Singer', + 'Abhay kumar' => 'Abhay Kumar', + 'AlexandruX Gagniuc' => 'Alexandru Gagniuc', + 'Anish K. Patel' => 'Anish K Patel', + 'Bao Zheng' => 'Zheng Bao', + 'Bernhard M. Wiedemann' => 'Bernhard M. Wiedermann', + 'Björn Busse' => 'Bjarn Busse', + 'BryantOu' => 'Bryant Ou', + 'Chen Wisley' => 'Wisley Chen', + 'Cheng-Yi Chiang' => 'Jimmy Cheng-Yi Chiang', + 'Chris Ching (using chromium account)' => 'Chris Ching,', + 'ChromeOS Developer' => 'Dave Parker', + 'Cristi M' => 'Cristian Magherusan-Stanciu', + 'Cristian M?gheru?an-Stanciu' => 'Cristian Magherusan-Stanciu', + 'Cristian MÄgheruÈan-Stanciu' => 'Cristian Magherusan-Stanciu', + 'Cristian MÄgheruÈan-Stanciu' => 'Cristian Magherusan-Stanciu', + 'DAWEI CHIEN' => 'Dawei Chien', + 'efdesign98' => 'Frank Vibrans', + 'Eugene D. Myers' => 'Eugene Myers', + 'Frank Vibrans III' => 'Frank Vibrans', + 'frank vibrans' => 'Frank Vibrans', + 'Frank.Vibrans' => 'Frank Vibrans', + 'FrankChu' => 'Frank Chu', + 'garmin chang' => 'Garmin Chang', + 'Garmin.Chang' => 'Garmin Chang', + 'hannahwilliams2' => 'Hannah Williams', + 'HAOUAS Elyes' => 'Elyes Haouas', + 'Harshapriya N' => 'Harsha Priya', + 'Hsuan-ting Chen' => 'Hsuan Ting Chen', + 'Iru Cai (vimacs)' => 'Iru Cai', + 'Jérémy Compostella' => 'Jeremy Compostella', + 'Jérémy Compostella' => 'Jeremy Compostella', + 'JG Poxu' => 'Po Xu', + 'JonathonHall-Purism' => 'Jonathon Hall', + 'Karthikeyan Ramasubramanian' => 'Karthik Ramasubramanian', + 'Kerry She' => 'Kerry Sheh', + 'kewei.xu' => 'kewei xu', + 'Kumar, Gomathi' => 'Gomathi Kumar', + 'Kyösti Mälkki' => 'Kyösti Mälkki', + 'Kyösti Mälkki' => 'Kyösti Mälkki', + 'Marcello Sylvester Bauer' => 'Marcello Sylvester Bauer', + 'Martin L Roth' => 'Martin Roth', + 'Martin Roth - Personal' => 'Martin Roth', + 'Matt Ziegelbaum' => 'Matthew Ziegelbaum', + 'mengqi.zhang' => 'Mengqi Zhang', + 'mrnuke' => 'Alexandru Gagniuc', + 'Nina-CM Wu' => 'Nina Wu', + 'ot_zhenguo.li' => 'Zhenguo Li', + 'Patrick Georgi patrick.georgi' => 'Patrick Georgi', + 'Patrick Georgi patrick' => 'Patrick Georgi', + 'Pavlushka' => 'Pavel Sayekat', + 'Ravi Kumar Bokka' => 'Ravi Kumar', + 'Ravi kumar' => 'Ravi Kumar', + 'Ravi Sarawadi' => 'Ravishankar Sarawadi', + 'ravindr1' => 'Ravindra', + 'Ravindra N' => 'Ravindra', + 'Ricardo Ribalda Delgado' => 'Ricardo Ribalda', + 'ron minnich' => 'Ron Minnich', + 'Ronald G. Minnich' => 'Ron Minnich', + 'samrab' => 'Sudheer Amrabadi', + 'SANTHOSH JANARDHANA HASSAN' => 'Santhosh Janardhana Hassan', + 'semihalf-czapiga-jakub' => 'Jakub Czapiga', + 'Seunghwan Kim' => 'SH Kim', + 'Sooi, Li Cheng' => 'Li Cheng Sooi', + 'Stefan Reinauerstepan' => 'Stefan Reinauer', + 'stepan' => 'Stefan Reinauer', + 'Swift Geek (Sebastian Grzywna)' => 'Sebastian "Swift Geek" Grzywna', + 'Sylvain "ythier" Hitier' => 'Sylvain Hitier', + 'Thomas Gstädtner' => 'Thomas Gstaedtner', + 'UwePoeche' => 'Uwe Poeche', + 'UwePoeche' => 'Uwe Poeche', + 'Varshit Pandya' => 'Varshit B Pandya', + 'Wayne3 Wang' => 'Wayne Wang', + 'Wayne3_Wang' => 'Wayne Wang', + 'Xi Chen' => 'Xixi Chen', + 'Yu-Hsuan Hsu' => 'Yu-hsuan Hsu', + 'zbao' => 'Zheng Bao', + 'Zheng Bao zheng.bao' => 'Zheng Bao', + 'zhiyong tao' => 'Zhiyong Tao', + 'Дмитрий Понаморев' => 'Dmitry Ponamorev', + ); + + if ( !$URL_WITH_USER ) { + get_user(); } - # make sure the versions exist + print "Saving data to $infodir\n"; + + # Make sure the versions exist check_versions(); - #fetch patches if needed. Get ids of first and last commits - my @commits = `git log --pretty=%h "$old_version..$new_version" 2>/dev/null`; + # Fetch patches if needed. Get ids of first and last commits + my @commits = `git log --pretty=%H "$old_version..$new_version" 2>/dev/null`; get_commits(@commits); - my $last_commit_id = $commits[0]; - my $first_commit_id = $commits[@commits - 1]; + my $last_commit_id = $commits[0]; + my $first_commit_id = $commits[ @commits - 1 ]; chomp $last_commit_id; chomp $first_commit_id; print "Statistics from commit $first_commit_id to commit $last_commit_id\n"; - print "Patch, Date, Owner, Author, Submitter, Inserted lines, Deleted lines, Subject, Reviewers\n"; + print "Patch, Date, Owner, Author, Submitter, Inserted lines, Deleted lines, Subject, Reviewers, Commenters\n"; - #loop through all commits + # Loop through all commits for my $commit_id (@commits) { $commit_id =~ s/^\s+|\s+$//g; - my $submitter = ""; - my %patch_reviewers = (); + my $submitter = ""; + my %patch_reviewers = (); + my %patch_commenters = (); my $info; my $owner; my $author; my $author_email; my $inserted_lines = 0; - my $deleted_lines = 0; + my $deleted_lines = 0; my $subject; $number_of_commits++; print "\"$commit_id\", "; - #read the data file for the current commit - if (-f "$infodir/$commit_id" && -s "$infodir/$commit_id" > 20) { + # Read the data file for the current commit + if ( -f "$infodir/$commit_id" && -s "$infodir/$commit_id" > 20 ) { open( my $HANDLE, "<", "$infodir/$commit_id" ) or die "Error: could not open file '$infodir/$commit_id'\n"; $info = <$HANDLE>; close $HANDLE; my $commit_info = JSON::Util->decode($info); - #get the easy data + # Get the easy data $owner = $commit_info->{'owner'}{'name'}; - if (! $owner) { + if ( !$owner ) { $owner = $commit_info->{'owner'}{'username'}; } - if (! $owner) { - $owner = ""; + if ( !$owner ) { + $owner = "-"; } - $author = $commit_info->{'currentPatchSet'}{'author'}{'name'}; - $author_email = $commit_info->{'currentPatchSet'}{'author'}{'email'}; - if (! $author) { - $author = $commit_info->{'currentPatchSet'}{'author'}{'username'}; + if ( $owner && exists( $aliases{$owner} ) ) { + $owner = $aliases{$owner}; } + $author = $commit_info->{'currentPatchSet'}{'author'}{'name'}; + if ( $author && exists( $aliases{$author} ) ) { + $author = $aliases{$author}; + } + if ( !$author ) { + $author = $commit_info->{'currentPatchSet'}{'author'}{'username'}; + } + $author_email = $commit_info->{'currentPatchSet'}{'author'}{'email'}; $inserted_lines = $commit_info->{'currentPatchSet'}{'sizeInsertions'}; - $deleted_lines = $commit_info->{'currentPatchSet'}{'sizeDeletions'}; - $subject = $commit_info->{'subject'}; + $deleted_lines = $commit_info->{'currentPatchSet'}{'sizeDeletions'}; + $subject = $commit_info->{'subject'}; #get the patch's submitter my $approvals = $commit_info->{'currentPatchSet'}{'approvals'}; for my $approval (@$approvals) { - if ($approval->{'type'} eq "SUBM") { + if ( $approval->{'type'} eq "SUBM" ) { $submit_epoch = $approval->{'grantedOn'}; - $submitter = $approval->{'by'}{'name'}; + $submitter = $approval->{'by'}{'name'}; + if ( exists( $aliases{$submitter} ) ) { + $submitter = $aliases{$submitter}; + } } } - #get all the reviewers for all patch revisions + # Get all the commenters for all patch revisions + my $comments = $commit_info->{'comments'}; + for my $comment (@$comments) { + my $commenter; + if ( $comment->{'reviewer'}{'username'} ) { + if ( $comment->{'reviewer'}{'username'} eq "jenkins" ) { + next; + } + if ( $comment->{'reviewer'}{'username'} eq "hardwaretestrobot" ) { + next; + } + if ( $comment->{'reviewer'}{'username'} eq "raptor-automated-test" ) { + next; + } + } + + if ( $comment->{'reviewer'}{'name'} ) { + if ( $comment->{'reviewer'}{'name'} eq "Gerrit Code Review" ) { + next; + } + } + if ( $comment->{'message'} ) { + if ( $comment->{'message'} =~ "successfully cherry-picked" ) { + next; + } + if ( $comment->{'message'} =~ ": Code-Review" ) { + next; + } + if ( $comment->{'message'} =~ "Uploaded patch set" ) { + next; + } + } + + if ( !$commenter ) { + $commenter = $comment->{'reviewer'}{'name'}; + if ( $commenter && exists( $aliases{$commenter} ) ) { + $commenter = $aliases{$commenter}; + } + } + if ( !$commenter ) { + $commenter = $comment->{'reviewer'}{'username'}; + if ( $commenter && exists( $aliases{$commenter} ) ) { + $commenter = $aliases{$commenter}; + } + } + if ( $commenter && $author && $commenter eq $author ) { + next; + } + if ($commenter) { + if ( $commenter && exists $patch_commenters{$commenter} ) { + $patch_commenters{$commenter}++; + } else { + $patch_commenters{$commenter} = 1; + } + } + } + + # Get all the reviewers for all patch revisions my $patchsets = $commit_info->{'patchSets'}; for my $patch (@$patchsets) { - if (! $author) { + if ( !$author ) { $author = $patch->{'author'}{'name'}; + if ( $author && exists( $aliases{$author} ) ) { + $author = $aliases{$author}; + } } + my $approvals = $patch->{'approvals'}; for my $approval (@$approvals) { - if ( (! $submitter) && ($approval->{'type'} eq "SUBM")) { + if ( ( !$submitter ) && ( $approval->{'type'} eq "SUBM" ) ) { $submit_epoch = $approval->{'grantedOn'}; - $submitter = $approval->{'by'}{'name'}; + $submitter = $approval->{'by'}{'name'}; + if ( $submitter && exists( $aliases{$submitter} ) ) { + $submitter = $aliases{$submitter}; + } } - if ($approval->{'type'} eq "Code-Review") { + if ( $approval->{'type'} eq "Code-Review" ) { my $patch_reviewer = $approval->{'by'}{'name'}; if ($patch_reviewer) { - if (exists $patch_reviewers{$patch_reviewer}) { + if ( exists $patch_reviewers{$patch_reviewer} ) { $patch_reviewers{$patch_reviewer}++; } else { $patch_reviewers{$patch_reviewer} = 1; @@ -151,24 +322,36 @@ sub Main { } } else { - # get the info from git + + # Get the info from git my $logline = `git log --pretty="%ct@@@%s@@@%an@@@%aE@@@%cn" $commit_id^..$commit_id --`; $logline =~ m/^(.*)@@@(.*)@@@(.*)@@@(.*)@@@(.*)\n/; - ($submit_epoch, $subject, $author, $author_email, $submitter) = ($1, $2, $3, $4, $5); + ( $submit_epoch, $subject, $author, $author_email, $submitter ) = ( $1, $2, $3, $4, $5 ); + if ( exists( $aliases{$author} ) ) { + $author = $aliases{$author}; + } $owner = $author; + + if ( $submitter && exists( $aliases{$submitter} ) ) { + $submitter = $aliases{$submitter}; + } + $logline = `git log --pretty= --shortstat $commit_id^..$commit_id --`; - if ($logline =~ m/\s+(\d+)\s+insertion/) { + if ( $logline =~ m/\s+(\d+)\s+insertion/ ) { $inserted_lines = $1; } - if ($logline =~ m/\s+(\d+)\s+deletion/) { + if ( $logline =~ m/\s+(\d+)\s+deletion/ ) { $deleted_lines = $1 * -1; } my @loglines = `git log $commit_id^..$commit_id -- | grep '\\sReviewed-by:'`; - for my $line (@loglines){ - if ($line =~ m/.*:\s+(.*)\sfrom_epoch(epoch => $submit_epoch); - $dt->set_time_zone( 'Europe/Paris' ); - my $submit_time = $dt->strftime('%Y/%m/%d %H:%M:%S'); - if (!$first_submit_epoch) { + if ( $submitter eq $author ) { + $submitters{$submitter}{"self"}++; + } else { + $submitters{$submitter}{others}++; + } + + # Create a readable date + my $dt = DateTime->from_epoch( epoch => $submit_epoch ); + $dt->set_time_zone('Europe/Paris'); + my $submit_time = $dt->strftime('%Y/%m/%d'); + if ( !$first_submit_epoch ) { $first_submit_epoch = $submit_epoch; } - #create the list of reviewers to print + # Create the list of commenters to print + my $commenterlist = ""; + foreach my $commenter ( keys %patch_commenters ) { + if ( $commenter && exists( $aliases{$commenter} ) ) { + $commenter = $aliases{$commenter}; + } + + if ( $commenterlist eq "" ) { + $commenterlist = $commenter; + } else { + $commenterlist .= ", $commenter"; + } + + if ( $commenter && exists $commenters{$commenter} ) { + $commenters{$commenter}++; + } else { + $commenters{$commenter} = 1; + } + } + if ( !$commenterlist ) { + $commenterlist = "-"; + } + + # Create the list of reviewers to print my $reviewerlist = ""; - foreach my $reviewer (keys %patch_reviewers) { - if ($reviewerlist eq "") { + foreach my $reviewer ( keys %patch_reviewers ) { + if ( exists( $aliases{$reviewer} ) ) { + $reviewer = $aliases{$reviewer}; + } + + if ( $reviewerlist eq "" ) { $reviewerlist = $reviewer; } else { $reviewerlist .= ", $reviewer"; } - if (exists $reviewers{$reviewer}) { + if ( $reviewer && exists $reviewers{$reviewer} ) { $reviewers{$reviewer}++; } else { $reviewers{$reviewer} = 1; } } - if (! $reviewerlist) { - $reviewerlist = "-" + if ( !$reviewerlist ) { + $reviewerlist = "-"; } if ($print_commit_list) { - print "$submit_time, $owner, $author, $submitter, $inserted_lines, $deleted_lines, \"$subject\", \"$reviewerlist\"\n"; + print "$submit_time, $owner, $author, $submitter, $inserted_lines, $deleted_lines, \"$subject\", \"$reviewerlist\" , \"$commenterlist\"\n"; } else { print "$number_of_commits\n"; } $total_added += $inserted_lines; + if ( $inserted_lines - $deleted_lines > 100 ) { + $patches_over_100_lines++; + $total_lines_large_patches += $inserted_lines; + } $total_removed += $deleted_lines; - if (exists $owners{$owner}) { + if ( exists $owners{$owner} ) { $owners{$owner}++; } else { $owners{$owner} = 1; } - if (exists $authors{$author}{"num"}) { + if ( $author && exists $authors{$author}{"num"} ) { $authors{$author}{"num"}++; - $author_added{$author} += $inserted_lines; + $author_added{$author} += $inserted_lines; $author_removed{$author} += $deleted_lines; - $authors{$author}{"earliest_commit"}=$submit_time; + $authors{$author}{"earliest_commit"} = $submit_time; } else { - $authors{$author}{"num"} = 1; - $authors{$author}{"latest_commit"}=$submit_time; - $authors{$author}{"earliest_commit"}=$submit_time; - $author_added{$author} = $inserted_lines; - $author_removed{$author} = $deleted_lines; + $authors{$author}{"num"} = 1; + $authors{$author}{"latest_commit"} = $submit_time; + $authors{$author}{"earliest_commit"} = $submit_time; + $author_added{$author} = $inserted_lines; + $author_removed{$author} = $deleted_lines; } - if (! exists $authors{$author}{email} && $author_email) { - $authors{$author}{email} = "$author_email"; + if ( $author && ( !exists $authors{$author}{email} || $authors{$author}{email} eq "-" ) ) { + if ($author_email) { + $authors{$author}{email} = "$author_email"; + } elsif ( exists $email_addresses{$author} ) { + $authors{$author}{email} = $email_addresses{$author}; + } } } - my $Days = ($first_submit_epoch - $submit_epoch) / 86400; - if (($first_submit_epoch - $submit_epoch) % 86400) { + my $Days = ( $first_submit_epoch - $submit_epoch ) / 86400; + if ( ( $first_submit_epoch - $submit_epoch ) % 86400 ) { $Days += 1; } - print "- Total Commits: $number_of_commits\n"; + print "\n- Total Commits: $number_of_commits\n"; printf "- Average Commits per day: %.2f\n", $number_of_commits / $Days; print "- Total lines added: $total_added\n"; + printf "- Average lines added per commit: %.2f\n", $total_added / $number_of_commits; + print "- Number of patches adding more than 100 lines: $patches_over_100_lines\n"; + printf "- Average lines added per small commit: %.2f\n", ( $total_added - $total_lines_large_patches ) / ( $number_of_commits - $patches_over_100_lines ); + print "- Total lines removed: $total_removed\n"; - print "- Total difference: " . ($total_added + $total_removed) . "\n\n"; + printf "- Average lines removed per commit: %.2f\n", $total_removed / $number_of_commits; + print "- Total difference between added and removed: " . ( $total_added - $total_removed ) . "\n\n"; print "=== Authors - Number of commits ===\n"; + printf "%-30s ,%5s ,%5s ,%6s ,%6s , %-52s ,%6s, %-19s , %s\n", "Author", "Ptchs", "Revws", "Cmnts", "Sbmts", "Email", "Prcnt", "Last commit", "Earliest_commit"; + my $number_of_authors = 0; - foreach my $author (sort { $authors{$b}{num} <=> $authors{$a}{num} } (keys %authors) ) { - if (! exists $authors{$author}{"email"}) { - $authors{$author}{"email"} = "-"; - } - printf "%-25s %5d %-40s (%2.2f%%) {%s / %s}\n",$author, $authors{$author}{"num"}, $authors{$author}{"email"}, $authors{$author}{"num"} / $number_of_commits * 100, $authors{$author}{"latest_commit"}, $authors{$author}{"earliest_commit"}; + foreach my $author ( sort { $authors{$b}{num} <=> $authors{$a}{num} } ( keys %authors ) ) { + my $submissions = 0; + if ( $author && exists $submitters{$author} ) { + $submissions = $submitters{$author}{count}; + } + my $review_count = 0; + if ( $author && exists $reviewers{$author} ) { + $review_count = $reviewers{$author}; + } + + my $comment_count = 0; + if ( $author && exists $commenters{$author} ) { + $comment_count = $commenters{$author}; + } + + if ( $author && !exists $authors{$author}{"email"} ) { + $authors{$author}{"email"} = "-"; + } + + printf "%-30s ,%5d ,%5d ,%6d ,%6d , %-52s ,%5.2f%%, %s , %s\n", $author, $authors{$author}{"num"}, $review_count, + $comment_count, $submissions, $authors{$author}{"email"}, $authors{$author}{"num"} / $number_of_commits * 100, + $authors{$author}{"latest_commit"}, $authors{$author}{"earliest_commit"}; $number_of_authors++; } print "Total Authors: $number_of_authors\n\n"; print "=== Authors - Lines added ===\n"; - foreach my $author (sort { $author_added{$b} <=> $author_added{$a} } (keys %author_added) ) { - if ($author_added{$author}) { - printf "%-25s %5d (%2.3f%%)\n",$author, $author_added{$author}, $author_added{$author} / $total_added * 100; + foreach my $author ( sort { $author_added{$b} <=> $author_added{$a} } ( keys %author_added ) ) { + if ( $author_added{$author} ) { + printf "%-30s, %10d, %2.3f%%\n", $author, $author_added{$author}, $author_added{$author} / $total_added * 100; } } print "\n"; print "=== Authors - Lines removed ===\n"; - foreach my $author (sort { $author_removed{$a} <=> $author_removed{$b} } (keys %author_removed) ) { - if ($author_removed{$author}) { - printf "%-25s %5d (%2.3f%%)\n",$author,$author_removed{$author} * -1, $author_removed{$author} / $total_removed * 100; + foreach my $author ( sort { $author_removed{$b} <=> $author_removed{$a} } ( keys %author_removed ) ) { + if ( $author_removed{$author} ) { + printf "%-30s, %10d, %6.3f%%\n", $author, $author_removed{$author} * -1, $author_removed{$author} / $total_removed * 100; } } print "\n"; print "=== Reviewers - Number of patches reviewed ===\n"; my $number_of_reviewers = 0; - foreach my $reviewer (sort { $reviewers{$b} <=> $reviewers{$a} } (keys %reviewers) ) { - printf "%-25s %5d (%2.3f%%)\n",$reviewer, $reviewers{$reviewer}, $reviewers{$reviewer} / $number_of_commits * 100; + foreach my $reviewer ( sort { $reviewers{$b} <=> $reviewers{$a} } ( keys %reviewers ) ) { + printf "%-30s, %6d, %6.3f%%\n", $reviewer, $reviewers{$reviewer}, $reviewers{$reviewer} / $number_of_commits * 100; $number_of_reviewers++; } print "Total Reviewers: $number_of_reviewers\n\n"; print "=== Submitters - Number of patches submitted ===\n"; - foreach my $submitter (sort { $submitters{$b} <=> $submitters{$a} } (keys %submitters) ) { - printf "%-25s %5d (%2.3f%%)\n",$submitter, $submitters{$submitter}, $submitters{$submitter} / $number_of_commits * 100; + printf "%-30s, %6s, %7s, %6s, %7s, %6s, %7s\n", "Name", "#", "total%", "Own", "own%", "Other", "other%"; + foreach my $submitter ( sort { $submitters{$b}{count} <=> $submitters{$a}{count} } ( keys %submitters ) ) { + printf "%-30s, % 6d, %6.3f%%, %6d, %6.2f%%, %6d, %6.2f%%\n", + $submitter, + $submitters{$submitter}{count}, + $submitters{$submitter}{count} / $number_of_commits * 100, + $submitters{$submitter}{"self"}, + $submitters{$submitter}{"self"} / $submitters{$submitter}{count} * 100, + $submitters{$submitter}{others}, $submitters{$submitter}{others} / $submitters{$submitter}{count} * 100; } print "Total Submitters: $number_of_submitters\n\n"; print "Commits, Ave, Added, Removed, Diff, Authors, Reviewers, Submitters\n"; - printf "$number_of_commits, %.2f, $total_added, $total_removed, " . ($total_added + $total_removed) . ", $number_of_authors, $number_of_reviewers, $number_of_submitters\n", $number_of_commits / $Days; + printf "$number_of_commits, %.2f, $total_added, $total_removed, " . ( $total_added + $total_removed ) . ", $number_of_authors, $number_of_reviewers, $number_of_submitters\n", + $number_of_commits / $Days; } #------------------------------------------------------------------------------- #------------------------------------------------------------------------------- sub check_versions { `git cat-file -e $old_version^{commit} 2>/dev/null`; - if (${^CHILD_ERROR_NATIVE}){ + if ( ${^CHILD_ERROR_NATIVE} ) { print "Error: Old version ($old_version) does not exist.\n"; exit 1; } `git cat-file -e $new_version^{commit} 2>/dev/null`; - if (${^CHILD_ERROR_NATIVE}){ + if ( ${^CHILD_ERROR_NATIVE} ) { print "Error: New version ($new_version) does not exist.\n"; exit 1; } } -#------------------------------------------------------------------------------- #------------------------------------------------------------------------------- sub get_user { - my $url=`git config -l | grep remote.origin.url`; - if ($url =~ /.*url=ssh:\/\/(\w+@[a-zA-Z][a-zA-Z0-9\.]+:\d+)/) - { + my $url = `git config -l | grep remote.origin.url`; + + if ( $url =~ /.*url=ssh:\/\/(\w+@[a-zA-Z][a-zA-Z0-9\.]+:\d+)(\/\w+)*/ ) { $URL_WITH_USER = $1; } else { print "Error: Could not get a ssh url with a username from gitconfig.\n"; @@ -341,44 +599,44 @@ sub get_user { #------------------------------------------------------------------------------- #------------------------------------------------------------------------------- sub get_commits { - my @commits = @_; + my @commits = @_; my $submit_time = ""; - if (defined $SKIP_GERRIT_CHECK) { + if ( defined $SKIP_GERRIT_CHECK && $SKIP_GERRIT_CHECK ) { return; } - my $ssh = Net::OpenSSH->new("$URL_WITH_USER", ); - $ssh->error and die "Couldn't establish SSH connection to $URL_WITH_USER:". $ssh->error; + my $ssh = Net::OpenSSH->new( "$URL_WITH_USER", ); + $ssh->error and die "Couldn't establish SSH connection to $URL_WITH_USER:" . $ssh->error; print "Using URL: ssh://$URL_WITH_USER\n"; - if (! -d $infodir) { - mkpath($infodir) + if ( !-d $infodir ) { + mkpath($infodir); } for my $commit_id (@commits) { $commit_id =~ s/^\s+|\s+$//g; $submit_time = ""; - my $gerrit_review; + my $gerrit_review; - # Quit if we've reeached the last coreboot commit supporting these queries - if ($commit_id =~ /^7309709/) { + # Look for last coreboot commit + if ( $commit_id eq "7309709742" ) { last; } - if (-f "$infodir/$commit_id") { + if ( -f "$infodir/$commit_id" ) { $gerrit_review = 1; } else { $gerrit_review = `git log $commit_id^..$commit_id | grep '\\sReviewed-on:\\s'`; } - if ($gerrit_review && $commit_id && (! -f "$infodir/$commit_id") ) { + if ( $gerrit_review && $commit_id && ( !-f "$infodir/$commit_id" ) ) { print "Downloading $commit_id"; my @info = $ssh->capture("gerrit query --format=JSON --comments --files --current-patch-set --all-approvals --submit-records --dependencies commit:$commit_id"); $ssh->error and die "remote ls command failed: " . $ssh->error; - my $commit_info = JSON::Util->decode($info[0]); - my $rowcount = $commit_info->{'rowCount'}; - if (defined $rowcount && ($rowcount eq "0")) { + my $commit_info = JSON::Util->decode( $info[0] ); + my $rowcount = $commit_info->{'rowCount'}; + if ( defined $rowcount && ( $rowcount eq "0" ) ) { print " - no gerrit commit for that id.\n"; open( my $HANDLE, ">", "$infodir/$commit_id" ) or die "Error: could not open file '$infodir/$commit_id'\n"; print $HANDLE "No gerrit commit"; @@ -388,13 +646,13 @@ sub get_commits { my $approvals = $commit_info->{'currentPatchSet'}{'approvals'}; for my $approval (@$approvals) { - if ($approval->{'type'} eq "SUBM") { - $submit_time = $approval->{'grantedOn'} + if ( $approval->{'type'} eq "SUBM" ) { + $submit_time = $approval->{'grantedOn'}; } } - my $dt=""; + my $dt = ""; if ($submit_time) { - $dt = DateTime->from_epoch(epoch => $submit_time); + $dt = DateTime->from_epoch( epoch => $submit_time ); } else { print " - no submit time for that id.\n"; open( my $HANDLE, ">", "$infodir/$commit_id" ) or die "Error: could not open file '$infodir/$commit_id'\n"; @@ -408,9 +666,9 @@ sub get_commits { print $HANDLE $info[0]; close $HANDLE; - $dt->set_time_zone( 'Europe/Paris' ); + $dt->set_time_zone('Europe/Paris'); print " - submit time: " . $dt->strftime('%Y/%m/%d %H:%M:%S') . "\n"; - } elsif ($commit_id && (! -f "$infodir/$commit_id")) { + } elsif ( $commit_id && ( !-f "$infodir/$commit_id" ) ) { print "No gerrit commit for $commit_id\n"; open( my $HANDLE, ">", "$infodir/$commit_id" ) or die "Error: could not open file '$infodir/$commit_id'\n"; print $HANDLE "No gerrit commit"; @@ -426,16 +684,17 @@ sub get_commits { sub check_arguments { my $show_usage = 0; GetOptions( - 'help|?' => sub { usage() }, - 'url|u=s' => \$URL_WITH_USER, - 'skip|s' => \$SKIP_GERRIT_CHECK, + 'help|?' => sub { usage() }, + 'url|u=s' => \$URL_WITH_USER, + 'skip|s' => \$SKIP_GERRIT_CHECK, ); + # strip ssh:// from url if passed in. - if (defined $URL_WITH_USER) { + if ( defined $URL_WITH_USER ) { $URL_WITH_USER =~ s|ssh://||; } if (@ARGV) { - ($old_version, $new_version) = @ARGV; + ( $old_version, $new_version ) = @ARGV; } else { usage(); }