Update the git-ls-tree documentation
[git.git] / gitk
diff --git a/gitk b/gitk
index bd10f7d..a847ef6 100755 (executable)
--- a/gitk
+++ b/gitk
@@ -1,6 +1,6 @@
 #!/bin/sh
 # Tcl ignores the next line -*- tcl -*- \
 #!/bin/sh
 # Tcl ignores the next line -*- tcl -*- \
-exec wish8.4 "$0" -- "${1+$@}"
+exec wish "$0" -- "$@"
 
 # Copyright (C) 2005 Paul Mackerras.  All rights reserved.
 # This program is free software; it may be used, copied, modified
 
 # Copyright (C) 2005 Paul Mackerras.  All rights reserved.
 # This program is free software; it may be used, copied, modified
@@ -19,7 +19,7 @@ proc gitdir {} {
 proc getcommits {rargs} {
     global commits commfd phase canv mainfont env
     global startmsecs nextupdate ncmupdate
 proc getcommits {rargs} {
     global commits commfd phase canv mainfont env
     global startmsecs nextupdate ncmupdate
-    global ctext maincursor textcursor leftover
+    global ctext maincursor textcursor leftover gitencoding
 
     # check that we can find a .git directory somewhere...
     set gitdir [gitdir]
 
     # check that we can find a .git directory somewhere...
     set gitdir [gitdir]
@@ -30,7 +30,7 @@ proc getcommits {rargs} {
     set commits {}
     set phase getcommits
     set startmsecs [clock clicks -milliseconds]
     set commits {}
     set phase getcommits
     set startmsecs [clock clicks -milliseconds]
-    set nextupdate [expr $startmsecs + 100]
+    set nextupdate [expr {$startmsecs + 100}]
     set ncmupdate 1
     if [catch {
        set parse_args [concat --default HEAD $rargs]
     set ncmupdate 1
     if [catch {
        set parse_args [concat --default HEAD $rargs]
@@ -49,7 +49,7 @@ proc getcommits {rargs} {
        exit 1
     }
     set leftover {}
        exit 1
     }
     set leftover {}
-    fconfigure $commfd -blocking 0 -translation lf
+    fconfigure $commfd -blocking 0 -translation lf -encoding $gitencoding
     fileevent $commfd readable [list getcommitlines $commfd]
     $canv delete all
     $canv create text 3 3 -anchor nw -text "Reading commits..." \
     fileevent $commfd readable [list getcommitlines $commfd]
     $canv delete all
     $canv create text 3 3 -anchor nw -text "Reading commits..." \
@@ -60,7 +60,7 @@ proc getcommits {rargs} {
 
 proc getcommitlines {commfd}  {
     global commits parents cdate children
 
 proc getcommitlines {commfd}  {
     global commits parents cdate children
-    global commitlisted phase commitinfo nextupdate
+    global commitlisted phase nextupdate
     global stopped redisplaying leftover
 
     set stuff [read $commfd]
     global stopped redisplaying leftover
 
     set stuff [read $commfd]
@@ -74,9 +74,9 @@ proc getcommitlines {commfd}  {
        }
        if {[string range $err 0 4] == "usage"} {
            set err \
        }
        if {[string range $err 0 4] == "usage"} {
            set err \
-{Gitk: error reading commits: bad arguments to git-rev-list.
-(Note: arguments to gitk are passed to git-rev-list
-to allow selection of commits to be displayed.)}
+               "Gitk: error reading commits: bad arguments to git-rev-list.\
+               (Note: arguments to gitk are passed to git-rev-list\
+               to allow selection of commits to be displayed.)"
        } else {
            set err "Error reading commits: $err"
        }
        } else {
            set err "Error reading commits: $err"
        }
@@ -196,42 +196,44 @@ proc parsecommit {id contents listed olds} {
            incr ncleft($p)
        }
     }
            incr ncleft($p)
        }
     }
-    foreach line [split $contents "\n"] {
-       if {$inhdr} {
-           if {$line == {}} {
-               set inhdr 0
-           } else {
-               set tag [lindex $line 0]
-               if {$tag == "author"} {
-                   set x [expr {[llength $line] - 2}]
-                   set audate [lindex $line $x]
-                   set auname [lrange $line 1 [expr {$x - 1}]]
-               } elseif {$tag == "committer"} {
-                   set x [expr {[llength $line] - 2}]
-                   set comdate [lindex $line $x]
-                   set comname [lrange $line 1 [expr {$x - 1}]]
-               }
-           }
-       } else {
-           if {$comment == {}} {
-               set headline [string trim $line]
-           } else {
-               append comment "\n"
-           }
-           if {!$listed} {
-               # git-rev-list indents the comment by 4 spaces;
-               # if we got this via git-cat-file, add the indentation
-               append comment "    "
-           }
-           append comment $line
+    set hdrend [string first "\n\n" $contents]
+    if {$hdrend < 0} {
+       # should never happen...
+       set hdrend [string length $contents]
+    }
+    set header [string range $contents 0 [expr {$hdrend - 1}]]
+    set comment [string range $contents [expr {$hdrend + 2}] end]
+    foreach line [split $header "\n"] {
+       set tag [lindex $line 0]
+       if {$tag == "author"} {
+           set audate [lindex $line end-1]
+           set auname [lrange $line 1 end-2]
+       } elseif {$tag == "committer"} {
+           set comdate [lindex $line end-1]
+           set comname [lrange $line 1 end-2]
        }
     }
        }
     }
-    if {$audate != {}} {
-       set audate [clock format $audate -format "%Y-%m-%d %H:%M:%S"]
+    set headline {}
+    # take the first line of the comment as the headline
+    set i [string first "\n" $comment]
+    if {$i >= 0} {
+       set headline [string trim [string range $comment 0 $i]]
+    } else {
+       set headline $comment
+    }
+    if {!$listed} {
+       # git-rev-list indents the comment by 4 spaces;
+       # if we got this via git-cat-file, add the indentation
+       set newcomment {}
+       foreach line [split $comment "\n"] {
+           append newcomment "    "
+           append newcomment $line
+           append newcomment "\n"
+       }
+       set comment $newcomment
     }
     if {$comdate != {}} {
        set cdate($id) $comdate
     }
     if {$comdate != {}} {
        set cdate($id) $comdate
-       set comdate [clock format $comdate -format "%Y-%m-%d %H:%M:%S"]
     }
     set commitinfo($id) [list $headline $auname $audate \
                             $comname $comdate $comment]
     }
     set commitinfo($id) [list $headline $auname $audate \
                             $comname $comdate $comment]
@@ -239,77 +241,43 @@ proc parsecommit {id contents listed olds} {
 
 proc readrefs {} {
     global tagids idtags headids idheads tagcontents
 
 proc readrefs {} {
     global tagids idtags headids idheads tagcontents
-
-    set tags [glob -nocomplain -types f [gitdir]/refs/tags/*]
-    foreach f $tags {
-       catch {
-           set fd [open $f r]
-           set line [read $fd]
-           if {[regexp {^[0-9a-f]{40}} $line id]} {
-               set direct [file tail $f]
-               set tagids($direct) $id
-               lappend idtags($id) $direct
-               set tagblob [exec git-cat-file tag $id]
-               set contents [split $tagblob "\n"]
-               set obj {}
-               set type {}
-               set tag {}
-               foreach l $contents {
-                   if {$l == {}} break
-                   switch -- [lindex $l 0] {
-                       "object" {set obj [lindex $l 1]}
-                       "type" {set type [lindex $l 1]}
-                       "tag" {set tag [string range $l 4 end]}
-                   }
-               }
-               if {$obj != {} && $type == "commit" && $tag != {}} {
-                   set tagids($tag) $obj
-                   lappend idtags($obj) $tag
-                   set tagcontents($tag) $tagblob
-               }
-           }
-           close $fd
-       }
-    }
-    set heads [glob -nocomplain -types f [gitdir]/refs/heads/*]
-    foreach f $heads {
-       catch {
-           set fd [open $f r]
-           set line [read $fd 40]
-           if {[regexp {^[0-9a-f]{40}} $line id]} {
-               set head [file tail $f]
-               set headids($head) $line
-               lappend idheads($line) $head
-           }
-           close $fd
-       }
-    }
-    readotherrefs refs {} {tags heads}
-}
-
-proc readotherrefs {base dname excl} {
     global otherrefids idotherrefs
 
     global otherrefids idotherrefs
 
-    set git [gitdir]
-    set files [glob -nocomplain -types f [file join $git $base *]]
-    foreach f $files {
-       catch {
-           set fd [open $f r]
-           set line [read $fd 40]
-           if {[regexp {^[0-9a-f]{40}} $line id]} {
-               set name "$dname[file tail $f]"
-               set otherrefids($name) $id
-               lappend idotherrefs($id) $name
+    set refd [open [list | git-ls-remote [gitdir]] r]
+    while {0 <= [set n [gets $refd line]]} {
+       if {![regexp {^([0-9a-f]{40})   refs/([^^]*)$} $line \
+           match id path]} {
+           continue
+       }
+       if {![regexp {^(tags|heads)/(.*)$} $path match type name]} {
+           set type others
+           set name $path
+       }
+       if {$type == "tags"} {
+           set tagids($name) $id
+           lappend idtags($id) $name
+           set obj {}
+           set type {}
+           set tag {}
+           catch {
+               set commit [exec git-rev-parse "$id^0"]
+               if {"$commit" != "$id"} {
+                   set tagids($name) $commit
+                   lappend idtags($commit) $name
+               }
+           }           
+           catch {
+               set tagcontents($name) [exec git-cat-file tag "$id"]
            }
            }
-           close $fd
+       } elseif { $type == "heads" } {
+           set headids($name) $id
+           lappend idheads($id) $name
+       } else {
+           set otherrefids($name) $id
+           lappend idotherrefs($id) $name
        }
     }
        }
     }
-    set dirs [glob -nocomplain -types d [file join $git $base *]]
-    foreach d $dirs {
-       set dir [file tail $d]
-       if {[lsearch -exact $excl $dir] >= 0} continue
-       readotherrefs [file join $base $dir] "$dname$dir/" {}
-    }
+    close $refd
 }
 
 proc error_popup msg {
 }
 
 proc error_popup msg {
@@ -329,23 +297,26 @@ proc makewindow {} {
     global findtype findtypemenu findloc findstring fstring geometry
     global entries sha1entry sha1string sha1but
     global maincursor textcursor curtextcursor
     global findtype findtypemenu findloc findstring fstring geometry
     global entries sha1entry sha1string sha1but
     global maincursor textcursor curtextcursor
-    global rowctxmenu gaudydiff mergemax
+    global rowctxmenu mergemax
 
     menu .bar
     .bar add cascade -label "File" -menu .bar.file
     menu .bar.file
     .bar.file add command -label "Reread references" -command rereadrefs
     .bar.file add command -label "Quit" -command doquit
 
     menu .bar
     .bar add cascade -label "File" -menu .bar.file
     menu .bar.file
     .bar.file add command -label "Reread references" -command rereadrefs
     .bar.file add command -label "Quit" -command doquit
+    menu .bar.edit
+    .bar add cascade -label "Edit" -menu .bar.edit
+    .bar.edit add command -label "Preferences" -command doprefs
     menu .bar.help
     .bar add cascade -label "Help" -menu .bar.help
     .bar.help add command -label "About gitk" -command about
     . configure -menu .bar
 
     if {![info exists geometry(canv1)]} {
     menu .bar.help
     .bar add cascade -label "Help" -menu .bar.help
     .bar.help add command -label "About gitk" -command about
     . configure -menu .bar
 
     if {![info exists geometry(canv1)]} {
-       set geometry(canv1) [expr 45 * $charspc]
-       set geometry(canv2) [expr 30 * $charspc]
-       set geometry(canv3) [expr 15 * $charspc]
-       set geometry(canvh) [expr 25 * $linespc + 4]
+       set geometry(canv1) [expr {45 * $charspc}]
+       set geometry(canv2) [expr {30 * $charspc}]
+       set geometry(canv3) [expr {15 * $charspc}]
+       set geometry(canvh) [expr {25 * $linespc + 4}]
        set geometry(ctextw) 80
        set geometry(ctexth) 30
        set geometry(cflistw) 30
        set geometry(ctextw) 80
        set geometry(ctexth) 30
        set geometry(cflistw) 30
@@ -446,25 +417,19 @@ proc makewindow {} {
     .ctop.cdet add .ctop.cdet.left
 
     $ctext tag conf filesep -font [concat $textfont bold] -back "#aaaaaa"
     .ctop.cdet add .ctop.cdet.left
 
     $ctext tag conf filesep -font [concat $textfont bold] -back "#aaaaaa"
-    if {$gaudydiff} {
-       $ctext tag conf hunksep -back blue -fore white
-       $ctext tag conf d0 -back "#ff8080"
-       $ctext tag conf d1 -back green
-    } else {
-       $ctext tag conf hunksep -fore blue
-       $ctext tag conf d0 -fore red
-       $ctext tag conf d1 -fore "#00a000"
-       $ctext tag conf m0 -fore red
-       $ctext tag conf m1 -fore blue
-       $ctext tag conf m2 -fore green
-       $ctext tag conf m3 -fore purple
-       $ctext tag conf m4 -fore brown
-       $ctext tag conf mmax -fore darkgrey
-       set mergemax 5
-       $ctext tag conf mresult -font [concat $textfont bold]
-       $ctext tag conf msep -font [concat $textfont bold]
-       $ctext tag conf found -back yellow
-    }
+    $ctext tag conf hunksep -fore blue
+    $ctext tag conf d0 -fore red
+    $ctext tag conf d1 -fore "#00a000"
+    $ctext tag conf m0 -fore red
+    $ctext tag conf m1 -fore blue
+    $ctext tag conf m2 -fore green
+    $ctext tag conf m3 -fore purple
+    $ctext tag conf m4 -fore brown
+    $ctext tag conf mmax -fore darkgrey
+    set mergemax 5
+    $ctext tag conf mresult -font [concat $textfont bold]
+    $ctext tag conf msep -font [concat $textfont bold]
+    $ctext tag conf found -back yellow
 
     frame .ctop.cdet.right
     set cflist .ctop.cdet.right.cfiles
 
     frame .ctop.cdet.right
     set cflist .ctop.cdet.right.cfiles
@@ -565,7 +530,7 @@ proc click {w} {
 
 proc savestuff {w} {
     global canv canv2 canv3 ctext cflist mainfont textfont
 
 proc savestuff {w} {
     global canv canv2 canv3 ctext cflist mainfont textfont
-    global stuffsaved findmergefiles gaudydiff maxgraphpct
+    global stuffsaved findmergefiles maxgraphpct
     global maxwidth
 
     if {$stuffsaved} return
     global maxwidth
 
     if {$stuffsaved} return
@@ -575,15 +540,14 @@ proc savestuff {w} {
        puts $f [list set mainfont $mainfont]
        puts $f [list set textfont $textfont]
        puts $f [list set findmergefiles $findmergefiles]
        puts $f [list set mainfont $mainfont]
        puts $f [list set textfont $textfont]
        puts $f [list set findmergefiles $findmergefiles]
-       puts $f [list set gaudydiff $gaudydiff]
        puts $f [list set maxgraphpct $maxgraphpct]
        puts $f [list set maxwidth $maxwidth]
        puts $f "set geometry(width) [winfo width .ctop]"
        puts $f "set geometry(height) [winfo height .ctop]"
        puts $f [list set maxgraphpct $maxgraphpct]
        puts $f [list set maxwidth $maxwidth]
        puts $f "set geometry(width) [winfo width .ctop]"
        puts $f "set geometry(height) [winfo height .ctop]"
-       puts $f "set geometry(canv1) [expr [winfo width $canv]-2]"
-       puts $f "set geometry(canv2) [expr [winfo width $canv2]-2]"
-       puts $f "set geometry(canv3) [expr [winfo width $canv3]-2]"
-       puts $f "set geometry(canvh) [expr [winfo height $canv]-2]"
+       puts $f "set geometry(canv1) [expr {[winfo width $canv]-2}]"
+       puts $f "set geometry(canv2) [expr {[winfo width $canv2]-2}]"
+       puts $f "set geometry(canv3) [expr {[winfo width $canv3]-2}]"
+       puts $f "set geometry(canvh) [expr {[winfo height $canv]-2}]"
        set wid [expr {([winfo width $ctext] - 8) \
                           / [font measure $textfont "0"]}]
        puts $f "set geometry(ctextw) $wid"
        set wid [expr {([winfo width $ctext] - 8) \
                           / [font measure $textfont "0"]}]
        puts $f "set geometry(ctextw) $wid"
@@ -612,12 +576,12 @@ proc resizeclistpanes {win w} {
                set sash0 30
            }
            if {$sash1 < $sash0 + 20} {
                set sash0 30
            }
            if {$sash1 < $sash0 + 20} {
-               set sash1 [expr $sash0 + 20]
+               set sash1 [expr {$sash0 + 20}]
            }
            if {$sash1 > $w - 10} {
            }
            if {$sash1 > $w - 10} {
-               set sash1 [expr $w - 10]
+               set sash1 [expr {$w - 10}]
                if {$sash0 > $sash1 - 20} {
                if {$sash0 > $sash1 - 20} {
-                   set sash0 [expr $sash1 - 20]
+                   set sash0 [expr {$sash1 - 20}]
                }
            }
        }
                }
            }
        }
@@ -640,7 +604,7 @@ proc resizecdetpanes {win w} {
                set sash0 45
            }
            if {$sash0 > $w - 15} {
                set sash0 45
            }
            if {$sash0 > $w - 15} {
-               set sash0 [expr $w - 15]
+               set sash0 [expr {$w - 15}]
            }
        }
        $win sash place 0 $sash0 [lindex $s0 1]
            }
        }
        $win sash place 0 $sash0 [lindex $s0 1]
@@ -683,7 +647,7 @@ Use and redistribute under the terms of the GNU General Public License} \
 }
 
 proc assigncolor {id} {
 }
 
 proc assigncolor {id} {
-    global commitinfo colormap commcolors colors nextcolor
+    global colormap commcolors colors nextcolor
     global parents nparents children nchildren
     global cornercrossings crossings
 
     global parents nparents children nchildren
     global cornercrossings crossings
 
@@ -783,10 +747,12 @@ proc bindline {t id} {
     $canv bind $t <Button-1> "lineclick %x %y $id 1"
 }
 
     $canv bind $t <Button-1> "lineclick %x %y $id 1"
 }
 
-proc drawlines {id xtra} {
+proc drawlines {id xtra delold} {
     global mainline mainlinearrow sidelines lthickness colormap canv
 
     global mainline mainlinearrow sidelines lthickness colormap canv
 
-    $canv delete lines.$id
+    if {$delold} {
+       $canv delete lines.$id
+    }
     if {[info exists mainline($id)]} {
        set t [$canv create line $mainline($id) \
                   -width [expr {($xtra + 1) * $lthickness}] \
     if {[info exists mainline($id)]} {
        set t [$canv create line $mainline($id) \
                   -width [expr {($xtra + 1) * $lthickness}] \
@@ -849,19 +815,19 @@ proc drawcommitline {level} {
     }
     set x [xcoord $level $level $lineno]
     set y1 $canvy
     }
     set x [xcoord $level $level $lineno]
     set y1 $canvy
-    set canvy [expr $canvy + $linespc]
+    set canvy [expr {$canvy + $linespc}]
     allcanvs conf -scrollregion \
     allcanvs conf -scrollregion \
-       [list 0 0 0 [expr $y1 + 0.5 * $linespc + 2]]
+       [list 0 0 0 [expr {$y1 + 0.5 * $linespc + 2}]]
     if {[info exists mainline($id)]} {
        lappend mainline($id) $x $y1
        if {$mainlinearrow($id) ne "none"} {
            set mainline($id) [trimdiagstart $mainline($id)]
        }
     }
     if {[info exists mainline($id)]} {
        lappend mainline($id) $x $y1
        if {$mainlinearrow($id) ne "none"} {
            set mainline($id) [trimdiagstart $mainline($id)]
        }
     }
-    drawlines $id 0
+    drawlines $id 0 0
     set orad [expr {$linespc / 3}]
     set orad [expr {$linespc / 3}]
-    set t [$canv create oval [expr $x - $orad] [expr $y1 - $orad] \
-              [expr $x + $orad - 1] [expr $y1 + $orad - 1] \
+    set t [$canv create oval [expr {$x - $orad}] [expr {$y1 - $orad}] \
+              [expr {$x + $orad - 1}] [expr {$y1 + $orad - 1}] \
               -fill $ofill -outline black -width 1]
     $canv raise $t
     $canv bind $t <1> {selcanvline {} %x %y}
               -fill $ofill -outline black -width 1]
     $canv raise $t
     $canv bind $t <1> {selcanvline {} %x %y}
@@ -878,6 +844,7 @@ proc drawcommitline {level} {
     set headline [lindex $commitinfo($id) 0]
     set name [lindex $commitinfo($id) 1]
     set date [lindex $commitinfo($id) 2]
     set headline [lindex $commitinfo($id) 0]
     set name [lindex $commitinfo($id) 1]
     set date [lindex $commitinfo($id) 2]
+    set date [formatdate $date]
     set linehtag($lineno) [$canv create text $xt $y1 -anchor w \
                               -text $headline -font $mainfont ]
     $canv bind $linehtag($lineno) <Button-3> "rowmenu %X %Y $id"
     set linehtag($lineno) [$canv create text $xt $y1 -anchor w \
                               -text $headline -font $mainfont ]
     $canv bind $linehtag($lineno) <Button-3> "rowmenu %X %Y $id"
@@ -915,8 +882,8 @@ proc drawtags {id x xt y1} {
     }
 
     set delta [expr {int(0.5 * ($linespc - $lthickness))}]
     }
 
     set delta [expr {int(0.5 * ($linespc - $lthickness))}]
-    set yt [expr $y1 - 0.5 * $linespc]
-    set yb [expr $yt + $linespc - 1]
+    set yt [expr {$y1 - 0.5 * $linespc}]
+    set yb [expr {$yt + $linespc - 1}]
     set xvals {}
     set wvals {}
     foreach tag $marks {
     set xvals {}
     set wvals {}
     foreach tag $marks {
@@ -929,12 +896,12 @@ proc drawtags {id x xt y1} {
               -width $lthickness -fill black -tags tag.$id]
     $canv lower $t
     foreach tag $marks x $xvals wid $wvals {
               -width $lthickness -fill black -tags tag.$id]
     $canv lower $t
     foreach tag $marks x $xvals wid $wvals {
-       set xl [expr $x + $delta]
-       set xr [expr $x + $delta + $wid + $lthickness]
+       set xl [expr {$x + $delta}]
+       set xr [expr {$x + $delta + $wid + $lthickness}]
        if {[incr ntags -1] >= 0} {
            # draw a tag
        if {[incr ntags -1] >= 0} {
            # draw a tag
-           set t [$canv create polygon $x [expr $yt + $delta] $xl $yt \
-                      $xr $yt $xr $yb $xl $yb $x [expr $yb - $delta] \
+           set t [$canv create polygon $x [expr {$yt + $delta}] $xl $yt \
+                      $xr $yt $xr $yb $xl $yb $x [expr {$yb - $delta}] \
                       -width 1 -outline black -fill yellow -tags tag.$id]
            $canv bind $t <1> [list showtag $tag 1]
            set rowtextx($idline($id)) [expr {$xr + $linespc}]
                       -width 1 -outline black -fill yellow -tags tag.$id]
            $canv bind $t <1> [list showtag $tag 1]
            set rowtextx($idline($id)) [expr {$xr + $linespc}]
@@ -945,7 +912,7 @@ proc drawtags {id x xt y1} {
            } else {
                set col "#ddddff"
            }
            } else {
                set col "#ddddff"
            }
-           set xl [expr $xl - $delta/2]
+           set xl [expr {$xl - $delta/2}]
            $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
                -width 1 -outline black -fill $col -tags tag.$id
        }
            $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
                -width 1 -outline black -fill $col -tags tag.$id
        }
@@ -1446,8 +1413,8 @@ proc decidenext {{noread 0}} {
 }
 
 proc drawcommit {id} {
 }
 
 proc drawcommit {id} {
-    global phase todo nchildren datemode nextupdate
-    global numcommits ncmupdate displayorder todo onscreen
+    global phase todo nchildren datemode nextupdate revlistorder
+    global numcommits ncmupdate displayorder todo onscreen parents
 
     if {$phase != "incrdraw"} {
        set phase incrdraw
 
     if {$phase != "incrdraw"} {
        set phase incrdraw
@@ -1459,19 +1426,29 @@ proc drawcommit {id} {
        lappend todo $id
        set onscreen($id) 0
     }
        lappend todo $id
        set onscreen($id) 0
     }
-    set level [decidenext 1]
-    if {$level == {} || $id != [lindex $todo $level]} {
-       return
-    }
-    while 1 {
-       lappend displayorder [lindex $todo $level]
-       if {[updatetodo $level $datemode]} {
-           set level [decidenext 1]
-           if {$level == {}} break
+    if {$revlistorder} {
+       set level [lsearch -exact $todo $id]
+       if {$level < 0} {
+           error_popup "oops, $id isn't in todo"
+           return
        }
        }
-       set id [lindex $todo $level]
-       if {![info exists commitlisted($id)]} {
-           break
+       lappend displayorder $id
+       updatetodo $level 0
+    } else {
+       set level [decidenext 1]
+       if {$level == {} || $id != [lindex $todo $level]} {
+           return
+       }
+       while 1 {
+           lappend displayorder [lindex $todo $level]
+           if {[updatetodo $level $datemode]} {
+               set level [decidenext 1]
+               if {$level == {}} break
+           }
+           set id [lindex $todo $level]
+           if {![info exists commitlisted($id)]} {
+               break
+           }
        }
     }
     drawmore 1
        }
     }
     drawmore 1
@@ -1510,7 +1487,7 @@ proc drawgraph {} {
 
     if {$displayorder == {}} return
     set startmsecs [clock clicks -milliseconds]
 
     if {$displayorder == {}} return
     set startmsecs [clock clicks -milliseconds]
-    set nextupdate [expr $startmsecs + 100]
+    set nextupdate [expr {$startmsecs + 100}]
     set ncmupdate 1
     initgraph
     foreach id $displayorder {
     set ncmupdate 1
     initgraph
     foreach id $displayorder {
@@ -1523,7 +1500,7 @@ proc drawrest {} {
     global phase stopped redisplaying selectedline
     global datemode todo displayorder
     global numcommits ncmupdate
     global phase stopped redisplaying selectedline
     global datemode todo displayorder
     global numcommits ncmupdate
-    global nextupdate startmsecs
+    global nextupdate startmsecs revlistorder
 
     set level [decidenext]
     if {$level >= 0} {
 
     set level [decidenext]
     if {$level >= 0} {
@@ -1536,10 +1513,10 @@ proc drawrest {} {
                if {$level < 0} break
            }
        }
                if {$level < 0} break
            }
        }
-       drawmore 0
     }
     }
+    drawmore 0
     set phase {}
     set phase {}
-    set drawmsecs [expr [clock clicks -milliseconds] - $startmsecs]
+    set drawmsecs [expr {[clock clicks -milliseconds] - $startmsecs}]
     #puts "overall $drawmsecs ms for $numcommits commits"
     if {$redisplaying} {
        if {$stopped == 0 && [info exists selectedline]} {
     #puts "overall $drawmsecs ms for $numcommits commits"
     if {$redisplaying} {
        if {$stopped == 0 && [info exists selectedline]} {
@@ -1567,8 +1544,8 @@ proc findmatches {f} {
        set matches {}
        set i 0
        while {[set j [string first $foundstring $str $i]] >= 0} {
        set matches {}
        set i 0
        while {[set j [string first $foundstring $str $i]] >= 0} {
-           lappend matches [list $j [expr $j+$foundstrlen-1]]
-           set i [expr $j + $foundstrlen]
+           lappend matches [list $j [expr {$j+$foundstrlen-1}]]
+           set i [expr {$j + $foundstrlen}]
        }
     }
     return $matches
        }
     }
     return $matches
@@ -1649,7 +1626,7 @@ proc findselectline {l} {
        set matches [findmatches $f]
        foreach match $matches {
            set start [lindex $match 0]
        set matches [findmatches $f]
        foreach match $matches {
            set start [lindex $match 0]
-           set end [expr [lindex $match 1] + 1]
+           set end [expr {[lindex $match 1] + 1}]
            $ctext tag add found "1.0 + $start c" "1.0 + $end c"
        }
     }
            $ctext tag add found "1.0 + $start c" "1.0 + $end c"
        }
     }
@@ -2003,9 +1980,10 @@ proc markmatches {canv l str tag matches font} {
        set start [lindex $match 0]
        set end [lindex $match 1]
        if {$start > $end} continue
        set start [lindex $match 0]
        set end [lindex $match 1]
        if {$start > $end} continue
-       set xoff [font measure $font [string range $str 0 [expr $start-1]]]
-       set xlen [font measure $font [string range $str 0 [expr $end]]]
-       set t [$canv create rect [expr $x0+$xoff] $y0 [expr $x0+$xlen+2] $y1 \
+       set xoff [font measure $font [string range $str 0 [expr {$start-1}]]]
+       set xlen [font measure $font [string range $str 0 [expr {$end}]]]
+       set t [$canv create rect [expr {$x0+$xoff}] $y0 \
+                  [expr {$x0+$xlen+2}] $y1 \
                   -outline {} -tags matches -fill yellow]
        $canv lower $t
     }
                   -outline {} -tags matches -fill yellow]
        $canv lower $t
     }
@@ -2097,8 +2075,8 @@ proc selectline {l isnew} {
     set ytop [expr {$y - $linespc - 1}]
     set ybot [expr {$y + $linespc + 1}]
     set wnow [$canv yview]
     set ytop [expr {$y - $linespc - 1}]
     set ybot [expr {$y + $linespc + 1}]
     set wnow [$canv yview]
-    set wtop [expr [lindex $wnow 0] * $ymax]
-    set wbot [expr [lindex $wnow 1] * $ymax]
+    set wtop [expr {[lindex $wnow 0] * $ymax}]
+    set wbot [expr {[lindex $wnow 1] * $ymax}]
     set wh [expr {$wbot - $wtop}]
     set newtop $wtop
     if {$ytop < $wtop} {
     set wh [expr {$wbot - $wtop}]
     set newtop $wtop
     if {$ytop < $wtop} {
@@ -2124,7 +2102,7 @@ proc selectline {l isnew} {
        if {$newtop < 0} {
            set newtop 0
        }
        if {$newtop < 0} {
            set newtop 0
        }
-       allcanvs yview moveto [expr $newtop * 1.0 / $ymax]
+       allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
     }
 
     if {$isnew} {
     }
 
     if {$isnew} {
@@ -2146,8 +2124,10 @@ proc selectline {l isnew} {
     $ctext mark set fmark.0 0.0
     $ctext mark gravity fmark.0 left
     set info $commitinfo($id)
     $ctext mark set fmark.0 0.0
     $ctext mark gravity fmark.0 left
     set info $commitinfo($id)
-    $ctext insert end "Author: [lindex $info 1]  [lindex $info 2]\n"
-    $ctext insert end "Committer: [lindex $info 3]  [lindex $info 4]\n"
+    set date [formatdate [lindex $info 2]]
+    $ctext insert end "Author: [lindex $info 1]  $date\n"
+    set date [formatdate [lindex $info 4]]
+    $ctext insert end "Committer: [lindex $info 3]  $date\n"
     if {[info exists idtags($id)]} {
        $ctext insert end "Tags:"
        foreach tag $idtags($id) {
     if {[info exists idtags($id)]} {
        $ctext insert end "Tags:"
        foreach tag $idtags($id) {
@@ -2181,7 +2161,7 @@ proc selectline {l isnew} {
     $cflist delete 0 end
     $cflist insert end "Comments"
     if {$nparents($id) == 1} {
     $cflist delete 0 end
     $cflist insert end "Comments"
     if {$nparents($id) == 1} {
-       startdiff [concat $id $parents($id)]
+       startdiff $id
     } elseif {$nparents($id) > 1} {
        mergediff $id
     }
     } elseif {$nparents($id) > 1} {
        mergediff $id
     }
@@ -2190,7 +2170,7 @@ proc selectline {l isnew} {
 proc selnextline {dir} {
     global selectedline
     if {![info exists selectedline]} return
 proc selnextline {dir} {
     global selectedline
     if {![info exists selectedline]} return
-    set l [expr $selectedline + $dir]
+    set l [expr {$selectedline + $dir}]
     unmarkmatches
     selectline $l 1
 }
     unmarkmatches
     selectline $l 1
 }
@@ -2290,12 +2270,12 @@ proc contmergediff {ids} {
     # diff the child against each of the parents, and diff
     # each of the parents against the GCA.
     while 1 {
     # diff the child against each of the parents, and diff
     # each of the parents against the GCA.
     while 1 {
-       if {[lindex $ids 0] == $diffmergeid && $diffmergegca ne {}} {
-           set ids [list [lindex $ids 1] $diffmergegca]
+       if {[lindex $ids 1] == $diffmergeid && $diffmergegca ne {}} {
+           set ids [list $diffmergegca [lindex $ids 0]]
        } else {
            if {[incr diffpindex] >= $nparents($diffmergeid)} break
            set p [lindex $parents($diffmergeid) $diffpindex]
        } else {
            if {[incr diffpindex] >= $nparents($diffmergeid)} break
            set p [lindex $parents($diffmergeid) $diffpindex]
-           set ids [list $diffmergeid $p]
+           set ids [list $p $diffmergeid]
        }
        if {![info exists treediffs($ids)]} {
            set diffids $ids
        }
        if {![info exists treediffs($ids)]} {
            set diffids $ids
@@ -2313,8 +2293,8 @@ proc contmergediff {ids} {
     if {$diffmergegca ne {}} {
        set files {}
        foreach p $parents($diffmergeid) {
     if {$diffmergegca ne {}} {
        set files {}
        foreach p $parents($diffmergeid) {
-           set gcadiffs $treediffs([list $p $diffmergegca])
-           foreach f $treediffs([list $diffmergeid $p]) {
+           set gcadiffs $treediffs([list $diffmergegca $p])
+           foreach f $treediffs([list $p $diffmergeid]) {
                if {[lsearch -exact $files $f] < 0
                    && [lsearch -exact $gcadiffs $f] >= 0} {
                    lappend files $f
                if {[lsearch -exact $files $f] < 0
                    && [lsearch -exact $gcadiffs $f] >= 0} {
                    lappend files $f
@@ -2327,7 +2307,7 @@ proc contmergediff {ids} {
        set files $treediffs([list $diffmergeid $p])
        for {set i 1} {$i < $nparents($diffmergeid) && $files ne {}} {incr i} {
            set p [lindex $parents($diffmergeid) $i]
        set files $treediffs([list $diffmergeid $p])
        for {set i 1} {$i < $nparents($diffmergeid) && $files ne {}} {incr i} {
            set p [lindex $parents($diffmergeid) $i]
-           set df $treediffs([list $diffmergeid $p])
+           set df $treediffs([list $p $diffmergeid])
            set nf {}
            foreach f $files {
                if {[lsearch -exact $df $f] >= 0} {
            set nf {}
            foreach f $files {
                if {[lsearch -exact $df $f] >= 0} {
@@ -2804,9 +2784,7 @@ proc gettreediffs {ids} {
     global treediff parents treepending
     set treepending $ids
     set treediff {}
     global treediff parents treepending
     set treepending $ids
     set treediff {}
-    set id [lindex $ids 0]
-    set p [lindex $ids 1]
-    if [catch {set gdtf [open "|git-diff-tree -r $p $id" r]}] return
+    if [catch {set gdtf [open [concat | git-diff-tree --no-commit-id -r $ids] r]}] return
     fconfigure $gdtf -blocking 0
     fileevent $gdtf readable [list gettreediffline $gdtf $ids]
 }
     fconfigure $gdtf -blocking 0
     fileevent $gdtf readable [list gettreediffline $gdtf $ids]
 }
@@ -2839,10 +2817,8 @@ proc getblobdiffs {ids} {
     global diffopts blobdifffd diffids env curdifftag curtagstart
     global difffilestart nextupdate diffinhdr treediffs
 
     global diffopts blobdifffd diffids env curdifftag curtagstart
     global difffilestart nextupdate diffinhdr treediffs
 
-    set id [lindex $ids 0]
-    set p [lindex $ids 1]
     set env(GIT_DIFF_OPTS) $diffopts
     set env(GIT_DIFF_OPTS) $diffopts
-    set cmd [list | git-diff-tree -r -p -C $p $id]
+    set cmd [concat | git-diff-tree --no-commit-id -r -p -C $ids]
     if {[catch {set bdf [open $cmd r]} err]} {
        puts "error getting diffs: $err"
        return
     if {[catch {set bdf [open $cmd r]} err]} {
        puts "error getting diffs: $err"
        return
@@ -2861,7 +2837,6 @@ proc getblobdiffline {bdf ids} {
     global diffids blobdifffd ctext curdifftag curtagstart
     global diffnexthead diffnextnote difffilestart
     global nextupdate diffinhdr treediffs
     global diffids blobdifffd ctext curdifftag curtagstart
     global diffnexthead diffnextnote difffilestart
     global nextupdate diffinhdr treediffs
-    global gaudydiff
 
     set n [gets $bdf line]
     if {$n < 0} {
 
     set n [gets $bdf line]
     if {$n < 0} {
@@ -2910,26 +2885,14 @@ proc getblobdiffline {bdf ids} {
        set diffinhdr 0
     } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
                   $line match f1l f1c f2l f2c rest]} {
        set diffinhdr 0
     } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
                   $line match f1l f1c f2l f2c rest]} {
-       if {$gaudydiff} {
-           $ctext insert end "\t" hunksep
-           $ctext insert end "    $f1l    " d0 "    $f2l    " d1
-           $ctext insert end "    $rest \n" hunksep
-       } else {
-           $ctext insert end "$line\n" hunksep
-       }
+       $ctext insert end "$line\n" hunksep
        set diffinhdr 0
     } else {
        set x [string range $line 0 0]
        if {$x == "-" || $x == "+"} {
            set tag [expr {$x == "+"}]
        set diffinhdr 0
     } else {
        set x [string range $line 0 0]
        if {$x == "-" || $x == "+"} {
            set tag [expr {$x == "+"}]
-           if {$gaudydiff} {
-               set line [string range $line 1 end]
-           }
            $ctext insert end "$line\n" d$tag
        } elseif {$x == " "} {
            $ctext insert end "$line\n" d$tag
        } elseif {$x == " "} {
-           if {$gaudydiff} {
-               set line [string range $line 1 end]
-           }
            $ctext insert end "$line\n"
        } elseif {$diffinhdr || $x == "\\"} {
            # e.g. "\ No newline at end of file"
            $ctext insert end "$line\n"
        } elseif {$diffinhdr || $x == "\\"} {
            # e.g. "\ No newline at end of file"
@@ -2985,8 +2948,8 @@ proc setcoords {} {
 
     set linespc [font metrics $mainfont -linespace]
     set charspc [font measure $mainfont "m"]
 
     set linespc [font metrics $mainfont -linespace]
     set charspc [font measure $mainfont "m"]
-    set canvy0 [expr 3 + 0.5 * $linespc]
-    set canvx0 [expr 3 + 0.5 * $linespc]
+    set canvy0 [expr {3 + 0.5 * $linespc}]
+    set canvx0 [expr {3 + 0.5 * $linespc}]
     set lthickness [expr {int($linespc / 9) + 1}]
     set xspc1(0) $linespc
     set xspc2 $linespc
     set lthickness [expr {int($linespc / 9) + 1}]
     set xspc1(0) $linespc
     set xspc2 $linespc
@@ -3143,7 +3106,7 @@ proc linehover {} {
     set t [$canv create rectangle $x0 $y0 $x1 $y1 \
               -fill \#ffff80 -outline black -width 1 -tags hover]
     $canv raise $t
     set t [$canv create rectangle $x0 $y0 $x1 $y1 \
               -fill \#ffff80 -outline black -width 1 -tags hover]
     $canv raise $t
-    set t [$canv create text $x $y -anchor nw -text $text -tags hover]
+    set t [$canv create text $x $y -anchor nw -text $text -tags hover -font $mainfont]
     $canv raise $t
 }
 
     $canv raise $t
 }
 
@@ -3178,7 +3141,7 @@ proc clickisonarrow {id y} {
 }
 
 proc arrowjump {id dirn y} {
 }
 
 proc arrowjump {id dirn y} {
-    global mainline sidelines canv
+    global mainline sidelines canv canv2 canv3
 
     set yt {}
     if {$dirn eq "down"} {
 
     set yt {}
     if {$dirn eq "down"} {
@@ -3216,6 +3179,8 @@ proc arrowjump {id dirn y} {
        set yfrac 0
     }
     $canv yview moveto $yfrac
        set yfrac 0
     }
     $canv yview moveto $yfrac
+    $canv2 yview moveto $yfrac
+    $canv3 yview moveto $yfrac
 }
 
 proc lineclick {x y id isnew} {
 }
 
 proc lineclick {x y id isnew} {
@@ -3226,7 +3191,7 @@ proc lineclick {x y id isnew} {
     normalline
     $canv delete hover
     # draw this line thicker than normal
     normalline
     $canv delete hover
     # draw this line thicker than normal
-    drawlines $id 1
+    drawlines $id 1 1
     set thickerline $id
     if {$isnew} {
        set ymax [lindex [$canv cget -scrollregion] 3]
     set thickerline $id
     if {$isnew} {
        set ymax [lindex [$canv cget -scrollregion] 3]
@@ -3255,7 +3220,8 @@ proc lineclick {x y id isnew} {
     set info $commitinfo($id)
     $ctext insert end "\n\t[lindex $info 0]\n"
     $ctext insert end "\tAuthor:\t[lindex $info 1]\n"
     set info $commitinfo($id)
     $ctext insert end "\n\t[lindex $info 0]\n"
     $ctext insert end "\tAuthor:\t[lindex $info 1]\n"
-    $ctext insert end "\tDate:\t[lindex $info 2]\n"
+    set date [formatdate [lindex $info 2]]
+    $ctext insert end "\tDate:\t$date\n"
     if {[info exists children($id)]} {
        $ctext insert end "\nChildren:"
        set i 0
     if {[info exists children($id)]} {
        $ctext insert end "\nChildren:"
        set i 0
@@ -3267,7 +3233,8 @@ proc lineclick {x y id isnew} {
            $ctext tag bind link$i <1> [list selbyid $child]
            $ctext insert end "\n\t[lindex $info 0]"
            $ctext insert end "\n\tAuthor:\t[lindex $info 1]"
            $ctext tag bind link$i <1> [list selbyid $child]
            $ctext insert end "\n\t[lindex $info 0]"
            $ctext insert end "\n\tAuthor:\t[lindex $info 1]"
-           $ctext insert end "\n\tDate:\t[lindex $info 2]\n"
+           set date [formatdate [lindex $info 2]]
+           $ctext insert end "\n\tDate:\t$date\n"
        }
     }
     $ctext conf -state disabled
        }
     }
     $ctext conf -state disabled
@@ -3278,7 +3245,7 @@ proc lineclick {x y id isnew} {
 proc normalline {} {
     global thickerline
     if {[info exists thickerline]} {
 proc normalline {} {
     global thickerline
     if {[info exists thickerline]} {
-       drawlines $thickerline 0
+       drawlines $thickerline 0 1
        unset thickerline
     }
 }
        unset thickerline
     }
 }
@@ -3355,7 +3322,7 @@ proc doseldiff {oldid newid} {
     $ctext conf -state disabled
     $ctext tag delete Comments
     $ctext tag remove found 1.0 end
     $ctext conf -state disabled
     $ctext tag delete Comments
     $ctext tag remove found 1.0 end
-    startdiff [list $newid $oldid]
+    startdiff [list $oldid $newid]
 }
 
 proc mkpatch {} {
 }
 
 proc mkpatch {} {
@@ -3650,34 +3617,113 @@ proc doquit {} {
     destroy .
 }
 
     destroy .
 }
 
+proc doprefs {} {
+    global maxwidth maxgraphpct diffopts findmergefiles
+    global oldprefs prefstop
+
+    set top .gitkprefs
+    set prefstop $top
+    if {[winfo exists $top]} {
+       raise $top
+       return
+    }
+    foreach v {maxwidth maxgraphpct diffopts findmergefiles} {
+       set oldprefs($v) [set $v]
+    }
+    toplevel $top
+    wm title $top "Gitk preferences"
+    label $top.ldisp -text "Commit list display options"
+    grid $top.ldisp - -sticky w -pady 10
+    label $top.spacer -text " "
+    label $top.maxwidthl -text "Maximum graph width (lines)" \
+       -font optionfont
+    spinbox $top.maxwidth -from 0 -to 100 -width 4 -textvariable maxwidth
+    grid $top.spacer $top.maxwidthl $top.maxwidth -sticky w
+    label $top.maxpctl -text "Maximum graph width (% of pane)" \
+       -font optionfont
+    spinbox $top.maxpct -from 1 -to 100 -width 4 -textvariable maxgraphpct
+    grid x $top.maxpctl $top.maxpct -sticky w
+    checkbutton $top.findm -variable findmergefiles
+    label $top.findml -text "Include merges for \"Find\" in \"Files\"" \
+       -font optionfont
+    grid $top.findm $top.findml - -sticky w
+    label $top.ddisp -text "Diff display options"
+    grid $top.ddisp - -sticky w -pady 10
+    label $top.diffoptl -text "Options for diff program" \
+       -font optionfont
+    entry $top.diffopt -width 20 -textvariable diffopts
+    grid x $top.diffoptl $top.diffopt -sticky w
+    frame $top.buts
+    button $top.buts.ok -text "OK" -command prefsok
+    button $top.buts.can -text "Cancel" -command prefscan
+    grid $top.buts.ok $top.buts.can
+    grid columnconfigure $top.buts 0 -weight 1 -uniform a
+    grid columnconfigure $top.buts 1 -weight 1 -uniform a
+    grid $top.buts - - -pady 10 -sticky ew
+}
+
+proc prefscan {} {
+    global maxwidth maxgraphpct diffopts findmergefiles
+    global oldprefs prefstop
+
+    foreach v {maxwidth maxgraphpct diffopts findmergefiles} {
+       set $v $oldprefs($v)
+    }
+    catch {destroy $prefstop}
+    unset prefstop
+}
+
+proc prefsok {} {
+    global maxwidth maxgraphpct
+    global oldprefs prefstop
+
+    catch {destroy $prefstop}
+    unset prefstop
+    if {$maxwidth != $oldprefs(maxwidth)
+       || $maxgraphpct != $oldprefs(maxgraphpct)} {
+       redisplay
+    }
+}
+
+proc formatdate {d} {
+    return [clock format $d -format "%Y-%m-%d %H:%M:%S"]
+}
+
 # defaults...
 set datemode 0
 # defaults...
 set datemode 0
-set boldnames 0
 set diffopts "-U 5 -p"
 set wrcomcmd "git-diff-tree --stdin -p --pretty"
 
 set diffopts "-U 5 -p"
 set wrcomcmd "git-diff-tree --stdin -p --pretty"
 
+set gitencoding ""
+catch {
+    set gitencoding [exec git-repo-config --get i18n.commitencoding]
+}
+if {$gitencoding == ""} {
+       set gitencoding "utf-8"
+}
+
 set mainfont {Helvetica 9}
 set textfont {Courier 9}
 set findmergefiles 0
 set mainfont {Helvetica 9}
 set textfont {Courier 9}
 set findmergefiles 0
-set gaudydiff 0
 set maxgraphpct 50
 set maxwidth 16
 set maxgraphpct 50
 set maxwidth 16
+set revlistorder 0
+set fastdate 0
 
 set colors {green red blue magenta darkgrey brown orange}
 
 catch {source ~/.gitk}
 
 set namefont $mainfont
 
 set colors {green red blue magenta darkgrey brown orange}
 
 catch {source ~/.gitk}
 
 set namefont $mainfont
-if {$boldnames} {
-    lappend namefont bold
-}
+
+font create optionfont -family sans-serif -size -12
 
 set revtreeargs {}
 foreach arg $argv {
     switch -regexp -- $arg {
        "^$" { }
 
 set revtreeargs {}
 foreach arg $argv {
     switch -regexp -- $arg {
        "^$" { }
-       "^-b" { set boldnames 1 }
        "^-d" { set datemode 1 }
        "^-d" { set datemode 1 }
+       "^-r" { set revlistorder 1 }
        default {
            lappend revtreeargs $arg
        }
        default {
            lappend revtreeargs $arg
        }