Merge branch 'jc/clone' into next
[git.git] / gitk
diff --git a/gitk b/gitk
index db61a15..f4c6624 100755 (executable)
--- a/gitk
+++ b/gitk
@@ -19,10 +19,10 @@ proc gitdir {} {
 proc parse_args {rargs} {
     global parsed_args
 
-    if [catch {
+    if {[catch {
        set parse_args [concat --default HEAD $rargs]
        set parsed_args [split [eval exec git-rev-parse $parse_args] "\n"]
-    }] {
+    }]} {
        # if git-rev-parse failed for some reason...
        if {$rargs == {}} {
            set rargs HEAD
@@ -32,10 +32,32 @@ proc parse_args {rargs} {
     return $parsed_args
 }
 
-proc getcommits {rargs} {
-    global oldcommits commits commfd phase canv mainfont env
+proc start_rev_list {rlargs} {
     global startmsecs nextupdate ncmupdate
-    global ctext maincursor textcursor leftover gitencoding
+    global commfd leftover tclencoding
+
+    set startmsecs [clock clicks -milliseconds]
+    set nextupdate [expr {$startmsecs + 100}]
+    set ncmupdate 1
+    if {[catch {
+       set commfd [open [concat | git-rev-list --header --topo-order \
+                             --parents $rlargs] r]
+    } err]} {
+       puts stderr "Error executing git-rev-list: $err"
+       exit 1
+    }
+    set leftover {}
+    fconfigure $commfd -blocking 0 -translation lf
+    if {$tclencoding != {}} {
+       fconfigure $commfd -encoding $tclencoding
+    }
+    fileevent $commfd readable [list getcommitlines $commfd]
+    . config -cursor watch
+    settextcursor watch
+}
+
+proc getcommits {rargs} {
+    global oldcommits commits phase canv mainfont env
 
     # check that we can find a .git directory somewhere...
     set gitdir [gitdir]
@@ -46,24 +68,10 @@ proc getcommits {rargs} {
     set oldcommits {}
     set commits {}
     set phase getcommits
-    set startmsecs [clock clicks -milliseconds]
-    set nextupdate [expr {$startmsecs + 100}]
-    set ncmupdate 1
-    set parsed_args [parse_args $rargs]
-    if [catch {
-       set commfd [open "|git-rev-list --header --topo-order --parents $parsed_args" r]
-    } err] {
-       puts stderr "Error executing git-rev-list: $err"
-       exit 1
-    }
-    set leftover {}
-    fconfigure $commfd -blocking 0 -translation lf -encoding $gitencoding
-    fileevent $commfd readable [list getcommitlines $commfd]
+    start_rev_list [parse_args $rargs]
     $canv delete all
     $canv create text 3 3 -anchor nw -text "Reading commits..." \
        -font $mainfont -tags textitems
-    . config -cursor watch
-    settextcursor watch
 }
 
 proc getcommitlines {commfd}  {
@@ -128,14 +136,6 @@ proc getcommitlines {commfd}  {
        set id [lindex $ids 0]
        set olds [lrange $ids 1 end]
        set cmit [string range $cmit [expr {$j + 1}] end]
-       if {$phase == "updatecommits"} {
-           $canv delete all
-           set oldcommits $commits
-           set commits {}
-           unset children
-           unset nchildren
-           set phase getcommits
-       }
        lappend commits $id
        set commitlisted($id) 1
        parsecommit $id $cmit 1 [lrange $ids 1 end]
@@ -181,10 +181,100 @@ proc doupdate {reading} {
 }
 
 proc readcommit {id} {
-    if [catch {set contents [exec git-cat-file commit $id]}] return
+    if {[catch {set contents [exec git-cat-file commit $id]}]} return
     parsecommit $id $contents 0 {}
 }
 
+proc updatecommits {rargs} {
+    global commitlisted commfd phase
+    global startmsecs nextupdate ncmupdate
+    global idtags idheads idotherrefs
+    global leftover
+    global parsed_args
+    global canv mainfont
+    global oldcommits commits
+    global parents nchildren children ncleft
+
+    set old_args $parsed_args
+    parse_args $rargs
+
+    if {$phase == "getcommits" || $phase == "incrdraw"} {
+       # havent read all the old commits, just start again from scratch
+       stopfindproc
+       set oldcommits {}
+       set commits {}
+       foreach v {children nchildren parents commitlisted commitinfo
+                  selectedline matchinglines treediffs
+                  mergefilelist currentid rowtextx} {
+           global $v
+           catch {unset $v}
+       }
+       readrefs
+       if {$phase == "incrdraw"} {
+           allcanvs delete all
+           $canv create text 3 3 -anchor nw -text "Reading commits..." \
+               -font $mainfont -tags textitems
+           set phase getcommits
+       }
+       start_rev_list $parsed_args
+       return
+    }
+
+    foreach id $old_args {
+       if {![regexp {^[0-9a-f]{40}$} $id]} continue
+       if {[info exists oldref($id)]} continue
+       set oldref($id) $id
+       lappend ignoreold "^$id"
+    }
+    foreach id $parsed_args {
+       if {![regexp {^[0-9a-f]{40}$} $id]} continue
+       if {[info exists ref($id)]} continue
+       set ref($id) $id
+       lappend ignorenew "^$id"
+    }
+
+    foreach a $old_args {
+       if {![info exists ref($a)]} {
+           lappend ignorenew $a
+       }
+    }
+
+    set phase updatecommits
+    set oldcommits $commits
+    set commits {}
+    set removed_commits [split [eval exec git-rev-list $ignorenew] "\n" ]
+    if {[llength $removed_commits] > 0} {
+       allcanvs delete all
+       foreach c $removed_commits {
+           set i [lsearch -exact $oldcommits $c]
+           if {$i >= 0} {
+               set oldcommits [lreplace $oldcommits $i $i]
+               unset commitlisted($c)
+               foreach p $parents($c) {
+                   if {[info exists nchildren($p)]} {
+                       set j [lsearch -exact $children($p) $c]
+                       if {$j >= 0} {
+                           set children($p) [lreplace $children($p) $j $j]
+                           incr nchildren($p) -1
+                       }
+                   }
+               }
+           }
+       }
+       set phase removecommits
+    }
+
+    set args {}
+    foreach a $parsed_args {
+       if {![info exists oldref($a)]} {
+           lappend args $a
+       }
+    }
+
+    readrefs
+    start_rev_list [concat $ignoreold $args]
+}
+
 proc updatechildren {id olds} {
     global children nchildren parents nparents ncleft
 
@@ -452,8 +542,19 @@ proc makewindow {rargs} {
     $ctext tag conf m2 -fore green
     $ctext tag conf m3 -fore purple
     $ctext tag conf m4 -fore brown
+    $ctext tag conf m5 -fore "#009090"
+    $ctext tag conf m6 -fore magenta
+    $ctext tag conf m7 -fore "#808000"
+    $ctext tag conf m8 -fore "#009000"
+    $ctext tag conf m9 -fore "#ff0080"
+    $ctext tag conf m10 -fore cyan
+    $ctext tag conf m11 -fore "#b07070"
+    $ctext tag conf m12 -fore "#70b0f0"
+    $ctext tag conf m13 -fore "#70f0b0"
+    $ctext tag conf m14 -fore "#f0b070"
+    $ctext tag conf m15 -fore "#ff70b0"
     $ctext tag conf mmax -fore darkgrey
-    set mergemax 5
+    set mergemax 16
     $ctext tag conf mresult -font [concat $textfont bold]
     $ctext tag conf msep -font [concat $textfont bold]
     $ctext tag conf found -back yellow
@@ -589,7 +690,7 @@ proc savestuff {w} {
 
 proc resizeclistpanes {win w} {
     global oldwidth
-    if [info exists oldwidth($win)] {
+    if {[info exists oldwidth($win)]} {
        set s0 [$win sash coord 0]
        set s1 [$win sash coord 1]
        if {$w < 60} {
@@ -620,7 +721,7 @@ proc resizeclistpanes {win w} {
 
 proc resizecdetpanes {win w} {
     global oldwidth
-    if [info exists oldwidth($win)] {
+    if {[info exists oldwidth($win)]} {
        set s0 [$win sash coord 0]
        if {$w < 60} {
            set sash0 [expr {int($w*3/4 - 2)}]
@@ -678,7 +779,7 @@ proc assigncolor {id} {
     global parents nparents children nchildren
     global cornercrossings crossings
 
-    if [info exists colormap($id)] return
+    if {[info exists colormap($id)]} return
     set ncolors [llength $colors]
     if {$nparents($id) <= 1 && $nchildren($id) == 1} {
        set child [lindex $children($id) 0]
@@ -746,7 +847,6 @@ proc assigncolor {id} {
 
 proc initgraph {} {
     global canvy canvy0 lineno numcommits nextcolor linespc
-    global mainline mainlinearrow sidelines
     global nchildren ncleft
     global displist nhyperspace
 
@@ -755,9 +855,11 @@ proc initgraph {} {
     set canvy $canvy0
     set lineno -1
     set numcommits 0
-    catch {unset mainline}
-    catch {unset mainlinearrow}
-    catch {unset sidelines}
+    foreach v {mainline mainlinearrow sidelines colormap cornercrossings
+               crossings idline lineid} {
+       global $v
+       catch {unset $v}
+    }
     foreach id [array names nchildren] {
        set ncleft($id) $nchildren($id)
     }
@@ -1426,30 +1528,23 @@ proc decidenext {{noread 0}} {
            }
        }
     }
-    if {$level < 0} {
-       if {$todo != {}} {
-           puts "ERROR: none of the pending commits can be done yet:"
-           foreach p $todo {
-               puts "  $p ($ncleft($p))"
-           }
-       }
-       return -1
-    }
 
     return $level
 }
 
 proc drawcommit {id reading} {
-    global phase todo nchildren datemode nextupdate revlistorder
-    global numcommits ncmupdate displayorder todo onscreen
+    global phase todo nchildren datemode nextupdate revlistorder ncleft
     global numcommits ncmupdate displayorder todo onscreen parents
+    global commitlisted commitordered
 
     if {$phase != "incrdraw"} {
        set phase incrdraw
        set displayorder {}
        set todo {}
        initgraph
+       catch {unset commitordered}
     }
+    set commitordered($id) 1
     if {$nchildren($id) == 0} {
        lappend todo $id
        set onscreen($id) 0
@@ -1464,18 +1559,16 @@ proc drawcommit {id reading} {
        updatetodo $level 0
     } else {
        set level [decidenext 1]
-       if {$level == {} || $id != [lindex $todo $level]} {
-           return
-       }
+       if {$level == {} || $level < 0} return
        while 1 {
+           set id [lindex $todo $level]
+           if {![info exists commitordered($id)]} {
+               break
+           }
            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
+               if {$level == {} || $level < 0} break
            }
        }
     }
@@ -1485,17 +1578,19 @@ proc drawcommit {id reading} {
 proc finishcommits {} {
     global phase oldcommits commits
     global canv mainfont ctext maincursor textcursor
-    global parents
+    global parents displayorder todo
 
     if {$phase == "incrdraw" || $phase == "removecommits"} {
        foreach id $oldcommits {
            lappend commits $id
-           updatechildren $id $parents($id)
            drawcommit $id 0
        }
        set oldcommits {}
        drawrest
     } elseif {$phase == "updatecommits"} {
+       # there were no new commits, in fact
+       set commits $oldcommits
+       set oldcommits {}
        set phase {}
     } else {
        $canv delete all
@@ -1535,7 +1630,7 @@ proc drawgraph {} {
 
 proc drawrest {} {
     global phase stopped redisplaying selectedline
-    global datemode todo displayorder
+    global datemode todo displayorder ncleft
     global numcommits ncmupdate
     global nextupdate startmsecs revlistorder
 
@@ -1551,6 +1646,13 @@ proc drawrest {} {
            }
        }
     }
+    if {$todo != {}} {
+       puts "ERROR: none of the pending commits can be done yet:"
+       foreach p $todo {
+           puts "  $p ($ncleft($p))"
+       }
+    }
+
     drawmore 0
     set phase {}
     set drawmsecs [expr {[clock clicks -milliseconds] - $startmsecs}]
@@ -1834,7 +1936,7 @@ proc findfiles {} {
     global selectedline numcommits lineid ctext
     global ffileline finddidsel parents nparents
     global findinprogress findstartline findinsertpos
-    global treediffs fdiffids fdiffsneeded fdiffpos
+    global treediffs fdiffid fdiffsneeded fdiffpos
     global findmergefiles
 
     if {$numcommits == 0} return
@@ -1851,11 +1953,9 @@ proc findfiles {} {
     while 1 {
        set id $lineid($l)
        if {$findmergefiles || $nparents($id) == 1} {
-           foreach p $parents($id) {
-               if {![info exists treediffs([list $id $p])]} {
-                   append diffsneeded "$id $p\n"
-                   lappend fdiffsneeded [list $id $p]
-               }
+           if {![info exists treediffs($id)]} {
+               append diffsneeded "$id\n"
+               lappend fdiffsneeded $id
            }
        }
        if {[incr l] >= $numcommits} {
@@ -1872,7 +1972,7 @@ proc findfiles {} {
            error_popup "Error starting search process: $err"
            return
        }
-       catch {unset fdiffids}
+       catch {unset fdiffid}
        set fdiffpos 0
        fconfigure $df -blocking 0
        fileevent $df readable [list readfilediffs $df]
@@ -1881,16 +1981,15 @@ proc findfiles {} {
     set finddidsel 0
     set findinsertpos end
     set id $lineid($l)
-    set p [lindex $parents($id) 0]
     . config -cursor watch
     settextcursor watch
     set findinprogress 1
-    findcont [list $id $p]
+    findcont $id
     update
 }
 
 proc readfilediffs {df} {
-    global findids fdiffids fdiffs
+    global findid fdiffid fdiffs
 
     set n [gets $df line]
     if {$n < 0} {
@@ -1900,19 +1999,19 @@ proc readfilediffs {df} {
                stopfindproc
                bell
                error_popup "Error in git-diff-tree: $err"
-           } elseif {[info exists findids]} {
-               set ids $findids
+           } elseif {[info exists findid]} {
+               set id $findid
                stopfindproc
                bell
-               error_popup "Couldn't find diffs for {$ids}"
+               error_popup "Couldn't find diffs for $id"
            }
        }
        return
     }
-    if {[regexp {^([0-9a-f]{40}) \(from ([0-9a-f]{40})\)} $line match id p]} {
+    if {[regexp {^([0-9a-f]{40})$} $line match id]} {
        # start of a new string of diffs
        donefilediff
-       set fdiffids [list $id $p]
+       set fdiffid $id
        set fdiffs {}
     } elseif {[string match ":*" $line]} {
        lappend fdiffs [lindex $line 5]
@@ -1920,53 +2019,50 @@ proc readfilediffs {df} {
 }
 
 proc donefilediff {} {
-    global fdiffids fdiffs treediffs findids
+    global fdiffid fdiffs treediffs findid
     global fdiffsneeded fdiffpos
 
-    if {[info exists fdiffids]} {
-       while {[lindex $fdiffsneeded $fdiffpos] ne $fdiffids
+    if {[info exists fdiffid]} {
+       while {[lindex $fdiffsneeded $fdiffpos] ne $fdiffid
               && $fdiffpos < [llength $fdiffsneeded]} {
            # git-diff-tree doesn't output anything for a commit
            # which doesn't change anything
-           set nullids [lindex $fdiffsneeded $fdiffpos]
-           set treediffs($nullids) {}
-           if {[info exists findids] && $nullids eq $findids} {
-               unset findids
-               findcont $nullids
+           set nullid [lindex $fdiffsneeded $fdiffpos]
+           set treediffs($nullid) {}
+           if {[info exists findid] && $nullid eq $findid} {
+               unset findid
+               findcont $nullid
            }
            incr fdiffpos
        }
        incr fdiffpos
 
-       if {![info exists treediffs($fdiffids)]} {
-           set treediffs($fdiffids) $fdiffs
+       if {![info exists treediffs($fdiffid)]} {
+           set treediffs($fdiffid) $fdiffs
        }
-       if {[info exists findids] && $fdiffids eq $findids} {
-           unset findids
-           findcont $fdiffids
+       if {[info exists findid] && $fdiffid eq $findid} {
+           unset findid
+           findcont $fdiffid
        }
     }
 }
 
-proc findcont {ids} {
-    global findids treediffs parents nparents
+proc findcont {id} {
+    global findid treediffs parents nparents
     global ffileline findstartline finddidsel
     global lineid numcommits matchinglines findinprogress
     global findmergefiles
 
-    set id [lindex $ids 0]
-    set p [lindex $ids 1]
-    set pi [lsearch -exact $parents($id) $p]
     set l $ffileline
     while 1 {
        if {$findmergefiles || $nparents($id) == 1} {
-           if {![info exists treediffs($ids)]} {
-               set findids $ids
+           if {![info exists treediffs($id)]} {
+               set findid $id
                set ffileline $l
                return
            }
            set doesmatch 0
-           foreach f $treediffs($ids) {
+           foreach f $treediffs($id) {
                set x [findmatches $f]
                if {$x != {}} {
                    set doesmatch 1
@@ -1975,21 +2071,13 @@ proc findcont {ids} {
            }
            if {$doesmatch} {
                insertmatch $l $id
-               set pi $nparents($id)
            }
-       } else {
-           set pi $nparents($id)
        }
-       if {[incr pi] >= $nparents($id)} {
-           set pi 0
-           if {[incr l] >= $numcommits} {
-               set l 0
-           }
-           if {$l == $findstartline} break
-           set id $lineid($l)
+       if {[incr l] >= $numcommits} {
+           set l 0
        }
-       set p [lindex $parents($id) $pi]
-       set ids [list $id $p]
+       if {$l == $findstartline} break
+       set id $lineid($l)
     }
     stopfindproc
     if {!$finddidsel} {
@@ -2091,6 +2179,7 @@ proc selectline {l isnew} {
     global canvy0 linespc parents nparents children
     global cflist currentid sha1entry
     global commentend idtags idline linknum
+    global mergemax
 
     $canv delete hover
     normalline
@@ -2174,11 +2263,26 @@ proc selectline {l isnew} {
     }
  
     set comment {}
-    if {[info exists parents($id)]} {
+    if {$nparents($id) > 1} {
+       set np 0
        foreach p $parents($id) {
-           append comment "Parent: [commit_descriptor $p]\n"
+           if {$np >= $mergemax} {
+               set tag mmax
+           } else {
+               set tag m$np
+           }
+           $ctext insert end "Parent: " $tag
+           appendwithlinks [commit_descriptor $p]
+           incr np
+       }
+    } else {
+       if {[info exists parents($id)]} {
+           foreach p $parents($id) {
+               append comment "Parent: [commit_descriptor $p]\n"
+           }
        }
     }
+
     if {[info exists children($id)]} {
        foreach c $children($id) {
            append comment "Child:  [commit_descriptor $c]\n"
@@ -2270,529 +2374,100 @@ proc goforw {} {
 }
 
 proc mergediff {id} {
-    global parents diffmergeid diffmergegca mergefilelist diffpindex
+    global parents diffmergeid diffopts mdifffd
+    global difffilestart
 
     set diffmergeid $id
-    set diffpindex -1
-    set diffmergegca [findgca $parents($id)]
-    if {[info exists mergefilelist($id)]} {
-       if {$mergefilelist($id) ne {}} {
-           showmergediff
-       }
-    } else {
-       contmergediff {}
-    }
-}
-
-proc findgca {ids} {
-    set gca {}
-    foreach id $ids {
-       if {$gca eq {}} {
-           set gca $id
-       } else {
-           if {[catch {
-               set gca [exec git-merge-base $gca $id]
-           } err]} {
-               return {}
-           }
-       }
-    }
-    return $gca
-}
-
-proc contmergediff {ids} {
-    global diffmergeid diffpindex parents nparents diffmergegca
-    global treediffs mergefilelist diffids treepending
-
-    # diff the child against each of the parents, and diff
-    # each of the parents against the GCA.
-    while 1 {
-       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]
-           set ids [list $p $diffmergeid]
-       }
-       if {![info exists treediffs($ids)]} {
-           set diffids $ids
-           if {![info exists treepending]} {
-               gettreediffs $ids
-           }
-           return
-       }
-    }
-
-    # If a file in some parent is different from the child and also
-    # different from the GCA, then it's interesting.
-    # If we don't have a GCA, then a file is interesting if it is
-    # different from the child in all the parents.
-    if {$diffmergegca ne {}} {
-       set files {}
-       foreach p $parents($diffmergeid) {
-           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
-               }
-           }
-       }
-       set files [lsort $files]
-    } else {
-       set p [lindex $parents($diffmergeid) 0]
-       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 $p $diffmergeid])
-           set nf {}
-           foreach f $files {
-               if {[lsearch -exact $df $f] >= 0} {
-                   lappend nf $f
-               }
-           }
-           set files $nf
-       }
-    }
-
-    set mergefilelist($diffmergeid) $files
-    if {$files ne {}} {
-       showmergediff
-    }
-}
-
-proc showmergediff {} {
-    global cflist diffmergeid mergefilelist parents
-    global diffopts diffinhunk currentfile currenthunk filelines
-    global diffblocked groupfilelast mergefds groupfilenum grouphunks
-
-    set files $mergefilelist($diffmergeid)
-    foreach f $files {
-       $cflist insert end $f
-    }
+    catch {unset difffilestart}
+    # this doesn't seem to actually affect anything...
     set env(GIT_DIFF_OPTS) $diffopts
-    set flist {}
-    catch {unset currentfile}
-    catch {unset currenthunk}
-    catch {unset filelines}
-    catch {unset groupfilenum}
-    catch {unset grouphunks}
-    set groupfilelast -1
-    foreach p $parents($diffmergeid) {
-       set cmd [list | git-diff-tree -p $p $diffmergeid]
-       set cmd [concat $cmd $mergefilelist($diffmergeid)]
-       if {[catch {set f [open $cmd r]} err]} {
-           error_popup "Error getting diffs: $err"
-           foreach f $flist {
-               catch {close $f}
-           }
-           return
-       }
-       lappend flist $f
-       set ids [list $diffmergeid $p]
-       set mergefds($ids) $f
-       set diffinhunk($ids) 0
-       set diffblocked($ids) 0
-       fconfigure $f -blocking 0
-       fileevent $f readable [list getmergediffline $f $ids $diffmergeid]
+    set cmd [concat | git-diff-tree --no-commit-id --cc $id]
+    if {[catch {set mdf [open $cmd r]} err]} {
+       error_popup "Error getting merge diffs: $err"
+       return
     }
+    fconfigure $mdf -blocking 0
+    set mdifffd($id) $mdf
+    fileevent $mdf readable [list getmergediffline $mdf $id]
+    set nextupdate [expr {[clock clicks -milliseconds] + 100}]
 }
 
-proc getmergediffline {f ids id} {
-    global diffmergeid diffinhunk diffoldlines diffnewlines
-    global currentfile currenthunk
-    global diffoldstart diffnewstart diffoldlno diffnewlno
-    global diffblocked mergefilelist
-    global noldlines nnewlines difflcounts filelines
+proc getmergediffline {mdf id} {
+    global diffmergeid ctext cflist nextupdate nparents mergemax
+    global difffilestart
 
-    set n [gets $f line]
+    set n [gets $mdf line]
     if {$n < 0} {
-       if {![eof $f]} return
-    }
-
-    if {!([info exists diffmergeid] && $diffmergeid == $id)} {
-       if {$n < 0} {
-           close $f
+       if {[eof $mdf]} {
+           close $mdf
        }
        return
     }
-
-    if {$diffinhunk($ids) != 0} {
-       set fi $currentfile($ids)
-       if {$n > 0 && [regexp {^[-+ \\]} $line match]} {
-           # continuing an existing hunk
-           set line [string range $line 1 end]
-           set p [lindex $ids 1]
-           if {$match eq "-" || $match eq " "} {
-               set filelines($p,$fi,$diffoldlno($ids)) $line
-               incr diffoldlno($ids)
-           }
-           if {$match eq "+" || $match eq " "} {
-               set filelines($id,$fi,$diffnewlno($ids)) $line
-               incr diffnewlno($ids)
-           }
-           if {$match eq " "} {
-               if {$diffinhunk($ids) == 2} {
-                   lappend difflcounts($ids) \
-                       [list $noldlines($ids) $nnewlines($ids)]
-                   set noldlines($ids) 0
-                   set diffinhunk($ids) 1
-               }
-               incr noldlines($ids)
-           } elseif {$match eq "-" || $match eq "+"} {
-               if {$diffinhunk($ids) == 1} {
-                   lappend difflcounts($ids) [list $noldlines($ids)]
-                   set noldlines($ids) 0
-                   set nnewlines($ids) 0
-                   set diffinhunk($ids) 2
-               }
-               if {$match eq "-"} {
-                   incr noldlines($ids)
-               } else {
-                   incr nnewlines($ids)
-               }
-           }
-           # and if it's \ No newline at end of line, then what?
-           return
-       }
-       # end of a hunk
-       if {$diffinhunk($ids) == 1 && $noldlines($ids) != 0} {
-           lappend difflcounts($ids) [list $noldlines($ids)]
-       } elseif {$diffinhunk($ids) == 2
-                 && ($noldlines($ids) != 0 || $nnewlines($ids) != 0)} {
-           lappend difflcounts($ids) [list $noldlines($ids) $nnewlines($ids)]
-       }
-       set currenthunk($ids) [list $currentfile($ids) \
-                                  $diffoldstart($ids) $diffnewstart($ids) \
-                                  $diffoldlno($ids) $diffnewlno($ids) \
-                                  $difflcounts($ids)]
-       set diffinhunk($ids) 0
-       # -1 = need to block, 0 = unblocked, 1 = is blocked
-       set diffblocked($ids) -1
-       processhunks
-       if {$diffblocked($ids) == -1} {
-           fileevent $f readable {}
-           set diffblocked($ids) 1
-       }
-    }
-
-    if {$n < 0} {
-       # eof
-       if {!$diffblocked($ids)} {
-           close $f
-           set currentfile($ids) [llength $mergefilelist($diffmergeid)]
-           set currenthunk($ids) [list $currentfile($ids) 0 0 0 0 {}]
-           processhunks
-       }
-    } elseif {[regexp {^diff --git a/(.*) b/} $line match fname]} {
-       # start of a new file
-       set currentfile($ids) \
-           [lsearch -exact $mergefilelist($diffmergeid) $fname]
-    } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
-                  $line match f1l f1c f2l f2c rest]} {
-       if {[info exists currentfile($ids)] && $currentfile($ids) >= 0} {
-           # start of a new hunk
-           if {$f1l == 0 && $f1c == 0} {
-               set f1l 1
-           }
-           if {$f2l == 0 && $f2c == 0} {
-               set f2l 1
-           }
-           set diffinhunk($ids) 1
-           set diffoldstart($ids) $f1l
-           set diffnewstart($ids) $f2l
-           set diffoldlno($ids) $f1l
-           set diffnewlno($ids) $f2l
-           set difflcounts($ids) {}
-           set noldlines($ids) 0
-           set nnewlines($ids) 0
-       }
-    }
-}
-
-proc processhunks {} {
-    global diffmergeid parents nparents currenthunk
-    global mergefilelist diffblocked mergefds
-    global grouphunks grouplinestart grouplineend groupfilenum
-
-    set nfiles [llength $mergefilelist($diffmergeid)]
-    while 1 {
-       set fi $nfiles
-       set lno 0
-       # look for the earliest hunk
-       foreach p $parents($diffmergeid) {
-           set ids [list $diffmergeid $p]
-           if {![info exists currenthunk($ids)]} return
-           set i [lindex $currenthunk($ids) 0]
-           set l [lindex $currenthunk($ids) 2]
-           if {$i < $fi || ($i == $fi && $l < $lno)} {
-               set fi $i
-               set lno $l
-               set pi $p
-           }
-       }
-
-       if {$fi < $nfiles} {
-           set ids [list $diffmergeid $pi]
-           set hunk $currenthunk($ids)
-           unset currenthunk($ids)
-           if {$diffblocked($ids) > 0} {
-               fileevent $mergefds($ids) readable \
-                   [list getmergediffline $mergefds($ids) $ids $diffmergeid]
-           }
-           set diffblocked($ids) 0
-
-           if {[info exists groupfilenum] && $groupfilenum == $fi
-               && $lno <= $grouplineend} {
-               # add this hunk to the pending group
-               lappend grouphunks($pi) $hunk
-               set endln [lindex $hunk 4]
-               if {$endln > $grouplineend} {
-                   set grouplineend $endln
-               }
-               continue
-           }
-       }
-
-       # succeeding stuff doesn't belong in this group, so
-       # process the group now
-       if {[info exists groupfilenum]} {
-           processgroup
-           unset groupfilenum
-           unset grouphunks
-       }
-
-       if {$fi >= $nfiles} break
-
-       # start a new group
-       set groupfilenum $fi
-       set grouphunks($pi) [list $hunk]
-       set grouplinestart $lno
-       set grouplineend [lindex $hunk 4]
+    if {![info exists diffmergeid] || $id != $diffmergeid} {
+       return
     }
-}
-
-proc processgroup {} {
-    global groupfilelast groupfilenum difffilestart
-    global mergefilelist diffmergeid ctext filelines
-    global parents diffmergeid diffoffset
-    global grouphunks grouplinestart grouplineend nparents
-    global mergemax
-
     $ctext conf -state normal
-    set id $diffmergeid
-    set f $groupfilenum
-    if {$groupfilelast != $f} {
+    if {[regexp {^diff --cc (.*)} $line match fname]} {
+       # start of a new file
        $ctext insert end "\n"
        set here [$ctext index "end - 1c"]
-       set difffilestart($f) $here
-       set mark fmark.[expr {$f + 1}]
-       $ctext mark set $mark $here
-       $ctext mark gravity $mark left
-       set header [lindex $mergefilelist($id) $f]
-       set l [expr {(78 - [string length $header]) / 2}]
+       set i [$cflist index end]
+       $ctext mark set fmark.$i $here
+       $ctext mark gravity fmark.$i left
+       set difffilestart([expr {$i-1}]) $here
+       $cflist insert end $fname
+       set l [expr {(78 - [string length $fname]) / 2}]
        set pad [string range "----------------------------------------" 1 $l]
-       $ctext insert end "$pad $header $pad\n" filesep
-       set groupfilelast $f
-       foreach p $parents($id) {
-           set diffoffset($p) 0
-       }
-    }
-
-    $ctext insert end "@@" msep
-    set nlines [expr {$grouplineend - $grouplinestart}]
-    set events {}
-    set pnum 0
-    foreach p $parents($id) {
-       set startline [expr {$grouplinestart + $diffoffset($p)}]
-       set ol $startline
-       set nl $grouplinestart
-       if {[info exists grouphunks($p)]} {
-           foreach h $grouphunks($p) {
-               set l [lindex $h 2]
-               if {$nl < $l} {
-                   for {} {$nl < $l} {incr nl} {
-                       set filelines($p,$f,$ol) $filelines($id,$f,$nl)
-                       incr ol
-                   }
-               }
-               foreach chunk [lindex $h 5] {
-                   if {[llength $chunk] == 2} {
-                       set olc [lindex $chunk 0]
-                       set nlc [lindex $chunk 1]
-                       set nnl [expr {$nl + $nlc}]
-                       lappend events [list $nl $nnl $pnum $olc $nlc]
-                       incr ol $olc
-                       set nl $nnl
-                   } else {
-                       incr ol [lindex $chunk 0]
-                       incr nl [lindex $chunk 0]
-                   }
-               }
-           }
-       }
-       if {$nl < $grouplineend} {
-           for {} {$nl < $grouplineend} {incr nl} {
-               set filelines($p,$f,$ol) $filelines($id,$f,$nl)
-               incr ol
-           }
-       }
-       set nlines [expr {$ol - $startline}]
-       $ctext insert end " -$startline,$nlines" msep
-       incr pnum
-    }
-
-    set nlines [expr {$grouplineend - $grouplinestart}]
-    $ctext insert end " +$grouplinestart,$nlines @@\n" msep
-
-    set events [lsort -integer -index 0 $events]
-    set nevents [llength $events]
-    set nmerge $nparents($diffmergeid)
-    set l $grouplinestart
-    for {set i 0} {$i < $nevents} {set i $j} {
-       set nl [lindex $events $i 0]
-       while {$l < $nl} {
-           $ctext insert end " $filelines($id,$f,$l)\n"
-           incr l
-       }
-       set e [lindex $events $i]
-       set enl [lindex $e 1]
-       set j $i
-       set active {}
-       while 1 {
-           set pnum [lindex $e 2]
-           set olc [lindex $e 3]
-           set nlc [lindex $e 4]
-           if {![info exists delta($pnum)]} {
-               set delta($pnum) [expr {$olc - $nlc}]
-               lappend active $pnum
+       $ctext insert end "$pad $fname $pad\n" filesep
+    } elseif {[regexp {^@@} $line]} {
+       $ctext insert end "$line\n" hunksep
+    } elseif {[regexp {^[0-9a-f]{40}$} $line] || [regexp {^index} $line]} {
+       # do nothing
+    } else {
+       # parse the prefix - one ' ', '-' or '+' for each parent
+       set np $nparents($id)
+       set spaces {}
+       set minuses {}
+       set pluses {}
+       set isbad 0
+       for {set j 0} {$j < $np} {incr j} {
+           set c [string range $line $j $j]
+           if {$c == " "} {
+               lappend spaces $j
+           } elseif {$c == "-"} {
+               lappend minuses $j
+           } elseif {$c == "+"} {
+               lappend pluses $j
            } else {
-               incr delta($pnum) [expr {$olc - $nlc}]
-           }
-           if {[incr j] >= $nevents} break
-           set e [lindex $events $j]
-           if {[lindex $e 0] >= $enl} break
-           if {[lindex $e 1] > $enl} {
-               set enl [lindex $e 1]
-           }
-       }
-       set nlc [expr {$enl - $l}]
-       set ncol mresult
-       set bestpn -1
-       if {[llength $active] == $nmerge - 1} {
-           # no diff for one of the parents, i.e. it's identical
-           for {set pnum 0} {$pnum < $nmerge} {incr pnum} {
-               if {![info exists delta($pnum)]} {
-                   if {$pnum < $mergemax} {
-                       lappend ncol m$pnum
-                   } else {
-                       lappend ncol mmax
-                   }
-                   break
-               }
-           }
-       } elseif {[llength $active] == $nmerge} {
-           # all parents are different, see if one is very similar
-           set bestsim 30
-           for {set pnum 0} {$pnum < $nmerge} {incr pnum} {
-               set sim [similarity $pnum $l $nlc $f \
-                            [lrange $events $i [expr {$j-1}]]]
-               if {$sim > $bestsim} {
-                   set bestsim $sim
-                   set bestpn $pnum
-               }
-           }
-           if {$bestpn >= 0} {
-               lappend ncol m$bestpn
+               set isbad 1
+               break
            }
        }
-       set pnum -1
-       foreach p $parents($id) {
-           incr pnum
-           if {![info exists delta($pnum)] || $pnum == $bestpn} continue
-           set olc [expr {$nlc + $delta($pnum)}]
-           set ol [expr {$l + $diffoffset($p)}]
-           incr diffoffset($p) $delta($pnum)
-           unset delta($pnum)
-           for {} {$olc > 0} {incr olc -1} {
-               $ctext insert end "-$filelines($p,$f,$ol)\n" m$pnum
-               incr ol
-           }
-       }
-       set endl [expr {$l + $nlc}]
-       if {$bestpn >= 0} {
-           # show this pretty much as a normal diff
-           set p [lindex $parents($id) $bestpn]
-           set ol [expr {$l + $diffoffset($p)}]
-           incr diffoffset($p) $delta($bestpn)
-           unset delta($bestpn)
-           for {set k $i} {$k < $j} {incr k} {
-               set e [lindex $events $k]
-               if {[lindex $e 2] != $bestpn} continue
-               set nl [lindex $e 0]
-               set ol [expr {$ol + $nl - $l}]
-               for {} {$l < $nl} {incr l} {
-                   $ctext insert end "+$filelines($id,$f,$l)\n" $ncol
-               }
-               set c [lindex $e 3]
-               for {} {$c > 0} {incr c -1} {
-                   $ctext insert end "-$filelines($p,$f,$ol)\n" m$bestpn
-                   incr ol
-               }
-               set nl [lindex $e 1]
-               for {} {$l < $nl} {incr l} {
-                   $ctext insert end "+$filelines($id,$f,$l)\n" mresult
-               }
-           }
+       set tags {}
+       set num {}
+       if {!$isbad && $minuses ne {} && $pluses eq {}} {
+           # line doesn't appear in result, parents in $minuses have the line
+           set num [lindex $minuses 0]
+       } elseif {!$isbad && $pluses ne {} && $minuses eq {}} {
+           # line appears in result, parents in $pluses don't have the line
+           lappend tags mresult
+           set num [lindex $spaces 0]
        }
-       for {} {$l < $endl} {incr l} {
-           $ctext insert end "+$filelines($id,$f,$l)\n" $ncol
+       if {$num ne {}} {
+           if {$num >= $mergemax} {
+               set num "max"
+           }
+           lappend tags m$num
        }
-    }
-    while {$l < $grouplineend} {
-       $ctext insert end " $filelines($id,$f,$l)\n"
-       incr l
+       $ctext insert end "$line\n" $tags
     }
     $ctext conf -state disabled
-}
-
-proc similarity {pnum l nlc f events} {
-    global diffmergeid parents diffoffset filelines
-
-    set id $diffmergeid
-    set p [lindex $parents($id) $pnum]
-    set ol [expr {$l + $diffoffset($p)}]
-    set endl [expr {$l + $nlc}]
-    set same 0
-    set diff 0
-    foreach e $events {
-       if {[lindex $e 2] != $pnum} continue
-       set nl [lindex $e 0]
-       set ol [expr {$ol + $nl - $l}]
-       for {} {$l < $nl} {incr l} {
-           incr same [string length $filelines($id,$f,$l)]
-           incr same
-       }
-       set oc [lindex $e 3]
-       for {} {$oc > 0} {incr oc -1} {
-           incr diff [string length $filelines($p,$f,$ol)]
-           incr diff
-           incr ol
-       }
-       set nl [lindex $e 1]
-       for {} {$l < $nl} {incr l} {
-           incr diff [string length $filelines($id,$f,$l)]
-           incr diff
-       }
-    }
-    for {} {$l < $endl} {incr l} {
-       incr same [string length $filelines($id,$f,$l)]
-       incr same
-    }
-    if {$same == 0} {
-       return 0
+    if {[clock clicks -milliseconds] >= $nextupdate} {
+       incr nextupdate 100
+       fileevent $mdf readable {}
+       update
+       fileevent $mdf readable [list getmergediffline $mdf $id]
     }
-    return [expr {200 * $same / (2 * $same + $diff)}]
 }
 
 proc startdiff {ids} {
@@ -2821,7 +2496,9 @@ proc gettreediffs {ids} {
     global treediff parents treepending
     set treepending $ids
     set treediff {}
-    if [catch {set gdtf [open [concat | git-diff-tree --no-commit-id -r $ids] 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]
 }
@@ -3626,80 +3303,6 @@ proc rereadrefs {} {
     }
 }
 
-proc updatecommits {rargs} {
-    global commitlisted commfd phase
-    global startmsecs nextupdate ncmupdate
-    global idtags idheads idotherrefs
-    global leftover
-    global parsed_args
-    global canv
-    global oldcommits commits
-    global parents nchildren children ncleft
-
-    set old_args $parsed_args
-    parse_args $rargs
-
-    foreach id $old_args {
-       if {![regexp {^[0-9a-f]{40}$} $id]} continue
-       if {[info exists oldref($id)]} continue
-       set oldref($id) $id
-       lappend ignoreold "^$id"
-    }
-    foreach id $parsed_args {
-       if {![regexp {^[0-9a-f]{40}$} $id]} continue
-       if {[info exists ref($id)]} continue
-       set ref($id) $id
-       lappend ignorenew "^$id"
-    }
-
-    foreach a $old_args {
-       if {![info exists ref($a)]} {
-           lappend ignorenew $a
-       }
-    }
-
-    set phase updatecommits
-    set removed_commits [split [eval exec git-rev-list $ignorenew] "\n" ]
-    if {[llength $removed_commits] > 0} {
-       $canv delete all
-       set oldcommits {}
-       foreach c $commits {
-           if {[lsearch $c $removed_commits] < 0} {
-               lappend oldcommits $c
-           } else {
-               unset commitlisted($c)
-           }
-       }
-       set commits {}
-       unset children
-       unset nchildren
-       set phase removecommits
-    }
-
-    set args {}
-    foreach a $parsed_args {
-       if {![info exists oldref($a)]} {
-           lappend args $a
-       }
-    }
-
-    readrefs
-    if [catch {
-       set commfd [open "|git-rev-list --header --topo-order --parents $ignoreold $args" r]
-    } err] {
-       puts stderr "Error executing git-rev-list: $err"
-       exit 1
-    }
-    set startmsecs [clock clicks -milliseconds]
-    set nextupdate [expr $startmsecs + 100]
-    set ncmupdate 1
-    set leftover {}
-    fconfigure $commfd -blocking 0 -translation lf
-    fileevent $commfd readable [list getcommitlines $commfd]
-    . config -cursor watch
-    settextcursor watch
-}
-
 proc showtag {tag isnew} {
     global ctext cflist tagcontents tagids linknum
 
@@ -3797,17 +3400,294 @@ proc formatdate {d} {
     return [clock format $d -format "%Y-%m-%d %H:%M:%S"]
 }
 
+# This list of encoding names and aliases is distilled from
+# http://www.iana.org/assignments/character-sets.
+# Not all of them are supported by Tcl.
+set encoding_aliases {
+    { ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ASCII
+      ISO646-US US-ASCII us IBM367 cp367 csASCII }
+    { ISO-10646-UTF-1 csISO10646UTF1 }
+    { ISO_646.basic:1983 ref csISO646basic1983 }
+    { INVARIANT csINVARIANT }
+    { ISO_646.irv:1983 iso-ir-2 irv csISO2IntlRefVersion }
+    { BS_4730 iso-ir-4 ISO646-GB gb uk csISO4UnitedKingdom }
+    { NATS-SEFI iso-ir-8-1 csNATSSEFI }
+    { NATS-SEFI-ADD iso-ir-8-2 csNATSSEFIADD }
+    { NATS-DANO iso-ir-9-1 csNATSDANO }
+    { NATS-DANO-ADD iso-ir-9-2 csNATSDANOADD }
+    { SEN_850200_B iso-ir-10 FI ISO646-FI ISO646-SE se csISO10Swedish }
+    { SEN_850200_C iso-ir-11 ISO646-SE2 se2 csISO11SwedishForNames }
+    { KS_C_5601-1987 iso-ir-149 KS_C_5601-1989 KSC_5601 korean csKSC56011987 }
+    { ISO-2022-KR csISO2022KR }
+    { EUC-KR csEUCKR }
+    { ISO-2022-JP csISO2022JP }
+    { ISO-2022-JP-2 csISO2022JP2 }
+    { JIS_C6220-1969-jp JIS_C6220-1969 iso-ir-13 katakana x0201-7
+      csISO13JISC6220jp }
+    { JIS_C6220-1969-ro iso-ir-14 jp ISO646-JP csISO14JISC6220ro }
+    { IT iso-ir-15 ISO646-IT csISO15Italian }
+    { PT iso-ir-16 ISO646-PT csISO16Portuguese }
+    { ES iso-ir-17 ISO646-ES csISO17Spanish }
+    { greek7-old iso-ir-18 csISO18Greek7Old }
+    { latin-greek iso-ir-19 csISO19LatinGreek }
+    { DIN_66003 iso-ir-21 de ISO646-DE csISO21German }
+    { NF_Z_62-010_(1973) iso-ir-25 ISO646-FR1 csISO25French }
+    { Latin-greek-1 iso-ir-27 csISO27LatinGreek1 }
+    { ISO_5427 iso-ir-37 csISO5427Cyrillic }
+    { JIS_C6226-1978 iso-ir-42 csISO42JISC62261978 }
+    { BS_viewdata iso-ir-47 csISO47BSViewdata }
+    { INIS iso-ir-49 csISO49INIS }
+    { INIS-8 iso-ir-50 csISO50INIS8 }
+    { INIS-cyrillic iso-ir-51 csISO51INISCyrillic }
+    { ISO_5427:1981 iso-ir-54 ISO5427Cyrillic1981 }
+    { ISO_5428:1980 iso-ir-55 csISO5428Greek }
+    { GB_1988-80 iso-ir-57 cn ISO646-CN csISO57GB1988 }
+    { GB_2312-80 iso-ir-58 chinese csISO58GB231280 }
+    { NS_4551-1 iso-ir-60 ISO646-NO no csISO60DanishNorwegian
+      csISO60Norwegian1 }
+    { NS_4551-2 ISO646-NO2 iso-ir-61 no2 csISO61Norwegian2 }
+    { NF_Z_62-010 iso-ir-69 ISO646-FR fr csISO69French }
+    { videotex-suppl iso-ir-70 csISO70VideotexSupp1 }
+    { PT2 iso-ir-84 ISO646-PT2 csISO84Portuguese2 }
+    { ES2 iso-ir-85 ISO646-ES2 csISO85Spanish2 }
+    { MSZ_7795.3 iso-ir-86 ISO646-HU hu csISO86Hungarian }
+    { JIS_C6226-1983 iso-ir-87 x0208 JIS_X0208-1983 csISO87JISX0208 }
+    { greek7 iso-ir-88 csISO88Greek7 }
+    { ASMO_449 ISO_9036 arabic7 iso-ir-89 csISO89ASMO449 }
+    { iso-ir-90 csISO90 }
+    { JIS_C6229-1984-a iso-ir-91 jp-ocr-a csISO91JISC62291984a }
+    { JIS_C6229-1984-b iso-ir-92 ISO646-JP-OCR-B jp-ocr-b
+      csISO92JISC62991984b }
+    { JIS_C6229-1984-b-add iso-ir-93 jp-ocr-b-add csISO93JIS62291984badd }
+    { JIS_C6229-1984-hand iso-ir-94 jp-ocr-hand csISO94JIS62291984hand }
+    { JIS_C6229-1984-hand-add iso-ir-95 jp-ocr-hand-add
+      csISO95JIS62291984handadd }
+    { JIS_C6229-1984-kana iso-ir-96 csISO96JISC62291984kana }
+    { ISO_2033-1983 iso-ir-98 e13b csISO2033 }
+    { ANSI_X3.110-1983 iso-ir-99 CSA_T500-1983 NAPLPS csISO99NAPLPS }
+    { ISO_8859-1:1987 iso-ir-100 ISO_8859-1 ISO-8859-1 latin1 l1 IBM819
+      CP819 csISOLatin1 }
+    { ISO_8859-2:1987 iso-ir-101 ISO_8859-2 ISO-8859-2 latin2 l2 csISOLatin2 }
+    { T.61-7bit iso-ir-102 csISO102T617bit }
+    { T.61-8bit T.61 iso-ir-103 csISO103T618bit }
+    { ISO_8859-3:1988 iso-ir-109 ISO_8859-3 ISO-8859-3 latin3 l3 csISOLatin3 }
+    { ISO_8859-4:1988 iso-ir-110 ISO_8859-4 ISO-8859-4 latin4 l4 csISOLatin4 }
+    { ECMA-cyrillic iso-ir-111 KOI8-E csISO111ECMACyrillic }
+    { CSA_Z243.4-1985-1 iso-ir-121 ISO646-CA csa7-1 ca csISO121Canadian1 }
+    { CSA_Z243.4-1985-2 iso-ir-122 ISO646-CA2 csa7-2 csISO122Canadian2 }
+    { CSA_Z243.4-1985-gr iso-ir-123 csISO123CSAZ24341985gr }
+    { ISO_8859-6:1987 iso-ir-127 ISO_8859-6 ISO-8859-6 ECMA-114 ASMO-708
+      arabic csISOLatinArabic }
+    { ISO_8859-6-E csISO88596E ISO-8859-6-E }
+    { ISO_8859-6-I csISO88596I ISO-8859-6-I }
+    { ISO_8859-7:1987 iso-ir-126 ISO_8859-7 ISO-8859-7 ELOT_928 ECMA-118
+      greek greek8 csISOLatinGreek }
+    { T.101-G2 iso-ir-128 csISO128T101G2 }
+    { ISO_8859-8:1988 iso-ir-138 ISO_8859-8 ISO-8859-8 hebrew
+      csISOLatinHebrew }
+    { ISO_8859-8-E csISO88598E ISO-8859-8-E }
+    { ISO_8859-8-I csISO88598I ISO-8859-8-I }
+    { CSN_369103 iso-ir-139 csISO139CSN369103 }
+    { JUS_I.B1.002 iso-ir-141 ISO646-YU js yu csISO141JUSIB1002 }
+    { ISO_6937-2-add iso-ir-142 csISOTextComm }
+    { IEC_P27-1 iso-ir-143 csISO143IECP271 }
+    { ISO_8859-5:1988 iso-ir-144 ISO_8859-5 ISO-8859-5 cyrillic
+      csISOLatinCyrillic }
+    { JUS_I.B1.003-serb iso-ir-146 serbian csISO146Serbian }
+    { JUS_I.B1.003-mac macedonian iso-ir-147 csISO147Macedonian }
+    { ISO_8859-9:1989 iso-ir-148 ISO_8859-9 ISO-8859-9 latin5 l5 csISOLatin5 }
+    { greek-ccitt iso-ir-150 csISO150 csISO150GreekCCITT }
+    { NC_NC00-10:81 cuba iso-ir-151 ISO646-CU csISO151Cuba }
+    { ISO_6937-2-25 iso-ir-152 csISO6937Add }
+    { GOST_19768-74 ST_SEV_358-88 iso-ir-153 csISO153GOST1976874 }
+    { ISO_8859-supp iso-ir-154 latin1-2-5 csISO8859Supp }
+    { ISO_10367-box iso-ir-155 csISO10367Box }
+    { ISO-8859-10 iso-ir-157 l6 ISO_8859-10:1992 csISOLatin6 latin6 }
+    { latin-lap lap iso-ir-158 csISO158Lap }
+    { JIS_X0212-1990 x0212 iso-ir-159 csISO159JISX02121990 }
+    { DS_2089 DS2089 ISO646-DK dk csISO646Danish }
+    { us-dk csUSDK }
+    { dk-us csDKUS }
+    { JIS_X0201 X0201 csHalfWidthKatakana }
+    { KSC5636 ISO646-KR csKSC5636 }
+    { ISO-10646-UCS-2 csUnicode }
+    { ISO-10646-UCS-4 csUCS4 }
+    { DEC-MCS dec csDECMCS }
+    { hp-roman8 roman8 r8 csHPRoman8 }
+    { macintosh mac csMacintosh }
+    { IBM037 cp037 ebcdic-cp-us ebcdic-cp-ca ebcdic-cp-wt ebcdic-cp-nl
+      csIBM037 }
+    { IBM038 EBCDIC-INT cp038 csIBM038 }
+    { IBM273 CP273 csIBM273 }
+    { IBM274 EBCDIC-BE CP274 csIBM274 }
+    { IBM275 EBCDIC-BR cp275 csIBM275 }
+    { IBM277 EBCDIC-CP-DK EBCDIC-CP-NO csIBM277 }
+    { IBM278 CP278 ebcdic-cp-fi ebcdic-cp-se csIBM278 }
+    { IBM280 CP280 ebcdic-cp-it csIBM280 }
+    { IBM281 EBCDIC-JP-E cp281 csIBM281 }
+    { IBM284 CP284 ebcdic-cp-es csIBM284 }
+    { IBM285 CP285 ebcdic-cp-gb csIBM285 }
+    { IBM290 cp290 EBCDIC-JP-kana csIBM290 }
+    { IBM297 cp297 ebcdic-cp-fr csIBM297 }
+    { IBM420 cp420 ebcdic-cp-ar1 csIBM420 }
+    { IBM423 cp423 ebcdic-cp-gr csIBM423 }
+    { IBM424 cp424 ebcdic-cp-he csIBM424 }
+    { IBM437 cp437 437 csPC8CodePage437 }
+    { IBM500 CP500 ebcdic-cp-be ebcdic-cp-ch csIBM500 }
+    { IBM775 cp775 csPC775Baltic }
+    { IBM850 cp850 850 csPC850Multilingual }
+    { IBM851 cp851 851 csIBM851 }
+    { IBM852 cp852 852 csPCp852 }
+    { IBM855 cp855 855 csIBM855 }
+    { IBM857 cp857 857 csIBM857 }
+    { IBM860 cp860 860 csIBM860 }
+    { IBM861 cp861 861 cp-is csIBM861 }
+    { IBM862 cp862 862 csPC862LatinHebrew }
+    { IBM863 cp863 863 csIBM863 }
+    { IBM864 cp864 csIBM864 }
+    { IBM865 cp865 865 csIBM865 }
+    { IBM866 cp866 866 csIBM866 }
+    { IBM868 CP868 cp-ar csIBM868 }
+    { IBM869 cp869 869 cp-gr csIBM869 }
+    { IBM870 CP870 ebcdic-cp-roece ebcdic-cp-yu csIBM870 }
+    { IBM871 CP871 ebcdic-cp-is csIBM871 }
+    { IBM880 cp880 EBCDIC-Cyrillic csIBM880 }
+    { IBM891 cp891 csIBM891 }
+    { IBM903 cp903 csIBM903 }
+    { IBM904 cp904 904 csIBBM904 }
+    { IBM905 CP905 ebcdic-cp-tr csIBM905 }
+    { IBM918 CP918 ebcdic-cp-ar2 csIBM918 }
+    { IBM1026 CP1026 csIBM1026 }
+    { EBCDIC-AT-DE csIBMEBCDICATDE }
+    { EBCDIC-AT-DE-A csEBCDICATDEA }
+    { EBCDIC-CA-FR csEBCDICCAFR }
+    { EBCDIC-DK-NO csEBCDICDKNO }
+    { EBCDIC-DK-NO-A csEBCDICDKNOA }
+    { EBCDIC-FI-SE csEBCDICFISE }
+    { EBCDIC-FI-SE-A csEBCDICFISEA }
+    { EBCDIC-FR csEBCDICFR }
+    { EBCDIC-IT csEBCDICIT }
+    { EBCDIC-PT csEBCDICPT }
+    { EBCDIC-ES csEBCDICES }
+    { EBCDIC-ES-A csEBCDICESA }
+    { EBCDIC-ES-S csEBCDICESS }
+    { EBCDIC-UK csEBCDICUK }
+    { EBCDIC-US csEBCDICUS }
+    { UNKNOWN-8BIT csUnknown8BiT }
+    { MNEMONIC csMnemonic }
+    { MNEM csMnem }
+    { VISCII csVISCII }
+    { VIQR csVIQR }
+    { KOI8-R csKOI8R }
+    { IBM00858 CCSID00858 CP00858 PC-Multilingual-850+euro }
+    { IBM00924 CCSID00924 CP00924 ebcdic-Latin9--euro }
+    { IBM01140 CCSID01140 CP01140 ebcdic-us-37+euro }
+    { IBM01141 CCSID01141 CP01141 ebcdic-de-273+euro }
+    { IBM01142 CCSID01142 CP01142 ebcdic-dk-277+euro ebcdic-no-277+euro }
+    { IBM01143 CCSID01143 CP01143 ebcdic-fi-278+euro ebcdic-se-278+euro }
+    { IBM01144 CCSID01144 CP01144 ebcdic-it-280+euro }
+    { IBM01145 CCSID01145 CP01145 ebcdic-es-284+euro }
+    { IBM01146 CCSID01146 CP01146 ebcdic-gb-285+euro }
+    { IBM01147 CCSID01147 CP01147 ebcdic-fr-297+euro }
+    { IBM01148 CCSID01148 CP01148 ebcdic-international-500+euro }
+    { IBM01149 CCSID01149 CP01149 ebcdic-is-871+euro }
+    { IBM1047 IBM-1047 }
+    { PTCP154 csPTCP154 PT154 CP154 Cyrillic-Asian }
+    { Amiga-1251 Ami1251 Amiga1251 Ami-1251 }
+    { UNICODE-1-1 csUnicode11 }
+    { CESU-8 csCESU-8 }
+    { BOCU-1 csBOCU-1 }
+    { UNICODE-1-1-UTF-7 csUnicode11UTF7 }
+    { ISO-8859-14 iso-ir-199 ISO_8859-14:1998 ISO_8859-14 latin8 iso-celtic
+      l8 }
+    { ISO-8859-15 ISO_8859-15 Latin-9 }
+    { ISO-8859-16 iso-ir-226 ISO_8859-16:2001 ISO_8859-16 latin10 l10 }
+    { GBK CP936 MS936 windows-936 }
+    { JIS_Encoding csJISEncoding }
+    { Shift_JIS MS_Kanji csShiftJIS }
+    { Extended_UNIX_Code_Packed_Format_for_Japanese csEUCPkdFmtJapanese
+      EUC-JP }
+    { Extended_UNIX_Code_Fixed_Width_for_Japanese csEUCFixWidJapanese }
+    { ISO-10646-UCS-Basic csUnicodeASCII }
+    { ISO-10646-Unicode-Latin1 csUnicodeLatin1 ISO-10646 }
+    { ISO-Unicode-IBM-1261 csUnicodeIBM1261 }
+    { ISO-Unicode-IBM-1268 csUnicodeIBM1268 }
+    { ISO-Unicode-IBM-1276 csUnicodeIBM1276 }
+    { ISO-Unicode-IBM-1264 csUnicodeIBM1264 }
+    { ISO-Unicode-IBM-1265 csUnicodeIBM1265 }
+    { ISO-8859-1-Windows-3.0-Latin-1 csWindows30Latin1 }
+    { ISO-8859-1-Windows-3.1-Latin-1 csWindows31Latin1 }
+    { ISO-8859-2-Windows-Latin-2 csWindows31Latin2 }
+    { ISO-8859-9-Windows-Latin-5 csWindows31Latin5 }
+    { Adobe-Standard-Encoding csAdobeStandardEncoding }
+    { Ventura-US csVenturaUS }
+    { Ventura-International csVenturaInternational }
+    { PC8-Danish-Norwegian csPC8DanishNorwegian }
+    { PC8-Turkish csPC8Turkish }
+    { IBM-Symbols csIBMSymbols }
+    { IBM-Thai csIBMThai }
+    { HP-Legal csHPLegal }
+    { HP-Pi-font csHPPiFont }
+    { HP-Math8 csHPMath8 }
+    { Adobe-Symbol-Encoding csHPPSMath }
+    { HP-DeskTop csHPDesktop }
+    { Ventura-Math csVenturaMath }
+    { Microsoft-Publishing csMicrosoftPublishing }
+    { Windows-31J csWindows31J }
+    { GB2312 csGB2312 }
+    { Big5 csBig5 }
+}
+
+proc tcl_encoding {enc} {
+    global encoding_aliases
+    set names [encoding names]
+    set lcnames [string tolower $names]
+    set enc [string tolower $enc]
+    set i [lsearch -exact $lcnames $enc]
+    if {$i < 0} {
+       # look for "isonnn" instead of "iso-nnn" or "iso_nnn"
+       if {[regsub {^iso[-_]} $enc iso encx]} {
+           set i [lsearch -exact $lcnames $encx]
+       }
+    }
+    if {$i < 0} {
+       foreach l $encoding_aliases {
+           set ll [string tolower $l]
+           if {[lsearch -exact $ll $enc] < 0} continue
+           # look through the aliases for one that tcl knows about
+           foreach e $ll {
+               set i [lsearch -exact $lcnames $e]
+               if {$i < 0} {
+                   if {[regsub {^iso[-_]} $e iso ex]} {
+                       set i [lsearch -exact $lcnames $ex]
+                   }
+               }
+               if {$i >= 0} break
+           }
+           break
+       }
+    }
+    if {$i >= 0} {
+       return [lindex $names $i]
+    }
+    return {}
+}
+
 # defaults...
 set datemode 0
 set diffopts "-U 5 -p"
 set wrcomcmd "git-diff-tree --stdin -p --pretty"
 
-set gitencoding ""
+set gitencoding {}
 catch {
     set gitencoding [exec git-repo-config --get i18n.commitencoding]
 }
 if {$gitencoding == ""} {
-       set gitencoding "utf-8"
+    set gitencoding "utf-8"
+}
+set tclencoding [tcl_encoding $gitencoding]
+if {$tclencoding == {}} {
+    puts stderr "Warning: encoding $gitencoding is not supported by Tcl/Tk"
 }
 
 set mainfont {Helvetica 9}