Skip to content

Commit

Permalink
'rsync' command to create and send bundles (manual smoke test only)
Browse files Browse the repository at this point in the history
run 'ssh git@host rsync -h' for usage, as usual
  • Loading branch information
sitaramc committed Jul 19, 2012
1 parent 8ad1eee commit f4eb6dc
Showing 1 changed file with 149 additions and 0 deletions.
149 changes: 149 additions & 0 deletions src/commands/rsync
@@ -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(@_);
}

0 comments on commit f4eb6dc

Please sign in to comment.