Code

Merge branch 'sp/mmap'
[git.git] / gitk
diff --git a/gitk b/gitk
index 101cf9bd9ff8c3b8180ab50e6ceb4655576f9fbf..3dabc695167dc33c6655efbabbd9a0c3e870ccda 100755 (executable)
--- a/gitk
+++ b/gitk
@@ -2,7 +2,7 @@
 # Tcl ignores the next line -*- tcl -*- \
 exec wish "$0" -- "$@"
 
 # Tcl ignores the next line -*- tcl -*- \
 exec wish "$0" -- "$@"
 
-# Copyright (C) 2005 Paul Mackerras.  All rights reserved.
+# Copyright (C) 2005-2006 Paul Mackerras.  All rights reserved.
 # This program is free software; it may be used, copied, modified
 # and distributed under the terms of the GNU General Public Licence,
 # either version 2, or (at your option) any later version.
 # This program is free software; it may be used, copied, modified
 # and distributed under the terms of the GNU General Public Licence,
 # either version 2, or (at your option) any later version.
@@ -17,13 +17,12 @@ proc gitdir {} {
 }
 
 proc start_rev_list {view} {
 }
 
 proc start_rev_list {view} {
-    global startmsecs nextupdate ncmupdate
+    global startmsecs nextupdate
     global commfd leftover tclencoding datemode
     global viewargs viewfiles commitidx
 
     set startmsecs [clock clicks -milliseconds]
     set nextupdate [expr {$startmsecs + 100}]
     global commfd leftover tclencoding datemode
     global viewargs viewfiles commitidx
 
     set startmsecs [clock clicks -milliseconds]
     set nextupdate [expr {$startmsecs + 100}]
-    set ncmupdate 1
     set commitidx($view) 0
     set args $viewargs($view)
     if {$viewfiles($view) ne {}} {
     set commitidx($view) 0
     set args $viewargs($view)
     if {$viewfiles($view) ne {}} {
@@ -79,7 +78,7 @@ proc getcommitlines {fd view}  {
     global parentlist childlist children curview hlview
     global vparentlist vchildlist vdisporder vcmitlisted
 
     global parentlist childlist children curview hlview
     global vparentlist vchildlist vdisporder vcmitlisted
 
-    set stuff [read $fd]
+    set stuff [read $fd 500000]
     if {$stuff == {}} {
        if {![eof $fd]} return
        global viewname
     if {$stuff == {}} {
        if {![eof $fd]} return
        global viewname
@@ -185,9 +184,9 @@ proc getcommitlines {fd view}  {
     }
     if {$gotsome} {
        if {$view == $curview} {
     }
     if {$gotsome} {
        if {$view == $curview} {
-           layoutmore
+           while {[layoutmore $nextupdate]} doupdate
        } elseif {[info exists hlview] && $view == $hlview} {
        } elseif {[info exists hlview] && $view == $hlview} {
-           highlightmore
+           vhighlightmore
        }
     }
     if {[clock clicks -milliseconds] >= $nextupdate} {
        }
     }
     if {[clock clicks -milliseconds] >= $nextupdate} {
@@ -196,20 +195,13 @@ proc getcommitlines {fd view}  {
 }
 
 proc doupdate {} {
 }
 
 proc doupdate {} {
-    global commfd nextupdate numcommits ncmupdate
+    global commfd nextupdate numcommits
 
     foreach v [array names commfd] {
        fileevent $commfd($v) readable {}
     }
     update
     set nextupdate [expr {[clock clicks -milliseconds] + 100}]
 
     foreach v [array names commfd] {
        fileevent $commfd($v) readable {}
     }
     update
     set nextupdate [expr {[clock clicks -milliseconds] + 100}]
-    if {$numcommits < 100} {
-       set ncmupdate [expr {$numcommits + 1}]
-    } elseif {$numcommits < 10000} {
-       set ncmupdate [expr {$numcommits + 10}]
-    } else {
-       set ncmupdate [expr {$numcommits + 100}]
-    }
     foreach v [array names commfd] {
        set fd $commfd($v)
        fileevent $fd readable [list getcommitlines $fd $v]
     foreach v [array names commfd] {
        set fd $commfd($v)
        fileevent $fd readable [list getcommitlines $fd $v]
@@ -223,7 +215,7 @@ proc readcommit {id} {
 
 proc updatecommits {} {
     global viewdata curview phase displayorder
 
 proc updatecommits {} {
     global viewdata curview phase displayorder
-    global children commitrow
+    global children commitrow selectedline thickerline
 
     if {$phase ne {}} {
        stop_rev_list
 
     if {$phase ne {}} {
        stop_rev_list
@@ -235,7 +227,10 @@ proc updatecommits {} {
        catch {unset commitrow($n,$id)}
     }
     set curview -1
        catch {unset commitrow($n,$id)}
     }
     set curview -1
+    catch {unset selectedline}
+    catch {unset thickerline}
     catch {unset viewdata($n)}
     catch {unset viewdata($n)}
+    discardallcommits
     readrefs
     showview $n
 }
     readrefs
     showview $n
 }
@@ -309,7 +304,7 @@ proc getcommit {id} {
 
 proc readrefs {} {
     global tagids idtags headids idheads tagcontents
 
 proc readrefs {} {
     global tagids idtags headids idheads tagcontents
-    global otherrefids idotherrefs
+    global otherrefids idotherrefs mainhead
 
     foreach v {tagids idtags headids idheads otherrefids idotherrefs} {
        catch {unset $v}
 
     foreach v {tagids idtags headids idheads otherrefids idotherrefs} {
        catch {unset $v}
@@ -338,13 +333,13 @@ proc readrefs {} {
            set tag {}
            catch {
                set commit [exec git rev-parse "$id^0"]
            set tag {}
            catch {
                set commit [exec git rev-parse "$id^0"]
-               if {"$commit" != "$id"} {
+               if {$commit != $id} {
                    set tagids($name) $commit
                    lappend idtags($commit) $name
                }
            }           
            catch {
                    set tagids($name) $commit
                    lappend idtags($commit) $name
                }
            }           
            catch {
-               set tagcontents($name) [exec git cat-file tag "$id"]
+               set tagcontents($name) [exec git cat-file tag $id]
            }
        } elseif { $type == "heads" } {
            set headids($name) $id
            }
        } elseif { $type == "heads" } {
            set headids($name) $id
@@ -355,6 +350,13 @@ proc readrefs {} {
        }
     }
     close $refd
        }
     }
     close $refd
+    set mainhead {}
+    catch {
+       set thehead [exec git symbolic-ref HEAD]
+       if {[string match "refs/heads/*" $thehead]} {
+           set mainhead [string range $thehead 11 end]
+       }
+    }
 }
 
 proc show_error {w top msg} {
 }
 
 proc show_error {w top msg} {
@@ -374,6 +376,23 @@ proc error_popup msg {
     show_error $w $w $msg
 }
 
     show_error $w $w $msg
 }
 
+proc confirm_popup msg {
+    global confirm_ok
+    set confirm_ok 0
+    set w .confirm
+    toplevel $w
+    wm transient $w .
+    message $w.m -text $msg -justify center -aspect 400
+    pack $w.m -side top -fill x -padx 20 -pady 20
+    button $w.ok -text OK -command "set confirm_ok 1; destroy $w"
+    pack $w.ok -side left -fill x
+    button $w.cancel -text Cancel -command "destroy $w"
+    pack $w.cancel -side right -fill x
+    bind $w <Visibility> "grab $w; focus $w"
+    tkwait window $w
+    return $confirm_ok
+}
+
 proc makewindow {} {
     global canv canv2 canv3 linespc charspc ctext cflist
     global textfont mainfont uifont
 proc makewindow {} {
     global canv canv2 canv3 linespc charspc ctext cflist
     global textfont mainfont uifont
@@ -381,6 +400,10 @@ proc makewindow {} {
     global entries sha1entry sha1string sha1but
     global maincursor textcursor curtextcursor
     global rowctxmenu mergemax wrapcomment
     global entries sha1entry sha1string sha1but
     global maincursor textcursor curtextcursor
     global rowctxmenu mergemax wrapcomment
+    global highlight_files gdttype
+    global searchstring sstring
+    global bgcolor fgcolor bglist fglist diffcolors
+    global headctxmenu
 
     menu .bar
     .bar add cascade -label "File" -menu .bar.file
 
     menu .bar
     .bar add cascade -label "File" -menu .bar.file
@@ -396,20 +419,14 @@ proc makewindow {} {
     .bar.edit configure -font $uifont
 
     menu .bar.view -font $uifont
     .bar.edit configure -font $uifont
 
     menu .bar.view -font $uifont
-    menu .bar.view.hl -font $uifont -tearoff 0
     .bar add cascade -label "View" -menu .bar.view
     .bar.view add command -label "New view..." -command {newview 0}
     .bar.view add command -label "Edit view..." -command editview \
        -state disabled
     .bar.view add command -label "Delete view" -command delview -state disabled
     .bar add cascade -label "View" -menu .bar.view
     .bar.view add command -label "New view..." -command {newview 0}
     .bar.view add command -label "Edit view..." -command editview \
        -state disabled
     .bar.view add command -label "Delete view" -command delview -state disabled
-    .bar.view add cascade -label "Highlight" -menu .bar.view.hl
     .bar.view add separator
     .bar.view add radiobutton -label "All files" -command {showview 0} \
        -variable selectedview -value 0
     .bar.view add separator
     .bar.view add radiobutton -label "All files" -command {showview 0} \
        -variable selectedview -value 0
-    .bar.view.hl add command -label "New view..." -command {newview 1}
-    .bar.view.hl add command -label "Remove" -command delhighlight \
-       -state disabled
-    .bar.view.hl add separator
     
     menu .bar.help
     .bar add cascade -label "Help" -menu .bar.help
     
     menu .bar.help
     .bar add cascade -label "Help" -menu .bar.help
@@ -436,6 +453,8 @@ proc makewindow {} {
     }
     frame .ctop.top
     frame .ctop.top.bar
     }
     frame .ctop.top
     frame .ctop.top.bar
+    frame .ctop.top.lbar
+    pack .ctop.top.lbar -side bottom -fill x
     pack .ctop.top.bar -side bottom -fill x
     set cscroll .ctop.top.csb
     scrollbar $cscroll -command {allcanvs yview} -highlightthickness 0
     pack .ctop.top.bar -side bottom -fill x
     set cscroll .ctop.top.csb
     scrollbar $cscroll -command {allcanvs yview} -highlightthickness 0
@@ -445,18 +464,19 @@ proc makewindow {} {
     .ctop add .ctop.top
     set canv .ctop.top.clist.canv
     canvas $canv -height $geometry(canvh) -width $geometry(canv1) \
     .ctop add .ctop.top
     set canv .ctop.top.clist.canv
     canvas $canv -height $geometry(canvh) -width $geometry(canv1) \
-       -bg white -bd 0 \
+       -background $bgcolor -bd 0 \
        -yscrollincr $linespc -yscrollcommand "scrollcanv $cscroll"
     .ctop.top.clist add $canv
     set canv2 .ctop.top.clist.canv2
     canvas $canv2 -height $geometry(canvh) -width $geometry(canv2) \
        -yscrollincr $linespc -yscrollcommand "scrollcanv $cscroll"
     .ctop.top.clist add $canv
     set canv2 .ctop.top.clist.canv2
     canvas $canv2 -height $geometry(canvh) -width $geometry(canv2) \
-       -bg white -bd 0 -yscrollincr $linespc
+       -background $bgcolor -bd 0 -yscrollincr $linespc
     .ctop.top.clist add $canv2
     set canv3 .ctop.top.clist.canv3
     canvas $canv3 -height $geometry(canvh) -width $geometry(canv3) \
     .ctop.top.clist add $canv2
     set canv3 .ctop.top.clist.canv3
     canvas $canv3 -height $geometry(canvh) -width $geometry(canv3) \
-       -bg white -bd 0 -yscrollincr $linespc
+       -background $bgcolor -bd 0 -yscrollincr $linespc
     .ctop.top.clist add $canv3
     bind .ctop.top.clist <Configure> {resizeclistpanes %W %w}
     .ctop.top.clist add $canv3
     bind .ctop.top.clist <Configure> {resizeclistpanes %W %w}
+    lappend bglist $canv $canv2 $canv3
 
     set sha1entry .ctop.top.bar.sha1
     set entries $sha1entry
 
     set sha1entry .ctop.top.bar.sha1
     set entries $sha1entry
@@ -497,41 +517,87 @@ proc makewindow {} {
     set findstring {}
     set fstring .ctop.top.bar.findstring
     lappend entries $fstring
     set findstring {}
     set fstring .ctop.top.bar.findstring
     lappend entries $fstring
-    entry $fstring -width 30 -font $textfont -textvariable findstring -font $textfont
+    entry $fstring -width 30 -font $textfont -textvariable findstring
+    trace add variable findstring write find_change
     pack $fstring -side left -expand 1 -fill x
     set findtype Exact
     set findtypemenu [tk_optionMenu .ctop.top.bar.findtype \
                          findtype Exact IgnCase Regexp]
     pack $fstring -side left -expand 1 -fill x
     set findtype Exact
     set findtypemenu [tk_optionMenu .ctop.top.bar.findtype \
                          findtype Exact IgnCase Regexp]
+    trace add variable findtype write find_change
     .ctop.top.bar.findtype configure -font $uifont
     .ctop.top.bar.findtype.menu configure -font $uifont
     set findloc "All fields"
     tk_optionMenu .ctop.top.bar.findloc findloc "All fields" Headline \
     .ctop.top.bar.findtype configure -font $uifont
     .ctop.top.bar.findtype.menu configure -font $uifont
     set findloc "All fields"
     tk_optionMenu .ctop.top.bar.findloc findloc "All fields" Headline \
-       Comments Author Committer Files Pickaxe
+       Comments Author Committer
+    trace add variable findloc write find_change
     .ctop.top.bar.findloc configure -font $uifont
     .ctop.top.bar.findloc.menu configure -font $uifont
     .ctop.top.bar.findloc configure -font $uifont
     .ctop.top.bar.findloc.menu configure -font $uifont
-
     pack .ctop.top.bar.findloc -side right
     pack .ctop.top.bar.findtype -side right
     pack .ctop.top.bar.findloc -side right
     pack .ctop.top.bar.findtype -side right
-    # for making sure type==Exact whenever loc==Pickaxe
-    trace add variable findloc write findlocchange
+
+    label .ctop.top.lbar.flabel -text "Highlight:  Commits " \
+       -font $uifont
+    pack .ctop.top.lbar.flabel -side left -fill y
+    set gdttype "touching paths:"
+    set gm [tk_optionMenu .ctop.top.lbar.gdttype gdttype "touching paths:" \
+               "adding/removing string:"]
+    trace add variable gdttype write hfiles_change
+    $gm conf -font $uifont
+    .ctop.top.lbar.gdttype conf -font $uifont
+    pack .ctop.top.lbar.gdttype -side left -fill y
+    entry .ctop.top.lbar.fent -width 25 -font $textfont \
+       -textvariable highlight_files
+    trace add variable highlight_files write hfiles_change
+    lappend entries .ctop.top.lbar.fent
+    pack .ctop.top.lbar.fent -side left -fill x -expand 1
+    label .ctop.top.lbar.vlabel -text " OR in view" -font $uifont
+    pack .ctop.top.lbar.vlabel -side left -fill y
+    global viewhlmenu selectedhlview
+    set viewhlmenu [tk_optionMenu .ctop.top.lbar.vhl selectedhlview None]
+    $viewhlmenu entryconf None -command delvhighlight
+    $viewhlmenu conf -font $uifont
+    .ctop.top.lbar.vhl conf -font $uifont
+    pack .ctop.top.lbar.vhl -side left -fill y
+    label .ctop.top.lbar.rlabel -text " OR " -font $uifont
+    pack .ctop.top.lbar.rlabel -side left -fill y
+    global highlight_related
+    set m [tk_optionMenu .ctop.top.lbar.relm highlight_related None \
+              "Descendent" "Not descendent" "Ancestor" "Not ancestor"]
+    $m conf -font $uifont
+    .ctop.top.lbar.relm conf -font $uifont
+    trace add variable highlight_related write vrel_change
+    pack .ctop.top.lbar.relm -side left -fill y
 
     panedwindow .ctop.cdet -orient horizontal
     .ctop add .ctop.cdet
     frame .ctop.cdet.left
 
     panedwindow .ctop.cdet -orient horizontal
     .ctop add .ctop.cdet
     frame .ctop.cdet.left
+    frame .ctop.cdet.left.bot
+    pack .ctop.cdet.left.bot -side bottom -fill x
+    button .ctop.cdet.left.bot.search -text "Search" -command dosearch \
+       -font $uifont
+    pack .ctop.cdet.left.bot.search -side left -padx 5
+    set sstring .ctop.cdet.left.bot.sstring
+    entry $sstring -width 20 -font $textfont -textvariable searchstring
+    lappend entries $sstring
+    trace add variable searchstring write incrsearch
+    pack $sstring -side left -expand 1 -fill x
     set ctext .ctop.cdet.left.ctext
     set ctext .ctop.cdet.left.ctext
-    text $ctext -bg white -state disabled -font $textfont \
+    text $ctext -background $bgcolor -foreground $fgcolor \
+       -state disabled -font $textfont \
        -width $geometry(ctextw) -height $geometry(ctexth) \
        -width $geometry(ctextw) -height $geometry(ctexth) \
-       -yscrollcommand {.ctop.cdet.left.sb set} -wrap none
+       -yscrollcommand scrolltext -wrap none
     scrollbar .ctop.cdet.left.sb -command "$ctext yview"
     pack .ctop.cdet.left.sb -side right -fill y
     pack $ctext -side left -fill both -expand 1
     .ctop.cdet add .ctop.cdet.left
     scrollbar .ctop.cdet.left.sb -command "$ctext yview"
     pack .ctop.cdet.left.sb -side right -fill y
     pack $ctext -side left -fill both -expand 1
     .ctop.cdet add .ctop.cdet.left
+    lappend bglist $ctext
+    lappend fglist $ctext
 
     $ctext tag conf comment -wrap $wrapcomment
     $ctext tag conf filesep -font [concat $textfont bold] -back "#aaaaaa"
 
     $ctext tag conf comment -wrap $wrapcomment
     $ctext tag conf filesep -font [concat $textfont bold] -back "#aaaaaa"
-    $ctext tag conf hunksep -fore blue
-    $ctext tag conf d0 -fore red
-    $ctext tag conf d1 -fore "#00a000"
+    $ctext tag conf hunksep -fore [lindex $diffcolors 2]
+    $ctext tag conf d0 -fore [lindex $diffcolors 0]
+    $ctext tag conf d1 -fore [lindex $diffcolors 1]
     $ctext tag conf m0 -fore red
     $ctext tag conf m1 -fore blue
     $ctext tag conf m2 -fore green
     $ctext tag conf m0 -fore red
     $ctext tag conf m1 -fore blue
     $ctext tag conf m2 -fore green
@@ -564,16 +630,21 @@ proc makewindow {} {
     pack .ctop.cdet.right.mode -side top -fill x
     set cflist .ctop.cdet.right.cfiles
     set indent [font measure $mainfont "nn"]
     pack .ctop.cdet.right.mode -side top -fill x
     set cflist .ctop.cdet.right.cfiles
     set indent [font measure $mainfont "nn"]
-    text $cflist -width $geometry(cflistw) -background white -font $mainfont \
+    text $cflist -width $geometry(cflistw) \
+       -background $bgcolor -foreground $fgcolor \
+       -font $mainfont \
        -tabs [list $indent [expr {2 * $indent}]] \
        -yscrollcommand ".ctop.cdet.right.sb set" \
        -cursor [. cget -cursor] \
        -spacing1 1 -spacing3 1
        -tabs [list $indent [expr {2 * $indent}]] \
        -yscrollcommand ".ctop.cdet.right.sb set" \
        -cursor [. cget -cursor] \
        -spacing1 1 -spacing3 1
+    lappend bglist $cflist
+    lappend fglist $cflist
     scrollbar .ctop.cdet.right.sb -command "$cflist yview"
     pack .ctop.cdet.right.sb -side right -fill y
     pack $cflist -side left -fill both -expand 1
     $cflist tag configure highlight \
        -background [$cflist cget -selectbackground]
     scrollbar .ctop.cdet.right.sb -command "$cflist yview"
     pack .ctop.cdet.right.sb -side right -fill y
     pack $cflist -side left -fill both -expand 1
     $cflist tag configure highlight \
        -background [$cflist cget -selectbackground]
+    $cflist tag configure bold -font [concat $mainfont bold]
     .ctop.cdet add .ctop.cdet.right
     bind .ctop.cdet <Configure> {resizecdetpanes %W %w}
 
     .ctop.cdet add .ctop.cdet.right
     bind .ctop.cdet <Configure> {resizecdetpanes %W %w}
 
@@ -589,6 +660,8 @@ proc makewindow {} {
     bindkey <End> sellastline
     bind . <Key-Up> "selnextline -1"
     bind . <Key-Down> "selnextline 1"
     bindkey <End> sellastline
     bind . <Key-Up> "selnextline -1"
     bind . <Key-Down> "selnextline 1"
+    bind . <Shift-Key-Up> "next_highlight -1"
+    bind . <Shift-Key-Down> "next_highlight 1"
     bindkey <Key-Right> "goforw"
     bindkey <Key-Left> "goback"
     bind . <Key-Prior> "selnextpage -1"
     bindkey <Key-Right> "goforw"
     bindkey <Key-Left> "goback"
     bind . <Key-Prior> "selnextpage -1"
@@ -620,7 +693,8 @@ proc makewindow {} {
     bind . <Control-q> doquit
     bind . <Control-f> dofind
     bind . <Control-g> {findnext 0}
     bind . <Control-q> doquit
     bind . <Control-f> dofind
     bind . <Control-g> {findnext 0}
-    bind . <Control-r> findprev
+    bind . <Control-r> dosearchback
+    bind . <Control-s> dosearch
     bind . <Control-equal> {incrfont 1}
     bind . <Control-KP_Add> {incrfont 1}
     bind . <Control-minus> {incrfont -1}
     bind . <Control-equal> {incrfont 1}
     bind . <Control-KP_Add> {incrfont 1}
     bind . <Control-minus> {incrfont -1}
@@ -647,6 +721,16 @@ proc makewindow {} {
     $rowctxmenu add command -label "Make patch" -command mkpatch
     $rowctxmenu add command -label "Create tag" -command mktag
     $rowctxmenu add command -label "Write commit to file" -command writecommit
     $rowctxmenu add command -label "Make patch" -command mkpatch
     $rowctxmenu add command -label "Create tag" -command mktag
     $rowctxmenu add command -label "Write commit to file" -command writecommit
+    $rowctxmenu add command -label "Create new branch" -command mkbranch
+    $rowctxmenu add command -label "Cherry-pick this commit" \
+       -command cherrypick
+
+    set headctxmenu .headctxmenu
+    menu $headctxmenu -tearoff 0
+    $headctxmenu add command -label "Check out this branch" \
+       -command cobranch
+    $headctxmenu add command -label "Remove this branch" \
+       -command rmbranch
 }
 
 # mouse-2 makes all windows scan vertically, but only the one
 }
 
 # mouse-2 makes all windows scan vertically, but only the one
@@ -665,6 +749,7 @@ proc canvscan {op w x y} {
 proc scrollcanv {cscroll f0 f1} {
     $cscroll set $f0 $f1
     drawfrac $f0 $f1
 proc scrollcanv {cscroll f0 f1} {
     $cscroll set $f0 $f1
     drawfrac $f0 $f1
+    flushhighlights
 }
 
 # when we make a key binding for the toplevel, make sure
 }
 
 # when we make a key binding for the toplevel, make sure
@@ -695,9 +780,10 @@ proc click {w} {
 proc savestuff {w} {
     global canv canv2 canv3 ctext cflist mainfont textfont uifont
     global stuffsaved findmergefiles maxgraphpct
 proc savestuff {w} {
     global canv canv2 canv3 ctext cflist mainfont textfont uifont
     global stuffsaved findmergefiles maxgraphpct
-    global maxwidth
+    global maxwidth showneartags
     global viewname viewfiles viewargs viewperm nextviewnum
     global cmitmode wrapcomment
     global viewname viewfiles viewargs viewperm nextviewnum
     global cmitmode wrapcomment
+    global colors bgcolor fgcolor diffcolors
 
     if {$stuffsaved} return
     if {![winfo viewable .]} return
 
     if {$stuffsaved} return
     if {![winfo viewable .]} return
@@ -711,6 +797,11 @@ proc savestuff {w} {
        puts $f [list set maxwidth $maxwidth]
        puts $f [list set cmitmode $cmitmode]
        puts $f [list set wrapcomment $wrapcomment]
        puts $f [list set maxwidth $maxwidth]
        puts $f [list set cmitmode $cmitmode]
        puts $f [list set wrapcomment $wrapcomment]
+       puts $f [list set showneartags $showneartags]
+       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 "set geometry(width) [winfo width .ctop]"
        puts $f "set geometry(height) [winfo height .ctop]"
        puts $f "set geometry(canv1) [expr {[winfo width $canv]-2}]"
        puts $f "set geometry(width) [winfo width .ctop]"
        puts $f "set geometry(height) [winfo height .ctop]"
        puts $f "set geometry(canv1) [expr {[winfo width $canv]-2}]"
@@ -848,6 +939,8 @@ Gitk key bindings:
 <Ctrl-Down>    Scroll commit list down one line
 <Ctrl-PageUp>  Scroll commit list up one page
 <Ctrl-PageDown>        Scroll commit list down one page
 <Ctrl-Down>    Scroll commit list down one line
 <Ctrl-PageUp>  Scroll commit list up one page
 <Ctrl-PageDown>        Scroll commit list down one page
+<Shift-Up>     Move to previous highlighted line
+<Shift-Down>   Move to next highlighted line
 <Delete>, b    Scroll diff view up one page
 <Backspace>    Scroll diff view up one page
 <Space>                Scroll diff view down one page
 <Delete>, b    Scroll diff view up one page
 <Backspace>    Scroll diff view up one page
 <Space>                Scroll diff view down one page
@@ -855,11 +948,12 @@ u         Scroll diff view up 18 lines
 d              Scroll diff view down 18 lines
 <Ctrl-F>               Find
 <Ctrl-G>               Move to next find hit
 d              Scroll diff view down 18 lines
 <Ctrl-F>               Find
 <Ctrl-G>               Move to next find hit
-<Ctrl-R>               Move to previous 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
 <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
+<Ctrl-S>               Search for next hit in diff view
+<Ctrl-R>               Search for previous hit in diff view
 <Ctrl-KP+>     Increase font size
 <Ctrl-plus>    Increase font size
 <Ctrl-KP->     Decrease font size
 <Ctrl-KP+>     Increase font size
 <Ctrl-plus>    Increase font size
 <Ctrl-KP->     Decrease font size
@@ -926,7 +1020,7 @@ proc treeview {w l openlevs} {
                $w insert end $str
                $w image create end -align center -image $bm -padx 1 \
                    -name a:$ix
                $w insert end $str
                $w image create end -align center -image $bm -padx 1 \
                    -name a:$ix
-               $w insert end $d
+               $w insert end $d [highlight_tag $prefix]
                $w mark set s:$ix "end -1c"
                $w mark gravity s:$ix left
            }
                $w mark set s:$ix "end -1c"
                $w mark gravity s:$ix left
            }
@@ -938,7 +1032,7 @@ proc treeview {w l openlevs} {
                set str "\n"
                for {set i 0} {$i < $lev} {incr i} {append str "\t"}
                $w insert end $str
                set str "\n"
                for {set i 0} {$i < $lev} {incr i} {append str "\t"}
                $w insert end $str
-               $w insert end $tail
+               $w insert end $tail [highlight_tag $f]
            }
            lappend treecontents($prefix) $tail
        }
            }
            lappend treecontents($prefix) $tail
        }
@@ -975,6 +1069,22 @@ proc linetoelt {l} {
     }
 }
 
     }
 }
 
+proc highlight_tree {y prefix} {
+    global treeheight treecontents cflist
+
+    foreach e $treecontents($prefix) {
+       set path $prefix$e
+       if {[highlight_tag $path] ne {}} {
+           $cflist tag add bold $y.0 "$y.0 lineend"
+       }
+       incr y
+       if {[string index $e end] eq "/" && $treeheight($path) > 1} {
+           set y [highlight_tree $y $path]
+       }
+    }
+    return $y
+}
+
 proc treeclosedir {w dir} {
     global treediropen treeheight treeparent treeindex
 
 proc treeclosedir {w dir} {
     global treediropen treeheight treeparent treeindex
 
@@ -1008,8 +1118,8 @@ proc treeopendir {w dir} {
        incr treeheight($x) $n
     }
     foreach e $treecontents($dir) {
        incr treeheight($x) $n
     }
     foreach e $treecontents($dir) {
+       set de $dir$e
        if {[string index $e end] eq "/"} {
        if {[string index $e end] eq "/"} {
-           set de $dir$e
            set iy $treeindex($de)
            $w mark set d:$iy e:$ix
            $w mark gravity d:$iy left
            set iy $treeindex($de)
            $w mark set d:$iy e:$ix
            $w mark gravity d:$iy left
@@ -1017,13 +1127,13 @@ proc treeopendir {w dir} {
            set treediropen($de) 0
            $w image create e:$ix -align center -image tri-rt -padx 1 \
                -name a:$iy
            set treediropen($de) 0
            $w image create e:$ix -align center -image tri-rt -padx 1 \
                -name a:$iy
-           $w insert e:$ix $e
+           $w insert e:$ix $e [highlight_tag $de]
            $w mark set s:$iy e:$ix
            $w mark gravity s:$iy left
            set treeheight($de) 1
        } else {
            $w insert e:$ix $str
            $w mark set s:$iy e:$ix
            $w mark gravity s:$iy left
            set treeheight($de) 1
        } else {
            $w insert e:$ix $str
-           $w insert e:$ix $e
+           $w insert e:$ix $e [highlight_tag $de]
        }
     }
     $w mark gravity e:$ix left
        }
     }
     $w mark gravity e:$ix left
@@ -1119,20 +1229,56 @@ proc init_flist {first} {
     set difffilestart {}
 }
 
     set difffilestart {}
 }
 
-proc add_flist {fl} {
-    global flistmode cflist
+proc highlight_tag {f} {
+    global highlight_paths
+
+    foreach p $highlight_paths {
+       if {[string match $p $f]} {
+           return "bold"
+       }
+    }
+    return {}
+}
+
+proc highlight_filelist {} {
+    global cmitmode cflist
 
     $cflist conf -state normal
 
     $cflist conf -state normal
-    if {$flistmode eq "flat"} {
-       foreach f $fl {
-           $cflist insert end "\n$f"
+    if {$cmitmode ne "tree"} {
+       set end [lindex [split [$cflist index end] .] 0]
+       for {set l 2} {$l < $end} {incr l} {
+           set line [$cflist get $l.0 "$l.0 lineend"]
+           if {[highlight_tag $line] ne {}} {
+               $cflist tag add bold $l.0 "$l.0 lineend"
+           }
        }
        }
+    } else {
+       highlight_tree 2 {}
+    }
+    $cflist conf -state disabled
+}
+
+proc unhighlight_filelist {} {
+    global cflist
+
+    $cflist conf -state normal
+    $cflist tag remove bold 1.0 end
+    $cflist conf -state disabled
+}
+
+proc add_flist {fl} {
+    global cflist
+
+    $cflist conf -state normal
+    foreach f $fl {
+       $cflist insert end "\n"
+       $cflist insert end $f [highlight_tag $f]
     }
     $cflist conf -state disabled
 }
 
 proc sel_flist {w x y} {
     }
     $cflist conf -state disabled
 }
 
 proc sel_flist {w x y} {
-    global flistmode ctext difffilestart cflist cflist_top cmitmode
+    global ctext difffilestart cflist cflist_top cmitmode
 
     if {$cmitmode eq "tree"} return
     if {![info exists cflist_top]} return
 
     if {$cmitmode eq "tree"} return
     if {![info exists cflist_top]} return
@@ -1315,25 +1461,27 @@ proc vieweditor {top n title} {
     focus $top.t
 }
 
     focus $top.t
 }
 
-proc doviewmenu {m first cmd op args} {
+proc doviewmenu {m first cmd op argv} {
     set nmenu [$m index end]
     for {set i $first} {$i <= $nmenu} {incr i} {
        if {[$m entrycget $i -command] eq $cmd} {
     set nmenu [$m index end]
     for {set i $first} {$i <= $nmenu} {incr i} {
        if {[$m entrycget $i -command] eq $cmd} {
-           eval $m $op $i $args
+           eval $m $op $i $argv
            break
        }
     }
 }
 
 proc allviewmenus {n op args} {
            break
        }
     }
 }
 
 proc allviewmenus {n op args} {
-    doviewmenu .bar.view 7 [list showview $n] $op $args
-    doviewmenu .bar.view.hl 3 [list addhighlight $n] $op $args
+    global viewhlmenu
+
+    doviewmenu .bar.view 5 [list showview $n] $op $args
+    doviewmenu $viewhlmenu 1 [list addvhighlight $n] $op $args
 }
 
 proc newviewok {top n} {
     global nextviewnum newviewperm newviewname newishighlight
     global viewname viewfiles viewperm selectedview curview
 }
 
 proc newviewok {top n} {
     global nextviewnum newviewperm newviewname newishighlight
     global viewname viewfiles viewperm selectedview curview
-    global viewargs newviewargs
+    global viewargs newviewargs viewhlmenu
 
     if {[catch {
        set newargs [shellsplit $newviewargs($n)]
 
     if {[catch {
        set newargs [shellsplit $newviewargs($n)]
@@ -1361,14 +1509,17 @@ proc newviewok {top n} {
        if {!$newishighlight} {
            after idle showview $n
        } else {
        if {!$newishighlight} {
            after idle showview $n
        } else {
-           after idle addhighlight $n
+           after idle addvhighlight $n
        }
     } else {
        # editing an existing view
        set viewperm($n) $newviewperm($n)
        if {$newviewname($n) ne $viewname($n)} {
            set viewname($n) $newviewname($n)
        }
     } else {
        # editing an existing view
        set viewperm($n) $newviewperm($n)
        if {$newviewname($n) ne $viewname($n)} {
            set viewname($n) $newviewname($n)
-           allviewmenus $n entryconf -label $viewname($n)
+           doviewmenu .bar.view 5 [list showview $n] \
+               entryconf [list -label $viewname($n)]
+           doviewmenu $viewhlmenu 1 [list addvhighlight $n] \
+               entryconf [list -label $viewname($n) -value $viewname($n)]
        }
        if {$files ne $viewfiles($n) || $newargs ne $viewargs($n)} {
            set viewfiles($n) $files
        }
        if {$files ne $viewfiles($n) || $newargs ne $viewargs($n)} {
            set viewfiles($n) $files
@@ -1382,9 +1533,13 @@ proc newviewok {top n} {
 }
 
 proc delview {} {
 }
 
 proc delview {} {
-    global curview viewdata viewperm
+    global curview viewdata viewperm hlview selectedhlview
 
     if {$curview == 0} return
 
     if {$curview == 0} return
+    if {[info exists hlview] && $hlview == $curview} {
+       set selectedhlview None
+       unset hlview
+    }
     allviewmenus $curview delete
     set viewdata($curview) {}
     set viewperm($curview) 0
     allviewmenus $curview delete
     set viewdata($curview) {}
     set viewperm($curview) 0
@@ -1392,12 +1547,12 @@ proc delview {} {
 }
 
 proc addviewmenu {n} {
 }
 
 proc addviewmenu {n} {
-    global viewname
+    global viewname viewhlmenu
 
     .bar.view add radiobutton -label $viewname($n) \
        -command [list showview $n] -variable selectedview -value $n
 
     .bar.view add radiobutton -label $viewname($n) \
        -command [list showview $n] -variable selectedview -value $n
-    .bar.view.hl add radiobutton -label $viewname($n) \
-       -command [list addhighlight $n] -variable selectedhlview -value $n
+    $viewhlmenu add radiobutton -label $viewname($n) \
+       -command [list addvhighlight $n] -variable selectedhlview
 }
 
 proc flatten {var} {
 }
 
 proc flatten {var} {
@@ -1429,8 +1584,9 @@ proc showview {n} {
     global pending_select phase
     global commitidx rowlaidout rowoptim linesegends
     global commfd nextupdate
     global pending_select phase
     global commitidx rowlaidout rowoptim linesegends
     global commfd nextupdate
-    global selectedview hlview selectedhlview
+    global selectedview
     global vparentlist vchildlist vdisporder vcmitlisted
     global vparentlist vchildlist vdisporder vcmitlisted
+    global hlview selectedhlview
 
     if {$n == $curview} return
     set selid {}
 
     if {$n == $curview} return
     set selid {}
@@ -1469,14 +1625,15 @@ proc showview {n} {
     catch {unset matchinglines}
     catch {unset treediffs}
     clear_display
     catch {unset matchinglines}
     catch {unset treediffs}
     clear_display
+    if {[info exists hlview] && $hlview == $n} {
+       unset hlview
+       set selectedhlview None
+    }
 
     set curview $n
     set selectedview $n
 
     set curview $n
     set selectedview $n
-    set selectedhlview -1
-    .bar.view entryconf 2 -state [expr {$n == 0? "disabled": "normal"}]
-    .bar.view entryconf 3 -state [expr {$n == 0? "disabled": "normal"}]
-    catch {unset hlview}
-    .bar.view.hl entryconf 1 -state disabled
+    .bar.view entryconf Edit* -state [expr {$n == 0? "disabled": "normal"}]
+    .bar.view entryconf Delete* -state [expr {$n == 0? "disabled": "normal"}]
 
     if {![info exists viewdata($n)]} {
        set pending_select $selid
 
     if {![info exists viewdata($n)]} {
        set pending_select $selid
@@ -1532,7 +1689,7 @@ proc showview {n} {
            show_status "Reading commits..."
        }
        if {[info exists commfd($n)]} {
            show_status "Reading commits..."
        }
        if {[info exists commfd($n)]} {
-           layoutmore
+           layoutmore {}
        } else {
            finishcommits
        }
        } else {
            finishcommits
        }
@@ -1541,18 +1698,75 @@ proc showview {n} {
     }
 }
 
     }
 }
 
-proc addhighlight {n} {
-    global hlview curview viewdata highlighted highlightedrows
-    global selectedhlview
+# Stuff relating to the highlighting facility
+
+proc ishighlighted {row} {
+    global vhighlights fhighlights nhighlights rhighlights
+
+    if {[info exists nhighlights($row)] && $nhighlights($row) > 0} {
+       return $nhighlights($row)
+    }
+    if {[info exists vhighlights($row)] && $vhighlights($row) > 0} {
+       return $vhighlights($row)
+    }
+    if {[info exists fhighlights($row)] && $fhighlights($row) > 0} {
+       return $fhighlights($row)
+    }
+    if {[info exists rhighlights($row)] && $rhighlights($row) > 0} {
+       return $rhighlights($row)
+    }
+    return 0
+}
+
+proc bolden {row font} {
+    global canv linehtag selectedline boldrows
+
+    lappend boldrows $row
+    $canv itemconf $linehtag($row) -font $font
+    if {[info exists selectedline] && $row == $selectedline} {
+       $canv delete secsel
+       set t [eval $canv create rect [$canv bbox $linehtag($row)] \
+                  -outline {{}} -tags secsel \
+                  -fill [$canv cget -selectbackground]]
+       $canv lower $t
+    }
+}
+
+proc bolden_name {row font} {
+    global canv2 linentag selectedline boldnamerows
+
+    lappend boldnamerows $row
+    $canv2 itemconf $linentag($row) -font $font
+    if {[info exists selectedline] && $row == $selectedline} {
+       $canv2 delete secsel
+       set t [eval $canv2 create rect [$canv2 bbox $linentag($row)] \
+                  -outline {{}} -tags secsel \
+                  -fill [$canv2 cget -selectbackground]]
+       $canv2 lower $t
+    }
+}
+
+proc unbolden {} {
+    global mainfont boldrows
+
+    set stillbold {}
+    foreach row $boldrows {
+       if {![ishighlighted $row]} {
+           bolden $row $mainfont
+       } else {
+           lappend stillbold $row
+       }
+    }
+    set boldrows $stillbold
+}
+
+proc addvhighlight {n} {
+    global hlview curview viewdata vhl_done vhighlights commitidx
 
     if {[info exists hlview]} {
 
     if {[info exists hlview]} {
-       delhighlight
+       delvhighlight
     }
     set hlview $n
     }
     set hlview $n
-    set selectedhlview $n
-    .bar.view.hl entryconf 1 -state normal
-    set highlighted($n) 0
-    set highlightedrows {}
     if {$n != $curview && ![info exists viewdata($n)]} {
        set viewdata($n) [list getcommits {{}} {{}} {} {} {} 0 0 0 {}]
        set vparentlist($n) {}
     if {$n != $curview && ![info exists viewdata($n)]} {
        set viewdata($n) [list getcommits {{}} {{}} {} {} {} 0 0 0 {}]
        set vparentlist($n) {}
@@ -1560,34 +1774,25 @@ proc addhighlight {n} {
        set vdisporder($n) {}
        set vcmitlisted($n) {}
        start_rev_list $n
        set vdisporder($n) {}
        set vcmitlisted($n) {}
        start_rev_list $n
-    } else {
-       highlightmore
+    }
+    set vhl_done $commitidx($hlview)
+    if {$vhl_done > 0} {
+       drawvisible
     }
 }
 
     }
 }
 
-proc delhighlight {} {
-    global hlview highlightedrows canv linehtag mainfont
-    global selectedhlview selectedline
+proc delvhighlight {} {
+    global hlview vhighlights
 
     if {![info exists hlview]} return
     unset hlview
 
     if {![info exists hlview]} return
     unset hlview
-    set selectedhlview {}
-    .bar.view.hl entryconf 1 -state disabled
-    foreach l $highlightedrows {
-       $canv itemconf $linehtag($l) -font $mainfont
-       if {$l == $selectedline} {
-           $canv delete secsel
-           set t [eval $canv create rect [$canv bbox $linehtag($l)] \
-                      -outline {{}} -tags secsel \
-                      -fill [$canv cget -selectbackground]]
-           $canv lower $t
-       }
-    }
+    catch {unset vhighlights}
+    unbolden
 }
 
 }
 
-proc highlightmore {} {
-    global hlview highlighted commitidx highlightedrows linehtag mainfont
-    global displayorder vdisporder curview canv commitrow selectedline
+proc vhighlightmore {} {
+    global hlview vhl_done commitidx vhighlights
+    global displayorder vdisporder curview mainfont
 
     set font [concat $mainfont bold]
     set max $commitidx($hlview)
 
     set font [concat $mainfont bold]
     set max $commitidx($hlview)
@@ -1596,25 +1801,399 @@ proc highlightmore {} {
     } else {
        set disp $vdisporder($hlview)
     }
     } else {
        set disp $vdisporder($hlview)
     }
-    for {set i $highlighted($hlview)} {$i < $max} {incr i} {
+    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 [lindex $disp $i]
        if {[info exists commitrow($curview,$id)]} {
            set row $commitrow($curview,$id)
-           if {[info exists linehtag($row)]} {
-               $canv itemconf $linehtag($row) -font $font
-               lappend highlightedrows $row
-               if {$row == $selectedline} {
-                   $canv delete secsel
-                   set t [eval $canv create rect \
-                              [$canv bbox $linehtag($row)] \
-                              -outline {{}} -tags secsel \
-                              -fill [$canv cget -selectbackground]]
-                   $canv lower $t
+           if {$r0 <= $row && $row <= $r1} {
+               if {![highlighted $row]} {
+                   bolden $row $font
+               }
+               set vhighlights($row) 1
+           }
+       }
+    }
+    set vhl_done $max
+}
+
+proc askvhighlight {row id} {
+    global hlview vhighlights commitrow iddrawn mainfont
+
+    if {[info exists commitrow($hlview,$id)]} {
+       if {[info exists iddrawn($id)] && ![ishighlighted $row]} {
+           bolden $row [concat $mainfont bold]
+       }
+       set vhighlights($row) 1
+    } else {
+       set vhighlights($row) 0
+    }
+}
+
+proc hfiles_change {name ix op} {
+    global highlight_files filehighlight fhighlights fh_serial
+    global mainfont highlight_paths
+
+    if {[info exists filehighlight]} {
+       # delete previous highlights
+       catch {close $filehighlight}
+       unset filehighlight
+       catch {unset fhighlights}
+       unbolden
+       unhighlight_filelist
+    }
+    set highlight_paths {}
+    after cancel do_file_hl $fh_serial
+    incr fh_serial
+    if {$highlight_files ne {}} {
+       after 300 do_file_hl $fh_serial
+    }
+}
+
+proc makepatterns {l} {
+    set ret {}
+    foreach e $l {
+       set ee [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} $e]
+       if {[string index $ee end] eq "/"} {
+           lappend ret "$ee*"
+       } else {
+           lappend ret $ee
+           lappend ret "$ee/*"
+       }
+    }
+    return $ret
+}
+
+proc do_file_hl {serial} {
+    global highlight_files filehighlight highlight_paths gdttype fhl_list
+
+    if {$gdttype eq "touching paths:"} {
+       if {[catch {set paths [shellsplit $highlight_files]}]} return
+       set highlight_paths [makepatterns $paths]
+       highlight_filelist
+       set gdtargs [concat -- $paths]
+    } else {
+       set gdtargs [list "-S$highlight_files"]
+    }
+    set cmd [concat | git-diff-tree -r -s --stdin $gdtargs]
+    set filehighlight [open $cmd r+]
+    fconfigure $filehighlight -blocking 0
+    fileevent $filehighlight readable readfhighlight
+    set fhl_list {}
+    drawvisible
+    flushhighlights
+}
+
+proc flushhighlights {} {
+    global filehighlight fhl_list
+
+    if {[info exists filehighlight]} {
+       lappend fhl_list {}
+       puts $filehighlight ""
+       flush $filehighlight
+    }
+}
+
+proc askfilehighlight {row id} {
+    global filehighlight fhighlights fhl_list
+
+    lappend fhl_list $id
+    set fhighlights($row) -1
+    puts $filehighlight $id
+}
+
+proc readfhighlight {} {
+    global filehighlight fhighlights commitrow curview mainfont iddrawn
+    global fhl_list
+
+    while {[gets $filehighlight line] >= 0} {
+       set line [string trim $line]
+       set i [lsearch -exact $fhl_list $line]
+       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 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]} {
+           bolden $row [concat $mainfont bold]
+       }
+       set fhighlights($row) 1
+    }
+    if {[eof $filehighlight]} {
+       # strange...
+       puts "oops, git-diff-tree died"
+       catch {close $filehighlight}
+       unset filehighlight
+    }
+    next_hlcont
+}
+
+proc find_change {name ix op} {
+    global nhighlights mainfont boldnamerows
+    global findstring findpattern findtype
+
+    # delete previous highlights, if any
+    foreach row $boldnamerows {
+       bolden_name $row $mainfont
+    }
+    set boldnamerows {}
+    catch {unset nhighlights}
+    unbolden
+    if {$findtype ne "Regexp"} {
+       set e [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} \
+                  $findstring]
+       set findpattern "*$e*"
+    }
+    drawvisible
+}
+
+proc askfindhighlight {row id} {
+    global nhighlights commitinfo iddrawn mainfont
+    global findstring findtype findloc findpattern
+
+    if {![info exists commitinfo($id)]} {
+       getcommit $id
+    }
+    set info $commitinfo($id)
+    set isbold 0
+    set fldtypes {Headline Author Date Committer CDate Comments}
+    foreach f $info ty $fldtypes {
+       if {$findloc ne "All fields" && $findloc ne $ty} {
+           continue
+       }
+       if {$findtype eq "Regexp"} {
+           set doesmatch [regexp $findstring $f]
+       } elseif {$findtype eq "IgnCase"} {
+           set doesmatch [string match -nocase $findpattern $f]
+       } else {
+           set doesmatch [string match $findpattern $f]
+       }
+       if {$doesmatch} {
+           if {$ty eq "Author"} {
+               set isbold 2
+           } else {
+               set isbold 1
+           }
+       }
+    }
+    if {[info exists iddrawn($id)]} {
+       if {$isbold && ![ishighlighted $row]} {
+           bolden $row [concat $mainfont bold]
+       }
+       if {$isbold >= 2} {
+           bolden_name $row [concat $mainfont bold]
+       }
+    }
+    set nhighlights($row) $isbold
+}
+
+proc vrel_change {name ix op} {
+    global highlight_related
+
+    rhighlight_none
+    if {$highlight_related ne "None"} {
+       after idle drawvisible
+    }
+}
+
+# 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
+
+    catch {unset descendent}
+    set desc_todo [list $a]
+    catch {unset ancestor}
+    set anc_todo [list $a]
+    if {$highlight_related ne "None"} {
+       rhighlight_none
+       after idle drawvisible
+    }
+}
+
+proc rhighlight_none {} {
+    global rhighlights
+
+    catch {unset rhighlights}
+    unbolden
+}
+
+proc is_descendent {a} {
+    global curview children commitrow descendent desc_todo
+
+    set v $curview
+    set la $commitrow($v,$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} {
+           lappend leftover $do
+           continue
+       }
+       foreach nk $children($v,$do) {
+           if {![info exists descendent($nk)]} {
+               set descendent($nk) 1
+               lappend todo $nk
+               if {$nk eq $a} {
+                   set done 1
+               }
+           }
+       }
+       if {$done} {
+           set desc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
+           return
+       }
+    }
+    set descendent($a) 0
+    set desc_todo $leftover
+}
+
+proc is_ancestor {a} {
+    global curview parentlist commitrow ancestor anc_todo
+
+    set v $curview
+    set la $commitrow($v,$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} {
+           lappend leftover $do
+           continue
+       }
+       foreach np [lindex $parentlist $commitrow($v,$do)] {
+           if {![info exists ancestor($np)]} {
+               set ancestor($np) 1
+               lappend todo $np
+               if {$np eq $a} {
+                   set done 1
+               }
+           }
+       }
+       if {$done} {
+           set anc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
+           return
+       }
+    }
+    set ancestor($a) 0
+    set anc_todo $leftover
+}
+
+proc askrelhighlight {row id} {
+    global descendent highlight_related iddrawn mainfont rhighlights
+    global selectedline ancestor
+
+    if {![info exists selectedline]} return
+    set isbold 0
+    if {$highlight_related eq "Descendent" ||
+       $highlight_related eq "Not descendent"} {
+       if {![info exists descendent($id)]} {
+           is_descendent $id
+       }
+       if {$descendent($id) == ($highlight_related eq "Descendent")} {
+           set isbold 1
+       }
+    } elseif {$highlight_related eq "Ancestor" ||
+             $highlight_related eq "Not ancestor"} {
+       if {![info exists ancestor($id)]} {
+           is_ancestor $id
+       }
+       if {$ancestor($id) == ($highlight_related eq "Ancestor")} {
+           set isbold 1
+       }
+    }
+    if {[info exists iddrawn($id)]} {
+       if {$isbold && ![ishighlighted $row]} {
+           bolden $row [concat $mainfont bold]
+       }
+    }
+    set rhighlights($row) $isbold
+}
+
+proc next_hlcont {} {
+    global fhl_row fhl_dirn displayorder numcommits
+    global vhighlights fhighlights nhighlights rhighlights
+    global hlview filehighlight findstring highlight_related
+
+    if {![info exists fhl_dirn] || $fhl_dirn == 0} return
+    set row $fhl_row
+    while {1} {
+       if {$row < 0 || $row >= $numcommits} {
+           bell
+           set fhl_dirn 0
+           return
+       }
+       set id [lindex $displayorder $row]
+       if {[info exists hlview]} {
+           if {![info exists vhighlights($row)]} {
+               askvhighlight $row $id
+           }
+           if {$vhighlights($row) > 0} break
+       }
+       if {$findstring ne {}} {
+           if {![info exists nhighlights($row)]} {
+               askfindhighlight $row $id
+           }
+           if {$nhighlights($row) > 0} break
+       }
+       if {$highlight_related ne "None"} {
+           if {![info exists rhighlights($row)]} {
+               askrelhighlight $row $id
+           }
+           if {$rhighlights($row) > 0} break
+       }
+       if {[info exists filehighlight]} {
+           if {![info exists fhighlights($row)]} {
+               # ask for a few more while we're at it...
+               set r $row
+               for {set n 0} {$n < 100} {incr n} {
+                   if {![info exists fhighlights($r)]} {
+                       askfilehighlight $r [lindex $displayorder $r]
+                   }
+                   incr r $fhl_dirn
+                   if {$r < 0 || $r >= $numcommits} break
                }
                }
+               flushhighlights
+           }
+           if {$fhighlights($row) < 0} {
+               set fhl_row $row
+               return
            }
            }
+           if {$fhighlights($row) > 0} break
        }
        }
+       incr row $fhl_dirn
     }
     }
-    set highlighted($hlview) $max
+    set fhl_dirn 0
+    selectline $row 1
+}
+
+proc next_highlight {dirn} {
+    global selectedline fhl_row fhl_dirn
+    global hlview filehighlight findstring highlight_related
+
+    if {![info exists selectedline]} return
+    if {!([info exists hlview] || $findstring ne {} ||
+         $highlight_related ne "None" || [info exists filehighlight])} return
+    set fhl_row [expr {$selectedline + $dirn}]
+    set fhl_dirn $dirn
+    next_hlcont
+}
+
+proc cancel_next_highlight {} {
+    global fhl_dirn
+
+    set fhl_dirn 0
 }
 
 # Graph layout functions
 }
 
 # Graph layout functions
@@ -1791,20 +2370,38 @@ proc visiblerows {} {
     return [list $r0 $r1]
 }
 
     return [list $r0 $r1]
 }
 
-proc layoutmore {} {
+proc layoutmore {tmax} {
     global rowlaidout rowoptim commitidx numcommits optim_delay
     global uparrowlen curview
 
     global rowlaidout rowoptim commitidx numcommits optim_delay
     global uparrowlen curview
 
-    set row $rowlaidout
-    set rowlaidout [layoutrows $row $commitidx($curview) 0]
-    set orow [expr {$rowlaidout - $uparrowlen - 1}]
-    if {$orow > $rowoptim} {
-       optimize_rows $rowoptim 0 $orow
-       set rowoptim $orow
-    }
-    set canshow [expr {$rowoptim - $optim_delay}]
-    if {$canshow > $numcommits} {
-       showstuff $canshow
+    while {1} {
+       if {$rowoptim - $optim_delay > $numcommits} {
+           showstuff [expr {$rowoptim - $optim_delay}]
+       } elseif {$rowlaidout - $uparrowlen - 1 > $rowoptim} {
+           set nr [expr {$rowlaidout - $uparrowlen - 1 - $rowoptim}]
+           if {$nr > 100} {
+               set nr 100
+           }
+           optimize_rows $rowoptim 0 [expr {$rowoptim + $nr}]
+           incr rowoptim $nr
+       } elseif {$commitidx($curview) > $rowlaidout} {
+           set nr [expr {$commitidx($curview) - $rowlaidout}]
+           # may need to increase this threshold if uparrowlen or
+           # mingaplen are increased...
+           if {$nr > 150} {
+               set nr 150
+           }
+           set row $rowlaidout
+           set rowlaidout [layoutrows $row [expr {$row + $nr}] 0]
+           if {$rowlaidout == $row} {
+               return 0
+           }
+       } else {
+           return 0
+       }
+       if {$tmax ne {} && [clock clicks -milliseconds] >= $tmax} {
+           return 1
+       }
     }
 }
 
     }
 }
 
@@ -2332,12 +2929,11 @@ proc drawlines {id} {
 }
 
 proc drawcmittext {id row col rmx} {
 }
 
 proc drawcmittext {id row col rmx} {
-    global linespc canv canv2 canv3 canvy0
+    global linespc canv canv2 canv3 canvy0 fgcolor
     global commitlisted commitinfo rowidlist
     global rowtextx idpos idtags idheads idotherrefs
     global linehtag linentag linedtag
     global commitlisted commitinfo rowidlist
     global rowtextx idpos idtags idheads idotherrefs
     global linehtag linentag linedtag
-    global mainfont canvxmax
-    global hlview commitrow highlightedrows
+    global mainfont canvxmax boldrows boldnamerows fgcolor
 
     set ofill [expr {[lindex $commitlisted $row]? "blue": "white"}]
     set x [xc $row $col]
 
     set ofill [expr {[lindex $commitlisted $row]? "blue": "white"}]
     set x [xc $row $col]
@@ -2345,7 +2941,7 @@ proc drawcmittext {id row col rmx} {
     set orad [expr {$linespc / 3}]
     set t [$canv create oval [expr {$x - $orad}] [expr {$y - $orad}] \
               [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
     set orad [expr {$linespc / 3}]
     set t [$canv create oval [expr {$x - $orad}] [expr {$y - $orad}] \
               [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
-              -fill $ofill -outline black -width 1]
+              -fill $ofill -outline $fgcolor -width 1 -tags circle]
     $canv raise $t
     $canv bind $t <1> {selcanvline {} %x %y}
     set xt [xc $row [llength [lindex $rowidlist $row]]]
     $canv raise $t
     $canv bind $t <1> {selcanvline {} %x %y}
     set xt [xc $row [llength [lindex $rowidlist $row]]]
@@ -2363,17 +2959,23 @@ proc drawcmittext {id row col rmx} {
     set date [lindex $commitinfo($id) 2]
     set date [formatdate $date]
     set font $mainfont
     set date [lindex $commitinfo($id) 2]
     set date [formatdate $date]
     set font $mainfont
-    if {[info exists hlview] && [info exists commitrow($hlview,$id)]} {
+    set nfont $mainfont
+    set isbold [ishighlighted $row]
+    if {$isbold > 0} {
+       lappend boldrows $row
        lappend font bold
        lappend font bold
-       lappend highlightedrows $row
+       if {$isbold > 1} {
+           lappend boldnamerows $row
+           lappend nfont bold
+       }
     }
     }
-    set linehtag($row) [$canv create text $xt $y -anchor w \
-                           -text $headline -font $font]
+    set linehtag($row) [$canv create text $xt $y -anchor w -fill $fgcolor \
+                           -text $headline -font $font -tags text]
     $canv bind $linehtag($row) <Button-3> "rowmenu %X %Y $id"
     $canv bind $linehtag($row) <Button-3> "rowmenu %X %Y $id"
-    set linentag($row) [$canv2 create text 3 $y -anchor w \
-                           -text $name -font $mainfont]
-    set linedtag($row) [$canv3 create text 3 $y -anchor w \
-                           -text $date -font $mainfont]
+    set linentag($row) [$canv2 create text 3 $y -anchor w -fill $fgcolor \
+                           -text $name -font $nfont -tags text]
+    set linedtag($row) [$canv3 create text 3 $y -anchor w -fill $fgcolor \
+                           -text $date -font $mainfont -tags text]
     set xr [expr {$xt + [font measure $mainfont $headline]}]
     if {$xr > $canvxmax} {
        set canvxmax $xr
     set xr [expr {$xt + [font measure $mainfont $headline]}]
     if {$xr > $canvxmax} {
        set canvxmax $xr
@@ -2385,6 +2987,9 @@ proc drawcmitrow {row} {
     global displayorder rowidlist
     global idrangedrawn iddrawn
     global commitinfo parentlist numcommits
     global displayorder rowidlist
     global idrangedrawn iddrawn
     global commitinfo parentlist numcommits
+    global filehighlight fhighlights findstring nhighlights
+    global hlview vhighlights
+    global highlight_related rhighlights
 
     if {$row >= $numcommits} return
     foreach id [lindex $rowidlist $row] {
 
     if {$row >= $numcommits} return
     foreach id [lindex $rowidlist $row] {
@@ -2405,6 +3010,18 @@ proc drawcmitrow {row} {
     }
 
     set id [lindex $displayorder $row]
     }
 
     set id [lindex $displayorder $row]
+    if {[info exists hlview] && ![info exists vhighlights($row)]} {
+       askvhighlight $row $id
+    }
+    if {[info exists filehighlight] && ![info exists fhighlights($row)]} {
+       askfilehighlight $row $id
+    }
+    if {$findstring ne {} && ![info exists nhighlights($row)]} {
+       askfindhighlight $row $id
+    }
+    if {$highlight_related ne "None" && ![info exists rhighlights($row)]} {
+       askrelhighlight $row $id
+    }
     if {[info exists iddrawn($id)]} return
     set col [lsearch -exact [lindex $rowidlist $row] $id]
     if {$col < 0} {
     if {[info exists iddrawn($id)]} return
     set col [lsearch -exact [lindex $rowidlist $row] $id]
     if {$col < 0} {
@@ -2453,10 +3070,15 @@ proc drawvisible {} {
 
 proc clear_display {} {
     global iddrawn idrangedrawn
 
 proc clear_display {} {
     global iddrawn idrangedrawn
+    global vhighlights fhighlights nhighlights rhighlights
 
     allcanvs delete all
     catch {unset iddrawn}
     catch {unset idrangedrawn}
 
     allcanvs delete all
     catch {unset iddrawn}
     catch {unset idrangedrawn}
+    catch {unset vhighlights}
+    catch {unset fhighlights}
+    catch {unset nhighlights}
+    catch {unset rhighlights}
 }
 
 proc findcrossings {id} {
 }
 
 proc findcrossings {id} {
@@ -2573,9 +3195,9 @@ proc bindline {t id} {
 }
 
 proc drawtags {id x xt y1} {
 }
 
 proc drawtags {id x xt y1} {
-    global idtags idheads idotherrefs
+    global idtags idheads idotherrefs mainhead
     global linespc lthickness
     global linespc lthickness
-    global canv mainfont commitrow rowtextx curview
+    global canv mainfont commitrow rowtextx curview fgcolor bgcolor
 
     set marks {}
     set ntags 0
 
     set marks {}
     set ntags 0
@@ -2600,8 +3222,14 @@ proc drawtags {id x xt y1} {
     set yb [expr {$yt + $linespc - 1}]
     set xvals {}
     set wvals {}
     set yb [expr {$yt + $linespc - 1}]
     set xvals {}
     set wvals {}
+    set i -1
     foreach tag $marks {
     foreach tag $marks {
-       set wid [font measure $mainfont $tag]
+       incr i
+       if {$i >= $ntags && $i < $ntags + $nheads && $tag eq $mainhead} {
+           set wid [font measure [concat $mainfont bold] $tag]
+       } else {
+           set wid [font measure $mainfont $tag]
+       }
        lappend xvals $xt
        lappend wvals $wid
        set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}]
        lappend xvals $xt
        lappend wvals $wid
        set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}]
@@ -2612,6 +3240,7 @@ proc drawtags {id x xt y1} {
     foreach tag $marks x $xvals wid $wvals {
        set xl [expr {$x + $delta}]
        set xr [expr {$x + $delta + $wid + $lthickness}]
     foreach tag $marks x $xvals wid $wvals {
        set xl [expr {$x + $delta}]
        set xr [expr {$x + $delta + $wid + $lthickness}]
+       set font $mainfont
        if {[incr ntags -1] >= 0} {
            # draw a tag
            set t [$canv create polygon $x [expr {$yt + $delta}] $xl $yt \
        if {[incr ntags -1] >= 0} {
            # draw a tag
            set t [$canv create polygon $x [expr {$yt + $delta}] $xl $yt \
@@ -2623,6 +3252,9 @@ proc drawtags {id x xt y1} {
            # draw a head or other ref
            if {[incr nheads -1] >= 0} {
                set col green
            # draw a head or other ref
            if {[incr nheads -1] >= 0} {
                set col green
+               if {$tag eq $mainhead} {
+                   lappend font bold
+               }
            } else {
                set col "#ddddff"
            }
            } else {
                set col "#ddddff"
            }
@@ -2638,10 +3270,12 @@ proc drawtags {id x xt y1} {
                        -width 0 -fill "#ffddaa" -tags tag.$id
            }
        }
                        -width 0 -fill "#ffddaa" -tags tag.$id
            }
        }
-       set t [$canv create text $xl $y1 -anchor w -text $tag \
-                  -font $mainfont -tags tag.$id]
+       set t [$canv create text $xl $y1 -anchor w -text $tag -fill $fgcolor \
+                  -font $font -tags [list tag.$id text]]
        if {$ntags >= 0} {
            $canv bind $t <1> [list showtag $tag 1]
        if {$ntags >= 0} {
            $canv bind $t <1> [list showtag $tag 1]
+       } elseif {$nheads >= 0} {
+           $canv bind $t <Button-3> [list headmenu %X %Y $id $tag]
        }
     }
     return $xt
        }
     }
     return $xt
@@ -2660,16 +3294,16 @@ proc xcoord {i level ln} {
 }
 
 proc show_status {msg} {
 }
 
 proc show_status {msg} {
-    global canv mainfont
+    global canv mainfont fgcolor
 
     clear_display
 
     clear_display
-    $canv create text 3 3 -anchor nw -text $msg -font $mainfont -tags textitems
+    $canv create text 3 3 -anchor nw -text $msg -font $mainfont \
+       -tags text -fill $fgcolor
 }
 
 proc finishcommits {} {
     global commitidx phase curview
 }
 
 proc finishcommits {} {
     global commitidx phase curview
-    global canv mainfont ctext maincursor textcursor
-    global findinprogress pending_select
+    global pending_select
 
     if {$commitidx($curview) > 0} {
        drawrest
 
     if {$commitidx($curview) > 0} {
        drawrest
@@ -2680,41 +3314,141 @@ proc finishcommits {} {
     catch {unset pending_select}
 }
 
     catch {unset pending_select}
 }
 
-# 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} {
-    global ctext curtextcursor
+# 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 childlist commitlisted
+    global commitrow curview rowidlist rowoffsets numcommits
+    global rowrangelist idrowranges rowlaidout rowoptim numcommits
+    global linesegends selectedline
 
 
-    if {[$ctext cget -cursor] == $curtextcursor} {
-       $ctext config -cursor $c
+    if {$row >= $numcommits} {
+       puts "oops, inserting new row $row but only have $numcommits rows"
+       return
     }
     }
-    set curtextcursor $c
-}
-
-proc nowbusy {what} {
-    global isbusy
-
-    if {[array names isbusy] eq {}} {
-       . config -cursor watch
-       settextcursor watch
+    set p [lindex $displayorder $row]
+    set displayorder [linsert $displayorder $row $newcmit]
+    set parentlist [linsert $parentlist $row $p]
+    set kids [lindex $childlist $row]
+    lappend kids $newcmit
+    lset childlist $row $kids
+    set childlist [linsert $childlist $row {}]
+    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
     }
     }
-    set isbusy($what) 1
-}
-
-proc notbusy {what} {
-    global isbusy maincursor textcursor
 
 
-    catch {unset isbusy($what)}
-    if {[array names isbusy] eq {}} {
-       . config -cursor $maincursor
-       settextcursor $textcursor
+    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 {}]
+    set l [llength $rowrangelist]
+    for {set r 0} {$r < $l} {incr r} {
+       set ranges [lindex $rowrangelist $r]
+       if {$ranges ne {} && [lindex $ranges end] >= $row} {
+           set newranges {}
+           foreach x $ranges {
+               if {$x >= $row} {
+                   lappend newranges [expr {$x + 1}]
+               } else {
+                   lappend newranges $x
+               }
+           }
+           lset rowrangelist $r $newranges
+       }
+    }
+    if {[llength $kids] > 1} {
+       set rp1 [expr {$row + 1}]
+       set ranges [lindex $rowrangelist $rp1]
+       if {$ranges eq {}} {
+           set ranges [list $row $rp1]
+       } elseif {[lindex $ranges end-1] == $rp1} {
+           lset ranges end-1 $row
+       }
+       lset rowrangelist $rp1 $ranges
+    }
+    foreach id [array names idrowranges] {
+       set ranges $idrowranges($id)
+       if {$ranges ne {} && [lindex $ranges end] >= $row} {
+           set newranges {}
+           foreach x $ranges {
+               if {$x >= $row} {
+                   lappend newranges [expr {$x + 1}]
+               } else {
+                   lappend newranges $x
+               }
+           }
+           set idrowranges($id) $newranges
+       }
+    }
+
+    set linesegends [linsert $linesegends $row {}]
+
+    incr rowlaidout
+    incr rowoptim
+    incr numcommits
+
+    if {[info exists selectedline] && $selectedline >= $row} {
+       incr selectedline
+    }
+    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} {
+    global ctext curtextcursor
+
+    if {[$ctext cget -cursor] == $curtextcursor} {
+       $ctext config -cursor $c
+    }
+    set curtextcursor $c
+}
+
+proc nowbusy {what} {
+    global isbusy
+
+    if {[array names isbusy] eq {}} {
+       . config -cursor watch
+       settextcursor watch
+    }
+    set isbusy($what) 1
+}
+
+proc notbusy {what} {
+    global isbusy maincursor textcursor
+
+    catch {unset isbusy($what)}
+    if {[array names isbusy] eq {}} {
+       . config -cursor $maincursor
+       settextcursor $textcursor
     }
 }
 
 proc drawrest {} {
     }
 }
 
 proc drawrest {} {
-    global numcommits
     global startmsecs
     global startmsecs
-    global canvy0 numcommits linespc
     global rowlaidout commitidx curview
     global pending_select
 
     global rowlaidout commitidx curview
     global pending_select
 
@@ -2728,6 +3462,7 @@ proc drawrest {} {
     }
 
     set drawmsecs [expr {[clock clicks -milliseconds] - $startmsecs}]
     }
 
     set drawmsecs [expr {[clock clicks -milliseconds] - $startmsecs}]
+    #global numcommits
     #puts "overall $drawmsecs ms for $numcommits commits"
 }
 
     #puts "overall $drawmsecs ms for $numcommits commits"
 }
 
@@ -2760,12 +3495,9 @@ proc dofind {} {
 
     stopfindproc
     unmarkmatches
 
     stopfindproc
     unmarkmatches
+    cancel_next_highlight
     focus .
     set matchinglines {}
     focus .
     set matchinglines {}
-    if {$findloc == "Pickaxe"} {
-       findpatches
-       return
-    }
     if {$findtype == "IgnCase"} {
        set foundstring [string tolower $findstring]
     } else {
     if {$findtype == "IgnCase"} {
        set foundstring [string tolower $findstring]
     } else {
@@ -2775,17 +3507,13 @@ proc dofind {} {
     if {$foundstrlen == 0} return
     regsub -all {[*?\[\\]} $foundstring {\\&} matchstring
     set matchstring "*$matchstring*"
     if {$foundstrlen == 0} return
     regsub -all {[*?\[\\]} $foundstring {\\&} matchstring
     set matchstring "*$matchstring*"
-    if {$findloc == "Files"} {
-       findfiles
-       return
-    }
     if {![info exists selectedline]} {
        set oldsel -1
     } else {
        set oldsel $selectedline
     }
     set didsel 0
     if {![info exists selectedline]} {
        set oldsel -1
     } else {
        set oldsel $selectedline
     }
     set didsel 0
-    set fldtypes {Headline Author Date Committer CDate Comment}
+    set fldtypes {Headline Author Date Committer CDate Comments}
     set l -1
     foreach id $displayorder {
        set d $commitdata($id)
     set l -1
     foreach id $displayorder {
        set d $commitdata($id)
@@ -2888,18 +3616,6 @@ proc findprev {} {
     }
 }
 
     }
 }
 
-proc findlocchange {name ix op} {
-    global findloc findtype findtypemenu
-    if {$findloc == "Pickaxe"} {
-       set findtype Exact
-       set state disabled
-    } else {
-       set state normal
-    }
-    $findtypemenu entryconf 1 -state $state
-    $findtypemenu entryconf 2 -state $state
-}
-
 proc stopfindproc {{done 0}} {
     global findprocpid findprocfile findids
     global ctext findoldcursor phase maincursor textcursor
 proc stopfindproc {{done 0}} {
     global findprocpid findprocfile findids
     global ctext findoldcursor phase maincursor textcursor
@@ -2917,247 +3633,6 @@ proc stopfindproc {{done 0}} {
     notbusy find
 }
 
     notbusy find
 }
 
-proc findpatches {} {
-    global findstring selectedline numcommits
-    global findprocpid findprocfile
-    global finddidsel ctext displayorder findinprogress
-    global findinsertpos
-
-    if {$numcommits == 0} return
-
-    # make a list of all the ids to search, starting at the one
-    # after the selected line (if any)
-    if {[info exists selectedline]} {
-       set l $selectedline
-    } else {
-       set l -1
-    }
-    set inputids {}
-    for {set i 0} {$i < $numcommits} {incr i} {
-       if {[incr l] >= $numcommits} {
-           set l 0
-       }
-       append inputids [lindex $displayorder $l] "\n"
-    }
-
-    if {[catch {
-       set f [open [list | git diff-tree --stdin -s -r -S$findstring \
-                        << $inputids] r]
-    } err]} {
-       error_popup "Error starting search process: $err"
-       return
-    }
-
-    set findinsertpos end
-    set findprocfile $f
-    set findprocpid [pid $f]
-    fconfigure $f -blocking 0
-    fileevent $f readable readfindproc
-    set finddidsel 0
-    nowbusy find
-    set findinprogress 1
-}
-
-proc readfindproc {} {
-    global findprocfile finddidsel
-    global commitrow matchinglines findinsertpos curview
-
-    set n [gets $findprocfile line]
-    if {$n < 0} {
-       if {[eof $findprocfile]} {
-           stopfindproc 1
-           if {!$finddidsel} {
-               bell
-           }
-       }
-       return
-    }
-    if {![regexp {^[0-9a-f]{40}} $line id]} {
-       error_popup "Can't parse git diff-tree output: $line"
-       stopfindproc
-       return
-    }
-    if {![info exists commitrow($curview,$id)]} {
-       puts stderr "spurious id: $id"
-       return
-    }
-    set l $commitrow($curview,$id)
-    insertmatch $l $id
-}
-
-proc insertmatch {l id} {
-    global matchinglines findinsertpos finddidsel
-
-    if {$findinsertpos == "end"} {
-       if {$matchinglines != {} && $l < [lindex $matchinglines 0]} {
-           set matchinglines [linsert $matchinglines 0 $l]
-           set findinsertpos 1
-       } else {
-           lappend matchinglines $l
-       }
-    } else {
-       set matchinglines [linsert $matchinglines $findinsertpos $l]
-       incr findinsertpos
-    }
-    markheadline $l $id
-    if {!$finddidsel} {
-       findselectline $l
-       set finddidsel 1
-    }
-}
-
-proc findfiles {} {
-    global selectedline numcommits displayorder ctext
-    global ffileline finddidsel parentlist
-    global findinprogress findstartline findinsertpos
-    global treediffs fdiffid fdiffsneeded fdiffpos
-    global findmergefiles
-
-    if {$numcommits == 0} return
-
-    if {[info exists selectedline]} {
-       set l [expr {$selectedline + 1}]
-    } else {
-       set l 0
-    }
-    set ffileline $l
-    set findstartline $l
-    set diffsneeded {}
-    set fdiffsneeded {}
-    while 1 {
-       set id [lindex $displayorder $l]
-       if {$findmergefiles || [llength [lindex $parentlist $l]] == 1} {
-           if {![info exists treediffs($id)]} {
-               append diffsneeded "$id\n"
-               lappend fdiffsneeded $id
-           }
-       }
-       if {[incr l] >= $numcommits} {
-           set l 0
-       }
-       if {$l == $findstartline} break
-    }
-
-    # start off a git diff-tree process if needed
-    if {$diffsneeded ne {}} {
-       if {[catch {
-           set df [open [list | git diff-tree -r --stdin << $diffsneeded] r]
-       } err ]} {
-           error_popup "Error starting search process: $err"
-           return
-       }
-       catch {unset fdiffid}
-       set fdiffpos 0
-       fconfigure $df -blocking 0
-       fileevent $df readable [list readfilediffs $df]
-    }
-
-    set finddidsel 0
-    set findinsertpos end
-    set id [lindex $displayorder $l]
-    nowbusy find
-    set findinprogress 1
-    findcont
-    update
-}
-
-proc readfilediffs {df} {
-    global findid fdiffid fdiffs
-
-    set n [gets $df line]
-    if {$n < 0} {
-       if {[eof $df]} {
-           donefilediff
-           if {[catch {close $df} err]} {
-               stopfindproc
-               bell
-               error_popup "Error in git diff-tree: $err"
-           } elseif {[info exists findid]} {
-               set id $findid
-               stopfindproc
-               bell
-               error_popup "Couldn't find diffs for $id"
-           }
-       }
-       return
-    }
-    if {[regexp {^([0-9a-f]{40})$} $line match id]} {
-       # start of a new string of diffs
-       donefilediff
-       set fdiffid $id
-       set fdiffs {}
-    } elseif {[string match ":*" $line]} {
-       lappend fdiffs [lindex $line 5]
-    }
-}
-
-proc donefilediff {} {
-    global fdiffid fdiffs treediffs findid
-    global fdiffsneeded fdiffpos
-
-    if {[info exists fdiffid]} {
-       while {[lindex $fdiffsneeded $fdiffpos] ne $fdiffid
-              && $fdiffpos < [llength $fdiffsneeded]} {
-           # git diff-tree doesn't output anything for a commit
-           # which doesn't change anything
-           set nullid [lindex $fdiffsneeded $fdiffpos]
-           set treediffs($nullid) {}
-           if {[info exists findid] && $nullid eq $findid} {
-               unset findid
-               findcont
-           }
-           incr fdiffpos
-       }
-       incr fdiffpos
-
-       if {![info exists treediffs($fdiffid)]} {
-           set treediffs($fdiffid) $fdiffs
-       }
-       if {[info exists findid] && $fdiffid eq $findid} {
-           unset findid
-           findcont
-       }
-    }
-}
-
-proc findcont {} {
-    global findid treediffs parentlist
-    global ffileline findstartline finddidsel
-    global displayorder numcommits matchinglines findinprogress
-    global findmergefiles
-
-    set l $ffileline
-    while {1} {
-       set id [lindex $displayorder $l]
-       if {$findmergefiles || [llength [lindex $parentlist $l]] == 1} {
-           if {![info exists treediffs($id)]} {
-               set findid $id
-               set ffileline $l
-               return
-           }
-           set doesmatch 0
-           foreach f $treediffs($id) {
-               set x [findmatches $f]
-               if {$x != {}} {
-                   set doesmatch 1
-                   break
-               }
-           }
-           if {$doesmatch} {
-               insertmatch $l $id
-           }
-       }
-       if {[incr l] >= $numcommits} {
-           set l 0
-       }
-       if {$l == $findstartline} break
-    }
-    stopfindproc
-    if {!$finddidsel} {
-       bell
-    }
-}
-
 # mark a commit as matching by putting a yellow background
 # behind the headline
 proc markheadline {l id} {
 # mark a commit as matching by putting a yellow background
 # behind the headline
 proc markheadline {l id} {
@@ -3222,7 +3697,7 @@ proc commit_descriptor {p} {
     if {[llength $commitinfo($p)] > 1} {
        set l [lindex $commitinfo($p) 0]
     }
     if {[llength $commitinfo($p)] > 1} {
        set l [lindex $commitinfo($p) 0]
     }
-    return "$p ($l)"
+    return "$p ($l)\n"
 }
 
 # append some text to the ctext widget, and make any SHA1 ID
 }
 
 # append some text to the ctext widget, and make any SHA1 ID
@@ -3232,7 +3707,6 @@ proc appendwithlinks {text tags} {
 
     set start [$ctext index "end - 1c"]
     $ctext insert end $text $tags
 
     set start [$ctext index "end - 1c"]
     $ctext insert end $text $tags
-    $ctext insert end "\n"
     set links [regexp -indices -all -inline {[0-9a-f]{40}} $text]
     foreach l $links {
        set s [lindex $l 0]
     set links [regexp -indices -all -inline {[0-9a-f]{40}} $text]
     foreach l $links {
        set s [lindex $l 0]
@@ -3267,6 +3741,69 @@ proc viewnextline {dir} {
     allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
 }
 
     allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
 }
 
+# add a list of tag or branch names at position pos
+# returns the number of names inserted
+proc appendrefs {pos tags var} {
+    global ctext commitrow linknum curview $var
+
+    if {[catch {$ctext index $pos}]} {
+       return 0
+    }
+    set tags [lsort $tags]
+    set sep {}
+    foreach tag $tags {
+       set id [set $var\($tag\)]
+       set lk link$linknum
+       incr linknum
+       $ctext insert $pos $sep
+       $ctext insert $pos $tag $lk
+       $ctext tag conf $lk -foreground blue
+       if {[info exists commitrow($curview,$id)]} {
+           $ctext tag bind $lk <1> \
+               [list selectline $commitrow($curview,$id) 1]
+           $ctext tag conf $lk -underline 1
+           $ctext tag bind $lk <Enter> { %W configure -cursor hand2 }
+           $ctext tag bind $lk <Leave> { %W configure -cursor $curtextcursor }
+       }
+       set sep ", "
+    }
+    return [llength $tags]
+}
+
+proc taglist {ids} {
+    global idtags
+
+    set tags {}
+    foreach id $ids {
+       foreach tag $idtags($id) {
+           lappend tags $tag
+       }
+    }
+    return $tags
+}
+
+# called when we have finished computing the nearby tags
+proc dispneartags {} {
+    global selectedline currentid ctext anc_tags desc_tags showneartags
+    global desc_heads
+
+    if {![info exists selectedline] || !$showneartags} return
+    set id $currentid
+    $ctext conf -state normal
+    if {[info exists desc_heads($id)]} {
+       if {[appendrefs branch $desc_heads($id) headids] > 1} {
+           $ctext insert "branch -2c" "es"
+       }
+    }
+    if {[info exists anc_tags($id)]} {
+       appendrefs follows [taglist $anc_tags($id)] tagids
+    }
+    if {[info exists desc_tags($id)]} {
+       appendrefs precedes [taglist $desc_tags($id)] tagids
+    }
+    $ctext conf -state disabled
+}
+
 proc selectline {l isnew} {
     global canv canv2 canv3 ctext commitinfo selectedline
     global displayorder linehtag linentag linedtag
 proc selectline {l isnew} {
     global canv canv2 canv3 ctext commitinfo selectedline
     global displayorder linehtag linentag linedtag
@@ -3274,11 +3811,12 @@ proc selectline {l isnew} {
     global currentid sha1entry
     global commentend idtags linknum
     global mergemax numcommits pending_select
     global currentid sha1entry
     global commentend idtags linknum
     global mergemax numcommits pending_select
-    global cmitmode
+    global cmitmode desc_tags anc_tags showneartags allcommits desc_heads
 
     catch {unset pending_select}
     $canv delete hover
     normalline
 
     catch {unset pending_select}
     $canv delete hover
     normalline
+    cancel_next_highlight
     if {$l < 0 || $l >= $numcommits} return
     set y [expr {$canvy0 + $l * $linespc}]
     set ymax [lindex [$canv cget -scrollregion] 3]
     if {$l < 0 || $l >= $numcommits} return
     set y [expr {$canvy0 + $l * $linespc}]
     set ymax [lindex [$canv cget -scrollregion] 3]
@@ -3342,9 +3880,10 @@ proc selectline {l isnew} {
     $sha1entry insert 0 $id
     $sha1entry selection from 0
     $sha1entry selection to end
     $sha1entry insert 0 $id
     $sha1entry selection from 0
     $sha1entry selection to end
+    rhighlight_sel $id
 
     $ctext conf -state normal
 
     $ctext conf -state normal
-    $ctext delete 0.0 end
+    clear_ctext
     set linknum 0
     set info $commitinfo($id)
     set date [formatdate [lindex $info 2]]
     set linknum 0
     set info $commitinfo($id)
     set date [formatdate [lindex $info 2]]
@@ -3375,16 +3914,44 @@ proc selectline {l isnew} {
        }
     } else {
        foreach p $olds {
        }
     } else {
        foreach p $olds {
-           append headers "Parent: [commit_descriptor $p]\n"
+           append headers "Parent: [commit_descriptor $p]"
        }
     }
 
     foreach c [lindex $childlist $l] {
        }
     }
 
     foreach c [lindex $childlist $l] {
-       append headers "Child:  [commit_descriptor $c]\n"
+       append headers "Child:  [commit_descriptor $c]"
     }
 
     # make anything that looks like a SHA1 ID be a clickable link
     appendwithlinks $headers {}
     }
 
     # make anything that looks like a SHA1 ID be a clickable link
     appendwithlinks $headers {}
+    if {$showneartags} {
+       if {![info exists allcommits]} {
+           getallcommits
+       }
+       $ctext insert end "Branch: "
+       $ctext mark set branch "end -1c"
+       $ctext mark gravity branch left
+       if {[info exists desc_heads($id)]} {
+           if {[appendrefs branch $desc_heads($id) headids] > 1} {
+               # turn "Branch" into "Branches"
+               $ctext insert "branch -2c" "es"
+           }
+       }
+       $ctext insert end "\nFollows: "
+       $ctext mark set follows "end -1c"
+       $ctext mark gravity follows left
+       if {[info exists anc_tags($id)]} {
+           appendrefs follows [taglist $anc_tags($id)] tagids
+       }
+       $ctext insert end "\nPrecedes: "
+       $ctext mark set precedes "end -1c"
+       $ctext mark gravity precedes left
+       if {[info exists desc_tags($id)]} {
+           appendrefs precedes [taglist $desc_tags($id)] tagids
+       }
+       $ctext insert end "\n"
+    }
+    $ctext insert end "\n"
     appendwithlinks [lindex $info 5] {comment}
 
     $ctext tag delete Comments
     appendwithlinks [lindex $info 5] {comment}
 
     $ctext tag delete Comments
@@ -3448,6 +4015,8 @@ proc unselectline {} {
     catch {unset selectedline}
     catch {unset currentid}
     allcanvs delete secsel
     catch {unset selectedline}
     catch {unset currentid}
     allcanvs delete secsel
+    rhighlight_none
+    cancel_next_highlight
 }
 
 proc reselectline {} {
 }
 
 proc reselectline {} {
@@ -3581,7 +4150,7 @@ proc showfile {f} {
     fconfigure $bf -blocking 0
     fileevent $bf readable [list getblobline $bf $diffids]
     $ctext config -state normal
     fconfigure $bf -blocking 0
     fileevent $bf readable [list getblobline $bf $diffids]
     $ctext config -state normal
-    $ctext delete $commentend end
+    clear_ctext $commentend
     $ctext insert end "\n"
     $ctext insert end "$f\n" filesep
     $ctext config -state disabled
     $ctext insert end "\n"
     $ctext insert end "$f\n" filesep
     $ctext config -state disabled
@@ -3871,16 +4440,163 @@ proc getblobdiffline {bdf ids} {
     }
 }
 
     }
 }
 
+proc prevfile {} {
+    global difffilestart ctext
+    set prev [lindex $difffilestart 0]
+    set here [$ctext index @0,0]
+    foreach loc $difffilestart {
+       if {[$ctext compare $loc >= $here]} {
+           $ctext yview $prev
+           return
+       }
+       set prev $loc
+    }
+    $ctext yview $prev
+}
+
 proc nextfile {} {
     global difffilestart ctext
     set here [$ctext index @0,0]
     foreach loc $difffilestart {
        if {[$ctext compare $loc > $here]} {
            $ctext yview $loc
 proc nextfile {} {
     global difffilestart ctext
     set here [$ctext index @0,0]
     foreach loc $difffilestart {
        if {[$ctext compare $loc > $here]} {
            $ctext yview $loc
+           return
+       }
+    }
+}
+
+proc clear_ctext {{first 1.0}} {
+    global ctext smarktop smarkbot
+
+    set l [lindex [split $first .] 0]
+    if {![info exists smarktop] || [$ctext compare $first < $smarktop.0]} {
+       set smarktop $l
+    }
+    if {![info exists smarkbot] || [$ctext compare $first < $smarkbot.0]} {
+       set smarkbot $l
+    }
+    $ctext delete $first end
+}
+
+proc incrsearch {name ix op} {
+    global ctext searchstring searchdirn
+
+    $ctext tag remove found 1.0 end
+    if {[catch {$ctext index anchor}]} {
+       # no anchor set, use start of selection, or of visible area
+       set sel [$ctext tag ranges sel]
+       if {$sel ne {}} {
+           $ctext mark set anchor [lindex $sel 0]
+       } elseif {$searchdirn eq "-forwards"} {
+           $ctext mark set anchor @0,0
+       } else {
+           $ctext mark set anchor @0,[winfo height $ctext]
+       }
+    }
+    if {$searchstring ne {}} {
+       set here [$ctext search $searchdirn -- $searchstring anchor]
+       if {$here ne {}} {
+           $ctext see $here
+       }
+       searchmarkvisible 1
+    }
+}
+
+proc dosearch {} {
+    global sstring ctext searchstring searchdirn
+
+    focus $sstring
+    $sstring icursor end
+    set searchdirn -forwards
+    if {$searchstring ne {}} {
+       set sel [$ctext tag ranges sel]
+       if {$sel ne {}} {
+           set start "[lindex $sel 0] + 1c"
+       } elseif {[catch {set start [$ctext index anchor]}]} {
+           set start "@0,0"
+       }
+       set match [$ctext search -count mlen -- $searchstring $start]
+       $ctext tag remove sel 1.0 end
+       if {$match eq {}} {
+           bell
+           return
+       }
+       $ctext see $match
+       set mend "$match + $mlen c"
+       $ctext tag add sel $match $mend
+       $ctext mark unset anchor
+    }
+}
+
+proc dosearchback {} {
+    global sstring ctext searchstring searchdirn
+
+    focus $sstring
+    $sstring icursor end
+    set searchdirn -backwards
+    if {$searchstring ne {}} {
+       set sel [$ctext tag ranges sel]
+       if {$sel ne {}} {
+           set start [lindex $sel 0]
+       } elseif {[catch {set start [$ctext index anchor]}]} {
+           set start @0,[winfo height $ctext]
+       }
+       set match [$ctext search -backwards -count ml -- $searchstring $start]
+       $ctext tag remove sel 1.0 end
+       if {$match eq {}} {
+           bell
+           return
+       }
+       $ctext see $match
+       set mend "$match + $ml c"
+       $ctext tag add sel $match $mend
+       $ctext mark unset anchor
+    }
+}
+
+proc searchmark {first last} {
+    global ctext searchstring
+
+    set mend $first.0
+    while {1} {
+       set match [$ctext search -count mlen -- $searchstring $mend $last.end]
+       if {$match eq {}} break
+       set mend "$match + $mlen c"
+       $ctext tag add found $match $mend
+    }
+}
+
+proc searchmarkvisible {doall} {
+    global ctext smarktop smarkbot
+
+    set topline [lindex [split [$ctext index @0,0] .] 0]
+    set botline [lindex [split [$ctext index @0,[winfo height $ctext]] .] 0]
+    if {$doall || $botline < $smarktop || $topline > $smarkbot} {
+       # no overlap with previous
+       searchmark $topline $botline
+       set smarktop $topline
+       set smarkbot $botline
+    } else {
+       if {$topline < $smarktop} {
+           searchmark $topline [expr {$smarktop-1}]
+           set smarktop $topline
+       }
+       if {$botline > $smarkbot} {
+           searchmark [expr {$smarkbot+1}] $botline
+           set smarkbot $botline
        }
     }
 }
 
        }
     }
 }
 
+proc scrolltext {f0 f1} {
+    global searchstring
+
+    .ctop.cdet.left.sb set $f0 $f1
+    if {$searchstring ne {}} {
+       searchmarkvisible 0
+    }
+}
+
 proc setcoords {} {
     global linespc charspc canvx0 canvy0 mainfont
     global xspc1 xspc2 lthickness
 proc setcoords {} {
     global linespc charspc canvx0 canvy0 mainfont
     global xspc1 xspc2 lthickness
@@ -3907,6 +4623,7 @@ proc redisplay {} {
     drawvisible
     if {[info exists selectedline]} {
        selectline $selectedline 0
     drawvisible
     if {[info exists selectedline]} {
        selectline $selectedline 0
+       allcanvs yview moveto [lindex $span 0]
     }
 }
 
     }
 }
 
@@ -4050,7 +4767,8 @@ proc linehover {} {
     set t [$canv create rectangle $x0 $y0 $x1 $y1 \
               -fill \#ffff80 -outline black -width 1 -tags hover]
     $canv raise $t
     set t [$canv create rectangle $x0 $y0 $x1 $y1 \
               -fill \#ffff80 -outline black -width 1 -tags hover]
     $canv raise $t
-    set t [$canv create text $x $y -anchor nw -text $text -tags hover -font $mainfont]
+    set t [$canv create text $x $y -anchor nw -text $text -tags hover \
+              -font $mainfont]
     $canv raise $t
 }
 
     $canv raise $t
 }
 
@@ -4115,7 +4833,7 @@ proc lineclick {x y id isnew} {
     }
     # fill the details pane with info about this line
     $ctext conf -state normal
     }
     # fill the details pane with info about this line
     $ctext conf -state normal
-    $ctext delete 0.0 end
+    clear_ctext
     $ctext tag conf link -foreground blue -underline 1
     $ctext tag bind link <Enter> { %W configure -cursor hand2 }
     $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
     $ctext tag conf link -foreground blue -underline 1
     $ctext tag bind link <Enter> { %W configure -cursor hand2 }
     $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
@@ -4181,9 +4899,9 @@ proc rowmenu {x y id} {
     } else {
        set state normal
     }
     } else {
        set state normal
     }
-    $rowctxmenu entryconfigure 0 -state $state
-    $rowctxmenu entryconfigure 1 -state $state
-    $rowctxmenu entryconfigure 2 -state $state
+    $rowctxmenu entryconfigure "Diff this*" -state $state
+    $rowctxmenu entryconfigure "Diff selected*" -state $state
+    $rowctxmenu entryconfigure "Make patch" -state $state
     set rowmenuid $id
     tk_popup $rowctxmenu $x $y
 }
     set rowmenuid $id
     tk_popup $rowctxmenu $x $y
 }
@@ -4208,7 +4926,7 @@ proc doseldiff {oldid newid} {
     global commitinfo
 
     $ctext conf -state normal
     global commitinfo
 
     $ctext conf -state normal
-    $ctext delete 0.0 end
+    clear_ctext
     init_flist "Top"
     $ctext insert end "From "
     $ctext tag conf link -foreground blue -underline 1
     init_flist "Top"
     $ctext insert end "From "
     $ctext tag conf link -foreground blue -underline 1
@@ -4373,16 +5091,24 @@ proc domktag {} {
     set tagids($tag) $id
     lappend idtags($id) $tag
     redrawtags $id
     set tagids($tag) $id
     lappend idtags($id) $tag
     redrawtags $id
+    addedtag $id
 }
 
 proc redrawtags {id} {
     global canv linehtag commitrow idpos selectedline curview
 }
 
 proc redrawtags {id} {
     global canv linehtag commitrow idpos selectedline curview
+    global mainfont canvxmax
 
     if {![info exists commitrow($curview,$id)]} return
     drawcmitrow $commitrow($curview,$id)
     $canv delete tag.$id
     set xt [eval drawtags $id $idpos($id)]
     $canv coords $linehtag($commitrow($curview,$id)) $xt [lindex $idpos($id) 2]
 
     if {![info exists commitrow($curview,$id)]} return
     drawcmitrow $commitrow($curview,$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]}]
+    if {$xr > $canvxmax} {
+       set canvxmax $xr
+       setcanvscroll
+    }
     if {[info exists selectedline]
        && $selectedline == $commitrow($curview,$id)} {
        selectline $selectedline 0
     if {[info exists selectedline]
        && $selectedline == $commitrow($curview,$id)} {
        selectline $selectedline 0
@@ -4456,26 +5182,537 @@ proc wrcomcan {} {
     unset wrcomtop
 }
 
     unset wrcomtop
 }
 
-proc listrefs {id} {
-    global idtags idheads idotherrefs
+proc mkbranch {} {
+    global rowmenuid mkbrtop
 
 
-    set x {}
+    set top .makebranch
+    catch {destroy $top}
+    toplevel $top
+    label $top.title -text "Create new branch"
+    grid $top.title - -pady 10
+    label $top.id -text "ID:"
+    entry $top.sha1 -width 40 -relief flat
+    $top.sha1 insert 0 $rowmenuid
+    $top.sha1 conf -state readonly
+    grid $top.id $top.sha1 -sticky w
+    label $top.nlab -text "Name:"
+    entry $top.name -width 40
+    grid $top.nlab $top.name -sticky w
+    frame $top.buts
+    button $top.buts.go -text "Create" -command [list mkbrgo $top]
+    button $top.buts.can -text "Cancel" -command "catch {destroy $top}"
+    grid $top.buts.go $top.buts.can
+    grid columnconfigure $top.buts 0 -weight 1 -uniform a
+    grid columnconfigure $top.buts 1 -weight 1 -uniform a
+    grid $top.buts - -pady 10 -sticky ew
+    focus $top.name
+}
+
+proc mkbrgo {top} {
+    global headids idheads
+
+    set name [$top.name get]
+    set id [$top.sha1 get]
+    if {$name eq {}} {
+       error_popup "Please specify a name for the new branch"
+       return
+    }
+    catch {destroy $top}
+    nowbusy newbranch
+    update
+    if {[catch {
+       exec git branch $name $id
+    } err]} {
+       notbusy newbranch
+       error_popup $err
+    } else {
+       addedhead $id $name
+       # XXX should update list of heads displayed for selected commit
+       notbusy newbranch
+       redrawtags $id
+    }
+}
+
+proc cherrypick {} {
+    global rowmenuid curview commitrow
+    global mainhead desc_heads anc_tags desc_tags allparents allchildren
+
+    if {[info exists desc_heads($rowmenuid)]
+       && [lsearch -exact $desc_heads($rowmenuid) $mainhead] >= 0} {
+       set ok [confirm_popup "Commit [string range $rowmenuid 0 7] is already\
+                       included in branch $mainhead -- really re-apply it?"]
+       if {!$ok} return
+    }
+    nowbusy cherrypick
+    update
+    set oldhead [exec git rev-parse HEAD]
+    # Unfortunately git-cherry-pick writes stuff to stderr even when
+    # no error occurs, and exec takes that as an indication of error...
+    if {[catch {exec sh -c "git cherry-pick -r $rowmenuid 2>&1"} err]} {
+       notbusy cherrypick
+       error_popup $err
+       return
+    }
+    set newhead [exec git rev-parse HEAD]
+    if {$newhead eq $oldhead} {
+       notbusy cherrypick
+       error_popup "No changes committed"
+       return
+    }
+    set allparents($newhead) $oldhead
+    lappend allchildren($oldhead) $newhead
+    set desc_heads($newhead) $mainhead
+    if {[info exists anc_tags($oldhead)]} {
+       set anc_tags($newhead) $anc_tags($oldhead)
+    }
+    set desc_tags($newhead) {}
+    if {[info exists commitrow($curview,$oldhead)]} {
+       insertrow $commitrow($curview,$oldhead) $newhead
+       if {$mainhead ne {}} {
+           movedhead $newhead $mainhead
+       }
+       redrawtags $oldhead
+       redrawtags $newhead
+    }
+    notbusy cherrypick
+}
+
+# context menu for a head
+proc headmenu {x y id head} {
+    global headmenuid headmenuhead headctxmenu
+
+    set headmenuid $id
+    set headmenuhead $head
+    tk_popup $headctxmenu $x $y
+}
+
+proc cobranch {} {
+    global headmenuid headmenuhead mainhead headids
+
+    # check the tree is clean first??
+    set oldmainhead $mainhead
+    nowbusy checkout
+    update
+    if {[catch {
+       exec git checkout $headmenuhead
+    } err]} {
+       notbusy checkout
+       error_popup $err
+    } else {
+       notbusy checkout
+       set mainhead $headmenuhead
+       if {[info exists headids($oldmainhead)]} {
+           redrawtags $headids($oldmainhead)
+       }
+       redrawtags $headmenuid
+    }
+}
+
+proc rmbranch {} {
+    global desc_heads headmenuid headmenuhead mainhead
+    global headids idheads
+
+    set head $headmenuhead
+    set id $headmenuid
+    if {$head eq $mainhead} {
+       error_popup "Cannot delete the currently checked-out branch"
+       return
+    }
+    if {$desc_heads($id) 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
+    }
+    nowbusy rmbranch
+    update
+    if {[catch {exec git branch -D $head} err]} {
+       notbusy rmbranch
+       error_popup $err
+       return
+    }
+    removedhead $id $head
+    redrawtags $id
+    notbusy rmbranch
+}
+
+# Stuff for finding nearby tags
+proc getallcommits {} {
+    global allcstart allcommits allcfd allids
+
+    set allids {}
+    set fd [open [concat | git rev-list --all --topo-order --parents] r]
+    set allcfd $fd
+    fconfigure $fd -blocking 0
+    set allcommits "reading"
+    nowbusy allcommits
+    restartgetall $fd
+}
+
+proc discardallcommits {} {
+    global allparents allchildren allcommits allcfd
+    global desc_tags anc_tags alldtags tagisdesc allids desc_heads
+
+    if {![info exists allcommits]} return
+    if {$allcommits eq "reading"} {
+       catch {close $allcfd}
+    }
+    foreach v {allcommits allchildren allparents allids desc_tags anc_tags
+               alldtags tagisdesc desc_heads} {
+       catch {unset $v}
+    }
+}
+
+proc restartgetall {fd} {
+    global allcstart
+
+    fileevent $fd readable [list getallclines $fd]
+    set allcstart [clock clicks -milliseconds]
+}
+
+proc combine_dtags {l1 l2} {
+    global tagisdesc notfirstd
+
+    set res [lsort -unique [concat $l1 $l2]]
+    for {set i 0} {$i < [llength $res]} {incr i} {
+       set x [lindex $res $i]
+       for {set j [expr {$i+1}]} {$j < [llength $res]} {} {
+           set y [lindex $res $j]
+           if {[info exists tagisdesc($x,$y)]} {
+               if {$tagisdesc($x,$y) > 0} {
+                   # x is a descendent of y, exclude x
+                   set res [lreplace $res $i $i]
+                   incr i -1
+                   break
+               } else {
+                   # y is a descendent of x, exclude y
+                   set res [lreplace $res $j $j]
+               }
+           } else {
+               # no relation, keep going
+               incr j
+           }
+       }
+    }
+    return $res
+}
+
+proc combine_atags {l1 l2} {
+    global tagisdesc
+
+    set res [lsort -unique [concat $l1 $l2]]
+    for {set i 0} {$i < [llength $res]} {incr i} {
+       set x [lindex $res $i]
+       for {set j [expr {$i+1}]} {$j < [llength $res]} {} {
+           set y [lindex $res $j]
+           if {[info exists tagisdesc($x,$y)]} {
+               if {$tagisdesc($x,$y) < 0} {
+                   # x is an ancestor of y, exclude x
+                   set res [lreplace $res $i $i]
+                   incr i -1
+                   break
+               } else {
+                   # y is an ancestor of x, exclude y
+                   set res [lreplace $res $j $j]
+               }
+           } else {
+               # no relation, keep going
+               incr j
+           }
+       }
+    }
+    return $res
+}
+
+proc forward_pass {id children} {
+    global idtags desc_tags idheads desc_heads alldtags tagisdesc
+
+    set dtags {}
+    set dheads {}
+    foreach child $children {
+       if {[info exists idtags($child)]} {
+           set ctags [list $child]
+       } else {
+           set ctags $desc_tags($child)
+       }
+       if {$dtags eq {}} {
+           set dtags $ctags
+       } elseif {$ctags ne $dtags} {
+           set dtags [combine_dtags $dtags $ctags]
+       }
+       set cheads $desc_heads($child)
+       if {$dheads eq {}} {
+           set dheads $cheads
+       } elseif {$cheads ne $dheads} {
+           set dheads [lsort -unique [concat $dheads $cheads]]
+       }
+    }
+    set desc_tags($id) $dtags
     if {[info exists idtags($id)]} {
     if {[info exists idtags($id)]} {
-       set x $idtags($id)
+       set adt $dtags
+       foreach tag $dtags {
+           set adt [concat $adt $alldtags($tag)]
+       }
+       set adt [lsort -unique $adt]
+       set alldtags($id) $adt
+       foreach tag $adt {
+           set tagisdesc($id,$tag) -1
+           set tagisdesc($tag,$id) 1
+       }
     }
     }
-    set y {}
     if {[info exists idheads($id)]} {
     if {[info exists idheads($id)]} {
-       set y $idheads($id)
+       set dheads [concat $dheads $idheads($id)]
     }
     }
-    set z {}
-    if {[info exists idotherrefs($id)]} {
-       set z $idotherrefs($id)
+    set desc_heads($id) $dheads
+}
+
+proc getallclines {fd} {
+    global allparents allchildren allcommits allcstart
+    global desc_tags anc_tags idtags tagisdesc allids
+    global idheads travindex
+
+    while {[gets $fd line] >= 0} {
+       set id [lindex $line 0]
+       lappend allids $id
+       set olds [lrange $line 1 end]
+       set allparents($id) $olds
+       if {![info exists allchildren($id)]} {
+           set allchildren($id) {}
+       }
+       foreach p $olds {
+           lappend allchildren($p) $id
+       }
+       # compute nearest tagged descendents as we go
+       # also compute descendent heads
+       forward_pass $id $allchildren($id)
+       if {[clock clicks -milliseconds] - $allcstart >= 50} {
+           fileevent $fd readable {}
+           after idle restartgetall $fd
+           return
+       }
+    }
+    if {[eof $fd]} {
+       set travindex [llength $allids]
+       set allcommits "traversing"
+       after idle restartatags
+       if {[catch {close $fd} err]} {
+           error_popup "Error reading full commit graph: $err.\n\
+                        Results may be incomplete."
+       }
+    }
+}
+
+# walk backward through the tree and compute nearest tagged ancestors
+proc restartatags {} {
+    global allids allparents idtags anc_tags travindex
+
+    set t0 [clock clicks -milliseconds]
+    set i $travindex
+    while {[incr i -1] >= 0} {
+       set id [lindex $allids $i]
+       set atags {}
+       foreach p $allparents($id) {
+           if {[info exists idtags($p)]} {
+               set ptags [list $p]
+           } else {
+               set ptags $anc_tags($p)
+           }
+           if {$atags eq {}} {
+               set atags $ptags
+           } elseif {$ptags ne $atags} {
+               set atags [combine_atags $atags $ptags]
+           }
+       }
+       set anc_tags($id) $atags
+       if {[clock clicks -milliseconds] - $t0 >= 50} {
+           set travindex $i
+           after idle restartatags
+           return
+       }
+    }
+    set allcommits "done"
+    set travindex 0
+    notbusy allcommits
+    dispneartags
+}
+
+# update the desc_tags and anc_tags arrays for a new tag just added
+proc addedtag {id} {
+    global desc_tags anc_tags allparents allchildren allcommits
+    global idtags tagisdesc alldtags
+
+    if {![info exists desc_tags($id)]} return
+    set adt $desc_tags($id)
+    foreach t $desc_tags($id) {
+       set adt [concat $adt $alldtags($t)]
+    }
+    set adt [lsort -unique $adt]
+    set alldtags($id) $adt
+    foreach t $adt {
+       set tagisdesc($id,$t) -1
+       set tagisdesc($t,$id) 1
+    }
+    if {[info exists anc_tags($id)]} {
+       set todo $anc_tags($id)
+       while {$todo ne {}} {
+           set do [lindex $todo 0]
+           set todo [lrange $todo 1 end]
+           if {[info exists tagisdesc($id,$do)]} continue
+           set tagisdesc($do,$id) -1
+           set tagisdesc($id,$do) 1
+           if {[info exists anc_tags($do)]} {
+               set todo [concat $todo $anc_tags($do)]
+           }
+       }
+    }
+
+    set lastold $desc_tags($id)
+    set lastnew [list $id]
+    set nup 0
+    set nch 0
+    set todo $allparents($id)
+    while {$todo ne {}} {
+       set do [lindex $todo 0]
+       set todo [lrange $todo 1 end]
+       if {![info exists desc_tags($do)]} continue
+       if {$desc_tags($do) ne $lastold} {
+           set lastold $desc_tags($do)
+           set lastnew [combine_dtags $lastold [list $id]]
+           incr nch
+       }
+       if {$lastold eq $lastnew} continue
+       set desc_tags($do) $lastnew
+       incr nup
+       if {![info exists idtags($do)]} {
+           set todo [concat $todo $allparents($do)]
+       }
+    }
+
+    if {![info exists anc_tags($id)]} return
+    set lastold $anc_tags($id)
+    set lastnew [list $id]
+    set nup 0
+    set nch 0
+    set todo $allchildren($id)
+    while {$todo ne {}} {
+       set do [lindex $todo 0]
+       set todo [lrange $todo 1 end]
+       if {![info exists anc_tags($do)]} continue
+       if {$anc_tags($do) ne $lastold} {
+           set lastold $anc_tags($do)
+           set lastnew [combine_atags $lastold [list $id]]
+           incr nch
+       }
+       if {$lastold eq $lastnew} continue
+       set anc_tags($do) $lastnew
+       incr nup
+       if {![info exists idtags($do)]} {
+           set todo [concat $todo $allchildren($do)]
+       }
+    }
+}
+
+# update the desc_heads array for a new head just added
+proc addedhead {hid head} {
+    global desc_heads allparents headids idheads
+
+    set headids($head) $hid
+    lappend idheads($hid) $head
+
+    set todo [list $hid]
+    while {$todo ne {}} {
+       set do [lindex $todo 0]
+       set todo [lrange $todo 1 end]
+       if {![info exists desc_heads($do)] ||
+           [lsearch -exact $desc_heads($do) $head] >= 0} continue
+       set oldheads $desc_heads($do)
+       lappend desc_heads($do) $head
+       set heads $desc_heads($do)
+       while {1} {
+           set p $allparents($do)
+           if {[llength $p] != 1 || ![info exists desc_heads($p)] ||
+               $desc_heads($p) ne $oldheads} break
+           set do $p
+           set desc_heads($do) $heads
+       }
+       set todo [concat $todo $p]
+    }
+}
+
+# update the desc_heads array for a head just removed
+proc removedhead {hid head} {
+    global desc_heads allparents headids idheads
+
+    unset headids($head)
+    if {$idheads($hid) eq $head} {
+       unset idheads($hid)
+    } else {
+       set i [lsearch -exact $idheads($hid) $head]
+       if {$i >= 0} {
+           set idheads($hid) [lreplace $idheads($hid) $i $i]
+       }
+    }
+
+    set todo [list $hid]
+    while {$todo ne {}} {
+       set do [lindex $todo 0]
+       set todo [lrange $todo 1 end]
+       if {![info exists desc_heads($do)]} continue
+       set i [lsearch -exact $desc_heads($do) $head]
+       if {$i < 0} continue
+       set oldheads $desc_heads($do)
+       set heads [lreplace $desc_heads($do) $i $i]
+       while {1} {
+           set desc_heads($do) $heads
+           set p $allparents($do)
+           if {[llength $p] != 1 || ![info exists desc_heads($p)] ||
+               $desc_heads($p) ne $oldheads} break
+           set do $p
+       }
+       set todo [concat $todo $p]
+    }
+}
+
+# update things for a head moved to a child of its previous location
+proc movedhead {id name} {
+    global headids idheads
+
+    set oldid $headids($name)
+    set headids($name) $id
+    if {$idheads($oldid) eq $name} {
+       unset idheads($oldid)
+    } else {
+       set i [lsearch -exact $idheads($oldid) $name]
+       if {$i >= 0} {
+           set idheads($oldid) [lreplace $idheads($oldid) $i $i]
+       }
+    }
+    lappend idheads($id) $name
+}
+
+proc changedrefs {} {
+    global desc_heads desc_tags anc_tags allcommits allids
+    global allchildren allparents idtags travindex
+
+    if {![info exists allcommits]} return
+    catch {unset desc_heads}
+    catch {unset desc_tags}
+    catch {unset anc_tags}
+    catch {unset alldtags}
+    catch {unset tagisdesc}
+    foreach id $allids {
+       forward_pass $id $allchildren($id)
+    }
+    if {$allcommits ne "reading"} {
+       set travindex [llength $allids]
+       if {$allcommits ne "traversing"} {
+           set allcommits "traversing"
+           after idle restartatags
+       }
     }
     }
-    return [list $x $y $z]
 }
 
 proc rereadrefs {} {
 }
 
 proc rereadrefs {} {
-    global idtags idheads idotherrefs
+    global idtags idheads idotherrefs mainhead
 
     set refids [concat [array names idtags] \
                    [array names idheads] [array names idotherrefs]]
 
     set refids [concat [array names idtags] \
                    [array names idheads] [array names idotherrefs]]
@@ -4484,17 +5721,39 @@ proc rereadrefs {} {
            set ref($id) [listrefs $id]
        }
     }
            set ref($id) [listrefs $id]
        }
     }
+    set oldmainhead $mainhead
     readrefs
     readrefs
+    changedrefs
     set refids [lsort -unique [concat $refids [array names idtags] \
                        [array names idheads] [array names idotherrefs]]]
     foreach id $refids {
        set v [listrefs $id]
     set refids [lsort -unique [concat $refids [array names idtags] \
                        [array names idheads] [array names idotherrefs]]]
     foreach id $refids {
        set v [listrefs $id]
-       if {![info exists ref($id)] || $ref($id) != $v} {
+       if {![info exists ref($id)] || $ref($id) != $v ||
+           ($id eq $oldmainhead && $id ne $mainhead) ||
+           ($id eq $mainhead && $id ne $oldmainhead)} {
            redrawtags $id
        }
     }
 }
 
            redrawtags $id
        }
     }
 }
 
+proc listrefs {id} {
+    global idtags idheads idotherrefs
+
+    set x {}
+    if {[info exists idtags($id)]} {
+       set x $idtags($id)
+    }
+    set y {}
+    if {[info exists idheads($id)]} {
+       set y $idheads($id)
+    }
+    set z {}
+    if {[info exists idotherrefs($id)]} {
+       set z $idotherrefs($id)
+    }
+    return [list $x $y $z]
+}
+
 proc showtag {tag isnew} {
     global ctext tagcontents tagids linknum
 
 proc showtag {tag isnew} {
     global ctext tagcontents tagids linknum
 
@@ -4502,7 +5761,7 @@ proc showtag {tag isnew} {
        addtohistory [list showtag $tag 0]
     }
     $ctext conf -state normal
        addtohistory [list showtag $tag 0]
     }
     $ctext conf -state normal
-    $ctext delete 0.0 end
+    clear_ctext
     set linknum 0
     if {[info exists tagcontents($tag)]} {
        set text $tagcontents($tag)
     set linknum 0
     if {[info exists tagcontents($tag)]} {
        set text $tagcontents($tag)
@@ -4521,8 +5780,9 @@ proc doquit {} {
 }
 
 proc doprefs {} {
 }
 
 proc doprefs {} {
-    global maxwidth maxgraphpct diffopts findmergefiles
-    global oldprefs prefstop
+    global maxwidth maxgraphpct diffopts
+    global oldprefs prefstop showneartags
+    global bgcolor fgcolor ctext diffcolors
 
     set top .gitkprefs
     set prefstop $top
 
     set top .gitkprefs
     set prefstop $top
@@ -4530,7 +5790,7 @@ proc doprefs {} {
        raise $top
        return
     }
        raise $top
        return
     }
-    foreach v {maxwidth maxgraphpct diffopts findmergefiles} {
+    foreach v {maxwidth maxgraphpct diffopts showneartags} {
        set oldprefs($v) [set $v]
     }
     toplevel $top
        set oldprefs($v) [set $v]
     }
     toplevel $top
@@ -4546,16 +5806,46 @@ proc doprefs {} {
        -font optionfont
     spinbox $top.maxpct -from 1 -to 100 -width 4 -textvariable maxgraphpct
     grid x $top.maxpctl $top.maxpct -sticky w
        -font optionfont
     spinbox $top.maxpct -from 1 -to 100 -width 4 -textvariable maxgraphpct
     grid x $top.maxpctl $top.maxpct -sticky w
-    checkbutton $top.findm -variable findmergefiles
-    label $top.findml -text "Include merges for \"Find\" in \"Files\"" \
-       -font optionfont
-    grid $top.findm $top.findml - -sticky w
+
     label $top.ddisp -text "Diff display options"
     grid $top.ddisp - -sticky w -pady 10
     label $top.diffoptl -text "Options for diff program" \
        -font optionfont
     entry $top.diffopt -width 20 -textvariable diffopts
     grid x $top.diffoptl $top.diffopt -sticky w
     label $top.ddisp -text "Diff display options"
     grid $top.ddisp - -sticky w -pady 10
     label $top.diffoptl -text "Options for diff program" \
        -font optionfont
     entry $top.diffopt -width 20 -textvariable diffopts
     grid x $top.diffoptl $top.diffopt -sticky w
+    frame $top.ntag
+    label $top.ntag.l -text "Display nearby tags" -font optionfont
+    checkbutton $top.ntag.b -variable showneartags
+    pack $top.ntag.b $top.ntag.l -side left
+    grid x $top.ntag -sticky w
+
+    label $top.cdisp -text "Colors: press to choose"
+    grid $top.cdisp - -sticky w -pady 10
+    label $top.bg -padx 40 -relief sunk -background $bgcolor
+    button $top.bgbut -text "Background" -font optionfont \
+       -command [list choosecolor bgcolor 0 $top.bg background setbg]
+    grid x $top.bgbut $top.bg -sticky w
+    label $top.fg -padx 40 -relief sunk -background $fgcolor
+    button $top.fgbut -text "Foreground" -font optionfont \
+       -command [list choosecolor fgcolor 0 $top.fg foreground setfg]
+    grid x $top.fgbut $top.fg -sticky w
+    label $top.diffold -padx 40 -relief sunk -background [lindex $diffcolors 0]
+    button $top.diffoldbut -text "Diff: old lines" -font optionfont \
+       -command [list choosecolor diffcolors 0 $top.diffold "diff old lines" \
+                     [list $ctext tag conf d0 -foreground]]
+    grid x $top.diffoldbut $top.diffold -sticky w
+    label $top.diffnew -padx 40 -relief sunk -background [lindex $diffcolors 1]
+    button $top.diffnewbut -text "Diff: new lines" -font optionfont \
+       -command [list choosecolor diffcolors 1 $top.diffnew "diff new lines" \
+                     [list $ctext tag conf d1 -foreground]]
+    grid x $top.diffnewbut $top.diffnew -sticky w
+    label $top.hunksep -padx 40 -relief sunk -background [lindex $diffcolors 2]
+    button $top.hunksepbut -text "Diff: hunk header" -font optionfont \
+       -command [list choosecolor diffcolors 2 $top.hunksep \
+                     "diff hunk header" \
+                     [list $ctext tag conf hunksep -foreground]]
+    grid x $top.hunksepbut $top.hunksep -sticky w
+
     frame $top.buts
     button $top.buts.ok -text "OK" -command prefsok
     button $top.buts.can -text "Cancel" -command prefscan
     frame $top.buts
     button $top.buts.ok -text "OK" -command prefsok
     button $top.buts.can -text "Cancel" -command prefscan
@@ -4565,11 +5855,40 @@ proc doprefs {} {
     grid $top.buts - - -pady 10 -sticky ew
 }
 
     grid $top.buts - - -pady 10 -sticky ew
 }
 
+proc choosecolor {v vi w x cmd} {
+    global $v
+
+    set c [tk_chooseColor -initialcolor [lindex [set $v] $vi] \
+              -title "Gitk: choose color for $x"]
+    if {$c eq {}} return
+    $w conf -background $c
+    lset $v $vi $c
+    eval $cmd $c
+}
+
+proc setbg {c} {
+    global bglist
+
+    foreach w $bglist {
+       $w conf -background $c
+    }
+}
+
+proc setfg {c} {
+    global fglist canv
+
+    foreach w $fglist {
+       $w conf -foreground $c
+    }
+    allcanvs itemconf text -fill $c
+    $canv itemconf circle -outline $c
+}
+
 proc prefscan {} {
 proc prefscan {} {
-    global maxwidth maxgraphpct diffopts findmergefiles
-    global oldprefs prefstop
+    global maxwidth maxgraphpct diffopts
+    global oldprefs prefstop showneartags
 
 
-    foreach v {maxwidth maxgraphpct diffopts findmergefiles} {
+    foreach v {maxwidth maxgraphpct diffopts showneartags} {
        set $v $oldprefs($v)
     }
     catch {destroy $prefstop}
        set $v $oldprefs($v)
     }
     catch {destroy $prefstop}
@@ -4578,13 +5897,15 @@ proc prefscan {} {
 
 proc prefsok {} {
     global maxwidth maxgraphpct
 
 proc prefsok {} {
     global maxwidth maxgraphpct
-    global oldprefs prefstop
+    global oldprefs prefstop showneartags
 
     catch {destroy $prefstop}
     unset prefstop
     if {$maxwidth != $oldprefs(maxwidth)
        || $maxgraphpct != $oldprefs(maxgraphpct)} {
        redisplay
 
     catch {destroy $prefstop}
     unset prefstop
     if {$maxwidth != $oldprefs(maxwidth)
        || $maxgraphpct != $oldprefs(maxgraphpct)} {
        redisplay
+    } elseif {$showneartags != $oldprefs(showneartags)} {
+       reselectline
     }
 }
 
     }
 }
 
@@ -4893,11 +6214,14 @@ set fastdate 0
 set uparrowlen 7
 set downarrowlen 7
 set mingaplen 30
 set uparrowlen 7
 set downarrowlen 7
 set mingaplen 30
-set flistmode "flat"
 set cmitmode "patch"
 set wrapcomment "none"
 set cmitmode "patch"
 set wrapcomment "none"
+set showneartags 1
 
 set colors {green red blue magenta darkgrey brown orange}
 
 set colors {green red blue magenta darkgrey brown orange}
+set bgcolor white
+set fgcolor black
+set diffcolors {red "#00a000" blue}
 
 catch {source ~/.gitk}
 
 
 catch {source ~/.gitk}
 
@@ -4946,13 +6270,19 @@ if {$i >= 0} {
 
 set history {}
 set historyindex 0
 
 set history {}
 set historyindex 0
+set fh_serial 0
+set nhl_names {}
+set highlight_paths {}
+set searchdirn -forwards
+set boldrows {}
+set boldnamerows {}
 
 set optim_delay 16
 
 set nextviewnum 1
 set curview 0
 set selectedview 0
 
 set optim_delay 16
 
 set nextviewnum 1
 set curview 0
 set selectedview 0
-set selectedhlview {}
+set selectedhlview None
 set viewfiles(0) {}
 set viewperm(0) 0
 set viewargs(0) {}
 set viewfiles(0) {}
 set viewperm(0) 0
 set viewargs(0) {}
@@ -4975,8 +6305,8 @@ if {$cmdline_files ne {} || $revtreeargs ne {}} {
     set viewargs(1) $revtreeargs
     set viewperm(1) 0
     addviewmenu 1
     set viewargs(1) $revtreeargs
     set viewperm(1) 0
     addviewmenu 1
-    .bar.view entryconf 2 -state normal
-    .bar.view entryconf 3 -state normal
+    .bar.view entryconf Edit* -state normal
+    .bar.view entryconf Delete* -state normal
 }
 
 if {[info exists permviews]} {
 }
 
 if {[info exists permviews]} {