new mirror function: 'status all all'
[gitolite.git] / src / commands / rsync
blob1109ac4d3fa64696e6640438d44dde16691103e2
1 #!/usr/bin/perl
2 use strict;
3 use warnings;
5 use lib $ENV{GL_LIBDIR};
6 use Gitolite::Easy;
8 =for admins
10 BUNDLE SUPPORT
12 (1) For each repo in gitolite.conf for which you want bundle support (or
13 '@all', if you wish), add the following line:
15 option bundle = 1
17 Or you can say:
19 option bundle.ttl = <number>
21 A bundle file that is more than <number> seconds old (default value
22 86400, i.e., 1 day) is recreated on the next bundle request. Increase
23 this if your repo is not terribly active.
25 Note: a bundle file is also deleted and recreated if it contains a ref
26 that was then either deleted or rewound in the repo. This is checked
27 on every invocation.
29 (2) Add 'rsync' to the ENABLE list in the rc file
32 GENERIC RSYNC SUPPORT
34 TBD
36 =cut
38 =for usage
39 rsync helper for gitolite
41 BUNDLE SUPPORT
43 Admins: see src/commands/rsync for setup instructions
45 Users:
46 rsync -P git@host:repo.bundle .
47 # downloads a file called "<basename of repo>.bundle"; repeat as
48 # needed till the whole thing is downloaded
49 git clone repo.bundle repo
50 cd repo
51 git remote set-url origin git@host:repo
52 git fetch origin # and maybe git pull, etc. to freshen the clone
54 GENERIC RSYNC SUPPORT
56 TBD
58 =cut
60 usage() if not @ARGV or $ARGV[0] eq '-h';
62 # rsync driver program. Several things can be done later, but for now it
63 # drives just the 'bundle' transfer.
65 if ( $ENV{SSH_ORIGINAL_COMMAND} =~ /^rsync --server --sender (-[-\w=.]+ )+\. (\S+)\.bundle$/ ) {
67 my $repo = $2;
68 $repo =~ s/\.git$//;
70 # all errors have the same message to avoid leaking info
71 can_read($repo) or _die "you are not authorised";
72 my %config = config( $repo, "gitolite-options.bundle" ) or _die "you are not authorised";
74 my $ttl = $config{'gitolite-options.bundle.ttl'} || 86400; # in seconds (default 1 day)
76 my $bundle = bundle_create( $repo, $ttl );
78 $ENV{SSH_ORIGINAL_COMMAND} =~ s( \S+\.bundle)( $bundle);
79 trace( 1, "rsync bundle", $ENV{SSH_ORIGINAL_COMMAND} );
80 Gitolite::Common::_system( split ' ', $ENV{SSH_ORIGINAL_COMMAND} );
81 exit 0;
84 _warn "invalid rsync command '$ENV{SSH_ORIGINAL_COMMAND}'";
85 usage();
87 # ----------------------------------------------------------------------
88 # helpers
89 # ----------------------------------------------------------------------
91 sub bundle_create {
92 my ( $repo, $ttl ) = @_;
93 my $bundle = "$repo.bundle";
94 $bundle =~ s(.*/)();
95 my $recreate = 0;
97 my ( %b, %r );
98 if ( -f $bundle ) {
99 %b = map { chomp; reverse split; } `git ls-remote --heads --tags $bundle`;
100 %r = map { chomp; reverse split; } `git ls-remote --heads --tags .`;
102 for my $ref ( sort keys %b ) {
104 my $mtime = ( stat $bundle )[9];
105 if ( time() - $mtime > $ttl ) {
106 trace( 1, "bundle too old" );
107 $recreate++;
108 last;
111 if ( not $r{$ref} ) {
112 trace( 1, "ref '$ref' deleted in repo" );
113 $recreate++;
114 last;
117 if ( $r{$ref} eq $b{$ref} ) {
118 # same on both sides; ignore
119 delete $r{$ref};
120 delete $b{$ref};
121 next;
124 `git rev-list --count --left-right $b{$ref}...$r{$ref}` =~ /^(\d+)\s+(\d+)$/ or _die "git too old";
125 if ($1) {
126 trace( 1, "ref '$ref' rewound in repo" );
127 $recreate++;
128 last;
133 } else {
134 trace( 1, "no bundle found" );
135 $recreate++;
138 return $bundle if not $recreate;
140 trace( 1, "creating bundle for '$repo'" );
141 -f $bundle and ( unlink $bundle or die "a horrible death" );
142 system("git bundle create $bundle --branches --tags >&2");
144 return $bundle;
147 sub trace {
148 Gitolite::Common::trace(@_);