3
##############################################################################
5
# by David Cantrell <david@cantrell.org.uk>
7
# This program calculates the differences between two directories. It is
8
# designed to work with two different subdirectories under the rsnapshot
9
# snapshot_root. For example:
11
# rsnapshot-diff /.snapshots/daily.0/ /.snapshots/daily.1/
13
# http://www.rsnapshot.org/
14
##############################################################################
16
# $Id: rsnapshot-diff.pl,v 1.3 2006/05/31 19:51:03 drhyde Exp $
20
rsnapshot-diff - a utility for comparing the disk usage of two snapshots
27
use constant DEBUG => 0;
30
my $program_name = 'rsnapshot-diff';
36
my $result = getopts('vVhi', \%opts);
41
$program_name [-vVhi] dir1 dir2
43
$program_name shows the differences between two 'rsnapshot' backups.
47
-V be more verbose (mutter about unchanged files)
48
-i ignore symlinks, directories, and special files in verbose output
49
dir1 the first directory to look at
50
dir2 the second directory to look at
52
if you want to look at directories called '-h' or '-v' pass a
53
first parameter of '--'.
55
$program_name always show the changes made starting from the older
56
of the two directories.
63
rsnapshot-diff [-h|vVi] dir1 dir2
67
rsnapshot-diff is a companion utility for rsnapshot, which traverses two
68
parallel directory structures and calculates the difference between them.
69
By default it is silent apart from displaying summary information at the
70
end, but it can be made more verbose.
72
In the summary, "added" files may very well include files which at first
73
glance also appear at the same place in the older directory structure.
74
However, because the files differ in some respect, they are different files.
75
They have a different inode number. Consequently if you use -v most of its
76
output may appear to be pairs of files with the same name being removed
85
Displays help information
89
Be verbose. This will spit out a list of all changes as they are encountered,
90
as well as the summary at the end.
92
=item -V (more verbose)
94
Be more verbose - as well as listed changes, unchanged files will be listed
99
If verbosity is turned on, -i suppresses information about symlinks,
100
directories, and special files.
104
These are the only compulsory parameters, and should be the names of two
105
directories to compare. Their order doesn't matter, rsnapshot-diff will
106
always compare the younger to the older, so files that appear only in the
107
older will be reported as having been removed, and files that appear only
108
in the younger will be reported as having been added.
115
if ($opts{'v'}) { $verbose = 1; }
118
if ($opts{'V'}) { $verbose = 2; }
121
if ($opts{'i'}) { $ignore = 1; }
124
if(!exists($ARGV[1]) || !-d $ARGV[0] || !-d $ARGV[1]) {
125
die("$program_name\nUsage: $program_name [-vVhi] dir1 dir2\nType $program_name -h for details\n");
128
my($dirold, $dirnew) = @ARGV;
129
($dirold, $dirnew) = ($dirnew, $dirold) if(-M $dirold < -M $dirnew);
130
print "Comparing $dirold to $dirnew\n";
132
my($addedfiles, $addedspace, $deletedfiles, $deletedspace) = (0, 0, 0, 0);
134
compare_dirs($dirold, $dirnew);
136
print "Between $dirold and $dirnew:\n";
137
print " $addedfiles were added, taking $addedspace bytes;\n";
138
print " $deletedfiles were removed, saving $deletedspace bytes;\n";
143
opendir(OLD, $old) || die("Can't open dir $old\n");
144
opendir(NEW, $new) || die("Can't open dir $new\n");
146
my $fn = $old.'/'.$_;
147
($_, (mystat($fn))[1])
148
} grep { $_ ne '.' && $_ ne '..' } readdir(OLD);
150
my $fn = $new.'/'.$_;
151
($_, (mystat($fn))[1])
152
} grep { $_ ne '.' && $_ ne '..' } readdir(NEW);
156
my @added = grep { !exists($old{$_}) } keys %new;
157
my @deleted = grep { !exists($new{$_}) } keys %old;
158
my @changed = grep { !-d $new.'/'.$_ && exists($old{$_}) && $old{$_} != $new{$_} } keys %new;
160
add(map { $new.'/'.$_ } @added, @changed);
161
remove(map { $old.'/'.$_ } @deleted, @changed);
164
my %changed = map { ($_, 1) } @changed, @added, @deleted;
165
print "0 $new/$_\n" foreach(grep { !-d "$new/$_" && !exists($changed{$_}) } keys %new);
168
foreach (grep { !-l $new.'/'.$_ && !-l $old.'/'.$_ && -d $new.'/'.$_ && -d $old.'/'.$_ } keys %new) {
169
print "Comparing subdirs $new/$_ and $old/$_ ...\n" if(DEBUG);
170
compare_dirs($old.'/'.$_, $new.'/'.$_);
176
print "Adding ".join(', ', @added)."\n" if(DEBUG && @added);
177
foreach(grep { !-d } @added) {
179
$addedspace += (mystat($_))[7];
180
# if ignore is on, only print files
181
unless ($ignore && (-l || !-f)) {
182
print "+ $_\n" if($verbose);
185
foreach my $dir (grep { !-l && -d } @added) {
186
opendir(DIR, $dir) || die("Can't open dir $dir\n");
187
add(map { $dir.'/'.$_ } grep { $_ ne '.' && $_ ne '..' } readdir(DIR))
193
print "Removing ".join(', ', @removed)."\n" if(DEBUG && @removed);
194
foreach(grep { !-d } @removed) {
196
$deletedspace += (mystat($_))[7];
197
# if ignore is on, only print files
198
unless ($ignore && (-l || !-f)) {
199
print "- $_\n" if($verbose);
202
foreach my $dir (grep { !-l && -d } @removed) {
203
opendir(DIR, $dir) || die("Can't open dir $dir\n");
204
remove(map { $dir.'/'.$_ } grep { $_ ne '.' && $_ ne '..' } readdir(DIR))
213
my @stat = (-l) ? lstat() : stat();
215
# on first stat, memorise device
216
$device = $stat[0] unless(defined($device));
217
die("Can't compare across devices.\n(looking at $_)\n")
218
unless($device == $stat[0] || -p $_);
230
Please report bugs (and other comments) to the rsnapshot-discuss mailing list:
232
L<http://lists.sourceforge.net/lists/listinfo/rsnapshot-discuss>
236
David Cantrell E<lt>david@cantrell.org.ukE<gt>
240
Copyright 2005 David Cantrell
244
This program is free software; you can redistribute it and/or modify
245
it under the terms of the GNU General Public License as published by
246
the Free Software Foundation; either version 2 of the License, or
247
(at your option) any later version.
249
This program is distributed in the hope that it will be useful,
250
but WITHOUT ANY WARRANTY; without even the implied warranty of
251
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
252
GNU General Public License for more details.
254
You should have received a copy of the GNU General Public License along
255
with this program; if not, write to the Free Software Foundation, Inc.,
256
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA