From: Paul Mackerras <paulus@samba.org>
Date: Sat, 16 Feb 2008 11:24:52 +0000 (+1100)
Subject: Merge branch 'master' into dev
X-Git-Tag: v1.5.6-rc0~54^2~14
X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/585fb5985dcc5c129cb9f095c73dce88c8aacf77?hp=-c

Merge branch 'master' into dev
---

585fb5985dcc5c129cb9f095c73dce88c8aacf77
diff --combined gitk
index 09f431b035,f1f21e97bf..1dd73f7d85
--- a/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,35 -85,24 +97,35 @@@ 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
 +    global pending_select mainheadid
  
      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 {[regexp {^[0-9a-fA-F]{40}$} $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}
      }
@@@ -133,139 -110,37 +133,139 @@@
      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
  	set progresscoords {0 0}
  	set proglastnc 0
 +	set pending_select $mainheadid
      }
  }
  
 -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 pending_select
 +
 +    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 {}
 +    set flags {}
 +    foreach c $commits {
 +	if {[string match "^*" $c]} {
 +	    lappend neg $c
 +	} elseif {[regexp {^[0-9a-fA-F]{40}$} $c]} {
 +	    if {!([info exists varcid($view,$c)] ||
 +		  [lsearch -exact $viewincl($view) $c] >= 0)} {
 +		lappend pos $c
 +	    }
 +	} else {
 +	    lappend flags $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 $flags "--" $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
 +    set pending_select $mainheadid
 +    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
 +    return 0
 +}
 +
  # This makes a string representation of a positive integer which
  # sorts as a string in numerical order
  proc strrep {n} {
@@@ -279,710 -154,46 +279,710 @@@
      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 cmitlisted children parents varcid varctok vtokmod
 +    global varccommits ordertok commitidx numcommits curview
 +    global targetid targetrow
 +
 +    readcommit $id
 +    set vid $v,$id
 +    set cmitlisted($vid) 1
 +    set children($vid) {}
 +    set parents($vid) [list $p]
 +    set a [newvarc $v $id]
 +    set varcid($vid) $a
 +    if {[string compare [lindex $varctok($v) $a] $vtokmod($v)] < 0} {
 +	modify_arc $v $a
 +    }
 +    lappend varccommits($v,$a) $id
 +    set vp $v,$p
 +    if {[llength [lappend children($vp) $id]] > 1} {
 +	set children($vp) [lsort -command [list vtokcmp $v] $children($vp)]
 +	catch {unset ordertok}
 +    }
 +    fix_reversal $p $a $v
 +    incr commitidx($v)
 +    if {$v == $curview} {
 +	set numcommits $commitidx($v)
 +	setcanvscroll
 +	if {[info exists targetid]} {
 +	    if {![comes_before $targetid $p]} {
 +		incr targetrow
 +	    }
 +	}
 +    }
 +}
 +
 +proc insertfakerow {id p} {
 +    global varcid varccommits parents children cmitlisted
 +    global commitidx varctok vtokmod targetid targetrow curview numcommits
 +
 +    set v $curview
 +    set a $varcid($v,$p)
 +    set i [lsearch -exact $varccommits($v,$a) $p]
 +    if {$i < 0} {
 +	puts "oops: insertfakerow 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
 +    set numcommits [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
 +	}
 +    }
 +    setcanvscroll
 +    drawvisible
 +}
 +
 +proc removefakerow {id} {
 +    global varcid varccommits parents children commitidx
 +    global varctok vtokmod cmitlisted currentid selectedline
 +    global targetid curview numcommits
 +
 +    set v $curview
 +    if {[llength $parents($v,$id)] != 1} {
 +	puts "oops: removefakerow [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: removefakerow 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)
 +    set numcommits [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
 +    }
 +    setcanvscroll
 +    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 [expr {$r + $al - 1}]] 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]} {
@@@ -1003,15 -214,6 +1003,15 @@@
  	    }
  	    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
  	}
@@@ -1019,17 -221,16 +1019,17 @@@
      }
      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}]]
  	}
@@@ -1039,11 -240,12 +1039,12 @@@
  	set listed 1
  	if {$j >= 0 && [string match "commit *" $cmit]} {
  	    set ids [string range $cmit 7 [expr {$j - 1}]]
- 	    if {[string match {[-<>]*} $ids]} {
+ 	    if {[string match {[-^<>]*} $ids]} {
  		switch -- [string index $ids 0] {
  		    "-" {set listed 0}
- 		    "<" {set listed 2}
- 		    ">" {set listed 3}
+ 		    "^" {set listed 2}
+ 		    "<" {set listed 3}
+ 		    ">" {set listed 4}
  		}
  		set ids [string range $ids 1 end]
  	    }
@@@ -1064,63 -266,57 +1065,63 @@@
  	    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
 +	set a 0
 +	if {[info exists varcid($vid)]} {
 +	    if {$cmitlisted($vid) || !$listed} continue
 +	    set a $varcid($vid)
  	}
  	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
 +	if {![info exists children($vid)]} {
 +	    set children($vid) {}
 +	} elseif {$a == 0 && [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]
 +	}
 +	if {[string compare [lindex $varctok($view) $a] $vtokmod($view)] < 0} {
 +	    modify_arc $view $a
 +	}
 +	if {![info exists varcid($vid)]} {
 +	    set varcid($vid) $a
 +	    lappend varccommits($view,$a) $id
 +	    incr commitidx($view)
 +	}
 +
 +	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
  	}
 +
  	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)
  	}
@@@ -1128,9 -324,6 +1129,9 @@@
      }
      if {$gotsome} {
  	run chewcommits $view
 +	foreach s $scripts {
 +	    eval $s
 +	}
  	if {$view == $curview} {
  	    # update progress bar
  	    global progressdirn progresscoords proglastnc
@@@ -1164,14 -357,13 +1165,14 @@@
  
  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]
@@@ -1180,11 -372,11 +1181,11 @@@
  	    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} {
@@@ -1198,6 -390,35 +1199,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
  
@@@ -1324,10 -545,10 +1325,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
  	}
@@@ -1407,11 -628,12 +1408,12 @@@ 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
      global diffcontextstring diffcontext
+     global ignorespace
      global maincursor textcursor curtextcursor
      global rowctxmenu fakerowmenu mergemax wrapcomment
      global highlight_files gdttype
@@@ -1426,7 -648,6 +1428,7 @@@
      .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
@@@ -1630,6 -851,9 +1632,9 @@@
      trace add variable diffcontextstring write diffcontextchange
      lappend entries .bleft.mid.diffcontext
      pack .bleft.mid.labeldiffcontext .bleft.mid.diffcontext -side left
+     checkbutton .bleft.mid.ignspace -text [mc "Ignore space change"] \
+ 	-command changeignorespace -variable ignorespace
+     pack .bleft.mid.ignspace -side left -padx 5
      set ctext .bleft.ctext
      text $ctext -background $bgcolor -foreground $fgcolor \
  	-state disabled -font textfont \
@@@ -1777,6 -1001,7 +1782,7 @@@
      bind . <$M1B-r> dosearchback
      bind . <$M1B-s> dosearch
      bind . <$M1B-equal> {incrfont 1}
+     bind . <$M1B-plus> {incrfont 1}
      bind . <$M1B-KP_Add> {incrfont 1}
      bind . <$M1B-minus> {incrfont -1}
      bind . <$M1B-KP_Subtract> {incrfont -1}
@@@ -1869,7 -1094,7 +1875,7 @@@ proc canvscan {op w x y} 
  
  proc scrollcanv {cscroll f0 f1} {
      $cscroll set $f0 $f1
 -    drawfrac $f0 $f1
 +    drawvisible
      flushhighlights
  }
  
@@@ -2087,45 -1312,45 +2093,45 @@@ proc keys {} 
      }
      toplevel $w
      wm title $w [mc "Gitk key bindings"]
-     message $w.m -text [mc "
- Gitk key bindings:
- 
- <$M1T-Q>		Quit
- <Home>		Move to first commit
- <End>		Move to last commit
- <Up>, p, i	Move up one commit
- <Down>, n, k	Move down one commit
- <Left>, z, j	Go back in history list
- <Right>, x, l	Go forward in history list
- <PageUp>	Move up one page in commit list
- <PageDown>	Move down one page in commit list
- <$M1T-Home>	Scroll to top of commit list
- <$M1T-End>	Scroll to bottom of commit list
- <$M1T-Up>	Scroll commit list up one line
- <$M1T-Down>	Scroll commit list down one line
- <$M1T-PageUp>	Scroll commit list up one page
- <$M1T-PageDown>	Scroll commit list down one page
- <Shift-Up>	Find backwards (upwards, later commits)
- <Shift-Down>	Find forwards (downwards, earlier commits)
- <Delete>, b	Scroll diff view up one page
- <Backspace>	Scroll diff view up one page
- <Space>		Scroll diff view down one page
- u		Scroll diff view up 18 lines
- d		Scroll diff view down 18 lines
- <$M1T-F>		Find
- <$M1T-G>		Move to next find hit
- <Return>	Move to next find hit
- /		Move to next find hit, or redo find
- ?		Move to previous find hit
- f		Scroll diff view to next file
- <$M1T-S>		Search for next hit in diff view
- <$M1T-R>		Search for previous hit in diff view
- <$M1T-KP+>	Increase font size
- <$M1T-plus>	Increase font size
- <$M1T-KP->	Decrease font size
- <$M1T-minus>	Decrease font size
- <F5>		Update
- "] \
+     message $w.m -text "
+ [mc "Gitk key bindings:"]
+ 
+ [mc "<%s-Q>		Quit" $M1T]
+ [mc "<Home>		Move to first commit"]
+ [mc "<End>		Move to last commit"]
+ [mc "<Up>, p, i	Move up one commit"]
+ [mc "<Down>, n, k	Move down one commit"]
+ [mc "<Left>, z, j	Go back in history list"]
+ [mc "<Right>, x, l	Go forward in history list"]
+ [mc "<PageUp>	Move up one page in commit list"]
+ [mc "<PageDown>	Move down one page in commit list"]
+ [mc "<%s-Home>	Scroll to top of commit list" $M1T]
+ [mc "<%s-End>	Scroll to bottom of commit list" $M1T]
+ [mc "<%s-Up>	Scroll commit list up one line" $M1T]
+ [mc "<%s-Down>	Scroll commit list down one line" $M1T]
+ [mc "<%s-PageUp>	Scroll commit list up one page" $M1T]
+ [mc "<%s-PageDown>	Scroll commit list down one page" $M1T]
+ [mc "<Shift-Up>	Find backwards (upwards, later commits)"]
+ [mc "<Shift-Down>	Find forwards (downwards, earlier commits)"]
+ [mc "<Delete>, b	Scroll diff view up one page"]
+ [mc "<Backspace>	Scroll diff view up one page"]
+ [mc "<Space>		Scroll diff view down one page"]
+ [mc "u		Scroll diff view up 18 lines"]
+ [mc "d		Scroll diff view down 18 lines"]
+ [mc "<%s-F>		Find" $M1T]
+ [mc "<%s-G>		Move to next find hit" $M1T]
+ [mc "<Return>	Move to next find hit"]
+ [mc "/		Move to next find hit, or redo find"]
+ [mc "?		Move to previous find hit"]
+ [mc "f		Scroll diff view to next file"]
+ [mc "<%s-S>		Search for next hit in diff view" $M1T]
+ [mc "<%s-R>		Search for previous hit in diff view" $M1T]
+ [mc "<%s-KP+>	Increase font size" $M1T]
+ [mc "<%s-plus>	Increase font size" $M1T]
+ [mc "<%s-KP->	Decrease font size" $M1T]
+ [mc "<%s-minus>	Decrease font size" $M1T]
+ [mc "<F5>		Update"]
+ " \
  	    -justify left -bg white -border 2 -relief groove
      pack $w.m -side top -fill both -padx 2 -pady 2
      button $w.ok -text [mc "Close"] -command "destroy $w" -default active
@@@ -2419,7 -1644,7 +2425,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
@@@ -2759,7 -1984,7 +2765,7 @@@ proc newviewok {top n} 
  	    set viewfiles($n) $files
  	    set viewargs($n) $newargs
  	    if {$curview == $n} {
 -		run updatecommits
 +		run reloadcommits
  	    }
  	}
      }
@@@ -2767,7 -1992,7 +2773,7 @@@
  }
  
  proc delview {} {
 -    global curview viewdata viewperm hlview selectedhlview
 +    global curview viewperm hlview selectedhlview
  
      if {$curview == 0} return
      if {[info exists hlview] && $hlview == $curview} {
@@@ -2775,6 -2000,7 +2781,6 @@@
  	unset hlview
      }
      allviewmenus $curview delete
 -    set viewdata($curview) {}
      set viewperm($curview) 0
      showview 0
  }
@@@ -2788,30 -2014,52 +2794,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 mainheadid
      global commitidx
 -    global commfd
 -    global selectedview selectfirst
 -    global vparentlist vdisporder vcmitlisted
 +    global selectedview
      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
@@@ -2819,6 -2067,17 +2825,6 @@@
      }
      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} {
@@@ -2826,8 -2085,6 +2832,8 @@@
  	set selectedhlview [mc "None"]
      }
      catch {unset commitinterest}
 +    catch {unset cached_commitrow}
 +    catch {unset ordertok}
  
      set curview $n
      set selectedview $n
@@@ -2835,7 -2092,7 +2841,7 @@@
      .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
  	}
@@@ -2843,11 -2100,14 +2849,11 @@@
  	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}
@@@ -2859,8 -2119,9 +2865,8 @@@
      setcanvscroll
      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}]
@@@ -2873,24 -2134,21 +2879,24 @@@
      drawvisible
      if {$row ne {}} {
  	selectline $row 0
 -    } elseif {$selid ne {}} {
 -	set pending_select $selid
 +    } elseif {$mainheadid ne {} && [commitinview $mainheadid $curview]} {
 +	selectline [rowofcommit $mainheadid] 1
 +    } elseif {!$viewcomplete($n)} {
 +	if {$selid ne {}} {
 +	    set pending_select $selid
 +	} else {
 +	    set pending_select $mainheadid
 +	}
      } else {
  	set row [first_real_row]
  	if {$row < $numcommits} {
  	    selectline $row 0
 -	} else {
 -	    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"]
      }
@@@ -2898,20 -2156,20 +2904,20 @@@
  
  # 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
  }
@@@ -2949,7 -2207,7 +2955,7 @@@ proc unbolden {} 
  
      set stillbold {}
      foreach row $boldrows {
 -	if {![ishighlighted $row]} {
 +	if {![ishighlighted [commitonrow $row]]} {
  	    bolden $row mainfont
  	} else {
  	    lappend stillbold $row
@@@ -2959,13 -2217,17 +2965,13 @@@
  }
  
  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)
@@@ -2984,21 -2246,27 +2990,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
  	    }
  	}
      }
@@@ -3006,15 -2274,15 +3012,15 @@@
  }
  
  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
      }
  }
  
@@@ -3152,12 -2420,12 +3158,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]} {
@@@ -3170,16 -2438,18 +3176,16 @@@
  	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...
@@@ -3228,7 -2498,7 +3234,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
@@@ -3238,7 -2508,7 +3244,7 @@@
  	    markrowmatches $row $id
  	}
      }
 -    set nhighlights($row) $isbold
 +    set nhighlights($id) $isbold
  }
  
  proc markrowmatches {row id} {
@@@ -3276,7 -2546,7 +3282,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]
@@@ -3296,16 -2566,16 +3302,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
  	}
@@@ -3328,20 -2598,20 +3334,20 @@@
  }
  
  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
@@@ -3365,12 -2635,12 +3371,12 @@@ proc askrelhighlight {row id} 
  
      if {![info exists selectedline]} return
      set isbold 0
-     if {$highlight_related eq [mc "Descendent"] ||
- 	$highlight_related eq [mc "Not descendent"]} {
+     if {$highlight_related eq [mc "Descendant"] ||
+ 	$highlight_related eq [mc "Not descendant"]} {
  	if {![info exists descendent($id)]} {
  	    is_descendent $id
  	}
- 	if {$descendent($id) == ($highlight_related eq [mc "Descendent"])} {
+ 	if {$descendent($id) == ($highlight_related eq [mc "Descendant"])} {
  	    set isbold 1
  	}
      } elseif {$highlight_related eq [mc "Ancestor"] ||
@@@ -3383,11 -2653,11 +3389,11 @@@
  	}
      }
      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
@@@ -3418,81 -2688,40 +3424,81 @@@ 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) $varcid($curview,$p)]
 +	    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 {}
@@@ -3501,6 -2730,7 +3507,6 @@@
      set canvxmax [$canv cget -width]
      catch {unset colormap}
      catch {unset rowtextx}
 -    set selectfirst 1
  }
  
  proc setcanvscroll {} {
@@@ -3532,20 -2762,30 +3538,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 commitidx viewcomplete curview
 +    global numcommits pending_select selectedline curview
      global 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
      }
@@@ -3558,46 -2798,65 +3564,46 @@@
  	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
 -    }
 -    if {$selectfirst} {
 -	if {[info exists selectedline] || [info exists pending_select]} {
 -	    set selectfirst 0
 -	} else {
 -	    set l [first_real_row]
 -	    selectline $l 1
 -	    set selectfirst 0
 -	}
 +	[commitinview $pending_select $curview]} {
 +	selectline [rowofcommit $pending_select] 1
      }
  }
  
  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]} {
 +	removefakerow $nullid
      }
 -    if {$localirow >= 0} {
 -	removerow $localirow
 -	set localirow -1
 +    if {[commitinview $nullid2 $curview]} {
 +	removefakerow $nullid2
      }
      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} {
@@@ -3609,32 -2868,26 +3615,32 @@@
      # 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]} {
 +	    removefakerow $nullid
 +	}
 +	insertfakerow $nullid2 $mainheadid
 +    } elseif {!$isdiff && [commitinview $nullid2 $curview]} {
 +	removefakerow $nullid2
      }
      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
@@@ -3647,55 -2900,50 +3653,55 @@@
      # 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
 +	}
 +	insertfakerow $nullid $p
 +    } elseif {!$isdiff && [commitinview $nullid $curview]} {
 +	removefakerow $nullid
      }
      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]
  	    }
  	}
      }
@@@ -3704,7 -2952,7 +3710,7 @@@
  
  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} {
@@@ -3718,7 -2966,6 +3724,7 @@@
      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}]]
@@@ -3727,7 -2974,7 +3733,7 @@@
  	    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]
  	    }
  	}
      }
@@@ -3737,25 -2984,25 +3743,25 @@@
  	    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]
  	    }
  	}
      }
@@@ -3801,9 -3048,8 +3807,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}]
@@@ -3859,7 -3105,7 +3865,7 @@@
  		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]
  		    }
@@@ -3868,7 -3114,7 +3874,7 @@@
  		    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]
  			}
@@@ -4083,7 -3329,7 +4089,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)
@@@ -4093,13 -3339,13 +4099,13 @@@
      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 &&
@@@ -4132,7 -3378,7 +4138,7 @@@
  	if {$child eq $id} {
  	    lappend ret $row
  	}
 -	set prev $id
 +	set prev $child
  	set prevrow $row
      }
      return $ret
@@@ -4380,29 -3626,29 +4386,29 @@@ 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
+     # listed is 0 for boundary, 1 for normal, 2 for negative, 3 for left, 4 for right
 -    set listed [lindex $commitlisted $row]
 +    set listed $cmitlisted($curview,$id)
      if {$id eq $nullid} {
  	set ofill red
      } elseif {$id eq $nullid2} {
  	set ofill green
      } else {
- 	set ofill [expr {$listed != 0? "blue": "white"}]
+ 	set ofill [expr {$listed != 0 ? $listed == 2 ? "gray" : "blue" : "white"}]
      }
      set x [xc $row $col]
      set y [yc $row]
      set orad [expr {$linespc / 3}]
-     if {$listed <= 1} {
+     if {$listed <= 2} {
  	set t [$canv create oval [expr {$x - $orad}] [expr {$y - $orad}] \
  		   [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
  		   -fill $ofill -outline $fgcolor -width 1 -tags circle]
-     } elseif {$listed == 2} {
+     } elseif {$listed == 3} {
  	# triangle pointing left for left-side commits
  	set t [$canv create polygon \
  		   [expr {$x - $orad}] $y \
@@@ -4443,7 -3689,7 +4449,7 @@@
      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
@@@ -4472,7 -3718,7 +4478,7 @@@
  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
@@@ -4480,16 -3726,16 +4486,16 @@@
      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)]} {
@@@ -4592,77 -3838,23 +4598,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
 +    if {$ymax eq {} || $ymax == 0 || $numcommits == 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
@@@ -4715,7 -3907,7 +4721,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]
@@@ -4727,7 -3919,7 +4733,7 @@@
      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
  	}
@@@ -4755,7 -3947,7 +4761,7 @@@
  		&& [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)
@@@ -4788,7 -3980,7 +4794,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
@@@ -4838,7 -4030,7 +4844,7 @@@
  		       $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} {
@@@ -4892,6 -4084,103 +4898,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} {
@@@ -4994,9 -4283,9 +5000,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
@@@ -5032,31 -4321,14 +5038,31 @@@
  	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
  	    }
@@@ -5072,27 -4344,16 +5078,27 @@@
  	}
      } 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
  	    }
  	}
      }
@@@ -5186,9 -4447,7 +5192,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
@@@ -5209,7 -4468,7 +5215,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
@@@ -5227,11 -4486,11 +5233,11 @@@
  }
  
  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 {
@@@ -5282,7 -4541,7 +5288,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
@@@ -5385,12 -4644,12 +5391,12 @@@ 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
      global cmitmode showneartags allcommits
 +    global targetrow targetid
  
      catch {unset pending_select}
      $canv delete hover
@@@ -5436,15 -4695,14 +5442,15 @@@
  
      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
 +    set targetid $id
 +    set targetrow $l
      $sha1entry delete 0 end
      $sha1entry insert 0 $id
      $sha1entry selection from 0
@@@ -5468,7 -4726,7 +5474,7 @@@
      }
  
      set headers {}
 -    set olds [lindex $parentlist $l]
 +    set olds $parents($curview,$id)
      if {[llength $olds] > 1} {
  	set np 0
  	foreach p $olds {
@@@ -5526,7 -4784,7 +5532,7 @@@
      } elseif {[llength $olds] <= 1} {
  	startdiff $id
      } else {
 -	mergediff $id $l
 +	mergediff $id
      }
  }
  
@@@ -5771,16 -5029,17 +5777,17 @@@ proc getblobline {bf id} 
      return [expr {$nl >= 1000? 2: 1}]
  }
  
 -proc mergediff {id l} {
 +proc mergediff {id} {
      global diffmergeid mdifffd
      global diffids
 +    global parents
+     global diffcontext
 -    global parentlist
      global limitdiffs viewfiles curview
  
      set diffmergeid $id
      set diffids $id
      # this doesn't seem to actually affect anything...
-     set cmd [concat | git diff-tree --no-commit-id --cc $id]
+     set cmd [concat | git diff-tree --no-commit-id --cc -U$diffcontext $id]
      if {$limitdiffs && $viewfiles($curview) ne {}} {
  	set cmd [concat $cmd -- $viewfiles($curview)]
      }
@@@ -5790,7 -5049,7 +5797,7 @@@
      }
      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]
  }
@@@ -6017,13 -5276,21 +6024,21 @@@ proc diffcontextchange {n1 n2 op} 
      }
  }
  
+ proc changeignorespace {} {
+     reselectline
+ }
+ 
  proc getblobdiffs {ids} {
      global blobdifffd diffids env
      global diffinhdr treediffs
      global diffcontext
+     global ignorespace
      global limitdiffs viewfiles curview
  
      set cmd [diffcmd $ids "-p -C --no-commit-id -U$diffcontext"]
+     if {$ignorespace} {
+ 	append cmd " -w"
+     }
      if {$limitdiffs && $viewfiles($curview) ne {}} {
  	set cmd [concat $cmd -- $viewfiles($curview)]
      }
@@@ -6418,7 -5685,7 +6433,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
@@@ -6469,7 -5736,8 +6484,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
@@@ -6480,18 -5748,23 +6495,18 @@@
      } 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]} {
@@@ -6600,7 -5873,7 +6615,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
@@@ -6668,9 -5941,9 +6683,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
      }
  }
  
@@@ -6683,13 -5956,13 +6698,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
@@@ -6707,15 -5980,15 +6722,15 @@@
  }
  
  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
@@@ -6878,11 -6151,7 +6893,7 @@@ proc domktag {} 
  	return
      }
      if {[catch {
- 	set dir [gitdir]
- 	set fname [file join $dir "refs/tags" $tag]
- 	set f [open $fname w]
- 	puts $f $id
- 	close $f
+ 	exec git tag $tag $id
      } err]} {
  	error_popup "[mc "Error creating tag:"] $err"
  	return
@@@ -6897,24 -6166,24 +6908,24 @@@
  }
  
  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
      }
  }
  
@@@ -7040,8 -6309,8 +7051,8 @@@ proc mkbrgo {top} 
  }
  
  proc cherrypick {} {
 -    global rowmenuid curview commitrow
 -    global mainhead
 +    global rowmenuid curview viewincl
 +    global mainhead mainheadid
  
      set oldhead [exec git rev-parse HEAD]
      set dheads [descheads $rowmenuid]
@@@ -7067,28 -6336,20 +7078,28 @@@
  	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
 +	    set mainheadid $newhead
 +	}
 +	# remove oldhead from viewincl and add newhead
 +	set i [lsearch -exact $viewincl($curview) $oldhead]
 +	if {$i >= 0} {
 +	    set viewincl($curview) [lreplace $viewincl($curview) $i $i]
  	}
 +	lappend viewincl($curview) $newhead
  	redrawtags $oldhead
  	redrawtags $newhead
 +	selbyid $newhead
      }
      notbusy cherrypick
  }
  
  proc resethead {} {
 -    global mainheadid mainhead rowmenuid confirm_ok resettype
 +    global mainhead rowmenuid confirm_ok resettype
  
      set confirm_ok 0
      set w ".confirmreset"
@@@ -7127,7 -6388,6 +7138,7 @@@
  	dohidelocalchanges
  	filerun $fd [list readresetstat $fd]
  	nowbusy reset [mc "Resetting"]
 +	selbyid $rowmenuid
      }
  }
  
@@@ -7200,7 -6460,6 +7211,7 @@@ proc cobranch {} 
  	    redrawtags $headids($oldmainhead)
  	}
  	redrawtags $headmenuid
 +	selbyid $headmenuid
      }
      if {$showlocalchanges} {
  	dodiffindex
@@@ -7315,13 -6574,13 +7326,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}
@@@ -7330,7 -6589,7 +7341,7 @@@
      }
      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}
@@@ -7339,7 -6598,7 +7350,7 @@@
      }
      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}
@@@ -8483,7 -7742,7 +8494,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]]
@@@ -8492,7 -7751,7 +8503,7 @@@
  	    set ref($id) [listrefs $id]
  	}
      }
 -    set oldmainhead $mainhead
 +    set oldmainhead $mainheadid
      readrefs
      changedrefs
      set refids [lsort -unique [concat $refids [array names idtags] \
@@@ -8500,8 -7759,8 +8511,8 @@@
      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
  	}
      }
@@@ -9210,6 -8469,7 +9221,7 @@@ set bgcolor whit
  set fgcolor black
  set diffcolors {red "#00a000" blue}
  set diffcontext 3
+ set ignorespace 0
  set selectbgcolor gray85
  
  ## For msgcat loading, first locate the installation location.
@@@ -9376,11 -8636,12 +9388,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