#!/usr/bin/env perl #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | # / __| | | | |_) | | # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # # Copyright (C) Daniel Stenberg, , 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() { 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, ") { 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;