Code

Merge branch 'master' into dev
authorPaul Mackerras <paulus@samba.org>
Wed, 19 Dec 2007 23:25:50 +0000 (10:25 +1100)
committerPaul Mackerras <paulus@samba.org>
Wed, 19 Dec 2007 23:25:50 +0000 (10:25 +1100)
1  2 
gitk

diff --combined gitk
index 53106018197da8a71e93138c2cd23f9717987d1b,684e6141ebb7c6363c7c1d279807144e8f961e4e..86dd575ca71655606c1be4293d0bcd6e0cdb34ef
--- 1/gitk
--- 2/gitk
+++ b/gitk
@@@ -47,24 -47,12 +47,24 @@@ proc filereadable {fd script} 
      lappend runq [list $fd $script]
  }
  
 +proc nukefile {fd} {
 +    global runq
 +
 +    for {set i 0} {$i < [llength $runq]} {} {
 +      if {[lindex $runq $i 0] eq $fd} {
 +          set runq [lreplace $runq $i $i]
 +      } else {
 +          incr i
 +      }
 +    }
 +}
 +
  proc dorunq {} {
      global isonrunq runq
  
      set tstart [clock clicks -milliseconds]
      set t0 $tstart
 -    while {$runq ne {}} {
 +    while {[llength $runq] > 0} {
        set fd [lindex $runq 0 0]
        set script [lindex $runq 0 1]
        set repeat [eval $script]
@@@ -97,34 -85,24 +97,34 @@@ proc start_rev_list {view} 
      global viewargs viewfiles commitidx viewcomplete vnextroot
      global showlocalchanges commitinterest mainheadid
      global progressdirn progresscoords proglastnc curview
 +    global viewincl viewactive loginstance viewinstances
  
      set startmsecs [clock clicks -milliseconds]
      set commitidx($view) 0
      set viewcomplete($view) 0
 +    set viewactive($view) 1
      set vnextroot($view) 0
 -    set order "--topo-order"
 -    if {$datemode} {
 -      set order "--date-order"
 +    varcinit $view
 +
 +    set commits [eval exec git rev-parse --default HEAD --revs-only \
 +                   $viewargs($view)]
 +    set viewincl($view) {}
 +    foreach c $commits {
 +      if {![string match "^*" $c]} {
 +          lappend viewincl($view) $c
 +      }
      }
      if {[catch {
 -      set fd [open [concat | git log --no-color -z --pretty=raw $order --parents \
 -                       --boundary $viewargs($view) "--" $viewfiles($view)] r]
 +      set fd [open [concat | git log --no-color -z --pretty=raw --parents \
 +                       --boundary $commits "--" $viewfiles($view)] r]
      } err]} {
-       error_popup "Error executing git log: $err"
 -      error_popup "[mc "Error executing git rev-list:"] $err"
++      error_popup "[mc "Error executing git log:"] $err"
        exit 1
      }
 -    set commfd($view) $fd
 -    set leftover($view) {}
 +    set i [incr loginstance]
 +    set viewinstances($view) [list $i]
 +    set commfd($i) $fd
 +    set leftover($i) {}
      if {$showlocalchanges} {
        lappend commitinterest($mainheadid) {dodiffindex}
      }
      if {$tclencoding != {}} {
        fconfigure $fd -encoding $tclencoding
      }
 -    filerun $fd [list getcommitlines $fd $view]
 +    filerun $fd [list getcommitlines $fd $i $view]
-     nowbusy $view "Reading"
+     nowbusy $view [mc "Reading"]
      if {$view == $curview} {
        set progressdirn 1
        set progresscoords {0 0}
      }
  }
  
 -proc stop_rev_list {} {
 -    global commfd curview
 +proc stop_rev_list {view} {
 +    global commfd viewinstances leftover
  
 -    if {![info exists commfd($curview)]} return
 -    set fd $commfd($curview)
 -    catch {
 -      set pid [pid $fd]
 -      exec kill $pid
 +    foreach inst $viewinstances($view) {
 +      set fd $commfd($inst)
 +      catch {
 +          set pid [pid $fd]
 +          exec kill $pid
 +      }
 +      catch {close $fd}
 +      nukefile $fd
 +      unset commfd($inst)
 +      unset leftover($inst)
      }
 -    catch {close $fd}
 -    unset commfd($curview)
 +    set viewinstances($view) {}
  }
  
  proc getcommits {} {
 -    global phase canv curview
 +    global canv curview
  
 -    set phase getcommits
      initlayout
      start_rev_list $curview
-     show_status "Reading commits..."
+     show_status [mc "Reading commits..."]
  }
  
 +proc updatecommits {} {
 +    global curview viewargs viewfiles viewincl viewinstances
 +    global viewactive viewcomplete loginstance tclencoding mainheadid
 +    global varcid startmsecs commfd showneartags showlocalchanges leftover
 +
 +    if {$showlocalchanges && [commitinview $mainheadid $curview]} {
 +      dodiffindex
 +    }
 +    set view $curview
 +    set commits [exec git rev-parse --default HEAD --revs-only \
 +                   $viewargs($view)]
 +    set pos {}
 +    set neg {}
 +    foreach c $commits {
 +      if {[string match "^*" $c]} {
 +          lappend neg $c
 +      } else {
 +          if {!([info exists varcid($view,$c)] ||
 +                [lsearch -exact $viewincl($view) $c] >= 0)} {
 +              lappend pos $c
 +          }
 +      }
 +    }
 +    if {$pos eq {}} {
 +      return
 +    }
 +    foreach id $viewincl($view) {
 +      lappend neg "^$id"
 +    }
 +    set viewincl($view) [concat $viewincl($view) $pos]
 +    if {[catch {
 +      set fd [open [concat | git log --no-color -z --pretty=raw --parents \
 +                       --boundary $pos $neg "--" $viewfiles($view)] r]
 +    } err]} {
 +      error_popup "Error executing git log: $err"
 +      exit 1
 +    }
 +    if {$viewactive($view) == 0} {
 +      set startmsecs [clock clicks -milliseconds]
 +    }
 +    set i [incr loginstance]
 +    lappend viewinstances($view) $i
 +    set commfd($i) $fd
 +    set leftover($i) {}
 +    fconfigure $fd -blocking 0 -translation lf -eofchar {}
 +    if {$tclencoding != {}} {
 +      fconfigure $fd -encoding $tclencoding
 +    }
 +    filerun $fd [list getcommitlines $fd $i $view]
 +    incr viewactive($view)
 +    set viewcomplete($view) 0
 +    nowbusy $view "Reading"
 +    readrefs
 +    changedrefs
 +    if {$showneartags} {
 +      getallcommits
 +    }
 +}
 +
 +proc reloadcommits {} {
 +    global curview viewcomplete selectedline currentid thickerline
 +    global showneartags treediffs commitinterest cached_commitrow
 +    global progresscoords
 +
 +    if {!$viewcomplete($curview)} {
 +      stop_rev_list $curview
 +      set progresscoords {0 0}
 +      adjustprogress
 +    }
 +    resetvarcs $curview
 +    catch {unset selectedline}
 +    catch {unset currentid}
 +    catch {unset thickerline}
 +    catch {unset treediffs}
 +    readrefs
 +    changedrefs
 +    if {$showneartags} {
 +      getallcommits
 +    }
 +    clear_display
 +    catch {unset commitinterest}
 +    catch {unset cached_commitrow}
 +    setcanvscroll
 +    getcommits
 +}
 +
  # This makes a string representation of a positive integer which
  # sorts as a string in numerical order
  proc strrep {n} {
      return [format "z%.8x" $n]
  }
  
 -proc getcommitlines {fd view}  {
 -    global commitlisted commitinterest
 -    global leftover commfd
 -    global displayorder commitidx viewcomplete commitrow commitdata
 -    global parentlist children curview hlview
 -    global vparentlist vdisporder vcmitlisted
 -    global ordertok vnextroot idpending
 +# Procedures used in reordering commits from git log (without
 +# --topo-order) into the order for display.
 +
 +proc varcinit {view} {
 +    global varcstart vupptr vdownptr vleftptr vbackptr varctok varcrow
 +    global vtokmod varcmod vrowmod varcix vlastins
 +
 +    set varcstart($view) {{}}
 +    set vupptr($view) {0}
 +    set vdownptr($view) {0}
 +    set vleftptr($view) {0}
 +    set vbackptr($view) {0}
 +    set varctok($view) {{}}
 +    set varcrow($view) {{}}
 +    set vtokmod($view) {}
 +    set varcmod($view) 0
 +    set vrowmod($view) 0
 +    set varcix($view) {{}}
 +    set vlastins($view) {0}
 +}
 +
 +proc resetvarcs {view} {
 +    global varcid varccommits parents children vseedcount ordertok
 +
 +    foreach vid [array names varcid $view,*] {
 +      unset varcid($vid)
 +      unset children($vid)
 +      unset parents($vid)
 +    }
 +    # some commits might have children but haven't been seen yet
 +    foreach vid [array names children $view,*] {
 +      unset children($vid)
 +    }
 +    foreach va [array names varccommits $view,*] {
 +      unset varccommits($va)
 +    }
 +    foreach vd [array names vseedcount $view,*] {
 +      unset vseedcount($vd)
 +    }
 +    catch {unset ordertok}
 +}
 +
 +proc newvarc {view id} {
 +    global varcid varctok parents children datemode
 +    global vupptr vdownptr vleftptr vbackptr varcrow varcix varcstart
 +    global commitdata commitinfo vseedcount varccommits vlastins
 +
 +    set a [llength $varctok($view)]
 +    set vid $view,$id
 +    if {[llength $children($vid)] == 0 || $datemode} {
 +      if {![info exists commitinfo($id)]} {
 +          parsecommit $id $commitdata($id) 1
 +      }
 +      set cdate [lindex $commitinfo($id) 4]
 +      if {![string is integer -strict $cdate]} {
 +          set cdate 0
 +      }
 +      if {![info exists vseedcount($view,$cdate)]} {
 +          set vseedcount($view,$cdate) -1
 +      }
 +      set c [incr vseedcount($view,$cdate)]
 +      set cdate [expr {$cdate ^ 0xffffffff}]
 +      set tok "s[strrep $cdate][strrep $c]"
 +    } else {
 +      set tok {}
 +    }
 +    set ka 0
 +    if {[llength $children($vid)] > 0} {
 +      set kid [lindex $children($vid) end]
 +      set k $varcid($view,$kid)
 +      if {[string compare [lindex $varctok($view) $k] $tok] > 0} {
 +          set ki $kid
 +          set ka $k
 +          set tok [lindex $varctok($view) $k]
 +      }
 +    }
 +    if {$ka != 0} {
 +      set i [lsearch -exact $parents($view,$ki) $id]
 +      set j [expr {[llength $parents($view,$ki)] - 1 - $i}]
 +      append tok [strrep $j]
 +    }
 +    set c [lindex $vlastins($view) $ka]
 +    if {$c == 0 || [string compare $tok [lindex $varctok($view) $c]] < 0} {
 +      set c $ka
 +      set b [lindex $vdownptr($view) $ka]
 +    } else {
 +      set b [lindex $vleftptr($view) $c]
 +    }
 +    while {$b != 0 && [string compare $tok [lindex $varctok($view) $b]] >= 0} {
 +      set c $b
 +      set b [lindex $vleftptr($view) $c]
 +    }
 +    if {$c == $ka} {
 +      lset vdownptr($view) $ka $a
 +      lappend vbackptr($view) 0
 +    } else {
 +      lset vleftptr($view) $c $a
 +      lappend vbackptr($view) $c
 +    }
 +    lset vlastins($view) $ka $a
 +    lappend vupptr($view) $ka
 +    lappend vleftptr($view) $b
 +    if {$b != 0} {
 +      lset vbackptr($view) $b $a
 +    }
 +    lappend varctok($view) $tok
 +    lappend varcstart($view) $id
 +    lappend vdownptr($view) 0
 +    lappend varcrow($view) {}
 +    lappend varcix($view) {}
 +    set varccommits($view,$a) {}
 +    lappend vlastins($view) 0
 +    return $a
 +}
 +
 +proc splitvarc {p v} {
 +    global varcid varcstart varccommits varctok
 +    global vupptr vdownptr vleftptr vbackptr varcix varcrow vlastins
 +
 +    set oa $varcid($v,$p)
 +    set ac $varccommits($v,$oa)
 +    set i [lsearch -exact $varccommits($v,$oa) $p]
 +    if {$i <= 0} return
 +    set na [llength $varctok($v)]
 +    # "%" sorts before "0"...
 +    set tok "[lindex $varctok($v) $oa]%[strrep $i]"
 +    lappend varctok($v) $tok
 +    lappend varcrow($v) {}
 +    lappend varcix($v) {}
 +    set varccommits($v,$oa) [lrange $ac 0 [expr {$i - 1}]]
 +    set varccommits($v,$na) [lrange $ac $i end]
 +    lappend varcstart($v) $p
 +    foreach id $varccommits($v,$na) {
 +      set varcid($v,$id) $na
 +    }
 +    lappend vdownptr($v) [lindex $vdownptr($v) $oa]
 +    lset vdownptr($v) $oa $na
 +    lappend vupptr($v) $oa
 +    lappend vleftptr($v) 0
 +    lappend vbackptr($v) 0
 +    lappend vlastins($v) 0
 +    for {set b [lindex $vdownptr($v) $na]} {$b != 0} {set b [lindex $vleftptr($v) $b]} {
 +      lset vupptr($v) $b $na
 +    }
 +}
 +
 +proc renumbervarc {a v} {
 +    global parents children varctok varcstart varccommits
 +    global vupptr vdownptr vleftptr vbackptr vlastins varcid vtokmod datemode
 +
 +    set t1 [clock clicks -milliseconds]
 +    set todo {}
 +    set isrelated($a) 1
 +    set kidchanged($a) 1
 +    set ntot 0
 +    while {$a != 0} {
 +      if {[info exists isrelated($a)]} {
 +          lappend todo $a
 +          set id [lindex $varccommits($v,$a) end]
 +          foreach p $parents($v,$id) {
 +              if {[info exists varcid($v,$p)]} {
 +                  set isrelated($varcid($v,$p)) 1
 +              }
 +          }
 +      }
 +      incr ntot
 +      set b [lindex $vdownptr($v) $a]
 +      if {$b == 0} {
 +          while {$a != 0} {
 +              set b [lindex $vleftptr($v) $a]
 +              if {$b != 0} break
 +              set a [lindex $vupptr($v) $a]
 +          }
 +      }
 +      set a $b
 +    }
 +    foreach a $todo {
 +      if {![info exists kidchanged($a)]} continue
 +      set id [lindex $varcstart($v) $a]
 +      if {[llength $children($v,$id)] > 1} {
 +          set children($v,$id) [lsort -command [list vtokcmp $v] \
 +                                    $children($v,$id)]
 +      }
 +      set oldtok [lindex $varctok($v) $a]
 +      if {!$datemode} {
 +          set tok {}
 +      } else {
 +          set tok $oldtok
 +      }
 +      set ka 0
 +      if {[llength $children($v,$id)] > 0} {
 +          set kid [lindex $children($v,$id) end]
 +          set k $varcid($v,$kid)
 +          if {[string compare [lindex $varctok($v) $k] $tok] > 0} {
 +              set ki $kid
 +              set ka $k
 +              set tok [lindex $varctok($v) $k]
 +          }
 +      }
 +      if {$ka != 0} {
 +          set i [lsearch -exact $parents($v,$ki) $id]
 +          set j [expr {[llength $parents($v,$ki)] - 1 - $i}]
 +          append tok [strrep $j]
 +      }
 +      if {$tok eq $oldtok} {
 +          continue
 +      }
 +      set id [lindex $varccommits($v,$a) end]
 +      foreach p $parents($v,$id) {
 +          if {[info exists varcid($v,$p)]} {
 +              set kidchanged($varcid($v,$p)) 1
 +          } else {
 +              set sortkids($p) 1
 +          }
 +      }
 +      lset varctok($v) $a $tok
 +      set b [lindex $vupptr($v) $a]
 +      if {$b != $ka} {
 +          if {[string compare [lindex $varctok($v) $ka] $vtokmod($v)] < 0} {
 +              modify_arc $v $ka
 +          }
 +          if {[string compare [lindex $varctok($v) $b] $vtokmod($v)] < 0} {
 +              modify_arc $v $b
 +          }
 +          set c [lindex $vbackptr($v) $a]
 +          set d [lindex $vleftptr($v) $a]
 +          if {$c == 0} {
 +              lset vdownptr($v) $b $d
 +          } else {
 +              lset vleftptr($v) $c $d
 +          }
 +          if {$d != 0} {
 +              lset vbackptr($v) $d $c
 +          }
 +          lset vupptr($v) $a $ka
 +          set c [lindex $vlastins($v) $ka]
 +          if {$c == 0 || \
 +                  [string compare $tok [lindex $varctok($v) $c]] < 0} {
 +              set c $ka
 +              set b [lindex $vdownptr($v) $ka]
 +          } else {
 +              set b [lindex $vleftptr($v) $c]
 +          }
 +          while {$b != 0 && \
 +                    [string compare $tok [lindex $varctok($v) $b]] >= 0} {
 +              set c $b
 +              set b [lindex $vleftptr($v) $c]
 +          }
 +          if {$c == $ka} {
 +              lset vdownptr($v) $ka $a
 +              lset vbackptr($v) $a 0
 +          } else {
 +              lset vleftptr($v) $c $a
 +              lset vbackptr($v) $a $c
 +          }
 +          lset vleftptr($v) $a $b
 +          if {$b != 0} {
 +              lset vbackptr($v) $b $a
 +          }
 +          lset vlastins($v) $ka $a
 +      }
 +    }
 +    foreach id [array names sortkids] {
 +      if {[llength $children($v,$id)] > 1} {
 +          set children($v,$id) [lsort -command [list vtokcmp $v] \
 +                                    $children($v,$id)]
 +      }
 +    }
 +    set t2 [clock clicks -milliseconds]
 +    #puts "renumbervarc did [llength $todo] of $ntot arcs in [expr {$t2-$t1}]ms"
 +}
 +
 +proc fix_reversal {p a v} {
 +    global varcid varcstart varctok vupptr
 +
 +    set pa $varcid($v,$p)
 +    if {$p ne [lindex $varcstart($v) $pa]} {
 +      splitvarc $p $v
 +      set pa $varcid($v,$p)
 +    }
 +    # seeds always need to be renumbered
 +    if {[lindex $vupptr($v) $pa] == 0 ||
 +      [string compare [lindex $varctok($v) $a] \
 +           [lindex $varctok($v) $pa]] > 0} {
 +      renumbervarc $pa $v
 +    }
 +}
 +
 +proc insertrow {id p v} {
 +    global varcid varccommits parents children cmitlisted
 +    global commitidx varctok vtokmod
 +
 +    set a $varcid($v,$p)
 +    set i [lsearch -exact $varccommits($v,$a) $p]
 +    if {$i < 0} {
 +      puts "oops: insertrow can't find [shortids $p] on arc $a"
 +      return
 +    }
 +    set children($v,$id) {}
 +    set parents($v,$id) [list $p]
 +    set varcid($v,$id) $a
 +    lappend children($v,$p) $id
 +    set cmitlisted($v,$id) 1
 +    incr commitidx($v)
 +    # note we deliberately don't update varcstart($v) even if $i == 0
 +    set varccommits($v,$a) [linsert $varccommits($v,$a) $i $id]
 +    if {[string compare [lindex $varctok($v) $a] $vtokmod($v)] < 0} {
 +      modify_arc $v $a $i
 +    }
 +    drawvisible
 +}
 +
 +proc removerow {id v} {
 +    global varcid varccommits parents children commitidx
 +    global varctok vtokmod cmitlisted
 +
 +    if {[llength $parents($v,$id)] != 1} {
 +      puts "oops: removerow [shortids $id] has [llength $parents($v,$id)] parents"
 +      return
 +    }
 +    set p [lindex $parents($v,$id) 0]
 +    set a $varcid($v,$id)
 +    set i [lsearch -exact $varccommits($v,$a) $id]
 +    if {$i < 0} {
 +      puts "oops: removerow can't find [shortids $id] on arc $a"
 +      return
 +    }
 +    unset varcid($v,$id)
 +    set varccommits($v,$a) [lreplace $varccommits($v,$a) $i $i]
 +    unset parents($v,$id)
 +    unset children($v,$id)
 +    unset cmitlisted($v,$id)
 +    incr commitidx($v) -1
 +    set j [lsearch -exact $children($v,$p) $id]
 +    if {$j >= 0} {
 +      set children($v,$p) [lreplace $children($v,$p) $j $j]
 +    }
 +    if {[string compare [lindex $varctok($v) $a] $vtokmod($v)] < 0} {
 +      modify_arc $v $a $i
 +    }
 +    drawvisible
 +}
 +
 +proc vtokcmp {v a b} {
 +    global varctok varcid
 +
 +    return [string compare [lindex $varctok($v) $varcid($v,$a)] \
 +              [lindex $varctok($v) $varcid($v,$b)]]
 +}
 +
 +proc modify_arc {v a {lim {}}} {
 +    global varctok vtokmod varcmod varcrow vupptr curview vrowmod varccommits
 +    global vhighlights nhighlights fhighlights rhighlights
 +
 +    set vtokmod($v) [lindex $varctok($v) $a]
 +    set varcmod($v) $a
 +    if {$v == $curview} {
 +      while {$a != 0 && [lindex $varcrow($v) $a] eq {}} {
 +          set a [lindex $vupptr($v) $a]
 +          set lim {}
 +      }
 +      set r 0
 +      if {$a != 0} {
 +          if {$lim eq {}} {
 +              set lim [llength $varccommits($v,$a)]
 +          }
 +          set r [expr {[lindex $varcrow($v) $a] + $lim}]
 +      }
 +      set vrowmod($v) $r
 +      undolayout $r
 +    }
 +    catch {unset nhighlights}
 +    catch {unset fhighlights}
 +    catch {unset vhighlights}
 +    catch {unset rhighlights}
 +}
 +
 +proc update_arcrows {v} {
 +    global vtokmod varcmod vrowmod varcrow commitidx currentid selectedline
 +    global varcid vrownum varcorder varcix varccommits
 +    global vupptr vdownptr vleftptr varctok
 +    global displayorder parentlist curview cached_commitrow
 +
 +    set narctot [expr {[llength $varctok($v)] - 1}]
 +    set a $varcmod($v)
 +    while {$a != 0 && [lindex $varcix($v) $a] eq {}} {
 +      # go up the tree until we find something that has a row number,
 +      # or we get to a seed
 +      set a [lindex $vupptr($v) $a]
 +    }
 +    if {$a == 0} {
 +      set a [lindex $vdownptr($v) 0]
 +      if {$a == 0} return
 +      set vrownum($v) {0}
 +      set varcorder($v) [list $a]
 +      lset varcix($v) $a 0
 +      lset varcrow($v) $a 0
 +      set arcn 0
 +      set row 0
 +    } else {
 +      set arcn [lindex $varcix($v) $a]
 +      # see if a is the last arc; if so, nothing to do
 +      if {$arcn == $narctot - 1} {
 +          return
 +      }
 +      if {[llength $vrownum($v)] > $arcn + 1} {
 +          set vrownum($v) [lrange $vrownum($v) 0 $arcn]
 +          set varcorder($v) [lrange $varcorder($v) 0 $arcn]
 +      }
 +      set row [lindex $varcrow($v) $a]
 +    }
 +    if {$v == $curview} {
 +      if {[llength $displayorder] > $vrowmod($v)} {
 +          set displayorder [lrange $displayorder 0 [expr {$vrowmod($v) - 1}]]
 +          set parentlist [lrange $parentlist 0 [expr {$vrowmod($v) - 1}]]
 +      }
 +      catch {unset cached_commitrow}
 +    }
 +    while {1} {
 +      set p $a
 +      incr row [llength $varccommits($v,$a)]
 +      # go down if possible
 +      set b [lindex $vdownptr($v) $a]
 +      if {$b == 0} {
 +          # if not, go left, or go up until we can go left
 +          while {$a != 0} {
 +              set b [lindex $vleftptr($v) $a]
 +              if {$b != 0} break
 +              set a [lindex $vupptr($v) $a]
 +          }
 +          if {$a == 0} break
 +      }
 +      set a $b
 +      incr arcn
 +      lappend vrownum($v) $row
 +      lappend varcorder($v) $a
 +      lset varcix($v) $a $arcn
 +      lset varcrow($v) $a $row
 +    }
 +    set vtokmod($v) [lindex $varctok($v) $p]
 +    set varcmod($v) $p
 +    set vrowmod($v) $row
 +    if {[info exists currentid]} {
 +      set selectedline [rowofcommit $currentid]
 +    }
 +}
 +
 +# Test whether view $v contains commit $id
 +proc commitinview {id v} {
 +    global varcid
 +
 +    return [info exists varcid($v,$id)]
 +}
 +
 +# Return the row number for commit $id in the current view
 +proc rowofcommit {id} {
 +    global varcid varccommits varcrow curview cached_commitrow
 +    global varctok vtokmod
 +
 +    if {[info exists cached_commitrow($id)]} {
 +      return $cached_commitrow($id)
 +    }
 +    set v $curview
 +    if {![info exists varcid($v,$id)]} {
 +      puts "oops rowofcommit no arc for [shortids $id]"
 +      return {}
 +    }
 +    set a $varcid($v,$id)
 +    if {[string compare [lindex $varctok($v) $a] $vtokmod($v)] > 0} {
 +      update_arcrows $v
 +    }
 +    set i [lsearch -exact $varccommits($v,$a) $id]
 +    if {$i < 0} {
 +      puts "oops didn't find commit [shortids $id] in arc $a"
 +      return {}
 +    }
 +    incr i [lindex $varcrow($v) $a]
 +    set cached_commitrow($id) $i
 +    return $i
 +}
 +
 +proc bsearch {l elt} {
 +    if {[llength $l] == 0 || $elt <= [lindex $l 0]} {
 +      return 0
 +    }
 +    set lo 0
 +    set hi [llength $l]
 +    while {$hi - $lo > 1} {
 +      set mid [expr {int(($lo + $hi) / 2)}]
 +      set t [lindex $l $mid]
 +      if {$elt < $t} {
 +          set hi $mid
 +      } elseif {$elt > $t} {
 +          set lo $mid
 +      } else {
 +          return $mid
 +      }
 +    }
 +    return $lo
 +}
 +
 +# Make sure rows $start..$end-1 are valid in displayorder and parentlist
 +proc make_disporder {start end} {
 +    global vrownum curview commitidx displayorder parentlist
 +    global varccommits varcorder parents vrowmod varcrow
 +    global d_valid_start d_valid_end
 +
 +    if {$end > $vrowmod($curview)} {
 +      update_arcrows $curview
 +    }
 +    set ai [bsearch $vrownum($curview) $start]
 +    set start [lindex $vrownum($curview) $ai]
 +    set narc [llength $vrownum($curview)]
 +    for {set r $start} {$ai < $narc && $r < $end} {incr ai} {
 +      set a [lindex $varcorder($curview) $ai]
 +      set l [llength $displayorder]
 +      set al [llength $varccommits($curview,$a)]
 +      if {$l < $r + $al} {
 +          if {$l < $r} {
 +              set pad [ntimes [expr {$r - $l}] {}]
 +              set displayorder [concat $displayorder $pad]
 +              set parentlist [concat $parentlist $pad]
 +          } elseif {$l > $r} {
 +              set displayorder [lrange $displayorder 0 [expr {$r - 1}]]
 +              set parentlist [lrange $parentlist 0 [expr {$r - 1}]]
 +          }
 +          foreach id $varccommits($curview,$a) {
 +              lappend displayorder $id
 +              lappend parentlist $parents($curview,$id)
 +          }
 +      } elseif {[lindex $displayorder $r] eq {}} {
 +          set i $r
 +          foreach id $varccommits($curview,$a) {
 +              lset displayorder $i $id
 +              lset parentlist $i $parents($curview,$id)
 +              incr i
 +          }
 +      }
 +      incr r $al
 +    }
 +}
 +
 +proc commitonrow {row} {
 +    global displayorder
 +
 +    set id [lindex $displayorder $row]
 +    if {$id eq {}} {
 +      make_disporder $row [expr {$row + 1}]
 +      set id [lindex $displayorder $row]
 +    }
 +    return $id
 +}
 +
 +proc closevarcs {v} {
 +    global varctok varccommits varcid parents children
 +    global cmitlisted commitidx commitinterest vtokmod
 +
 +    set missing_parents 0
 +    set scripts {}
 +    set narcs [llength $varctok($v)]
 +    for {set a 1} {$a < $narcs} {incr a} {
 +      set id [lindex $varccommits($v,$a) end]
 +      foreach p $parents($v,$id) {
 +          if {[info exists varcid($v,$p)]} continue
 +          # add p as a new commit
 +          incr missing_parents
 +          set cmitlisted($v,$p) 0
 +          set parents($v,$p) {}
 +          if {[llength $children($v,$p)] == 1 &&
 +              [llength $parents($v,$id)] == 1} {
 +              set b $a
 +          } else {
 +              set b [newvarc $v $p]
 +          }
 +          set varcid($v,$p) $b
 +          if {[string compare [lindex $varctok($v) $b] $vtokmod($v)] < 0} {
 +              modify_arc $v $b
 +          }
 +          lappend varccommits($v,$b) $p
 +          incr commitidx($v)
 +          if {[info exists commitinterest($p)]} {
 +              foreach script $commitinterest($p) {
 +                  lappend scripts [string map [list "%I" $p] $script]
 +              }
 +              unset commitinterest($id)
 +          }
 +      }
 +    }
 +    if {$missing_parents > 0} {
 +      foreach s $scripts {
 +          eval $s
 +      }
 +    }
 +}
 +
 +proc getcommitlines {fd inst view}  {
 +    global cmitlisted commitinterest leftover
 +    global commitidx commitdata datemode
 +    global parents children curview hlview
 +    global vnextroot idpending ordertok
 +    global varccommits varcid varctok vtokmod
  
      set stuff [read $fd 500000]
      # git log doesn't terminate the last commit with a null...
 -    if {$stuff == {} && $leftover($view) ne {} && [eof $fd]} {
 +    if {$stuff == {} && $leftover($inst) ne {} && [eof $fd]} {
        set stuff "\0"
      }
      if {$stuff == {}} {
        if {![eof $fd]} {
            return 1
        }
 -      # Check if we have seen any ids listed as parents that haven't
 -      # appeared in the list
 -      foreach vid [array names idpending "$view,*"] {
 -          # should only get here if git log is buggy
 -          set id [lindex [split $vid ","] 1]
 -          set commitrow($vid) $commitidx($view)
 -          incr commitidx($view)
 -          if {$view == $curview} {
 -              lappend parentlist {}
 -              lappend displayorder $id
 -              lappend commitlisted 0
 -          } else {
 -              lappend vparentlist($view) {}
 -              lappend vdisporder($view) $id
 -              lappend vcmitlisted($view) 0
 -          }
 +      global commfd viewcomplete viewactive viewname progresscoords
 +      global viewinstances
 +      unset commfd($inst)
 +      set i [lsearch -exact $viewinstances($view) $inst]
 +      if {$i >= 0} {
 +          set viewinstances($view) [lreplace $viewinstances($view) $i $i]
        }
 -      set viewcomplete($view) 1
 -      global viewname progresscoords
 -      unset commfd($view)
 -      notbusy $view
 -      set progresscoords {0 0}
 -      adjustprogress
        # set it blocking so we wait for the process to terminate
        fconfigure $fd -blocking 1
        if {[catch {close $fd} err]} {
            }
            error_popup $err
        }
 +      if {[incr viewactive($view) -1] <= 0} {
 +          set viewcomplete($view) 1
 +          # Check if we have seen any ids listed as parents that haven't
 +          # appeared in the list
 +          closevarcs $view
 +          notbusy $view
 +          set progresscoords {0 0}
 +          adjustprogress
 +      }
        if {$view == $curview} {
            run chewcommits $view
        }
      }
      set start 0
      set gotsome 0
 +    set scripts {}
      while 1 {
        set i [string first "\0" $stuff $start]
        if {$i < 0} {
 -          append leftover($view) [string range $stuff $start end]
 +          append leftover($inst) [string range $stuff $start end]
            break
        }
        if {$start == 0} {
 -          set cmit $leftover($view)
 +          set cmit $leftover($inst)
            append cmit [string range $stuff 0 [expr {$i - 1}]]
 -          set leftover($view) {}
 +          set leftover($inst) {}
        } else {
            set cmit [string range $stuff $start [expr {$i - 1}]]
        }
            if {[string length $shortcmit] > 80} {
                set shortcmit "[string range $shortcmit 0 80]..."
            }
-           error_popup "Can't parse git log output: {$shortcmit}"
+           error_popup "[mc "Can't parse git log output:"] {$shortcmit}"
            exit 1
        }
        set id [lindex $ids 0]
 -      if {![info exists ordertok($view,$id)]} {
 -          set otok "o[strrep $vnextroot($view)]"
 -          incr vnextroot($view)
 -          set ordertok($view,$id) $otok
 -      } else {
 -          set otok $ordertok($view,$id)
 -          unset idpending($view,$id)
 -      }
 +      set vid $view,$id
 +      if {!$listed && [info exists parents($vid)]} continue
        if {$listed} {
            set olds [lrange $ids 1 end]
 -          if {[llength $olds] == 1} {
 -              set p [lindex $olds 0]
 -              lappend children($view,$p) $id
 -              if {![info exists ordertok($view,$p)]} {
 -                  set ordertok($view,$p) $ordertok($view,$id)
 -                  set idpending($view,$p) 1
 -              }
 -          } else {
 -              set i 0
 -              foreach p $olds {
 -                  if {$i == 0 || [lsearch -exact $olds $p] >= $i} {
 -                      lappend children($view,$p) $id
 -                  }
 -                  if {![info exists ordertok($view,$p)]} {
 -                      set ordertok($view,$p) "$otok[strrep $i]]"
 -                      set idpending($view,$p) 1
 -                  }
 -                  incr i
 -              }
 -          }
        } else {
            set olds {}
        }
 -      if {![info exists children($view,$id)]} {
 -          set children($view,$id) {}
 -      }
        set commitdata($id) [string range $cmit [expr {$j + 1}] end]
 -      set commitrow($view,$id) $commitidx($view)
 -      incr commitidx($view)
 -      if {$view == $curview} {
 -          lappend parentlist $olds
 -          lappend displayorder $id
 -          lappend commitlisted $listed
 -      } else {
 -          lappend vparentlist($view) $olds
 -          lappend vdisporder($view) $id
 -          lappend vcmitlisted($view) $listed
 +      set cmitlisted($vid) $listed
 +      set parents($vid) $olds
 +      set a 0
 +      if {![info exists children($vid)]} {
 +          set children($vid) {}
 +      } elseif {[llength $children($vid)] == 1} {
 +          set k [lindex $children($vid) 0]
 +          if {[llength $parents($view,$k)] == 1 &&
 +              (!$datemode ||
 +               $varcid($view,$k) == [llength $varctok($view)] - 1)} {
 +              set a $varcid($view,$k)
 +          }
 +      }
 +      if {$a == 0} {
 +          # new arc
 +          set a [newvarc $view $id]
 +      }
 +      set varcid($vid) $a
 +      if {[string compare [lindex $varctok($view) $a] $vtokmod($view)] < 0} {
 +          modify_arc $view $a
 +      }
 +      lappend varccommits($view,$a) $id
 +
 +      set i 0
 +      foreach p $olds {
 +          if {$i == 0 || [lsearch -exact $olds $p] >= $i} {
 +              set vp $view,$p
 +              if {[llength [lappend children($vp) $id]] > 1 &&
 +                  [vtokcmp $view [lindex $children($vp) end-1] $id] > 0} {
 +                  set children($vp) [lsort -command [list vtokcmp $view] \
 +                                         $children($vp)]
 +                  catch {unset ordertok}
 +              }
 +              if {[info exists varcid($view,$p)]} {
 +                  fix_reversal $p $a $view
 +              }
 +          }
 +          incr i
        }
 +
 +      incr commitidx($view)
        if {[info exists commitinterest($id)]} {
            foreach script $commitinterest($id) {
 -              eval [string map [list "%I" $id] $script]
 +              lappend scripts [string map [list "%I" $id] $script]
            }
            unset commitinterest($id)
        }
      }
      if {$gotsome} {
        run chewcommits $view
 +      foreach s $scripts {
 +          eval $s
 +      }
        if {$view == $curview} {
            # update progress bar
            global progressdirn progresscoords proglastnc
  
  proc chewcommits {view} {
      global curview hlview viewcomplete
 -    global selectedline pending_select
 +    global pending_select
  
      if {$view == $curview} {
        layoutmore
        if {$viewcomplete($view)} {
 -          global displayorder commitidx phase
 +          global commitidx varctok
            global numcommits startmsecs
 +          global mainheadid commitinfo nullid
  
            if {[info exists pending_select]} {
                set row [first_real_row]
            if {$commitidx($curview) > 0} {
                #set ms [expr {[clock clicks -milliseconds] - $startmsecs}]
                #puts "overall $ms ms for $numcommits commits"
 +              #puts "[llength $varctok($view)] arcs, $commitidx($view) commits"
            } else {
-               show_status "No commits selected"
+               show_status [mc "No commits selected"]
            }
            notbusy layout
 -          set phase {}
        }
      }
      if {[info exists hlview] && $view == $hlview} {
@@@ -1093,6 -389,35 +1093,6 @@@ proc readcommit {id} 
      parsecommit $id $contents 0
  }
  
 -proc updatecommits {} {
 -    global viewdata curview phase displayorder ordertok idpending
 -    global children commitrow selectedline thickerline showneartags
 -
 -    if {$phase ne {}} {
 -      stop_rev_list
 -      set phase {}
 -    }
 -    set n $curview
 -    foreach id $displayorder {
 -      catch {unset children($n,$id)}
 -      catch {unset commitrow($n,$id)}
 -      catch {unset ordertok($n,$id)}
 -    }
 -    foreach vid [array names idpending "$n,*"] {
 -      unset idpending($vid)
 -    }
 -    set curview -1
 -    catch {unset selectedline}
 -    catch {unset thickerline}
 -    catch {unset viewdata($n)}
 -    readrefs
 -    changedrefs
 -    if {$showneartags} {
 -      getallcommits
 -    }
 -    showview $n
 -}
 -
  proc parsecommit {id contents listed} {
      global commitinfo cdate
  
@@@ -1158,7 -483,7 +1158,7 @@@ proc getcommit {id} 
      } else {
        readcommit $id
        if {![info exists commitinfo($id)]} {
-           set commitinfo($id) {"No commit information available"}
+           set commitinfo($id) [list [mc "No commit information available"]]
        }
      }
      return 1
@@@ -1219,10 -544,10 +1219,10 @@@ proc readrefs {} 
  
  # skip over fake commits
  proc first_real_row {} {
 -    global nullid nullid2 displayorder numcommits
 +    global nullid nullid2 numcommits
  
      for {set row 0} {$row < $numcommits} {incr row} {
 -      set id [lindex $displayorder $row]
 +      set id [commitonrow $row]
        if {$id ne $nullid && $id ne $nullid2} {
            break
        }
@@@ -1257,7 -582,7 +1257,7 @@@ proc removehead {id name} 
  proc show_error {w top msg} {
      message $w.m -text $msg -justify center -aspect 400
      pack $w.m -side top -fill x -padx 20 -pady 20
-     button $w.ok -text OK -command "destroy $top"
+     button $w.ok -text [mc OK] -command "destroy $top"
      pack $w.ok -side bottom -fill x
      bind $top <Visibility> "grab $top; focus $top"
      bind $top <Key-Return> "destroy $top"
@@@ -1279,9 -604,9 +1279,9 @@@ proc confirm_popup msg 
      wm transient $w .
      message $w.m -text $msg -justify center -aspect 400
      pack $w.m -side top -fill x -padx 20 -pady 20
-     button $w.ok -text OK -command "set confirm_ok 1; destroy $w"
+     button $w.ok -text [mc OK] -command "set confirm_ok 1; destroy $w"
      pack $w.ok -side left -fill x
-     button $w.cancel -text Cancel -command "destroy $w"
+     button $w.cancel -text [mc Cancel] -command "destroy $w"
      pack $w.cancel -side right -fill x
      bind $w <Visibility> "grab $w; focus $w"
      tkwait window $w
@@@ -1305,34 -630,33 +1305,34 @@@ proc makewindow {} 
      global have_tk85
  
      menu .bar
-     .bar add cascade -label "File" -menu .bar.file
+     .bar add cascade -label [mc "File"] -menu .bar.file
      .bar configure -font uifont
      menu .bar.file
-     .bar.file add command -label "Update" -command updatecommits
-     .bar.file add command -label "Reload" -command reloadcommits
-     .bar.file add command -label "Reread references" -command rereadrefs
-     .bar.file add command -label "List references" -command showrefs
-     .bar.file add command -label "Quit" -command doquit
+     .bar.file add command -label [mc "Update"] -command updatecommits
++    .bar.file add command -label [mc "Reload"] -command reloadcommits
+     .bar.file add command -label [mc "Reread references"] -command rereadrefs
+     .bar.file add command -label [mc "List references"] -command showrefs
+     .bar.file add command -label [mc "Quit"] -command doquit
      .bar.file configure -font uifont
      menu .bar.edit
-     .bar add cascade -label "Edit" -menu .bar.edit
-     .bar.edit add command -label "Preferences" -command doprefs
+     .bar add cascade -label [mc "Edit"] -menu .bar.edit
+     .bar.edit add command -label [mc "Preferences"] -command doprefs
      .bar.edit configure -font uifont
  
      menu .bar.view -font uifont
-     .bar add cascade -label "View" -menu .bar.view
-     .bar.view add command -label "New view..." -command {newview 0}
-     .bar.view add command -label "Edit view..." -command editview \
+     .bar add cascade -label [mc "View"] -menu .bar.view
+     .bar.view add command -label [mc "New view..."] -command {newview 0}
+     .bar.view add command -label [mc "Edit view..."] -command editview \
        -state disabled
-     .bar.view add command -label "Delete view" -command delview -state disabled
+     .bar.view add command -label [mc "Delete view"] -command delview -state disabled
      .bar.view add separator
-     .bar.view add radiobutton -label "All files" -command {showview 0} \
+     .bar.view add radiobutton -label [mc "All files"] -command {showview 0} \
        -variable selectedview -value 0
  
      menu .bar.help
-     .bar add cascade -label "Help" -menu .bar.help
-     .bar.help add command -label "About gitk" -command about
-     .bar.help add command -label "Key bindings" -command keys
+     .bar add cascade -label [mc "Help"] -menu .bar.help
+     .bar.help add command -label [mc "About gitk"] -command about
+     .bar.help add command -label [mc "Key bindings"] -command keys
      .bar.help configure -font uifont
      . configure -menu .bar
  
      set sha1entry .tf.bar.sha1
      set entries $sha1entry
      set sha1but .tf.bar.sha1label
-     button $sha1but -text "SHA1 ID: " -state disabled -relief flat \
+     button $sha1but -text [mc "SHA1 ID: "] -state disabled -relief flat \
        -command gotocommit -width 8 -font uifont
      $sha1but conf -disabledforeground [$sha1but cget -foreground]
      pack .tf.bar.sha1label -side left
      set progupdatepending 0
  
      # build up the bottom bar of upper window
-     label .tf.lbar.flabel -text "Find " -font uifont
-     button .tf.lbar.fnext -text "next" -command {dofind 1 1} -font uifont
-     button .tf.lbar.fprev -text "prev" -command {dofind -1 1} -font uifont
-     label .tf.lbar.flab2 -text " commit " -font uifont
+     label .tf.lbar.flabel -text "[mc "Find"] " -font uifont
+     button .tf.lbar.fnext -text [mc "next"] -command {dofind 1 1} -font uifont
+     button .tf.lbar.fprev -text [mc "prev"] -command {dofind -1 1} -font uifont
+     label .tf.lbar.flab2 -text " [mc "commit"] " -font uifont
      pack .tf.lbar.flabel .tf.lbar.fnext .tf.lbar.fprev .tf.lbar.flab2 \
        -side left -fill y
-     set gdttype "containing:"
+     set gdttype [mc "containing:"]
      set gm [tk_optionMenu .tf.lbar.gdttype gdttype \
-               "containing:" \
-               "touching paths:" \
-               "adding/removing string:"]
+               [mc "containing:"] \
+               [mc "touching paths:"] \
+               [mc "adding/removing string:"]]
      trace add variable gdttype write gdttype_change
      $gm conf -font uifont
      .tf.lbar.gdttype conf -font uifont
      lappend entries $fstring
      entry $fstring -width 30 -font textfont -textvariable findstring
      trace add variable findstring write find_change
-     set findtype Exact
+     set findtype [mc "Exact"]
      set findtypemenu [tk_optionMenu .tf.lbar.findtype \
-                     findtype Exact IgnCase Regexp]
+                     findtype [mc "Exact"] [mc "IgnCase"] [mc "Regexp"]]
      trace add variable findtype write findcom_change
      .tf.lbar.findtype configure -font uifont
      .tf.lbar.findtype.menu configure -font uifont
-     set findloc "All fields"
-     tk_optionMenu .tf.lbar.findloc findloc "All fields" Headline \
-       Comments Author Committer
+     set findloc [mc "All fields"]
+     tk_optionMenu .tf.lbar.findloc findloc [mc "All fields"] [mc "Headline"] \
+       [mc "Comments"] [mc "Author"] [mc "Committer"]
      trace add variable findloc write find_change
      .tf.lbar.findloc configure -font uifont
      .tf.lbar.findloc.menu configure -font uifont
      frame .bleft.top
      frame .bleft.mid
  
-     button .bleft.top.search -text "Search" -command dosearch \
+     button .bleft.top.search -text [mc "Search"] -command dosearch \
        -font uifont
      pack .bleft.top.search -side left -padx 5
      set sstring .bleft.top.sstring
      lappend entries $sstring
      trace add variable searchstring write incrsearch
      pack $sstring -side left -expand 1 -fill x
-     radiobutton .bleft.mid.diff -text "Diff" -font uifont \
+     radiobutton .bleft.mid.diff -text [mc "Diff"] -font uifont \
        -command changediffdisp -variable diffelide -value {0 0}
-     radiobutton .bleft.mid.old -text "Old version" -font uifont \
+     radiobutton .bleft.mid.old -text [mc "Old version"] -font uifont \
        -command changediffdisp -variable diffelide -value {0 1}
-     radiobutton .bleft.mid.new -text "New version" -font uifont \
+     radiobutton .bleft.mid.new -text [mc "New version"] -font uifont \
        -command changediffdisp -variable diffelide -value {1 0}
-     label .bleft.mid.labeldiffcontext -text "      Lines of context: " \
+     label .bleft.mid.labeldiffcontext -text "      [mc "Lines of context"]: " \
        -font uifont
      pack .bleft.mid.diff .bleft.mid.old .bleft.mid.new -side left
      spinbox .bleft.mid.diffcontext -width 5 -font textfont \
      # lower right
      frame .bright
      frame .bright.mode
-     radiobutton .bright.mode.patch -text "Patch" \
+     radiobutton .bright.mode.patch -text [mc "Patch"] \
        -command reselectline -variable cmitmode -value "patch"
      .bright.mode.patch configure -font uifont
-     radiobutton .bright.mode.tree -text "Tree" \
+     radiobutton .bright.mode.tree -text [mc "Tree"] \
        -command reselectline -variable cmitmode -value "tree"
      .bright.mode.tree configure -font uifont
      grid .bright.mode.patch .bright.mode.tree -sticky ew
  
      set rowctxmenu .rowctxmenu
      menu $rowctxmenu -tearoff 0
-     $rowctxmenu add command -label "Diff this -> selected" \
+     $rowctxmenu add command -label [mc "Diff this -> selected"] \
        -command {diffvssel 0}
-     $rowctxmenu add command -label "Diff selected -> this" \
+     $rowctxmenu add command -label [mc "Diff selected -> this"] \
        -command {diffvssel 1}
-     $rowctxmenu add command -label "Make patch" -command mkpatch
-     $rowctxmenu add command -label "Create tag" -command mktag
-     $rowctxmenu add command -label "Write commit to file" -command writecommit
-     $rowctxmenu add command -label "Create new branch" -command mkbranch
-     $rowctxmenu add command -label "Cherry-pick this commit" \
+     $rowctxmenu add command -label [mc "Make patch"] -command mkpatch
+     $rowctxmenu add command -label [mc "Create tag"] -command mktag
+     $rowctxmenu add command -label [mc "Write commit to file"] -command writecommit
+     $rowctxmenu add command -label [mc "Create new branch"] -command mkbranch
+     $rowctxmenu add command -label [mc "Cherry-pick this commit"] \
        -command cherrypick
-     $rowctxmenu add command -label "Reset HEAD branch to here" \
+     $rowctxmenu add command -label [mc "Reset HEAD branch to here"] \
        -command resethead
  
      set fakerowmenu .fakerowmenu
      menu $fakerowmenu -tearoff 0
-     $fakerowmenu add command -label "Diff this -> selected" \
+     $fakerowmenu add command -label [mc "Diff this -> selected"] \
        -command {diffvssel 0}
-     $fakerowmenu add command -label "Diff selected -> this" \
+     $fakerowmenu add command -label [mc "Diff selected -> this"] \
        -command {diffvssel 1}
-     $fakerowmenu add command -label "Make patch" -command mkpatch
- #    $fakerowmenu add command -label "Commit" -command {mkcommit 0}
- #    $fakerowmenu add command -label "Commit all" -command {mkcommit 1}
- #    $fakerowmenu add command -label "Revert local changes" -command revertlocal
+     $fakerowmenu add command -label [mc "Make patch"] -command mkpatch
+ #    $fakerowmenu add command -label [mc "Commit"] -command {mkcommit 0}
+ #    $fakerowmenu add command -label [mc "Commit all"] -command {mkcommit 1}
+ #    $fakerowmenu add command -label [mc "Revert local changes"] -command revertlocal
  
      set headctxmenu .headctxmenu
      menu $headctxmenu -tearoff 0
-     $headctxmenu add command -label "Check out this branch" \
+     $headctxmenu add command -label [mc "Check out this branch"] \
        -command cobranch
-     $headctxmenu add command -label "Remove this branch" \
+     $headctxmenu add command -label [mc "Remove this branch"] \
        -command rmbranch
  
      global flist_menu
      set flist_menu .flistctxmenu
      menu $flist_menu -tearoff 0
-     $flist_menu add command -label "Highlight this too" \
+     $flist_menu add command -label [mc "Highlight this too"] \
        -command {flist_hl 0}
-     $flist_menu add command -label "Highlight this only" \
+     $flist_menu add command -label [mc "Highlight this only"] \
        -command {flist_hl 1}
  }
  
@@@ -1953,17 -1277,17 +1953,17 @@@ proc about {} 
        return
      }
      toplevel $w
-     wm title $w "About gitk"
-     message $w.m -text {
+     wm title $w [mc "About gitk"]
+     message $w.m -text [mc "
  Gitk - a commit viewer for git
  
- Copyright Â© 2005-2007 Paul Mackerras
+ Copyright Â© 2005-2006 Paul Mackerras
  
- Use and redistribute under the terms of the GNU General Public License} \
+ Use and redistribute under the terms of the GNU General Public License"] \
            -justify center -aspect 400 -border 2 -bg white -relief groove
      pack $w.m -side top -fill x -padx 2 -pady 2
      $w.m configure -font uifont
-     button $w.ok -text Close -command "destroy $w" -default active
+     button $w.ok -text [mc "Close"] -command "destroy $w" -default active
      pack $w.ok -side bottom
      $w.ok configure -font uifont
      bind $w <Visibility> "focus $w.ok"
@@@ -1984,8 -1308,8 +1984,8 @@@ proc keys {} 
        set M1T Ctrl
      }
      toplevel $w
-     wm title $w "Gitk key bindings"
-     message $w.m -text "
+     wm title $w [mc "Gitk key bindings"]
+     message $w.m -text [mc "
  Gitk key bindings:
  
  <$M1T-Q>              Quit
@@@ -2023,11 -1347,11 +2023,11 @@@ f            Scroll diff view to next fil
  <$M1T-KP->    Decrease font size
  <$M1T-minus>  Decrease font size
  <F5>          Update
- " \
+ "] \
            -justify left -bg white -border 2 -relief groove
      pack $w.m -side top -fill both -padx 2 -pady 2
      $w.m configure -font uifont
-     button $w.ok -text Close -command "destroy $w" -default active
+     button $w.ok -text [mc "Close"] -command "destroy $w" -default active
      pack $w.ok -side bottom
      $w.ok configure -font uifont
      bind $w <Visibility> "focus $w.ok"
@@@ -2319,7 -1643,7 +2319,7 @@@ image create bitmap reficon-o -backgrou
      -data $rectdata -maskdata $rectmask
  
  proc init_flist {first} {
 -    global cflist cflist_top selectedline difffilestart
 +    global cflist cflist_top difffilestart
  
      $cflist conf -state normal
      $cflist delete 0.0 end
@@@ -2419,12 -1743,12 +2419,12 @@@ proc flist_hl {only} 
      global flist_menu_file findstring gdttype
  
      set x [shellquote $flist_menu_file]
-     if {$only || $findstring eq {} || $gdttype ne "touching paths:"} {
+     if {$only || $findstring eq {} || $gdttype ne [mc "touching paths:"]} {
        set findstring $x
      } else {
        append findstring " " $x
      }
-     set gdttype "touching paths:"
+     set gdttype [mc "touching paths:"]
  }
  
  # Functions for adding and removing shell-type quoting
@@@ -2536,7 -1860,7 +2536,7 @@@ proc newview {ishighlight} 
      set newviewname($nextviewnum) "View $nextviewnum"
      set newviewperm($nextviewnum) 0
      set newviewargs($nextviewnum) [shellarglist $revtreeargs]
-     vieweditor $top $nextviewnum "Gitk view definition"
+     vieweditor $top $nextviewnum [mc "Gitk view definition"]
  }
  
  proc editview {} {
@@@ -2561,20 -1885,20 +2561,20 @@@ proc vieweditor {top n title} 
  
      toplevel $top
      wm title $top $title
-     label $top.nl -text "Name" -font uifont
+     label $top.nl -text [mc "Name"] -font uifont
      entry $top.name -width 20 -textvariable newviewname($n) -font uifont
      grid $top.nl $top.name -sticky w -pady 5
-     checkbutton $top.perm -text "Remember this view" -variable newviewperm($n) \
+     checkbutton $top.perm -text [mc "Remember this view"] -variable newviewperm($n) \
        -font uifont
      grid $top.perm - -pady 5 -sticky w
      message $top.al -aspect 1000 -font uifont \
-       -text "Commits to include (arguments to git rev-list):"
+       -text [mc "Commits to include (arguments to git rev-list):"]
      grid $top.al - -sticky w -pady 5
      entry $top.args -width 50 -textvariable newviewargs($n) \
        -background white -font uifont
      grid $top.args - -sticky ew -padx 5
      message $top.l -aspect 1000 -font uifont \
-       -text "Enter files and directories to include, one per line:"
+       -text [mc "Enter files and directories to include, one per line:"]
      grid $top.l - -sticky w
      text $top.t -width 40 -height 10 -background white -font uifont
      if {[info exists viewfiles($n)]} {
      }
      grid $top.t - -sticky ew -padx 5
      frame $top.buts
-     button $top.buts.ok -text "OK" -command [list newviewok $top $n] \
+     button $top.buts.ok -text [mc "OK"] -command [list newviewok $top $n] \
        -font uifont
-     button $top.buts.can -text "Cancel" -command [list destroy $top] \
+     button $top.buts.can -text [mc "Cancel"] -command [list destroy $top] \
        -font uifont
      grid $top.buts.ok $top.buts.can
      grid columnconfigure $top.buts 0 -weight 1 -uniform a
@@@ -2623,7 -1947,7 +2623,7 @@@ proc newviewok {top n} 
      if {[catch {
        set newargs [shellsplit $newviewargs($n)]
      } err]} {
-       error_popup "Error in commit selection arguments: $err"
+       error_popup "[mc "Error in commit selection arguments:"] $err"
        wm raise $top
        focus $top
        return
            set viewfiles($n) $files
            set viewargs($n) $newargs
            if {$curview == $n} {
 -              run updatecommits
 +              run reloadcommits
            }
        }
      }
  }
  
  proc delview {} {
 -    global curview viewdata viewperm hlview selectedhlview
 +    global curview viewperm hlview selectedhlview
  
      if {$curview == 0} return
      if {[info exists hlview] && $hlview == $curview} {
-       set selectedhlview None
+       set selectedhlview [mc "None"]
        unset hlview
      }
      allviewmenus $curview delete
 -    set viewdata($curview) {}
      set viewperm($curview) 0
      showview 0
  }
@@@ -2691,30 -2016,52 +2691,30 @@@ proc addviewmenu {n} 
      # -command [list addvhighlight $n] -variable selectedhlview
  }
  
 -proc flatten {var} {
 -    global $var
 -
 -    set ret {}
 -    foreach i [array names $var] {
 -      lappend ret $i [set $var\($i\)]
 -    }
 -    return $ret
 -}
 -
 -proc unflatten {var l} {
 -    global $var
 -
 -    catch {unset $var}
 -    foreach {i v} $l {
 -      set $var\($i\) $v
 -    }
 -}
 -
  proc showview {n} {
 -    global curview viewdata viewfiles
 +    global curview viewfiles cached_commitrow ordertok
      global displayorder parentlist rowidlist rowisopt rowfinal
 -    global colormap rowtextx commitrow nextcolor canvxmax
 -    global numcommits commitlisted
 +    global colormap rowtextx nextcolor canvxmax
 +    global numcommits viewcomplete
      global selectedline currentid canv canvy0
      global treediffs
 -    global pending_select phase
 +    global pending_select
      global commitidx
 -    global commfd
      global selectedview selectfirst
 -    global vparentlist vdisporder vcmitlisted
      global hlview selectedhlview commitinterest
  
      if {$n == $curview} return
      set selid {}
 +    set ymax [lindex [$canv cget -scrollregion] 3]
 +    set span [$canv yview]
 +    set ytop [expr {[lindex $span 0] * $ymax}]
 +    set ybot [expr {[lindex $span 1] * $ymax}]
 +    set yscreen [expr {($ybot - $ytop) / 2}]
      if {[info exists selectedline]} {
        set selid $currentid
        set y [yc $selectedline]
 -      set ymax [lindex [$canv cget -scrollregion] 3]
 -      set span [$canv yview]
 -      set ytop [expr {[lindex $span 0] * $ymax}]
 -      set ybot [expr {[lindex $span 1] * $ymax}]
        if {$ytop < $y && $y < $ybot} {
            set yscreen [expr {$y - $ytop}]
 -      } else {
 -          set yscreen [expr {($ybot - $ytop) / 2}]
        }
      } elseif {[info exists pending_select]} {
        set selid $pending_select
      }
      unselectline
      normalline
 -    if {$curview >= 0} {
 -      set vparentlist($curview) $parentlist
 -      set vdisporder($curview) $displayorder
 -      set vcmitlisted($curview) $commitlisted
 -      if {$phase ne {} ||
 -          ![info exists viewdata($curview)] ||
 -          [lindex $viewdata($curview) 0] ne {}} {
 -          set viewdata($curview) \
 -              [list $phase $rowidlist $rowisopt $rowfinal]
 -      }
 -    }
      catch {unset treediffs}
      clear_display
      if {[info exists hlview] && $hlview == $n} {
        unset hlview
-       set selectedhlview None
+       set selectedhlview [mc "None"]
      }
      catch {unset commitinterest}
 +    catch {unset cached_commitrow}
 +    catch {unset ordertok}
  
      set curview $n
      set selectedview $n
-     .bar.view entryconf Edit* -state [expr {$n == 0? "disabled": "normal"}]
-     .bar.view entryconf Delete* -state [expr {$n == 0? "disabled": "normal"}]
+     .bar.view entryconf [mc "Edit view..."] -state [expr {$n == 0? "disabled": "normal"}]
+     .bar.view entryconf [mc "Delete view"] -state [expr {$n == 0? "disabled": "normal"}]
  
      run refill_reflist
 -    if {![info exists viewdata($n)]} {
 +    if {![info exists viewcomplete($n)]} {
        if {$selid ne {}} {
            set pending_select $selid
        }
        return
      }
  
 -    set v $viewdata($n)
 -    set phase [lindex $v 0]
 -    set displayorder $vdisporder($n)
 -    set parentlist $vparentlist($n)
 -    set commitlisted $vcmitlisted($n)
 -    set rowidlist [lindex $v 1]
 -    set rowisopt [lindex $v 2]
 -    set rowfinal [lindex $v 3]
 +    set displayorder {}
 +    set parentlist {}
 +    set rowidlist {}
 +    set rowisopt {}
 +    set rowfinal {}
      set numcommits $commitidx($n)
  
      catch {unset colormap}
      set yf 0
      set row {}
      set selectfirst 0
 -    if {$selid ne {} && [info exists commitrow($n,$selid)]} {
 -      set row $commitrow($n,$selid)
 +    if {$selid ne {} && [commitinview $selid $n]} {
 +      set row [rowofcommit $selid]
        # try to get the selected row in the same position on the screen
        set ymax [lindex [$canv cget -scrollregion] 3]
        set ytop [expr {[yc $row] - $yscreen}]
            set selectfirst 1
        }
      }
 -    if {$phase ne {}} {
 -      if {$phase eq "getcommits"} {
 +    if {!$viewcomplete($n)} {
 +      if {$numcommits == 0} {
-           show_status "Reading commits..."
-       } else {
-           run chewcommits $n
+           show_status [mc "Reading commits..."]
        }
 -      run chewcommits $n
      } elseif {$numcommits == 0} {
-       show_status "No commits selected"
+       show_status [mc "No commits selected"]
      }
  }
  
@@@ -2861,13 -2219,17 +2859,13 @@@ proc unbolden {} 
  }
  
  proc addvhighlight {n} {
 -    global hlview curview viewdata vhl_done vhighlights commitidx
 +    global hlview viewcomplete curview vhl_done vhighlights commitidx
  
      if {[info exists hlview]} {
        delvhighlight
      }
      set hlview $n
 -    if {$n != $curview && ![info exists viewdata($n)]} {
 -      set viewdata($n) [list getcommits {{}} 0 0 0]
 -      set vparentlist($n) {}
 -      set vdisporder($n) {}
 -      set vcmitlisted($n) {}
 +    if {$n != $curview && ![info exists viewcomplete($n)]} {
        start_rev_list $n
      }
      set vhl_done $commitidx($hlview)
@@@ -2886,16 -2248,22 +2884,16 @@@ proc delvhighlight {} 
  }
  
  proc vhighlightmore {} {
 -    global hlview vhl_done commitidx vhighlights
 -    global displayorder vdisporder curview
 +    global hlview vhl_done commitidx vhighlights curview
  
      set max $commitidx($hlview)
 -    if {$hlview == $curview} {
 -      set disp $displayorder
 -    } else {
 -      set disp $vdisporder($hlview)
 -    }
      set vr [visiblerows]
      set r0 [lindex $vr 0]
      set r1 [lindex $vr 1]
      for {set i $vhl_done} {$i < $max} {incr i} {
 -      set id [lindex $disp $i]
 -      if {[info exists commitrow($curview,$id)]} {
 -          set row $commitrow($curview,$id)
 +      set id [commitonrow $i $hlview]
 +      if {[commitinview $id $curview]} {
 +          set row [rowofcommit $id]
            if {$r0 <= $row && $row <= $r1} {
                if {![highlighted $row]} {
                    bolden $row mainfontbold
  }
  
  proc askvhighlight {row id} {
 -    global hlview vhighlights commitrow iddrawn
 +    global hlview vhighlights iddrawn
  
 -    if {[info exists commitrow($hlview,$id)]} {
 +    if {[commitinview $id $hlview]} {
        if {[info exists iddrawn($id)] && ![ishighlighted $row]} {
            bolden $row mainfontbold
        }
@@@ -2945,7 -2313,7 +2943,7 @@@ proc gdttype_change {name ix op} 
  
      stopfinding
      if {$findstring ne {}} {
-       if {$gdttype eq "containing:"} {
+       if {$gdttype eq [mc "containing:"]} {
            if {$highlight_files ne {}} {
                set highlight_files {}
                hfiles_change
@@@ -2968,7 -2336,7 +2966,7 @@@ proc find_change {name ix op} 
      global gdttype findstring highlight_files
  
      stopfinding
-     if {$gdttype eq "containing:"} {
+     if {$gdttype eq [mc "containing:"]} {
        findcom_change
      } else {
        if {$highlight_files ne $findstring} {
@@@ -2992,9 -2360,9 +2990,9 @@@ proc findcom_change args 
      catch {unset nhighlights}
      unbolden
      unmarkmatches
-     if {$gdttype ne "containing:" || $findstring eq {}} {
+     if {$gdttype ne [mc "containing:"] || $findstring eq {}} {
        set findpattern {}
-     } elseif {$findtype eq "Regexp"} {
+     } elseif {$findtype eq [mc "Regexp"]} {
        set findpattern $findstring
      } else {
        set e [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} \
@@@ -3020,12 -2388,12 +3018,12 @@@ proc makepatterns {l} 
  proc do_file_hl {serial} {
      global highlight_files filehighlight highlight_paths gdttype fhl_list
  
-     if {$gdttype eq "touching paths:"} {
+     if {$gdttype eq [mc "touching paths:"]} {
        if {[catch {set paths [shellsplit $highlight_files]}]} return
        set highlight_paths [makepatterns $paths]
        highlight_filelist
        set gdtargs [concat -- $paths]
-     } elseif {$gdttype eq "adding/removing string:"} {
+     } elseif {$gdttype eq [mc "adding/removing string:"]} {
        set gdtargs [list "-S$highlight_files"]
      } else {
        # must be "containing:", i.e. we're searching commit info
@@@ -3059,7 -2427,7 +3057,7 @@@ proc askfilehighlight {row id} 
  }
  
  proc readfhighlight {} {
 -    global filehighlight fhighlights commitrow curview iddrawn
 +    global filehighlight fhighlights curview iddrawn
      global fhl_list find_dirn
  
      if {![info exists filehighlight]} {
        if {$i < 0} continue
        for {set j 0} {$j < $i} {incr j} {
            set id [lindex $fhl_list $j]
 -          if {[info exists commitrow($curview,$id)]} {
 -              set fhighlights($commitrow($curview,$id)) 0
 +          if {[commitinview $id $curview]} {
 +              set fhighlights([rowofcommit $id]) 0
            }
        }
        set fhl_list [lrange $fhl_list [expr {$i+1}] end]
        if {$line eq {}} continue
 -      if {![info exists commitrow($curview,$line)]} continue
 -      set row $commitrow($curview,$line)
 +      if {![commitinview $line $curview]} continue
 +      set row [rowofcommit $line]
        if {[info exists iddrawn($line)] && ![ishighlighted $row]} {
            bolden $row mainfontbold
        }
  proc doesmatch {f} {
      global findtype findpattern
  
-     if {$findtype eq "Regexp"} {
+     if {$findtype eq [mc "Regexp"]} {
        return [regexp $findpattern $f]
-     } elseif {$findtype eq "IgnCase"} {
+     } elseif {$findtype eq [mc "IgnCase"]} {
        return [string match -nocase $findpattern $f]
      } else {
        return [string match $findpattern $f]
@@@ -3120,11 -2488,11 +3118,11 @@@ proc askfindhighlight {row id} 
      }
      set info $commitinfo($id)
      set isbold 0
-     set fldtypes {Headline Author Date Committer CDate Comments}
+     set fldtypes [list [mc Headline] [mc Author] [mc Date] [mc Committer] [mc CDate] [mc Comments]]
      foreach f $info ty $fldtypes {
-       if {($findloc eq "All fields" || $findloc eq $ty) &&
+       if {($findloc eq [mc "All fields"] || $findloc eq $ty) &&
            [doesmatch $f]} {
-           if {$ty eq "Author"} {
+           if {$ty eq [mc "Author"]} {
                set isbold 2
                break
            }
@@@ -3152,14 -2520,14 +3150,14 @@@ proc markrowmatches {row id} 
      set author [lindex $commitinfo($id) 1]
      $canv delete match$row
      $canv2 delete match$row
-     if {$findloc eq "All fields" || $findloc eq "Headline"} {
+     if {$findloc eq [mc "All fields"] || $findloc eq [mc "Headline"]} {
        set m [findmatches $headline]
        if {$m ne {}} {
            markmatches $canv $row $headline $linehtag($row) $m \
                [$canv itemcget $linehtag($row) -font] $row
        }
      }
-     if {$findloc eq "All fields" || $findloc eq "Author"} {
+     if {$findloc eq [mc "All fields"] || $findloc eq [mc "Author"]} {
        set m [findmatches $author]
        if {$m ne {}} {
            markmatches $canv2 $row $author $linentag($row) $m \
@@@ -3172,7 -2540,7 +3170,7 @@@ proc vrel_change {name ix op} 
      global highlight_related
  
      rhighlight_none
-     if {$highlight_related ne "None"} {
+     if {$highlight_related ne [mc "None"]} {
        run drawvisible
      }
  }
@@@ -3186,7 -2554,7 +3184,7 @@@ proc rhighlight_sel {a} 
      set desc_todo [list $a]
      catch {unset ancestor}
      set anc_todo [list $a]
-     if {$highlight_related ne "None"} {
+     if {$highlight_related ne [mc "None"]} {
        rhighlight_none
        run drawvisible
      }
@@@ -3200,16 -2568,16 +3198,16 @@@ proc rhighlight_none {} 
  }
  
  proc is_descendent {a} {
 -    global curview children commitrow descendent desc_todo
 +    global curview children descendent desc_todo
  
      set v $curview
 -    set la $commitrow($v,$a)
 +    set la [rowofcommit $a]
      set todo $desc_todo
      set leftover {}
      set done 0
      for {set i 0} {$i < [llength $todo]} {incr i} {
        set do [lindex $todo $i]
 -      if {$commitrow($v,$do) < $la} {
 +      if {[rowofcommit $do] < $la} {
            lappend leftover $do
            continue
        }
  }
  
  proc is_ancestor {a} {
 -    global curview parentlist commitrow ancestor anc_todo
 +    global curview parents ancestor anc_todo
  
      set v $curview
 -    set la $commitrow($v,$a)
 +    set la [rowofcommit $a]
      set todo $anc_todo
      set leftover {}
      set done 0
      for {set i 0} {$i < [llength $todo]} {incr i} {
        set do [lindex $todo $i]
 -      if {![info exists commitrow($v,$do)] || $commitrow($v,$do) > $la} {
 +      if {![commitinview $do $v] || [rowofcommit $do] > $la} {
            lappend leftover $do
            continue
        }
 -      foreach np [lindex $parentlist $commitrow($v,$do)] {
 +      foreach np $parents($v,$do) {
            if {![info exists ancestor($np)]} {
                set ancestor($np) 1
                lappend todo $np
@@@ -3269,20 -2637,20 +3267,20 @@@ proc askrelhighlight {row id} 
  
      if {![info exists selectedline]} return
      set isbold 0
-     if {$highlight_related eq "Descendent" ||
-       $highlight_related eq "Not descendent"} {
+     if {$highlight_related eq [mc "Descendent"] ||
+       $highlight_related eq [mc "Not descendent"]} {
        if {![info exists descendent($id)]} {
            is_descendent $id
        }
-       if {$descendent($id) == ($highlight_related eq "Descendent")} {
+       if {$descendent($id) == ($highlight_related eq [mc "Descendent"])} {
            set isbold 1
        }
-     } elseif {$highlight_related eq "Ancestor" ||
-             $highlight_related eq "Not ancestor"} {
+     } elseif {$highlight_related eq [mc "Ancestor"] ||
+             $highlight_related eq [mc "Not ancestor"]} {
        if {![info exists ancestor($id)]} {
            is_ancestor $id
        }
-       if {$ancestor($id) == ($highlight_related eq "Ancestor")} {
+       if {$ancestor($id) == ($highlight_related eq [mc "Ancestor"])} {
            set isbold 1
        }
      }
@@@ -3322,87 -2690,40 +3320,87 @@@ proc ntimes {n o} 
      return $ret
  }
  
 +proc ordertoken {id} {
 +    global ordertok curview varcid varcstart varctok curview parents children
 +    global nullid nullid2
 +
 +    if {[info exists ordertok($id)]} {
 +      return $ordertok($id)
 +    }
 +    set origid $id
 +    set todo {}
 +    while {1} {
 +      if {[info exists varcid($curview,$id)]} {
 +          set a $varcid($curview,$id)
 +          set p [lindex $varcstart($curview) $a]
 +      } else {
 +          set p [lindex $children($curview,$id) 0]
 +      }
 +      if {[info exists ordertok($p)]} {
 +          set tok $ordertok($p)
 +          break
 +      }
 +      if {[llength $children($curview,$p)] == 0} {
 +          # it's a root
 +          set tok [lindex $varctok($curview) $a]
 +          break
 +      }
 +      set id [lindex $children($curview,$p) 0]
 +      if {$id eq $nullid || $id eq $nullid2} {
 +          # XXX treat it as a root
 +          set tok [lindex $varctok($curview) $a]
 +          break
 +      }
 +      if {[llength $parents($curview,$id)] == 1} {
 +          lappend todo [list $p {}]
 +      } else {
 +          set j [lsearch -exact $parents($curview,$id) $p]
 +          if {$j < 0} {
 +              puts "oops didn't find [shortids $p] in parents of [shortids $id]"
 +          }
 +          lappend todo [list $p [strrep $j]]
 +      }
 +    }
 +    for {set i [llength $todo]} {[incr i -1] >= 0} {} {
 +      set p [lindex $todo $i 0]
 +      append tok [lindex $todo $i 1]
 +      set ordertok($p) $tok
 +    }
 +    set ordertok($origid) $tok
 +    return $tok
 +}
 +
  # Work out where id should go in idlist so that order-token
  # values increase from left to right
  proc idcol {idlist id {i 0}} {
 -    global ordertok curview
 -
 -    set t $ordertok($curview,$id)
 -    if {$i >= [llength $idlist] ||
 -      $t < $ordertok($curview,[lindex $idlist $i])} {
 +    set t [ordertoken $id]
 +    if {$i < 0} {
 +      set i 0
 +    }
 +    if {$i >= [llength $idlist] || $t < [ordertoken [lindex $idlist $i]]} {
        if {$i > [llength $idlist]} {
            set i [llength $idlist]
        }
 -      while {[incr i -1] >= 0 &&
 -             $t < $ordertok($curview,[lindex $idlist $i])} {}
 +      while {[incr i -1] >= 0 && $t < [ordertoken [lindex $idlist $i]]} {}
        incr i
      } else {
 -      if {$t > $ordertok($curview,[lindex $idlist $i])} {
 +      if {$t > [ordertoken [lindex $idlist $i]]} {
            while {[incr i] < [llength $idlist] &&
 -                 $t >= $ordertok($curview,[lindex $idlist $i])} {}
 +                 $t >= [ordertoken [lindex $idlist $i]]} {}
        }
      }
      return $i
  }
  
  proc initlayout {} {
 -    global rowidlist rowisopt rowfinal displayorder commitlisted
 +    global rowidlist rowisopt rowfinal displayorder parentlist
      global numcommits canvxmax canv
      global nextcolor
 -    global parentlist
      global colormap rowtextx
      global selectfirst
  
      set numcommits 0
      set displayorder {}
 -    set commitlisted {}
      set parentlist {}
      set nextcolor 0
      set rowidlist {}
@@@ -3443,20 -2764,30 +3441,20 @@@ proc visiblerows {} 
  }
  
  proc layoutmore {} {
 -    global commitidx viewcomplete numcommits
 -    global uparrowlen downarrowlen mingaplen curview
 -
 -    set show $commitidx($curview)
 -    if {$show > $numcommits || $viewcomplete($curview)} {
 -      showstuff $show $viewcomplete($curview)
 -    }
 -}
 -
 -proc showstuff {canshow last} {
 -    global numcommits commitrow pending_select selectedline curview
 -    global mainheadid displayorder selectfirst
 -    global lastscrollset commitinterest
 +    global commitidx viewcomplete curview
 +    global numcommits pending_select selectedline curview
 +    global selectfirst lastscrollset commitinterest
  
 +    set canshow $commitidx($curview)
 +    if {$canshow <= $numcommits && !$viewcomplete($curview)} return
      if {$numcommits == 0} {
 -      global phase
 -      set phase "incrdraw"
        allcanvs delete all
      }
      set r0 $numcommits
      set prev $numcommits
      set numcommits $canshow
      set t [clock clicks -milliseconds]
 -    if {$prev < 100 || $last || $t - $lastscrollset > 500} {
 +    if {$prev < 100 || $viewcomplete($curview) || $t - $lastscrollset > 500} {
        set lastscrollset $t
        setcanvscroll
      }
        drawcommits $r0 $r1
      }
      if {[info exists pending_select] &&
 -      [info exists commitrow($curview,$pending_select)] &&
 -      $commitrow($curview,$pending_select) < $numcommits} {
 -      selectline $commitrow($curview,$pending_select) 1
 +      [commitinview $pending_select $curview]} {
 +      selectline [rowofcommit $pending_select] 1
      }
      if {$selectfirst} {
        if {[info exists selectedline] || [info exists pending_select]} {
  }
  
  proc doshowlocalchanges {} {
 -    global curview mainheadid phase commitrow
 +    global curview mainheadid
  
 -    if {[info exists commitrow($curview,$mainheadid)] &&
 -      ($phase eq {} || $commitrow($curview,$mainheadid) < $numcommits - 1)} {
 +    if {[commitinview $mainheadid $curview]} {
        dodiffindex
 -    } elseif {$phase ne {}} {
 -      lappend commitinterest($mainheadid) {}
 +    } else {
 +      lappend commitinterest($mainheadid) {dodiffindex}
      }
  }
  
  proc dohidelocalchanges {} {
 -    global localfrow localirow lserial
 +    global nullid nullid2 lserial curview
  
 -    if {$localfrow >= 0} {
 -      removerow $localfrow
 -      set localfrow -1
 -      if {$localirow > 0} {
 -          incr localirow -1
 -      }
 +    if {[commitinview $nullid $curview]} {
 +      removerow $nullid $curview
      }
 -    if {$localirow >= 0} {
 -      removerow $localirow
 -      set localirow -1
 +    if {[commitinview $nullid2 $curview]} {
 +      removerow $nullid2 $curview
      }
      incr lserial
  }
  
  # spawn off a process to do git diff-index --cached HEAD
  proc dodiffindex {} {
 -    global localirow localfrow lserial showlocalchanges
 +    global lserial showlocalchanges
  
      if {!$showlocalchanges} return
      incr lserial
 -    set localfrow -1
 -    set localirow -1
      set fd [open "|git diff-index --cached HEAD" r]
      fconfigure $fd -blocking 0
      filerun $fd [list readdiffindex $fd $lserial]
  }
  
  proc readdiffindex {fd serial} {
 -    global localirow commitrow mainheadid nullid2 curview
 -    global commitinfo commitdata lserial
 +    global mainheadid nullid2 curview commitinfo commitdata lserial
  
      set isdiff 1
      if {[gets $fd line] < 0} {
      # we only need to see one line and we don't really care what it says...
      close $fd
  
 -    # now see if there are any local changes not checked in to the index
 -    if {$serial == $lserial} {
 -      set fd [open "|git diff-files" r]
 -      fconfigure $fd -blocking 0
 -      filerun $fd [list readdifffiles $fd $serial]
 +    if {$serial != $lserial} {
 +      return 0
      }
  
 -    if {$isdiff && $serial == $lserial && $localirow == -1} {
 +    # now see if there are any local changes not checked in to the index
 +    set fd [open "|git diff-files" r]
 +    fconfigure $fd -blocking 0
 +    filerun $fd [list readdifffiles $fd $serial]
 +
 +    if {$isdiff && ![commitinview $nullid2 $curview]} {
        # add the line for the changes in the index to the graph
-       set hl "Local changes checked in to index but not committed"
 -      set localirow $commitrow($curview,$mainheadid)
+       set hl [mc "Local changes checked in to index but not committed"]
        set commitinfo($nullid2) [list  $hl {} {} {} {} "    $hl\n"]
        set commitdata($nullid2) "\n    $hl\n"
 -      insertrow $localirow $nullid2
 +      insertrow $nullid2 $mainheadid $curview
 +    } elseif {!$isdiff && [commitinview $nullid2 $curview]} {
 +      removerow $nullid2 $curview
      }
      return 0
  }
  
  proc readdifffiles {fd serial} {
 -    global localirow localfrow commitrow mainheadid nullid curview
 +    global mainheadid nullid nullid2 curview
      global commitinfo commitdata lserial
  
      set isdiff 1
      # we only need to see one line and we don't really care what it says...
      close $fd
  
 -    if {$isdiff && $serial == $lserial && $localfrow == -1} {
 +    if {$serial != $lserial} {
 +      return 0
 +    }
 +
 +    if {$isdiff && ![commitinview $nullid $curview]} {
        # add the line for the local diff to the graph
-       set hl "Local uncommitted changes, not checked in to index"
 -      if {$localirow >= 0} {
 -          set localfrow $localirow
 -          incr localirow
 -      } else {
 -          set localfrow $commitrow($curview,$mainheadid)
 -      }
+       set hl [mc "Local uncommitted changes, not checked in to index"]
        set commitinfo($nullid) [list  $hl {} {} {} {} "    $hl\n"]
        set commitdata($nullid) "\n    $hl\n"
 -      insertrow $localfrow $nullid
 +      if {[commitinview $nullid2 $curview]} {
 +          set p $nullid2
 +      } else {
 +          set p $mainheadid
 +      }
 +      insertrow $nullid $p $curview
 +    } elseif {!$isdiff && [commitinview $nullid $curview]} {
 +      removerow $nullid $curview
      }
      return 0
  }
  
  proc nextuse {id row} {
 -    global commitrow curview children
 +    global curview children
  
      if {[info exists children($curview,$id)]} {
        foreach kid $children($curview,$id) {
 -          if {![info exists commitrow($curview,$kid)]} {
 +          if {![commitinview $kid $curview]} {
                return -1
            }
 -          if {$commitrow($curview,$kid) > $row} {
 -              return $commitrow($curview,$kid)
 +          if {[rowofcommit $kid] > $row} {
 +              return [rowofcommit $kid]
            }
        }
      }
 -    if {[info exists commitrow($curview,$id)]} {
 -      return $commitrow($curview,$id)
 +    if {[commitinview $id $curview]} {
 +      return [rowofcommit $id]
      }
      return -1
  }
  
  proc prevuse {id row} {
 -    global commitrow curview children
 +    global curview children
  
      set ret -1
      if {[info exists children($curview,$id)]} {
        foreach kid $children($curview,$id) {
 -          if {![info exists commitrow($curview,$kid)]} break
 -          if {$commitrow($curview,$kid) < $row} {
 -              set ret $commitrow($curview,$kid)
 +          if {![commitinview $kid $curview]} break
 +          if {[rowofcommit $kid] < $row} {
 +              set ret [rowofcommit $kid]
            }
        }
      }
  
  proc make_idlist {row} {
      global displayorder parentlist uparrowlen downarrowlen mingaplen
 -    global commitidx curview ordertok children commitrow
 +    global commitidx curview children
  
      set r [expr {$row - $mingaplen - $downarrowlen - 1}]
      if {$r < 0} {
      if {$rb > $commitidx($curview)} {
        set rb $commitidx($curview)
      }
 +    make_disporder $r [expr {$rb + 1}]
      set ids {}
      for {} {$r < $ra} {incr r} {
        set nextid [lindex $displayorder [expr {$r + 1}]]
            set rn [nextuse $p $r]
            if {$rn >= $row &&
                $rn <= $r + $downarrowlen + $mingaplen + $uparrowlen} {
 -              lappend ids [list $ordertok($curview,$p) $p]
 +              lappend ids [list [ordertoken $p] $p]
            }
        }
      }
            if {$p eq $nextid} continue
            set rn [nextuse $p $r]
            if {$rn < 0 || $rn >= $row} {
 -              lappend ids [list $ordertok($curview,$p) $p]
 +              lappend ids [list [ordertoken $p] $p]
            }
        }
      }
      set id [lindex $displayorder $row]
 -    lappend ids [list $ordertok($curview,$id) $id]
 +    lappend ids [list [ordertoken $id] $id]
      while {$r < $rb} {
        foreach p [lindex $parentlist $r] {
            set firstkid [lindex $children($curview,$p) 0]
 -          if {$commitrow($curview,$firstkid) < $row} {
 -              lappend ids [list $ordertok($curview,$p) $p]
 +          if {[rowofcommit $firstkid] < $row} {
 +              lappend ids [list [ordertoken $p] $p]
            }
        }
        incr r
        set id [lindex $displayorder $r]
        if {$id ne {}} {
            set firstkid [lindex $children($curview,$id) 0]
 -          if {$firstkid ne {} && $commitrow($curview,$firstkid) < $row} {
 -              lappend ids [list $ordertok($curview,$id) $id]
 +          if {$firstkid ne {} && [rowofcommit $firstkid] < $row} {
 +              lappend ids [list [ordertoken $id] $id]
            }
        }
      }
@@@ -3718,9 -3050,8 +3716,9 @@@ proc layoutrows {row endrow} 
      global rowidlist rowisopt rowfinal displayorder
      global uparrowlen downarrowlen maxwidth mingaplen
      global children parentlist
 -    global commitidx viewcomplete curview commitrow
 +    global commitidx viewcomplete curview
  
 +    make_disporder [expr {$row - 1}] [expr {$endrow + $uparrowlen}]
      set idlist {}
      if {$row > 0} {
        set rm1 [expr {$row - 1}]
                foreach p [lindex $parentlist $r] {
                    if {[lsearch -exact $idlist $p] >= 0} continue
                    set fk [lindex $children($curview,$p) 0]
 -                  if {$commitrow($curview,$fk) < $row} {
 +                  if {[rowofcommit $fk] < $row} {
                        set x [idcol $idlist $p $x]
                        set idlist [linsert $idlist $x $p]
                    }
                    set p [lindex $displayorder $r]
                    if {[lsearch -exact $idlist $p] < 0} {
                        set fk [lindex $children($curview,$p) 0]
 -                      if {$fk ne {} && $commitrow($curview,$fk) < $row} {
 +                      if {$fk ne {} && [rowofcommit $fk] < $row} {
                            set x [idcol $idlist $p $x]
                            set idlist [linsert $idlist $x $p]
                        }
@@@ -4000,7 -3331,7 +3998,7 @@@ proc linewidth {id} 
  }
  
  proc rowranges {id} {
 -    global commitrow curview children uparrowlen downarrowlen
 +    global curview children uparrowlen downarrowlen
      global rowidlist
  
      set kids $children($curview,$id)
      set ret {}
      lappend kids $id
      foreach child $kids {
 -      if {![info exists commitrow($curview,$child)]} break
 -      set row $commitrow($curview,$child)
 +      if {![commitinview $child $curview]} break
 +      set row [rowofcommit $child]
        if {![info exists prev]} {
            lappend ret [expr {$row + 1}]
        } else {
            if {$row <= $prevrow} {
 -              puts "oops children out of order [shortids $id] $row < [shortids $prev] $prevrow"
 +              puts "oops children of [shortids $id] out of order [shortids $child] $row <= [shortids $prev] $prevrow"
            }
            # see if the line extends the whole way from prevrow to row
            if {$row > $prevrow + $uparrowlen + $downarrowlen &&
        if {$child eq $id} {
            lappend ret $row
        }
 -      set prev $id
 +      set prev $child
        set prevrow $row
      }
      return $ret
@@@ -4297,14 -3628,14 +4295,14 @@@ proc drawlines {id} 
  }
  
  proc drawcmittext {id row col} {
 -    global linespc canv canv2 canv3 canvy0 fgcolor curview
 -    global commitlisted commitinfo rowidlist parentlist
 +    global linespc canv canv2 canv3 fgcolor curview
 +    global cmitlisted commitinfo rowidlist parentlist
      global rowtextx idpos idtags idheads idotherrefs
      global linehtag linentag linedtag selectedline
      global canvxmax boldrows boldnamerows fgcolor nullid nullid2
  
      # listed is 0 for boundary, 1 for normal, 2 for left, 3 for right
 -    set listed [lindex $commitlisted $row]
 +    set listed $cmitlisted($curview,$id)
      if {$id eq $nullid} {
        set ofill red
      } elseif {$id eq $nullid2} {
  proc drawcmitrow {row} {
      global displayorder rowidlist nrows_drawn
      global iddrawn markingmatches
 -    global commitinfo parentlist numcommits
 +    global commitinfo numcommits
      global filehighlight fhighlights findpattern nhighlights
      global hlview vhighlights
      global highlight_related rhighlights
      if {$findpattern ne {} && ![info exists nhighlights($row)]} {
        askfindhighlight $row $id
      }
-     if {$highlight_related ne "None" && ![info exists rhighlights($row)]} {
+     if {$highlight_related ne [mc "None"] && ![info exists rhighlights($row)]} {
        askrelhighlight $row $id
      }
      if {![info exists iddrawn($id)]} {
@@@ -4509,24 -3840,6 +4507,24 @@@ proc drawcommits {row {endrow {}}} 
      }
  }
  
 +proc undolayout {row} {
 +    global uparrowlen mingaplen downarrowlen
 +    global rowidlist rowisopt rowfinal need_redisplay
 +
 +    set r [expr {$row - ($uparrowlen + $mingaplen + $downarrowlen)}]
 +    if {$r < 0} {
 +      set r 0
 +    }
 +    if {[llength $rowidlist] > $r} {
 +      incr r -1
 +      set rowidlist [lrange $rowidlist 0 $r]
 +      set rowfinal [lrange $rowfinal 0 $r]
 +      set rowisopt [lrange $rowisopt 0 $r]
 +      set need_redisplay 1
 +      run drawvisible
 +    }
 +}
 +
  proc drawfrac {f0 f1} {
      global canv linespc
  
@@@ -4596,7 -3909,7 +4594,7 @@@ proc findcrossings {id} 
  
  proc assigncolor {id} {
      global colormap colors nextcolor
 -    global commitrow parentlist children children curview
 +    global parents children children curview
  
      if {[info exists colormap($id)]} return
      set ncolors [llength $colors]
      if {[llength $kids] == 1} {
        set child [lindex $kids 0]
        if {[info exists colormap($child)]
 -          && [llength [lindex $parentlist $commitrow($curview,$child)]] == 1} {
 +          && [llength $parents($curview,$child)] == 1} {
            set colormap($id) $colormap($child)
            return
        }
                && [lsearch -exact $badcolors $colormap($child)] < 0} {
                lappend badcolors $colormap($child)
            }
 -          foreach p [lindex $parentlist $commitrow($curview,$child)] {
 +          foreach p $parents($curview,$child) {
                if {[info exists colormap($p)]
                    && [lsearch -exact $badcolors $colormap($p)] < 0} {
                    lappend badcolors $colormap($p)
@@@ -4669,7 -3982,7 +4667,7 @@@ proc bindline {t id} 
  proc drawtags {id x xt y1} {
      global idtags idheads idotherrefs mainhead
      global linespc lthickness
 -    global canv commitrow rowtextx curview fgcolor bgcolor
 +    global canv rowtextx curview fgcolor bgcolor
  
      set marks {}
      set ntags 0
                       $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($commitrow($curview,$id)) [expr {$xr + $linespc}]
 +          set rowtextx([rowofcommit $id]) [expr {$xr + $linespc}]
        } else {
            # draw a head or other ref
            if {[incr nheads -1] >= 0} {
@@@ -4773,6 -4086,103 +4771,6 @@@ proc show_status {msg} 
        -tags text -fill $fgcolor
  }
  
 -# Insert a new commit as the child of the commit on row $row.
 -# The new commit will be displayed on row $row and the commits
 -# on that row and below will move down one row.
 -proc insertrow {row newcmit} {
 -    global displayorder parentlist commitlisted children
 -    global commitrow curview rowidlist rowisopt rowfinal numcommits
 -    global numcommits
 -    global selectedline commitidx ordertok
 -
 -    if {$row >= $numcommits} {
 -      puts "oops, inserting new row $row but only have $numcommits rows"
 -      return
 -    }
 -    set p [lindex $displayorder $row]
 -    set displayorder [linsert $displayorder $row $newcmit]
 -    set parentlist [linsert $parentlist $row $p]
 -    set kids $children($curview,$p)
 -    lappend kids $newcmit
 -    set children($curview,$p) $kids
 -    set children($curview,$newcmit) {}
 -    set commitlisted [linsert $commitlisted $row 1]
 -    set l [llength $displayorder]
 -    for {set r $row} {$r < $l} {incr r} {
 -      set id [lindex $displayorder $r]
 -      set commitrow($curview,$id) $r
 -    }
 -    incr commitidx($curview)
 -    set ordertok($curview,$newcmit) $ordertok($curview,$p)
 -
 -    if {$row < [llength $rowidlist]} {
 -      set idlist [lindex $rowidlist $row]
 -      if {$idlist ne {}} {
 -          if {[llength $kids] == 1} {
 -              set col [lsearch -exact $idlist $p]
 -              lset idlist $col $newcmit
 -          } else {
 -              set col [llength $idlist]
 -              lappend idlist $newcmit
 -          }
 -      }
 -      set rowidlist [linsert $rowidlist $row $idlist]
 -      set rowisopt [linsert $rowisopt $row 0]
 -      set rowfinal [linsert $rowfinal $row [lindex $rowfinal $row]]
 -    }
 -
 -    incr numcommits
 -
 -    if {[info exists selectedline] && $selectedline >= $row} {
 -      incr selectedline
 -    }
 -    redisplay
 -}
 -
 -# Remove a commit that was inserted with insertrow on row $row.
 -proc removerow {row} {
 -    global displayorder parentlist commitlisted children
 -    global commitrow curview rowidlist rowisopt rowfinal numcommits
 -    global numcommits
 -    global linesegends selectedline commitidx
 -
 -    if {$row >= $numcommits} {
 -      puts "oops, removing row $row but only have $numcommits rows"
 -      return
 -    }
 -    set rp1 [expr {$row + 1}]
 -    set id [lindex $displayorder $row]
 -    set p [lindex $parentlist $row]
 -    set displayorder [lreplace $displayorder $row $row]
 -    set parentlist [lreplace $parentlist $row $row]
 -    set commitlisted [lreplace $commitlisted $row $row]
 -    set kids $children($curview,$p)
 -    set i [lsearch -exact $kids $id]
 -    if {$i >= 0} {
 -      set kids [lreplace $kids $i $i]
 -      set children($curview,$p) $kids
 -    }
 -    set l [llength $displayorder]
 -    for {set r $row} {$r < $l} {incr r} {
 -      set id [lindex $displayorder $r]
 -      set commitrow($curview,$id) $r
 -    }
 -    incr commitidx($curview) -1
 -
 -    if {$row < [llength $rowidlist]} {
 -      set rowidlist [lreplace $rowidlist $row $row]
 -      set rowisopt [lreplace $rowisopt $row $row]
 -      set rowfinal [lreplace $rowfinal $row $row]
 -    }
 -
 -    incr numcommits -1
 -
 -    if {[info exists selectedline] && $selectedline > $row} {
 -      incr selectedline -1
 -    }
 -    redisplay
 -}
 -
  # Don't change the text pane cursor if it is currently the hand cursor,
  # showing that we are over a sha1 ID link.
  proc settextcursor {c} {
@@@ -4816,11 -4226,11 +4814,11 @@@ proc notbusy {what} 
  
  proc findmatches {f} {
      global findtype findstring
-     if {$findtype == "Regexp"} {
+     if {$findtype == [mc "Regexp"]} {
        set matches [regexp -indices -all -inline $findstring $f]
      } else {
        set fs $findstring
-       if {$findtype == "IgnCase"} {
+       if {$findtype == [mc "IgnCase"]} {
            set f [string tolower $f]
            set fs [string tolower $fs]
        }
@@@ -4851,8 -4261,8 +4849,8 @@@ proc dofind {{dirn 1} {wrap 1}} 
        set findstartline $selectedline
      }
      set findcurline $findstartline
-     nowbusy finding "Searching"
-     if {$gdttype ne "containing:" && ![info exists filehighlight]} {
+     nowbusy finding [mc "Searching"]
+     if {$gdttype ne [mc "containing:"] && ![info exists filehighlight]} {
        after cancel do_file_hl $fh_serial
        do_file_hl $fh_serial
      }
@@@ -4875,14 -4285,14 +4873,14 @@@ proc stopfinding {} 
  
  proc findmore {} {
      global commitdata commitinfo numcommits findpattern findloc
 -    global findstartline findcurline displayorder
 +    global findstartline findcurline findallowwrap
      global find_dirn gdttype fhighlights fprogcoord
 -    global findallowwrap
 +    global curview varcorder vrownum varccommits
  
      if {![info exists find_dirn]} {
        return 0
      }
-     set fldtypes {Headline Author Date Committer CDate Comments}
+     set fldtypes [list [mc "Headline"] [mc "Author"] [mc "Date"] [mc "Committer"] [mc "CDate"] [mc "Comments"]]
      set l $findcurline
      set moretodo 0
      if {$find_dirn > 0} {
      }
      set found 0
      set domore 1
-     if {$gdttype eq "containing:"} {
 +    set ai [bsearch $vrownum($curview) $l]
 +    set a [lindex $varcorder($curview) $ai]
 +    set arow [lindex $vrownum($curview) $ai]
 +    set ids [lindex $varccommits($curview,$a)]
 +    set arowend [expr {$arow + [llength $ids]}]
+     if {$gdttype eq [mc "containing:"]} {
        for {} {$n > 0} {incr n -1; incr l $find_dirn} {
 -          set id [lindex $displayorder $l]
 +          if {$l < $arow || $l >= $arowend} {
 +              incr ai $find_dirn
 +              set a [lindex $varcorder($curview) $ai]
 +              set arow [lindex $vrownum($curview) $ai]
 +              set ids [lindex $varccommits($curview,$a)]
 +              set arowend [expr {$arow + [llength $ids]}]
 +          }
 +          set id [lindex $ids [expr {$l - $arow}]]
            # shouldn't happen unless git log doesn't give all the commits...
 -          if {![info exists commitdata($id)]} continue
 -          if {![doesmatch $commitdata($id)]} continue
 +          if {![info exists commitdata($id)] ||
 +              ![doesmatch $commitdata($id)]} {
 +              continue
 +          }
            if {![info exists commitinfo($id)]} {
                getcommit $id
            }
            set info $commitinfo($id)
            foreach f $info ty $fldtypes {
-               if {($findloc eq "All fields" || $findloc eq $ty) &&
+               if {($findloc eq [mc "All fields"] || $findloc eq $ty) &&
                    [doesmatch $f]} {
                    set found 1
                    break
        }
      } else {
        for {} {$n > 0} {incr n -1; incr l $find_dirn} {
 -          set id [lindex $displayorder $l]
 +          if {$l < $arow || $l >= $arowend} {
 +              incr ai $find_dirn
 +              set a [lindex $varcorder($curview) $ai]
 +              set arow [lindex $vrownum($curview) $ai]
 +              set ids [lindex $varccommits($curview,$a)]
 +              set arowend [expr {$arow + [llength $ids]}]
 +          }
 +          set id [lindex $ids [expr {$l - $arow}]]
            if {![info exists fhighlights($l)]} {
                askfilehighlight $l $id
                if {$domore} {
@@@ -5003,7 -4392,7 +5001,7 @@@ proc findselectline {l} 
      set markingmatches 1
      set findcurline $l
      selectline $l 1
-     if {$findloc == "All fields" || $findloc == "Comments"} {
+     if {$findloc == [mc "All fields"] || $findloc == [mc "Comments"]} {
        # highlight the matches in the comments
        set f [$ctext get 1.0 $commentend]
        set matches [findmatches $f]
@@@ -5081,7 -4470,7 +5079,7 @@@ proc commit_descriptor {p} 
  # append some text to the ctext widget, and make any SHA1 ID
  # that we know about be a clickable link.
  proc appendwithlinks {text tags} {
 -    global ctext commitrow linknum curview pendinglinks
 +    global ctext linknum curview pendinglinks
  
      set start [$ctext index "end - 1c"]
      $ctext insert end $text $tags
  }
  
  proc setlink {id lk} {
 -    global curview commitrow ctext pendinglinks commitinterest
 +    global curview ctext pendinglinks commitinterest
  
 -    if {[info exists commitrow($curview,$id)]} {
 +    if {[commitinview $id $curview]} {
        $ctext tag conf $lk -foreground blue -underline 1
 -      $ctext tag bind $lk <1> [list selectline $commitrow($curview,$id) 1]
 +      $ctext tag bind $lk <1> [list selectline [rowofcommit $id] 1]
        $ctext tag bind $lk <Enter> {linkcursor %W 1}
        $ctext tag bind $lk <Leave> {linkcursor %W -1}
      } else {
@@@ -5154,7 -4543,7 +5152,7 @@@ proc viewnextline {dir} 
  # add a list of tag or branch names at position pos
  # returns the number of names inserted
  proc appendrefs {pos ids var} {
 -    global ctext commitrow linknum curview $var maxrefs
 +    global ctext linknum curview $var maxrefs
  
      if {[catch {$ctext index $pos}]} {
        return 0
@@@ -5257,7 -4646,8 +5255,7 @@@ proc make_secsel {l} 
  
  proc selectline {l isnew} {
      global canv ctext commitinfo selectedline
 -    global displayorder
 -    global canvy0 linespc parentlist children curview
 +    global canvy0 linespc parents children curview
      global currentid sha1entry
      global commentend idtags linknum
      global mergemax numcommits pending_select
  
      set selectedline $l
  
 -    set id [lindex $displayorder $l]
 +    set id [commitonrow $l]
      set currentid $id
      $sha1entry delete 0 end
      $sha1entry insert 0 $id
      set linknum 0
      set info $commitinfo($id)
      set date [formatdate [lindex $info 2]]
-     $ctext insert end "Author: [lindex $info 1]  $date\n"
+     $ctext insert end "[mc "Author"]: [lindex $info 1]  $date\n"
      set date [formatdate [lindex $info 4]]
-     $ctext insert end "Committer: [lindex $info 3]  $date\n"
+     $ctext insert end "[mc "Committer"]: [lindex $info 3]  $date\n"
      if {[info exists idtags($id)]} {
-       $ctext insert end "Tags:"
+       $ctext insert end [mc "Tags:"]
        foreach tag $idtags($id) {
            $ctext insert end " $tag"
        }
      }
  
      set headers {}
 -    set olds [lindex $parentlist $l]
 +    set olds $parents($curview,$id)
      if {[llength $olds] > 1} {
        set np 0
        foreach p $olds {
            } else {
                set tag m$np
            }
-           $ctext insert end "Parent: " $tag
+           $ctext insert end "[mc "Parent"]: " $tag
            appendwithlinks [commit_descriptor $p] {}
            incr np
        }
      } else {
        foreach p $olds {
-           append headers "Parent: [commit_descriptor $p]"
+           append headers "[mc "Parent"]: [commit_descriptor $p]"
        }
      }
  
      foreach c $children($curview,$id) {
-       append headers "Child:  [commit_descriptor $c]"
+       append headers "[mc "Child"]:  [commit_descriptor $c]"
      }
  
      # make anything that looks like a SHA1 ID be a clickable link
        if {![info exists allcommits]} {
            getallcommits
        }
-       $ctext insert end "Branch: "
+       $ctext insert end "[mc "Branch"]: "
        $ctext mark set branch "end -1c"
        $ctext mark gravity branch left
-       $ctext insert end "\nFollows: "
+       $ctext insert end "\n[mc "Follows"]: "
        $ctext mark set follows "end -1c"
        $ctext mark gravity follows left
-       $ctext insert end "\nPrecedes: "
+       $ctext insert end "\n[mc "Precedes"]: "
        $ctext mark set precedes "end -1c"
        $ctext mark gravity precedes left
        $ctext insert end "\n"
      $ctext conf -state disabled
      set commentend [$ctext index "end - 1c"]
  
-     init_flist "Comments"
+     init_flist [mc "Comments"]
      if {$cmitmode eq "tree"} {
        gettree $id
      } elseif {[llength $olds] <= 1} {
        startdiff $id
      } else {
 -      mergediff $id $l
 +      mergediff $id
      }
  }
  
@@@ -5641,10 -5031,10 +5639,10 @@@ proc getblobline {bf id} 
      return [expr {$nl >= 1000? 2: 1}]
  }
  
 -proc mergediff {id l} {
 +proc mergediff {id} {
      global diffmergeid mdifffd
      global diffids
 -    global parentlist
 +    global parents
      global limitdiffs viewfiles curview
  
      set diffmergeid $id
        set cmd [concat $cmd -- $viewfiles($curview)]
      }
      if {[catch {set mdf [open $cmd r]} err]} {
-       error_popup "Error getting merge diffs: $err"
+       error_popup "[mc "Error getting merge diffs:"] $err"
        return
      }
      fconfigure $mdf -blocking 0
      set mdifffd($id) $mdf
 -    set np [llength [lindex $parentlist $l]]
 +    set np [llength $parents($curview,$id)]
      settabs $np
      filerun $mdf [list getmergediffline $mdf $id $np]
  }
@@@ -6288,7 -5678,7 +6286,7 @@@ proc fontname {f} 
  }
  
  proc incrfont {inc} {
 -    global mainfont textfont ctext canv phase cflist showrefstop
 +    global mainfont textfont ctext canv cflist showrefstop
      global stopped entries fontattr
  
      unmarkmatches
@@@ -6332,14 -5722,15 +6330,14 @@@ proc sha1change {n1 n2 op} 
      }
      if {[$sha1but cget -state] == $state} return
      if {$state == "normal"} {
-       $sha1but conf -state normal -relief raised -text "Goto: "
+       $sha1but conf -state normal -relief raised -text "[mc "Goto:"] "
      } else {
-       $sha1but conf -state disabled -relief flat -text "SHA1 ID: "
+       $sha1but conf -state disabled -relief flat -text "[mc "SHA1 ID:"] "
      }
  }
  
  proc gotocommit {} {
 -    global sha1string currentid commitrow tagids headids
 -    global displayorder numcommits curview
 +    global sha1string tagids headids curview varcid
  
      if {$sha1string == {}
        || ([info exists currentid] && $sha1string == $currentid)} return
      } else {
        set id [string tolower $sha1string]
        if {[regexp {^[0-9a-f]{4,39}$} $id]} {
 -          set matches {}
 -          foreach i $displayorder {
 -              if {[string match $id* $i]} {
 -                  lappend matches $i
 -              }
 -          }
 +          set matches [array names varcid "$curview,$id*"]
            if {$matches ne {}} {
                if {[llength $matches] > 1} {
-                   error_popup "Short SHA1 id $id is ambiguous"
+                   error_popup [mc "Short SHA1 id %s is ambiguous" $id]
                    return
                }
 -              set id [lindex $matches 0]
 +              set id [lindex [split [lindex $matches 0] ","] 1]
            }
        }
      }
 -    if {[info exists commitrow($curview,$id)]} {
 -      selectline $commitrow($curview,$id) 1
 +    if {[commitinview $id $curview]} {
 +      selectline [rowofcommit $id] 1
        return
      }
      if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
-       set type "SHA1 id"
+       set msg [mc "SHA1 id %s is not known" $sha1string]
      } else {
-       set type "Tag/Head"
+       set msg [mc "Tag/Head %s is not known" $sha1string]
      }
-     error_popup "$type $sha1string is not known"
+     error_popup $msg
  }
  
  proc lineenter {x y id} {
@@@ -6470,7 -5866,7 +6468,7 @@@ proc arrowjump {id n y} 
  }
  
  proc lineclick {x y id isnew} {
 -    global ctext commitinfo children canv thickerline curview commitrow
 +    global ctext commitinfo children canv thickerline curview
  
      if {![info exists commitinfo($id)] && ![getcommit $id]} return
      unmarkmatches
      $ctext conf -state normal
      clear_ctext
      settabs 0
-     $ctext insert end "Parent:\t"
+     $ctext insert end "[mc "Parent"]:\t"
      $ctext insert end $id link0
      setlink $id link0
      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 "\t[mc "Author"]:\t[lindex $info 1]\n"
      set date [formatdate [lindex $info 2]]
-     $ctext insert end "\tDate:\t$date\n"
+     $ctext insert end "\t[mc "Date"]:\t$date\n"
      set kids $children($curview,$id)
      if {$kids ne {}} {
-       $ctext insert end "\nChildren:"
+       $ctext insert end "\n[mc "Children"]:"
        set i 0
        foreach child $kids {
            incr i
            $ctext insert end $child link$i
            setlink $child link$i
            $ctext insert end "\n\t[lindex $info 0]"
-           $ctext insert end "\n\tAuthor:\t[lindex $info 1]"
+           $ctext insert end "\n\t[mc "Author"]:\t[lindex $info 1]"
            set date [formatdate [lindex $info 2]]
-           $ctext insert end "\n\tDate:\t$date\n"
+           $ctext insert end "\n\t[mc "Date"]:\t$date\n"
        }
      }
      $ctext conf -state disabled
@@@ -6538,9 -5934,9 +6536,9 @@@ proc normalline {} 
  }
  
  proc selbyid {id} {
 -    global commitrow curview
 -    if {[info exists commitrow($curview,$id)]} {
 -      selectline $commitrow($curview,$id) 1
 +    global curview
 +    if {[commitinview $id $curview]} {
 +      selectline [rowofcommit $id] 1
      }
  }
  
@@@ -6553,39 -5949,39 +6551,39 @@@ proc mstime {} 
  }
  
  proc rowmenu {x y id} {
 -    global rowctxmenu commitrow selectedline rowmenuid curview
 +    global rowctxmenu selectedline rowmenuid curview
      global nullid nullid2 fakerowmenu mainhead
  
      stopfinding
      set rowmenuid $id
      if {![info exists selectedline]
 -      || $commitrow($curview,$id) eq $selectedline} {
 +      || [rowofcommit $id] eq $selectedline} {
        set state disabled
      } else {
        set state normal
      }
      if {$id ne $nullid && $id ne $nullid2} {
        set menu $rowctxmenu
-       $menu entryconfigure 7 -label "Reset $mainhead branch to here"
+       $menu entryconfigure 7 -label [mc "Reset %s branch to here" $mainhead]
      } else {
        set menu $fakerowmenu
      }
-     $menu entryconfigure "Diff this*" -state $state
-     $menu entryconfigure "Diff selected*" -state $state
-     $menu entryconfigure "Make patch" -state $state
+     $menu entryconfigure [mc "Diff this -> selected"] -state $state
+     $menu entryconfigure [mc "Diff selected -> this"] -state $state
+     $menu entryconfigure [mc "Make patch"] -state $state
      tk_popup $menu $x $y
  }
  
  proc diffvssel {dirn} {
 -    global rowmenuid selectedline displayorder
 +    global rowmenuid selectedline
  
      if {![info exists selectedline]} return
      if {$dirn} {
 -      set oldid [lindex $displayorder $selectedline]
 +      set oldid [commitonrow $selectedline]
        set newid $rowmenuid
      } else {
        set oldid $rowmenuid
 -      set newid [lindex $displayorder $selectedline]
 +      set newid [commitonrow $selectedline]
      }
      addtohistory [list doseldiff $oldid $newid]
      doseldiff $oldid $newid
@@@ -6597,13 -5993,13 +6595,13 @@@ proc doseldiff {oldid newid} 
  
      $ctext conf -state normal
      clear_ctext
-     init_flist "Top"
-     $ctext insert end "From "
+     init_flist [mc "Top"]
+     $ctext insert end "[mc "From"] "
      $ctext insert end $oldid link0
      setlink $oldid link0
      $ctext insert end "\n     "
      $ctext insert end [lindex $commitinfo($oldid) 0]
-     $ctext insert end "\n\nTo   "
+     $ctext insert end "\n\n[mc "To"]   "
      $ctext insert end $newid link1
      setlink $newid link1
      $ctext insert end "\n     "
@@@ -6626,9 -6022,9 +6624,9 @@@ proc mkpatch {} 
      set patchtop $top
      catch {destroy $top}
      toplevel $top
-     label $top.title -text "Generate patch"
+     label $top.title -text [mc "Generate patch"]
      grid $top.title - -pady 10
-     label $top.from -text "From:"
+     label $top.from -text [mc "From:"]
      entry $top.fromsha1 -width 40 -relief flat
      $top.fromsha1 insert 0 $oldid
      $top.fromsha1 conf -state readonly
      $top.fromhead insert 0 $oldhead
      $top.fromhead conf -state readonly
      grid x $top.fromhead -sticky w
-     label $top.to -text "To:"
+     label $top.to -text [mc "To:"]
      entry $top.tosha1 -width 40 -relief flat
      $top.tosha1 insert 0 $newid
      $top.tosha1 conf -state readonly
      $top.tohead insert 0 $newhead
      $top.tohead conf -state readonly
      grid x $top.tohead -sticky w
-     button $top.rev -text "Reverse" -command mkpatchrev -padx 5
+     button $top.rev -text [mc "Reverse"] -command mkpatchrev -padx 5
      grid $top.rev x -pady 10
-     label $top.flab -text "Output file:"
+     label $top.flab -text [mc "Output file:"]
      entry $top.fname -width 60
      $top.fname insert 0 [file normalize "patch$patchnum.patch"]
      incr patchnum
      grid $top.flab $top.fname -sticky w
      frame $top.buts
-     button $top.buts.gen -text "Generate" -command mkpatchgo
-     button $top.buts.can -text "Cancel" -command mkpatchcan
+     button $top.buts.gen -text [mc "Generate"] -command mkpatchgo
+     button $top.buts.can -text [mc "Cancel"] -command mkpatchcan
      grid $top.buts.gen $top.buts.can
      grid columnconfigure $top.buts 0 -weight 1 -uniform a
      grid columnconfigure $top.buts 1 -weight 1 -uniform a
@@@ -6690,7 -6086,7 +6688,7 @@@ proc mkpatchgo {} 
      set cmd [lrange $cmd 1 end]
      lappend cmd >$fname &
      if {[catch {eval exec $cmd} err]} {
-       error_popup "Error creating patch: $err"
+       error_popup "[mc "Error creating patch:"] $err"
      }
      catch {destroy $patchtop}
      unset patchtop
@@@ -6710,9 -6106,9 +6708,9 @@@ proc mktag {} 
      set mktagtop $top
      catch {destroy $top}
      toplevel $top
-     label $top.title -text "Create tag"
+     label $top.title -text [mc "Create tag"]
      grid $top.title - -pady 10
-     label $top.id -text "ID:"
+     label $top.id -text [mc "ID:"]
      entry $top.sha1 -width 40 -relief flat
      $top.sha1 insert 0 $rowmenuid
      $top.sha1 conf -state readonly
      $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
      $top.head conf -state readonly
      grid x $top.head -sticky w
-     label $top.tlab -text "Tag name:"
+     label $top.tlab -text [mc "Tag name:"]
      entry $top.tag -width 60
      grid $top.tlab $top.tag -sticky w
      frame $top.buts
-     button $top.buts.gen -text "Create" -command mktaggo
-     button $top.buts.can -text "Cancel" -command mktagcan
+     button $top.buts.gen -text [mc "Create"] -command mktaggo
+     button $top.buts.can -text [mc "Cancel"] -command mktagcan
      grid $top.buts.gen $top.buts.can
      grid columnconfigure $top.buts 0 -weight 1 -uniform a
      grid columnconfigure $top.buts 1 -weight 1 -uniform a
@@@ -6740,11 -6136,11 +6738,11 @@@ proc domktag {} 
      set id [$mktagtop.sha1 get]
      set tag [$mktagtop.tag get]
      if {$tag == {}} {
-       error_popup "No tag name specified"
+       error_popup [mc "No tag name specified"]
        return
      }
      if {[info exists tagids($tag)]} {
-       error_popup "Tag \"$tag\" already exists"
+       error_popup [mc "Tag \"%s\" already exists" $tag]
        return
      }
      if {[catch {
        puts $f $id
        close $f
      } err]} {
-       error_popup "Error creating tag: $err"
+       error_popup "[mc "Error creating tag:"] $err"
        return
      }
  
  }
  
  proc redrawtags {id} {
 -    global canv linehtag commitrow idpos selectedline curview
 +    global canv linehtag idpos selectedline curview
      global canvxmax iddrawn
  
 -    if {![info exists commitrow($curview,$id)]} return
 +    if {![commitinview $id $curview]} return
      if {![info exists iddrawn($id)]} return
 -    drawcommits $commitrow($curview,$id)
 +    drawcommits [rowofcommit $id]
      $canv delete tag.$id
      set xt [eval drawtags $id $idpos($id)]
 -    $canv coords $linehtag($commitrow($curview,$id)) $xt [lindex $idpos($id) 2]
 -    set text [$canv itemcget $linehtag($commitrow($curview,$id)) -text]
 +    $canv coords $linehtag([rowofcommit $id]) $xt [lindex $idpos($id) 2]
 +    set text [$canv itemcget $linehtag([rowofcommit $id]) -text]
      set xr [expr {$xt + [font measure mainfont $text]}]
      if {$xr > $canvxmax} {
        set canvxmax $xr
        setcanvscroll
      }
      if {[info exists selectedline]
 -      && $selectedline == $commitrow($curview,$id)} {
 +      && $selectedline == [rowofcommit $id]} {
        selectline $selectedline 0
      }
  }
@@@ -6807,9 -6203,9 +6805,9 @@@ proc writecommit {} 
      set wrcomtop $top
      catch {destroy $top}
      toplevel $top
-     label $top.title -text "Write commit to file"
+     label $top.title -text [mc "Write commit to file"]
      grid $top.title - -pady 10
-     label $top.id -text "ID:"
+     label $top.id -text [mc "ID:"]
      entry $top.sha1 -width 40 -relief flat
      $top.sha1 insert 0 $rowmenuid
      $top.sha1 conf -state readonly
      $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
      $top.head conf -state readonly
      grid x $top.head -sticky w
-     label $top.clab -text "Command:"
+     label $top.clab -text [mc "Command:"]
      entry $top.cmd -width 60 -textvariable wrcomcmd
      grid $top.clab $top.cmd -sticky w -pady 10
-     label $top.flab -text "Output file:"
+     label $top.flab -text [mc "Output file:"]
      entry $top.fname -width 60
      $top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6]"]
      grid $top.flab $top.fname -sticky w
      frame $top.buts
-     button $top.buts.gen -text "Write" -command wrcomgo
-     button $top.buts.can -text "Cancel" -command wrcomcan
+     button $top.buts.gen -text [mc "Write"] -command wrcomgo
+     button $top.buts.can -text [mc "Cancel"] -command wrcomcan
      grid $top.buts.gen $top.buts.can
      grid columnconfigure $top.buts 0 -weight 1 -uniform a
      grid columnconfigure $top.buts 1 -weight 1 -uniform a
@@@ -6842,7 -6238,7 +6840,7 @@@ proc wrcomgo {} 
      set cmd "echo $id | [$wrcomtop.cmd get]"
      set fname [$wrcomtop.fname get]
      if {[catch {exec sh -c $cmd >$fname &} err]} {
-       error_popup "Error writing commit: $err"
+       error_popup "[mc "Error writing commit:"] $err"
      }
      catch {destroy $wrcomtop}
      unset wrcomtop
@@@ -6861,19 -6257,19 +6859,19 @@@ proc mkbranch {} 
      set top .makebranch
      catch {destroy $top}
      toplevel $top
-     label $top.title -text "Create new branch"
+     label $top.title -text [mc "Create new branch"]
      grid $top.title - -pady 10
-     label $top.id -text "ID:"
+     label $top.id -text [mc "ID:"]
      entry $top.sha1 -width 40 -relief flat
      $top.sha1 insert 0 $rowmenuid
      $top.sha1 conf -state readonly
      grid $top.id $top.sha1 -sticky w
-     label $top.nlab -text "Name:"
+     label $top.nlab -text [mc "Name:"]
      entry $top.name -width 40
      grid $top.nlab $top.name -sticky w
      frame $top.buts
-     button $top.buts.go -text "Create" -command [list mkbrgo $top]
-     button $top.buts.can -text "Cancel" -command "catch {destroy $top}"
+     button $top.buts.go -text [mc "Create"] -command [list mkbrgo $top]
+     button $top.buts.can -text [mc "Cancel"] -command "catch {destroy $top}"
      grid $top.buts.go $top.buts.can
      grid columnconfigure $top.buts 0 -weight 1 -uniform a
      grid columnconfigure $top.buts 1 -weight 1 -uniform a
@@@ -6887,7 -6283,7 +6885,7 @@@ proc mkbrgo {top} 
      set name [$top.name get]
      set id [$top.sha1 get]
      if {$name eq {}} {
-       error_popup "Please specify a name for the new branch"
+       error_popup [mc "Please specify a name for the new branch"]
        return
      }
      catch {destroy $top}
  }
  
  proc cherrypick {} {
 -    global rowmenuid curview commitrow
 +    global rowmenuid curview
      global mainhead
  
      set oldhead [exec git rev-parse HEAD]
      set dheads [descheads $rowmenuid]
      if {$dheads ne {} && [lsearch -exact $dheads $oldhead] >= 0} {
-       set ok [confirm_popup "Commit [string range $rowmenuid 0 7] is already\
-                       included in branch $mainhead -- really re-apply it?"]
+       set ok [confirm_popup [mc "Commit %s is already\
+               included in branch %s -- really re-apply it?" \
+                                  [string range $rowmenuid 0 7] $mainhead]]
        if {!$ok} return
      }
-     nowbusy cherrypick "Cherry-picking"
+     nowbusy cherrypick [mc "Cherry-picking"]
      update
      # Unfortunately git-cherry-pick writes stuff to stderr even when
      # no error occurs, and exec takes that as an indication of error...
      set newhead [exec git rev-parse HEAD]
      if {$newhead eq $oldhead} {
        notbusy cherrypick
-       error_popup "No changes committed"
+       error_popup [mc "No changes committed"]
        return
      }
      addnewchild $newhead $oldhead
 -    if {[info exists commitrow($curview,$oldhead)]} {
 -      insertrow $commitrow($curview,$oldhead) $newhead
 +    if {[commitinview $oldhead $curview]} {
 +      insertrow $newhead $oldhead $curview
        if {$mainhead ne {}} {
            movehead $newhead $mainhead
            movedhead $newhead $mainhead
@@@ -6955,28 -6352,28 +6954,28 @@@ proc resethead {} 
      set w ".confirmreset"
      toplevel $w
      wm transient $w .
-     wm title $w "Confirm reset"
+     wm title $w [mc "Confirm reset"]
      message $w.m -text \
-       "Reset branch $mainhead to [string range $rowmenuid 0 7]?" \
+       [mc "Reset branch %s to %s?" $mainhead [string range $rowmenuid 0 7]] \
        -justify center -aspect 1000
      pack $w.m -side top -fill x -padx 20 -pady 20
      frame $w.f -relief sunken -border 2
-     message $w.f.rt -text "Reset type:" -aspect 1000
+     message $w.f.rt -text [mc "Reset type:"] -aspect 1000
      grid $w.f.rt -sticky w
      set resettype mixed
      radiobutton $w.f.soft -value soft -variable resettype -justify left \
-       -text "Soft: Leave working tree and index untouched"
+       -text [mc "Soft: Leave working tree and index untouched"]
      grid $w.f.soft -sticky w
      radiobutton $w.f.mixed -value mixed -variable resettype -justify left \
-       -text "Mixed: Leave working tree untouched, reset index"
+       -text [mc "Mixed: Leave working tree untouched, reset index"]
      grid $w.f.mixed -sticky w
      radiobutton $w.f.hard -value hard -variable resettype -justify left \
-       -text "Hard: Reset working tree and index\n(discard ALL local changes)"
+       -text [mc "Hard: Reset working tree and index\n(discard ALL local changes)"]
      grid $w.f.hard -sticky w
      pack $w.f -side top -fill x
-     button $w.ok -text OK -command "set confirm_ok 1; destroy $w"
+     button $w.ok -text [mc OK] -command "set confirm_ok 1; destroy $w"
      pack $w.ok -side left -fill x -padx 20 -pady 20
-     button $w.cancel -text Cancel -command "destroy $w"
+     button $w.cancel -text [mc Cancel] -command "destroy $w"
      pack $w.cancel -side right -fill x -padx 20 -pady 20
      bind $w <Visibility> "grab $w; focus $w"
      tkwait window $w
      } else {
        dohidelocalchanges
        filerun $fd [list readresetstat $fd]
-       nowbusy reset "Resetting"
+       nowbusy reset [mc "Resetting"]
      }
  }
  
@@@ -7044,7 -6441,7 +7043,7 @@@ proc cobranch {} 
  
      # check the tree is clean first??
      set oldmainhead $mainhead
-     nowbusy checkout "Checking out"
+     nowbusy checkout [mc "Checking out"]
      update
      dohidelocalchanges
      if {[catch {
@@@ -7074,14 -6471,14 +7073,14 @@@ proc rmbranch {} 
      set id $headmenuid
      # this check shouldn't be needed any more...
      if {$head eq $mainhead} {
-       error_popup "Cannot delete the currently checked-out branch"
+       error_popup [mc "Cannot delete the currently checked-out branch"]
        return
      }
      set dheads [descheads $id]
      if {[llength $dheads] == 1 && $idheads($dheads) eq $head} {
        # the stuff on this branch isn't on any other branch
-       if {![confirm_popup "The commits on branch $head aren't on any other\
-                       branch.\nReally delete branch $head?"]} return
+       if {![confirm_popup [mc "The commits on branch %s aren't on any other\
+                       branch.\nReally delete branch %s?" $head $head]]} return
      }
      nowbusy rmbranch
      update
@@@ -7111,7 -6508,7 +7110,7 @@@ proc showrefs {} 
        return
      }
      toplevel $top
-     wm title $top "Tags and heads: [file tail [pwd]]"
+     wm title $top [mc "Tags and heads: %s" [file tail [pwd]]]
      text $top.list -background $bgcolor -foreground $fgcolor \
        -selectbackground $selectbgcolor -font mainfont \
        -xscrollcommand "$top.xsb set" -yscrollcommand "$top.ysb set" \
      grid $top.list $top.ysb -sticky nsew
      grid $top.xsb x -sticky ew
      frame $top.f
-     label $top.f.l -text "Filter: " -font uifont
+     label $top.f.l -text "[mc "Filter"]: " -font uifont
      entry $top.f.e -width 20 -textvariable reflistfilter -font uifont
      set reflistfilter "*"
      trace add variable reflistfilter write reflistfilter_change
      pack $top.f.e -side right -fill x -expand 1
      pack $top.f.l -side left
      grid $top.f - -sticky ew -pady 2
-     button $top.close -command [list destroy $top] -text "Close" \
+     button $top.close -command [list destroy $top] -text [mc "Close"] \
        -font uifont
      grid $top.close -
      grid columnconfigure $top 0 -weight 1
@@@ -7175,13 -6572,13 +7174,13 @@@ proc reflistfilter_change {n1 n2 op} 
  
  proc refill_reflist {} {
      global reflist reflistfilter showrefstop headids tagids otherrefids
 -    global commitrow curview commitinterest
 +    global curview commitinterest
  
      if {![info exists showrefstop] || ![winfo exists $showrefstop]} return
      set refs {}
      foreach n [array names headids] {
        if {[string match $reflistfilter $n]} {
 -          if {[info exists commitrow($curview,$headids($n))]} {
 +          if {[commitinview $headids($n) $curview]} {
                lappend refs [list $n H]
            } else {
                set commitinterest($headids($n)) {run refill_reflist}
      }
      foreach n [array names tagids] {
        if {[string match $reflistfilter $n]} {
 -          if {[info exists commitrow($curview,$tagids($n))]} {
 +          if {[commitinview $tagids($n) $curview]} {
                lappend refs [list $n T]
            } else {
                set commitinterest($tagids($n)) {run refill_reflist}
      }
      foreach n [array names otherrefids] {
        if {[string match $reflistfilter $n]} {
 -          if {[info exists commitrow($curview,$otherrefids($n))]} {
 +          if {[commitinview $otherrefids($n) $curview]} {
                lappend refs [list $n o]
            } else {
                set commitinterest($otherrefids($n)) {run refill_reflist}
@@@ -7420,9 -6817,9 +7419,9 @@@ proc getallclines {fd} 
            dropcache $err
            return
        }
-       error_popup "Error reading commit topology information;\
+       error_popup "[mc "Error reading commit topology information;\
                branch and preceding/following tag information\
-               will be incomplete.\n($err)"
+               will be incomplete."]\n($err)"
        set cacheok 0
      }
      if {[incr allcommits -1] == 0} {
@@@ -8404,7 -7801,7 +8403,7 @@@ proc showtag {tag isnew} 
      if {[info exists tagcontents($tag)]} {
        set text $tagcontents($tag)
      } else {
-       set text "Tag: $tag\nId:  $tagids($tag)"
+       set text "[mc "Tag"]: $tag\n[mc "Id"]:  $tagids($tag)"
      }
      appendwithlinks $text {}
      $ctext conf -state disabled
@@@ -8444,7 -7841,7 +8443,7 @@@ proc choosefont {font which} 
        font create sample
        eval font config sample [font actual $font]
        toplevel $top
-       wm title $top "Gitk font chooser"
+       wm title $top [mc "Gitk font chooser"]
        label $top.l -textvariable fontparam(which) -font uifont
        pack $top.l -side top
        set fontlist [lsort [font families]]
            -textvariable fontparam(size) \
            -validatecommand {string is integer -strict %s}
        checkbutton $top.g.bold -padx 5 \
-           -font {{Times New Roman} 12 bold} -text "B" -indicatoron 0 \
+           -font {{Times New Roman} 12 bold} -text [mc "B"] -indicatoron 0 \
            -variable fontparam(weight) -onvalue bold -offvalue normal
        checkbutton $top.g.ital -padx 5 \
-           -font {{Times New Roman} 12 italic} -text "I" -indicatoron 0  \
+           -font {{Times New Roman} 12 italic} -text [mc "I"] -indicatoron 0  \
            -variable fontparam(slant) -onvalue italic -offvalue roman
        pack $top.g.size $top.g.bold $top.g.ital -side left
        pack $top.g -side top
        bind $top.c <Configure> [list centertext $top.c]
        pack $top.c -side top -fill x
        frame $top.buts
-       button $top.buts.ok -text "OK" -command fontok -default active \
+       button $top.buts.ok -text [mc "OK"] -command fontok -default active \
            -font uifont
-       button $top.buts.can -text "Cancel" -command fontcan -default normal \
+       button $top.buts.can -text [mc "Cancel"] -command fontcan -default normal \
            -font uifont
        grid $top.buts.ok $top.buts.can
        grid columnconfigure $top.buts 0 -weight 1 -uniform a
@@@ -8559,85 -7956,85 +8558,85 @@@ proc doprefs {} 
        set oldprefs($v) [set $v]
      }
      toplevel $top
-     wm title $top "Gitk preferences"
-     label $top.ldisp -text "Commit list display options"
+     wm title $top [mc "Gitk preferences"]
+     label $top.ldisp -text [mc "Commit list display options"]
      $top.ldisp configure -font uifont
      grid $top.ldisp - -sticky w -pady 10
      label $top.spacer -text " "
-     label $top.maxwidthl -text "Maximum graph width (lines)" \
+     label $top.maxwidthl -text [mc "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)" \
+     label $top.maxpctl -text [mc "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
      frame $top.showlocal
-     label $top.showlocal.l -text "Show local changes" -font optionfont
+     label $top.showlocal.l -text [mc "Show local changes"] -font optionfont
      checkbutton $top.showlocal.b -variable showlocalchanges
      pack $top.showlocal.b $top.showlocal.l -side left
      grid x $top.showlocal -sticky w
  
-     label $top.ddisp -text "Diff display options"
+     label $top.ddisp -text [mc "Diff display options"]
      $top.ddisp configure -font uifont
      grid $top.ddisp - -sticky w -pady 10
-     label $top.tabstopl -text "Tab spacing" -font optionfont
+     label $top.tabstopl -text [mc "Tab spacing"] -font optionfont
      spinbox $top.tabstop -from 1 -to 20 -width 4 -textvariable tabstop
      grid x $top.tabstopl $top.tabstop -sticky w
      frame $top.ntag
-     label $top.ntag.l -text "Display nearby tags" -font optionfont
+     label $top.ntag.l -text [mc "Display nearby tags"] -font optionfont
      checkbutton $top.ntag.b -variable showneartags
      pack $top.ntag.b $top.ntag.l -side left
      grid x $top.ntag -sticky w
      frame $top.ldiff
-     label $top.ldiff.l -text "Limit diffs to listed paths" -font optionfont
+     label $top.ldiff.l -text [mc "Limit diffs to listed paths"] -font optionfont
      checkbutton $top.ldiff.b -variable limitdiffs
      pack $top.ldiff.b $top.ldiff.l -side left
      grid x $top.ldiff -sticky w
  
-     label $top.cdisp -text "Colors: press to choose"
+     label $top.cdisp -text [mc "Colors: press to choose"]
      $top.cdisp configure -font uifont
      grid $top.cdisp - -sticky w -pady 10
      label $top.bg -padx 40 -relief sunk -background $bgcolor
-     button $top.bgbut -text "Background" -font optionfont \
+     button $top.bgbut -text [mc "Background"] -font optionfont \
        -command [list choosecolor bgcolor 0 $top.bg background setbg]
      grid x $top.bgbut $top.bg -sticky w
      label $top.fg -padx 40 -relief sunk -background $fgcolor
-     button $top.fgbut -text "Foreground" -font optionfont \
+     button $top.fgbut -text [mc "Foreground"] -font optionfont \
        -command [list choosecolor fgcolor 0 $top.fg foreground setfg]
      grid x $top.fgbut $top.fg -sticky w
      label $top.diffold -padx 40 -relief sunk -background [lindex $diffcolors 0]
-     button $top.diffoldbut -text "Diff: old lines" -font optionfont \
+     button $top.diffoldbut -text [mc "Diff: old lines"] -font optionfont \
        -command [list choosecolor diffcolors 0 $top.diffold "diff old lines" \
                      [list $ctext tag conf d0 -foreground]]
      grid x $top.diffoldbut $top.diffold -sticky w
      label $top.diffnew -padx 40 -relief sunk -background [lindex $diffcolors 1]
-     button $top.diffnewbut -text "Diff: new lines" -font optionfont \
+     button $top.diffnewbut -text [mc "Diff: new lines"] -font optionfont \
        -command [list choosecolor diffcolors 1 $top.diffnew "diff new lines" \
                      [list $ctext tag conf d1 -foreground]]
      grid x $top.diffnewbut $top.diffnew -sticky w
      label $top.hunksep -padx 40 -relief sunk -background [lindex $diffcolors 2]
-     button $top.hunksepbut -text "Diff: hunk header" -font optionfont \
+     button $top.hunksepbut -text [mc "Diff: hunk header"] -font optionfont \
        -command [list choosecolor diffcolors 2 $top.hunksep \
                      "diff hunk header" \
                      [list $ctext tag conf hunksep -foreground]]
      grid x $top.hunksepbut $top.hunksep -sticky w
      label $top.selbgsep -padx 40 -relief sunk -background $selectbgcolor
-     button $top.selbgbut -text "Select bg" -font optionfont \
+     button $top.selbgbut -text [mc "Select bg"] -font optionfont \
        -command [list choosecolor selectbgcolor 0 $top.selbgsep background setselbg]
      grid x $top.selbgbut $top.selbgsep -sticky w
  
-     label $top.cfont -text "Fonts: press to choose"
+     label $top.cfont -text [mc "Fonts: press to choose"]
      $top.cfont configure -font uifont
      grid $top.cfont - -sticky w -pady 10
-     mkfontdisp mainfont $top "Main font"
-     mkfontdisp textfont $top "Diff display font"
-     mkfontdisp uifont $top "User interface font"
+     mkfontdisp mainfont $top [mc "Main font"]
+     mkfontdisp textfont $top [mc "Diff display font"]
+     mkfontdisp uifont $top [mc "User interface font"]
  
      frame $top.buts
-     button $top.buts.ok -text "OK" -command prefsok -default active
+     button $top.buts.ok -text [mc "OK"] -command prefsok -default active
      $top.buts.ok configure -font uifont
-     button $top.buts.can -text "Cancel" -command prefscan -default normal
+     button $top.buts.can -text [mc "Cancel"] -command prefscan -default normal
      $top.buts.can configure -font uifont
      grid $top.buts.ok $top.buts.can
      grid columnconfigure $top.buts 0 -weight 1 -uniform a
@@@ -8650,7 -8047,7 +8649,7 @@@ proc choosecolor {v vi w x cmd} 
      global $v
  
      set c [tk_chooseColor -initialcolor [lindex [set $v] $vi] \
-              -title "Gitk: choose color for $x"]
+              -title [mc "Gitk: choose color for %s" $x]]
      if {$c eq {}} return
      $w conf -background $c
      lset $v $vi $c
@@@ -9031,8 -8428,8 +9030,8 @@@ proc tcl_encoding {enc} 
  
  # First check that Tcl/Tk is recent enough
  if {[catch {package require Tk 8.4} err]} {
-     show_error {} . "Sorry, gitk cannot run with this version of Tcl/Tk.\n\
-                    Gitk requires at least Tcl/Tk 8.4."
+     show_error {} . [mc "Sorry, gitk cannot run with this version of Tcl/Tk.\n\
+                    Gitk requires at least Tcl/Tk 8.4."]
      exit 1
  }
  
@@@ -9080,6 -8477,25 +9079,25 @@@ set diffcolors {red "#00a000" blue
  set diffcontext 3
  set selectbgcolor gray85
  
+ ## For msgcat loading, first locate the installation location.
+ if { [info exists ::env(GITK_MSGSDIR)] } {
+     ## Msgsdir was manually set in the environment.
+     set gitk_msgsdir $::env(GITK_MSGSDIR)
+ } else {
+     ## Let's guess the prefix from argv0.
+     set gitk_prefix [file dirname [file dirname [file normalize $argv0]]]
+     set gitk_libdir [file join $gitk_prefix share gitk lib]
+     set gitk_msgsdir [file join $gitk_libdir msgs]
+     unset gitk_prefix
+ }
+ ## Internationalization (i18n) through msgcat and gettext. See
+ ## http://www.gnu.org/software/gettext/manual/html_node/Tcl.html
+ package require msgcat
+ namespace import ::msgcat::mc
+ ## And eventually load the actual message catalog
+ ::msgcat::mcload $gitk_msgsdir
  catch {source ~/.gitk}
  
  font create optionfont -family sans-serif -size -12
@@@ -9097,11 -8513,11 +9115,11 @@@ eval font create uifont [fontflags uifo
  
  # check that we can find a .git directory somewhere...
  if {[catch {set gitdir [gitdir]}]} {
-     show_error {} . "Cannot find a git repository here."
+     show_error {} . [mc "Cannot find a git repository here."]
      exit 1
  }
  if {![file isdirectory $gitdir]} {
-     show_error {} . "Cannot find the git directory \"$gitdir\"."
+     show_error {} . [mc "Cannot find the git directory \"%s\"." $gitdir]
      exit 1
  }
  
@@@ -9140,8 -8556,8 +9158,8 @@@ if {$i >= [llength $argv] && $revtreear
        # with git log and git rev-list, check revtreeargs for filenames.
        foreach arg $revtreeargs {
            if {[file exists $arg]} {
-               show_error {} . "Ambiguous argument '$arg': both revision\
-                                and filename"
+               show_error {} . [mc "Ambiguous argument '%s': both revision\
+                                and filename" $arg]
                exit 1
            }
        }
        if {$i > 0} {
            set err [string range $err [expr {$i + 6}] end]
        }
-       show_error {} . "Bad arguments to gitk:\n$err"
+       show_error {} . "[mc "Bad arguments to gitk:"]\n$err"
        exit 1
      }
  }
@@@ -9164,7 -8580,7 +9182,7 @@@ if {$mergeonly} 
      if {[catch {
        set fd [open "| git ls-files -u" r]
      } err]} {
-       show_error {} . "Couldn't get list of unmerged files: $err"
+       show_error {} . "[mc "Couldn't get list of unmerged files:"] $err"
        exit 1
      }
      while {[gets $fd line] >= 0} {
      catch {close $fd}
      if {$mlist eq {}} {
        if {$nr_unmerged == 0} {
-           show_error {} . "No files selected: --merge specified but\
-                            no files are unmerged."
+           show_error {} . [mc "No files selected: --merge specified but\
+                            no files are unmerged."]
        } else {
-           show_error {} . "No files selected: --merge specified but\
-                            no unmerged files are within file limit."
+           show_error {} . [mc "No files selected: --merge specified but\
+                            no unmerged files are within file limit."]
        }
        exit 1
      }
@@@ -9216,18 -8632,19 +9234,18 @@@ set firsttabstop 
  set nextviewnum 1
  set curview 0
  set selectedview 0
- set selectedhlview None
- set highlight_related None
+ set selectedhlview [mc "None"]
+ set highlight_related [mc "None"]
  set highlight_files {}
  set viewfiles(0) {}
  set viewperm(0) 0
  set viewargs(0) {}
  
 +set loginstance 0
  set cmdlineok 0
  set stopped 0
  set stuffsaved 0
  set patchnum 0
 -set localirow -1
 -set localfrow -1
  set lserial 0
  setcoords
  makewindow
@@@ -9241,13 -8658,13 +9259,13 @@@ if {$cmdline_files ne {} || $revtreearg
      set curview 1
      set selectedview 1
      set nextviewnum 2
-     set viewname(1) "Command line"
+     set viewname(1) [mc "Command line"]
      set viewfiles(1) $cmdline_files
      set viewargs(1) $revtreeargs
      set viewperm(1) 0
      addviewmenu 1
-     .bar.view entryconf Edit* -state normal
-     .bar.view entryconf Delete* -state normal
+     .bar.view entryconf [mc "Edit view..."] -state normal
+     .bar.view entryconf [mc "Delete view"] -state normal
  }
  
  if {[info exists permviews]} {