Misc doc improvements
[git.git] / gitk
diff --git a/gitk b/gitk
index 5201332..9be10a4 100755 (executable)
--- a/gitk
+++ b/gitk
@@ -34,10 +34,10 @@ proc start_rev_list {view} {
        set order "--date-order"
     }
     if {[catch {
        set order "--date-order"
     }
     if {[catch {
-       set fd [open [concat | git-rev-list --header $order \
+       set fd [open [concat | git rev-list --header $order \
                          --parents --boundary --default HEAD $args] r]
     } err]} {
                          --parents --boundary --default HEAD $args] r]
     } err]} {
-       puts stderr "Error executing git-rev-list: $err"
+       puts stderr "Error executing git rev-list: $err"
        exit 1
     }
     set commfd($view) $fd
        exit 1
     }
     set commfd($view) $fd
@@ -94,10 +94,10 @@ proc getcommitlines {fd view}  {
            }
            if {[string range $err 0 4] == "usage"} {
                set err "Gitk: error reading commits$fv:\
            }
            if {[string range $err 0 4] == "usage"} {
                set err "Gitk: error reading commits$fv:\
-                       bad arguments to git-rev-list."
+                       bad arguments to git rev-list."
                if {$viewname($view) eq "Command line"} {
                    append err \
                if {$viewname($view) eq "Command line"} {
                    append err \
-                       "  (Note: arguments to gitk are passed to git-rev-list\
+                       "  (Note: arguments to gitk are passed to git rev-list\
                         to allow selection of commits to be displayed.)"
                }
            } else {
                         to allow selection of commits to be displayed.)"
                }
            } else {
@@ -148,7 +148,7 @@ proc getcommitlines {fd view}  {
            if {[string length $shortcmit] > 80} {
                set shortcmit "[string range $shortcmit 0 80]..."
            }
            if {[string length $shortcmit] > 80} {
                set shortcmit "[string range $shortcmit 0 80]..."
            }
-           error_popup "Can't parse git-rev-list output: {$shortcmit}"
+           error_popup "Can't parse git rev-list output: {$shortcmit}"
            exit 1
        }
        set id [lindex $ids 0]
            exit 1
        }
        set id [lindex $ids 0]
@@ -217,7 +217,7 @@ proc doupdate {} {
 }
 
 proc readcommit {id} {
 }
 
 proc readcommit {id} {
-    if {[catch {set contents [exec git-cat-file commit $id]}]} return
+    if {[catch {set contents [exec git cat-file commit $id]}]} return
     parsecommit $id $contents 0
 }
 
     parsecommit $id $contents 0
 }
 
@@ -238,6 +238,7 @@ proc updatecommits {} {
     catch {unset selectedline}
     catch {unset thickerline}
     catch {unset viewdata($n)}
     catch {unset selectedline}
     catch {unset thickerline}
     catch {unset viewdata($n)}
+    discardallcommits
     readrefs
     showview $n
 }
     readrefs
     showview $n
 }
@@ -278,8 +279,8 @@ proc parsecommit {id contents listed} {
        set headline $comment
     }
     if {!$listed} {
        set headline $comment
     }
     if {!$listed} {
-       # git-rev-list indents the comment by 4 spaces;
-       # if we got this via git-cat-file, add the indentation
+       # git rev-list indents the comment by 4 spaces;
+       # if we got this via git cat-file, add the indentation
        set newcomment {}
        foreach line [split $comment "\n"] {
            append newcomment "    "
        set newcomment {}
        foreach line [split $comment "\n"] {
            append newcomment "    "
@@ -339,14 +340,14 @@ proc readrefs {} {
            set type {}
            set tag {}
            catch {
            set type {}
            set tag {}
            catch {
-               set commit [exec git-rev-parse "$id^0"]
+               set commit [exec git rev-parse "$id^0"]
                if {"$commit" != "$id"} {
                    set tagids($name) $commit
                    lappend idtags($commit) $name
                }
            }           
            catch {
                if {"$commit" != "$id"} {
                    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
@@ -359,21 +360,21 @@ proc readrefs {} {
     close $refd
 }
 
     close $refd
 }
 
-proc show_error {w msg} {
+proc show_error {w top msg} {
     message $w.m -text $msg -justify center -aspect 400
     pack $w.m -side top -fill x -padx 20 -pady 20
     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 "destroy $w"
+    button $w.ok -text OK -command "destroy $top"
     pack $w.ok -side bottom -fill x
     pack $w.ok -side bottom -fill x
-    bind $w <Visibility> "grab $w; focus $w"
-    bind $w <Key-Return> "destroy $w"
-    tkwait window $w
+    bind $top <Visibility> "grab $top; focus $top"
+    bind $top <Key-Return> "destroy $top"
+    tkwait window $top
 }
 
 proc error_popup msg {
     set w .error
     toplevel $w
     wm transient $w .
 }
 
 proc error_popup msg {
     set w .error
     toplevel $w
     wm transient $w .
-    show_error $w $msg
+    show_error $w $w $msg
 }
 
 proc makewindow {} {
 }
 
 proc makewindow {} {
@@ -382,8 +383,9 @@ proc makewindow {} {
     global findtype findtypemenu findloc findstring fstring geometry
     global entries sha1entry sha1string sha1but
     global maincursor textcursor curtextcursor
     global findtype findtypemenu findloc findstring fstring geometry
     global entries sha1entry sha1string sha1but
     global maincursor textcursor curtextcursor
-    global rowctxmenu mergemax
-    global highlight_files highlight_names
+    global rowctxmenu mergemax wrapcomment
+    global highlight_files gdttype
+    global searchstring sstring
 
     menu .bar
     .bar add cascade -label "File" -menu .bar.file
 
     menu .bar
     .bar add cascade -label "File" -menu .bar.file
@@ -497,26 +499,33 @@ proc makewindow {} {
     set fstring .ctop.top.bar.findstring
     lappend entries $fstring
     entry $fstring -width 30 -font $textfont -textvariable findstring
     set fstring .ctop.top.bar.findstring
     lappend entries $fstring
     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 touching paths:" \
+    label .ctop.top.lbar.flabel -text "Highlight:  Commits " \
        -font $uifont
     pack .ctop.top.lbar.flabel -side left -fill y
        -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
     entry .ctop.top.lbar.fent -width 25 -font $textfont \
        -textvariable highlight_files
     trace add variable highlight_files write hfiles_change
@@ -530,27 +539,39 @@ proc makewindow {} {
     $viewhlmenu conf -font $uifont
     .ctop.top.lbar.vhl conf -font $uifont
     pack .ctop.top.lbar.vhl -side left -fill y
     $viewhlmenu conf -font $uifont
     .ctop.top.lbar.vhl conf -font $uifont
     pack .ctop.top.lbar.vhl -side left -fill y
-    label .ctop.top.lbar.alabel -text " OR author/committer:" \
-       -font $uifont
-    pack .ctop.top.lbar.alabel -side left -fill y
-    entry .ctop.top.lbar.aent -width 20 -font $textfont \
-       -textvariable highlight_names
-    trace add variable highlight_names write hnames_change
-    lappend entries .ctop.top.lbar.aent
-    pack .ctop.top.lbar.aent -side right -fill x -expand 1
+    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
     text $ctext -bg white -state disabled -font $textfont \
        -width $geometry(ctextw) -height $geometry(ctexth) \
     set ctext .ctop.cdet.left.ctext
     text $ctext -bg white -state disabled -font $textfont \
        -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
 
+    $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 filesep -font [concat $textfont bold] -back "#aaaaaa"
     $ctext tag conf hunksep -fore blue
     $ctext tag conf d0 -fore red
@@ -613,6 +634,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"
@@ -644,7 +667,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}
@@ -720,9 +744,9 @@ 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 viewname viewfiles viewargs viewperm nextviewnum
-    global cmitmode
+    global cmitmode wrapcomment
 
     if {$stuffsaved} return
     if {![winfo viewable .]} return
 
     if {$stuffsaved} return
     if {![winfo viewable .]} return
@@ -735,6 +759,8 @@ proc savestuff {w} {
        puts $f [list set maxgraphpct $maxgraphpct]
        puts $f [list set maxwidth $maxwidth]
        puts $f [list set cmitmode $cmitmode]
        puts $f [list set maxgraphpct $maxgraphpct]
        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 "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}]"
@@ -872,6 +898,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
@@ -879,11 +907,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
@@ -950,7 +979,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
            }
@@ -962,7 +991,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
        }
@@ -999,6 +1028,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
 
@@ -1032,8 +1077,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
@@ -1041,13 +1086,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
@@ -1155,11 +1200,10 @@ proc highlight_tag {f} {
 }
 
 proc highlight_filelist {} {
 }
 
 proc highlight_filelist {} {
-    global flistmode cflist
+    global cmitmode cflist
 
 
-    global highlight_paths
-    if {$flistmode eq "flat"} {
-       $cflist conf -state normal
+    $cflist conf -state normal
+    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"]
        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"]
@@ -1167,35 +1211,33 @@ proc highlight_filelist {} {
                $cflist tag add bold $l.0 "$l.0 lineend"
            }
        }
                $cflist tag add bold $l.0 "$l.0 lineend"
            }
        }
-       $cflist conf -state disabled
+    } else {
+       highlight_tree 2 {}
     }
     }
+    $cflist conf -state disabled
 }
 
 proc unhighlight_filelist {} {
 }
 
 proc unhighlight_filelist {} {
-    global flistmode cflist
+    global cflist
 
 
-    if {$flistmode eq "flat"} {
-       $cflist conf -state normal
-       $cflist tag remove bold 1.0 end
-       $cflist conf -state disabled
-    }
+    $cflist conf -state normal
+    $cflist tag remove bold 1.0 end
+    $cflist conf -state disabled
 }
 
 proc add_flist {fl} {
 }
 
 proc add_flist {fl} {
-    global flistmode cflist
+    global cflist
 
 
-    if {$flistmode eq "flat"} {
-       $cflist conf -state normal
-       foreach f $fl {
-           $cflist insert end "\n"
-           $cflist insert end $f [highlight_tag $f]
-       }
-       $cflist conf -state disabled
+    $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} {
 }
 
 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
@@ -1350,7 +1392,7 @@ proc vieweditor {top n title} {
     checkbutton $top.perm -text "Remember this view" -variable newviewperm($n)
     grid $top.perm - -pady 5 -sticky w
     message $top.al -aspect 1000 -font $uifont \
     checkbutton $top.perm -text "Remember this view" -variable newviewperm($n)
     grid $top.perm - -pady 5 -sticky w
     message $top.al -aspect 1000 -font $uifont \
-       -text "Commits to include (arguments to git-rev-list):"
+       -text "Commits to include (arguments to git rev-list):"
     grid $top.al - -sticky w -pady 5
     entry $top.args -width 50 -textvariable newviewargs($n) \
        -background white
     grid $top.al - -sticky w -pady 5
     entry $top.args -width 50 -textvariable newviewargs($n) \
        -background white
@@ -1618,7 +1660,7 @@ proc showview {n} {
 # Stuff relating to the highlighting facility
 
 proc ishighlighted {row} {
 # Stuff relating to the highlighting facility
 
 proc ishighlighted {row} {
-    global vhighlights fhighlights nhighlights
+    global vhighlights fhighlights nhighlights rhighlights
 
     if {[info exists nhighlights($row)] && $nhighlights($row) > 0} {
        return $nhighlights($row)
 
     if {[info exists nhighlights($row)] && $nhighlights($row) > 0} {
        return $nhighlights($row)
@@ -1629,14 +1671,18 @@ proc ishighlighted {row} {
     if {[info exists fhighlights($row)] && $fhighlights($row) > 0} {
        return $fhighlights($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} {
     return 0
 }
 
 proc bolden {row font} {
-    global canv linehtag selectedline
+    global canv linehtag selectedline boldrows
 
 
+    lappend boldrows $row
     $canv itemconf $linehtag($row) -font $font
     $canv itemconf $linehtag($row) -font $font
-    if {$row == $selectedline} {
+    if {[info exists selectedline] && $row == $selectedline} {
        $canv delete secsel
        set t [eval $canv create rect [$canv bbox $linehtag($row)] \
                   -outline {{}} -tags secsel \
        $canv delete secsel
        set t [eval $canv create rect [$canv bbox $linehtag($row)] \
                   -outline {{}} -tags secsel \
@@ -1646,10 +1692,11 @@ proc bolden {row font} {
 }
 
 proc bolden_name {row font} {
 }
 
 proc bolden_name {row font} {
-    global canv2 linentag selectedline
+    global canv2 linentag selectedline boldnamerows
 
 
+    lappend boldnamerows $row
     $canv2 itemconf $linentag($row) -font $font
     $canv2 itemconf $linentag($row) -font $font
-    if {$row == $selectedline} {
+    if {[info exists selectedline] && $row == $selectedline} {
        $canv2 delete secsel
        set t [eval $canv2 create rect [$canv2 bbox $linentag($row)] \
                   -outline {{}} -tags secsel \
        $canv2 delete secsel
        set t [eval $canv2 create rect [$canv2 bbox $linentag($row)] \
                   -outline {{}} -tags secsel \
@@ -1658,14 +1705,18 @@ proc bolden_name {row font} {
     }
 }
 
     }
 }
 
-proc unbolden {rows} {
-    global mainfont
+proc unbolden {} {
+    global mainfont boldrows
 
 
-    foreach row $rows {
+    set stillbold {}
+    foreach row $boldrows {
        if {![ishighlighted $row]} {
            bolden $row $mainfont
        if {![ishighlighted $row]} {
            bolden $row $mainfont
+       } else {
+           lappend stillbold $row
        }
     }
        }
     }
+    set boldrows $stillbold
 }
 
 proc addvhighlight {n} {
 }
 
 proc addvhighlight {n} {
@@ -1691,15 +1742,11 @@ proc addvhighlight {n} {
 
 proc delvhighlight {} {
     global hlview vhighlights
 
 proc delvhighlight {} {
     global hlview vhighlights
-    global selectedline
 
     if {![info exists hlview]} return
     unset hlview
 
     if {![info exists hlview]} return
     unset hlview
-    set rows [array names vhighlights]
-    if {$rows ne {}} {
-       unset vhighlights
-       unbolden $rows
-    }
+    catch {unset vhighlights}
+    unbolden
 }
 
 proc vhighlightmore {} {
 }
 
 proc vhighlightmore {} {
@@ -1752,11 +1799,8 @@ proc hfiles_change {name ix op} {
        # delete previous highlights
        catch {close $filehighlight}
        unset filehighlight
        # delete previous highlights
        catch {close $filehighlight}
        unset filehighlight
-       set rows [array names fhighlights]
-       if {$rows ne {}} {
-           unset fhighlights
-           unbolden $rows
-       }
+       catch {unset fhighlights}
+       unbolden
        unhighlight_filelist
     }
     set highlight_paths {}
        unhighlight_filelist
     }
     set highlight_paths {}
@@ -1782,96 +1826,121 @@ proc makepatterns {l} {
 }
 
 proc do_file_hl {serial} {
 }
 
 proc do_file_hl {serial} {
-    global highlight_files filehighlight highlight_paths
+    global highlight_files filehighlight highlight_paths gdttype fhl_list
 
 
-    if {[catch {set paths [shellsplit $highlight_files]}]} return
-    set highlight_paths [makepatterns $paths]
-    highlight_filelist
-    set cmd [concat | git-diff-tree -r -s --stdin -- $paths]
+    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 filehighlight [open $cmd r+]
     fconfigure $filehighlight -blocking 0
     fileevent $filehighlight readable readfhighlight
+    set fhl_list {}
     drawvisible
     flushhighlights
 }
 
 proc flushhighlights {} {
     drawvisible
     flushhighlights
 }
 
 proc flushhighlights {} {
-    global filehighlight
+    global filehighlight fhl_list
 
     if {[info exists filehighlight]} {
 
     if {[info exists filehighlight]} {
+       lappend fhl_list {}
        puts $filehighlight ""
        flush $filehighlight
     }
 }
 
 proc askfilehighlight {row id} {
        puts $filehighlight ""
        flush $filehighlight
     }
 }
 
 proc askfilehighlight {row id} {
-    global filehighlight fhighlights
+    global filehighlight fhighlights fhl_list
 
 
-    set fhighlights($row) 0
+    lappend fhl_list $id
+    set fhighlights($row) -1
     puts $filehighlight $id
 }
 
 proc readfhighlight {} {
     global filehighlight fhighlights commitrow curview mainfont iddrawn
     puts $filehighlight $id
 }
 
 proc readfhighlight {} {
     global filehighlight fhighlights commitrow curview mainfont iddrawn
-
-    set n [gets $filehighlight line]
-    if {$n < 0} {
-       if {[eof $filehighlight]} {
-           # strange...
-           puts "oops, git-diff-tree died"
-           catch {close $filehighlight}
-           unset filehighlight
+    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
+           }
        }
        }
-       return
+       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
     }
     }
-    set line [string trim $line]
-    if {$line eq {}} return
-    if {![info exists commitrow($curview,$line)]} return
-    set row $commitrow($curview,$line)
-    if {[info exists iddrawn($line)] && ![ishighlighted $row]} {
-       bolden $row [concat $mainfont bold]
+    if {[eof $filehighlight]} {
+       # strange...
+       puts "oops, git-diff-tree died"
+       catch {close $filehighlight}
+       unset filehighlight
     }
     }
-    set fhighlights($row) 1
+    next_hlcont
 }
 
 }
 
-proc hnames_change {name ix op} {
-    global highlight_names nhighlights nhl_names mainfont
+proc find_change {name ix op} {
+    global nhighlights mainfont boldnamerows
+    global findstring findpattern findtype
 
     # delete previous highlights, if any
 
     # delete previous highlights, if any
-    set rows [array names nhighlights]
-    if {$rows ne {}} {
-       foreach row $rows {
-           if {$nhighlights($row) >= 2} {
-               bolden_name $row $mainfont
-           }
-       }
-       unset nhighlights
-       unbolden $rows
+    foreach row $boldnamerows {
+       bolden_name $row $mainfont
     }
     }
-    if {[catch {set nhl_names [shellsplit $highlight_names]}]} {
-       set nhl_names {}
-       return
+    set boldnamerows {}
+    catch {unset nhighlights}
+    unbolden
+    if {$findtype ne "Regexp"} {
+       set e [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} \
+                  $findstring]
+       set findpattern "*$e*"
     }
     drawvisible
 }
 
     }
     drawvisible
 }
 
-proc asknamehighlight {row id} {
-    global nhl_names nhighlights commitinfo iddrawn mainfont
+proc askfindhighlight {row id} {
+    global nhighlights commitinfo iddrawn mainfont
+    global findstring findtype findloc findpattern
 
     if {![info exists commitinfo($id)]} {
        getcommit $id
     }
 
     if {![info exists commitinfo($id)]} {
        getcommit $id
     }
+    set info $commitinfo($id)
     set isbold 0
     set isbold 0
-    set author [lindex $commitinfo($id) 1]
-    set committer [lindex $commitinfo($id) 3]
-    foreach name $nhl_names {
-       set pattern "*$name*"
-       if {[string match -nocase $pattern $author]} {
-           set isbold 2
-           break
+    set fldtypes {Headline Author Date Committer CDate Comments}
+    foreach f $info ty $fldtypes {
+       if {$findloc ne "All fields" && $findloc ne $ty} {
+           continue
        }
        }
-       if {!$isbold && [string match -nocase $pattern $committer]} {
-           set isbold 1
+       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 {[info exists iddrawn($id)]} {
@@ -1885,6 +1954,207 @@ proc asknamehighlight {row id} {
     set nhighlights($row) $isbold
 }
 
     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 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
 
 proc shortids {ids} {
 # Graph layout functions
 
 proc shortids {ids} {
@@ -2604,7 +2874,7 @@ proc drawcmittext {id row col rmx} {
     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 mainfont canvxmax boldrows boldnamerows
 
     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]
@@ -2633,8 +2903,10 @@ proc drawcmittext {id row col rmx} {
     set nfont $mainfont
     set isbold [ishighlighted $row]
     if {$isbold > 0} {
     set nfont $mainfont
     set isbold [ishighlighted $row]
     if {$isbold > 0} {
+       lappend boldrows $row
        lappend font bold
        if {$isbold > 1} {
        lappend font bold
        if {$isbold > 1} {
+           lappend boldnamerows $row
            lappend nfont bold
        }
     }
            lappend nfont bold
        }
     }
@@ -2656,8 +2928,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 nhl_names nhighlights
+    global filehighlight fhighlights findstring nhighlights
     global hlview vhighlights
     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] {
@@ -2684,8 +2957,11 @@ proc drawcmitrow {row} {
     if {[info exists filehighlight] && ![info exists fhighlights($row)]} {
        askfilehighlight $row $id
     }
     if {[info exists filehighlight] && ![info exists fhighlights($row)]} {
        askfilehighlight $row $id
     }
-    if {$nhl_names ne {} && ![info exists nhighlights($row)]} {
-       asknamehighlight $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 {[info exists iddrawn($id)]} return
     set col [lsearch -exact [lindex $rowidlist $row] $id]
@@ -2735,7 +3011,7 @@ proc drawvisible {} {
 
 proc clear_display {} {
     global iddrawn idrangedrawn
 
 proc clear_display {} {
     global iddrawn idrangedrawn
-    global vhighlights fhighlights nhighlights
+    global vhighlights fhighlights nhighlights rhighlights
 
     allcanvs delete all
     catch {unset iddrawn}
 
     allcanvs delete all
     catch {unset iddrawn}
@@ -2743,6 +3019,7 @@ proc clear_display {} {
     catch {unset vhighlights}
     catch {unset fhighlights}
     catch {unset nhighlights}
     catch {unset vhighlights}
     catch {unset fhighlights}
     catch {unset nhighlights}
+    catch {unset rhighlights}
 }
 
 proc findcrossings {id} {
 }
 
 proc findcrossings {id} {
@@ -3046,12 +3323,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 {
@@ -3061,17 +3335,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)
@@ -3174,18 +3444,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
@@ -3203,247 +3461,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} {
@@ -3508,17 +3525,16 @@ 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
 # that we know about be a clickable link.
 }
 
 # append some text to the ctext widget, and make any SHA1 ID
 # that we know about be a clickable link.
-proc appendwithlinks {text} {
+proc appendwithlinks {text tags} {
     global ctext commitrow linknum curview
 
     set start [$ctext index "end - 1c"]
     global ctext commitrow linknum curview
 
     set start [$ctext index "end - 1c"]
-    $ctext insert end $text
-    $ctext insert end "\n"
+    $ctext insert end $text $tags
     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]
@@ -3553,6 +3569,64 @@ 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 l var} {
+    global ctext commitrow linknum curview idtags $var
+
+    if {[catch {$ctext index $pos}]} {
+       return 0
+    }
+    set tags {}
+    foreach id $l {
+       foreach tag [set $var\($id\)] {
+           lappend tags [concat $tag $id]
+       }
+    }
+    set tags [lsort -index 1 $tags]
+    set sep {}
+    foreach tag $tags {
+       set name [lindex $tag 0]
+       set id [lindex $tag 1]
+       set lk link$linknum
+       incr linknum
+       $ctext insert $pos $sep
+       $ctext insert $pos $name $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]
+}
+
+# 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) idheads] > 1} {
+           $ctext insert "branch -2c" "es"
+       }
+    }
+    if {[info exists anc_tags($id)]} {
+       appendrefs follows $anc_tags($id) idtags
+    }
+    if {[info exists desc_tags($id)]} {
+       appendrefs precedes $desc_tags($id) idtags
+    }
+    $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
@@ -3560,11 +3634,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]
@@ -3628,9 +3703,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]]
@@ -3645,7 +3721,7 @@ proc selectline {l isnew} {
        $ctext insert end "\n"
     }
  
        $ctext insert end "\n"
     }
  
-    set comment {}
+    set headers {}
     set olds [lindex $parentlist $l]
     if {[llength $olds] > 1} {
        set np 0
     set olds [lindex $parentlist $l]
     if {[llength $olds] > 1} {
        set np 0
@@ -3656,23 +3732,50 @@ proc selectline {l isnew} {
                set tag m$np
            }
            $ctext insert end "Parent: " $tag
                set tag m$np
            }
            $ctext insert end "Parent: " $tag
-           appendwithlinks [commit_descriptor $p]
+           appendwithlinks [commit_descriptor $p] {}
            incr np
        }
     } else {
        foreach p $olds {
            incr np
        }
     } else {
        foreach p $olds {
-           append comment "Parent: [commit_descriptor $p]\n"
+           append headers "Parent: [commit_descriptor $p]"
        }
     }
 
     foreach c [lindex $childlist $l] {
        }
     }
 
     foreach c [lindex $childlist $l] {
-       append comment "Child:  [commit_descriptor $c]\n"
+       append headers "Child:  [commit_descriptor $c]"
     }
     }
-    append comment "\n"
-    append comment [lindex $info 5]
 
     # make anything that looks like a SHA1 ID be a clickable link
 
     # make anything that looks like a SHA1 ID be a clickable link
-    appendwithlinks $comment
+    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) idheads] > 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 $anc_tags($id) idtags
+       }
+       $ctext insert end "\nPrecedes: "
+       $ctext mark set precedes "end -1c"
+       $ctext mark gravity precedes left
+       if {[info exists desc_tags($id)]} {
+           appendrefs precedes $desc_tags($id) idtags
+       }
+       $ctext insert end "\n"
+    }
+    $ctext insert end "\n"
+    appendwithlinks [lindex $info 5] {comment}
 
     $ctext tag delete Comments
     $ctext tag remove found 1.0 end
 
     $ctext tag delete Comments
     $ctext tag remove found 1.0 end
@@ -3735,6 +3838,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 {} {
@@ -3813,7 +3918,7 @@ proc gettree {id} {
     catch {unset diffmergeid}
     if {![info exists treefilelist($id)]} {
        if {![info exists treepending]} {
     catch {unset diffmergeid}
     if {![info exists treefilelist($id)]} {
        if {![info exists treepending]} {
-           if {[catch {set gtf [open [concat | git-ls-tree -r $id] r]}]} {
+           if {[catch {set gtf [open [concat | git ls-tree -r $id] r]}]} {
                return
            }
            set treepending $id
                return
            }
            set treepending $id
@@ -3861,14 +3966,14 @@ proc showfile {f} {
        return
     }
     set blob [lindex $treeidlist($diffids) $i]
        return
     }
     set blob [lindex $treeidlist($diffids) $i]
-    if {[catch {set bf [open [concat | git-cat-file blob $blob] r]} err]} {
+    if {[catch {set bf [open [concat | git cat-file blob $blob] r]} err]} {
        puts "oops, error reading blob $blob: $err"
        return
     }
     fconfigure $bf -blocking 0
     fileevent $bf readable [list getblobline $bf $diffids]
     $ctext config -state normal
        puts "oops, error reading blob $blob: $err"
        return
     }
     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
@@ -3903,7 +4008,7 @@ proc mergediff {id l} {
     set diffids $id
     # this doesn't seem to actually affect anything...
     set env(GIT_DIFF_OPTS) $diffopts
     set diffids $id
     # this doesn't seem to actually affect anything...
     set env(GIT_DIFF_OPTS) $diffopts
-    set cmd [concat | git-diff-tree --no-commit-id --cc $id]
+    set cmd [concat | git diff-tree --no-commit-id --cc $id]
     if {[catch {set mdf [open $cmd r]} err]} {
        error_popup "Error getting merge diffs: $err"
        return
     if {[catch {set mdf [open $cmd r]} err]} {
        error_popup "Error getting merge diffs: $err"
        return
@@ -4015,7 +4120,7 @@ proc gettreediffs {ids} {
     set treepending $ids
     set treediff {}
     if {[catch \
     set treepending $ids
     set treediff {}
     if {[catch \
-        {set gdtf [open [concat | git-diff-tree --no-commit-id -r $ids] r]} \
+        {set gdtf [open [concat | git diff-tree --no-commit-id -r $ids] r]} \
        ]} return
     fconfigure $gdtf -blocking 0
     fileevent $gdtf readable [list gettreediffline $gdtf $ids]
        ]} return
     fconfigure $gdtf -blocking 0
     fileevent $gdtf readable [list gettreediffline $gdtf $ids]
@@ -4051,7 +4156,7 @@ proc getblobdiffs {ids} {
     global nextupdate diffinhdr treediffs
 
     set env(GIT_DIFF_OPTS) $diffopts
     global nextupdate diffinhdr treediffs
 
     set env(GIT_DIFF_OPTS) $diffopts
-    set cmd [concat | git-diff-tree --no-commit-id -r -p -C $ids]
+    set cmd [concat | git diff-tree --no-commit-id -r -p -C $ids]
     if {[catch {set bdf [open $cmd r]} err]} {
        puts "error getting diffs: $err"
        return
     if {[catch {set bdf [open $cmd r]} err]} {
        puts "error getting diffs: $err"
        return
@@ -4168,6 +4273,138 @@ proc nextfile {} {
     }
 }
 
     }
 }
 
+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
@@ -4402,7 +4639,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 }
@@ -4495,7 +4732,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
@@ -4588,7 +4825,7 @@ proc mkpatchgo {} {
     set oldid [$patchtop.fromsha1 get]
     set newid [$patchtop.tosha1 get]
     set fname [$patchtop.fname get]
     set oldid [$patchtop.fromsha1 get]
     set newid [$patchtop.tosha1 get]
     set fname [$patchtop.fname get]
-    if {[catch {exec git-diff-tree -p $oldid $newid >$fname &} err]} {
+    if {[catch {exec git diff-tree -p $oldid $newid >$fname &} err]} {
        error_popup "Error creating patch: $err"
     }
     catch {destroy $patchtop}
        error_popup "Error creating patch: $err"
     }
     catch {destroy $patchtop}
@@ -4664,12 +4901,19 @@ proc domktag {} {
 
 proc redrawtags {id} {
     global canv linehtag commitrow idpos selectedline curview
 
 proc redrawtags {id} {
     global canv linehtag commitrow idpos selectedline curview
+    global mainfont
 
     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
@@ -4743,22 +4987,192 @@ proc wrcomcan {} {
     unset wrcomtop
 }
 
     unset wrcomtop
 }
 
-proc listrefs {id} {
-    global idtags idheads idotherrefs
+# Stuff for finding nearby tags
+proc getallcommits {} {
+    global allcstart allcommits allcfd
 
 
-    set x {}
-    if {[info exists idtags($id)]} {
-       set x $idtags($id)
+    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}
     }
     }
-    set y {}
-    if {[info exists idheads($id)]} {
-       set y $idheads($id)
+    foreach v {allcommits allchildren allparents allids desc_tags anc_tags
+               alldtags tagisdesc desc_heads} {
+       catch {unset $v}
     }
     }
-    set z {}
-    if {[info exists idotherrefs($id)]} {
-       set z $idotherrefs($id)
+}
+
+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 [list $x $y $z]
+    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 getallclines {fd} {
+    global allparents allchildren allcommits allcstart
+    global desc_tags anc_tags idtags alldtags tagisdesc allids
+    global desc_heads idheads
+
+    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
+       set dtags {}
+       set dheads {}
+       foreach child $allchildren($id) {
+           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)]} {
+           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
+           }
+       }
+       if {[info exists idheads($id)]} {
+           lappend dheads $id
+       }
+       set desc_heads($id) $dheads
+       if {[clock clicks -milliseconds] - $allcstart >= 50} {
+           fileevent $fd readable {}
+           after idle restartgetall $fd
+           return
+       }
+    }
+    if {[eof $fd]} {
+       after idle restartatags [llength $allids]
+       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 {i} {
+    global allids allparents idtags anc_tags t0
+
+    set t0 [clock clicks -milliseconds]
+    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} {
+           after idle restartatags $i
+           return
+       }
+    }
+    set allcommits "done"
+    notbusy allcommits
+    dispneartags
 }
 
 proc rereadrefs {} {
 }
 
 proc rereadrefs {} {
@@ -4789,14 +5203,14 @@ 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)
     } else {
        set text "Tag: $tag\nId:  $tagids($tag)"
     }
     set linknum 0
     if {[info exists tagcontents($tag)]} {
        set text $tagcontents($tag)
     } else {
        set text "Tag: $tag\nId:  $tagids($tag)"
     }
-    appendwithlinks $text
+    appendwithlinks $text {}
     $ctext conf -state disabled
     init_flist {}
 }
     $ctext conf -state disabled
     init_flist {}
 }
@@ -4808,8 +5222,8 @@ proc doquit {} {
 }
 
 proc doprefs {} {
 }
 
 proc doprefs {} {
-    global maxwidth maxgraphpct diffopts findmergefiles
-    global oldprefs prefstop
+    global maxwidth maxgraphpct diffopts
+    global oldprefs prefstop showneartags
 
     set top .gitkprefs
     set prefstop $top
 
     set top .gitkprefs
     set prefstop $top
@@ -4817,7 +5231,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
@@ -4833,16 +5247,17 @@ 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
     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
@@ -4853,10 +5268,10 @@ proc doprefs {} {
 }
 
 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}
@@ -4865,13 +5280,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
     }
 }
 
     }
 }
 
@@ -5155,11 +5572,11 @@ proc tcl_encoding {enc} {
 # defaults...
 set datemode 0
 set diffopts "-U 5 -p"
 # defaults...
 set datemode 0
 set diffopts "-U 5 -p"
-set wrcomcmd "git-diff-tree --stdin -p --pretty"
+set wrcomcmd "git diff-tree --stdin -p --pretty"
 
 set gitencoding {}
 catch {
 
 set gitencoding {}
 catch {
-    set gitencoding [exec git-repo-config --get i18n.commitencoding]
+    set gitencoding [exec git repo-config --get i18n.commitencoding]
 }
 if {$gitencoding == ""} {
     set gitencoding "utf-8"
 }
 if {$gitencoding == ""} {
     set gitencoding "utf-8"
@@ -5180,8 +5597,9 @@ 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 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}
 
@@ -5203,7 +5621,7 @@ foreach arg $argv {
 # check that we can find a .git directory somewhere...
 set gitdir [gitdir]
 if {![file isdirectory $gitdir]} {
 # check that we can find a .git directory somewhere...
 set gitdir [gitdir]
 if {![file isdirectory $gitdir]} {
-    show_error . "Cannot find the git directory \"$gitdir\"."
+    show_error {} . "Cannot find the git directory \"$gitdir\"."
     exit 1
 }
 
     exit 1
 }
 
@@ -5214,7 +5632,7 @@ if {$i >= 0} {
     set revtreeargs [lrange $revtreeargs 0 [expr {$i - 1}]]
 } elseif {$revtreeargs ne {}} {
     if {[catch {
     set revtreeargs [lrange $revtreeargs 0 [expr {$i - 1}]]
 } elseif {$revtreeargs ne {}} {
     if {[catch {
-       set f [eval exec git-rev-parse --no-revs --no-flags $revtreeargs]
+       set f [eval exec git rev-parse --no-revs --no-flags $revtreeargs]
        set cmdline_files [split $f "\n"]
        set n [llength $cmdline_files]
        set revtreeargs [lrange $revtreeargs 0 end-$n]
        set cmdline_files [split $f "\n"]
        set n [llength $cmdline_files]
        set revtreeargs [lrange $revtreeargs 0 end-$n]
@@ -5223,9 +5641,9 @@ if {$i >= 0} {
        # so look for "fatal:".
        set i [string first "fatal:" $err]
        if {$i > 0} {
        # so look for "fatal:".
        set i [string first "fatal:" $err]
        if {$i > 0} {
-           set err [string range [expr {$i + 6}] end]
+           set err [string range $err [expr {$i + 6}] end]
        }
        }
-       show_error . "Bad arguments to gitk:\n$err"
+       show_error {} . "Bad arguments to gitk:\n$err"
        exit 1
     }
 }
        exit 1
     }
 }
@@ -5233,9 +5651,11 @@ if {$i >= 0} {
 set history {}
 set historyindex 0
 set fh_serial 0
 set history {}
 set historyindex 0
 set fh_serial 0
-set highlight_names {}
 set nhl_names {}
 set highlight_paths {}
 set nhl_names {}
 set highlight_paths {}
+set searchdirn -forwards
+set boldrows {}
+set boldnamerows {}
 
 set optim_delay 16
 
 
 set optim_delay 16