blob: 8e22a53d9f1c7beab4b7460023c3d0789aa6d290 [file] [log] [blame]
Martin Roth0ad5fbd2020-12-24 12:06:38 -07001#!/usr/bin/env perl
Martin Rothce005da2016-04-01 14:30:33 -06002#
Patrick Georgi7333a112020-05-08 20:48:04 +02003# SPDX-License-Identifier: GPL-2.0-only
Martin Rothce005da2016-04-01 14:30:33 -06004
5package gerrit_stats;
6
7# To install any needed modules install the cpanm app, and use it to install the required modules:
8# sudo cpan App::cpanminus
9# sudo /usr/local/bin/cpanm JSON::Util Net::OpenSSH DateTime Devel::Size
10
Martin Rothae7d8372022-12-10 22:00:40 -070011# perltidy -l=200 -bt=2 -ce
12
Martin Rothce005da2016-04-01 14:30:33 -060013use strict;
14use warnings;
15use English qw( -no_match_vars );
16use File::Find;
17use File::Path;
18use Getopt::Long;
19use Getopt::Std;
20use JSON::Util;
21use Net::OpenSSH;
22use Data::Dumper qw(Dumper);
23use DateTime;
24use Devel::Size qw(size total_size);
25
26my $old_version;
27my $new_version;
Martin Rothae7d8372022-12-10 22:00:40 -070028my $infodir = "$ENV{'HOME'}/.local/commit_info/" . `git config -l | grep remote.origin.url | sed 's|.*@||' | sed 's|:.*||'`;
Martin Rothce005da2016-04-01 14:30:33 -060029chomp($infodir);
30my $URL_WITH_USER;
31my $SKIP_GERRIT_CHECK;
32my $print_commit_list = 1;
33
34#disable print buffering
35$OUTPUT_AUTOFLUSH = 1;
36binmode STDOUT, ":utf8";
37
38Main();
39
40#-------------------------------------------------------------------------------
41# Main
42#-------------------------------------------------------------------------------
43sub Main {
44 check_arguments();
45
Martin Rothae7d8372022-12-10 22:00:40 -070046 my %submitters = ();
47 my %authors = ();
48 my %owners = ();
49 my %reviewers = ();
50 my %commenters = ();
51 my %author_added = ();
52 my %author_removed = ();
53 my $total_added = 0;
54 my $total_removed = 0;
55 my $number_of_commits = 0;
56 my $number_of_submitters = 0;
57 my $submit_epoch = "";
58 my $first_submit_epoch = "";
59 my $patches_over_100_lines = 0;
60 my $total_lines_large_patches = 0;
61 my %email_addresses = (
62 'Kacper Stojek' => 'kacper.stojek@3mdeb.com',
63 'Damien Zammit' => 'damien@zamaudio.com',
64 'Pavel Sayekat' => 'pavelsayekat@gmail.com',
65 'Lance Zhao' => 'lance.zhao@gmail.com',
66 );
67
68 my %aliases = (
69 ' Felix Singer' => 'Felix Singer',
70 'Abhay kumar' => 'Abhay Kumar',
71 'AlexandruX Gagniuc' => 'Alexandru Gagniuc',
72 'Anish K. Patel' => 'Anish K Patel',
73 'Bao Zheng' => 'Zheng Bao',
74 'Bernhard M. Wiedemann' => 'Bernhard M. Wiedermann',
75 'Björn Busse' => 'Bjarn Busse',
76 'BryantOu' => 'Bryant Ou',
77 'Chen Wisley' => 'Wisley Chen',
78 'Cheng-Yi Chiang' => 'Jimmy Cheng-Yi Chiang',
79 'Chris Ching (using chromium account)' => 'Chris Ching,',
80 'ChromeOS Developer' => 'Dave Parker',
81 'Cristi M' => 'Cristian Magherusan-Stanciu',
82 'Cristian M?gheru?an-Stanciu' => 'Cristian Magherusan-Stanciu',
83 'Cristian MÄgheruÈan-Stanciu' => 'Cristian Magherusan-Stanciu',
84 'Cristian MÄgheruÈan-Stanciu' => 'Cristian Magherusan-Stanciu',
85 'DAWEI CHIEN' => 'Dawei Chien',
86 'efdesign98' => 'Frank Vibrans',
87 'Eugene D. Myers' => 'Eugene Myers',
88 'Frank Vibrans III' => 'Frank Vibrans',
89 'frank vibrans' => 'Frank Vibrans',
90 'Frank.Vibrans' => 'Frank Vibrans',
91 'FrankChu' => 'Frank Chu',
92 'garmin chang' => 'Garmin Chang',
93 'Garmin.Chang' => 'Garmin Chang',
94 'hannahwilliams2' => 'Hannah Williams',
95 'HAOUAS Elyes' => 'Elyes Haouas',
96 'Harshapriya N' => 'Harsha Priya',
97 'Hsuan-ting Chen' => 'Hsuan Ting Chen',
98 'Iru Cai (vimacs)' => 'Iru Cai',
99 'Jérémy Compostella' => 'Jeremy Compostella',
100 'Jérémy Compostella' => 'Jeremy Compostella',
101 'JG Poxu' => 'Po Xu',
102 'JonathonHall-Purism' => 'Jonathon Hall',
103 'Karthikeyan Ramasubramanian' => 'Karthik Ramasubramanian',
104 'Kerry She' => 'Kerry Sheh',
105 'kewei.xu' => 'kewei xu',
106 'Kumar, Gomathi' => 'Gomathi Kumar',
107 'Kyösti Mälkki' => 'Kyösti Mälkki',
108 'Kyösti Mälkki' => 'Kyösti Mälkki',
109 'Marcello Sylvester Bauer' => 'Marcello Sylvester Bauer',
110 'Martin L Roth' => 'Martin Roth',
111 'Martin Roth - Personal' => 'Martin Roth',
112 'Matt Ziegelbaum' => 'Matthew Ziegelbaum',
113 'mengqi.zhang' => 'Mengqi Zhang',
114 'mrnuke' => 'Alexandru Gagniuc',
115 'Nina-CM Wu' => 'Nina Wu',
116 'ot_zhenguo.li' => 'Zhenguo Li',
117 'Patrick Georgi patrick.georgi' => 'Patrick Georgi',
118 'Patrick Georgi patrick' => 'Patrick Georgi',
119 'Pavlushka' => 'Pavel Sayekat',
120 'Ravi Kumar Bokka' => 'Ravi Kumar',
121 'Ravi kumar' => 'Ravi Kumar',
122 'Ravi Sarawadi' => 'Ravishankar Sarawadi',
123 'ravindr1' => 'Ravindra',
124 'Ravindra N' => 'Ravindra',
125 'Ricardo Ribalda Delgado' => 'Ricardo Ribalda',
126 'ron minnich' => 'Ron Minnich',
127 'Ronald G. Minnich' => 'Ron Minnich',
128 'samrab' => 'Sudheer Amrabadi',
129 'SANTHOSH JANARDHANA HASSAN' => 'Santhosh Janardhana Hassan',
130 'semihalf-czapiga-jakub' => 'Jakub Czapiga',
131 'Seunghwan Kim' => 'SH Kim',
132 'Sooi, Li Cheng' => 'Li Cheng Sooi',
133 'Stefan Reinauerstepan' => 'Stefan Reinauer',
134 'stepan' => 'Stefan Reinauer',
135 'Swift Geek (Sebastian Grzywna)' => 'Sebastian "Swift Geek" Grzywna',
136 'Sylvain "ythier" Hitier' => 'Sylvain Hitier',
137 'Thomas Gstädtner' => 'Thomas Gstaedtner',
138 'UwePoeche' => 'Uwe Poeche',
139 'UwePoeche' => 'Uwe Poeche',
140 'Varshit Pandya' => 'Varshit B Pandya',
141 'Wayne3 Wang' => 'Wayne Wang',
142 'Wayne3_Wang' => 'Wayne Wang',
143 'Xi Chen' => 'Xixi Chen',
144 'Yu-Hsuan Hsu' => 'Yu-hsuan Hsu',
145 'zbao' => 'Zheng Bao',
146 'Zheng Bao zheng.bao' => 'Zheng Bao',
147 'zhiyong tao' => 'Zhiyong Tao',
148 'Дмитрий Понаморев' => 'Dmitry Ponamorev',
149 );
150
151 if ( !$URL_WITH_USER ) {
152 get_user();
Martin Rothce005da2016-04-01 14:30:33 -0600153 }
154
Martin Rothae7d8372022-12-10 22:00:40 -0700155 print "Saving data to $infodir\n";
156
157 # Make sure the versions exist
Martin Rothce005da2016-04-01 14:30:33 -0600158 check_versions();
159
Martin Rothae7d8372022-12-10 22:00:40 -0700160 # Fetch patches if needed. Get ids of first and last commits
161 my @commits = `git log --pretty=%H "$old_version..$new_version" 2>/dev/null`;
Martin Rothce005da2016-04-01 14:30:33 -0600162 get_commits(@commits);
Martin Rothae7d8372022-12-10 22:00:40 -0700163 my $last_commit_id = $commits[0];
164 my $first_commit_id = $commits[ @commits - 1 ];
Martin Rothce005da2016-04-01 14:30:33 -0600165 chomp $last_commit_id;
166 chomp $first_commit_id;
167
168 print "Statistics from commit $first_commit_id to commit $last_commit_id\n";
Martin Rothae7d8372022-12-10 22:00:40 -0700169 print "Patch, Date, Owner, Author, Submitter, Inserted lines, Deleted lines, Subject, Reviewers, Commenters\n";
Martin Rothce005da2016-04-01 14:30:33 -0600170
Martin Rothae7d8372022-12-10 22:00:40 -0700171 # Loop through all commits
Martin Rothce005da2016-04-01 14:30:33 -0600172 for my $commit_id (@commits) {
173 $commit_id =~ s/^\s+|\s+$//g;
174
Martin Rothae7d8372022-12-10 22:00:40 -0700175 my $submitter = "";
176 my %patch_reviewers = ();
177 my %patch_commenters = ();
Martin Rothce005da2016-04-01 14:30:33 -0600178 my $info;
179 my $owner;
180 my $author;
181 my $author_email;
182 my $inserted_lines = 0;
Martin Rothae7d8372022-12-10 22:00:40 -0700183 my $deleted_lines = 0;
Martin Rothce005da2016-04-01 14:30:33 -0600184 my $subject;
185
186 $number_of_commits++;
187 print "\"$commit_id\", ";
188
Martin Rothae7d8372022-12-10 22:00:40 -0700189 # Read the data file for the current commit
190 if ( -f "$infodir/$commit_id" && -s "$infodir/$commit_id" > 20 ) {
Martin Rothce005da2016-04-01 14:30:33 -0600191 open( my $HANDLE, "<", "$infodir/$commit_id" ) or die "Error: could not open file '$infodir/$commit_id'\n";
192 $info = <$HANDLE>;
193 close $HANDLE;
194
195 my $commit_info = JSON::Util->decode($info);
196
Martin Rothae7d8372022-12-10 22:00:40 -0700197 # Get the easy data
Martin Rothce005da2016-04-01 14:30:33 -0600198 $owner = $commit_info->{'owner'}{'name'};
Martin Rothae7d8372022-12-10 22:00:40 -0700199 if ( !$owner ) {
Martin Rothce005da2016-04-01 14:30:33 -0600200 $owner = $commit_info->{'owner'}{'username'};
201 }
Martin Rothae7d8372022-12-10 22:00:40 -0700202 if ( !$owner ) {
203 $owner = "-";
Martin Rothce005da2016-04-01 14:30:33 -0600204 }
Martin Rothae7d8372022-12-10 22:00:40 -0700205 if ( $owner && exists( $aliases{$owner} ) ) {
206 $owner = $aliases{$owner};
Martin Rothce005da2016-04-01 14:30:33 -0600207 }
Martin Roth647252b2023-08-20 19:25:53 -0600208 $owner =~ s/"/'/g;
209 $owner =~ s/,/./g;
Martin Rothce005da2016-04-01 14:30:33 -0600210
Martin Rothae7d8372022-12-10 22:00:40 -0700211 $author = $commit_info->{'currentPatchSet'}{'author'}{'name'};
212 if ( $author && exists( $aliases{$author} ) ) {
213 $author = $aliases{$author};
214 }
215 if ( !$author ) {
216 $author = $commit_info->{'currentPatchSet'}{'author'}{'username'};
217 }
Martin Roth647252b2023-08-20 19:25:53 -0600218 $author =~ s/"/'/g;
219 $author =~ s/,/./g;
Martin Rothae7d8372022-12-10 22:00:40 -0700220 $author_email = $commit_info->{'currentPatchSet'}{'author'}{'email'};
Martin Rothce005da2016-04-01 14:30:33 -0600221 $inserted_lines = $commit_info->{'currentPatchSet'}{'sizeInsertions'};
Martin Rothae7d8372022-12-10 22:00:40 -0700222 $deleted_lines = $commit_info->{'currentPatchSet'}{'sizeDeletions'};
223 $subject = $commit_info->{'subject'};
Martin Roth647252b2023-08-20 19:25:53 -0600224 $subject =~ s/"/'/g;
Martin Rothce005da2016-04-01 14:30:33 -0600225
226 #get the patch's submitter
227 my $approvals = $commit_info->{'currentPatchSet'}{'approvals'};
228 for my $approval (@$approvals) {
Martin Rothae7d8372022-12-10 22:00:40 -0700229 if ( $approval->{'type'} eq "SUBM" ) {
Martin Rothce005da2016-04-01 14:30:33 -0600230 $submit_epoch = $approval->{'grantedOn'};
Martin Rothae7d8372022-12-10 22:00:40 -0700231 $submitter = $approval->{'by'}{'name'};
232 if ( exists( $aliases{$submitter} ) ) {
233 $submitter = $aliases{$submitter};
234 }
Martin Rothce005da2016-04-01 14:30:33 -0600235 }
236 }
237
Martin Rothae7d8372022-12-10 22:00:40 -0700238 # Get all the commenters for all patch revisions
239 my $comments = $commit_info->{'comments'};
240 for my $comment (@$comments) {
241 my $commenter;
242 if ( $comment->{'reviewer'}{'username'} ) {
243 if ( $comment->{'reviewer'}{'username'} eq "jenkins" ) {
244 next;
245 }
246 if ( $comment->{'reviewer'}{'username'} eq "hardwaretestrobot" ) {
247 next;
248 }
249 if ( $comment->{'reviewer'}{'username'} eq "raptor-automated-test" ) {
250 next;
251 }
252 }
253
254 if ( $comment->{'reviewer'}{'name'} ) {
255 if ( $comment->{'reviewer'}{'name'} eq "Gerrit Code Review" ) {
256 next;
257 }
258 }
259 if ( $comment->{'message'} ) {
260 if ( $comment->{'message'} =~ "successfully cherry-picked" ) {
261 next;
262 }
263 if ( $comment->{'message'} =~ ": Code-Review" ) {
264 next;
265 }
266 if ( $comment->{'message'} =~ "Uploaded patch set" ) {
267 next;
268 }
269 }
270
271 if ( !$commenter ) {
272 $commenter = $comment->{'reviewer'}{'name'};
273 if ( $commenter && exists( $aliases{$commenter} ) ) {
274 $commenter = $aliases{$commenter};
275 }
276 }
277 if ( !$commenter ) {
278 $commenter = $comment->{'reviewer'}{'username'};
279 if ( $commenter && exists( $aliases{$commenter} ) ) {
280 $commenter = $aliases{$commenter};
281 }
282 }
Martin Roth647252b2023-08-20 19:25:53 -0600283 $commenter =~ s/"/'/g;
284 $commenter =~ s/,/./g;
Martin Rothae7d8372022-12-10 22:00:40 -0700285 if ( $commenter && $author && $commenter eq $author ) {
286 next;
287 }
288 if ($commenter) {
289 if ( $commenter && exists $patch_commenters{$commenter} ) {
290 $patch_commenters{$commenter}++;
291 } else {
292 $patch_commenters{$commenter} = 1;
293 }
294 }
295 }
296
297 # Get all the reviewers for all patch revisions
Martin Rothce005da2016-04-01 14:30:33 -0600298 my $patchsets = $commit_info->{'patchSets'};
299 for my $patch (@$patchsets) {
Martin Rothae7d8372022-12-10 22:00:40 -0700300 if ( !$author ) {
Martin Rothce005da2016-04-01 14:30:33 -0600301 $author = $patch->{'author'}{'name'};
Martin Rothae7d8372022-12-10 22:00:40 -0700302 if ( $author && exists( $aliases{$author} ) ) {
303 $author = $aliases{$author};
304 }
Martin Rothce005da2016-04-01 14:30:33 -0600305 }
Martin Rothae7d8372022-12-10 22:00:40 -0700306
Martin Rothce005da2016-04-01 14:30:33 -0600307 my $approvals = $patch->{'approvals'};
308 for my $approval (@$approvals) {
309
Martin Rothae7d8372022-12-10 22:00:40 -0700310 if ( ( !$submitter ) && ( $approval->{'type'} eq "SUBM" ) ) {
Martin Rothce005da2016-04-01 14:30:33 -0600311 $submit_epoch = $approval->{'grantedOn'};
Martin Rothae7d8372022-12-10 22:00:40 -0700312 $submitter = $approval->{'by'}{'name'};
313 if ( $submitter && exists( $aliases{$submitter} ) ) {
314 $submitter = $aliases{$submitter};
315 }
Martin Rothce005da2016-04-01 14:30:33 -0600316 }
Martin Roth647252b2023-08-20 19:25:53 -0600317 $submitter =~ s/"/'/g;
318 $submitter =~ s/,/./g;
Martin Rothce005da2016-04-01 14:30:33 -0600319
Martin Rothae7d8372022-12-10 22:00:40 -0700320 if ( $approval->{'type'} eq "Code-Review" ) {
Martin Rothce005da2016-04-01 14:30:33 -0600321 my $patch_reviewer = $approval->{'by'}{'name'};
322 if ($patch_reviewer) {
Martin Rothae7d8372022-12-10 22:00:40 -0700323 if ( exists $patch_reviewers{$patch_reviewer} ) {
Martin Rothce005da2016-04-01 14:30:33 -0600324 $patch_reviewers{$patch_reviewer}++;
325 } else {
326 $patch_reviewers{$patch_reviewer} = 1;
327 }
328 }
329 }
330 }
331 }
332
333 } else {
Martin Rothae7d8372022-12-10 22:00:40 -0700334
335 # Get the info from git
Martin Rothce005da2016-04-01 14:30:33 -0600336 my $logline = `git log --pretty="%ct@@@%s@@@%an@@@%aE@@@%cn" $commit_id^..$commit_id --`;
337 $logline =~ m/^(.*)@@@(.*)@@@(.*)@@@(.*)@@@(.*)\n/;
Martin Rothae7d8372022-12-10 22:00:40 -0700338 ( $submit_epoch, $subject, $author, $author_email, $submitter ) = ( $1, $2, $3, $4, $5 );
339 if ( exists( $aliases{$author} ) ) {
340 $author = $aliases{$author};
341 }
Martin Rothce005da2016-04-01 14:30:33 -0600342 $owner = $author;
Martin Rothae7d8372022-12-10 22:00:40 -0700343
344 if ( $submitter && exists( $aliases{$submitter} ) ) {
345 $submitter = $aliases{$submitter};
346 }
347
Martin Rothce005da2016-04-01 14:30:33 -0600348 $logline = `git log --pretty= --shortstat $commit_id^..$commit_id --`;
Martin Rothae7d8372022-12-10 22:00:40 -0700349 if ( $logline =~ m/\s+(\d+)\s+insertion/ ) {
Martin Rothce005da2016-04-01 14:30:33 -0600350 $inserted_lines = $1;
351 }
Martin Rothae7d8372022-12-10 22:00:40 -0700352 if ( $logline =~ m/\s+(\d+)\s+deletion/ ) {
Martin Rothce005da2016-04-01 14:30:33 -0600353 $deleted_lines = $1 * -1;
354 }
355 my @loglines = `git log $commit_id^..$commit_id -- | grep '\\sReviewed-by:'`;
Martin Rothae7d8372022-12-10 22:00:40 -0700356 for my $line (@loglines) {
357 if ( $line =~ m/.*:\s+(.*)\s</ ) {
Martin Rothce005da2016-04-01 14:30:33 -0600358 my $patch_reviewer = $1;
Martin Rothae7d8372022-12-10 22:00:40 -0700359 if ( exists( $aliases{$patch_reviewer} ) ) {
360 $patch_reviewer = $aliases{$patch_reviewer};
361 }
Martin Rothce005da2016-04-01 14:30:33 -0600362 if ($patch_reviewer) {
Martin Rothae7d8372022-12-10 22:00:40 -0700363 if ( exists $patch_reviewers{$patch_reviewer} ) {
Martin Rothce005da2016-04-01 14:30:33 -0600364 $patch_reviewers{$patch_reviewer}++;
365 } else {
366 $patch_reviewers{$patch_reviewer} = 1;
367 }
368 }
369 }
370 }
371
372 }
373
374 # Not entirely certain why this is needed, but for a number of patches have been submitted
375 # the submit time in gerrit is set to April 9, 2015.
Martin Rothae7d8372022-12-10 22:00:40 -0700376 if ( $submit_epoch == 1428586219 ) {
Martin Rothce005da2016-04-01 14:30:33 -0600377 my $logline = `git log --pretty="%ct" $commit_id^..$commit_id --`;
378 $logline =~ m/^(.*)\n/;
379 $submit_epoch = $1;
380 }
381
Martin Rothae7d8372022-12-10 22:00:40 -0700382 # Add the count and owner to the submitter hash
383 if ( $submitter && exists $submitters{$submitter} && exists $submitters{$submitter}{count} ) {
384 $submitters{$submitter}{count}++;
Martin Rothce005da2016-04-01 14:30:33 -0600385 } else {
Martin Rothae7d8372022-12-10 22:00:40 -0700386 $submitters{$submitter}{count} = 1;
Martin Rothce005da2016-04-01 14:30:33 -0600387 $number_of_submitters++;
Martin Rothae7d8372022-12-10 22:00:40 -0700388 $submitters{$submitter}{"self"} = 0;
389 $submitters{$submitter}{others} = 0;
390 $submitters{$submitter}{name} = $submitter;
Martin Rothce005da2016-04-01 14:30:33 -0600391 }
392
Martin Rothae7d8372022-12-10 22:00:40 -0700393 if ( $submitter eq $author ) {
394 $submitters{$submitter}{"self"}++;
395 } else {
396 $submitters{$submitter}{others}++;
397 }
398
399 # Create a readable date
400 my $dt = DateTime->from_epoch( epoch => $submit_epoch );
401 $dt->set_time_zone('Europe/Paris');
402 my $submit_time = $dt->strftime('%Y/%m/%d');
403 if ( !$first_submit_epoch ) {
Martin Rothce005da2016-04-01 14:30:33 -0600404 $first_submit_epoch = $submit_epoch;
405 }
406
Martin Rothae7d8372022-12-10 22:00:40 -0700407 # Create the list of commenters to print
408 my $commenterlist = "";
409 foreach my $commenter ( keys %patch_commenters ) {
410 if ( $commenter && exists( $aliases{$commenter} ) ) {
411 $commenter = $aliases{$commenter};
412 }
413
414 if ( $commenterlist eq "" ) {
415 $commenterlist = $commenter;
416 } else {
417 $commenterlist .= ", $commenter";
418 }
419
420 if ( $commenter && exists $commenters{$commenter} ) {
421 $commenters{$commenter}++;
422 } else {
423 $commenters{$commenter} = 1;
424 }
425 }
426 if ( !$commenterlist ) {
427 $commenterlist = "-";
428 }
429
430 # Create the list of reviewers to print
Martin Rothce005da2016-04-01 14:30:33 -0600431 my $reviewerlist = "";
Martin Rothae7d8372022-12-10 22:00:40 -0700432 foreach my $reviewer ( keys %patch_reviewers ) {
433 if ( exists( $aliases{$reviewer} ) ) {
434 $reviewer = $aliases{$reviewer};
435 }
436
437 if ( $reviewerlist eq "" ) {
Martin Rothce005da2016-04-01 14:30:33 -0600438 $reviewerlist = $reviewer;
439 } else {
440 $reviewerlist .= ", $reviewer";
441 }
442
Martin Rothae7d8372022-12-10 22:00:40 -0700443 if ( $reviewer && exists $reviewers{$reviewer} ) {
Martin Rothce005da2016-04-01 14:30:33 -0600444 $reviewers{$reviewer}++;
445 } else {
446 $reviewers{$reviewer} = 1;
447 }
448 }
Martin Rothae7d8372022-12-10 22:00:40 -0700449 if ( !$reviewerlist ) {
450 $reviewerlist = "-";
Martin Rothce005da2016-04-01 14:30:33 -0600451 }
452
453 if ($print_commit_list) {
Martin Rothae7d8372022-12-10 22:00:40 -0700454 print "$submit_time, $owner, $author, $submitter, $inserted_lines, $deleted_lines, \"$subject\", \"$reviewerlist\" , \"$commenterlist\"\n";
Martin Rothce005da2016-04-01 14:30:33 -0600455 } else {
456 print "$number_of_commits\n";
457 }
458 $total_added += $inserted_lines;
Martin Rothae7d8372022-12-10 22:00:40 -0700459 if ( $inserted_lines - $deleted_lines > 100 ) {
460 $patches_over_100_lines++;
461 $total_lines_large_patches += $inserted_lines;
462 }
Martin Rothce005da2016-04-01 14:30:33 -0600463 $total_removed += $deleted_lines;
Martin Rothae7d8372022-12-10 22:00:40 -0700464 if ( exists $owners{$owner} ) {
Martin Rothce005da2016-04-01 14:30:33 -0600465 $owners{$owner}++;
466 } else {
467 $owners{$owner} = 1;
468 }
469
Martin Rothae7d8372022-12-10 22:00:40 -0700470 if ( $author && exists $authors{$author}{"num"} ) {
Martin Rothce005da2016-04-01 14:30:33 -0600471 $authors{$author}{"num"}++;
Martin Rothae7d8372022-12-10 22:00:40 -0700472 $author_added{$author} += $inserted_lines;
Martin Rothce005da2016-04-01 14:30:33 -0600473 $author_removed{$author} += $deleted_lines;
Martin Rothae7d8372022-12-10 22:00:40 -0700474 $authors{$author}{"earliest_commit"} = $submit_time;
Martin Rothce005da2016-04-01 14:30:33 -0600475 } else {
Martin Rothae7d8372022-12-10 22:00:40 -0700476 $authors{$author}{"num"} = 1;
477 $authors{$author}{"latest_commit"} = $submit_time;
478 $authors{$author}{"earliest_commit"} = $submit_time;
479 $author_added{$author} = $inserted_lines;
480 $author_removed{$author} = $deleted_lines;
Martin Rothce005da2016-04-01 14:30:33 -0600481 }
Martin Rothae7d8372022-12-10 22:00:40 -0700482 if ( $author && ( !exists $authors{$author}{email} || $authors{$author}{email} eq "-" ) ) {
483 if ($author_email) {
484 $authors{$author}{email} = "$author_email";
485 } elsif ( exists $email_addresses{$author} ) {
486 $authors{$author}{email} = $email_addresses{$author};
487 }
Martin Rothce005da2016-04-01 14:30:33 -0600488 }
489 }
Martin Rothae7d8372022-12-10 22:00:40 -0700490 my $Days = ( $first_submit_epoch - $submit_epoch ) / 86400;
491 if ( ( $first_submit_epoch - $submit_epoch ) % 86400 ) {
Martin Rothce005da2016-04-01 14:30:33 -0600492 $Days += 1;
493 }
494
Martin Roth647252b2023-08-20 19:25:53 -0600495 print "\n* Total Commits: $number_of_commits\n";
496 printf "* Average Commits per day: %.2f\n", $number_of_commits / $Days;
497 print "* Total lines added: $total_added\n";
498 printf "* Average lines added per commit: %.2f\n", $total_added / $number_of_commits;
499 print "* Number of patches adding more than 100 lines: $patches_over_100_lines\n";
500 printf "* Average lines added per small commit: %.2f\n", ( $total_added - $total_lines_large_patches ) / ( $number_of_commits - $patches_over_100_lines );
Martin Rothae7d8372022-12-10 22:00:40 -0700501
Martin Roth647252b2023-08-20 19:25:53 -0600502 print "* Total lines removed: $total_removed\n";
503 printf "* Average lines removed per commit: %.2f\n", $total_removed / $number_of_commits;
504 print "* Total difference between added and removed: " . ( $total_added - $total_removed ) . "\n\n";
Martin Rothce005da2016-04-01 14:30:33 -0600505
506 print "=== Authors - Number of commits ===\n";
Martin Rothae7d8372022-12-10 22:00:40 -0700507 printf "%-30s ,%5s ,%5s ,%6s ,%6s , %-52s ,%6s, %-19s , %s\n", "Author", "Ptchs", "Revws", "Cmnts", "Sbmts", "Email", "Prcnt", "Last commit", "Earliest_commit";
508
Martin Rothce005da2016-04-01 14:30:33 -0600509 my $number_of_authors = 0;
Martin Rothae7d8372022-12-10 22:00:40 -0700510 foreach my $author ( sort { $authors{$b}{num} <=> $authors{$a}{num} } ( keys %authors ) ) {
511 my $submissions = 0;
512 if ( $author && exists $submitters{$author} ) {
513 $submissions = $submitters{$author}{count};
514 }
515 my $review_count = 0;
516 if ( $author && exists $reviewers{$author} ) {
517 $review_count = $reviewers{$author};
518 }
519
520 my $comment_count = 0;
521 if ( $author && exists $commenters{$author} ) {
522 $comment_count = $commenters{$author};
523 }
524
525 if ( $author && !exists $authors{$author}{"email"} ) {
526 $authors{$author}{"email"} = "-";
527 }
528
529 printf "%-30s ,%5d ,%5d ,%6d ,%6d , %-52s ,%5.2f%%, %s , %s\n", $author, $authors{$author}{"num"}, $review_count,
530 $comment_count, $submissions, $authors{$author}{"email"}, $authors{$author}{"num"} / $number_of_commits * 100,
531 $authors{$author}{"latest_commit"}, $authors{$author}{"earliest_commit"};
Martin Rothce005da2016-04-01 14:30:33 -0600532 $number_of_authors++;
533 }
534 print "Total Authors: $number_of_authors\n\n";
535
536 print "=== Authors - Lines added ===\n";
Martin Rothae7d8372022-12-10 22:00:40 -0700537 foreach my $author ( sort { $author_added{$b} <=> $author_added{$a} } ( keys %author_added ) ) {
538 if ( $author_added{$author} ) {
539 printf "%-30s, %10d, %2.3f%%\n", $author, $author_added{$author}, $author_added{$author} / $total_added * 100;
Martin Rothce005da2016-04-01 14:30:33 -0600540 }
541 }
542 print "\n";
543
544 print "=== Authors - Lines removed ===\n";
Martin Rothae7d8372022-12-10 22:00:40 -0700545 foreach my $author ( sort { $author_removed{$b} <=> $author_removed{$a} } ( keys %author_removed ) ) {
546 if ( $author_removed{$author} ) {
547 printf "%-30s, %10d, %6.3f%%\n", $author, $author_removed{$author} * -1, $author_removed{$author} / $total_removed * 100;
Martin Rothce005da2016-04-01 14:30:33 -0600548 }
549 }
550 print "\n";
551
552 print "=== Reviewers - Number of patches reviewed ===\n";
553 my $number_of_reviewers = 0;
Martin Rothae7d8372022-12-10 22:00:40 -0700554 foreach my $reviewer ( sort { $reviewers{$b} <=> $reviewers{$a} } ( keys %reviewers ) ) {
555 printf "%-30s, %6d, %6.3f%%\n", $reviewer, $reviewers{$reviewer}, $reviewers{$reviewer} / $number_of_commits * 100;
Martin Rothce005da2016-04-01 14:30:33 -0600556 $number_of_reviewers++;
557 }
558 print "Total Reviewers: $number_of_reviewers\n\n";
559
560 print "=== Submitters - Number of patches submitted ===\n";
Martin Rothae7d8372022-12-10 22:00:40 -0700561 printf "%-30s, %6s, %7s, %6s, %7s, %6s, %7s\n", "Name", "#", "total%", "Own", "own%", "Other", "other%";
562 foreach my $submitter ( sort { $submitters{$b}{count} <=> $submitters{$a}{count} } ( keys %submitters ) ) {
563 printf "%-30s, % 6d, %6.3f%%, %6d, %6.2f%%, %6d, %6.2f%%\n",
564 $submitter,
565 $submitters{$submitter}{count},
566 $submitters{$submitter}{count} / $number_of_commits * 100,
567 $submitters{$submitter}{"self"},
568 $submitters{$submitter}{"self"} / $submitters{$submitter}{count} * 100,
569 $submitters{$submitter}{others}, $submitters{$submitter}{others} / $submitters{$submitter}{count} * 100;
Martin Rothce005da2016-04-01 14:30:33 -0600570 }
571 print "Total Submitters: $number_of_submitters\n\n";
572
573 print "Commits, Ave, Added, Removed, Diff, Authors, Reviewers, Submitters\n";
Martin Rothae7d8372022-12-10 22:00:40 -0700574 printf "$number_of_commits, %.2f, $total_added, $total_removed, " . ( $total_added + $total_removed ) . ", $number_of_authors, $number_of_reviewers, $number_of_submitters\n",
575 $number_of_commits / $Days;
Martin Rothce005da2016-04-01 14:30:33 -0600576}
577
578#-------------------------------------------------------------------------------
579#-------------------------------------------------------------------------------
580sub check_versions {
581 `git cat-file -e $old_version^{commit} 2>/dev/null`;
Martin Rothae7d8372022-12-10 22:00:40 -0700582 if ( ${^CHILD_ERROR_NATIVE} ) {
Martin Rothce005da2016-04-01 14:30:33 -0600583 print "Error: Old version ($old_version) does not exist.\n";
584 exit 1;
585 }
586
587 `git cat-file -e $new_version^{commit} 2>/dev/null`;
Martin Rothae7d8372022-12-10 22:00:40 -0700588 if ( ${^CHILD_ERROR_NATIVE} ) {
Martin Rothce005da2016-04-01 14:30:33 -0600589 print "Error: New version ($new_version) does not exist.\n";
590 exit 1;
591 }
592}
593
594#-------------------------------------------------------------------------------
Martin Rothce005da2016-04-01 14:30:33 -0600595sub get_user {
Martin Rothce005da2016-04-01 14:30:33 -0600596
Martin Rothae7d8372022-12-10 22:00:40 -0700597 my $url = `git config -l | grep remote.origin.url`;
598
599 if ( $url =~ /.*url=ssh:\/\/(\w+@[a-zA-Z][a-zA-Z0-9\.]+:\d+)(\/\w+)*/ ) {
Martin Rothce005da2016-04-01 14:30:33 -0600600 $URL_WITH_USER = $1;
601 } else {
602 print "Error: Could not get a ssh url with a username from gitconfig.\n";
603 print " use the -u option to set a url.\n";
604 exit 1;
605 }
606}
607
608#-------------------------------------------------------------------------------
609#-------------------------------------------------------------------------------
610sub get_commits {
Martin Rothae7d8372022-12-10 22:00:40 -0700611 my @commits = @_;
Martin Rothce005da2016-04-01 14:30:33 -0600612 my $submit_time = "";
Martin Rothae7d8372022-12-10 22:00:40 -0700613 if ( defined $SKIP_GERRIT_CHECK && $SKIP_GERRIT_CHECK ) {
Martin Rothce005da2016-04-01 14:30:33 -0600614 return;
615 }
Martin Rothae7d8372022-12-10 22:00:40 -0700616 my $ssh = Net::OpenSSH->new( "$URL_WITH_USER", );
617 $ssh->error and die "Couldn't establish SSH connection to $URL_WITH_USER:" . $ssh->error;
Martin Rothce005da2016-04-01 14:30:33 -0600618
619 print "Using URL: ssh://$URL_WITH_USER\n";
620
Martin Rothae7d8372022-12-10 22:00:40 -0700621 if ( !-d $infodir ) {
622 mkpath($infodir);
Martin Rothce005da2016-04-01 14:30:33 -0600623 }
624
625 for my $commit_id (@commits) {
626 $commit_id =~ s/^\s+|\s+$//g;
627 $submit_time = "";
Martin Rothae7d8372022-12-10 22:00:40 -0700628 my $gerrit_review;
Martin Rothce005da2016-04-01 14:30:33 -0600629
Martin Rothae7d8372022-12-10 22:00:40 -0700630 # Look for last coreboot commit
631 if ( $commit_id eq "7309709742" ) {
Martin Rothce005da2016-04-01 14:30:33 -0600632 last;
633 }
634
Martin Rothae7d8372022-12-10 22:00:40 -0700635 if ( -f "$infodir/$commit_id" ) {
Martin Rothce005da2016-04-01 14:30:33 -0600636 $gerrit_review = 1;
637 } else {
638 $gerrit_review = `git log $commit_id^..$commit_id | grep '\\sReviewed-on:\\s'`;
639 }
640
Martin Rothae7d8372022-12-10 22:00:40 -0700641 if ( $gerrit_review && $commit_id && ( !-f "$infodir/$commit_id" ) ) {
Martin Rothce005da2016-04-01 14:30:33 -0600642 print "Downloading $commit_id";
643 my @info = $ssh->capture("gerrit query --format=JSON --comments --files --current-patch-set --all-approvals --submit-records --dependencies commit:$commit_id");
644 $ssh->error and die "remote ls command failed: " . $ssh->error;
645
Martin Rothae7d8372022-12-10 22:00:40 -0700646 my $commit_info = JSON::Util->decode( $info[0] );
647 my $rowcount = $commit_info->{'rowCount'};
648 if ( defined $rowcount && ( $rowcount eq "0" ) ) {
Martin Rothce005da2016-04-01 14:30:33 -0600649 print " - no gerrit commit for that id.\n";
650 open( my $HANDLE, ">", "$infodir/$commit_id" ) or die "Error: could not open file '$infodir/$commit_id'\n";
651 print $HANDLE "No gerrit commit";
652 close $HANDLE;
653 next;
654 }
655 my $approvals = $commit_info->{'currentPatchSet'}{'approvals'};
656
657 for my $approval (@$approvals) {
Martin Rothae7d8372022-12-10 22:00:40 -0700658 if ( $approval->{'type'} eq "SUBM" ) {
659 $submit_time = $approval->{'grantedOn'};
Martin Rothce005da2016-04-01 14:30:33 -0600660 }
661 }
Martin Rothae7d8372022-12-10 22:00:40 -0700662 my $dt = "";
Martin Rothce005da2016-04-01 14:30:33 -0600663 if ($submit_time) {
Martin Rothae7d8372022-12-10 22:00:40 -0700664 $dt = DateTime->from_epoch( epoch => $submit_time );
Martin Rothce005da2016-04-01 14:30:33 -0600665 } else {
666 print " - no submit time for that id.\n";
667 open( my $HANDLE, ">", "$infodir/$commit_id" ) or die "Error: could not open file '$infodir/$commit_id'\n";
668 print $HANDLE "No submit time";
669 close $HANDLE;
670
671 next;
672 }
673
674 open( my $HANDLE, ">", "$infodir/$commit_id" ) or die "Error: could not open file '$infodir/$commit_id'\n";
675 print $HANDLE $info[0];
676 close $HANDLE;
677
Martin Rothae7d8372022-12-10 22:00:40 -0700678 $dt->set_time_zone('Europe/Paris');
Martin Rothce005da2016-04-01 14:30:33 -0600679 print " - submit time: " . $dt->strftime('%Y/%m/%d %H:%M:%S') . "\n";
Martin Rothae7d8372022-12-10 22:00:40 -0700680 } elsif ( $commit_id && ( !-f "$infodir/$commit_id" ) ) {
Martin Rothce005da2016-04-01 14:30:33 -0600681 print "No gerrit commit for $commit_id\n";
682 open( my $HANDLE, ">", "$infodir/$commit_id" ) or die "Error: could not open file '$infodir/$commit_id'\n";
683 print $HANDLE "No gerrit commit";
684 close $HANDLE;
685 }
686 }
687 print "\n";
688}
689
690#-------------------------------------------------------------------------------
691# check_arguments parse the command line arguments
692#-------------------------------------------------------------------------------
693sub check_arguments {
694 my $show_usage = 0;
695 GetOptions(
Martin Rothae7d8372022-12-10 22:00:40 -0700696 'help|?' => sub { usage() },
697 'url|u=s' => \$URL_WITH_USER,
698 'skip|s' => \$SKIP_GERRIT_CHECK,
Martin Rothce005da2016-04-01 14:30:33 -0600699 );
Martin Rothae7d8372022-12-10 22:00:40 -0700700
Martin Rothce005da2016-04-01 14:30:33 -0600701 # strip ssh:// from url if passed in.
Martin Rothae7d8372022-12-10 22:00:40 -0700702 if ( defined $URL_WITH_USER ) {
Martin Rothce005da2016-04-01 14:30:33 -0600703 $URL_WITH_USER =~ s|ssh://||;
704 }
705 if (@ARGV) {
Martin Rothae7d8372022-12-10 22:00:40 -0700706 ( $old_version, $new_version ) = @ARGV;
Martin Rothce005da2016-04-01 14:30:33 -0600707 } else {
708 usage();
709 }
710}
711
712#-------------------------------------------------------------------------------
713# usage - Print the arguments for the user
714#-------------------------------------------------------------------------------
715sub usage {
716 print "gerrit_stats <options> [Old version] [New version]\n";
717 print "Old version should be a tag (4.1), a branch (origin/4.1), or a commit id\n";
Stefan Reinauer0d3a1fb2023-06-10 12:32:50 -0700718 print "New version can be 'HEAD' a branch (origin/main) a tag (4.2), or a commit id\n";
Martin Rothce005da2016-04-01 14:30:33 -0600719 print " Options:\n";
720 print " u | url [url] url with username.\n";
721 print "Example: \"$0 -u Gaumless\@review.coreboot.org:29418 origin/4.1 4.2\"\n";
722 exit(0);
723}
724
7251;