Merge branch 'new'
authorPaul Mackerras <paulus@samba.org>
Wed, 26 Apr 2006 06:32:59 +0000 (16:32 +1000)
committerPaul Mackerras <paulus@samba.org>
Wed, 26 Apr 2006 06:32:59 +0000 (16:32 +1000)
1  2 
gitk
diff --combined gitk
index 7e80a7b98484c537df9b4ecc1d02db0801f9415d,a33a1e8a94e1892d9f30dd8095af847f94fb2b1c..7c25d2ef9702b696fdec62b07943a868260dc4ea
--- 1/gitk
--- 2/gitk
+++ b/gitk
@@@ -16,21 -16,26 +16,26 @@@ proc gitdir {} 
      }
  }
  
- proc start_rev_list {rlargs} {
+ proc start_rev_list {} {
      global startmsecs nextupdate ncmupdate
      global commfd leftover tclencoding datemode
+     global revtreeargs curview viewfiles
  
      set startmsecs [clock clicks -milliseconds]
      set nextupdate [expr {$startmsecs + 100}]
      set ncmupdate 1
      initlayout
+     set args $revtreeargs
+     if {$viewfiles($curview) ne {}} {
+       set args [concat $args "--" $viewfiles($curview)]
+     }
      set order "--topo-order"
      if {$datemode} {
        set order "--date-order"
      }
      if {[catch {
        set commfd [open [concat | git-rev-list --header $order \
-                             --parents --boundary --default HEAD $rlargs] r]
+                             --parents --boundary --default HEAD $args] r]
      } err]} {
        puts stderr "Error executing git-rev-list: $err"
        exit 1
      settextcursor watch
  }
  
- proc getcommits {rargs} {
+ proc stop_rev_list {} {
+     global commfd
+     if {![info exists commfd]} return
+     catch {
+       set pid [pid $commfd]
+       exec kill $pid
+     }
+     catch {close $commfd}
+     unset commfd
+ }
+ proc getcommits {} {
      global phase canv mainfont
  
      set phase getcommits
-     start_rev_list $rargs
+     start_rev_list
      $canv delete all
      $canv create text 3 3 -anchor nw -text "Reading commits..." \
        -font $mainfont -tags textitems
@@@ -125,11 -142,12 +142,12 @@@ proc getcommitlines {commfd}  
        set id [lindex $ids 0]
        if {$listed} {
            set olds [lrange $ids 1 end]
-           if {[llength $olds] > 1} {
-               set olds [lsort -unique $olds]
-           }
+           set i 0
            foreach p $olds {
-               lappend children($p) $id
+               if {$i == 0 || [lsearch -exact $olds $p] >= $i} {
+                   lappend children($p) $id
+               }
+               incr i
            }
        } else {
            set olds {}
        lappend parentlist $olds
        if {[info exists children($id)]} {
            lappend childlist $children($id)
+           unset children($id)
        } else {
            lappend childlist {}
        }
@@@ -180,18 -199,18 +199,18 @@@ proc readcommit {id} 
      parsecommit $id $contents 0
  }
  
- proc updatecommits {rargs} {
-     stopfindproc
-     foreach v {colormap selectedline matchinglines treediffs
-       mergefilelist currentid rowtextx commitrow
-       rowidlist rowoffsets idrowranges idrangedrawn iddrawn
-       linesegends crossings cornercrossings} {
-       global $v
-       catch {unset $v}
+ proc updatecommits {} {
+     global viewdata curview revtreeargs phase
+     if {$phase ne {}} {
+       stop_rev_list
+       set phase {}
      }
-     allcanvs delete all
+     set n $curview
+     set curview -1
+     catch {unset viewdata($n)}
      readrefs
-     getcommits $rargs
+     showview $n
  }
  
  proc parsecommit {id contents listed} {
@@@ -274,16 -293,10 +293,16 @@@ proc readrefs {} 
            match id path]} {
            continue
        }
 +      if {[regexp {^remotes/.*/HEAD$} $path match]} {
 +          continue
 +      }
        if {![regexp {^(tags|heads)/(.*)$} $path match type name]} {
            set type others
            set name $path
        }
 +      if {[regexp {^remotes/} $path match]} {
 +          set type heads
 +      }
        if {$type == "tags"} {
            set tagids($name) $id
            lappend idtags($id) $name
@@@ -324,8 -337,9 +343,9 @@@ proc error_popup msg 
      tkwait window $w
  }
  
- proc makewindow {rargs} {
-     global canv canv2 canv3 linespc charspc ctext cflist textfont mainfont uifont
+ proc makewindow {} {
+     global canv canv2 canv3 linespc charspc ctext cflist
+     global textfont mainfont uifont
      global findtype findtypemenu findloc findstring fstring geometry
      global entries sha1entry sha1string sha1but
      global maincursor textcursor curtextcursor
      .bar add cascade -label "File" -menu .bar.file
      .bar configure -font $uifont
      menu .bar.file
-     .bar.file add command -label "Update" -command [list updatecommits $rargs]
+     .bar.file add command -label "Update" -command updatecommits
      .bar.file add command -label "Reread references" -command rereadrefs
      .bar.file add command -label "Quit" -command doquit
      .bar.file configure -font $uifont
      .bar add cascade -label "Edit" -menu .bar.edit
      .bar.edit add command -label "Preferences" -command doprefs
      .bar.edit configure -font $uifont
+     menu .bar.view -font $uifont
+     .bar add cascade -label "View" -menu .bar.view
+     .bar.view add command -label "New view..." -command newview
+     .bar.view add command -label "Edit view..." -command editview
+     .bar.view add command -label "Delete view" -command delview -state disabled
+     .bar.view add separator
+     .bar.view add radiobutton -label "All files" -command {showview 0} \
+       -variable selectedview -value 0
      menu .bar.help
      .bar add cascade -label "Help" -menu .bar.help
      .bar.help add command -label "About gitk" -command about
@@@ -612,6 -634,7 +640,7 @@@ proc savestuff {w} 
      global canv canv2 canv3 ctext cflist mainfont textfont uifont
      global stuffsaved findmergefiles maxgraphpct
      global maxwidth
+     global viewname viewfiles viewperm nextviewnum
  
      if {$stuffsaved} return
      if {![winfo viewable .]} return
        set wid [expr {([winfo width $cflist] - 11) \
                           / [font measure [$cflist cget -font] "0"]}]
        puts $f "set geometry(cflistw) $wid"
+       puts -nonewline $f "set permviews {"
+       for {set v 0} {$v < $nextviewnum} {incr v} {
+           if {$viewperm($v)} {
+               puts $f "{[list $viewname($v) $viewfiles($v)]}"
+           }
+       }
+       puts $f "}"
        close $f
        file rename -force "~/.gitk-new" "~/.gitk"
      }
@@@ -776,6 -806,280 +812,280 @@@ f              Scroll diff view to next fil
      pack $w.ok -side bottom
  }
  
+ proc newview {} {
+     global nextviewnum newviewname newviewperm uifont
+     set top .gitkview
+     if {[winfo exists $top]} {
+       raise $top
+       return
+     }
+     set newviewname($nextviewnum) "View $nextviewnum"
+     set newviewperm($nextviewnum) 0
+     vieweditor $top $nextviewnum "Gitk view definition" 
+ }
+ proc editview {} {
+     global curview
+     global viewname viewperm newviewname newviewperm
+     set top .gitkvedit-$curview
+     if {[winfo exists $top]} {
+       raise $top
+       return
+     }
+     set newviewname($curview) $viewname($curview)
+     set newviewperm($curview) $viewperm($curview)
+     vieweditor $top $curview "Gitk: edit view $viewname($curview)"
+ }
+ proc vieweditor {top n title} {
+     global newviewname newviewperm viewfiles
+     global uifont
+     toplevel $top
+     wm title $top $title
+     label $top.nl -text "Name" -font $uifont
+     entry $top.name -width 20 -textvariable newviewname($n)
+     grid $top.nl $top.name -sticky w -pady 5
+     checkbutton $top.perm -text "Remember this view" -variable newviewperm($n)
+     grid $top.perm - -pady 5 -sticky w
+     message $top.l -aspect 500 -font $uifont \
+       -text "Enter files and directories to include, one per line:"
+     grid $top.l - -sticky w
+     text $top.t -width 40 -height 10 -background white
+     if {[info exists viewfiles($n)]} {
+       foreach f $viewfiles($n) {
+           $top.t insert end $f
+           $top.t insert end "\n"
+       }
+       $top.t delete {end - 1c} end
+       $top.t mark set insert 0.0
+     }
+     grid $top.t - -sticky w -padx 5
+     frame $top.buts
+     button $top.buts.ok -text "OK" -command [list newviewok $top $n]
+     button $top.buts.can -text "Cancel" -command [list destroy $top]
+     grid $top.buts.ok $top.buts.can
+     grid columnconfigure $top.buts 0 -weight 1 -uniform a
+     grid columnconfigure $top.buts 1 -weight 1 -uniform a
+     grid $top.buts - -pady 10 -sticky ew
+     focus $top.t
+ }
+ proc viewmenuitem {n} {
+     set nmenu [.bar.view index end]
+     set targetcmd [list showview $n]
+     for {set i 6} {$i <= $nmenu} {incr i} {
+       if {[.bar.view entrycget $i -command] eq $targetcmd} {
+           return $i
+       }
+     }
+     return {}
+ }
+ proc newviewok {top n} {
+     global nextviewnum newviewperm newviewname
+     global viewname viewfiles viewperm selectedview curview
+     set files {}
+     foreach f [split [$top.t get 0.0 end] "\n"] {
+       set ft [string trim $f]
+       if {$ft ne {}} {
+           lappend files $ft
+       }
+     }
+     if {![info exists viewfiles($n)]} {
+       # creating a new view
+       incr nextviewnum
+       set viewname($n) $newviewname($n)
+       set viewperm($n) $newviewperm($n)
+       set viewfiles($n) $files
+       .bar.view add radiobutton -label $viewname($n) \
+           -command [list showview $n] -variable selectedview -value $n
+       after idle showview $n
+     } else {
+       # editing an existing view
+       set viewperm($n) $newviewperm($n)
+       if {$newviewname($n) ne $viewname($n)} {
+           set viewname($n) $newviewname($n)
+           set i [viewmenuitem $n]
+           if {$i ne {}} {
+               .bar.view entryconf $i -label $viewname($n)
+           }
+       }
+       if {$files ne $viewfiles($n)} {
+           set viewfiles($n) $files
+           if {$curview == $n} {
+               after idle updatecommits
+           }
+       }
+     }
+     catch {destroy $top}
+ }
+ proc delview {} {
+     global curview viewdata viewperm
+     if {$curview == 0} return
+     set i [viewmenuitem $curview]
+     if {$i ne {}} {
+       .bar.view delete $i
+     }
+     set viewdata($curview) {}
+     set viewperm($curview) 0
+     showview 0
+ }
+ 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 displayorder parentlist childlist rowidlist rowoffsets
+     global colormap rowtextx commitrow
+     global numcommits rowrangelist commitlisted idrowranges
+     global selectedline currentid canv canvy0
+     global matchinglines treediffs
+     global pending_select phase
+     global commitidx rowlaidout rowoptim linesegends leftover
+     global commfd nextupdate
+     global selectedview
+     if {$n == $curview} return
+     set selid {}
+     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}]
+       }
+     }
+     unselectline
+     normalline
+     stopfindproc
+     if {$curview >= 0} {
+       if {$phase ne {}} {
+           set viewdata($curview) \
+               [list $phase $displayorder $parentlist $childlist $rowidlist \
+                    $rowoffsets $rowrangelist $commitlisted \
+                    [flatten children] [flatten idrowranges] \
+                    [flatten idinlist] \
+                    $commitidx $rowlaidout $rowoptim $numcommits \
+                    $linesegends $leftover $commfd]
+           fileevent $commfd readable {}
+       } elseif {![info exists viewdata($curview)]
+                 || [lindex $viewdata($curview) 0] ne {}} {
+           set viewdata($curview) \
+               [list {} $displayorder $parentlist $childlist $rowidlist \
+                    $rowoffsets $rowrangelist $commitlisted]
+       }
+     }
+     catch {unset matchinglines}
+     catch {unset treediffs}
+     clear_display
+     set curview $n
+     set selectedview $n
+     .bar.view entryconf 2 -state [expr {$n == 0? "disabled": "normal"}]
+     .bar.view entryconf 3 -state [expr {$n == 0? "disabled": "normal"}]
+     if {![info exists viewdata($n)]} {
+       set pending_select $selid
+       getcommits
+       return
+     }
+     set v $viewdata($n)
+     set phase [lindex $v 0]
+     set displayorder [lindex $v 1]
+     set parentlist [lindex $v 2]
+     set childlist [lindex $v 3]
+     set rowidlist [lindex $v 4]
+     set rowoffsets [lindex $v 5]
+     set rowrangelist [lindex $v 6]
+     set commitlisted [lindex $v 7]
+     if {$phase eq {}} {
+       set numcommits [llength $displayorder]
+       catch {unset idrowranges}
+       catch {unset children}
+     } else {
+       unflatten children [lindex $v 8]
+       unflatten idrowranges [lindex $v 9]
+       unflatten idinlist [lindex $v 10]
+       set commitidx [lindex $v 11]
+       set rowlaidout [lindex $v 12]
+       set rowoptim [lindex $v 13]
+       set numcommits [lindex $v 14]
+       set linesegends [lindex $v 15]
+       set leftover [lindex $v 16]
+       set commfd [lindex $v 17]
+       fileevent $commfd readable [list getcommitlines $commfd]
+       set nextupdate [expr {[clock clicks -milliseconds] + 100}]
+     }
+     catch {unset colormap}
+     catch {unset rowtextx}
+     catch {unset commitrow}
+     set curview $n
+     set row 0
+     foreach id $displayorder {
+       set commitrow($id) $row
+       incr row
+     }
+     setcanvscroll
+     set yf 0
+     set row 0
+     if {$selid ne {} && [info exists commitrow($selid)]} {
+       set row $commitrow($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}]
+       if {$ytop < 0} {
+           set ytop 0
+       }
+       set yf [expr {$ytop * 1.0 / $ymax}]
+     }
+     allcanvs yview moveto $yf
+     drawvisible
+     selectline $row 0
+     if {$phase eq {}} {
+       global maincursor textcursor
+       . config -cursor $maincursor
+       settextcursor $textcursor
+     } else {
+       . config -cursor watch
+       settextcursor watch
+       if {$phase eq "getcommits"} {
+           global mainfont
+           $canv create text 3 3 -anchor nw -text "Reading commits..." \
+               -font $mainfont -tags textitems
+       }
+     }
+ }
  proc shortids {ids} {
      set res {}
      foreach id $ids {
@@@ -811,20 -1115,21 +1121,21 @@@ proc ntimes {n o} 
  }
  
  proc usedinrange {id l1 l2} {
-     global children commitrow
+     global children commitrow childlist
  
      if {[info exists commitrow($id)]} {
        set r $commitrow($id)
        if {$l1 <= $r && $r <= $l2} {
            return [expr {$r - $l1 + 1}]
        }
+       set kids [lindex $childlist $r]
+     } else {
+       set kids $children($id)
      }
-     foreach c $children($id) {
-       if {[info exists commitrow($c)]} {
-           set r $commitrow($c)
-           if {$l1 <= $r && $r <= $l2} {
-               return [expr {$r - $l1 + 1}]
-           }
+     foreach c $kids {
+       set r $commitrow($c)
+       if {$l1 <= $r && $r <= $l2} {
+           return [expr {$r - $l1 + 1}]
        }
      }
      return 0
@@@ -892,10 -1197,12 +1203,12 @@@ proc makeuparrow {oid x y z} 
  proc initlayout {} {
      global rowidlist rowoffsets displayorder commitlisted
      global rowlaidout rowoptim
-     global idinlist rowchk
+     global idinlist rowchk rowrangelist idrowranges
      global commitidx numcommits canvxmax canv
      global nextcolor
      global parentlist childlist children
+     global colormap rowtextx commitrow
+     global linesegends
  
      set commitidx 0
      set numcommits 0
      set commitlisted {}
      set parentlist {}
      set childlist {}
+     set rowrangelist {}
      catch {unset children}
      set nextcolor 0
      set rowidlist {{}}
      set rowlaidout 0
      set rowoptim 0
      set canvxmax [$canv cget -width]
+     catch {unset colormap}
+     catch {unset rowtextx}
+     catch {unset commitrow}
+     catch {unset idrowranges}
+     set linesegends {}
  }
  
  proc setcanvscroll {} {
@@@ -950,7 -1263,6 +1269,6 @@@ proc layoutmore {} 
      set rowlaidout [layoutrows $row $commitidx 0]
      set orow [expr {$rowlaidout - $uparrowlen - 1}]
      if {$orow > $rowoptim} {
-       checkcrossings $rowoptim $orow
        optimize_rows $rowoptim 0 $orow
        set rowoptim $orow
      }
  }
  
  proc showstuff {canshow} {
-     global numcommits
+     global numcommits commitrow pending_select selectedline
      global linesegends idrowranges idrangedrawn
  
      if {$numcommits == 0} {
      set rows [visiblerows]
      set r0 [lindex $rows 0]
      set r1 [lindex $rows 1]
+     set selrow -1
      for {set r $row} {$r < $canshow} {incr r} {
-       if {[info exists linesegends($r)]} {
-           foreach id $linesegends($r) {
-               set i -1
-               foreach {s e} $idrowranges($id) {
-                   incr i
-                   if {$e ne {} && $e < $numcommits && $s <= $r1 && $e >= $r0
-                       && ![info exists idrangedrawn($id,$i)]} {
-                       drawlineseg $id $i
-                       set idrangedrawn($id,$i) 1
-                   }
+       foreach id [lindex $linesegends [expr {$r+1}]] {
+           set i -1
+           foreach {s e} [rowranges $id] {
+               incr i
+               if {$e ne {} && $e < $numcommits && $s <= $r1 && $e >= $r0
+                   && ![info exists idrangedrawn($id,$i)]} {
+                   drawlineseg $id $i
+                   set idrangedrawn($id,$i) 1
                }
            }
        }
        drawcmitrow $row
        incr row
      }
+     if {[info exists pending_select] &&
+       [info exists commitrow($pending_select)] &&
+       $commitrow($pending_select) < $numcommits} {
+       selectline $commitrow($pending_select) 1
+     }
+     if {![info exists selectedline] && ![info exists pending_select]} {
+       selectline 0 1
+     }
  }
  
  proc layoutrows {row endrow last} {
      global childlist parentlist
      global idrowranges linesegends
      global commitidx
-     global idinlist rowchk
+     global idinlist rowchk rowrangelist
  
      set idlist [lindex $rowidlist $row]
      set offs [lindex $rowoffsets $row]
                lappend oldolds $p
            }
        }
+       set lse {}
        set nev [expr {[llength $idlist] + [llength $newolds]
                       + [llength $oldolds] - $maxwidth + 1}]
        if {$nev > 0} {
                        set offs [incrange $offs $x 1]
                        set idinlist($i) 0
                        set rm1 [expr {$row - 1}]
-                       lappend linesegends($rm1) $i
+                       lappend lse $i
                        lappend idrowranges($i) $rm1
                        if {[incr nev -1] <= 0} break
                        continue
            lset rowidlist $row $idlist
            lset rowoffsets $row $offs
        }
+       lappend linesegends $lse
        set col [lsearch -exact $idlist $id]
        if {$col < 0} {
            set col [llength $idlist]
        } else {
            unset idinlist($id)
        }
+       set ranges {}
        if {[info exists idrowranges($id)]} {
-           lappend idrowranges($id) $row
+           set ranges $idrowranges($id)
+           lappend ranges $row
+           unset idrowranges($id)
        }
+       lappend rowrangelist $ranges
        incr row
        set offs [ntimes [llength $idlist] 0]
        set l [llength $newolds]
@@@ -1120,6 -1445,7 +1451,7 @@@ proc addextraid {id row} 
      }
      if {[info exists children($id)]} {
        lappend childlist $children($id)
+       unset children($id)
      } else {
        lappend childlist {}
      }
  
  proc layouttail {} {
      global rowidlist rowoffsets idinlist commitidx
-     global idrowranges
+     global idrowranges rowrangelist
  
      set row $commitidx
      set idlist [lindex $rowidlist $row]
        addextraid $id $row
        unset idinlist($id)
        lappend idrowranges($id) $row
+       lappend rowrangelist $idrowranges($id)
+       unset idrowranges($id)
        incr row
        set offs [ntimes $col 0]
        set idlist [lreplace $idlist $col $col]
        lset rowoffsets $row 0
        makeuparrow $id 0 $row 0
        lappend idrowranges($id) $row
+       lappend rowrangelist $idrowranges($id)
+       unset idrowranges($id)
        incr row
        lappend rowidlist {}
        lappend rowoffsets {}
@@@ -1166,7 -1496,7 +1502,7 @@@ proc insert_pad {row col npad} 
  }
  
  proc optimize_rows {row col endrow} {
-     global rowidlist rowoffsets idrowranges linesegends displayorder
+     global rowidlist rowoffsets idrowranges displayorder
  
      for {} {$row < $endrow} {incr row} {
        set idlist [lindex $rowidlist $row]
            set z0 [lindex $rowoffsets $y0 $x0]
            if {$z0 eq {}} {
                set id [lindex $idlist $col]
-               if {[info exists idrowranges($id)] &&
-                   $y0 > [lindex $idrowranges($id) 0]} {
+               set ranges [rowranges $id]
+               if {$ranges ne {} && $y0 > [lindex $ranges 0]} {
                    set isarrow 1
                }
            }
                if {$o eq {}} {
                    # check if this is the link to the first child
                    set id [lindex $idlist $col]
-                   if {[info exists idrowranges($id)] &&
-                       $row == [lindex $idrowranges($id) 0]} {
+                   set ranges [rowranges $id]
+                   if {$ranges ne {} && $row == [lindex $ranges 0]} {
                        # it is, work out offset to child
                        set y0 [expr {$row - 1}]
                        set id [lindex $displayorder $y0]
@@@ -1299,13 -1629,34 +1635,34 @@@ proc linewidth {id} 
      return $wid
  }
  
+ proc rowranges {id} {
+     global phase idrowranges commitrow rowlaidout rowrangelist
+     set ranges {}
+     if {$phase eq {} ||
+       ([info exists commitrow($id)] && $commitrow($id) < $rowlaidout)} {
+       set ranges [lindex $rowrangelist $commitrow($id)]
+     } elseif {[info exists idrowranges($id)]} {
+       set ranges $idrowranges($id)
+     }
+     return $ranges
+ }
  proc drawlineseg {id i} {
-     global rowoffsets rowidlist idrowranges
+     global rowoffsets rowidlist
      global displayorder
      global canv colormap linespc
+     global numcommits commitrow
  
-     set startrow [lindex $idrowranges($id) [expr {2 * $i}]]
-     set row [lindex $idrowranges($id) [expr {2 * $i + 1}]]
+     set ranges [rowranges $id]
+     set downarrow 1
+     if {[info exists commitrow($id)] && $commitrow($id) < $numcommits} {
+       set downarrow [expr {$i < [llength $ranges] / 2 - 1}]
+     } else {
+       set downarrow 1
+     }
+     set startrow [lindex $ranges [expr {2 * $i}]]
+     set row [lindex $ranges [expr {2 * $i + 1}]]
      if {$startrow == $row} return
      assigncolor $id
      set coords {}
        }
      }
      if {[llength $coords] < 4} return
-     set last [expr {[llength $idrowranges($id)] / 2 - 1}]
-     if {$i < $last} {
+     if {$downarrow} {
        # This line has an arrow at the lower end: check if the arrow is
        # on a diagonal segment, and if so, work around the Tk 8.4
        # refusal to draw arrows on diagonal lines.
            }
        }
      }
-     set arrow [expr {2 * ($i > 0) + ($i < $last)}]
+     set arrow [expr {2 * ($i > 0) + $downarrow}]
      set arrow [lindex {none first last both} $arrow]
      set t [$canv create line $coords -width [linewidth $id] \
               -fill $colormap($id) -tags lines.$id -arrow $arrow]
  }
  
  proc drawparentlinks {id row col olds} {
-     global rowidlist canv colormap idrowranges
+     global rowidlist canv colormap
  
      set row2 [expr {$row + 1}]
      set x [xc $row $col]
        if {$x2 > $rmx} {
            set rmx $x2
        }
-       if {[info exists idrowranges($p)] &&
-           $row2 == [lindex $idrowranges($p) 0] &&
-           $row2 < [lindex $idrowranges($p) 1]} {
+       set ranges [rowranges $p]
+       if {$ranges ne {} && $row2 == [lindex $ranges 0]
+           && $row2 < [lindex $ranges 1]} {
            # drawlineseg will do this one for us
            continue
        }
  
  proc drawlines {id} {
      global colormap canv
-     global idrowranges idrangedrawn
+     global idrangedrawn
      global childlist iddrawn commitrow rowidlist
  
      $canv delete lines.$id
-     set nr [expr {[llength $idrowranges($id)] / 2}]
+     set nr [expr {[llength [rowranges $id]] / 2}]
      for {set i 0} {$i < $nr} {incr i} {
        if {[info exists idrangedrawn($id,$i)]} {
            drawlineseg $id $i
@@@ -1490,14 -1840,14 +1846,14 @@@ proc drawcmittext {id row col rmx} 
  
  proc drawcmitrow {row} {
      global displayorder rowidlist
-     global idrowranges idrangedrawn iddrawn
+     global idrangedrawn iddrawn
      global commitinfo parentlist numcommits
  
      if {$row >= $numcommits} return
      foreach id [lindex $rowidlist $row] {
-       if {![info exists idrowranges($id)]} continue
+       if {$id eq {}} continue
        set i -1
-       foreach {s e} $idrowranges($id) {
+       foreach {s e} [rowranges $id] {
            incr i
            if {$row < $s} continue
            if {$e eq {}} break
@@@ -1566,10 -1916,50 +1922,50 @@@ proc clear_display {} 
      catch {unset idrangedrawn}
  }
  
+ proc findcrossings {id} {
+     global rowidlist parentlist numcommits rowoffsets displayorder
+     set cross {}
+     set ccross {}
+     foreach {s e} [rowranges $id] {
+       if {$e >= $numcommits} {
+           set e [expr {$numcommits - 1}]
+       }
+       if {$e <= $s} continue
+       set x [lsearch -exact [lindex $rowidlist $e] $id]
+       if {$x < 0} {
+           puts "findcrossings: oops, no [shortids $id] in row $e"
+           continue
+       }
+       for {set row $e} {[incr row -1] >= $s} {} {
+           set olds [lindex $parentlist $row]
+           set kid [lindex $displayorder $row]
+           set kidx [lsearch -exact [lindex $rowidlist $row] $kid]
+           if {$kidx < 0} continue
+           set nextrow [lindex $rowidlist [expr {$row + 1}]]
+           foreach p $olds {
+               set px [lsearch -exact $nextrow $p]
+               if {$px < 0} continue
+               if {($kidx < $x && $x < $px) || ($px < $x && $x < $kidx)} {
+                   if {[lsearch -exact $ccross $p] >= 0} continue
+                   if {$x == $px + ($kidx < $px? -1: 1)} {
+                       lappend ccross $p
+                   } elseif {[lsearch -exact $cross $p] < 0} {
+                       lappend cross $p
+                   }
+               }
+           }
+           set inc [lindex $rowoffsets $row $x]
+           if {$inc eq {}} break
+           incr x $inc
+       }
+     }
+     return [concat $ccross {{}} $cross]
+ }
  proc assigncolor {id} {
      global colormap colors nextcolor
      global commitrow parentlist children childlist
-     global cornercrossings crossings
  
      if {[info exists colormap($id)]} return
      set ncolors [llength $colors]
        }
      }
      set badcolors {}
-     if {[info exists cornercrossings($id)]} {
-       foreach x $cornercrossings($id) {
-           if {[info exists colormap($x)]
-               && [lsearch -exact $badcolors $colormap($x)] < 0} {
-               lappend badcolors $colormap($x)
-           }
+     set origbad {}
+     foreach x [findcrossings $id] {
+       if {$x eq {}} {
+           # delimiter between corner crossings and other crossings
+           if {[llength $badcolors] >= $ncolors - 1} break
+           set origbad $badcolors
        }
-       if {[llength $badcolors] >= $ncolors} {
-           set badcolors {}
+       if {[info exists colormap($x)]
+           && [lsearch -exact $badcolors $colormap($x)] < 0} {
+           lappend badcolors $colormap($x)
        }
      }
-     set origbad $badcolors
-     if {[llength $badcolors] < $ncolors - 1} {
-       if {[info exists crossings($id)]} {
-           foreach x $crossings($id) {
-               if {[info exists colormap($x)]
-                   && [lsearch -exact $badcolors $colormap($x)] < 0} {
-                   lappend badcolors $colormap($x)
-               }
-           }
-           if {[llength $badcolors] >= $ncolors} {
-               set badcolors $origbad
-           }
-       }
-       set origbad $badcolors
+     if {[llength $badcolors] >= $ncolors} {
+       set badcolors $origbad
      }
+     set origbad $badcolors
      if {[llength $badcolors] < $ncolors - 1} {
        foreach child $kids {
            if {[info exists colormap($child)]
@@@ -1708,14 -2088,6 +2094,14 @@@ proc drawtags {id x xt y1} 
            set xl [expr {$xl - $delta/2}]
            $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
                -width 1 -outline black -fill $col -tags tag.$id
 +          if {[regexp {^(remotes/.*/|remotes/)} $tag match remoteprefix]} {
 +              set rwid [font measure $mainfont $remoteprefix]
 +              set xi [expr {$x + 1}]
 +              set yti [expr {$yt + 1}]
 +              set xri [expr {$x + $rwid}]
 +              $canv create polygon $xi $yti $xri $yti $xri $yb $xi $yb \
 +                      -width 0 -fill "#ffddaa" -tags tag.$id
 +          }
        }
        set t [$canv create text $xl $y1 -anchor w -text $tag \
                   -font $mainfont -tags tag.$id]
      return $xt
  }
  
- proc checkcrossings {row endrow} {
-     global displayorder parentlist rowidlist
-     for {} {$row < $endrow} {incr row} {
-       set id [lindex $displayorder $row]
-       set i [lsearch -exact [lindex $rowidlist $row] $id]
-       if {$i < 0} continue
-       set idlist [lindex $rowidlist [expr {$row+1}]]
-       foreach p [lindex $parentlist $row] {
-           set j [lsearch -exact $idlist $p]
-           if {$j > 0} {
-               if {$j < $i - 1} {
-                   notecrossings $row $p $j $i [expr {$j+1}]
-               } elseif {$j > $i + 1} {
-                   notecrossings $row $p $i $j [expr {$j-1}]
-               }
-           }
-       }
-     }
- }
- proc notecrossings {row id lo hi corner} {
-     global rowidlist crossings cornercrossings
-     for {set i $lo} {[incr i] < $hi} {} {
-       set p [lindex [lindex $rowidlist $row] $i]
-       if {$p == {}} continue
-       if {$i == $corner} {
-           if {![info exists cornercrossings($id)]
-               || [lsearch -exact $cornercrossings($id) $p] < 0} {
-               lappend cornercrossings($id) $p
-           }
-           if {![info exists cornercrossings($p)]
-               || [lsearch -exact $cornercrossings($p) $id] < 0} {
-               lappend cornercrossings($p) $id
-           }
-       } else {
-           if {![info exists crossings($id)]
-               || [lsearch -exact $crossings($id) $p] < 0} {
-               lappend crossings($id) $p
-           }
-           if {![info exists crossings($p)]
-               || [lsearch -exact $crossings($p) $id] < 0} {
-               lappend crossings($p) $id
-           }
-       }
-     }
- }
  proc xcoord {i level ln} {
      global canvx0 xspc1 xspc2
  
  proc finishcommits {} {
      global commitidx phase
      global canv mainfont ctext maincursor textcursor
-     global findinprogress
+     global findinprogress pending_select
  
      if {$commitidx > 0} {
        drawrest
        settextcursor $textcursor
      }
      set phase {}
+     catch {unset pending_select}
  }
  
  # Don't change the text pane cursor if it is currently the hand cursor,
@@@ -1822,12 -2146,16 +2160,16 @@@ proc drawrest {} 
      global startmsecs
      global canvy0 numcommits linespc
      global rowlaidout commitidx
+     global pending_select
  
      set row $rowlaidout
      layoutrows $rowlaidout $commitidx 1
      layouttail
      optimize_rows $row 0 $commitidx
      showstuff $commitidx
+     if {[info exists pending_select]} {
+       selectline 0 1
+     }
  
      set drawmsecs [expr {[clock clicks -milliseconds] - $startmsecs}]
      #puts "overall $drawmsecs ms for $numcommits commits"
@@@ -2017,7 -2345,7 +2359,7 @@@ proc stopfindproc {{done 0}} 
      }
      if {[info exists findinprogress]} {
        unset findinprogress
-       if {$phase != "incrdraw"} {
+       if {$phase eq {}} {
            . config -cursor $maincursor
            settextcursor $textcursor
        }
@@@ -2378,8 -2706,9 +2720,9 @@@ proc selectline {l isnew} 
      global canvy0 linespc parentlist childlist
      global cflist currentid sha1entry
      global commentend idtags linknum
-     global mergemax numcommits
+     global mergemax numcommits pending_select
  
+     catch {unset pending_select}
      $canv delete hover
      normalline
      if {$l < 0 || $l >= $numcommits} return
@@@ -2547,24 -2876,26 +2890,26 @@@ proc selnextpage {dir} 
  }
  
  proc unselectline {} {
-     global selectedline
+     global selectedline currentid
  
      catch {unset selectedline}
+     catch {unset currentid}
      allcanvs delete secsel
  }
  
  proc addtohistory {cmd} {
-     global history historyindex
+     global history historyindex curview
  
+     set elt [list $curview $cmd]
      if {$historyindex > 0
-       && [lindex $history [expr {$historyindex - 1}]] == $cmd} {
+       && [lindex $history [expr {$historyindex - 1}]] == $elt} {
        return
      }
  
      if {$historyindex < [llength $history]} {
-       set history [lreplace $history $historyindex end $cmd]
+       set history [lreplace $history $historyindex end $elt]
      } else {
-       lappend history $cmd
+       lappend history $elt
      }
      incr historyindex
      if {$historyindex > 1} {
      .ctop.top.bar.rightbut conf -state disabled
  }
  
+ proc godo {elt} {
+     global curview
+     set view [lindex $elt 0]
+     set cmd [lindex $elt 1]
+     if {$curview != $view} {
+       showview $view
+     }
+     eval $cmd
+ }
  proc goback {} {
      global history historyindex
  
      if {$historyindex > 1} {
        incr historyindex -1
-       set cmd [lindex $history [expr {$historyindex - 1}]]
-       eval $cmd
+       godo [lindex $history [expr {$historyindex - 1}]]
        .ctop.top.bar.rightbut conf -state normal
      }
      if {$historyindex <= 1} {
@@@ -2595,7 -2936,7 +2950,7 @@@ proc goforw {} 
      if {$historyindex < [llength $history]} {
        set cmd [lindex $history $historyindex]
        incr historyindex
-       eval $cmd
+       godo $cmd
        .ctop.top.bar.leftbut conf -state normal
      }
      if {$historyindex >= [llength $history]} {
@@@ -2931,7 -3272,7 +3286,7 @@@ proc incrfont {inc} 
      foreach e $entries {
        $e conf -font $mainfont
      }
-     if {$phase == "getcommits"} {
+     if {$phase eq "getcommits"} {
        $canv itemconf textitems -font $mainfont
      }
      redisplay
@@@ -3064,12 -3405,13 +3419,13 @@@ proc linehover {} 
  }
  
  proc clickisonarrow {id y} {
-     global lthickness idrowranges
+     global lthickness
  
+     set ranges [rowranges $id]
      set thresh [expr {2 * $lthickness + 6}]
-     set n [expr {[llength $idrowranges($id)] - 1}]
+     set n [expr {[llength $ranges] - 1}]
      for {set i 1} {$i < $n} {incr i} {
-       set row [lindex $idrowranges($id) $i]
+       set row [lindex $ranges $i]
        if {abs([yc $row] - $y) < $thresh} {
            return $i
        }
  }
  
  proc arrowjump {id n y} {
-     global idrowranges canv
+     global canv
  
      # 1 <-> 2, 3 <-> 4, etc...
      set n [expr {(($n - 1) ^ 1) + 1}]
-     set row [lindex $idrowranges($id) $n]
+     set row [lindex [rowranges $id] $n]
      set yt [yc $row]
      set ymax [lindex [$canv cget -scrollregion] 3]
      if {$ymax eq {} || $ymax <= 0} return
@@@ -3935,10 -4277,53 +4291,53 @@@ set historyindex 
  
  set optim_delay 16
  
+ set nextviewnum 1
+ set curview 0
+ set selectedview 0
+ set viewfiles(0) {}
+ set viewperm(0) 0
  set stopped 0
  set stuffsaved 0
  set patchnum 0
  setcoords
- makewindow $revtreeargs
+ makewindow
  readrefs
- getcommits $revtreeargs
+ set cmdline_files {}
+ catch {
+     set fileargs [eval exec git-rev-parse --no-revs --no-flags $revtreeargs]
+     set cmdline_files [split $fileargs "\n"]
+     set n [llength $cmdline_files]
+     set revtreeargs [lrange $revtreeargs 0 end-$n]
+ }
+ if {[lindex $revtreeargs end] eq "--"} {
+     set revtreeargs [lrange $revtreeargs 0 end-1]
+ }
+ if {$cmdline_files ne {}} {
+     # create a view for the files/dirs specified on the command line
+     set curview 1
+     set selectedview 1
+     set nextviewnum 2
+     set viewname(1) "Command line"
+     set viewfiles(1) $cmdline_files
+     set viewperm(1) 0
+     .bar.view add radiobutton -label $viewname(1) -command {showview 1} \
+       -variable selectedview -value 1
+     .bar.view entryconf 2 -state normal
+     .bar.view entryconf 3 -state normal
+ }
+ if {[info exists permviews]} {
+     foreach v $permviews {
+       set n $nextviewnum
+       incr nextviewnum
+       set viewname($n) [lindex $v 0]
+       set viewfiles($n) [lindex $v 1]
+       set viewperm($n) 1
+       .bar.view add radiobutton -label $viewname($n) \
+           -command [list showview $n] -variable selectedview -value $n
+     }
+ }
+ getcommits