3 #=======================================================================
5 # File ID: 6c4d0b82-6111-11e0-8250-e509f64f5c24
7 # Push received commits and tags to other remotes to save bandwith
10 # ©opyleft 2011– Øyvind A. Holm <sunny@sunbase.org>
11 # License: GNU General Public License version 2 or later, see end of
12 # file for legal stuff.
13 #=======================================================================
19 use Cwd
qw{ abs_path
};
28 'repodir' => "$ENV{'HOME'}/Git-spread",
44 $progname =~ s/^.*\/(.*?)$/$1/;
45 our $VERSION = '0.11.0+git';
47 Getopt
::Long
::Configure
('bundling');
50 'help|h' => \
$Opt{'help'},
51 'quiet|q+' => \
$Opt{'quiet'},
52 'repodir|r=s' => \
$Opt{'repodir'},
53 'run-once|1' => \
$Opt{'run-once'},
54 'verbose|v+' => \
$Opt{'verbose'},
55 'version' => \
$Opt{'version'},
57 ) || die("$progname: Option error. Use -h for help.\n");
59 $Opt{'verbose'} -= $Opt{'quiet'};
60 $Opt{'help'} && usage
(0);
61 if ($Opt{'version'}) {
66 my $CMD_GIT = defined($ENV{'GITSPREAD_GIT'}) ?
$ENV{'GITSPREAD_GIT'} : 'git';
67 my $repodir = abs_path
($Std{'repodir'});
68 defined($ENV{'GITSPREAD_REPODIR'}) &&
69 length($ENV{'GITSPREAD_REPODIR'}) &&
70 ($repodir = abs_path
($ENV{'GITSPREAD_REPODIR'}));
71 length($Opt{'repodir'}) && ($repodir = abs_path
($Opt{'repodir'}));
73 my $spooldir = abs_path
("$repodir/spool");
74 my $logfile = abs_path
("$repodir/$progname.log");
75 my $pidfile = abs_path
("$repodir/pid");
76 my $stopfile = abs_path
("$repodir/stop");
86 if (! -d
"$repodir/.") {
87 warn("$progname: $repodir: Missing repository top directory\n");
91 if (! -d
"$spooldir/.") {
92 mkdir($spooldir) or die("$progname: $spooldir: Cannot create spool directory: $!\n");
95 open($logfh, '>>', $logfile)
96 or die("$progname: $logfile: Cannot open logfile: $!\n");
98 flock($logfh, LOCK_EX
| LOCK_NB
)
99 or die("$progname: $logfile: Cannot lock file: $!.\n" .
100 "Is $progname already running?\n");
102 my $do_loop = $Opt{'run-once'} ?
0 : 1;
104 defined(my $pid = fork) or die("$progname: Cannot fork: $!");
107 my $start_str = "Starting $progname $VERSION, PID = $pid";
108 print("$start_str\n");
110 if (open(my $pidfh, '>', $pidfile)) {
111 print($pidfh "$pid\n");
117 setsid
or die("$progname: Cannot start a new session: $!\n");
121 logmsg
("$stopfile exists, terminating properly");
122 unlink($pidfile) || logmsg
("WARNING: Cannot remove $pidfile: $!");
123 unlink($stopfile) || logmsg
("WARNING: Cannot remove $stopfile: $!");
126 chdir($spooldir) || (logmsg
("$spooldir: Cannot chdir to spool directory: $!. Aborting."), exit(1));
127 my @repos = glob('*');
129 logmsg
('Found new commits: ' . join(' ', @repos));
130 for my $repo (@repos) {
144 logmsg
("Processing $repo");
145 my $repogit = "$repodir/$repo.git";
146 unless (chdir($repogit)) {
147 logmsg
("$repogit: Cannot chdir: $!. Ignoring repository.");
150 my @deleted_branches = ();
151 if (open(my $fh, '<', "$spooldir/$repo")) {
153 chomp(my $line = $_);
154 if ($line =~ /^[0-9a-f]{40} 0{40} refs\/heads\
/(.*)$/) {
155 my $deleted_branch = $1;
156 push(@deleted_branches, $deleted_branch);
161 logmsg
("$spooldir/$repo: Cannot open file for read: $!. Ignoring repository $repo.");
165 my $force = git_config_value
('gitspread.forcepush');
166 if ($force !~ /^(|false|true)$/) {
167 logmsg
("WARNING: $repo: gitspread.forcepush contains invalid value \"$force\". " .
171 my $force_opt = $force eq 'true' ?
'-f' : '';
172 for my $remote (git_remotes
()) {
173 if (scalar(@deleted_branches)) {
174 for my $branch (@deleted_branches) {
175 exec_command
($CMD_GIT, 'push', $remote, ":$branch");
178 exec_command
($CMD_GIT, 'push', $force_opt, '--all', $remote);
179 exec_command
($CMD_GIT, 'push', $force_opt, '--tags', $remote);
183 unlink("$spooldir/$repo") || logmsg
("WARNING: $spooldir/$repo: Cannot delete file: $!");
191 logmsg
("Executing '" . join(' ', @args) . "'");
192 system(join(' ', @args, ">>$logfile", '2>&1'));
197 sub git_config_value
{
201 if (open(my $pipefh, "$CMD_GIT config --get $option |")) {
206 logmsg
("FATAL: git_config_value(): " .
207 "Cannot open \"$CMD_GIT config --get\" pipe: $!. Aborting.");
213 } # git_config_value()
218 if (open(my $pipefh, "$CMD_GIT remote |")) {
221 length($_) && push(@retval, $_);
224 logmsg
("WARNING: Could not close \$pipefh in git_remotes(): $!");
226 logmsg
("FATAL: git_remotes(): " .
227 "Cannot open \"$CMD_GIT remote\" pipe: $!. Aborting.");
237 my ($sec, $min, $hour, $day, $mon, $year, $wday, $yday, $is_dst) =
239 my $timestamp = sprintf('%04u-%02u-%02u %02u:%02u:%02uZ',
240 $year+1900, $mon+1, $day, $hour, $min, $sec);
241 print($logfh "$timestamp - ", @txt, "\n");
247 # Print program version {{{
248 printf("%s$VERSION\n", $Opt{'verbose'} >= 0 ?
"$progname " : "");
254 # Send the help message to stdout {{{
257 if ($Opt{'verbose'} > 0) {
263 Usage: $progname [options]
266 if ($Opt{'verbose'} >= 0) {
273 Use X as directory to store bare repos.
274 Default: "$Std{'repodir'}"
276 Just pass through the program once instead of looping. Used for
279 Be more quiet. Can be repeated to increase silence.
281 Increase level of verbosity. Can be repeated.
283 Print version information. "Semantic versioning" is used, described
284 at <http://semver.org>.
286 To use an alternative version of git, set the \$GITSPREAD_GIT
287 environment variable to the git executable to use. For example:
289 export GITSPREAD_GIT=/usr/local/bin/git
299 # Print a status message to stderr based on verbosity level {{{
300 my ($verbose_level, $Txt) = @_;
302 if ($Opt{'verbose'} >= $verbose_level) {
303 print(STDERR
"$progname: $Txt\n");
311 # This program is free software: you can redistribute it and/or modify
312 # it under the terms of the GNU General Public License as published by
313 # the Free Software Foundation, either version 2 of the License, or (at
314 # your option) any later version.
316 # This program is distributed in the hope that it will be useful, but
317 # WITHOUT ANY WARRANTY; without even the implied warranty of
318 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
319 # See the GNU General Public License in the file COPYING for more
322 # You should have received a copy of the GNU General Public License
323 # along with this program.
324 # If not, see L<http://www.gnu.org/licenses/gpl-2.0.txt>.
326 # vim: set fenc=UTF-8 ft=perl fdm=marker ts=4 sw=4 sts=4 et fo+=w :