Blog: Compressing Subversion checkouts: svn_compress.pl

File svn_compress.pl, 5.0 KB (added by RevRagnarok, 16 years ago)

SVN Checkout Compressor (22 Aug 08)

 
#!/usr/bin/perl
# [email protected]
# "Disk space is cheap!" - SVN Authors
# "Except when backing it up!" - RevRagnarok
# This script will drastically reduce the size of an svn checkout so you can back up the folder.
# It will include a script to re-expand the directory with NO interaction with the server.
# It will also (optionally) write a script /tmp/svn_uncompress.sh that will restore all compressed
# folders.
# TODO:
# * Maybe write a "native" perl version of restore instead of a file...
# To restore busted directories:
# svn st -v | grep \^\! | cut -c41- > revertfiles ; svn revert --targets=revertfiles ; rm revertfiles
# Internal revision: $Id: svn_compress.pl 31 2008-08-21 18:10:37Z admaras $
use strict;
use warnings;
use File::Find;
use File::Spec;
use SVN::Client;
# use Data::Dumper;
use constant c_global_restore_script => 1;
$| = 1; # Always flush STDIO
if (not defined $ARGV[0]) {
print STDERR "Usage: $0 folder1 [folderN]\n";
print STDERR "\t'folder1' is required, remaining are optional.\n\n";
}
foreach my $dir (@ARGV) {
# Strip trailing...
$dir =~ s|/$||;
# Convert any / to -
my $dir2 = $dir;
$dir2 =~ s|/|-|g;
# Check if subversion
if (! -d ${dir}."/.svn") {
print STDERR "Skipping $dir - not a subversion checkout folder!\n";
next;
}
# Check if already done
if (-f ${dir}."/.svn_compress_was_here") {
print STDERR "Skipping $dir - already compressed and not re-expanded!\n";
next;
}
my @erased_restore = ();
print STDERR "\nReducing size of checkout folder '$dir':\n";
my $curdir = File::Spec->rel2abs(File::Spec->curdir());
my @orig_size = &du($dir);
chdir $dir;
# Now find any modified files
# Not sure how moving around directories behind the back of SVN would matter, so new object each directory cannot hurt
my $svnclient = SVN::Client->new(); # No auths or anything, because we only do local stuff
# This is the filter:
my $file_filter = sub {
my( $path, $status ) = @_;
my $entry = $status->entry;
return if !defined $entry;
return if $entry->kind != $SVN::Node::file; # skip non-files
# print "Current status of $path is ", $status->text_status, "\n";
if ($status->text_status != $SVN::Wc::Status::normal) { # only want "boring" entries
return unless $status->text_status == $SVN::Wc::Status::modified; # Only notify on modified (won't report missing, etc)
print STDERR "Keeping file: $path\n"; # Give out some warm and fuzzies about what is not being erased
} else {
push @erased_restore, $path if (unlink $path)
}
};
# Request file list from the client (and let IT handle all recursions, etc)
# $ctx->status($path, $revision, \&status_func, $recursive, $get_all, $update, $no_ignore, $pool);
$svnclient->status( '', 'WORKING', $file_filter, 1, 1, 0, 0); # '' = '.',
# (At this point, the status() callbacks above have already erased the files...)
# Marking our territory:
open NOTICE, "> .svn_compress_was_here";
close NOTICE;
if (! -f '!!README.TXT') { # Don't stomp an existing file with this name
open NOTICE, "> !!README.TXT";
print NOTICE <<STOP
This directory is a "compressed" subversion checkout directory.
It can be re-expanded by executing "restore_${dir2}.sh".
STOP
;close(NOTICE);
} else { # !!README.TXT already exists
open NOTICE, "> .svn_compress_was_here2"; # The restore script will leave notice file alone.
close NOTICE;
}
chdir $curdir;
my @post_size = &du($dir);
print STDERR "\tAn extra ", int (($orig_size[2]-$post_size[2])/1024), " KB was saved!\n";
my $count = scalar @erased_restore;
if ($count) {
print STDERR "\nCreating restore script for $count files:\n";
open(REVERTS, "> ${dir}/restore_list_${dir2}.txt");
print REVERTS join "\n", @erased_restore;
close(REVERTS);
open(REVERTS, "> restore_${dir2}.sh");
print REVERTS <<STOP
#!/bin/sh
rm \$0
cd ${dir}
echo Restoring $count files to ${dir}... >&2
svn revert -q --targets restore_list_${dir2}.txt
[ -f .svn_compress_was_here2 ] || rm \!\!README.TXT
rm restore_list_${dir2}.txt .svn_compress_was_here*
STOP
;close(REVERTS);
print STDERR "\trestore_${dir2}.sh done.\n";
if (c_global_restore_script) {
open(my $rfh, ">> /tmp/svn_uncompress.sh");
print $rfh "#!/bin/sh\nrm -f \$0\n"; # Doesn't matter if it is in there a bunch of times...
print $rfh "cd ${curdir}\n./restore_${dir2}.sh\n";
chmod (((stat $rfh)[2] & 07777) | 0100, $rfh); # set executable flag
close($rfh);
}
} # reverts
} # foreach dir
sub du ($) {
# This subroutine will return a pretty number, order of magnitude, and absolute number
return (0, 0, 0) unless -d $_[0];
my $size = 0;
find(sub {$size += -s}, $_[0]);
my $loop = 0;
my $origsize = $size;
while ($size > 1024) {
$loop++;
$size /= 1024;
}
return (sprintf('%0.2f', $size), $loop, $origsize);
}