2007-Dec-02 Here is a small script which compares two directory trees. It does not only look at the tree, but inspects the content of the files. -- Sarnold
Usage:
tddiff /path/to/webapp-1.2 /path/to/webapp-1.0 /path/to/changes/dir/to/be/created
It can compare two application source trees, for example, and put the differences into a changes directory. The changes directory needs to be non-existent before the script is invoked.
After the script terminates successfully, there should be two subdirectories in "changes":
* 'new' in which the new/modified (source) files are written verbatim * 'discarded' in which are written the files that do not exist anymore in the latest source tree.
Fields of interest: some small Linux distros like Puppy Linux need this kind of software: small, simple, require much less resources than C counterparts. There is potientally an interest for Microsoft Windows, where such programs, often written for UNIX, are missing.
The script
#!/usr/bin/env tclsh
proc usage {} {
puts stderr "usage: tddiff dir1/ dir2/ out/"
exit 1
}
proc assert {expr {msg {assertion failed}}} {
if {![uplevel 1 expr $expr]} {error $msg}
}
proc main {argv} {
if {[llength $argv] != 3} {
usage
}
foreach {dir1 dir2 out} $argv {}
assert {![file isdirectory $out]} "out directory '$out' already exists"
assert {![file exists $out]} "out directory '$out' is a regular file"
file mkdir $out
foreach f [NewIn $dir1 $dir2 .] {Transfer $dir1 $out/new $f}
foreach f [NewIn $dir2 $dir1 . on] {Transfer $dir2 $out/discarded $f}
}
# copy one file, creating directories if they miss
proc Transfer {in out subpath} {
set in $in/$subpath
set dest $out/[file dirname $subpath]
if {![file isdirectory $dest]} {
puts "Creating directory $dest"
file mkdir $dest
}
file copy $in $out/$subpath
}
proc NewIn {dir1 dir2 sub {deprecated off}} {
set res ""
foreach f [Subfiles $dir1 $sub] {
if {[file isdirectory $dir1/$f]} {
set res [concat $res [NewIn $dir1 $dir2 $f $deprecated]]
} else {
if {[NewFile $dir1/$f $dir2/$f $deprecated]} {lappend res $f}
}
}
set res
}
# returns true if file2 does not exist or if file1 is different in content
proc NewFile {file1 file2 {deprecated off}} {
if {![file exists $file2]} {return true}
if {$deprecated} {return false}
return [expr {![BinEqual $file1 $file2]}]
}
proc min {a b} {expr {($a>$b)?($b):$a}}
# compares two files, given their paths f1 and f2
proc BinEqual {f1 f2} {
if {[file size $f1] != [file size $f2]} {
# not the same size -> not equal
return false
}
set sz [file size $f1]
set a [open $f1]
set b [open $f2]
fconfigure $a -translation binary
fconfigure $b -translation binary
while {![eof $a] && ![eof $b]} {
if {![string equal [read $a [min $sz 4096]] [read $b [min $sz 4096]]]} {
close $a
close $b
return false
}
incr sz -4096
if {$sz<0} {
close $a
close $b
return true
}
}
close $a
close $b
return true
}
proc Subfiles {dir sub} {
set p [pwd]
cd $dir
set res [glob -nocomplain $sub/*]
cd $p
set res
}
catch {tcldebug::debug}
main $argv