use String::CRC32; my $ref = "repository-201119"; my $reflen = length($ref) + 1; # allow for trailing / my %refs; my %crcs; my %new; my %changed; my %moved; sub dir_walk { my ($top, $filefunc, $dirfunc) = @_; my $DIR; if (-d $top) { my $file; unless (opendir $DIR, $top) { warn "Couldn’t open directory $top: $!; skipping.\n"; return; } my @results; while ($file = readdir $DIR) { next if $file eq '.' || $file eq '..'; push@results,dir_walk("$top/$file", $filefunc, $dirfunc); } return $dirfunc ? $dirfunc->($top, @results) : () ; } else { return $filefunc ? $filefunc->($top): () ; } } sub filecrc32 { open my $fh, "<:raw", $_[0] or die $!; return crc32($fh); } sub addRefFile { $refs{substr($_[0], $reflen)} = filecrc32($_[0]); } sub cmpFile { my $file = $_[0]; my $crc = filecrc32($file); if (!defined($refs{$file})) { $new{$file} = $crc; } elsif ($crc != $refs{$file}) { $changed{$file} = $crc; } else { delete $refs{$file}; } } sub mvdir { $moved{$_[1]} = $_[0]; opendir my $newDir, $_[1]; while (my $f = readdir $newDir) { if (defined($refs{"$_[0]/$f"})) { delete $refs{"$_[0]/$f"}; delete $new{"$_[1]/$f"}; } } } # build up the old file list dir_walk($ref, \&addRefFile); # check each subtree in the old against the new # and top level files opendir my $DIR, $ref or die $!; while (my $f = readdir $DIR) { if (-d $f) { dir_walk($f, \&cmpFile) unless $f eq "." || $f eq ".."; } elsif (-f "$ref/$f") { cmpFile($f); } } closedir $DIR; for my $k (keys %refs) { push @{$crcs{$refs{$k}}}, $k; } for my $f (keys %new) { if (defined $new{$f} && defined($crcs{$new{$f}})) { # move dir will impact %new my $first = ${$crcs{$new{$f}}}[0]; my $second = ${$crcs{$new{$f}}}[1]; if ($second eq "") { my $ndir = $f =~ s/\/[^\/]*$//r; my $odir = $first =~ s/\/[^\/]*$/\1/r; if (!-d "$ref/$ndir" && !-d "$odir") { mvdir($odir, $ndir); } else { $moved{$f} = $first; delete $refs{$first}; delete $new{$f}; } } else { print "fix \"", join(":", @{$crcs{$new{$f}}}), "\" \"$f\"\n"; } } } for my $f (keys %changed) { delete $refs{$f} if defined($refs{$f}); } print "delete \"", join("\"\ndelete \"", sort(keys %refs)), "\"\n"; for my $k (sort keys %moved) { print "move \"$moved{$k}\" \"$k\"\n"; } print "replace \"", join("\"\nreplace \"", sort(keys %changed)), "\"\n"; print "add \"", join("\"\nadd \"", sort(keys %new)), "\n";