#!/usr/bin/perl ######################################################################## # FINE PRINT: # # Copyright (C) 2004-2005 Eric Wilhelm (ewilhelm at cpan dot org) # # Portions contributed by Deneys Maartens # # This program is copyrighted 2004-2005 by Eric Wilhelm and is # distributed under the terms of the GNU General Public License # (http://www.gnu.org/licenses/gpl.txt) and additional terms described # below. # # This program may be reproduced and distributed in whole or in part, in # any medium physical or electronic, as long as this copyright notice is # retained on all copies. Commercial redistribution is allowed and # encouraged; however, the author would like to be notified of any such # distributions. # # Please contact me with suggestions or improvements. # # BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY # FOR THE PROGRAM. THE PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF # ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR # PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE # PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME # THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. # # END FINE PRINT ######################################################################## # adjust to taste my $svnadmin = "/usr/bin/svnadmin"; my $svnlook = "/usr/bin/svnlook"; use warnings; use strict; sub usage { use File::Basename; my $progname = basename $0; return < [start_revision:]revision $progname [start_revision:]HEAD EOF } my $repo = shift; my $rev = shift; my $dest = shift; my $start; (defined($repo) and defined($rev) and defined($dest) ) or die usage(); foreach my $dir ($repo, $dest) { (-d $dir) or die "'$dir' is not a directory or does not exist!\n", usage(); } ($rev =~ m/:/) and (($start,$rev) = split(/:/, $rev, 2)); if ($rev eq 'HEAD') { my $pid = open(LOOK, "-|"); defined($pid) or die "no fork!"; if($pid) { $rev = ; close(LOOK); chomp($rev); $rev eq '' and die "look failed to find HEAD revision"; } else { exec($svnlook, 'youngest', $repo); } } foreach my $num ($start, $rev) { defined($num) or next; ($num =~ m/^\d+$/) or die "revision: '$num' is not a number!\n", usage(); } if(defined($start)) { ($start <= $rev) or die "start: $start must be <= end: $rev!\n" ,usage(); } else { $start = ''; } my $base = "incdump"; my $justify = "6"; # number of digits in incdump.###-### unless(length($start)) { # auto-determine the last incremental dump. run from then till now: opendir(DEST, $dest); my @files = grep({$_ =~ m/^$base/} readdir(DEST)); closedir(DEST); my $last_rev = -1; if(@files) { my @revs; # find the last of these: foreach my $file (@files) { my $rev = $file; $rev =~ s/^.*-//; push(@revs, $rev); } $last_rev = (sort({$a <=> $b} @revs))[-1]; } ## print "last rev: $last_rev\n"; # also check for full-dumps? if(-e "$dest/check_full") { my $full_dir = $dest; $full_dir =~ s/inc\/*$/full/; # print "looking for $full_dir\n"; if(-d $full_dir) { opendir(FULL, $full_dir); my @had = map({s/^.*\.//; $_} grep({$_ =~ m/^full/} readdir(FULL))); # print "check @had\n"; closedir(FULL); if(@had) { my $last = (sort({$a <=> $b} @had))[-1]; if(defined($last)) { ## print "last was: $last\n"; # full dump acts as last incremental ($last > $last_rev) and ($last_rev = $last); } } } } # leaves us at 0 if there was nothing in the backup dir $start = $last_rev + 1; } # we only have a problem here when trying to auto-detect the start revision ($start <= $rev) or die "backup to end: $rev out-of-sequence with start: $start\n"; my $filename = sprintf("incdump.%0${justify}d-%0${justify}d", $start, $rev); ## print "out to $dest/$filename\n"; my $outfile = $dest . "/" .$filename; (-e $outfile) and die "backup $filename already done.\n"; # build the command: my @run = ( $svnadmin, "dump", $repo, "--incremental", "-r", $start . ":" . $rev, ); my $pid = open(DUMP, "-|"); defined($pid) or die "no fork!"; if($pid) { # parent: open(OUT, ">", $outfile) or die "cannot write to $outfile"; while(my $line = ) { print OUT $line; } close(OUT); close(DUMP); } else { #child my $ret = system(@run); if($ret) { warn "error running dump\n"; unlink($outfile) or warn "sorry, cannot remove dumpfile '$outfile'\n"; die "command returned $ret\n"; } exit; }