Description edit
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}