3 # Show projects' pack and/or object counts
4 # Use "show-pack-counts.pl --sorted" to see in pack count order
5 # Use the --objects option to show pack object counts instead of pack counts
10 BEGIN {*VERSION = \'2.0'}
14 use lib "__BASEDIR__";
21 $shbin = $Girocco::Config::posix_sh_bin;
22 defined($shbin) && $shbin ne "" or $shbin = "/bin/sh";
26 select((select(STDERR), $| = 1)[0]);
27 exit(&main(@ARGV)||0);
29 my ($quiet, $progress);
32 pod2usage(-exitval => 2);
37 pod2usage(-exitval => 2);
41 pod2usage(-verbose => 2, -exitval => 0);
45 print basename($0), " version ", $VERSION, "\n";
50 return defined($_[0]) ? $_[0] : $_[1];
53 my ($lastpct, $lastts);
56 return unless $progress;
57 my ($prog, $total, $prefix) = @_;
58 $total or $total = 100;
59 defined $prefix or $prefix = "";
61 defined $lastts or $lastts = $now + 1;
64 defined $lastpct or $lastpct = "";
65 my $pct = int($prog * 100 / $total);
66 $pct = 100 if $pct > 100;
67 if ($pct ne $lastpct) {
69 $prefix ne "" and $prefix .= " ";
70 printf STDERR "\r%s%3d%%", $prefix, $pct;
76 return unless $progress && defined($lastpct);
78 defined $prefix or $prefix = "";
79 $prefix ne "" and $prefix .= " ";
80 printf STDERR "\r%s100%%, done.\n", $prefix;
83 my ($packs, $packobjs, $loose, $sorton);
87 my ($help, $version, $asc);
89 close(DATA) if fileno(DATA);
91 Getopt::Long::Configure('bundling');
92 $_ eq "--sorted" || $_ eq "--sort" and $_ .= "=" foreach @_;
94 'help|h' => sub {do_help},
95 'version|V' => sub {do_version},
96 'quiet|q' => sub {$progress = 0},
97 'progress|P' => \$progress,
98 'objects|packed' => sub {$packobjs = 1},
99 'no-objects|no-packed' => sub {$packobjs = 0},
100 'packs' => sub {$packs = 1},
101 'no-packs' => sub {$packs = 0},
102 'loose' => sub {$loose = 1},
103 'no-loose' => sub {$loose = 0},
104 'full' => sub {$packs = $packobjs = $loose = 1;
105 $sorton = "objects"},
106 'sorted|sort:s' => \$sorton,
108 $packs = 1 if !defined($packs) && !defval($packobjs,0) && !defval($loose,0);
109 $packobjs = 1 if !defined($packobjs) && defined($packs) && !$packs && !defval($loose,0);
110 $packobjs || $packs || $loose or
111 die_usage_msg "At least one of --packs, --objects or --loose must be active\n";
112 if (defined($sorton)) {
113 $sorton =~ /^\+/ and $asc = 1;
114 $sorton =~ s/^[+-]//;
115 $sorton eq "" and $sorton = $packs ? "packs" : "objects";
116 $sorton =~ tr/A-Z/a-z/;
117 if ($sorton eq "packs") {
118 $packs or die_usage_msg "Sorting on \"packs\" requires the --packs option\n";
119 } elsif ($sorton eq "loose") {
120 $loose or die_usage_msg "Sorting on \"loose\" requires the --loose option\n";
121 } elsif ($sorton eq "packed") {
122 $packobjs or die_usage_msg "Sorting on \"packed\" requires the --objects option\n";
123 } elsif ($sorton eq "objects") {
124 $loose || $packobjs or
125 die_usage_msg "Sorting on \"objects\" requires the --objects and/or --loose option\n";
126 if ($loose && !$packobjs) {
128 } elsif ($packobjs && !$loose) {
132 die_usage_msg "Unknown sort field: $sorton\n";
140 my $reporoot = $Girocco::Config::reporoot;
142 my %projs = map {($_ => 1)} Girocco::Project::get_full_list;
146 exists($projs{$p}) and push(@projlist, $p), next;
147 $p =~ s/\.git$//i && exists($projs{$p}) and push(@projlist, $p), next;
148 $p =~ s,^\Q$reporoot\E/,, && exists($projs{$p}) and push(@projlist, $p), next;
149 warn "Ignoring unknown project: $_\n";
153 @projlist = sort({lc($a) cmp lc($b)} Girocco::Project::get_full_list);
157 my $pmsg = "Inspecting projects:";
158 foreach my $proj (@projlist) {
159 my ($pcks, $pobjs, $lobjs) =
160 inspect_proj("$reporoot/$proj.git", $packs, $packobjs, $loose);
162 push(@results, [$proj, $pcks, $pobjs, $lobjs]);
163 progress(scalar(@results), scalar(@projlist), $pmsg);
165 show_one($proj, $pcks, $pobjs, $lobjs);
169 progress_done($pmsg);
171 my $desc = $asc ? 1 : -1;
172 $sorton eq "packs" and $sorter = sub {
173 my $x = $$a[1] <=> $$b[1];
175 $x or $x = lc($$a[0]) cmp lc($$b[0]);
178 $sorton eq "packed" and $sorter = sub {
179 my $x = $$a[2] <=> $$b[2];
181 $x or $x = lc($$a[0]) cmp lc($$b[0]);
184 $sorton eq "loose" and $sorter = sub {
185 my $x = $$a[3] <=> $$b[3];
187 $x or $x = lc($$a[0]) cmp lc($$b[0]);
190 $sorton eq "objects" and $sorter = sub {
191 my $x = ($$a[2] + $$a[3]) <=> ($$b[2] + $$b[3]);
193 $x or $x = lc($$a[0]) cmp lc($$b[0]);
196 foreach my $info (sort($sorter @results)) {
204 my ($name, $pcks, $pobjs, $lobjs) = @_;
206 if (defined($sorton)) {
207 $sorton eq "packs" and $line .= $pcks;
208 $sorton eq "packed" and $line .= $pobjs;
209 $sorton eq "loose" and $line .= $lobjs;
210 $sorton eq "objects" and $line .= ($pobjs + $lobjs);
214 } elsif ($packobjs && $loose) {
215 $line .= ($pobjs + $lobjs);
216 } elsif ($packobjs) {
224 $packs and push(@info, "packs: " . $pcks);
225 $packobjs and push(@info, "packed: " . $pobjs);
226 $loose and push(@info, "loose: " . $lobjs);
228 length($name) < 8 and $line .= "\t";
229 length($name) < 16 and $line .= "\t";
230 length($name) < 24 and $line .= "\t";
231 $line .= " \t(" . join(", ", @info) . ")";
238 $lpbin = $Girocco::Config::basedir . "/bin/list_packs";
239 @lpcmd = ($lpbin, "--exclude-no-idx");
242 sub count_proj_packs {
245 my $pd = $projdir . "/objects/pack";
246 -d $pd or warn("no such project path (anymore) $pd\n"), return undef;
247 open LPCMD, '-|', @lpcmd, ($objs ? "--count-objects" : "--count"), $pd or
248 warn("failed to run list_packs for project dir $pd\n"), return undef;
249 my $pcount = <LPCMD>;
252 $pcount =~ /^\d+$/ or
253 warn("list_packs produced invalid output for project dir $pd\n"), return undef;
257 my ($octet, $octet19);
259 $octet = '[0-9a-f][0-9a-f]';
260 $octet19 = $octet x 19;
263 sub count_proj_loose {
265 my $od = $projdir . "/objects";
266 -d $od or warn("no such project path (anymore) $od\n"), return undef;
267 open FINDCMD, '-|', $shbin, '-c', "cd " . quotemeta($od) . " && " .
268 "find -L $octet -maxdepth 1 -name '$octet19*' -print 2>/dev/null | wc -l" or
269 warn("failed to run find for project dir $od\n"), return undef;
270 my $ocount = <FINDCMD>;
271 $ocount =~ s/\s+//gs;
273 $ocount =~ /^\d+$/ or
274 warn("find + wc produced invalid output for project dir $od\n"), return undef;
280 my ($pcks, $pobjs, $lobjs);
281 $pcks = count_proj_packs($path) || 0 if $packs;
282 $pobjs = count_proj_packs($path, 1) || 0 if $packobjs;
283 $lobjs = count_proj_loose($path) || 0 if $loose;
284 return ($pcks, $pobjs, $lobjs);
291 show-pack-counts.pl - show projects' pack and/or object counts
295 show-pack-counts.pl [<options>] [<projname>...]
298 -h | --help detailed instructions
299 -V | --version show version
300 -q | --quiet suppress progress messages
301 -P | --progress show progress messages (default)
302 --sorted[=[+]<field>] sort output on <field>
303 --objects count objects in packs instead of packs
304 --no-objects omit objects in packs count (default)
305 --packs always include pack count
306 --no-packs always omit pack count
307 --loose count loose objects (implies --no-packs)
308 --no-loose omit loose objects count (default)
309 --full shortcut meta option that sets
310 --packs --objects --loose --sorted=objects
312 <field> can be loose, packed, packs or objects and may be preceded by an
313 optional + to sort in ascending rather than descending order.
314 If no sorted option is given output will be shown unsorted as it's gathered.
315 The default sort field depends on the options being packs if pack counts are
316 included and objects otherwise. Using a field name of + changes the
317 default sorting order to ascending without changing the default sort field.
319 <projname>... if given, only inspect these projects
325 =item B<-h>, B<--help>
327 Print the full description of show-pack-counts.pl's options.
329 =item B<-V>, B<--version>
331 Print the version of show-pack-counts.pl.
333 =item B<-q>, B<--quiet>
335 Suppress progress messages. There are never any progress messages unless
336 sorted output is active.
338 =item B<-P>, B<--progress>
340 Show progress information to STDERR while collecting sorted output. This is
341 the default unless STDERR is not a TTY.
343 =item B<--sorted[=>I<<fieldE<gt>>B<]>
345 Collect all the output first and then show it in sorted order.
347 Possible I<<fieldE<gt>> names are:
353 Sort by number of loose objects (requires B<--loose> option too)
357 Sort by total number of objects in packs (requires B<--objects> option too)
361 Sort by total number of packs (requires possibly implied B<--packs> option)
365 Sort by total objects counted (requires B<--objects> and/or B<--loose>)
369 Sort by total number of packs (requires possibly implied B<--packs> option)
373 Keep the default sort field but sort in ascending order rather than descending
374 order. When prefixed to one of the other sort field names, sort on that field
375 but in ascending rather than descending order.
379 If the B<--sorted=[+]<field>> option is given more than once then later options
380 override earlier ones (there is no multi-field sorting). This can be used to
381 change the sorting order used with B<--full>.
383 If B<--sorted> is used (or B<--sorted=+>) the sort field is chosen based on
384 what information is being collected. If packs are being counted the sort will
385 be on the number of packs (B<--sorted=packs>).
387 If packs are not being counted the sort will be on the total number of objects
388 in each project being either objects in packs, loose objects or the sum of
389 both objects in packs and loose objects depending on what options have been
390 given (B<--sorted=objects>).
394 Instead of counting packs, count objects in them. Implies B<--no-packs>
395 unless an explicit B<--packs> is given too.
397 =item B<--no-objects>
399 Omit the count of objects in packs. Included for completeness. Reactivates
400 implicit B<--packs> if given.
404 Count the number of packs present. This is implied by default but may be
405 given explicitly to always include the packs count.
409 Omit the count of packs. At least one other counting option must be specified
410 with this (either B<--objects> or B<--loose>).
418 Do not count loose objects.
422 A shortcut meta option that behaves the same as replacing it with B<--packs>
423 B<--objects> B<--loose> B<--sorted=objects> at that point.
425 =item I<<projnameE<gt>>
427 If no project names are specified then I<all> projects are processed.
429 If one or more project names are specified then only those projects are
430 processed. Specifying non-existent projects produces a warning for them,
431 but the rest of the projects specified will still be processed.
433 Each B<projname> may be either a full absolute path starting with
434 $Girocco::Config::reporoot or just the project name part with or without
437 Any explicitly specified projects that do exist but are not known to
438 Girocco will be skipped (with a warning).
444 Count the number of packs, objects contained in packs and/or loose objects
445 and show the results.
447 Output can be sorted and the projects inspected can be limited.