Merge branch 'master' into dev
authorPaul Mackerras <paulus@samba.org>
Mon, 20 Aug 2007 10:00:19 +0000 (20:00 +1000)
committerPaul Mackerras <paulus@samba.org>
Mon, 20 Aug 2007 10:00:19 +0000 (20:00 +1000)
1  2 
gitk
diff --combined gitk
index f5b2da3a88f831b6188d3f994313cf4c2253bf2d,b7730ae20224f8d91484caf40470ec3025aa0c48..d2f5eeeaaf81483a78a3d60a8f4d13203d4fded4
--- 1/gitk
--- 2/gitk
+++ b/gitk
@@@ -82,12 -82,11 +82,12 @@@ proc dorunq {} 
  proc start_rev_list {view} {
      global startmsecs
      global commfd leftover tclencoding datemode
 -    global viewargs viewfiles commitidx
 +    global viewargs viewfiles commitidx vnextroot
      global lookingforhead showlocalchanges
  
      set startmsecs [clock clicks -milliseconds]
      set commitidx($view) 0
 +    set vnextroot($view) 0
      set order "--topo-order"
      if {$datemode} {
        set order "--date-order"
@@@ -132,26 -131,12 +132,26 @@@ proc getcommits {} 
      show_status "Reading commits..."
  }
  
 +# This makes a string representation of a positive integer which
 +# sorts as a string in numerical order
 +proc strrep {n} {
 +    if {$n < 16} {
 +      return [format "%x" $n]
 +    } elseif {$n < 256} {
 +      return [format "x%.2x" $n]
 +    } elseif {$n < 65536} {
 +      return [format "y%.4x" $n]
 +    }
 +    return [format "z%.8x" $n]
 +}
 +
  proc getcommitlines {fd view}  {
      global commitlisted
      global leftover commfd
      global displayorder commitidx commitrow commitdata
      global parentlist children curview hlview
      global vparentlist vdisporder vcmitlisted
 +    global ordertok vnextroot
  
      set stuff [read $fd 500000]
      # git log doesn't terminate the last commit with a null...
            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)
 +      }
        if {$listed} {
            set olds [lrange $ids 1 end]
 -          set i 0
 -          foreach p $olds {
 -              if {$i == 0 || [lsearch -exact $olds $p] >= $i} {
 -                  lappend children($view,$p) $id
 +          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)
 +              }
 +          } 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]]"
 +                  }
 +                  incr i
                }
 -              incr i
            }
        } else {
            set olds {}
@@@ -552,6 -519,7 +552,7 @@@ proc makewindow {} 
      global textfont mainfont uifont tabstop
      global findtype findtypemenu findloc findstring fstring geometry
      global entries sha1entry sha1string sha1but
+     global diffcontextstring diffcontext
      global maincursor textcursor curtextcursor
      global rowctxmenu fakerowmenu mergemax wrapcomment
      global highlight_files gdttype
      menu .bar.file
      .bar.file add command -label "Update" -command updatecommits
      .bar.file add command -label "Reread references" -command rereadrefs
+     .bar.file add command -label "List references" -command showrefs
      .bar.file add command -label "Quit" -command doquit
      .bar.file configure -font $uifont
      menu .bar.edit
        -command changediffdisp -variable diffelide -value {0 1}
      radiobutton .bleft.mid.new -text "New version" \
        -command changediffdisp -variable diffelide -value {1 0}
+     label .bleft.mid.labeldiffcontext -text "      Lines of context: " \
+       -font $uifont
      pack .bleft.mid.diff .bleft.mid.old .bleft.mid.new -side left
+     spinbox .bleft.mid.diffcontext -width 5 -font $textfont \
+       -from 1 -increment 1 -to 10000000 \
+       -validate all -validatecommand "diffcontextvalidate %P" \
+       -textvariable diffcontextstring
+     .bleft.mid.diffcontext set $diffcontext
+     trace add variable diffcontextstring write diffcontextchange
+     lappend entries .bleft.mid.diffcontext
+     pack .bleft.mid.labeldiffcontext .bleft.mid.diffcontext -side left
      set ctext .bleft.ctext
      text $ctext -background $bgcolor -foreground $fgcolor \
        -tabs "[expr {$tabstop * $charspc}]" \
@@@ -1034,8 -1013,8 +1046,8 @@@ proc savestuff {w} 
      global stuffsaved findmergefiles maxgraphpct
      global maxwidth showneartags showlocalchanges
      global viewname viewfiles viewargs viewperm nextviewnum
-     global cmitmode wrapcomment
-     global colors bgcolor fgcolor diffcolors selectbgcolor
+     global cmitmode wrapcomment datetimeformat
+     global colors bgcolor fgcolor diffcolors diffcontext selectbgcolor
  
      if {$stuffsaved} return
      if {![winfo viewable .]} return
        puts $f [list set wrapcomment $wrapcomment]
        puts $f [list set showneartags $showneartags]
        puts $f [list set showlocalchanges $showlocalchanges]
+       puts $f [list set datetimeformat $datetimeformat]
        puts $f [list set bgcolor $bgcolor]
        puts $f [list set fgcolor $fgcolor]
        puts $f [list set colors $colors]
        puts $f [list set diffcolors $diffcolors]
+       puts $f [list set diffcontext $diffcontext]
        puts $f [list set selectbgcolor $selectbgcolor]
  
        puts $f "set geometry(main) [wm geometry .]"
@@@ -1486,6 -1467,38 +1500,38 @@@ image create bitmap tri-dn -background 
         0x00, 0x00};
  }
  
+ image create bitmap reficon-T -background black -foreground yellow -data {
+     #define tagicon_width 13
+     #define tagicon_height 9
+     static unsigned char tagicon_bits[] = {
+        0x00, 0x00, 0x00, 0x00, 0xf0, 0x07, 0xf8, 0x07,
+        0xfc, 0x07, 0xf8, 0x07, 0xf0, 0x07, 0x00, 0x00, 0x00, 0x00};
+ } -maskdata {
+     #define tagicon-mask_width 13
+     #define tagicon-mask_height 9
+     static unsigned char tagicon-mask_bits[] = {
+        0x00, 0x00, 0xf0, 0x0f, 0xf8, 0x0f, 0xfc, 0x0f,
+        0xfe, 0x0f, 0xfc, 0x0f, 0xf8, 0x0f, 0xf0, 0x0f, 0x00, 0x00};
+ }
+ set rectdata {
+     #define headicon_width 13
+     #define headicon_height 9
+     static unsigned char headicon_bits[] = {
+        0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0xf8, 0x07,
+        0xf8, 0x07, 0xf8, 0x07, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00};
+ }
+ set rectmask {
+     #define headicon-mask_width 13
+     #define headicon-mask_height 9
+     static unsigned char headicon-mask_bits[] = {
+        0x00, 0x00, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f,
+        0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0x00, 0x00};
+ }
+ image create bitmap reficon-H -background black -foreground green \
+     -data $rectdata -maskdata $rectmask
+ image create bitmap reficon-o -background black -foreground "#ddddff" \
+     -data $rectdata -maskdata $rectmask
  proc init_flist {first} {
      global cflist cflist_top selectedline difffilestart
  
@@@ -1879,7 -1892,7 +1925,7 @@@ proc unflatten {var l} 
  
  proc showview {n} {
      global curview viewdata viewfiles
 -    global displayorder parentlist rowidlist rowoffsets
 +    global displayorder parentlist rowidlist
      global colormap rowtextx commitrow nextcolor canvxmax
      global numcommits rowrangelist commitlisted idrowranges rowchk
      global selectedline currentid canv canvy0
        set vcmitlisted($curview) $commitlisted
        if {$phase ne {}} {
            set viewdata($curview) \
 -              [list $phase $rowidlist $rowoffsets $rowrangelist \
 +              [list $phase $rowidlist {} $rowrangelist \
                     [flatten idrowranges] [flatten idinlist] \
                     $rowlaidout $rowoptim $numcommits]
        } elseif {![info exists viewdata($curview)]
                  || [lindex $viewdata($curview) 0] ne {}} {
            set viewdata($curview) \
 -              [list {} $rowidlist $rowoffsets $rowrangelist]
 +              [list {} $rowidlist {} $rowrangelist]
        }
      }
      catch {unset treediffs}
      set parentlist $vparentlist($n)
      set commitlisted $vcmitlisted($n)
      set rowidlist [lindex $v 1]
 -    set rowoffsets [lindex $v 2]
      set rowrangelist [lindex $v 3]
      if {$phase eq {}} {
        set numcommits [llength $displayorder]
      } elseif {$numcommits == 0} {
        show_status "No commits selected"
      }
+     run refill_reflist
  }
  
  # Stuff relating to the highlighting facility
@@@ -2599,43 -2614,67 +2646,43 @@@ proc usedinrange {id l1 l2} 
      return 0
  }
  
 -proc sanity {row {full 0}} {
 -    global rowidlist rowoffsets
 +# 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 col -1
 -    set ids [lindex $rowidlist $row]
 -    foreach id $ids {
 -      incr col
 -      if {$id eq {}} continue
 -      if {$col < [llength $ids] - 1 &&
 -          [lsearch -exact -start [expr {$col+1}] $ids $id] >= 0} {
 -          puts "oops: [shortids $id] repeated in row $row col $col: {[shortids [lindex $rowidlist $row]]}"
 -      }
 -      set o [lindex $rowoffsets $row $col]
 -      set y $row
 -      set x $col
 -      while {$o ne {}} {
 -          incr y -1
 -          incr x $o
 -          if {[lindex $rowidlist $y $x] != $id} {
 -              puts "oops: rowoffsets wrong at row [expr {$y+1}] col [expr {$x-$o}]"
 -              puts "  id=[shortids $id] check started at row $row"
 -              for {set i $row} {$i >= $y} {incr i -1} {
 -                  puts "  row $i ids={[shortids [lindex $rowidlist $i]]} offs={[lindex $rowoffsets $i]}"
 -              }
 -              break
 -          }
 -          if {!$full} break
 -          set o [lindex $rowoffsets $y $x]
 +    set t $ordertok($curview,$id)
 +    if {$i >= [llength $idlist] ||
 +      $t < $ordertok($curview,[lindex $idlist $i])} {
 +      if {$i > [llength $idlist]} {
 +          set i [llength $idlist]
 +      }
 +      while {[incr i -1] >= 0 &&
 +             $t < $ordertok($curview,[lindex $idlist $i])} {}
 +      incr i
 +    } else {
 +      if {$t > $ordertok($curview,[lindex $idlist $i])} {
 +          while {[incr i] < [llength $idlist] &&
 +                 $t >= $ordertok($curview,[lindex $idlist $i])} {}
        }
      }
 +    return $i
  }
  
 -proc makeuparrow {oid x y z} {
 -    global rowidlist rowoffsets uparrowlen idrowranges displayorder
 +proc makeuparrow {oid y x} {
 +    global rowidlist uparrowlen idrowranges displayorder
  
 -    for {set i 1} {$i < $uparrowlen && $y > 1} {incr i} {
 +    for {set i 0} {$i < $uparrowlen && $y > 1} {incr i} {
        incr y -1
 -      incr x $z
 -      set off0 [lindex $rowoffsets $y]
 -      for {set x0 $x} {1} {incr x0} {
 -          if {$x0 >= [llength $off0]} {
 -              set x0 [llength [lindex $rowoffsets [expr {$y-1}]]]
 -              break
 -          }
 -          set z [lindex $off0 $x0]
 -          if {$z ne {}} {
 -              incr x0 $z
 -              break
 -          }
 -      }
 -      set z [expr {$x0 - $x}]
 -      lset rowidlist $y [linsert [lindex $rowidlist $y] $x $oid]
 -      lset rowoffsets $y [linsert [lindex $rowoffsets $y] $x $z]
 +      set idl [lindex $rowidlist $y]
 +      set x [idcol $idl $oid $x]
 +      lset rowidlist $y [linsert $idl $x $oid]
      }
 -    set tmp [lreplace [lindex $rowoffsets $y] $x $x {}]
 -    lset rowoffsets $y [incrange $tmp [expr {$x+1}] -1]
      lappend idrowranges($oid) [lindex $displayorder $y]
  }
  
  proc initlayout {} {
 -    global rowidlist rowoffsets displayorder commitlisted
 +    global rowidlist displayorder commitlisted
      global rowlaidout rowoptim
      global idinlist rowchk rowrangelist idrowranges
      global numcommits canvxmax canv
      set rowrangelist {}
      set nextcolor 0
      set rowidlist {{}}
 -    set rowoffsets {{}}
      catch {unset idinlist}
      catch {unset rowchk}
      set rowlaidout 0
@@@ -2711,8 -2751,8 +2758,8 @@@ proc layoutmore {tmax allread} 
            set nr [expr {$commitidx($curview) - $rowlaidout}]
            # may need to increase this threshold if uparrowlen or
            # mingaplen are increased...
 -          if {$nr > 150} {
 -              set nr 150
 +          if {$nr > 200} {
 +              set nr 200
            }
            set row $rowlaidout
            set rowlaidout [layoutrows $row [expr {$row + $nr}] $allread]
  proc showstuff {canshow last} {
      global numcommits commitrow pending_select selectedline curview
      global lookingforhead mainheadid displayorder selectfirst
-     global lastscrollset
+     global lastscrollset commitinterest
  
      if {$numcommits == 0} {
        global phase
        set phase "incrdraw"
        allcanvs delete all
      }
+     for {set l $numcommits} {$l < $canshow} {incr l} {
+       set id [lindex $displayorder $l]
+       if {[info exists commitinterest($id)]} {
+           foreach script $commitinterest($id) {
+               eval [string map [list "%I" $id] $script]
+           }
+           unset commitinterest($id)
+       }
+     }
      set r0 $numcommits
      set prev $numcommits
      set numcommits $canshow
@@@ -2893,7 -2942,7 +2949,7 @@@ proc readdifffiles {fd serial} 
  }
  
  proc layoutrows {row endrow last} {
 -    global rowidlist rowoffsets displayorder
 +    global rowidlist displayorder
      global uparrowlen downarrowlen maxwidth mingaplen
      global children parentlist
      global idrowranges
      global idinlist rowchk rowrangelist
  
      set idlist [lindex $rowidlist $row]
 -    set offs [lindex $rowoffsets $row]
      while {$row < $endrow} {
        set id [lindex $displayorder $row]
 -      set nev [expr {[llength $idlist] - $maxwidth + 1}]
 -      foreach p [lindex $parentlist $row] {
 -          if {![info exists idinlist($p)] || !$idinlist($p)} {
 -              incr nev
 -          }
 -      }
 -      if {$nev > 0} {
 +      if {1} {
            if {!$last &&
                $row + $uparrowlen + $mingaplen >= $commitidx($curview)} break
            for {set x [llength $idlist]} {[incr x -1] >= 0} {} {
                               [expr {$row + $uparrowlen + $mingaplen}]]
                    if {$r == 0} {
                        set idlist [lreplace $idlist $x $x]
 -                      set offs [lreplace $offs $x $x]
 -                      set offs [incrange $offs $x 1]
                        set idinlist($i) 0
                        set rm1 [expr {$row - 1}]
                        lappend idrowranges($i) [lindex $displayorder $rm1]
 -                      if {[incr nev -1] <= 0} break
                        continue
                    }
                    set rowchk($i) [expr {$row + $r}]
                }
            }
            lset rowidlist $row $idlist
 -          lset rowoffsets $row $offs
        }
        set oldolds {}
        set newolds {}
        }
        set col [lsearch -exact $idlist $id]
        if {$col < 0} {
 -          set col [llength $idlist]
 -          lappend idlist $id
 +          set col [idcol $idlist $id]
 +          set idlist [linsert $idlist $col $id]
            lset rowidlist $row $idlist
 -          set z {}
            if {$children($curview,$id) ne {}} {
 -              set z [expr {[llength [lindex $rowidlist [expr {$row-1}]]] - $col}]
                unset idinlist($id)
 -          }
 -          lappend offs $z
 -          lset rowoffsets $row $offs
 -          if {$z ne {}} {
 -              makeuparrow $id $col $row $z
 +              makeuparrow $id $row $col
            }
        } else {
            unset idinlist($id)
        }
        lappend rowrangelist $ranges
        incr row
 -      set offs [ntimes [llength $idlist] 0]
 -      set l [llength $newolds]
 -      set idlist [eval lreplace \$idlist $col $col $newolds]
 -      set o 0
 -      if {$l != 1} {
 -          set offs [lrange $offs 0 [expr {$col - 1}]]
 -          foreach x $newolds {
 -              lappend offs {}
 -              incr o -1
 -          }
 -          incr o
 -          set tmp [expr {[llength $idlist] - [llength $offs]}]
 -          if {$tmp > 0} {
 -              set offs [concat $offs [ntimes $tmp $o]]
 -          }
 -      } else {
 -          lset offs $col {}
 -      }
 +      set idlist [lreplace $idlist $col $col]
 +      set x $col
        foreach i $newolds {
 +          set x [idcol $idlist $i $x]
 +          set idlist [linsert $idlist $x $i]
            set idrowranges($i) $id
        }
 -      incr col $l
        foreach oid $oldolds {
 -          set idlist [linsert $idlist $col $oid]
 -          set offs [linsert $offs $col $o]
 -          makeuparrow $oid $col $row $o
 -          incr col
 +          set x [idcol $idlist $oid $x]
 +          set idlist [linsert $idlist $x $oid]
 +          makeuparrow $oid $row $x
        }
        lappend rowidlist $idlist
 -      lappend rowoffsets $offs
      }
      return $row
  }
@@@ -2990,7 -3073,7 +3046,7 @@@ proc addextraid {id row} 
  }
  
  proc layouttail {} {
 -    global rowidlist rowoffsets idinlist commitidx curview
 +    global rowidlist idinlist commitidx curview
      global idrowranges rowrangelist
  
      set row $commitidx($curview)
        lappend rowrangelist $idrowranges($id)
        unset idrowranges($id)
        incr row
 -      set offs [ntimes $col 0]
        set idlist [lreplace $idlist $col $col]
        lappend rowidlist $idlist
 -      lappend rowoffsets $offs
      }
  
      foreach id [array names idinlist] {
        unset idinlist($id)
        addextraid $id $row
        lset rowidlist $row [list $id]
 -      lset rowoffsets $row 0
 -      makeuparrow $id 0 $row 0
 +      makeuparrow $id $row 0
        lappend idrowranges($id) $id
        lappend rowrangelist $idrowranges($id)
        unset idrowranges($id)
        incr row
        lappend rowidlist {}
 -      lappend rowoffsets {}
      }
  }
  
  proc insert_pad {row col npad} {
 -    global rowidlist rowoffsets
 +    global rowidlist
  
      set pad [ntimes $npad {}]
 -    lset rowidlist $row [eval linsert [list [lindex $rowidlist $row]] $col $pad]
 -    set tmp [eval linsert [list [lindex $rowoffsets $row]] $col $pad]
 -    lset rowoffsets $row [incrange $tmp [expr {$col + $npad}] [expr {-$npad}]]
 +    set idlist [lindex $rowidlist $row]
 +    set bef [lrange $idlist 0 [expr {$col - 1}]]
 +    set aft [lrange $idlist $col end]
 +    set i [lsearch -exact $aft {}]
 +    if {$i > 0} {
 +      set aft [lreplace $aft $i $i]
 +    }
 +    lset rowidlist $row [concat $bef $pad $aft]
  }
  
  proc optimize_rows {row col endrow} {
 -    global rowidlist rowoffsets displayorder
 +    global rowidlist displayorder
  
 +    if {$row < 1} {
 +      set row 1
 +    }
 +    set idlist [lindex $rowidlist [expr {$row - 1}]]
 +    if {$row >= 2} {
 +      set previdlist [lindex $rowidlist [expr {$row - 2}]]
 +    } else {
 +      set previdlist {}
 +    }
      for {} {$row < $endrow} {incr row} {
 +      set pprevidlist $previdlist
 +      set previdlist $idlist
        set idlist [lindex $rowidlist $row]
 -      set offs [lindex $rowoffsets $row]
        set haspad 0
 -      for {} {$col < [llength $offs]} {incr col} {
 -          if {[lindex $idlist $col] eq {}} {
 +      set y0 [expr {$row - 1}]
 +      set ym [expr {$row - 2}]
 +      set x0 -1
 +      set xm -1
 +      for {} {$col < [llength $idlist]} {incr col} {
 +          set id [lindex $idlist $col]
 +          if {[lindex $previdlist $col] eq $id} continue
 +          if {$id eq {}} {
                set haspad 1
                continue
            }
 -          set z [lindex $offs $col]
 -          if {$z eq {}} continue
 +          set x0 [lsearch -exact $previdlist $id]
 +          if {$x0 < 0} continue
 +          set z [expr {$x0 - $col}]
            set isarrow 0
 -          set x0 [expr {$col + $z}]
 -          set y0 [expr {$row - 1}]
 -          set z0 [lindex $rowoffsets $y0 $x0]
 +          set z0 {}
 +          if {$ym >= 0} {
 +              set xm [lsearch -exact $pprevidlist $id]
 +              if {$xm >= 0} {
 +                  set z0 [expr {$xm - $x0}]
 +              }
 +          }
            if {$z0 eq {}} {
 -              set id [lindex $idlist $col]
                set ranges [rowranges $id]
                if {$ranges ne {} && $y0 > [lindex $ranges 0]} {
                    set isarrow 1
                }
            }
 +          if {!$isarrow && $id ne [lindex $displayorder $row] &&
 +              [lsearch -exact [lindex $rowidlist [expr {$row+1}]] $id] < 0} {
 +              set isarrow 1
 +          }
            # Looking at lines from this row to the previous row,
            # make them go straight up if they end in an arrow on
            # the previous row; otherwise make them go straight up
                # Line currently goes left too much;
                # insert pads in the previous row, then optimize it
                set npad [expr {-1 - $z + $isarrow}]
 -              set offs [incrange $offs $col $npad]
                insert_pad $y0 $x0 $npad
                if {$y0 > 0} {
                    optimize_rows $y0 $x0 $row
                }
 -              set z [lindex $offs $col]
 -              set x0 [expr {$col + $z}]
 -              set z0 [lindex $rowoffsets $y0 $x0]
 +              set previdlist [lindex $rowidlist $y0]
 +              set x0 [lsearch -exact $previdlist $id]
 +              set z [expr {$x0 - $col}]
 +              if {$z0 ne {}} {
 +                  set pprevidlist [lindex $rowidlist $ym]
 +                  set xm [lsearch -exact $pprevidlist $id]
 +                  set z0 [expr {$xm - $x0}]
 +              }
            } elseif {$z > 1 || ($z > 0 && $isarrow)} {
                # Line currently goes right too much;
 -              # insert pads in this line and adjust the next's rowoffsets
 +              # insert pads in this line
                set npad [expr {$z - 1 + $isarrow}]
 -              set y1 [expr {$row + 1}]
 -              set offs2 [lindex $rowoffsets $y1]
 -              set x1 -1
 -              foreach z $offs2 {
 -                  incr x1
 -                  if {$z eq {} || $x1 + $z < $col} continue
 -                  if {$x1 + $z > $col} {
 -                      incr npad
 -                  }
 -                  lset rowoffsets $y1 [incrange $offs2 $x1 $npad]
 -                  break
 -              }
 -              set pad [ntimes $npad {}]
 -              set idlist [eval linsert \$idlist $col $pad]
 -              set tmp [eval linsert \$offs $col $pad]
 +              insert_pad $row $col $npad
 +              set idlist [lindex $rowidlist $row]
                incr col $npad
 -              set offs [incrange $tmp $col [expr {-$npad}]]
 -              set z [lindex $offs $col]
 +              set z [expr {$x0 - $col}]
                set haspad 1
            }
 -          if {$z0 eq {} && !$isarrow} {
 +          if {$z0 eq {} && !$isarrow && $ym >= 0} {
                # this line links to its first child on row $row-2
 -              set rm2 [expr {$row - 2}]
 -              set id [lindex $displayorder $rm2]
 -              set xc [lsearch -exact [lindex $rowidlist $rm2] $id]
 +              set id [lindex $displayorder $ym]
 +              set xc [lsearch -exact $pprevidlist $id]
                if {$xc >= 0} {
                    set z0 [expr {$xc - $x0}]
                }
            # avoid lines jigging left then immediately right
            if {$z0 ne {} && $z < 0 && $z0 > 0} {
                insert_pad $y0 $x0 1
 -              set offs [incrange $offs $col 1]
 -              optimize_rows $y0 [expr {$x0 + 1}] $row
 +              incr x0
 +              optimize_rows $y0 $x0 $row
 +              set previdlist [lindex $rowidlist $y0]
 +              set pprevidlist [lindex $rowidlist $ym]
            }
        }
        if {!$haspad} {
 -          set o {}
            # Find the first column that doesn't have a line going right
            for {set col [llength $idlist]} {[incr col -1] >= 0} {} {
 -              set o [lindex $offs $col]
 -              if {$o eq {}} {
 +              set id [lindex $idlist $col]
 +              if {$id eq {}} break
 +              set x0 [lsearch -exact $previdlist $id]
 +              if {$x0 < 0} {
                    # check if this is the link to the first child
 -                  set id [lindex $idlist $col]
                    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]
 -                      set x0 [lsearch -exact [lindex $rowidlist $y0] $id]
 -                      if {$x0 >= 0} {
 -                          set o [expr {$x0 - $col}]
 -                      }
 +                      set x0 [lsearch -exact $previdlist $id]
                    }
                }
 -              if {$o eq {} || $o <= 0} break
 +              if {$x0 <= $col} break
            }
            # Insert a pad at that column as long as it has a line and
 -          # isn't the last column, and adjust the next row' offsets
 -          if {$o ne {} && [incr col] < [llength $idlist]} {
 -              set y1 [expr {$row + 1}]
 -              set offs2 [lindex $rowoffsets $y1]
 -              set x1 -1
 -              foreach z $offs2 {
 -                  incr x1
 -                  if {$z eq {} || $x1 + $z < $col} continue
 -                  lset rowoffsets $y1 [incrange $offs2 $x1 1]
 -                  break
 -              }
 +          # isn't the last column
 +          if {$x0 >= 0 && [incr col] < [llength $idlist]} {
                set idlist [linsert $idlist $col {}]
 -              set tmp [linsert $offs $col {}]
 -              incr col
 -              set offs [incrange $tmp $col -1]
            }
        }
        lset rowidlist $row $idlist
 -      lset rowoffsets $row $offs
        set col 0
      }
  }
@@@ -3200,9 -3284,31 +3256,9 @@@ proc rowranges {id} 
      return $linenos
  }
  
 -# work around tk8.4 refusal to draw arrows on diagonal segments
 -proc adjarrowhigh {coords} {
 -    global linespc
 -
 -    set x0 [lindex $coords 0]
 -    set x1 [lindex $coords 2]
 -    if {$x0 != $x1} {
 -      set y0 [lindex $coords 1]
 -      set y1 [lindex $coords 3]
 -      if {$y0 - $y1 <= 2 * $linespc && $x1 == [lindex $coords 4]} {
 -          # we have a nearby vertical segment, just trim off the diag bit
 -          set coords [lrange $coords 2 end]
 -      } else {
 -          set slope [expr {($x0 - $x1) / ($y0 - $y1)}]
 -          set xi [expr {$x0 - $slope * $linespc / 2}]
 -          set yi [expr {$y0 - $linespc / 2}]
 -          set coords [lreplace $coords 0 1 $xi $y0 $xi $yi]
 -      }
 -    }
 -    return $coords
 -}
 -
  proc drawlineseg {id row endrow arrowlow} {
      global rowidlist displayorder iddrawn linesegs
 -    global canv colormap linespc curview maxlinelen
 +    global canv colormap linespc curview maxlinelen parentlist
  
      set cols [list [lsearch -exact [lindex $rowidlist $row] $id]]
      set le [expr {$row + 1}]
        set itl [lindex $lines [expr {$i-1}] 2]
        set al [$canv itemcget $itl -arrow]
        set arrowlow [expr {$al eq "last" || $al eq "both"}]
 -    } elseif {$arrowlow &&
 -            [lsearch -exact [lindex $rowidlist [expr {$row-1}]] $id] >= 0} {
 -      set arrowlow 0
 +    } elseif {$arrowlow} {
 +      if {[lsearch -exact [lindex $rowidlist [expr {$row-1}]] $id] >= 0 ||
 +          [lsearch -exact [lindex $parentlist [expr {$row-1}]] $id] >= 0} {
 +          set arrowlow 0
 +      }
      }
      set arrow [lindex {none first last both} [expr {$arrowhigh + 2*$arrowlow}]]
      for {set y $le} {[incr y -1] > $row} {} {
            set xc [lsearch -exact [lindex $rowidlist $row] $ch]
            if {$xc < 0} {
                puts "oops: drawlineseg: child $ch not on row $row"
 -          } else {
 -              if {$xc < $x - 1} {
 +          } elseif {$xc != $x} {
 +              if {($arrowhigh && $le == $row + 1) || $dir == 0} {
 +                  set d [expr {int(0.5 * $linespc)}]
 +                  set x1 [xc $row $x]
 +                  if {$xc < $x} {
 +                      set x2 [expr {$x1 - $d}]
 +                  } else {
 +                      set x2 [expr {$x1 + $d}]
 +                  }
 +                  set y2 [yc $row]
 +                  set y1 [expr {$y2 + $d}]
 +                  lappend coords $x1 $y1 $x2 $y2
 +              } elseif {$xc < $x - 1} {
                    lappend coords [xc $row [expr {$x-1}]] [yc $row]
                } elseif {$xc > $x + 1} {
                    lappend coords [xc $row [expr {$x+1}]] [yc $row]
        } else {
            set xn [xc $row $xp]
            set yn [yc $row]
 -          # work around tk8.4 refusal to draw arrows on diagonal segments
 -          if {$arrowlow && $xn != [lindex $coords end-1]} {
 -              if {[llength $coords] < 4 ||
 -                  [lindex $coords end-3] != [lindex $coords end-1] ||
 -                  [lindex $coords end] - $yn > 2 * $linespc} {
 -                  set xn [xc $row [expr {$xp - 0.5 * $dir}]]
 -                  set yo [yc [expr {$row + 0.5}]]
 -                  lappend coords $xn $yo $xn $yn
 -              }
 -          } else {
 -              lappend coords $xn $yn
 -          }
 +          lappend coords $xn $yn
        }
        if {!$joinhigh} {
 -          if {$arrowhigh} {
 -              set coords [adjarrowhigh $coords]
 -          }
            assigncolor $id
            set t [$canv create line $coords -width [linewidth $id] \
                       -fill $colormap($id) -tags lines.$id -arrow $arrow]
        set coords [concat $coords $clow]
        if {!$joinhigh} {
            lset lines [expr {$i-1}] 1 $le
 -          if {$arrowhigh} {
 -              set coords [adjarrowhigh $coords]
 -          }
        } else {
            # coalesce two pieces
            $canv delete $ith
  
  proc drawparentlinks {id row} {
      global rowidlist canv colormap curview parentlist
 -    global idpos
 +    global idpos linespc
  
      set rowids [lindex $rowidlist $row]
      set col [lsearch -exact $rowids $id]
      set x [xc $row $col]
      set y [yc $row]
      set y2 [yc $row2]
 +    set d [expr {int(0.5 * $linespc)}]
 +    set ymid [expr {$y + $d}]
      set ids [lindex $rowidlist $row2]
      # rmx = right-most X coord used
      set rmx 0
        if {$x2 > $rmx} {
            set rmx $x2
        }
 -      if {[lsearch -exact $rowids $p] < 0} {
 +      set j [lsearch -exact $rowids $p]
 +      if {$j < 0} {
            # drawlineseg will do this one for us
            continue
        }
        assigncolor $p
        # should handle duplicated parents here...
        set coords [list $x $y]
 -      if {$i < $col - 1} {
 -          lappend coords [xc $row [expr {$i + 1}]] $y
 -      } elseif {$i > $col + 1} {
 -          lappend coords [xc $row [expr {$i - 1}]] $y
 +      if {$i != $col} {
 +          # if attaching to a vertical segment, draw a smaller
 +          # slant for visual distinctness
 +          if {$i == $j} {
 +              if {$i < $col} {
 +                  lappend coords [expr {$x2 + $d}] $y $x2 $ymid
 +              } else {
 +                  lappend coords [expr {$x2 - $d}] $y $x2 $ymid
 +              }
 +          } elseif {$i < $col && $i < $j} {
 +              # segment slants towards us already
 +              lappend coords [xc $row $j] $y
 +          } else {
 +              if {$i < $col - 1} {
 +                  lappend coords [expr {$x2 + $linespc}] $y
 +              } elseif {$i > $col + 1} {
 +                  lappend coords [expr {$x2 - $linespc}] $y
 +              }
 +              lappend coords $x2 $y2
 +          }
 +      } else {
 +          lappend coords $x2 $y2
        }
 -      lappend coords $x2 $y2
        set t [$canv create line $coords -width [linewidth $p] \
                   -fill $colormap($p) -tags lines.$p]
        $canv lower $t
@@@ -3663,7 -3753,7 +3719,7 @@@ proc clear_display {} 
  }
  
  proc findcrossings {id} {
 -    global rowidlist parentlist numcommits rowoffsets displayorder
 +    global rowidlist parentlist numcommits displayorder
  
      set cross {}
      set ccross {}
            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 x [lsearch -exact [lindex $rowidlist $row] $id]
 +          if {$x < 0} break
            set olds [lindex $parentlist $row]
            set kid [lindex $displayorder $row]
            set kidx [lsearch -exact [lindex $rowidlist $row] $kid]
                    }
                }
            }
 -          set inc [lindex $rowoffsets $row $x]
 -          if {$inc eq {}} break
 -          incr x $inc
        }
      }
      return [concat $ccross {{}} $cross]
@@@ -3881,7 -3977,7 +3937,7 @@@ proc show_status {msg} 
  # on that row and below will move down one row.
  proc insertrow {row newcmit} {
      global displayorder parentlist commitlisted children
 -    global commitrow curview rowidlist rowoffsets numcommits
 +    global commitrow curview rowidlist numcommits
      global rowrangelist rowlaidout rowoptim numcommits
      global selectedline rowchk commitidx
  
      incr commitidx($curview)
  
      set idlist [lindex $rowidlist $row]
 -    set offs [lindex $rowoffsets $row]
 -    set newoffs {}
 -    foreach x $idlist {
 -      if {$x eq {} || ($x eq $p && [llength $kids] == 1)} {
 -          lappend newoffs {}
 -      } else {
 -          lappend newoffs 0
 -      }
 -    }
      if {[llength $kids] == 1} {
        set col [lsearch -exact $idlist $p]
        lset idlist $col $newcmit
      } else {
        set col [llength $idlist]
        lappend idlist $newcmit
 -      lappend offs {}
 -      lset rowoffsets $row $offs
      }
      set rowidlist [linsert $rowidlist $row $idlist]
 -    set rowoffsets [linsert $rowoffsets [expr {$row+1}] $newoffs]
  
      set rowrangelist [linsert $rowrangelist $row {}]
      if {[llength $kids] > 1} {
  # Remove a commit that was inserted with insertrow on row $row.
  proc removerow {row} {
      global displayorder parentlist commitlisted children
 -    global commitrow curview rowidlist rowoffsets numcommits
 +    global commitrow curview rowidlist numcommits
      global rowrangelist idrowranges rowlaidout rowoptim numcommits
      global linesegends selectedline rowchk commitidx
  
      incr commitidx($curview) -1
  
      set rowidlist [lreplace $rowidlist $row $row]
 -    set rowoffsets [lreplace $rowoffsets $rp1 $rp1]
 -    if {$kids ne {}} {
 -      set offs [lindex $rowoffsets $row]
 -      set offs [lreplace $offs end end]
 -      lset rowoffsets $row $offs
 -    }
  
      set rowrangelist [lreplace $rowrangelist $row $row]
      if {[llength $kids] > 0} {
@@@ -4413,6 -4527,7 +4469,7 @@@ proc selectline {l isnew} 
      $canv delete hover
      normalline
      cancel_next_highlight
+     unsel_reflist
      if {$l < 0 || $l >= $numcommits} return
      set y [expr {$canvy0 + $l * $linespc}]
      set ymax [lindex [$canv cget -scrollregion] 3]
@@@ -4994,12 -5109,29 +5051,29 @@@ proc gettreediffline {gdtf ids} 
      return 0
  }
  
+ # empty string or positive integer
+ proc diffcontextvalidate {v} {
+     return [regexp {^(|[1-9][0-9]*)$} $v]
+ }
+ proc diffcontextchange {n1 n2 op} {
+     global diffcontextstring diffcontext
+     if {[string is integer -strict $diffcontextstring]} {
+       if {$diffcontextstring > 0} {
+           set diffcontext $diffcontextstring
+           reselectline
+       }
+     }
+ }
  proc getblobdiffs {ids} {
      global diffopts blobdifffd diffids env
      global diffinhdr treediffs
+     global diffcontext
  
      set env(GIT_DIFF_OPTS) $diffopts
-     if {[catch {set bdf [open [diffcmd $ids {-p -C --no-commit-id}] r]} err]} {
+     if {[catch {set bdf [open [diffcmd $ids "-p -C --no-commit-id -U$diffcontext"] r]} err]} {
        puts "error getting diffs: $err"
        return
      }
@@@ -5058,8 -5190,8 +5132,8 @@@ proc getblobdiffline {bdf ids} 
            # the middle char will be a space, and the two bits either
            # side will be a/name and b/name, or "a/name" and "b/name".
            # If the name has changed we'll get "rename from" and
-           # "rename to" lines following this, and we'll use them
-           # to get the filenames.
+           # "rename to" or "copy from" and "copy to" lines following this,
+           # and we'll use them to get the filenames.
            # This complexity is necessary because spaces in the filename(s)
            # don't get escaped.
            set l [string length $line]
            set diffinhdr 0
  
        } elseif {$diffinhdr} {
-           if {![string compare -length 12 "rename from " $line]} {
-               set fname [string range $line 12 end]
+           if {![string compare -length 12 "rename from " $line] ||
+               ![string compare -length 10 "copy from " $line]} {
+               set fname [string range $line [expr 6 + [string first " from " $line] ] end]
                if {[string index $fname 0] eq "\""} {
                    set fname [lindex $fname 0]
                }
                if {$i >= 0} {
                    setinlist difffilestart $i $curdiffstart
                }
-           } elseif {![string compare -length 10 $line "rename to "]} {
-               set fname [string range $line 10 end]
+           } elseif {![string compare -length 10 $line "rename to "] ||
+                     ![string compare -length 8 $line "copy to "]} {
+               set fname [string range $line [expr 4 + [string first " to " $line] ] end]
                if {[string index $fname 0] eq "\""} {
                    set fname [lindex $fname 0]
                }
@@@ -5324,7 -5458,7 +5400,7 @@@ proc redisplay {} 
  }
  
  proc incrfont {inc} {
-     global mainfont textfont ctext canv phase cflist
+     global mainfont textfont ctext canv phase cflist showrefstop
      global charspc tabstop
      global stopped entries
      unmarkmatches
      if {$phase eq "getcommits"} {
        $canv itemconf textitems -font $mainfont
      }
+     if {[info exists showrefstop] && [winfo exists $showrefstop]} {
+       $showrefstop.list conf -font $mainfont
+     }
      redisplay
  }
  
@@@ -5798,6 -5935,8 +5877,8 @@@ proc domktag {} 
      lappend idtags($id) $tag
      redrawtags $id
      addedtag $id
+     dispneartags 0
+     run refill_reflist
  }
  
  proc redrawtags {id} {
@@@ -5939,6 -6078,7 +6020,7 @@@ proc mkbrgo {top} 
        notbusy newbranch
        redrawtags $id
        dispneartags 0
+       run refill_reflist
      }
  }
  
@@@ -6110,7 -6250,7 +6192,7 @@@ proc cobranch {} 
  
  proc rmbranch {} {
      global headmenuid headmenuhead mainhead
-     global headids idheads
+     global idheads
  
      set head $headmenuhead
      set id $headmenuid
        return
      }
      set dheads [descheads $id]
-     if {$dheads eq $headids($head)} {
+     if {[llength $dheads] == 1 && $idheads($dheads) eq $head} {
        # the stuff on this branch isn't on any other branch
        if {![confirm_popup "The commits on branch $head aren't on any other\
                        branch.\nReally delete branch $head?"]} return
      redrawtags $id
      notbusy rmbranch
      dispneartags 0
+     run refill_reflist
+ }
+ # Display a list of tags and heads
+ proc showrefs {} {
+     global showrefstop bgcolor fgcolor selectbgcolor mainfont
+     global bglist fglist uifont reflistfilter reflist maincursor
+     set top .showrefs
+     set showrefstop $top
+     if {[winfo exists $top]} {
+       raise $top
+       refill_reflist
+       return
+     }
+     toplevel $top
+     wm title $top "Tags and heads: [file tail [pwd]]"
+     text $top.list -background $bgcolor -foreground $fgcolor \
+       -selectbackground $selectbgcolor -font $mainfont \
+       -xscrollcommand "$top.xsb set" -yscrollcommand "$top.ysb set" \
+       -width 30 -height 20 -cursor $maincursor \
+       -spacing1 1 -spacing3 1 -state disabled
+     $top.list tag configure highlight -background $selectbgcolor
+     lappend bglist $top.list
+     lappend fglist $top.list
+     scrollbar $top.ysb -command "$top.list yview" -orient vertical
+     scrollbar $top.xsb -command "$top.list xview" -orient horizontal
+     grid $top.list $top.ysb -sticky nsew
+     grid $top.xsb x -sticky ew
+     frame $top.f
+     label $top.f.l -text "Filter: " -font $uifont
+     entry $top.f.e -width 20 -textvariable reflistfilter -font $uifont
+     set reflistfilter "*"
+     trace add variable reflistfilter write reflistfilter_change
+     pack $top.f.e -side right -fill x -expand 1
+     pack $top.f.l -side left
+     grid $top.f - -sticky ew -pady 2
+     button $top.close -command [list destroy $top] -text "Close" \
+       -font $uifont
+     grid $top.close -
+     grid columnconfigure $top 0 -weight 1
+     grid rowconfigure $top 0 -weight 1
+     bind $top.list <1> {break}
+     bind $top.list <B1-Motion> {break}
+     bind $top.list <ButtonRelease-1> {sel_reflist %W %x %y; break}
+     set reflist {}
+     refill_reflist
+ }
+ proc sel_reflist {w x y} {
+     global showrefstop reflist headids tagids otherrefids
+     if {![winfo exists $showrefstop]} return
+     set l [lindex [split [$w index "@$x,$y"] "."] 0]
+     set ref [lindex $reflist [expr {$l-1}]]
+     set n [lindex $ref 0]
+     switch -- [lindex $ref 1] {
+       "H" {selbyid $headids($n)}
+       "T" {selbyid $tagids($n)}
+       "o" {selbyid $otherrefids($n)}
+     }
+     $showrefstop.list tag add highlight $l.0 "$l.0 lineend"
+ }
+ proc unsel_reflist {} {
+     global showrefstop
+     if {![info exists showrefstop] || ![winfo exists $showrefstop]} return
+     $showrefstop.list tag remove highlight 0.0 end
+ }
+ proc reflistfilter_change {n1 n2 op} {
+     global reflistfilter
+     after cancel refill_reflist
+     after 200 refill_reflist
+ }
+ proc refill_reflist {} {
+     global reflist reflistfilter showrefstop headids tagids otherrefids
+     global commitrow 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))]} {
+               lappend refs [list $n H]
+           } else {
+               set commitinterest($headids($n)) {run refill_reflist}
+           }
+       }
+     }
+     foreach n [array names tagids] {
+       if {[string match $reflistfilter $n]} {
+           if {[info exists commitrow($curview,$tagids($n))]} {
+               lappend refs [list $n T]
+           } else {
+               set commitinterest($tagids($n)) {run refill_reflist}
+           }
+       }
+     }
+     foreach n [array names otherrefids] {
+       if {[string match $reflistfilter $n]} {
+           if {[info exists commitrow($curview,$otherrefids($n))]} {
+               lappend refs [list $n o]
+           } else {
+               set commitinterest($otherrefids($n)) {run refill_reflist}
+           }
+       }
+     }
+     set refs [lsort -index 0 $refs]
+     if {$refs eq $reflist} return
+     # Update the contents of $showrefstop.list according to the
+     # differences between $reflist (old) and $refs (new)
+     $showrefstop.list conf -state normal
+     $showrefstop.list insert end "\n"
+     set i 0
+     set j 0
+     while {$i < [llength $reflist] || $j < [llength $refs]} {
+       if {$i < [llength $reflist]} {
+           if {$j < [llength $refs]} {
+               set cmp [string compare [lindex $reflist $i 0] \
+                            [lindex $refs $j 0]]
+               if {$cmp == 0} {
+                   set cmp [string compare [lindex $reflist $i 1] \
+                                [lindex $refs $j 1]]
+               }
+           } else {
+               set cmp -1
+           }
+       } else {
+           set cmp 1
+       }
+       switch -- $cmp {
+           -1 {
+               $showrefstop.list delete "[expr {$j+1}].0" "[expr {$j+2}].0"
+               incr i
+           }
+           0 {
+               incr i
+               incr j
+           }
+           1 {
+               set l [expr {$j + 1}]
+               $showrefstop.list image create $l.0 -align baseline \
+                   -image reficon-[lindex $refs $j 1] -padx 2
+               $showrefstop.list insert $l.1 "[lindex $refs $j 0]\n"
+               incr j
+           }
+       }
+     }
+     set reflist $refs
+     # delete last newline
+     $showrefstop.list delete end-2c end-1c
+     $showrefstop.list conf -state disabled
  }
  
  # Stuff for finding nearby tags
@@@ -7034,6 -7331,7 +7273,7 @@@ proc rereadrefs {} 
            redrawtags $id
        }
      }
+     run refill_reflist
  }
  
  proc listrefs {id} {
@@@ -7254,8 -7552,9 +7494,9 @@@ proc prefsok {} 
  }
  
  proc formatdate {d} {
+     global datetimeformat
      if {$d ne {}} {
-       set d [clock format $d -format "%Y-%m-%d %H:%M:%S"]
+       set d [clock format $d -format $datetimeformat]
      }
      return $d
  }
@@@ -7559,20 -7858,22 +7800,22 @@@ set maxgraphpct 5
  set maxwidth 16
  set revlistorder 0
  set fastdate 0
 -set uparrowlen 7
 -set downarrowlen 7
 -set mingaplen 30
 +set uparrowlen 5
 +set downarrowlen 5
 +set mingaplen 100
  set cmitmode "patch"
  set wrapcomment "none"
  set showneartags 1
  set maxrefs 20
  set maxlinelen 200
  set showlocalchanges 1
+ set datetimeformat "%Y-%m-%d %H:%M:%S"
  
  set colors {green red blue magenta darkgrey brown orange}
  set bgcolor white
  set fgcolor black
  set diffcolors {red "#00a000" blue}
+ set diffcontext 3
  set selectbgcolor gray85
  
  catch {source ~/.gitk}