Version 8 of Wiki History Diff

Updated 2003-11-10 09:19:37

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.


RobertAbitbol 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.


 ##+##########################################################################
 #
 # 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
 #

 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 -height 20 -bd 0 \
            -font {courier 8} -yscrollcommand [list .sb$w set]      
        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]
 }
 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) 0
    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"]

    set url "http://mini.net/tclhist/$pnum.$ver"
    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"

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.


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.

 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