3 # update-all-config.pl - Update all out-of-date config
8 BEGIN {*VERSION = \'2.0'}
16 eval 'require Pod::Text::Termcap; 1;' and
17 @Pod::Usage
::ISA
= (qw( Pod::Text::Termcap ));
18 defined($ENV{PERLDOC
}) && $ENV{PERLDOC
} ne "" or
19 $ENV{PERLDOC
} = "-oterm -oman";
21 use lib
"__BASEDIR__";
29 $shbin = $Girocco::Config
::posix_sh_bin
;
30 defined($shbin) && $shbin ne "" or $shbin = "/bin/sh";
33 exit(&main
(@ARGV)||0);
35 my ($dryrun, $force, $quiet);
38 pod2usage
(-exitval
=> 2);
42 pod2usage
(-verbose
=> 2, -exitval
=> 0);
46 print basename
($0), " version ", $VERSION, "\n";
50 my ($dmode, $dperm, $drwxmode, $fmode, $fmodeoct, $fperm, $wall);
54 $drwxmode='ug+rwx,o+rx';
62 my $htmlcache_owning_group_id;
63 my $ctags_owning_group_id;
66 sub pwarn
{ $progress->warn(@_) }
73 close(DATA
) if fileno(DATA
);
74 Getopt
::Long
::Configure
('bundling');
76 'help|h' => sub {do_help
},
77 'version|V' => sub {do_version
},
78 'dry-run|n' => \
$dryrun,
82 $dryrun and $quiet = 0;
84 -f jailed_file
("/etc/group") or
85 die "Girocco group file not found: " . jailed_file
("/etc/group") . "\n";
87 if (!defined($Girocco::Config
::owning_group
) || $Girocco::Config
::owning_group
eq "") {
88 die "\$Girocco::Config::owning_group unset, refusing to run without --force\n" unless $force;
96 warn "Mode 666 in effect\n" unless $quiet;
97 } elsif (($owning_group_id = scalar(getgrnam($Girocco::Config
::owning_group
))) !~ /^\d+$/) {
98 die "\$Girocco::Config::owning_group invalid ($Girocco::Config::owning_group), refusing to run\n";
100 if (defined($owning_group_id) && $Girocco::Config
::htmlcache_owning_group
) {
101 die "\$Girocco::Config::htmlcache_owning_group invalid ($Girocco::Config::htmlcache_owning_group), refusing to run\n"
102 unless ($htmlcache_owning_group_id = scalar(getgrnam($Girocco::Config
::htmlcache_owning_group
))) =~ /^\d+$/;
104 if (defined($owning_group_id) && $Girocco::Config
::ctags_owning_group
) {
105 die "\$Girocco::Config::ctags_owning_group invalid ($Girocco::Config::ctags_owning_group), refusing to run\n"
106 unless ($ctags_owning_group_id = scalar(getgrnam($Girocco::Config
::ctags_owning_group
))) =~ /^\d+$/;
109 my @allprojs = Girocco
::Project
::get_full_list
;
112 my $root = $Girocco::Config
::reporoot
;
113 $root or die "\$Girocco::Config::reporoot is invalid\n";
115 $root ne "" or $root = "/";
116 $root = realpath
($root);
117 $root = $1 if $root =~ m
|^(/.+)$|;
119 my %projnames = map {($_ => 1)} @allprojs;
123 -d
$_ and $_ = realpath
($_);
124 $_ = $1 if $_ =~ m
|^(.+)$|;
127 if (!exists($projnames{$_})) {
128 warn "$_: unknown to Girocco (not in etc/group)\n"
135 @projects = sort {lc($a) cmp lc($b) || $a cmp $b} @allprojs;
140 $progress = Girocco
::CLIUtil
::Progress
->new(scalar(@projects),
141 "Updating config files");
142 foreach (@projects) {
143 my $projdir = "$root/$_.git";
145 pwarn
"$_: does not exist -- skipping\n" unless $quiet;
148 if (!is_git_dir
($projdir)) {
149 pwarn
"$_: is not a .git directory -- skipping\n" unless $quiet;
152 if (-e
"$projdir/.noconfig") {
153 pwarn
"$_: found .noconfig -- skipping\n" unless $quiet;
156 if (!chdir($projdir)) {
157 pwarn
"$_: chdir to project directory failed: $!\n" unless $quiet;
160 process_one_project
($_) or $bad = 1;
161 } continue {$progress->update}
167 my (@mkdirs, @mkfiles);
168 my (@fixdpermsdirs, @fixdpermsrwx, @fixfpermsfiles, @fixfpermsdirs);
170 @mkdirs = qw(refs info hooks ctags htmlcache bundles reflogs objects objects/info);
171 @mkfiles = qw(config info/lastactivity);
172 @fixdpermsdirs = qw(. refs info ctags htmlcache bundles reflogs objects objects/info);
173 @fixdpermsrwx = qw(refs objects);
174 @fixfpermsfiles = qw(HEAD config description packed-refs README.html info/lastactivity
175 info/alternates info/http-alternates info/packs);
176 @fixfpermsdirs = qw(ctags);
179 my (@boolvars, @falsevars, @false0vars, @truevars);
181 @boolvars = qw(gitweb.statusupdates);
182 @falsevars = qw(core.ignorecase receive.denynonfastforwards);
183 @false0vars = qw(receive.autogc);
184 @truevars = qw(receive.fsckobjects receive.updateserverinfo repack.writebitmaps transfer.fsckobjects);
190 return defined($_[0]) ?
$_[0] : $_[1];
197 open $duperr, '>&2' or last;
198 my $errfd = POSIX
::open(File
::Spec
->devnull, &POSIX
::O_RDWR
);
199 defined($errfd) or close($duperr), $duperr = undef, last;
200 POSIX
::dup2
($errfd, 2) or close($duperr), $duperr = undef;
201 POSIX
::close($errfd);
204 my $ans = open $fd, '-|', "find", @_;
205 if ($noe && defined($duperr) && defined(fileno($duperr))) {
206 POSIX
::dup2
(fileno($duperr), 2);
209 $ans or die "\nfind failed: $!\n";
213 sub openfind
{ return openfind_
(0, @_); }
214 sub openfindne
{ return openfind_
(1, @_); }
220 my ($i,$r) = (index($_,"."),rindex($_,"."));
221 substr($_,$i+1,$r-$i-1);
222 } grep(/^remote\.[^.].*\.url$/i, keys(%$config)));
225 sub has_default_fetch_spec
228 my $default = $config->{'remotes.default'};
229 my @remotes = defined($default) ?
split(' ', $default) : all_remotes
($config);
231 defval
($config->{"remote.$_.url"},"") ne "" or next;
232 !defined($default) && git_bool
($config->{"remote.$_.skipdefaultupdate"}) and next;
233 defval
($config->{"remote.$_.fetch"},"") ne "" and return 1;
238 sub is_native_git_mirror_url
241 defined($bu) && $bu ne "" or return 0;
242 # All current or former natively supported by Git URLs return true:
243 # 1. rsync: (removed in 2.8.0, also recognize rsync+ and rsync::)
244 # 2. ftp:/ftps: (strongly discouraged)
246 # 4. http:/https: (smart and non-smart)
248 # 6. scp-like ssh syntax [user@]host:[^:/]
255 (?
:[^\s
:\@
]+\@
)?
[^\s
:\@
+]+:(?
!\
/\/)[^\s
:\\]
262 return lc($v) =~ /^(?:false|true|umask|group|all|world|everybody|0|1|2|0[0-7]{1,3})$/
267 local $_ = lc(shift);
268 /^(?:false|umask|00*)$/ and return 0;
269 /^(?:true|group|1|00*1)$/ and return 1;
270 /^(?:all|world|everybody|2|00*2)$/ and return 2;
271 /^0[0-7]*$/ and return oct($_);
275 sub process_one_project
284 pwarn
"$proj: bypassing project, exists but not directory: $_\n" unless $quiet;
285 $reallybad = $bad = 1;
288 my $grpid = $owning_group_id;
289 $grpid = $htmlcache_owning_group_id
290 if $htmlcache_owning_group_id && $_ eq "htmlcache";
291 $grpid = $ctags_owning_group_id
292 if $ctags_owning_group_id && $_ eq "ctags";
293 do_mkdir
($proj, $_, $grpid) or $bad = 1, last;
297 return 0 if $reallybad;
299 -d
$_ && check_dperm
($proj, $_) or $bad = 1 foreach (@fixdpermsdirs);
300 my $fp = openfindne
(@fixdpermsrwx, qw(-xdev -type d ( ! -path objects/?? -o -prune ) ! -perm
), "-$drwxmode", "-print");
303 change_dpermrwx
($proj, $_) or $bad = 1;
305 close($fp) or $bad = 1;
306 $fp = openfind
(qw(. -xdev -type d ( ! -path ./objects/?? -o -prune ) ! -perm
-a
+rx
-print));
309 change_dpermrx
($proj, $_) or $bad = 1;
311 close($fp) or $bad = 1;
316 pwarn
"$proj: bypassing project, exists but not file: $_\n" unless $quiet;
317 $reallybad = $bad = 1;
321 my $result = "(dryrun)";
325 open($tf, '>', $_) && close ($tf) or $result = "FAILED", $bad = 1;
327 pmsg
($proj, "$_: created", $result) unless $quiet;
330 return 0 if $reallybad;
332 $dryrun || check_fperm
($proj, "config") or $bad = 1;
333 my $config = read_config_file_hash
("config", !$quiet);
334 if (!defined($config)) {
335 pwarn
"$proj: could not read config file -- skipping\n" unless $quiet;
339 my $do_config = sub {
340 my ($item, $val) = @_;
341 my $oldval = defval
($config->{$item},"");
342 my $result = "(dryrun)";
345 system($Girocco::Config
::git_bin
, "config", "--file", "config", "--replace-all", $item, $val) == 0 or
346 $result = "FAILED", $bad = 1;
348 if (!exists($config->{$item})) {
349 pmsg
($proj, "config $item: created \"$val\"", $result) unless $quiet;
351 pmsg
($proj, "config $item: \"$oldval\" -> \"$val\"", $result) unless $quiet;
354 my $do_config_unset = sub {
355 my ($item, $msg) = @_;
356 defined($msg) or $msg = "";
357 $msg eq "" or $msg = " " . $msg;
358 my $oldval = defval
($config->{$item},"");
359 my $result = "(dryrun)";
362 system($Girocco::Config
::git_bin
, "config", "--file", "config", "--unset-all", $item) == 0 or
363 $result = "FAILED", $bad = 1;
365 pmsg
($proj, "config $item: removed$msg \"$oldval\"", $result) unless $quiet;
368 my $repovers = $config->{'core.repositoryformatversion'};
369 if (!defined($repovers)) {
371 } elsif ($repovers =~ /^[2345]$/) {
372 pmsg
($proj, "WARNING: unknown core.repositoryformatversion value left unchanged: \"$repovers\"")
374 } elsif ($repovers !~ /^[01]$/) {
375 pmsg
($proj, "WARNING: replacing invalid core.repositoryformatversion value: \"$repovers\"")
379 &$do_config('core.repositoryformatversion', 0) if $repovers eq "";
380 my $hookspath = $Girocco::Config
::reporoot
. "/_global/hooks";
381 my $cfghooks = defval
($config->{'core.hookspath'},"");
382 if ($cfghooks ne $hookspath) {
383 my $updatehookspath = 1;
384 $hookspath = $Girocco::Config
::reporoot
. "/$proj.git/hooks" if $Girocco::Config
::localhooks
;
385 if ($cfghooks =~ m{^/[^/]} && -d
$cfghooks && -d
"hooks") {
386 # tolerate this situation provided the realpath of $cfghooks
387 # matches the realpath of the hooks subdirectory and the hooks
388 # subdirectory exists; actually making sure the correct symlinks
389 # are present remains up to update-all-hooks not us
390 if (realpath
($cfghooks) eq realpath
("hooks")) {
391 # we do, however, insist that it be stored exactly
392 # as $reporoot/<project_name>.git/hooks in this case because
393 # that's the only guaranteed version that works in the chroot
394 $hookspath = $Girocco::Config
::reporoot
. "/$proj.git/hooks";
395 $cfghooks eq $hookspath and $updatehookspath = 0;
398 &$do_config('core.hookspath', $hookspath) if $updatehookspath;
400 my $cmplvl = defval
($config->{'core.compression'},"");
401 if ($cmplvl !~ /^-?\d+$/ || $cmplvl < -1 || $cmplvl > 9 || "" . (0 + $cmplvl) ne "" . $cmplvl) {
402 pmsg
($proj, "WARNING: replacing invalid core.compression value: \"$cmplvl\"")
403 unless $cmplvl eq "" || $quiet;
405 } elsif ($cmplvl != 5) {
406 pmsg
($proj, "WARNING: suboptimal core.compression value left unchanged: \"$cmplvl\"") unless $quiet;
408 $cmplvl ne "" or &$do_config('core.compression', 5);
409 my $grpshr = defval
($config->{'core.sharedrepository'},"");
410 if ($grpshr eq "" || (git_valid_shared
($grpshr) &&
411 git_get_shared
($grpshr) != $Girocco::Config
::git_shared_repository_setting
)) {
412 &$do_config('core.sharedrepository', $Girocco::Config
::git_shared_repository_setting
);
413 } elsif (!git_valid_shared
($grpshr)) {
414 pmsg
($proj, "WARNING: odd core.sharedrepository value left unchanged: \"$grpshr\"") unless $quiet;
416 if (git_bool
($config->{'core.bare'})) {
418 my $laru = $config->{'core.logallrefupdates'};
419 if (defined($laru)) {
420 if (valid_bool
($laru)) {
422 if (git_bool
($laru)) {
423 pmsg
($proj, "WARNING: core.logallrefupdates is true (left unchanged)")
424 unless $quiet || -d
"worktrees";
427 pmsg
($proj, "WARNING: replacing non-boolean core.logallrefupdates value") unless $quiet;
430 !$setlaru or &$do_config('core.logallrefupdates', 'false');
432 pmsg
($proj, "WARNING: core.bare is not true (left unchanged)") unless $quiet;
434 my $precious = defval
($config->{'extensions.preciousobjects'},"");
435 valid_bool
($precious) && git_bool
($precious) or &$do_config('extensions.preciousobjects', 'true');
436 defval
($config->{'transfer.unpacklimit'},"") eq "1" or &$do_config('transfer.unpacklimit', 1);
437 lc(defval
($config->{'receive.denydeletecurrent'},"")) eq "warn" or &$do_config('receive.denydeletecurrent', 'warn');
439 !exists($config->{$_}) || valid_bool
(defval
($config->{$_},"")) or &$do_config_unset($_, "(not a boolean)");
440 } foreach (@boolvars);
442 (valid_bool
(defval
($config->{$_},"")) && !git_bool
($config->{$_})) or &$do_config($_, "false");
443 } foreach (@falsevars);
445 (valid_bool
(defval
($config->{$_},"")) && !git_bool
($config->{$_})) or &$do_config($_, 0);
446 } foreach (@false0vars);
448 (valid_bool
(defval
($config->{$_},"")) && git_bool
($config->{$_})) or &$do_config($_, "true");
449 } foreach (@truevars);
451 if (defined($Girocco::Config
::owning_group
) && $Girocco::Config
::owning_group
ne "") {
452 $fp = openfind
(qw(. -xdev ( -type d -o -type f ) ! -group
), $Girocco::Config
::owning_group
, "-print");
455 my $grpid = $owning_group_id;
456 $grpid = $htmlcache_owning_group_id if $htmlcache_owning_group_id && m{^\./htmlcache(?:/|$)}i;
457 $grpid = $ctags_owning_group_id if $ctags_owning_group_id && m{^\./ctags(?:/|$)}i;
458 change_group
($proj, $_, $grpid) or $bad = 1;
460 close($fp) or $bad = 1;
462 foreach (@fixfpermsfiles) {
465 pwarn
"$proj: bypassing project, exists but not file: $_\n" unless $quiet;
466 $reallybad = $bad = 1;
469 check_fperm
($proj, $_) or $bad = 1;
472 return 0 if $reallybad;
474 $fp = openfindne
(@fixfpermsdirs, qw(-xdev -type f ! -perm), $fmodeoct, "-print");
477 check_fperm
($proj, $_) or $bad = 1;
479 close($fp) or $bad = 1;
480 $fp = openfind
(qw(. -xdev -type f ! -perm -a+r -print));
483 check_fpermr
($proj, $_) or $bad = 1;
485 close($fp) or $bad = 1;
486 $fp = openfind
(qw(. -xdev -type d ( -path ./hooks -o -path ./mob/hooks ) -prune
-o
-type f
( -perm
-u
=x
-o
-perm
-g
=x
-o
-perm
-o
=x
) -print));
489 check_fpermnox
($proj, $_) or $bad = 1;
491 close($fp) or $bad = 1;
493 my $bu = defval
($config->{'gitweb.baseurl'},"");
495 $bu eq "" or pmsg
($proj, "WARNING: .nofetch exists but gitweb.baseurl is not empty ($bu)") unless $quiet;
498 if (has_default_fetch_spec
($config)) {
499 pmsg
($proj, "WARNING: gitweb.baseurl is empty and .nofetch does not exist but fetch spec does") unless $quiet;
501 pmsg
($proj, "WARNING: gitweb.baseurl is empty and .nofetch does not exist") unless $quiet;
503 } elsif (is_native_git_mirror_url
($bu) && !has_default_fetch_spec
($config)) {
504 pmsg
($proj, "WARNING: gitweb.baseurl is not empty but fetch spec is") unless $quiet;
513 my ($proj, $subdir, $grpid) = @_;
516 mkdir($subdir) && -d
"$subdir" or $result = "FAILED";
517 if ($grpid && $grpid != $owning_group_id) {
518 my @info = stat($subdir);
519 if (@info < 6 || $info[2] eq "" || $info[4] eq "" || $info[5] eq "") {
521 } elsif ($info[5] != $grpid) {
522 if (!chown($info[4], $grpid, $subdir)) {
524 pwarn
"chgrp: ($proj) $subdir: $!\n" unless $quiet;
525 } elsif (!chmod($info[2] & 07777, $subdir)) {
527 pwarn
"chmod: ($proj) $subdir: $!\n" unless $quiet;
532 $result = "(dryrun)";
534 pmsg
($proj, "$subdir/: created", $result) unless $quiet;
535 return $result ne "FAILED";
539 my ($proj, $subdir) = @_;
540 my $oldmode = (stat($subdir))[2];
541 if (!defined($oldmode) || $oldmode eq "") {
542 pwarn
"chmod: ($proj) $subdir: No such file or directory\n" unless $quiet;
545 my $newmode = ($oldmode & ~07777) | $dmode;
546 $newmode == $oldmode and return 1;
549 if (!chmod($newmode & 07777, $subdir)) {
551 pwarn
"chmod: ($proj) $subdir: $!\n" unless $quiet;
554 $result = "(dryrun)";
556 pmsg
($proj, "$subdir/:", get_mode_perm
($oldmode), '->', get_mode_perm
($newmode), $result) unless $quiet;
557 return $result ne "FAILED";
560 sub change_dpermrwx
{
561 my ($proj, $subdir) = @_;
562 my $oldmode = (stat($subdir))[2];
563 if (!defined($oldmode) || $oldmode eq "") {
564 pwarn
"chmod: ($proj) $subdir: No such file or directory\n" unless $quiet;
567 my $newmode = $oldmode | ($wall ?
0777 : 0775);
568 $newmode == $oldmode and return 1;
571 if (!chmod($newmode & 07777, $subdir)) {
573 pwarn
"chmod: ($proj) $subdir: $!\n" unless $quiet;
576 $result = "(dryrun)";
578 pmsg
($proj, "$subdir/:", get_mode_perm
($oldmode), '->', get_mode_perm
($newmode), $result) unless $quiet;
579 return $result ne "FAILED";
583 my ($proj, $subdir) = @_;
585 my $oldmode = (stat($subdir))[2];
586 if (!defined($oldmode) || $oldmode eq "") {
587 pwarn
"chmod: ($proj) $subdir: No such file or directory\n" unless $quiet;
590 my $newmode = $oldmode | 0555;
591 $newmode == $oldmode and return 1;
594 if (!chmod($newmode & 07777, $subdir)) {
596 pwarn
"chmod: ($proj) $subdir: $!\n" unless $quiet;
599 $result = "(dryrun)";
601 pmsg
($proj, "$subdir/:", get_mode_perm
($oldmode), '->', get_mode_perm
($newmode), $result) unless $quiet;
602 return $result ne "FAILED";
606 my ($proj, $file) = @_;
607 my $oldmode = (stat($file))[2];
608 if (!defined($oldmode) || $oldmode eq "") {
609 pwarn
"chmod: ($proj) $file: No such file or directory\n" unless $quiet;
612 my $newmode = ($oldmode & ~07777) | $fmode;
613 $newmode == $oldmode and return 1;
616 if (!chmod($newmode & 07777, $file)) {
618 pwarn
"chmod: ($proj) $file: $!\n" unless $quiet;
621 $result = "(dryrun)";
623 pmsg
($proj, "$file:", get_mode_perm
($oldmode), '->', get_mode_perm
($newmode), $result) unless $quiet;
624 return $result ne "FAILED";
628 my ($proj, $file) = @_;
630 my $oldmode = (stat($file))[2];
631 if (!defined($oldmode) || $oldmode eq "") {
632 pwarn
"chmod: ($proj) $file: No such file or directory\n" unless $quiet;
635 my $newmode = $oldmode | 0444;
636 $newmode == $oldmode and return 1;
639 if (!chmod($newmode & 07777, $file)) {
641 pwarn
"chmod: ($proj) $file: $!\n" unless $quiet;
644 $result = "(dryrun)";
646 pmsg
($proj, "$file:", get_mode_perm
($oldmode), '->', get_mode_perm
($newmode), $result) unless $quiet;
647 return $result ne "FAILED";
651 my ($proj, $file) = @_;
653 my $oldmode = (stat($file))[2];
654 if (!defined($oldmode) || $oldmode eq "") {
655 pwarn
"chmod: ($proj) $file: No such file or directory\n" unless $quiet;
658 my $newmode = $oldmode & ~0111;
659 $newmode == $oldmode and return 1;
662 if (!chmod($newmode & 07777, $file)) {
664 pwarn
"chmod: ($proj) $file: $!\n" unless $quiet;
667 $result = "(dryrun)";
669 pmsg
($proj, "$file:", get_mode_perm
($oldmode), '->', get_mode_perm
($newmode), $result) unless $quiet;
670 return $result ne "FAILED";
674 my ($proj, $item, $grpid) = @_;
676 my @info = stat($item);
677 if (@info < 6 || $info[2] eq "" || $info[4] eq "" || $info[5] eq "") {
678 pwarn
"chgrp: ($proj) $item: No such file or directory\n" unless $quiet;
681 $info[5] == $grpid and return 1;
684 if (!chown($info[4], $grpid, $item)) {
686 pwarn
"chgrp: ($proj) $item: $!\n" unless $quiet;
687 } elsif (!chmod($info[2] & 07777, $item)) {
689 pwarn
"chmod: ($proj) $item: $!\n" unless $quiet;
692 $result = "(dryrun)";
694 my $isdir = ((($info[2] >> 12) & 017) == 004) ?
'/' : '';
695 pmsg
($proj, "$item$isdir: group", get_grp_nam
($info[5]), '->', get_grp_nam
($grpid), $result) unless $quiet;
696 return $result ne "FAILED";
699 my $wrote; BEGIN {$wrote = ""}
702 my $msg = join(" ", @_);
706 $prefix = $wrote . $proj . ":\n";
709 $progress->emit($prefix . " " . join(' ', @_) . "\n");
746 my $str = $ftypes{($mode >> 12) & 017} .
747 $fperms{($mode >> 6) & 7} .
748 $fperms{($mode >> 3) & 7} .
750 substr($str,3,1) = ($mode & 0100) ?
's' : 'S' if $mode & 04000;
751 substr($str,6,1) = ($mode & 0010) ?
's' : 'S' if $mode & 02000;
752 substr($str,9,1) = ($mode & 0001) ?
't' : 'T' if $mode & 01000;
757 my $mode = (stat($_[0]))[2];
758 defined($mode) or return '??????????';
759 return get_mode_perm
($mode);
764 defined($grpid) or return '?';
765 my $grpnm = scalar(getgrgid($grpid));
766 return defined($grpnm) && $grpnm ne "" ?
$grpnm : $grpid;
770 my $grp = (stat($_[0]))[5];
771 defined($grp) or return '?';
772 return get_grp_nam
($grp);
779 update-all-config.pl - Update all projects' config settings
783 update-all-config.pl [<options>] [<projname>]...
786 -h | --help detailed instructions
787 -V | --version show version
788 -n | --dry-run show what would be done but don't do it
789 -f | --force run without a Config.pm owning_group
790 -q | --quiet suppress change messages
792 <projname> if given, only operate on these projects
798 =item B<-h>, B<--help>
800 Print the full description of update-all-config.pl's options.
802 =item B<-V>, B<--version>
804 Print the version of update-all-config.pl.
806 =item B<-n>, B<--dry-run>
808 Do not actually make any changes, just show what would be done without
811 =item B<-q>, B<--quiet>
813 Suppress the messages about what's actually being changed. This option
814 is ignored if B<--dry-run> is in effect.
816 The warnings about missing and unknown-to-Girocco projects are also
817 suppressed by this option.
819 =item B<-f>, B<--force>
821 Allow running without a $Girocco::Config::owning_group set. This is not
822 recommended as it results in world-writable items being used (instead of
823 just world-readable).
827 If no project names are specified then I<all> projects are processed.
829 If one or more project names are specified then only those projects are
830 processed. Specifying non-existent projects produces a warning for them,
831 but the rest of the projects specified will still be processed.
833 Each B<projname> may be either a full absolute path starting with
834 $Girocco::Config::reporoot or just the project name part with or without
837 Any explicitly specified projects that do exist but are not known to
838 Girocco will be skipped (with a warning).
844 Inspect the C<config> files of Girocco projects (i.e. $GIT_DIR/config) and
845 look for anomalies and out-of-date settings.
847 Additionally check the existence and permissions on various files and
848 directories in the project.
850 If an explicity specified project is located under $Girocco::Config::reporoot
851 but is not actually known to Girocco (i.e. it's not in the etc/group file)
852 then it will be skipped.
854 By default, any anomalies or out-of-date settings will be corrected with a
855 message to that effect. However using B<--dry-run> will only show the
856 correction(s) which would be made without making them and B<--quiet> will make
857 the correction(s) without any messages.
859 Any projects that have a C<$GIT_DIR/.noconfig> file are always skipped (with a
860 message unless B<--quiet> is used).