#!/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);
|
}
|