Code

Merge branch 'master' into dev
authorPaul Mackerras <paulus@samba.org>
Wed, 9 Jan 2008 03:23:30 +0000 (14:23 +1100)
committerPaul Mackerras <paulus@samba.org>
Wed, 9 Jan 2008 03:23:30 +0000 (14:23 +1100)
1  2 
gitk

diff --combined gitk
index dc1872c7d0ecd2036e249fd2ee6152b860d54276,801a5a9d86d78b77811ef27b23cb37926c69f463..0dacfdadf1b17df160718b53ef39f441cd53515c
--- 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 "[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 [mc "Reading"]
      if {$view == $curview} {
        set progressdirn 1
      }
  }
  
 -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 [mc "Reading commits..."]
  }
  
 +proc updatecommits {} {
 +    global curview viewargs viewfiles viewincl viewinstances
 +    global viewactive viewcomplete loginstance tclencoding mainheadid
 +    global varcid startmsecs commfd showneartags showlocalchanges leftover
 +    global mainheadid
 +
 +    set oldmainid $mainheadid
 +    rereadrefs
 +    if {$showlocalchanges} {
 +      if {$mainheadid ne $oldmainid} {
 +          dohidelocalchanges
 +      }
 +      if {[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"
 +    if {$showneartags} {
 +      getallcommits
 +    }
 +}
 +
 +proc reloadcommits {} {
 +    global curview viewcomplete selectedline currentid thickerline
 +    global showneartags treediffs commitinterest cached_commitrow
 +    global progresscoords targetid
 +
 +    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}
 +    catch {unset targetid}
 +    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
 +      set kid [last_real_child $v,$id]
 +      if {$kid ne {}} {
 +          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 targetid targetrow
 +
 +    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
 +    }
 +    if {[info exists targetid]} {
 +      if {![comes_before $targetid $p]} {
 +          incr targetrow
 +      }
 +    }
 +    drawvisible
 +}
 +
 +proc removerow {id v} {
 +    global varcid varccommits parents children commitidx
 +    global varctok vtokmod cmitlisted currentid selectedline
 +    global targetid
 +
 +    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
 +    }
 +    if {[info exist currentid] && $id eq $currentid} {
 +      unset currentid
 +      unset selectedline
 +    }
 +    if {[info exists targetid] && $targetid eq $id} {
 +      set targetid $p
 +    }
 +    drawvisible
 +}
 +
 +proc first_real_child {vp} {
 +    global children nullid nullid2
 +
 +    foreach id $children($vp) {
 +      if {$id ne $nullid && $id ne $nullid2} {
 +          return $id
 +      }
 +    }
 +    return {}
 +}
 +
 +proc last_real_child {vp} {
 +    global children nullid nullid2
 +
 +    set kids $children($vp)
 +    for {set i [llength $kids]} {[incr i -1] >= 0} {} {
 +      set id [lindex $kids $i]
 +      if {$id ne $nullid && $id ne $nullid2} {
 +          return $id
 +      }
 +    }
 +    return {}
 +}
 +
 +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
 +
 +    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
 +    }
 +}
 +
 +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
 +
 +    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
 +    }
 +    if {[info exists cached_commitrow($id)]} {
 +      return $cached_commitrow($id)
 +    }
 +    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
 +}
 +
 +# Returns 1 if a is on an earlier row than b, otherwise 0
 +proc comes_before {a b} {
 +    global varcid varctok curview
 +
 +    set v $curview
 +    if {$a eq $b || ![info exists varcid($v,$a)] || \
 +          ![info exists varcid($v,$b)]} {
 +      return 0
 +    }
 +    if {$varcid($v,$a) != $varcid($v,$b)} {
 +      return [expr {[string compare [lindex $varctok($v) $varcid($v,$a)] \
 +                         [lindex $varctok($v) $varcid($v,$b)]] < 0}]
 +    }
 +    return [expr {[rowofcommit $a] < [rowofcommit $b]}]
 +}
 +
 +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}]]
        }
            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 [mc "No commits selected"]
            }
            notbusy layout
 -          set phase {}
        }
      }
      if {[info exists hlview] && $view == $hlview} {
@@@ -1148,6 -389,35 +1148,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
  
@@@ -1274,10 -544,10 +1274,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
        }
@@@ -1357,7 -627,7 +1357,7 @@@ proc setoptions {} 
  }
  
  proc makewindow {} {
 -    global canv canv2 canv3 linespc charspc ctext cflist
 +    global canv canv2 canv3 linespc charspc ctext cflist cscroll
      global tabstop
      global findtype findtypemenu findloc findstring fstring geometry
      global entries sha1entry sha1string sha1but
      .bar add cascade -label [mc "File"] -menu .bar.file
      menu .bar.file
      .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
@@@ -1819,7 -1088,7 +1819,7 @@@ proc canvscan {op w x y} 
  
  proc scrollcanv {cscroll f0 f1} {
      $cscroll set $f0 $f1
 -    drawfrac $f0 $f1
 +    drawvisible
      flushhighlights
  }
  
@@@ -2369,7 -1638,7 +2369,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
@@@ -2606,7 -1875,7 +2606,7 @@@ proc editview {} 
  }
  
  proc vieweditor {top n title} {
-     global newviewname newviewperm viewfiles
+     global newviewname newviewperm viewfiles bgcolor
  
      toplevel $top
      wm title $top $title
        -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
+       -background $bgcolor
      grid $top.args - -sticky ew -padx 5
      message $top.l -aspect 1000 \
        -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
+     text $top.t -width 40 -height 10 -background $bgcolor -font uifont
      if {[info exists viewfiles($n)]} {
        foreach f $viewfiles($n) {
            $top.t insert end $f
@@@ -2709,7 -1978,7 +2709,7 @@@ proc newviewok {top n} 
            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} {
        unset hlview
      }
      allviewmenus $curview delete
 -    set viewdata($curview) {}
      set viewperm($curview) 0
      showview 0
  }
@@@ -2738,30 -2008,52 +2738,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} {
        set selectedhlview [mc "None"]
      }
      catch {unset commitinterest}
 +    catch {unset cached_commitrow}
 +    catch {unset ordertok}
  
      set curview $n
      set selectedview $n
      .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 [mc "Reading commits..."]
        }
 -      run chewcommits $n
      } elseif {$numcommits == 0} {
        show_status [mc "No commits selected"]
      }
  
  # Stuff relating to the highlighting facility
  
 -proc ishighlighted {row} {
 +proc ishighlighted {id} {
      global vhighlights fhighlights nhighlights rhighlights
  
 -    if {[info exists nhighlights($row)] && $nhighlights($row) > 0} {
 -      return $nhighlights($row)
 +    if {[info exists nhighlights($id)] && $nhighlights($id) > 0} {
 +      return $nhighlights($id)
      }
 -    if {[info exists vhighlights($row)] && $vhighlights($row) > 0} {
 -      return $vhighlights($row)
 +    if {[info exists vhighlights($id)] && $vhighlights($id) > 0} {
 +      return $vhighlights($id)
      }
 -    if {[info exists fhighlights($row)] && $fhighlights($row) > 0} {
 -      return $fhighlights($row)
 +    if {[info exists fhighlights($id)] && $fhighlights($id) > 0} {
 +      return $fhighlights($id)
      }
 -    if {[info exists rhighlights($row)] && $rhighlights($row) > 0} {
 -      return $rhighlights($row)
 +    if {[info exists rhighlights($id)] && $rhighlights($id) > 0} {
 +      return $rhighlights($id)
      }
      return 0
  }
@@@ -2896,7 -2201,7 +2896,7 @@@ proc unbolden {} 
  
      set stillbold {}
      foreach row $boldrows {
 -      if {![ishighlighted $row]} {
 +      if {![ishighlighted [commitonrow $row]]} {
            bolden $row mainfont
        } else {
            lappend stillbold $row
  }
  
  proc addvhighlight {n} {
 -    global hlview curview viewdata vhl_done vhighlights commitidx
 +    global hlview viewcomplete curview vhl_done 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)
@@@ -2931,21 -2240,27 +2931,21 @@@ 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
                }
 -              set vhighlights($row) 1
 +              set vhighlights($id) 1
            }
        }
      }
  }
  
  proc askvhighlight {row id} {
 -    global hlview vhighlights commitrow iddrawn
 +    global hlview vhighlights iddrawn
  
 -    if {[info exists commitrow($hlview,$id)]} {
 -      if {[info exists iddrawn($id)] && ![ishighlighted $row]} {
 +    if {[commitinview $id $hlview]} {
 +      if {[info exists iddrawn($id)] && ![ishighlighted $id]} {
            bolden $row mainfontbold
        }
 -      set vhighlights($row) 1
 +      set vhighlights($id) 1
      } else {
 -      set vhighlights($row) 0
 +      set vhighlights($id) 0
      }
  }
  
@@@ -3099,12 -2414,12 +3099,12 @@@ proc askfilehighlight {row id} 
      global filehighlight fhighlights fhl_list
  
      lappend fhl_list $id
 -    set fhighlights($row) -1
 +    set fhighlights($id) -1
      puts $filehighlight $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
 -          }
 +          set fhighlights($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 {[info exists iddrawn($line)] && ![ishighlighted $row]} {
 +      if {![commitinview $line $curview]} continue
 +      set row [rowofcommit $line]
 +      if {[info exists iddrawn($line)] && ![ishighlighted $line]} {
            bolden $row mainfontbold
        }
 -      set fhighlights($row) 1
 +      set fhighlights($line) 1
      }
      if {[eof $filehighlight]} {
        # strange...
@@@ -3175,7 -2492,7 +3175,7 @@@ proc askfindhighlight {row id} 
        }
      }
      if {$isbold && [info exists iddrawn($id)]} {
 -      if {![ishighlighted $row]} {
 +      if {![ishighlighted $id]} {
            bolden $row mainfontbold
            if {$isbold > 1} {
                bolden_name $row mainfontbold
            markrowmatches $row $id
        }
      }
 -    set nhighlights($row) $isbold
 +    set nhighlights($id) $isbold
  }
  
  proc markrowmatches {row id} {
@@@ -3223,7 -2540,7 +3223,7 @@@ proc vrel_change {name ix op} 
  # prepare for testing whether commits are descendents or ancestors of a
  proc rhighlight_sel {a} {
      global descendent desc_todo ancestor anc_todo
 -    global highlight_related rhighlights
 +    global highlight_related
  
      catch {unset descendent}
      set desc_todo [list $a]
@@@ -3243,16 -2560,16 +3243,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
@@@ -3330,11 -2647,11 +3330,11 @@@ proc askrelhighlight {row id} 
        }
      }
      if {[info exists iddrawn($id)]} {
 -      if {$isbold && ![ishighlighted $row]} {
 +      if {$isbold && ![ishighlighted $id]} {
            bolden $row mainfontbold
        }
      }
 -    set rhighlights($row) $isbold
 +    set rhighlights($id) $isbold
  }
  
  # Graph layout functions
@@@ -3365,82 -2682,40 +3365,82 @@@ 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
 +      }
 +      set id [first_real_child $curview,$p]
 +      if {$id eq {}} {
 +          # it's 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 {}
@@@ -3481,20 -2756,30 +3481,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 nullid 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 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
 +      if {[commitinview $nullid $curview]} {
 +          removerow $nullid $curview
 +      }
 +      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
 -      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]
            }
        }
      }
@@@ -3759,9 -3042,8 +3759,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]
                        }
@@@ -4041,7 -3323,7 +4041,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
@@@ -4338,14 -3620,14 +4338,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} {
      set date [formatdate $date]
      set font mainfont
      set nfont mainfont
 -    set isbold [ishighlighted $row]
 +    set isbold [ishighlighted $id]
      if {$isbold > 0} {
        lappend boldrows $row
        set font mainfontbold
  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 {$row >= $numcommits} return
  
      set id [lindex $displayorder $row]
 -    if {[info exists hlview] && ![info exists vhighlights($row)]} {
 +    if {[info exists hlview] && ![info exists vhighlights($id)]} {
        askvhighlight $row $id
      }
 -    if {[info exists filehighlight] && ![info exists fhighlights($row)]} {
 +    if {[info exists filehighlight] && ![info exists fhighlights($id)]} {
        askfilehighlight $row $id
      }
 -    if {$findpattern ne {} && ![info exists nhighlights($row)]} {
 +    if {$findpattern ne {} && ![info exists nhighlights($id)]} {
        askfindhighlight $row $id
      }
 -    if {$highlight_related ne [mc "None"] && ![info exists rhighlights($row)]} {
 +    if {$highlight_related ne [mc "None"] && ![info exists rhighlights($id)]} {
        askrelhighlight $row $id
      }
      if {![info exists iddrawn($id)]} {
@@@ -4550,77 -3832,23 +4550,77 @@@ proc drawcommits {row {endrow {}}} 
      }
  }
  
 -proc drawfrac {f0 f1} {
 -    global canv linespc
 +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 drawvisible {} {
 +    global canv linespc curview vrowmod selectedline targetrow targetid
 +    global need_redisplay cscroll numcommits
 +
 +    set fs [$canv yview]
      set ymax [lindex [$canv cget -scrollregion] 3]
      if {$ymax eq {} || $ymax == 0} return
 +    set f0 [lindex $fs 0]
 +    set f1 [lindex $fs 1]
      set y0 [expr {int($f0 * $ymax)}]
 -    set row [expr {int(($y0 - 3) / $linespc) - 1}]
      set y1 [expr {int($f1 * $ymax)}]
 +
 +    if {[info exists targetid]} {
 +      if {[commitinview $targetid $curview]} {
 +          set r [rowofcommit $targetid]
 +          if {$r != $targetrow} {
 +              # Fix up the scrollregion and change the scrolling position
 +              # now that our target row has moved.
 +              set diff [expr {($r - $targetrow) * $linespc}]
 +              set targetrow $r
 +              setcanvscroll
 +              set ymax [lindex [$canv cget -scrollregion] 3]
 +              incr y0 $diff
 +              incr y1 $diff
 +              set f0 [expr {$y0 / $ymax}]
 +              set f1 [expr {$y1 / $ymax}]
 +              allcanvs yview moveto $f0
 +              $cscroll set $f0 $f1
 +              set need_redisplay 1
 +          }
 +      } else {
 +          unset targetid
 +      }
 +    }
 +
 +    set row [expr {int(($y0 - 3) / $linespc) - 1}]
      set endrow [expr {int(($y1 - 3) / $linespc) + 1}]
 +    if {$endrow >= $vrowmod($curview)} {
 +      update_arcrows $curview
 +    }
 +    if {[info exists selectedline] &&
 +      $row <= $selectedline && $selectedline <= $endrow} {
 +      set targetrow $selectedline
 +    } else {
 +      set targetrow [expr {int(($row + $endrow) / 2)}]
 +    }
 +    if {$targetrow >= $numcommits} {
 +      set targetrow [expr {$numcommits - 1}]
 +    }
 +    set targetid [commitonrow $targetrow]
      drawcommits $row $endrow
  }
  
 -proc drawvisible {} {
 -    global canv
 -    eval drawfrac [$canv yview]
 -}
 -
  proc clear_display {} {
      global iddrawn linesegs need_redisplay nrows_drawn
      global vhighlights fhighlights nhighlights rhighlights
@@@ -4673,7 -3901,7 +4673,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)
@@@ -4746,7 -3974,7 +4746,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} {
@@@ -4850,6 -4078,103 +4850,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} {
@@@ -4952,9 -4277,9 +4952,9 @@@ 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 vrowmod
  
      if {![info exists find_dirn]} {
        return 0
        set n 500
        set moretodo 1
      }
 +    if {$l + ($find_dirn > 0? $n: 1) > $vrowmod($curview)} {
 +      update_arcrows $curview
 +    }
      set found 0
      set domore 1
 +    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
            }
        }
      } else {
        for {} {$n > 0} {incr n -1; incr l $find_dirn} {
 -          set id [lindex $displayorder $l]
 -          if {![info exists fhighlights($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($id)]} {
 +              # this sets fhighlights($id) to -1
                askfilehighlight $l $id
 +          }
 +          if {$fhighlights($id) > 0} {
 +              set found $domore
 +              break
 +          }
 +          if {$fhighlights($id) < 0} {
                if {$domore} {
                    set domore 0
                    set findcurline [expr {$l - $find_dirn}]
                }
 -          } elseif {$fhighlights($l)} {
 -              set found $domore
 -              break
            }
        }
      }
@@@ -5144,9 -4441,7 +5144,9 @@@ proc selcanvline {w x y} 
        set l 0
      }
      if {$w eq $canv} {
 -      if {![info exists rowtextx($l)] || $x < $rowtextx($l)} return
 +      set xmax [lindex [$canv cget -scrollregion] 2]
 +      set xleft [expr {[lindex [$canv xview] 0] * $xmax}]
 +      if {![info exists rowtextx($l)] || $xleft + $x < $rowtextx($l)} return
      }
      unmarkmatches
      selectline $l 1
@@@ -5167,7 -4462,7 +5167,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 {
@@@ -5240,7 -4535,7 +5240,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
@@@ -5343,7 -4638,8 +5343,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
  
      make_secsel $l
  
 +    set id [commitonrow $l]
      if {$isnew} {
 -      addtohistory [list selectline $l 0]
 +      addtohistory [list selbyid $id]
      }
  
      set selectedline $l
 -
 -    set id [lindex $displayorder $l]
      set currentid $id
      $sha1entry delete 0 end
      $sha1entry insert 0 $id
      }
  
      set headers {}
 -    set olds [lindex $parentlist $l]
 +    set olds $parents($curview,$id)
      if {[llength $olds] > 1} {
        set np 0
        foreach p $olds {
      } elseif {[llength $olds] <= 1} {
        startdiff $id
      } else {
 -      mergediff $id $l
 +      mergediff $id
      }
  }
  
@@@ -5726,10 -5023,10 +5726,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
      }
      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]
  }
@@@ -6373,7 -5670,7 +6373,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
@@@ -6424,7 -5721,8 +6424,7 @@@ proc sha1change {n1 n2 op} 
  }
  
  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 [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]} {
@@@ -6555,7 -5858,7 +6555,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
@@@ -6623,9 -5926,9 +6623,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
      }
  }
  
@@@ -6638,13 -5941,13 +6638,13 @@@ 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
  }
  
  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
@@@ -6852,24 -6155,24 +6852,24 @@@ proc domktag {} 
  }
  
  proc redrawtags {id} {
 -    global canv linehtag commitrow idpos selectedline curview
 +    global canv linehtag idpos currentid 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)
 +    set row [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]
 -    set xr [expr {$xt + [font measure mainfont $text]}]
 +    $canv coords $linehtag($row) $xt [lindex $idpos($id) 2]
 +    set text [$canv itemcget $linehtag($row) -text]
 +    set font [$canv itemcget $linehtag($row) -font]
 +    set xr [expr {$xt + [font measure $font $text]}]
      if {$xr > $canvxmax} {
        set canvxmax $xr
        setcanvscroll
      }
 -    if {[info exists selectedline]
 -      && $selectedline == $commitrow($curview,$id)} {
 -      selectline $selectedline 0
 +    if {[info exists currentid] && $currentid == $id} {
 +      make_secsel $row
      }
  }
  
@@@ -6995,7 -6298,7 +6995,7 @@@ proc mkbrgo {top} 
  }
  
  proc cherrypick {} {
 -    global rowmenuid curview commitrow
 +    global rowmenuid curview
      global mainhead
  
      set oldhead [exec git rev-parse HEAD]
        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
@@@ -7260,13 -6563,13 +7260,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}
@@@ -8428,7 -7731,7 +8428,7 @@@ proc changedrefs {} 
  }
  
  proc rereadrefs {} {
 -    global idtags idheads idotherrefs mainhead
 +    global idtags idheads idotherrefs mainheadid
  
      set refids [concat [array names idtags] \
                    [array names idheads] [array names idotherrefs]]
            set ref($id) [listrefs $id]
        }
      }
 -    set oldmainhead $mainhead
 +    set oldmainhead $mainheadid
      readrefs
      changedrefs
      set refids [lsort -unique [concat $refids [array names idtags] \
      foreach id $refids {
        set v [listrefs $id]
        if {![info exists ref($id)] || $ref($id) != $v ||
 -          ($id eq $oldmainhead && $id ne $mainhead) ||
 -          ($id eq $mainhead && $id ne $oldmainhead)} {
 +          ($id eq $oldmainhead && $id ne $mainheadid) ||
 +          ($id eq $mainheadid && $id ne $oldmainhead)} {
            redrawtags $id
        }
      }
@@@ -9321,11 -8624,12 +9321,11 @@@ 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