'rsync' command to create and send bundles (manual smoke test only)
run 'ssh git@host rsync -h' for usage, as usual
This commit is contained in:
parent
8ad1eee220
commit
f4eb6dcb53
149
src/commands/rsync
Executable file
149
src/commands/rsync
Executable file
|
@ -0,0 +1,149 @@
|
||||||
|
#!/usr/bin/perl
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
use lib $ENV{GL_LIBDIR};
|
||||||
|
use Gitolite::Easy;
|
||||||
|
|
||||||
|
=for admins
|
||||||
|
|
||||||
|
BUNDLE SUPPORT
|
||||||
|
|
||||||
|
(1) For each repo in gitolite.conf for which you want bundle support (or
|
||||||
|
'@all', if you wish), add the following line:
|
||||||
|
|
||||||
|
option bundle = 1
|
||||||
|
|
||||||
|
Or you can say:
|
||||||
|
|
||||||
|
option bundle.ttl = <number>
|
||||||
|
|
||||||
|
A bundle file that is more than <number> seconds old (default value
|
||||||
|
86400, i.e., 1 day) is recreated on the next bundle request. Increase
|
||||||
|
this if your repo is not terribly active.
|
||||||
|
|
||||||
|
Note: a bundle file is also deleted and recreated if it contains a ref
|
||||||
|
that was then either deleted or rewound in the repo. This is checked
|
||||||
|
on every invocation.
|
||||||
|
|
||||||
|
(2) Add 'rsync' to the COMMANDS list in the rc file
|
||||||
|
|
||||||
|
|
||||||
|
GENERIC RSYNC SUPPORT
|
||||||
|
|
||||||
|
TBD
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
=for usage
|
||||||
|
rsync helper for gitolite
|
||||||
|
|
||||||
|
BUNDLE SUPPORT
|
||||||
|
|
||||||
|
Admins: see src/commands/rsync for setup instructions
|
||||||
|
|
||||||
|
Users:
|
||||||
|
rsync -P git@host:repo.bundle .
|
||||||
|
# downloads a file called "<basename of repo>.bundle"; repeat as
|
||||||
|
# needed till the whole thing is downloaded
|
||||||
|
git clone repo.bundle repo
|
||||||
|
cd repo
|
||||||
|
git remote set-url origin git@host:repo
|
||||||
|
git fetch origin # and maybe git pull, etc. to freshen the clone
|
||||||
|
|
||||||
|
GENERIC RSYNC SUPPORT
|
||||||
|
|
||||||
|
TBD
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
usage() if not @ARGV or $ARGV[0] eq '-h';
|
||||||
|
|
||||||
|
# rsync driver program. Several things can be done later, but for now it
|
||||||
|
# drives just the 'bundle' transfer.
|
||||||
|
|
||||||
|
if ( $ENV{SSH_ORIGINAL_COMMAND} =~ /^rsync --server --sender (-[-\w=.]+ )+\. (\S+)\.bundle$/ ) {
|
||||||
|
|
||||||
|
my $repo = $2;
|
||||||
|
$repo =~ s/\.git$//;
|
||||||
|
|
||||||
|
# all errors have the same message to avoid leaking info
|
||||||
|
can_read($repo) or _die "you are not authorised";
|
||||||
|
my %config = config( $repo, "gitolite-options.bundle" ) or _die "you are not authorised";
|
||||||
|
|
||||||
|
my $ttl = $config{'gitolite-options.bundle.ttl'} || 86400; # in seconds (default 1 day)
|
||||||
|
|
||||||
|
my $bundle = bundle_create( $repo, $ttl );
|
||||||
|
|
||||||
|
$ENV{SSH_ORIGINAL_COMMAND} =~ s( \S+\.bundle)( $bundle);
|
||||||
|
trace( 1, "rsync bundle", $ENV{SSH_ORIGINAL_COMMAND} );
|
||||||
|
Gitolite::Common::_system( split ' ', $ENV{SSH_ORIGINAL_COMMAND} );
|
||||||
|
exit 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_warn "invalid rsync command '$ENV{SSH_ORIGINAL_COMMAND}'";
|
||||||
|
usage();
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# helpers
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
sub bundle_create {
|
||||||
|
my ( $repo, $ttl ) = @_;
|
||||||
|
my $bundle = "$repo.bundle";
|
||||||
|
$bundle =~ s(.*/)();
|
||||||
|
my $recreate = 0;
|
||||||
|
|
||||||
|
my ( %b, %r );
|
||||||
|
if ( -f $bundle ) {
|
||||||
|
%b = map { chomp; reverse split; } `git ls-remote --heads --tags $bundle`;
|
||||||
|
%r = map { chomp; reverse split; } `git ls-remote --heads --tags .`;
|
||||||
|
|
||||||
|
for my $ref ( sort keys %b ) {
|
||||||
|
|
||||||
|
my $mtime = ( stat $bundle )[9];
|
||||||
|
if ( time() - $mtime > $ttl ) {
|
||||||
|
trace( 1, "bundle too old" );
|
||||||
|
$recreate++;
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( not $r{$ref} ) {
|
||||||
|
trace( 1, "ref '$ref' deleted in repo" );
|
||||||
|
$recreate++;
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $r{$ref} eq $b{$ref} ) {
|
||||||
|
# same on both sides; ignore
|
||||||
|
delete $r{$ref};
|
||||||
|
delete $b{$ref};
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
`git rev-list --count --left-right $b{$ref}...$r{$ref}` =~ /^(\d+)\s+(\d+)$/ or _die "git too old";
|
||||||
|
if ($1) {
|
||||||
|
trace( 1, "ref '$ref' rewound in repo" );
|
||||||
|
$recreate++;
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
trace( 1, "no bundle found" );
|
||||||
|
$recreate++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $bundle if not $recreate;
|
||||||
|
|
||||||
|
trace( 1, "creating bundle for '$repo'" );
|
||||||
|
-f $bundle and ( unlink $bundle or die "a horrible death" );
|
||||||
|
system("git bundle create $bundle --branches --tags >&2");
|
||||||
|
|
||||||
|
return $bundle;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub trace {
|
||||||
|
Gitolite::Common::trace(@_);
|
||||||
|
}
|
Loading…
Reference in a new issue