Richard Suchenwirth 2006-01-30: This Sunday fun project was created because I had no fun on Saturday - coding merrily for Sepp, I suddenly reached a point when the NoteBook widget did not react, and I wasn't exactly clear what changes since the "last know good" state had caused that. At work, I use CVS for version control, so it's easy to compare the current with earlier versions - but on the cellphone? So I decided I need some version control.
At currently 61 lines of code, this is of course a far cry from the "real things" like CVS. It's also less efficient in memory - previous versions are stored fully, not as diff. (But this is not so much of a concern, when pocket source files tend to be short anyway, not much more than a K or two.) For a version-controlled file foo.bar, there are in the same directory copies named foo#0.bar, foo#1.bar etc. (somehow like on old VAX/VMS), the highest version number being the HEAD. If the "top" instance (the one without #) is equal to HEAD, it is considered up-to-date.
Usage:
ver commit filename
Copies the top to new head, one higher than the current, if not up-to-date. (Also does cvs add-like: starts at 0 when the file wasn't versioned yet)
ver diff filename ?n?
Compares the top with the given numbered version (HEAD if no n given) line by line, displays the first three differences. (This is still crude, but already helpful on little changes). Returns "identical" if so, else {}.
ver status filename
Returns "up-to-date with #x", "locally modified - last: #x", or "nothing known" when there are no numbered versions.
ver tell filename
Returns the sorted list of available version numbers for the given top file, HEAD first.
proc ver {cmd {filename ""} {n ""}} { set c [info procs ver::$cmd*] if {[llength $c] != 1} {error "unknown $cmd - use one of: [ver::_cmds]"} $c $filename $n } namespace eval ver { } proc ver::commit {fn} { set last [_head $fn] if {[_equal $fn "" $last] && $last ne ""} { return up-to-date } if {$last eq ""} {set last -1} file copy $fn [_fmt $fn [incr last]] set last } proc ver::diff {fn {n ""}} { if {$n eq ""} {set n [_head $fn]} if {$n eq ""} {return "nothing to diff"} set f1 [open $fn] set f2 [open [_fmt $fn $n]] set diff 0 while {[gets $f1 a]>=0 \ && [gets $f2 b]>=0} { if {$a ne $b} { puts "< $a\n> $b"; incr diff if {$diff>3} break } } close $f1; close $f2 if !$diff {return identical} } proc ver::status {fn {n ""}} { set last [_head $fn] if {$last eq ""} {return "nothing known"} if [_equal $fn "" $last] { return "up-to-date with #$last" } else {return "locally modified - #$last"} } proc ver::tell {fn {n ""}} { set res {} foreach f [glob -noc [_fmt $fn *]] { regexp {#(.+)\.} $f -> no lappend res $no } lsort -integer -dec $res } #---------------------------- Internal functions start with _ proc ver::_cmds {} { lsort [string map {::ver:: ""} \ [info procs {::ver::[a-z]*}]] } proc ver::_equal {fn v1 v2} { expr {[file mtime [_fmt $fn $v1]]\ ==[file mtime [_fmt $fn $v2]]} } proc ver::_fmt {fn n} { if {$n eq ""} {return $fn} return [file root $fn]#$n[file ext $fn] } proc ver::_head fn {lindex [tell $fn] 0}