| #!/usr/bin/env perl |
| #*************************************************************************** |
| # _ _ ____ _ |
| # Project ___| | | | _ \| | |
| # / __| | | | |_) | | |
| # | (__| |_| | _ <| |___ |
| # \___|\___/|_| \_\_____| |
| # |
| # Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. |
| # |
| # This software is licensed as described in the file COPYING, which |
| # you should have received as part of this distribution. The terms |
| # are also available at https://curl.se/docs/copyright.html. |
| # |
| # You may opt to use, copy, modify, merge, publish, distribute and/or sell |
| # copies of the Software, and permit persons to whom the Software is |
| # furnished to do so, under the terms of the COPYING file. |
| # |
| # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| # KIND, either express or implied. |
| # |
| # SPDX-License-Identifier: curl |
| # |
| ########################################################################### |
| |
| # Display changes done in the repository from [tag] until now. |
| # |
| # Uses git for repo data. |
| # Uses docs/THANKS and RELEASE-NOTES for current status. |
| # |
| # In the git clone root, invoke 'scripts/delta [release tag]' |
| |
| use strict; |
| use warnings; |
| |
| use POSIX; |
| use IPC::Open3; |
| use Time::Piece; |
| |
| sub cmd { |
| my @out; |
| my $as_string = shift if($_[0] eq '$'); # return as string (vs. list of lines) |
| my $hideerr = shift if($_[0] eq '2>'); # 2>/dev/null |
| my $pid = open3(my $in, my $out, $hideerr ? '>/dev/null' : '>&STDERR', @_); |
| close $in; |
| push @out, <$out>; |
| waitpid($pid, 0); |
| return $as_string ? join('', @out) : @out; |
| } |
| |
| my $start = $ARGV[0] || ''; |
| |
| if($start eq "-h") { |
| print "Usage: summary [tag]\n"; |
| exit; |
| } |
| elsif($start eq "") { |
| my @tags = cmd('git', 'tag', '--sort=taggerdate', '--no-column', '--list', 'curl-*'); |
| $start = $tags[-1]; # latest tag |
| chomp $start; |
| } |
| |
| my $commits = cmd('$', 'git', 'rev-list', '--count', "$start.."); |
| my $committers = cmd('git', 'shortlog', '-s', "$start.."); |
| my $bcommitters = cmd('git', 'shortlog', '-s', $start); |
| |
| my $acommits = cmd('$', 'git', 'rev-list', '--count', 'HEAD'); |
| my $acommitters = cmd('git', 'shortlog', '-s', 'HEAD'); |
| |
| # delta from now compared to before |
| my $ncommitters = $acommitters - $bcommitters; |
| |
| # number of contributors right now |
| my $acontribs = cmd('./scripts/contrithanks.sh', 'stdout'); |
| # number when the tag was set |
| my $bcontribs = cmd('$', 'git', 'grep', '-h', '-c', '^[^ ]', "$start:docs/THANKS"); |
| # delta |
| my $contribs = $acontribs - $bcontribs; |
| |
| # number of setops: |
| sub setopts { |
| my $mode = shift; |
| my $opts = 0; |
| if(open(H, $mode, @_)) { |
| while(<H>) { |
| if(/^ CURLOPT(|DEPRECATED)\(/ && ($_ !~ /OBSOLETE/)) { |
| $opts++; |
| } |
| } |
| close(H); |
| } |
| else { |
| die join(' ', @_) . ": $!\n"; |
| } |
| return $opts; |
| } |
| my $asetopts = setopts('<', 'include/curl/curl.h'); |
| my $bsetopts = setopts('-|', 'git', 'show', "$start:include/curl/curl.h"); |
| my $nsetopts = $asetopts - $bsetopts; |
| |
| # Number of command line options: |
| my $aoptions = cmd('$', '2>', 'git', 'grep', '-h', '-c', '{ *"....--', 'src/tool_listhelp.c'); |
| my $boptions = cmd('$', '2>', 'git', 'grep', '-h', '-c', '{ *"....--', "$start:src/tool_listhelp.c"); |
| my $noptions = $aoptions - $boptions; |
| |
| # current local branch |
| my $branch = cmd('$', '2>', 'git', 'rev-parse', '--abbrev-ref', 'HEAD'); |
| chomp $branch; |
| # Number of files in git |
| my $afiles = cmd('git', 'ls-files'); |
| my $deletes = cmd('2>', 'git', 'diff-tree', '--diff-filter=A', '-r', '--summary', "origin/$branch", $start); |
| my $creates = cmd('2>', 'git', 'diff-tree', '--diff-filter=D', '-r', '--summary', "origin/$branch", $start); |
| |
| # Time since that tag |
| sub tagstamp { |
| my $col = shift; |
| foreach my $line (@_) { |
| if(index($line, "$start ") == 0) { |
| my @cols = split(/\|/, $line); |
| return $cols[$col - 1]; |
| } |
| } |
| } |
| my @tagged = cmd('git', 'for-each-ref', '--format=%(refname:short) | %(taggerdate:unix) | %(creatordate)', 'refs/tags/*'); |
| my $tagged = tagstamp(2, @tagged); # Unix timestamp |
| my $taggednice = tagstamp(3, @tagged); # human readable time |
| chomp $taggednice; |
| my $now = POSIX::strftime("%s", localtime()); |
| my $elapsed = $now - $tagged; # number of seconds since tag |
| my $total = $now - Time::Piece->strptime('19980320', '%Y%m%d')->epoch; |
| my $totalhttpget = $now - Time::Piece->strptime('19961111', '%Y%m%d')->epoch; |
| |
| # Number of public functions in libcurl |
| my $apublic = cmd('git', 'grep', '^CURL_EXTERN', '--', 'include/curl'); |
| my $bpublic = cmd('git', 'grep', '^CURL_EXTERN', $start, '--', 'include/curl'); |
| my $public = $apublic - $bpublic; |
| |
| # diffstat |
| my ($fileschanged, $insertions, $deletions); |
| my @diffstat = cmd('2>', 'git', 'diff', '--stat', "$start.."); |
| my $diffstat = $diffstat[-1]; |
| if($diffstat =~ /^ *(\d+) files changed, (\d+) insertions\(\+\), (\d+)/) { |
| ($fileschanged, $insertions, $deletions) = ($1, $2, $3); |
| } |
| |
| # Changes/bug-fixes currently logged |
| my $numchanges = 0; |
| my $numbugfixes = 0; |
| my $numcontributors = 0; |
| open(F, "<RELEASE-NOTES"); |
| my $mode = 0; |
| while(<F>) { |
| if($_ =~ /following changes:/) { |
| $mode = 1; |
| } |
| elsif($_ =~ /following bugfixes:/) { |
| $mode = 2; |
| } |
| elsif($_ =~ /known bugs:/) { |
| $mode = 3; |
| } |
| elsif($_ =~ /like these:/) { |
| $mode = 4; |
| } |
| if($_ =~ /^ o /) { |
| if($mode == 1) { |
| $numchanges++; |
| } |
| elsif($mode == 2) { |
| $numbugfixes++; |
| } |
| } |
| if(($mode == 4) && ($_ =~ /^ \((\d+) contributors/)) { |
| $numcontributors = $1; |
| } |
| } |
| close(F); |
| |
| ######################################################################## |
| # Produce the summary |
| |
| print "== Since $start $taggednice ==\n"; |
| my $days = $elapsed / 3600 / 24; |
| printf "Elapsed time: %.1f days (total %d / %d)\n", |
| $days, $total / 3600 / 24, $totalhttpget / 3600 / 24; |
| printf "Commits: %d (total %d)\n", |
| $commits, $acommits; |
| printf "Commit authors: %d, %d new (total %d)\n", |
| $committers, $ncommitters, $acommitters; |
| printf "Contributors: %d, %d new (total %d)\n", |
| $numcontributors, $contribs, $acontribs; |
| printf "New public functions: %d (total %d)\n", |
| $public, $apublic; |
| printf "New curl_easy_setopt() options: %d (total %d)\n", |
| $nsetopts, $asetopts; |
| printf "New command line options: %d (total %d)\n", |
| $noptions, $aoptions; |
| printf "Changes logged: %d\n", $numchanges; |
| printf "Bugfixes logged: %d (%.2f per day)\n", |
| $numbugfixes, $days ? $numbugfixes / $days : $numbugfixes; |
| printf "Added files: %d (total %d)\n", |
| $creates, $afiles; |
| printf "Deleted files: %d (delta: %d)\n", $deletes, |
| $creates - $deletes; |
| print "Diffstat:$diffstat" if(!$fileschanged); |
| printf "Files changed: %d (%.2f%%)\n", $fileschanged, $fileschanged * 100 / $afiles; |
| printf "Lines inserted: %d\n", $insertions; |
| printf "Lines deleted: %d (delta: %d)\n", $deletions, |
| $insertions - $deletions; |