Version 15 of Wiki History Diff

Updated 2003-11-11 12:10:43

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 [L1 ]).

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 which don't have good diff behavior. WikiDiff handle this second problem in a very clever way by doing a word level diff.


Robert Abitbol This sounds like a great idea. Can we try the code somwhere? Can you ask JCW to put it in the program so we could all try it? Thanks!

KPV I don't understand, here's the code. Just grab it and run it.


Robert Abitbol Thanks Keith but bear with me. I am not a TCL expert. I don't even have a TCL compiler. But hey if you can tell me where I can find a compiler, I'll be the happiest guy in the world and I'll definitely going to try it! Many thanks!

Robert - maybe you have lost sight of the purpose of this wiki. It's about Tcl - an interpreted language. Surely you can use it to find the information you seek. Try google if you can't find it here.

Robert Abitbol Sorry! I had no idea Tcl was an interpretable language and not a compilable one. I'll try to get or buy an interpreter so I can use your diffs and other programs. Thanks and sorry for the trouble.

male - 2003/11/10:

Sorry, to say Robert, but you came into this community flaming and speaking harsh words, but now you offer us, that you don't know anything about this community and tcl as 4th generation language? Look and learn before shouting, yelling and flaming!

And ... please, think in future time about the things you say!

You accuse and speak very loudly very quickly. Tame your tongue! It would ease the communction and the wish to work with you a lot!

After reading a lot from you I'm led into tempation to skip articles from you. And I fear to be not the only one!


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 [L2 ]. 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. [L3 ]). - 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
 #

 package require Tk
 package require http

 set S(diffprog) tkdiff                          ;# Examdiff.exe works well too
 set S(title) "Wiki History Diff"
 set S(bg) "#9977cc"
 set S(pnum) "xx"
 set S(pnum2) ""

 # 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}
     label .msg -textvariable S(msg)
     pack .msg -side bottom -fill x
     button .about -text About -highlightthickness 0 -command About
     pack .about -in .msg -side right -padx .1i

     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 <Key-Return> [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 <<ListboxSelect>> [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 <Alt-c> {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 <<ListboxSelect>>
         }
     } else {
         .l0 insert end <empty>
         .l1 insert end <empty>
     }
     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

     after 10 Cleanup
     .topl.ewiki icursor end
     if {! [string is integer -strict $S(pnum2)]} return
     if {$S(pnum) == $S(pnum2)} return
     set S(pnum) $S(pnum2)
     GetVersionInfo
     set S(pnum2) $S(pnum2)
 }
 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
 }

 DoDisplay
 set S(pnum2) [lindex $argv 0]
 .l0 insert end "Enter the number of a Wiki page to run diff on"
 return

Category Wikit