[Keith Vetter] 2003-11-07 : There's been a recent hue and cry for "a good diffs module" (see [There is a huge need for a diffs module!]). Here's something I've been using for half a year or so. You enter a wiki page number and it displays the revision history for that page. You then select any two entries, click a button and it will fetch those two revisions and run [tkdiff] on them (or if you prefer you can use ExamDiff [http://www.prestosoft.com/ps.asp?page=edp_examdiff]). This works reasonably well but with two flaws: first, the information in the wiki history seems to be a day behind the wiki itself, and second, wiki pages often contain very long lines that don't have good diff behavior. [WikiDiff] handles this second problem in a very clever way by doing a word level diff. [KPV] It seems these two flaws have been addressed. The first via an undocumented interface that lets you access the latest version; the second by the new version of [tkdiff] (4.0b1) that does inline comparisons. [KPV] 2004-01-23: Added Recents window, a Tkhtml widget showing Wiki page 4--Recent changes. Clicking on any link will put its Wiki page number into the Wiki Page entry. Double clicking any link will fetch that page's history. ---- ''Keith, FYI, on Mac OS X the above needs font "courier 10" to be readable (with X11). Also note that this works fine with the tkdiff starkit on [sdarchive]. Great utility! -[jcw]'' ---- ''This is probably a better place to write this than on the [Diffs Code Module in progress] page. --[PS]'' If anyone is interested, I have a port of wikidiff into wikit at [http://pascal.scheffers.net/software/wikit-hist.kit]. I have had it running for over a year now, sorry for not contributing sooner ;-). It still uses diff/patch from the filesystem, either the standard unix ones or cygwin versions, it should be easily changed to tcllib/diff (does it have patch too?). There are some bugs in the display code, but it *does* store the history in a chain of diffs either in wikit.tkd or a separate history.tkd flawlessly. if {0} { Usage: tclkit wikit-hist.kit my-wikit.tkd -history internal or tclkit wikit-hist.kit my-wikit.tkd -history /somewhere/history.tkd } To access the revision history, you need to run it as a CGI (no internal httpd in this version), you will find that the 'Updated on somedate' at the bottom of each wiki page is now augmented with a ''revision X'' link, click on that and you can get at the diffs. And you also need to set WIKIT_CSS=http://pascal.scheffers.net/pascal.css in your wiki.cgi file to have the colours in the diff show. -- [PS] ---- ''Yes, I'm interested (as you know!). Will have a look, thx. Another comment on Keith's code: it currently diffs against pages in history but not the last one, if it was changed very recently. There is an undocumented feature of this wiki: you can access raw pages as "NNN.txt" (e.g. [http://mini.net/tcl/10335.txt]). - [jcw]'' [KPV] 2003-11-10: that's just the feature this tool needs. I've upgraded the code to always list the latest version as one of the available version. This will be a (harmless) redundancy for most pages but quite useful for the critical ones. ---- ##+########################################################################## # # Wiki History Diff.tcl -- diffs back versions of the tcl Wiki pages # by Keith Vetter # # Revisions: # KPV Mar 11, 2003 - initial revision # KPV Nov 07, 2003 - some touch up work # KPV Nov 10, 2003 - added JCW's undocumented way of fetching latest version # KPV Jan 23, 2004 - added Recents window showing Wiki page 4 # package require Tk package require http set S(tkhtml) [expr {! [catch {package require Tkhtml}]}] set S(diffprog) tkdiff ;# Examdiff.exe works well too set S(title) "Wiki History Diff" set S(bg) "#9977cc" set S(pnum) "xx" ;# Page we want to compare set S(pnum2) "" ;# Entry widget version of pnum set S(busy) 0 set S(font) {courier 8} if {$tcl_platform(machine) == "Power Macintosh"} { set S(font) {courier 10} } # Stupid temp directory set S(tmp) [pwd] if {[file isdirectory "c:/temp"]} { set S(tmp) "c:/temp"} if {[file isdirectory "/tmp"]} { set S(tmp) "/tmp"} catch {set S(tmp) $env(TRASH_FOLDER)} ;# Macintosh(?) catch {set S(tmp) $env(TMPDIR)} catch {set S(tmp) $env(TMP)} catch {set S(tmp) $env(TEMP)} proc INFO {msg} {puts [set ::S(msg) $msg]} proc DoDisplay {} { global S wm title . "Wiki History Diff" wm protocol . WM_DELETE_WINDOW {Cleanup 1} frame .bottom pack .bottom -side bottom -fill x button .recent -text "Show Recent Changes" -command Recents if {! $S(tkhtml)} { .recent config -state disabled} label .msg -textvariable S(msg) button .about -text About -command About pack .recent -in .bottom -side left -padx .1i pack .about -in .bottom -side right -padx .1i pack .msg -in .bottom -side bottom -fill x option add *Label.background $S(bg) option add *Label.foreground white option add *Button.background $S(bg) option add *Button.activeBackground $S(bg) option add *Button.foreground white option add *Button.activeForeground white frame .top -bg $S(bg) -relief groove -bd 2 frame .topl -bg $S(bg) button .topl.wiki -text "Wiki Page " -command DoWikiPage -relief flat entry .topl.ewiki -textvariable S(pnum2) -relief sunken -width 6 bind .topl.ewiki [list .topl.wiki invoke] label .top.title -textvariable S(title) -fg magenta trace variable S(pnum2) w tracer set font [font actual [.top.title cget -font]] .top.title config -font "$font -weight bold -size 18" frame .topr -bg $S(bg) label .topr.l0 -text "Version " label .topr.e0 -textvar S(0) -width 5 -relief sunken label .topr.v -text " vs. " label .topr.l1 -text "Version " label .topr.e1 -textvar S(1) -width 5 -relief sunken label .topr.= -text " => " button .topr.diff -text "Run $S(diffprog)" -command RunTKDiff -bd 5 pack .top -side top -fill x pack .topr -in .top -side right pack .topl -in .top -side left pack .top.title -side top -expand 1 eval pack [winfo child .topl] -side left eval pack [winfo child .topr] -side left pack config .topl.wiki -padx {.1i .05i} pack config .topr.diff -padx {0 .1i} pack [frame .mid -bd 4 -relief ridge] -side top -fill both -expand 1 foreach w {0 1} { listbox .l$w -exportselection 0 -width 63 -activestyle none -bd 0 \ -font {courier 8} -yscrollcommand [list .sb$w set] -height 20 bind .l$w <> [list ListboxSelect %W $w] bind .l$w <1> [list focus %W] scrollbar .sb$w -orient v -command [list .l$w yview] } grid .l0 .sb0 .l1 .sb1 -in .mid -sticky news grid rowconfigure .mid 0 -weight 1 grid columnconfigure .mid {0 2} -weight 1 option clear bind all {console show} focus .topl.ewiki } proc ListboxSelect {W who} { global S set row [$W curselection] set data [$W get $row] set S($who) [lindex $data 1] if {! [string is integer -strict $S($who)]} { set S($who) "Latest" } } proc tracer {var1 var2 op} { global S if {$S(pnum2) == $S(pnum)} { .topl.ewiki config -bg [lindex [.topl.ewiki config -bg] 3] .topl.wiki config -relief flat } else { .topl.ewiki config -bg red .topl.wiki config -relief raised } } proc ReInit {} { global S set S(cnt) 0 } proc GetVersionInfo {} { global S .l0 delete 0 end .l1 delete 0 end update GetTitle INFO "Getting history for page $S(pnum)" set url "http://mini.net/tclhist/$S(pnum)*" set token [::http::geturl $url] set data [::http::data $token] ::http::cleanup $token set S(cnt) 1 .l0 insert end " Latest Version" .l1 insert end " Latest Version" foreach {version tstamp who c1 c2} $data { set when [clock format $tstamp -gmt 1 -format "%e %b %Y %T %Z"] set txt [format " version %2s %16s %s $c1 $c2" $version $who $when] .l0 insert end $txt .l1 insert end $txt incr S(cnt) } set S(0) [set S(1) ""] if {$S(cnt) > 0} { foreach i {0 1} { set el [expr {$S(cnt) > 1 ? 1 - $i : 0}] .l$i selection clear 0 end .l$i selection set $el .l$i selection anchor $el event generate .l$i <> } } else { .l0 insert end .l1 insert end } set msg "Wiki page $S(pnum) has $S(cnt) version" if {$S(cnt) != 1} {append msg s} INFO $msg } proc GetTitle {} { global S INFO "Getting title for page $S(pnum)" set url "http://mini.net/tclhist/$S(pnum)" set token [::http::geturl $url] set data [::http::data $token] ::http::cleanup $token set S(title) "No Wiki History" regexp -line {Title:\s*(.*)} $data => S(title) set S(title) "\"$S(title)\"" } proc GetVersion {pnum ver} { set fname [file join $::S(tmp) "wiki.$pnum.$ver"] set fout [open $fname "w"] if {[string is integer -strict $ver]} { set url "http://mini.net/tclhist/$pnum.$ver" } else { set url "http://mini.net/tcl/$pnum.txt" ;# Hidden feature } set token [::http::geturl $url -channel $fout] close $fout ::http::cleanup $token return $fname } proc RunTKDiff {} { global S Cleanup if {$S(0) == "" || $S(1) == ""} return set f1 [GetVersion $S(pnum) $S(0)] set f2 [GetVersion $S(pnum) $S(1)] exec $S(diffprog) $f1 $f2 & set ::TMPFILES($f1) 1 set ::TMPFILES($f2) 1 after 2000 ;# Pause to let tkdiff start } proc DoWikiPage {} { global S set S(busy) 1 while {1} { after 10 Cleanup .topl.ewiki icursor end if {! [string is integer -strict $S(pnum2)]} break if {$S(pnum) == $S(pnum2)} break set S(pnum) $S(pnum2) GetVersionInfo set S(pnum2) $S(pnum2) } set S(busy) 0 } proc Cleanup {{exit 0}} { global TMPFILES foreach fname [array names TMPFILES] { set n [catch {file delete $fname}] if {! $n} { unset TMPFILES($fname) } } if {$exit} exit } proc About {} { set msg "WikiDiff\nby Keith Vetter\nMarch 2003\n\n" append msg "Compares back revisions of a\nTcl'ers Wiki " append msg "page using tkdiff." tk_messageBox -title "About" -message $msg } proc Recents {} { if {! $::S(tkhtml)} return destroy .r toplevel .r wm title .r "WIKI Recent Changes" wm transient .r . scrollbar .r.v -o v -command {.r.h yv} html .r.h -yscrollcommand {.r.v set} -background white bind .r.h.x <1> [list Click 1 %x %y] bind .r.h.x [list Click 2 %x %y] bind .r.h [bind Text ] pack .r.v -side right -fill y pack .r.h -side left -fill both -expand 1 set url "http://mini.net/tcl/4.html" set token [::http::geturl $url] set data [::http::data $token] ::http::cleanup $token .r.h clear .r.h config -base $url .r.h parse $data focus .r.h } proc Click {cnt x y} { if {$::S(busy)} return set href [.r.h href $x $y] if {$href == ""} return set n [regexp {/(\d+)\}?$} $href => pnum] if {! $n} return set ::S(pnum2) $pnum if {$cnt == 2} { .topl.wiki invoke } } DoDisplay set S(pnum2) [lindex $argv 0] .l0 insert end "Enter the number of a Wiki page to run diff on" Recents return ---- [Category Wikit] | [Category Application]