git-svn: rebuild convenience and bugfixes
[git.git] / gitk
diff --git a/gitk b/gitk
index 6d6a2f3..9be10a4 100755 (executable)
--- a/gitk
+++ b/gitk
@@ -16,100 +16,112 @@ proc gitdir {} {
     }
 }
 
     }
 }
 
-proc start_rev_list {} {
+proc start_rev_list {view} {
     global startmsecs nextupdate ncmupdate
     global commfd leftover tclencoding datemode
     global startmsecs nextupdate ncmupdate
     global commfd leftover tclencoding datemode
-    global revtreeargs curview viewfiles
+    global viewargs viewfiles commitidx
 
     set startmsecs [clock clicks -milliseconds]
     set nextupdate [expr {$startmsecs + 100}]
     set ncmupdate 1
 
     set startmsecs [clock clicks -milliseconds]
     set nextupdate [expr {$startmsecs + 100}]
     set ncmupdate 1
-    initlayout
-    set args $revtreeargs
-    if {$viewfiles($curview) ne {}} {
-       set args [concat $args "--" $viewfiles($curview)]
+    set commitidx($view) 0
+    set args $viewargs($view)
+    if {$viewfiles($view) ne {}} {
+       set args [concat $args "--" $viewfiles($view)]
     }
     set order "--topo-order"
     if {$datemode} {
        set order "--date-order"
     }
     if {[catch {
     }
     set order "--topo-order"
     if {$datemode} {
        set order "--date-order"
     }
     if {[catch {
-       set commfd [open [concat | git-rev-list --header $order \
-                             --parents --boundary --default HEAD $args] r]
+       set fd [open [concat | git rev-list --header $order \
+                         --parents --boundary --default HEAD $args] r]
     } err]} {
     } err]} {
-       puts stderr "Error executing git-rev-list: $err"
+       puts stderr "Error executing git rev-list: $err"
        exit 1
     }
        exit 1
     }
-    set leftover {}
-    fconfigure $commfd -blocking 0 -translation lf
+    set commfd($view) $fd
+    set leftover($view) {}
+    fconfigure $fd -blocking 0 -translation lf
     if {$tclencoding != {}} {
     if {$tclencoding != {}} {
-       fconfigure $commfd -encoding $tclencoding
+       fconfigure $fd -encoding $tclencoding
     }
     }
-    fileevent $commfd readable [list getcommitlines $commfd]
-    . config -cursor watch
-    settextcursor watch
+    fileevent $fd readable [list getcommitlines $fd $view]
+    nowbusy $view
 }
 
 proc stop_rev_list {} {
 }
 
 proc stop_rev_list {} {
-    global commfd
+    global commfd curview
 
 
-    if {![info exists commfd]} return
+    if {![info exists commfd($curview)]} return
+    set fd $commfd($curview)
     catch {
     catch {
-       set pid [pid $commfd]
+       set pid [pid $fd]
        exec kill $pid
     }
        exec kill $pid
     }
-    catch {close $commfd}
-    unset commfd
+    catch {close $fd}
+    unset commfd($curview)
 }
 
 proc getcommits {} {
 }
 
 proc getcommits {} {
-    global phase canv mainfont
+    global phase canv mainfont curview
 
     set phase getcommits
 
     set phase getcommits
-    start_rev_list
-    $canv delete all
-    $canv create text 3 3 -anchor nw -text "Reading commits..." \
-       -font $mainfont -tags textitems
+    initlayout
+    start_rev_list $curview
+    show_status "Reading commits..."
 }
 
 }
 
-proc getcommitlines {commfd}  {
+proc getcommitlines {fd view}  {
     global commitlisted nextupdate
     global commitlisted nextupdate
-    global leftover
+    global leftover commfd
     global displayorder commitidx commitrow commitdata
     global displayorder commitidx commitrow commitdata
-    global parentlist childlist children
+    global parentlist childlist children curview hlview
+    global vparentlist vchildlist vdisporder vcmitlisted
 
 
-    set stuff [read $commfd]
+    set stuff [read $fd]
     if {$stuff == {}} {
     if {$stuff == {}} {
-       if {![eof $commfd]} return
+       if {![eof $fd]} return
+       global viewname
+       unset commfd($view)
+       notbusy $view
        # set it blocking so we wait for the process to terminate
        # set it blocking so we wait for the process to terminate
-       fconfigure $commfd -blocking 1
-       if {![catch {close $commfd} err]} {
-           after idle finishcommits
-           return
+       fconfigure $fd -blocking 1
+       if {[catch {close $fd} err]} {
+           set fv {}
+           if {$view != $curview} {
+               set fv " for the \"$viewname($view)\" view"
+           }
+           if {[string range $err 0 4] == "usage"} {
+               set err "Gitk: error reading commits$fv:\
+                       bad arguments to git rev-list."
+               if {$viewname($view) eq "Command line"} {
+                   append err \
+                       "  (Note: arguments to gitk are passed to git rev-list\
+                        to allow selection of commits to be displayed.)"
+               }
+           } else {
+               set err "Error reading commits$fv: $err"
+           }
+           error_popup $err
        }
        }
-       if {[string range $err 0 4] == "usage"} {
-           set err \
-               "Gitk: error reading commits: bad arguments to git-rev-list.\
-               (Note: arguments to gitk are passed to git-rev-list\
-               to allow selection of commits to be displayed.)"
-       } else {
-           set err "Error reading commits: $err"
+       if {$view == $curview} {
+           after idle finishcommits
        }
        }
-       error_popup $err
-       exit 1
+       return
     }
     set start 0
     set gotsome 0
     while 1 {
        set i [string first "\0" $stuff $start]
        if {$i < 0} {
     }
     set start 0
     set gotsome 0
     while 1 {
        set i [string first "\0" $stuff $start]
        if {$i < 0} {
-           append leftover [string range $stuff $start end]
+           append leftover($view) [string range $stuff $start end]
            break
        }
        if {$start == 0} {
            break
        }
        if {$start == 0} {
-           set cmit $leftover
+           set cmit $leftover($view)
            append cmit [string range $stuff 0 [expr {$i - 1}]]
            append cmit [string range $stuff 0 [expr {$i - 1}]]
-           set leftover {}
+           set leftover($view) {}
        } else {
            set cmit [string range $stuff $start [expr {$i - 1}]]
        }
        } else {
            set cmit [string range $stuff $start [expr {$i - 1}]]
        }
@@ -136,7 +148,7 @@ proc getcommitlines {commfd}  {
            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]
@@ -145,40 +157,49 @@ proc getcommitlines {commfd}  {
            set i 0
            foreach p $olds {
                if {$i == 0 || [lsearch -exact $olds $p] >= $i} {
            set i 0
            foreach p $olds {
                if {$i == 0 || [lsearch -exact $olds $p] >= $i} {
-                   lappend children($p) $id
+                   lappend children($view,$p) $id
                }
                incr i
            }
        } else {
            set olds {}
        }
                }
                incr i
            }
        } else {
            set olds {}
        }
-       lappend parentlist $olds
-       if {[info exists children($id)]} {
-           lappend childlist $children($id)
-           unset children($id)
-       } else {
-           lappend childlist {}
+       if {![info exists children($view,$id)]} {
+           set children($view,$id) {}
        }
        set commitdata($id) [string range $cmit [expr {$j + 1}] end]
        }
        set commitdata($id) [string range $cmit [expr {$j + 1}] end]
-       set commitrow($id) $commitidx
-       incr commitidx
-       lappend displayorder $id
-       lappend commitlisted $listed
+       set commitrow($view,$id) $commitidx($view)
+       incr commitidx($view)
+       if {$view == $curview} {
+           lappend parentlist $olds
+           lappend childlist $children($view,$id)
+           lappend displayorder $id
+           lappend commitlisted $listed
+       } else {
+           lappend vparentlist($view) $olds
+           lappend vchildlist($view) $children($view,$id)
+           lappend vdisporder($view) $id
+           lappend vcmitlisted($view) $listed
+       }
        set gotsome 1
     }
     if {$gotsome} {
        set gotsome 1
     }
     if {$gotsome} {
-       layoutmore
+       if {$view == $curview} {
+           layoutmore
+       } elseif {[info exists hlview] && $view == $hlview} {
+           vhighlightmore
+       }
     }
     if {[clock clicks -milliseconds] >= $nextupdate} {
     }
     if {[clock clicks -milliseconds] >= $nextupdate} {
-       doupdate 1
+       doupdate
     }
 }
 
     }
 }
 
-proc doupdate {reading} {
+proc doupdate {} {
     global commfd nextupdate numcommits ncmupdate
 
     global commfd nextupdate numcommits ncmupdate
 
-    if {$reading} {
-       fileevent $commfd readable {}
+    foreach v [array names commfd] {
+       fileevent $commfd($v) readable {}
     }
     update
     set nextupdate [expr {[clock clicks -milliseconds] + 100}]
     }
     update
     set nextupdate [expr {[clock clicks -milliseconds] + 100}]
@@ -189,26 +210,35 @@ proc doupdate {reading} {
     } else {
        set ncmupdate [expr {$numcommits + 100}]
     }
     } else {
        set ncmupdate [expr {$numcommits + 100}]
     }
-    if {$reading} {
-       fileevent $commfd readable [list getcommitlines $commfd]
+    foreach v [array names commfd] {
+       set fd $commfd($v)
+       fileevent $fd readable [list getcommitlines $fd $v]
     }
 }
 
 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
 }
 
 proc updatecommits {} {
     parsecommit $id $contents 0
 }
 
 proc updatecommits {} {
-    global viewdata curview revtreeargs phase
+    global viewdata curview phase displayorder
+    global children commitrow selectedline thickerline
 
     if {$phase ne {}} {
        stop_rev_list
        set phase {}
     }
     set n $curview
 
     if {$phase ne {}} {
        stop_rev_list
        set phase {}
     }
     set n $curview
+    foreach id $displayorder {
+       catch {unset children($n,$id)}
+       catch {unset commitrow($n,$id)}
+    }
     set curview -1
     set curview -1
+    catch {unset selectedline}
+    catch {unset thickerline}
     catch {unset viewdata($n)}
     catch {unset viewdata($n)}
+    discardallcommits
     readrefs
     showview $n
 }
     readrefs
     showview $n
 }
@@ -249,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 "    "
@@ -293,10 +323,16 @@ proc readrefs {} {
            match id path]} {
            continue
        }
            match id path]} {
            continue
        }
+       if {[regexp {^remotes/.*/HEAD$} $path match]} {
+           continue
+       }
        if {![regexp {^(tags|heads)/(.*)$} $path match type name]} {
            set type others
            set name $path
        }
        if {![regexp {^(tags|heads)/(.*)$} $path match type name]} {
            set type others
            set name $path
        }
+       if {[regexp {^remotes/} $path match]} {
+           set type heads
+       }
        if {$type == "tags"} {
            set tagids($name) $id
            lappend idtags($id) $name
        if {$type == "tags"} {
            set tagids($name) $id
            lappend idtags($id) $name
@@ -304,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
@@ -324,17 +360,21 @@ proc readrefs {} {
     close $refd
 }
 
     close $refd
 }
 
+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
+    button $w.ok -text OK -command "destroy $top"
+    pack $w.ok -side bottom -fill x
+    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 .
-    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"
-    pack $w.ok -side bottom -fill x
-    bind $w <Visibility> "grab $w; focus $w"
-    bind $w <Key-Return> "destroy $w"
-    tkwait window $w
+    show_error $w $w $msg
 }
 
 proc makewindow {} {
 }
 
 proc makewindow {} {
@@ -343,7 +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 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
@@ -357,13 +399,17 @@ proc makewindow {} {
     .bar add cascade -label "Edit" -menu .bar.edit
     .bar.edit add command -label "Preferences" -command doprefs
     .bar.edit configure -font $uifont
     .bar add cascade -label "Edit" -menu .bar.edit
     .bar.edit add command -label "Preferences" -command doprefs
     .bar.edit configure -font $uifont
+
     menu .bar.view -font $uifont
     .bar add cascade -label "View" -menu .bar.view
     menu .bar.view -font $uifont
     .bar add cascade -label "View" -menu .bar.view
-    .bar.view add command -label "New view..." -command newview
+    .bar.view add command -label "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 separator
     .bar.view add radiobutton -label "All files" -command {showview 0} \
        -variable selectedview -value 0
     .bar.view add command -label "Delete view" -command delview -state disabled
     .bar.view add separator
     .bar.view add radiobutton -label "All files" -command {showview 0} \
        -variable selectedview -value 0
+    
     menu .bar.help
     .bar add cascade -label "Help" -menu .bar.help
     .bar.help add command -label "About gitk" -command about
     menu .bar.help
     .bar add cascade -label "Help" -menu .bar.help
     .bar.help add command -label "About gitk" -command about
@@ -389,6 +435,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
@@ -450,36 +498,80 @@ 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 0 -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
     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
@@ -507,12 +599,26 @@ proc makewindow {} {
     $ctext tag conf found -back yellow
 
     frame .ctop.cdet.right
     $ctext tag conf found -back yellow
 
     frame .ctop.cdet.right
+    frame .ctop.cdet.right.mode
+    radiobutton .ctop.cdet.right.mode.patch -text "Patch" \
+       -command reselectline -variable cmitmode -value "patch"
+    radiobutton .ctop.cdet.right.mode.tree -text "Tree" \
+       -command reselectline -variable cmitmode -value "tree"
+    grid .ctop.cdet.right.mode.patch .ctop.cdet.right.mode.tree -sticky ew
+    pack .ctop.cdet.right.mode -side top -fill x
     set cflist .ctop.cdet.right.cfiles
     set cflist .ctop.cdet.right.cfiles
-    listbox $cflist -bg white -selectmode extended -width $geometry(cflistw) \
-       -yscrollcommand ".ctop.cdet.right.sb set" -font $mainfont
+    set indent [font measure $mainfont "nn"]
+    text $cflist -width $geometry(cflistw) -background white -font $mainfont \
+       -tabs [list $indent [expr {2 * $indent}]] \
+       -yscrollcommand ".ctop.cdet.right.sb set" \
+       -cursor [. cget -cursor] \
+       -spacing1 1 -spacing3 1
     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
     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}
 
@@ -528,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"
@@ -559,17 +667,20 @@ 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-KP_Subtract> {incrfont -1}
     bind . <Control-equal> {incrfont 1}
     bind . <Control-KP_Add> {incrfont 1}
     bind . <Control-minus> {incrfont -1}
     bind . <Control-KP_Subtract> {incrfont -1}
-    bind $cflist <<ListboxSelect>> listboxsel
     bind . <Destroy> {savestuff %W}
     bind . <Button-1> "click %W"
     bind $fstring <Key-Return> dofind
     bind $sha1entry <Key-Return> gotocommit
     bind $sha1entry <<PasteSelection>> clearsha1
     bind . <Destroy> {savestuff %W}
     bind . <Button-1> "click %W"
     bind $fstring <Key-Return> dofind
     bind $sha1entry <Key-Return> gotocommit
     bind $sha1entry <<PasteSelection>> clearsha1
+    bind $cflist <1> {sel_flist %W %x %y; break}
+    bind $cflist <B1-Motion> {sel_flist %W %x %y; break}
+    bind $cflist <ButtonRelease-1> {treeclick %W %x %y}
 
     set maincursor [. cget -cursor]
     set textcursor [$ctext cget -cursor]
 
     set maincursor [. cget -cursor]
     set textcursor [$ctext cget -cursor]
@@ -602,6 +713,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
@@ -632,8 +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 viewname viewfiles viewperm nextviewnum
+    global maxwidth showneartags
+    global viewname viewfiles viewargs viewperm nextviewnum
+    global cmitmode wrapcomment
 
     if {$stuffsaved} return
     if {![winfo viewable .]} return
 
     if {$stuffsaved} return
     if {![winfo viewable .]} return
@@ -645,6 +758,9 @@ proc savestuff {w} {
        puts $f [list set findmergefiles $findmergefiles]
        puts $f [list set maxgraphpct $maxgraphpct]
        puts $f [list set maxwidth $maxwidth]
        puts $f [list set findmergefiles $findmergefiles]
        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}]"
@@ -660,7 +776,7 @@ proc savestuff {w} {
        puts -nonewline $f "set permviews {"
        for {set v 0} {$v < $nextviewnum} {incr v} {
            if {$viewperm($v)} {
        puts -nonewline $f "set permviews {"
        for {set v 0} {$v < $nextviewnum} {incr v} {
            if {$viewperm($v)} {
-               puts $f "{[list $viewname($v) $viewfiles($v)]}"
+               puts $f "{[list $viewname($v) $viewfiles($v) $viewargs($v)]}"
            }
        }
        puts $f "}"
            }
        }
        puts $f "}"
@@ -782,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
@@ -789,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
@@ -805,32 +924,495 @@ f                Scroll diff view to next file
     pack $w.ok -side bottom
 }
 
     pack $w.ok -side bottom
 }
 
-proc newview {} {
-    global newviewname nextviewnum newviewtop newviewperm uifont
+# Procedures for manipulating the file list window at the
+# bottom right of the overall window.
+
+proc treeview {w l openlevs} {
+    global treecontents treediropen treeheight treeparent treeindex
+
+    set ix 0
+    set treeindex() 0
+    set lev 0
+    set prefix {}
+    set prefixend -1
+    set prefendstack {}
+    set htstack {}
+    set ht 0
+    set treecontents() {}
+    $w conf -state normal
+    foreach f $l {
+       while {[string range $f 0 $prefixend] ne $prefix} {
+           if {$lev <= $openlevs} {
+               $w mark set e:$treeindex($prefix) "end -1c"
+               $w mark gravity e:$treeindex($prefix) left
+           }
+           set treeheight($prefix) $ht
+           incr ht [lindex $htstack end]
+           set htstack [lreplace $htstack end end]
+           set prefixend [lindex $prefendstack end]
+           set prefendstack [lreplace $prefendstack end end]
+           set prefix [string range $prefix 0 $prefixend]
+           incr lev -1
+       }
+       set tail [string range $f [expr {$prefixend+1}] end]
+       while {[set slash [string first "/" $tail]] >= 0} {
+           lappend htstack $ht
+           set ht 0
+           lappend prefendstack $prefixend
+           incr prefixend [expr {$slash + 1}]
+           set d [string range $tail 0 $slash]
+           lappend treecontents($prefix) $d
+           set oldprefix $prefix
+           append prefix $d
+           set treecontents($prefix) {}
+           set treeindex($prefix) [incr ix]
+           set treeparent($prefix) $oldprefix
+           set tail [string range $tail [expr {$slash+1}] end]
+           if {$lev <= $openlevs} {
+               set ht 1
+               set treediropen($prefix) [expr {$lev < $openlevs}]
+               set bm [expr {$lev == $openlevs? "tri-rt": "tri-dn"}]
+               $w mark set d:$ix "end -1c"
+               $w mark gravity d:$ix left
+               set str "\n"
+               for {set i 0} {$i < $lev} {incr i} {append str "\t"}
+               $w insert end $str
+               $w image create end -align center -image $bm -padx 1 \
+                   -name a:$ix
+               $w insert end $d [highlight_tag $prefix]
+               $w mark set s:$ix "end -1c"
+               $w mark gravity s:$ix left
+           }
+           incr lev
+       }
+       if {$tail ne {}} {
+           if {$lev <= $openlevs} {
+               incr ht
+               set str "\n"
+               for {set i 0} {$i < $lev} {incr i} {append str "\t"}
+               $w insert end $str
+               $w insert end $tail [highlight_tag $f]
+           }
+           lappend treecontents($prefix) $tail
+       }
+    }
+    while {$htstack ne {}} {
+       set treeheight($prefix) $ht
+       incr ht [lindex $htstack end]
+       set htstack [lreplace $htstack end end]
+    }
+    $w conf -state disabled
+}
+
+proc linetoelt {l} {
+    global treeheight treecontents
+
+    set y 2
+    set prefix {}
+    while {1} {
+       foreach e $treecontents($prefix) {
+           if {$y == $l} {
+               return "$prefix$e"
+           }
+           set n 1
+           if {[string index $e end] eq "/"} {
+               set n $treeheight($prefix$e)
+               if {$y + $n > $l} {
+                   append prefix $e
+                   incr y
+                   break
+               }
+           }
+           incr y $n
+       }
+    }
+}
+
+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
+
+    set ix $treeindex($dir)
+    $w conf -state normal
+    $w delete s:$ix e:$ix
+    set treediropen($dir) 0
+    $w image configure a:$ix -image tri-rt
+    $w conf -state disabled
+    set n [expr {1 - $treeheight($dir)}]
+    while {$dir ne {}} {
+       incr treeheight($dir) $n
+       set dir $treeparent($dir)
+    }
+}
+
+proc treeopendir {w dir} {
+    global treediropen treeheight treeparent treecontents treeindex
+
+    set ix $treeindex($dir)
+    $w conf -state normal
+    $w image configure a:$ix -image tri-dn
+    $w mark set e:$ix s:$ix
+    $w mark gravity e:$ix right
+    set lev 0
+    set str "\n"
+    set n [llength $treecontents($dir)]
+    for {set x $dir} {$x ne {}} {set x $treeparent($x)} {
+       incr lev
+       append str "\t"
+       incr treeheight($x) $n
+    }
+    foreach e $treecontents($dir) {
+       set de $dir$e
+       if {[string index $e end] eq "/"} {
+           set iy $treeindex($de)
+           $w mark set d:$iy e:$ix
+           $w mark gravity d:$iy left
+           $w insert e:$ix $str
+           set treediropen($de) 0
+           $w image create e:$ix -align center -image tri-rt -padx 1 \
+               -name a:$iy
+           $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 insert e:$ix $e [highlight_tag $de]
+       }
+    }
+    $w mark gravity e:$ix left
+    $w conf -state disabled
+    set treediropen($dir) 1
+    set top [lindex [split [$w index @0,0] .] 0]
+    set ht [$w cget -height]
+    set l [lindex [split [$w index s:$ix] .] 0]
+    if {$l < $top} {
+       $w yview $l.0
+    } elseif {$l + $n + 1 > $top + $ht} {
+       set top [expr {$l + $n + 2 - $ht}]
+       if {$l < $top} {
+           set top $l
+       }
+       $w yview $top.0
+    }
+}
+
+proc treeclick {w x y} {
+    global treediropen cmitmode ctext cflist cflist_top
+
+    if {$cmitmode ne "tree"} return
+    if {![info exists cflist_top]} return
+    set l [lindex [split [$w index "@$x,$y"] "."] 0]
+    $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
+    $cflist tag add highlight $l.0 "$l.0 lineend"
+    set cflist_top $l
+    if {$l == 1} {
+       $ctext yview 1.0
+       return
+    }
+    set e [linetoelt $l]
+    if {[string index $e end] ne "/"} {
+       showfile $e
+    } elseif {$treediropen($e)} {
+       treeclosedir $w $e
+    } else {
+       treeopendir $w $e
+    }
+}
+
+proc setfilelist {id} {
+    global treefilelist cflist
+
+    treeview $cflist $treefilelist($id) 0
+}
+
+image create bitmap tri-rt -background black -foreground blue -data {
+    #define tri-rt_width 13
+    #define tri-rt_height 13
+    static unsigned char tri-rt_bits[] = {
+       0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x30, 0x00, 0x70, 0x00, 0xf0, 0x00,
+       0xf0, 0x01, 0xf0, 0x00, 0x70, 0x00, 0x30, 0x00, 0x10, 0x00, 0x00, 0x00,
+       0x00, 0x00};
+} -maskdata {
+    #define tri-rt-mask_width 13
+    #define tri-rt-mask_height 13
+    static unsigned char tri-rt-mask_bits[] = {
+       0x08, 0x00, 0x18, 0x00, 0x38, 0x00, 0x78, 0x00, 0xf8, 0x00, 0xf8, 0x01,
+       0xf8, 0x03, 0xf8, 0x01, 0xf8, 0x00, 0x78, 0x00, 0x38, 0x00, 0x18, 0x00,
+       0x08, 0x00};
+}
+image create bitmap tri-dn -background black -foreground blue -data {
+    #define tri-dn_width 13
+    #define tri-dn_height 13
+    static unsigned char tri-dn_bits[] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0xf8, 0x03,
+       0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00};
+} -maskdata {
+    #define tri-dn-mask_width 13
+    #define tri-dn-mask_height 13
+    static unsigned char tri-dn-mask_bits[] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x1f, 0xfe, 0x0f, 0xfc, 0x07,
+       0xf8, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00};
+}
+
+proc init_flist {first} {
+    global cflist cflist_top selectedline difffilestart
+
+    $cflist conf -state normal
+    $cflist delete 0.0 end
+    if {$first ne {}} {
+       $cflist insert end $first
+       set cflist_top 1
+       $cflist tag add highlight 1.0 "1.0 lineend"
+    } else {
+       catch {unset cflist_top}
+    }
+    $cflist conf -state disabled
+    set difffilestart {}
+}
+
+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
+    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} {
+    global ctext difffilestart cflist cflist_top cmitmode
+
+    if {$cmitmode eq "tree"} return
+    if {![info exists cflist_top]} return
+    set l [lindex [split [$w index "@$x,$y"] "."] 0]
+    $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
+    $cflist tag add highlight $l.0 "$l.0 lineend"
+    set cflist_top $l
+    if {$l == 1} {
+       $ctext yview 1.0
+    } else {
+       catch {$ctext yview [lindex $difffilestart [expr {$l - 2}]]}
+    }
+}
+
+# Functions for adding and removing shell-type quoting
+
+proc shellquote {str} {
+    if {![string match "*\['\"\\ \t]*" $str]} {
+       return $str
+    }
+    if {![string match "*\['\"\\]*" $str]} {
+       return "\"$str\""
+    }
+    if {![string match "*'*" $str]} {
+       return "'$str'"
+    }
+    return "\"[string map {\" \\\" \\ \\\\} $str]\""
+}
+
+proc shellarglist {l} {
+    set str {}
+    foreach a $l {
+       if {$str ne {}} {
+           append str " "
+       }
+       append str [shellquote $a]
+    }
+    return $str
+}
+
+proc shelldequote {str} {
+    set ret {}
+    set used -1
+    while {1} {
+       incr used
+       if {![regexp -start $used -indices "\['\"\\\\ \t]" $str first]} {
+           append ret [string range $str $used end]
+           set used [string length $str]
+           break
+       }
+       set first [lindex $first 0]
+       set ch [string index $str $first]
+       if {$first > $used} {
+           append ret [string range $str $used [expr {$first - 1}]]
+           set used $first
+       }
+       if {$ch eq " " || $ch eq "\t"} break
+       incr used
+       if {$ch eq "'"} {
+           set first [string first "'" $str $used]
+           if {$first < 0} {
+               error "unmatched single-quote"
+           }
+           append ret [string range $str $used [expr {$first - 1}]]
+           set used $first
+           continue
+       }
+       if {$ch eq "\\"} {
+           if {$used >= [string length $str]} {
+               error "trailing backslash"
+           }
+           append ret [string index $str $used]
+           continue
+       }
+       # here ch == "\""
+       while {1} {
+           if {![regexp -start $used -indices "\[\"\\\\]" $str first]} {
+               error "unmatched double-quote"
+           }
+           set first [lindex $first 0]
+           set ch [string index $str $first]
+           if {$first > $used} {
+               append ret [string range $str $used [expr {$first - 1}]]
+               set used $first
+           }
+           if {$ch eq "\""} break
+           incr used
+           append ret [string index $str $used]
+           incr used
+       }
+    }
+    return [list $used $ret]
+}
+
+proc shellsplit {str} {
+    set l {}
+    while {1} {
+       set str [string trimleft $str]
+       if {$str eq {}} break
+       set dq [shelldequote $str]
+       set n [lindex $dq 0]
+       set word [lindex $dq 1]
+       set str [string range $str $n end]
+       lappend l $word
+    }
+    return $l
+}
+
+# Code to implement multiple views
+
+proc newview {ishighlight} {
+    global nextviewnum newviewname newviewperm uifont newishighlight
+    global newviewargs revtreeargs
+
+    set newishighlight $ishighlight
     set top .gitkview
     if {[winfo exists $top]} {
        raise $top
        return
     }
     set top .gitkview
     if {[winfo exists $top]} {
        raise $top
        return
     }
-    set newviewtop $top
+    set newviewname($nextviewnum) "View $nextviewnum"
+    set newviewperm($nextviewnum) 0
+    set newviewargs($nextviewnum) [shellarglist $revtreeargs]
+    vieweditor $top $nextviewnum "Gitk view definition" 
+}
+
+proc editview {} {
+    global curview
+    global viewname viewperm newviewname newviewperm
+    global viewargs newviewargs
+
+    set top .gitkvedit-$curview
+    if {[winfo exists $top]} {
+       raise $top
+       return
+    }
+    set newviewname($curview) $viewname($curview)
+    set newviewperm($curview) $viewperm($curview)
+    set newviewargs($curview) [shellarglist $viewargs($curview)]
+    vieweditor $top $curview "Gitk: edit view $viewname($curview)"
+}
+
+proc vieweditor {top n title} {
+    global newviewname newviewperm viewfiles
+    global uifont
+
     toplevel $top
     toplevel $top
-    wm title $top "Gitk view definition"
+    wm title $top $title
     label $top.nl -text "Name" -font $uifont
     label $top.nl -text "Name" -font $uifont
-    entry $top.name -width 20 -textvariable newviewname
-    set newviewname "View $nextviewnum"
+    entry $top.name -width 20 -textvariable newviewname($n)
     grid $top.nl $top.name -sticky w -pady 5
     grid $top.nl $top.name -sticky w -pady 5
-    set newviewperm 0
-    checkbutton $top.perm -text "Remember this view" -variable newviewperm
+    checkbutton $top.perm -text "Remember this view" -variable newviewperm($n)
     grid $top.perm - -pady 5 -sticky w
     grid $top.perm - -pady 5 -sticky w
-    message $top.l -aspect 500 -font $uifont \
+    message $top.al -aspect 1000 -font $uifont \
+       -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.args - -sticky ew -padx 5
+    message $top.l -aspect 1000 -font $uifont \
        -text "Enter files and directories to include, one per line:"
     grid $top.l - -sticky w
     text $top.t -width 40 -height 10 -background white
        -text "Enter files and directories to include, one per line:"
     grid $top.l - -sticky w
     text $top.t -width 40 -height 10 -background white
-    grid $top.t - -sticky w -padx 5
+    if {[info exists viewfiles($n)]} {
+       foreach f $viewfiles($n) {
+           $top.t insert end $f
+           $top.t insert end "\n"
+       }
+       $top.t delete {end - 1c} end
+       $top.t mark set insert 0.0
+    }
+    grid $top.t - -sticky ew -padx 5
     frame $top.buts
     frame $top.buts
-    button $top.buts.ok -text "OK" -command newviewok
-    button $top.buts.can -text "Cancel" -command newviewcan
+    button $top.buts.ok -text "OK" -command [list newviewok $top $n]
+    button $top.buts.can -text "Cancel" -command [list destroy $top]
     grid $top.buts.ok $top.buts.can
     grid columnconfigure $top.buts 0 -weight 1 -uniform a
     grid columnconfigure $top.buts 1 -weight 1 -uniform a
     grid $top.buts.ok $top.buts.can
     grid columnconfigure $top.buts 0 -weight 1 -uniform a
     grid columnconfigure $top.buts 1 -weight 1 -uniform a
@@ -838,53 +1420,100 @@ proc newview {} {
     focus $top.t
 }
 
     focus $top.t
 }
 
-proc newviewok {} {
-    global newviewtop nextviewnum newviewperm
-    global viewname viewfiles viewperm selectedview
+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} {
+           eval $m $op $i $argv
+           break
+       }
+    }
+}
+
+proc allviewmenus {n op args} {
+    global viewhlmenu
 
 
-    set n $nextviewnum
-    incr nextviewnum
-    set viewname($n) [$newviewtop.name get]
-    set viewperm($n) $newviewperm
+    doviewmenu .bar.view 7 [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
+    global viewargs newviewargs viewhlmenu
+
+    if {[catch {
+       set newargs [shellsplit $newviewargs($n)]
+    } err]} {
+       error_popup "Error in commit selection arguments: $err"
+       wm raise $top
+       focus $top
+       return
+    }
     set files {}
     set files {}
-    foreach f [split [$newviewtop.t get 0.0 end] "\n"] {
+    foreach f [split [$top.t get 0.0 end] "\n"] {
        set ft [string trim $f]
        if {$ft ne {}} {
            lappend files $ft
        }
     }
        set ft [string trim $f]
        if {$ft ne {}} {
            lappend files $ft
        }
     }
-    set viewfiles($n) $files
-    catch {destroy $newviewtop}
-    unset newviewtop
-    .bar.view add radiobutton -label $viewname($n) \
-       -command [list showview $n] -variable selectedview -value $n
-    after idle showview $n
-}
-
-proc newviewcan {} {
-    global newviewtop
-
-    catch {destroy $newviewtop}
-    unset newviewtop
+    if {![info exists viewfiles($n)]} {
+       # creating a new view
+       incr nextviewnum
+       set viewname($n) $newviewname($n)
+       set viewperm($n) $newviewperm($n)
+       set viewfiles($n) $files
+       set viewargs($n) $newargs
+       addviewmenu $n
+       if {!$newishighlight} {
+           after idle showview $n
+       } else {
+           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)
+           doviewmenu .bar.view 7 [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
+           set viewargs($n) $newargs
+           if {$curview == $n} {
+               after idle updatecommits
+           }
+       }
+    }
+    catch {destroy $top}
 }
 
 proc delview {} {
 }
 
 proc delview {} {
-    global curview viewdata viewperm
+    global curview viewdata viewperm hlview selectedhlview
 
     if {$curview == 0} return
 
     if {$curview == 0} return
-    set nmenu [.bar.view index end]
-    set targetcmd [list showview $curview]
-    for {set i 5} {$i <= $nmenu} {incr i} {
-       if {[.bar.view entrycget $i -command] eq $targetcmd} {
-           .bar.view delete $i
-           break
-       }
+    if {[info exists hlview] && $hlview == $curview} {
+       set selectedhlview None
+       unset hlview
     }
     }
+    allviewmenus $curview delete
     set viewdata($curview) {}
     set viewperm($curview) 0
     showview 0
 }
 
     set viewdata($curview) {}
     set viewperm($curview) 0
     showview 0
 }
 
+proc addviewmenu {n} {
+    global viewname viewhlmenu
+
+    .bar.view add radiobutton -label $viewname($n) \
+       -command [list showview $n] -variable selectedview -value $n
+    $viewhlmenu add radiobutton -label $viewname($n) \
+       -command [list addvhighlight $n] -variable selectedhlview
+}
+
 proc flatten {var} {
     global $var
 
 proc flatten {var} {
     global $var
 
@@ -907,14 +1536,16 @@ proc unflatten {var l} {
 proc showview {n} {
     global curview viewdata viewfiles
     global displayorder parentlist childlist rowidlist rowoffsets
 proc showview {n} {
     global curview viewdata viewfiles
     global displayorder parentlist childlist rowidlist rowoffsets
-    global colormap rowtextx commitrow
+    global colormap rowtextx commitrow nextcolor canvxmax
     global numcommits rowrangelist commitlisted idrowranges
     global selectedline currentid canv canvy0
     global matchinglines treediffs
     global pending_select phase
     global numcommits rowrangelist commitlisted idrowranges
     global selectedline currentid canv canvy0
     global matchinglines treediffs
     global pending_select phase
-    global commitidx rowlaidout rowoptim linesegends leftover
+    global commitidx rowlaidout rowoptim linesegends
     global commfd nextupdate
     global selectedview
     global commfd nextupdate
     global selectedview
+    global vparentlist vchildlist vdisporder vcmitlisted
+    global hlview selectedhlview
 
     if {$n == $curview} return
     set selid {}
 
     if {$n == $curview} return
     set selid {}
@@ -935,29 +1566,33 @@ proc showview {n} {
     normalline
     stopfindproc
     if {$curview >= 0} {
     normalline
     stopfindproc
     if {$curview >= 0} {
+       set vparentlist($curview) $parentlist
+       set vchildlist($curview) $childlist
+       set vdisporder($curview) $displayorder
+       set vcmitlisted($curview) $commitlisted
        if {$phase ne {}} {
            set viewdata($curview) \
        if {$phase ne {}} {
            set viewdata($curview) \
-               [list $phase $displayorder $parentlist $childlist $rowidlist \
-                    $rowoffsets $rowrangelist $commitlisted \
-                    [flatten children] [flatten idrowranges] \
-                    [flatten idinlist] \
-                    $commitidx $rowlaidout $rowoptim $numcommits \
-                    $linesegends $leftover $commfd]
-           fileevent $commfd readable {}
+               [list $phase $rowidlist $rowoffsets $rowrangelist \
+                    [flatten idrowranges] [flatten idinlist] \
+                    $rowlaidout $rowoptim $numcommits $linesegends]
        } elseif {![info exists viewdata($curview)]
                  || [lindex $viewdata($curview) 0] ne {}} {
            set viewdata($curview) \
        } elseif {![info exists viewdata($curview)]
                  || [lindex $viewdata($curview) 0] ne {}} {
            set viewdata($curview) \
-               [list {} $displayorder $parentlist $childlist $rowidlist \
-                    $rowoffsets $rowrangelist $commitlisted]
+               [list {} $rowidlist $rowoffsets $rowrangelist]
        }
     }
     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
     .bar.view entryconf 2 -state [expr {$n == 0? "disabled": "normal"}]
 
     set curview $n
     set selectedview $n
     .bar.view entryconf 2 -state [expr {$n == 0? "disabled": "normal"}]
+    .bar.view entryconf 3 -state [expr {$n == 0? "disabled": "normal"}]
 
     if {![info exists viewdata($n)]} {
        set pending_select $selid
 
     if {![info exists viewdata($n)]} {
        set pending_select $selid
@@ -967,46 +1602,36 @@ proc showview {n} {
 
     set v $viewdata($n)
     set phase [lindex $v 0]
 
     set v $viewdata($n)
     set phase [lindex $v 0]
-    set displayorder [lindex $v 1]
-    set parentlist [lindex $v 2]
-    set childlist [lindex $v 3]
-    set rowidlist [lindex $v 4]
-    set rowoffsets [lindex $v 5]
-    set rowrangelist [lindex $v 6]
-    set commitlisted [lindex $v 7]
+    set displayorder $vdisporder($n)
+    set parentlist $vparentlist($n)
+    set childlist $vchildlist($n)
+    set commitlisted $vcmitlisted($n)
+    set rowidlist [lindex $v 1]
+    set rowoffsets [lindex $v 2]
+    set rowrangelist [lindex $v 3]
     if {$phase eq {}} {
        set numcommits [llength $displayorder]
        catch {unset idrowranges}
     if {$phase eq {}} {
        set numcommits [llength $displayorder]
        catch {unset idrowranges}
-       catch {unset children}
     } else {
     } else {
-       unflatten children [lindex $v 8]
-       unflatten idrowranges [lindex $v 9]
-       unflatten idinlist [lindex $v 10]
-       set commitidx [lindex $v 11]
-       set rowlaidout [lindex $v 12]
-       set rowoptim [lindex $v 13]
-       set numcommits [lindex $v 14]
-       set linesegends [lindex $v 15]
-       set leftover [lindex $v 16]
-       set commfd [lindex $v 17]
-       fileevent $commfd readable [list getcommitlines $commfd]
-       set nextupdate [expr {[clock clicks -milliseconds] + 100}]
+       unflatten idrowranges [lindex $v 4]
+       unflatten idinlist [lindex $v 5]
+       set rowlaidout [lindex $v 6]
+       set rowoptim [lindex $v 7]
+       set numcommits [lindex $v 8]
+       set linesegends [lindex $v 9]
     }
 
     catch {unset colormap}
     catch {unset rowtextx}
     }
 
     catch {unset colormap}
     catch {unset rowtextx}
-    catch {unset commitrow}
+    set nextcolor 0
+    set canvxmax [$canv cget -width]
     set curview $n
     set row 0
     set curview $n
     set row 0
-    foreach id $displayorder {
-       set commitrow($id) $row
-       incr row
-    }
     setcanvscroll
     set yf 0
     set row 0
     setcanvscroll
     set yf 0
     set row 0
-    if {$selid ne {} && [info exists commitrow($selid)]} {
-       set row $commitrow($selid)
+    if {$selid ne {} && [info exists commitrow($n,$selid)]} {
+       set row $commitrow($n,$selid)
        # try to get the selected row in the same position on the screen
        set ymax [lindex [$canv cget -scrollregion] 3]
        set ytop [expr {[yc $row] - $yscreen}]
        # try to get the selected row in the same position on the screen
        set ymax [lindex [$canv cget -scrollregion] 3]
        set ytop [expr {[yc $row] - $yscreen}]
@@ -1018,16 +1643,520 @@ proc showview {n} {
     allcanvs yview moveto $yf
     drawvisible
     selectline $row 0
     allcanvs yview moveto $yf
     drawvisible
     selectline $row 0
-    if {$phase eq {}} {
-       global maincursor textcursor
-       . config -cursor $maincursor
-       settextcursor $textcursor
+    if {$phase ne {}} {
+       if {$phase eq "getcommits"} {
+           show_status "Reading commits..."
+       }
+       if {[info exists commfd($n)]} {
+           layoutmore
+       } else {
+           finishcommits
+       }
+    } elseif {$numcommits == 0} {
+       show_status "No commits selected"
+    }
+}
+
+# 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]} {
+       delvhighlight
+    }
+    set hlview $n
+    if {$n != $curview && ![info exists viewdata($n)]} {
+       set viewdata($n) [list getcommits {{}} {{}} {} {} {} 0 0 0 {}]
+       set vparentlist($n) {}
+       set vchildlist($n) {}
+       set vdisporder($n) {}
+       set vcmitlisted($n) {}
+       start_rev_list $n
+    }
+    set vhl_done $commitidx($hlview)
+    if {$vhl_done > 0} {
+       drawvisible
+    }
+}
+
+proc delvhighlight {} {
+    global hlview vhighlights
+
+    if {![info exists hlview]} return
+    unset hlview
+    catch {unset vhighlights}
+    unbolden
+}
+
+proc vhighlightmore {} {
+    global hlview vhl_done commitidx vhighlights
+    global displayorder vdisporder curview mainfont
+
+    set font [concat $mainfont bold]
+    set max $commitidx($hlview)
+    if {$hlview == $curview} {
+       set disp $displayorder
     } else {
     } else {
-       . config -cursor watch
-       settextcursor watch
+       set disp $vdisporder($hlview)
+    }
+    set vr [visiblerows]
+    set r0 [lindex $vr 0]
+    set r1 [lindex $vr 1]
+    for {set i $vhl_done} {$i < $max} {incr i} {
+       set id [lindex $disp $i]
+       if {[info exists commitrow($curview,$id)]} {
+           set row $commitrow($curview,$id)
+           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 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} {
     set res {}
     foreach id $ids {
 proc shortids {ids} {
     set res {}
     foreach id $ids {
@@ -1063,19 +2192,19 @@ proc ntimes {n o} {
 }
 
 proc usedinrange {id l1 l2} {
 }
 
 proc usedinrange {id l1 l2} {
-    global children commitrow childlist
+    global children commitrow childlist curview
 
 
-    if {[info exists commitrow($id)]} {
-       set r $commitrow($id)
+    if {[info exists commitrow($curview,$id)]} {
+       set r $commitrow($curview,$id)
        if {$l1 <= $r && $r <= $l2} {
            return [expr {$r - $l1 + 1}]
        }
        set kids [lindex $childlist $r]
     } else {
        if {$l1 <= $r && $r <= $l2} {
            return [expr {$r - $l1 + 1}]
        }
        set kids [lindex $childlist $r]
     } else {
-       set kids $children($id)
+       set kids $children($curview,$id)
     }
     foreach c $kids {
     }
     foreach c $kids {
-       set r $commitrow($c)
+       set r $commitrow($curview,$c)
        if {$l1 <= $r && $r <= $l2} {
            return [expr {$r - $l1 + 1}]
        }
        if {$l1 <= $r && $r <= $l2} {
            return [expr {$r - $l1 + 1}]
        }
@@ -1146,20 +2275,18 @@ proc initlayout {} {
     global rowidlist rowoffsets displayorder commitlisted
     global rowlaidout rowoptim
     global idinlist rowchk rowrangelist idrowranges
     global rowidlist rowoffsets displayorder commitlisted
     global rowlaidout rowoptim
     global idinlist rowchk rowrangelist idrowranges
-    global commitidx numcommits canvxmax canv
+    global numcommits canvxmax canv
     global nextcolor
     global parentlist childlist children
     global nextcolor
     global parentlist childlist children
-    global colormap rowtextx commitrow
+    global colormap rowtextx
     global linesegends
 
     global linesegends
 
-    set commitidx 0
     set numcommits 0
     set displayorder {}
     set commitlisted {}
     set parentlist {}
     set childlist {}
     set rowrangelist {}
     set numcommits 0
     set displayorder {}
     set commitlisted {}
     set parentlist {}
     set childlist {}
     set rowrangelist {}
-    catch {unset children}
     set nextcolor 0
     set rowidlist {{}}
     set rowoffsets {{}}
     set nextcolor 0
     set rowidlist {{}}
     set rowoffsets {{}}
@@ -1170,7 +2297,6 @@ proc initlayout {} {
     set canvxmax [$canv cget -width]
     catch {unset colormap}
     catch {unset rowtextx}
     set canvxmax [$canv cget -width]
     catch {unset colormap}
     catch {unset rowtextx}
-    catch {unset commitrow}
     catch {unset idrowranges}
     set linesegends {}
 }
     catch {unset idrowranges}
     set linesegends {}
 }
@@ -1205,10 +2331,10 @@ proc visiblerows {} {
 
 proc layoutmore {} {
     global rowlaidout rowoptim commitidx numcommits optim_delay
 
 proc layoutmore {} {
     global rowlaidout rowoptim commitidx numcommits optim_delay
-    global uparrowlen
+    global uparrowlen curview
 
     set row $rowlaidout
 
     set row $rowlaidout
-    set rowlaidout [layoutrows $row $commitidx 0]
+    set rowlaidout [layoutrows $row $commitidx($curview) 0]
     set orow [expr {$rowlaidout - $uparrowlen - 1}]
     if {$orow > $rowoptim} {
        optimize_rows $rowoptim 0 $orow
     set orow [expr {$rowlaidout - $uparrowlen - 1}]
     if {$orow > $rowoptim} {
        optimize_rows $rowoptim 0 $orow
@@ -1222,7 +2348,7 @@ proc layoutmore {} {
 
 proc showstuff {canshow} {
     global numcommits commitrow pending_select selectedline
 
 proc showstuff {canshow} {
     global numcommits commitrow pending_select selectedline
-    global linesegends idrowranges idrangedrawn
+    global linesegends idrowranges idrangedrawn curview
 
     if {$numcommits == 0} {
        global phase
 
     if {$numcommits == 0} {
        global phase
@@ -1257,9 +2383,9 @@ proc showstuff {canshow} {
        incr row
     }
     if {[info exists pending_select] &&
        incr row
     }
     if {[info exists pending_select] &&
-       [info exists commitrow($pending_select)] &&
-       $commitrow($pending_select) < $numcommits} {
-       selectline $commitrow($pending_select) 1
+       [info exists commitrow($curview,$pending_select)] &&
+       $commitrow($curview,$pending_select) < $numcommits} {
+       selectline $commitrow($curview,$pending_select) 1
     }
     if {![info exists selectedline] && ![info exists pending_select]} {
        selectline 0 1
     }
     if {![info exists selectedline] && ![info exists pending_select]} {
        selectline 0 1
@@ -1271,7 +2397,7 @@ proc layoutrows {row endrow last} {
     global uparrowlen downarrowlen maxwidth mingaplen
     global childlist parentlist
     global idrowranges linesegends
     global uparrowlen downarrowlen maxwidth mingaplen
     global childlist parentlist
     global idrowranges linesegends
-    global commitidx
+    global commitidx curview
     global idinlist rowchk rowrangelist
 
     set idlist [lindex $rowidlist $row]
     global idinlist rowchk rowrangelist
 
     set idlist [lindex $rowidlist $row]
@@ -1291,7 +2417,8 @@ proc layoutrows {row endrow last} {
        set nev [expr {[llength $idlist] + [llength $newolds]
                       + [llength $oldolds] - $maxwidth + 1}]
        if {$nev > 0} {
        set nev [expr {[llength $idlist] + [llength $newolds]
                       + [llength $oldolds] - $maxwidth + 1}]
        if {$nev > 0} {
-           if {!$last && $row + $uparrowlen + $mingaplen >= $commitidx} break
+           if {!$last &&
+               $row + $uparrowlen + $mingaplen >= $commitidx($curview)} break
            for {set x [llength $idlist]} {[incr x -1] >= 0} {} {
                set i [lindex $idlist $x]
                if {![info exists rowchk($i)] || $row >= $rowchk($i)} {
            for {set x [llength $idlist]} {[incr x -1] >= 0} {} {
                set i [lindex $idlist $x]
                if {![info exists rowchk($i)] || $row >= $rowchk($i)} {
@@ -1380,30 +2507,28 @@ proc layoutrows {row endrow last} {
 proc addextraid {id row} {
     global displayorder commitrow commitinfo
     global commitidx commitlisted
 proc addextraid {id row} {
     global displayorder commitrow commitinfo
     global commitidx commitlisted
-    global parentlist childlist children
+    global parentlist childlist children curview
 
 
-    incr commitidx
+    incr commitidx($curview)
     lappend displayorder $id
     lappend commitlisted 0
     lappend parentlist {}
     lappend displayorder $id
     lappend commitlisted 0
     lappend parentlist {}
-    set commitrow($id) $row
+    set commitrow($curview,$id) $row
     readcommit $id
     if {![info exists commitinfo($id)]} {
        set commitinfo($id) {"No commit information available"}
     }
     readcommit $id
     if {![info exists commitinfo($id)]} {
        set commitinfo($id) {"No commit information available"}
     }
-    if {[info exists children($id)]} {
-       lappend childlist $children($id)
-       unset children($id)
-    } else {
-       lappend childlist {}
+    if {![info exists children($curview,$id)]} {
+       set children($curview,$id) {}
     }
     }
+    lappend childlist $children($curview,$id)
 }
 
 proc layouttail {} {
 }
 
 proc layouttail {} {
-    global rowidlist rowoffsets idinlist commitidx
+    global rowidlist rowoffsets idinlist commitidx curview
     global idrowranges rowrangelist
 
     global idrowranges rowrangelist
 
-    set row $commitidx
+    set row $commitidx($curview)
     set idlist [lindex $rowidlist $row]
     while {$idlist ne {}} {
        set col [expr {[llength $idlist] - 1}]
     set idlist [lindex $rowidlist $row]
     while {$idlist ne {}} {
        set col [expr {[llength $idlist] - 1}]
@@ -1578,12 +2703,13 @@ proc linewidth {id} {
 }
 
 proc rowranges {id} {
 }
 
 proc rowranges {id} {
-    global phase idrowranges commitrow rowlaidout rowrangelist
+    global phase idrowranges commitrow rowlaidout rowrangelist curview
 
     set ranges {}
     if {$phase eq {} ||
 
     set ranges {}
     if {$phase eq {} ||
-       ([info exists commitrow($id)] && $commitrow($id) < $rowlaidout)} {
-       set ranges [lindex $rowrangelist $commitrow($id)]
+       ([info exists commitrow($curview,$id)]
+        && $commitrow($curview,$id) < $rowlaidout)} {
+       set ranges [lindex $rowrangelist $commitrow($curview,$id)]
     } elseif {[info exists idrowranges($id)]} {
        set ranges $idrowranges($id)
     }
     } elseif {[info exists idrowranges($id)]} {
        set ranges $idrowranges($id)
     }
@@ -1594,11 +2720,12 @@ proc drawlineseg {id i} {
     global rowoffsets rowidlist
     global displayorder
     global canv colormap linespc
     global rowoffsets rowidlist
     global displayorder
     global canv colormap linespc
-    global numcommits commitrow
+    global numcommits commitrow curview
 
     set ranges [rowranges $id]
     set downarrow 1
 
     set ranges [rowranges $id]
     set downarrow 1
-    if {[info exists commitrow($id)] && $commitrow($id) < $numcommits} {
+    if {[info exists commitrow($curview,$id)]
+       && $commitrow($curview,$id) < $numcommits} {
        set downarrow [expr {$i < [llength $ranges] / 2 - 1}]
     } else {
        set downarrow 1
        set downarrow [expr {$i < [llength $ranges] / 2 - 1}]
     } else {
        set downarrow 1
@@ -1722,7 +2849,7 @@ proc drawparentlinks {id row col olds} {
 proc drawlines {id} {
     global colormap canv
     global idrangedrawn
 proc drawlines {id} {
     global colormap canv
     global idrangedrawn
-    global childlist iddrawn commitrow rowidlist
+    global children iddrawn commitrow rowidlist curview
 
     $canv delete lines.$id
     set nr [expr {[llength [rowranges $id]] / 2}]
 
     $canv delete lines.$id
     set nr [expr {[llength [rowranges $id]] / 2}]
@@ -1731,9 +2858,9 @@ proc drawlines {id} {
            drawlineseg $id $i
        }
     }
            drawlineseg $id $i
        }
     }
-    foreach child [lindex $childlist $commitrow($id)] {
+    foreach child $children($curview,$id) {
        if {[info exists iddrawn($child)]} {
        if {[info exists iddrawn($child)]} {
-           set row $commitrow($child)
+           set row $commitrow($curview,$child)
            set col [lsearch -exact [lindex $rowidlist $row] $child]
            if {$col >= 0} {
                drawparentlinks $child $row $col [list $id]
            set col [lsearch -exact [lindex $rowidlist $row] $child]
            if {$col >= 0} {
                drawparentlinks $child $row $col [list $id]
@@ -1747,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 namefont 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]
@@ -1772,11 +2899,22 @@ proc drawcmittext {id row col rmx} {
     set name [lindex $commitinfo($id) 1]
     set date [lindex $commitinfo($id) 2]
     set date [formatdate $date]
     set name [lindex $commitinfo($id) 1]
     set date [lindex $commitinfo($id) 2]
     set date [formatdate $date]
+    set font $mainfont
+    set nfont $mainfont
+    set isbold [ishighlighted $row]
+    if {$isbold > 0} {
+       lappend boldrows $row
+       lappend font bold
+       if {$isbold > 1} {
+           lappend boldnamerows $row
+           lappend nfont bold
+       }
+    }
     set linehtag($row) [$canv create text $xt $y -anchor w \
     set linehtag($row) [$canv create text $xt $y -anchor w \
-                           -text $headline -font $mainfont ]
+                           -text $headline -font $font]
     $canv bind $linehtag($row) <Button-3> "rowmenu %X %Y $id"
     set linentag($row) [$canv2 create text 3 $y -anchor w \
     $canv bind $linehtag($row) <Button-3> "rowmenu %X %Y $id"
     set linentag($row) [$canv2 create text 3 $y -anchor w \
-                           -text $name -font $namefont]
+                           -text $name -font $nfont]
     set linedtag($row) [$canv3 create text 3 $y -anchor w \
                            -text $date -font $mainfont]
     set xr [expr {$xt + [font measure $mainfont $headline]}]
     set linedtag($row) [$canv3 create text 3 $y -anchor w \
                            -text $date -font $mainfont]
     set xr [expr {$xt + [font measure $mainfont $headline]}]
@@ -1790,6 +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 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] {
@@ -1810,6 +2951,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} {
@@ -1858,10 +3011,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} {
@@ -1907,21 +3065,19 @@ proc findcrossings {id} {
 
 proc assigncolor {id} {
     global colormap colors nextcolor
 
 proc assigncolor {id} {
     global colormap colors nextcolor
-    global commitrow parentlist children childlist
+    global commitrow parentlist children children curview
 
     if {[info exists colormap($id)]} return
     set ncolors [llength $colors]
 
     if {[info exists colormap($id)]} return
     set ncolors [llength $colors]
-    if {[info exists commitrow($id)]} {
-       set kids [lindex $childlist $commitrow($id)]
-    } elseif {[info exists children($id)]} {
-       set kids $children($id)
+    if {[info exists children($curview,$id)]} {
+       set kids $children($curview,$id)
     } else {
        set kids {}
     }
     if {[llength $kids] == 1} {
        set child [lindex $kids 0]
        if {[info exists colormap($child)]
     } else {
        set kids {}
     }
     if {[llength $kids] == 1} {
        set child [lindex $kids 0]
        if {[info exists colormap($child)]
-           && [llength [lindex $parentlist $commitrow($child)]] == 1} {
+           && [llength [lindex $parentlist $commitrow($curview,$child)]] == 1} {
            set colormap($id) $colormap($child)
            return
        }
            set colormap($id) $colormap($child)
            return
        }
@@ -1949,7 +3105,7 @@ proc assigncolor {id} {
                && [lsearch -exact $badcolors $colormap($child)] < 0} {
                lappend badcolors $colormap($child)
            }
                && [lsearch -exact $badcolors $colormap($child)] < 0} {
                lappend badcolors $colormap($child)
            }
-           foreach p [lindex $parentlist $commitrow($child)] {
+           foreach p [lindex $parentlist $commitrow($curview,$child)] {
                if {[info exists colormap($p)]
                    && [lsearch -exact $badcolors $colormap($p)] < 0} {
                    lappend badcolors $colormap($p)
                if {[info exists colormap($p)]
                    && [lsearch -exact $badcolors $colormap($p)] < 0} {
                    lappend badcolors $colormap($p)
@@ -1982,7 +3138,7 @@ proc bindline {t id} {
 proc drawtags {id x xt y1} {
     global idtags idheads idotherrefs
     global linespc lthickness
 proc drawtags {id x xt y1} {
     global idtags idheads idotherrefs
     global linespc lthickness
-    global canv mainfont commitrow rowtextx
+    global canv mainfont commitrow rowtextx curview
 
     set marks {}
     set ntags 0
 
     set marks {}
     set ntags 0
@@ -2025,7 +3181,7 @@ proc drawtags {id x xt y1} {
                       $xr $yt $xr $yb $xl $yb $x [expr {$yb - $delta}] \
                       -width 1 -outline black -fill yellow -tags tag.$id]
            $canv bind $t <1> [list showtag $tag 1]
                       $xr $yt $xr $yb $xl $yb $x [expr {$yb - $delta}] \
                       -width 1 -outline black -fill yellow -tags tag.$id]
            $canv bind $t <1> [list showtag $tag 1]
-           set rowtextx($commitrow($id)) [expr {$xr + $linespc}]
+           set rowtextx($commitrow($curview,$id)) [expr {$xr + $linespc}]
        } else {
            # draw a head or other ref
            if {[incr nheads -1] >= 0} {
        } else {
            # draw a head or other ref
            if {[incr nheads -1] >= 0} {
@@ -2036,6 +3192,14 @@ proc drawtags {id x xt y1} {
            set xl [expr {$xl - $delta/2}]
            $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
                -width 1 -outline black -fill $col -tags tag.$id
            set xl [expr {$xl - $delta/2}]
            $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
                -width 1 -outline black -fill $col -tags tag.$id
+           if {[regexp {^(remotes/.*/|remotes/)} $tag match remoteprefix]} {
+               set rwid [font measure $mainfont $remoteprefix]
+               set xi [expr {$x + 1}]
+               set yti [expr {$yt + 1}]
+               set xri [expr {$x + $rwid}]
+               $canv create polygon $xi $yti $xri $yti $xri $yb $xi $yb \
+                       -width 0 -fill "#ffddaa" -tags tag.$id
+           }
        }
        set t [$canv create text $xl $y1 -anchor w -text $tag \
                   -font $mainfont -tags tag.$id]
        }
        set t [$canv create text $xl $y1 -anchor w -text $tag \
                   -font $mainfont -tags tag.$id]
@@ -2058,21 +3222,22 @@ proc xcoord {i level ln} {
     return $x
 }
 
     return $x
 }
 
+proc show_status {msg} {
+    global canv mainfont
+
+    clear_display
+    $canv create text 3 3 -anchor nw -text $msg -font $mainfont -tags textitems
+}
+
 proc finishcommits {} {
 proc finishcommits {} {
-    global commitidx phase
+    global commitidx phase curview
     global canv mainfont ctext maincursor textcursor
     global findinprogress pending_select
 
     global canv mainfont ctext maincursor textcursor
     global findinprogress pending_select
 
-    if {$commitidx > 0} {
+    if {$commitidx($curview) > 0} {
        drawrest
     } else {
        drawrest
     } else {
-       $canv delete all
-       $canv create text 3 3 -anchor nw -text "No commits selected" \
-           -font $mainfont -tags textitems
-    }
-    if {![info exists findinprogress]} {
-       . config -cursor $maincursor
-       settextcursor $textcursor
+       show_status "No commits selected"
     }
     set phase {}
     catch {unset pending_select}
     }
     set phase {}
     catch {unset pending_select}
@@ -2089,18 +3254,38 @@ proc settextcursor {c} {
     set curtextcursor $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 {} {
     global numcommits
     global startmsecs
     global canvy0 numcommits linespc
 proc drawrest {} {
     global numcommits
     global startmsecs
     global canvy0 numcommits linespc
-    global rowlaidout commitidx
+    global rowlaidout commitidx curview
     global pending_select
 
     set row $rowlaidout
     global pending_select
 
     set row $rowlaidout
-    layoutrows $rowlaidout $commitidx 1
+    layoutrows $rowlaidout $commitidx($curview) 1
     layouttail
     layouttail
-    optimize_rows $row 0 $commitidx
-    showstuff $commitidx
+    optimize_rows $row 0 $commitidx($curview)
+    showstuff $commitidx($curview)
     if {[info exists pending_select]} {
        selectline 0 1
     }
     if {[info exists pending_select]} {
        selectline 0 1
     }
@@ -2132,18 +3317,15 @@ proc findmatches {f} {
 proc dofind {} {
     global findtype findloc findstring markedmatches commitinfo
     global numcommits displayorder linehtag linentag linedtag
 proc dofind {} {
     global findtype findloc findstring markedmatches commitinfo
     global numcommits displayorder linehtag linentag linedtag
-    global mainfont namefont canv canv2 canv3 selectedline
+    global mainfont canv canv2 canv3 selectedline
     global matchinglines foundstring foundstrlen matchstring
     global commitdata
 
     stopfindproc
     unmarkmatches
     global matchinglines foundstring foundstrlen matchstring
     global commitdata
 
     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 {
@@ -2153,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)
@@ -2193,7 +3371,7 @@ proc dofind {} {
                markmatches $canv $l $f $linehtag($l) $matches $mainfont
            } elseif {$ty == "Author"} {
                drawcmitrow $l
                markmatches $canv $l $f $linehtag($l) $matches $mainfont
            } elseif {$ty == "Author"} {
                drawcmitrow $l
-               markmatches $canv2 $l $f $linentag($l) $matches $namefont
+               markmatches $canv2 $l $f $linentag($l) $matches $mainfont
            } elseif {$ty == "Date"} {
                drawcmitrow $l
                markmatches $canv3 $l $f $linedtag($l) $matches $mainfont
            } elseif {$ty == "Date"} {
                drawcmitrow $l
                markmatches $canv3 $l $f $linedtag($l) $matches $mainfont
@@ -2240,307 +3418,47 @@ proc findnext {restart} {
     if {![info exists selectedline]} return
     foreach l $matchinglines {
        if {$l > $selectedline} {
     if {![info exists selectedline]} return
     foreach l $matchinglines {
        if {$l > $selectedline} {
-           findselectline $l
-           return
-       }
-    }
-    bell
-}
-
-proc findprev {} {
-    global matchinglines selectedline
-    if {![info exists matchinglines]} {
-       dofind
-       return
-    }
-    if {![info exists selectedline]} return
-    set prev {}
-    foreach l $matchinglines {
-       if {$l >= $selectedline} break
-       set prev $l
-    }
-    if {$prev != {}} {
-       findselectline $prev
-    } else {
-       bell
-    }
-}
-
-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
-    global findinprogress
-
-    catch {unset findids}
-    if {[info exists findprocpid]} {
-       if {!$done} {
-           catch {exec kill $findprocpid}
-       }
-       catch {close $findprocfile}
-       unset findprocpid
-    }
-    if {[info exists findinprogress]} {
-       unset findinprogress
-       if {$phase eq {}} {
-           . config -cursor $maincursor
-           settextcursor $textcursor
-       }
-    }
-}
-
-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
-    . config -cursor watch
-    settextcursor watch
-    set findinprogress 1
-}
-
-proc readfindproc {} {
-    global findprocfile finddidsel
-    global commitrow matchinglines findinsertpos
-
-    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($id)]} {
-       puts stderr "spurious id: $id"
-       return
-    }
-    set l $commitrow($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"
+           findselectline $l
            return
        }
            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]
-    . config -cursor watch
-    settextcursor watch
-    set findinprogress 1
-    findcont
-    update
+    bell
 }
 
 }
 
-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"
-           }
-       }
+proc findprev {} {
+    global matchinglines selectedline
+    if {![info exists matchinglines]} {
+       dofind
        return
     }
        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
-       }
+    if {![info exists selectedline]} return
+    set prev {}
+    foreach l $matchinglines {
+       if {$l >= $selectedline} break
+       set prev $l
+    }
+    if {$prev != {}} {
+       findselectline $prev
+    } else {
+       bell
     }
 }
 
     }
 }
 
-proc findcont {} {
-    global findid treediffs parentlist
-    global ffileline findstartline finddidsel
-    global displayorder numcommits matchinglines findinprogress
-    global findmergefiles
+proc stopfindproc {{done 0}} {
+    global findprocpid findprocfile findids
+    global ctext findoldcursor phase maincursor textcursor
+    global findinprogress
 
 
-    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
+    catch {unset findids}
+    if {[info exists findprocpid]} {
+       if {!$done} {
+           catch {exec kill $findprocpid}
        }
        }
-       if {$l == $findstartline} break
-    }
-    stopfindproc
-    if {!$finddidsel} {
-       bell
+       catch {close $findprocfile}
+       unset findprocpid
     }
     }
+    catch {unset findinprogress}
+    notbusy find
 }
 
 # mark a commit as matching by putting a yellow background
 }
 
 # mark a commit as matching by putting a yellow background
@@ -2600,31 +3518,34 @@ proc selcanvline {w x y} {
 
 proc commit_descriptor {p} {
     global commitinfo
 
 proc commit_descriptor {p} {
     global commitinfo
+    if {![info exists commitinfo($p)]} {
+       getcommit $p
+    }
     set l "..."
     set l "..."
-    if {[info exists commitinfo($p)]} {
+    if {[llength $commitinfo($p)] > 1} {
        set l [lindex $commitinfo($p) 0]
     }
        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} {
-    global ctext commitrow linknum
+proc appendwithlinks {text tags} {
+    global ctext commitrow linknum curview
 
     set start [$ctext index "end - 1c"]
 
     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 e [lindex $l 1]
        set linkid [string range $text $s $e]
     set links [regexp -indices -all -inline {[0-9a-f]{40}} $text]
     foreach l $links {
        set s [lindex $l 0]
        set e [lindex $l 1]
        set linkid [string range $text $s $e]
-       if {![info exists commitrow($linkid)]} continue
+       if {![info exists commitrow($curview,$linkid)]} continue
        incr e
        $ctext tag add link "$start + $s c" "$start + $e c"
        $ctext tag add link$linknum "$start + $s c" "$start + $e c"
        incr e
        $ctext tag add link "$start + $s c" "$start + $e c"
        $ctext tag add link$linknum "$start + $s c" "$start + $e c"
-       $ctext tag bind link$linknum <1> [list selectline $commitrow($linkid) 1]
+       $ctext tag bind link$linknum <1> \
+           [list selectline $commitrow($curview,$linkid) 1]
        incr linknum
     }
     $ctext tag conf link -foreground blue -underline 1
        incr linknum
     }
     $ctext tag conf link -foreground blue -underline 1
@@ -2648,17 +3569,77 @@ 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
     global canvy0 linespc parentlist childlist
 proc selectline {l isnew} {
     global canv canv2 canv3 ctext commitinfo selectedline
     global displayorder linehtag linentag linedtag
     global canvy0 linespc parentlist childlist
-    global cflist currentid sha1entry
+    global currentid sha1entry
     global commentend idtags linknum
     global mergemax numcommits pending_select
     global commentend idtags linknum
     global mergemax numcommits pending_select
+    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]
@@ -2722,12 +3703,11 @@ 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 linknum 0
-    $ctext mark set fmark.0 0.0
-    $ctext mark gravity fmark.0 left
     set info $commitinfo($id)
     set date [formatdate [lindex $info 2]]
     $ctext insert end "Author: [lindex $info 1]  $date\n"
     set info $commitinfo($id)
     set date [formatdate [lindex $info 2]]
     $ctext insert end "Author: [lindex $info 1]  $date\n"
@@ -2741,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
@@ -2752,32 +3732,60 @@ 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 conf -state disabled
     set commentend [$ctext index "end - 1c"]
 
 
     $ctext tag delete Comments
     $ctext tag remove found 1.0 end
     $ctext conf -state disabled
     set commentend [$ctext index "end - 1c"]
 
-    $cflist delete 0 end
-    $cflist insert end "Comments"
-    if {[llength $olds] <= 1} {
+    init_flist "Comments"
+    if {$cmitmode eq "tree"} {
+       gettree $id
+    } elseif {[llength $olds] <= 1} {
        startdiff $id
     } else {
        mergediff $id $l
        startdiff $id
     } else {
        mergediff $id $l
@@ -2812,6 +3820,7 @@ proc selnextpage {dir} {
        set lpp 1
     }
     allcanvs yview scroll [expr {$dir * $lpp}] units
        set lpp 1
     }
     allcanvs yview scroll [expr {$dir * $lpp}] units
+    drawvisible
     if {![info exists selectedline]} return
     set l [expr {$selectedline + $dir * $lpp}]
     if {$l < 0} {
     if {![info exists selectedline]} return
     set l [expr {$selectedline + $dir * $lpp}]
     if {$l < 0} {
@@ -2829,6 +3838,16 @@ 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 {} {
+    global selectedline
+
+    if {[info exists selectedline]} {
+       selectline $selectedline 0
+    }
 }
 
 proc addtohistory {cmd} {
 }
 
 proc addtohistory {cmd} {
@@ -2892,17 +3911,104 @@ proc goforw {} {
     }
 }
 
     }
 }
 
+proc gettree {id} {
+    global treefilelist treeidlist diffids diffmergeid treepending
+
+    set diffids $id
+    catch {unset diffmergeid}
+    if {![info exists treefilelist($id)]} {
+       if {![info exists treepending]} {
+           if {[catch {set gtf [open [concat | git ls-tree -r $id] r]}]} {
+               return
+           }
+           set treepending $id
+           set treefilelist($id) {}
+           set treeidlist($id) {}
+           fconfigure $gtf -blocking 0
+           fileevent $gtf readable [list gettreeline $gtf $id]
+       }
+    } else {
+       setfilelist $id
+    }
+}
+
+proc gettreeline {gtf id} {
+    global treefilelist treeidlist treepending cmitmode diffids
+
+    while {[gets $gtf line] >= 0} {
+       if {[lindex $line 1] ne "blob"} continue
+       set sha1 [lindex $line 2]
+       set fname [lindex $line 3]
+       lappend treefilelist($id) $fname
+       lappend treeidlist($id) $sha1
+    }
+    if {![eof $gtf]} return
+    close $gtf
+    unset treepending
+    if {$cmitmode ne "tree"} {
+       if {![info exists diffmergeid]} {
+           gettreediffs $diffids
+       }
+    } elseif {$id ne $diffids} {
+       gettree $diffids
+    } else {
+       setfilelist $id
+    }
+}
+
+proc showfile {f} {
+    global treefilelist treeidlist diffids
+    global ctext commentend
+
+    set i [lsearch -exact $treefilelist($diffids) $f]
+    if {$i < 0} {
+       puts "oops, $f not in list for id $diffids"
+       return
+    }
+    set blob [lindex $treeidlist($diffids) $i]
+    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
+    clear_ctext $commentend
+    $ctext insert end "\n"
+    $ctext insert end "$f\n" filesep
+    $ctext config -state disabled
+    $ctext yview $commentend
+}
+
+proc getblobline {bf id} {
+    global diffids cmitmode ctext
+
+    if {$id ne $diffids || $cmitmode ne "tree"} {
+       catch {close $bf}
+       return
+    }
+    $ctext config -state normal
+    while {[gets $bf line] >= 0} {
+       $ctext insert end "$line\n"
+    }
+    if {[eof $bf]} {
+       # delete last newline
+       $ctext delete "end - 2c" "end - 1c"
+       close $bf
+    }
+    $ctext config -state disabled
+}
+
 proc mergediff {id l} {
     global diffmergeid diffopts mdifffd
 proc mergediff {id l} {
     global diffmergeid diffopts mdifffd
-    global difffilestart diffids
+    global diffids
     global parentlist
 
     set diffmergeid $id
     set diffids $id
     global parentlist
 
     set diffmergeid $id
     set diffids $id
-    catch {unset difffilestart}
     # this doesn't seem to actually affect anything...
     set env(GIT_DIFF_OPTS) $diffopts
     # 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
@@ -2934,11 +4040,8 @@ proc getmergediffline {mdf id np} {
        # start of a new file
        $ctext insert end "\n"
        set here [$ctext index "end - 1c"]
        # start of a new file
        $ctext insert end "\n"
        set here [$ctext index "end - 1c"]
-       set i [$cflist index end]
-       $ctext mark set fmark.$i $here
-       $ctext mark gravity fmark.$i left
-       set difffilestart([expr {$i-1}]) $here
-       $cflist insert end $fname
+       lappend difffilestart $here
+       add_flist [list $fname]
        set l [expr {(78 - [string length $fname]) / 2}]
        set pad [string range "----------------------------------------" 1 $l]
        $ctext insert end "$pad $fname $pad\n" filesep
        set l [expr {(78 - [string length $fname]) / 2}]
        set pad [string range "----------------------------------------" 1 $l]
        $ctext insert end "$pad $fname $pad\n" filesep
@@ -3008,9 +4111,7 @@ proc startdiff {ids} {
 
 proc addtocflist {ids} {
     global treediffs cflist
 
 proc addtocflist {ids} {
     global treediffs cflist
-    foreach f $treediffs($ids) {
-       $cflist insert end $f
-    }
+    add_flist $treediffs($ids)
     getblobdiffs $ids
 }
 
     getblobdiffs $ids
 }
 
@@ -3019,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]
@@ -3027,6 +4128,7 @@ proc gettreediffs {ids} {
 
 proc gettreediffline {gdtf ids} {
     global treediff treediffs treepending diffids diffmergeid
 
 proc gettreediffline {gdtf ids} {
     global treediff treediffs treepending diffids diffmergeid
+    global cmitmode
 
     set n [gets $gdtf line]
     if {$n < 0} {
 
     set n [gets $gdtf line]
     if {$n < 0} {
@@ -3034,7 +4136,9 @@ proc gettreediffline {gdtf ids} {
        close $gdtf
        set treediffs($ids) $treediff
        unset treepending
        close $gdtf
        set treediffs($ids) $treediff
        unset treepending
-       if {$ids != $diffids} {
+       if {$cmitmode eq "tree"} {
+           gettree $diffids
+       } elseif {$ids != $diffids} {
            if {![info exists diffmergeid]} {
                gettreediffs $diffids
            }
            if {![info exists diffmergeid]} {
                gettreediffs $diffids
            }
@@ -3049,10 +4153,10 @@ proc gettreediffline {gdtf ids} {
 
 proc getblobdiffs {ids} {
     global diffopts blobdifffd diffids env curdifftag curtagstart
 
 proc getblobdiffs {ids} {
     global diffopts blobdifffd diffids env curdifftag curtagstart
-    global difffilestart nextupdate diffinhdr treediffs
+    global nextupdate diffinhdr treediffs
 
     set env(GIT_DIFF_OPTS) $diffopts
 
     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
@@ -3062,11 +4166,23 @@ proc getblobdiffs {ids} {
     set blobdifffd($ids) $bdf
     set curdifftag Comments
     set curtagstart 0.0
     set blobdifffd($ids) $bdf
     set curdifftag Comments
     set curtagstart 0.0
-    catch {unset difffilestart}
     fileevent $bdf readable [list getblobdiffline $bdf $diffids]
     set nextupdate [expr {[clock clicks -milliseconds] + 100}]
 }
 
     fileevent $bdf readable [list getblobdiffline $bdf $diffids]
     set nextupdate [expr {[clock clicks -milliseconds] + 100}]
 }
 
+proc setinlist {var i val} {
+    global $var
+
+    while {[llength [set $var]] < $i} {
+       lappend $var {}
+    }
+    if {[llength [set $var]] == $i} {
+       lappend $var $val
+    } else {
+       lset $var $i $val
+    }
+}
+
 proc getblobdiffline {bdf ids} {
     global diffids blobdifffd ctext curdifftag curtagstart
     global diffnexthead diffnextnote difffilestart
 proc getblobdiffline {bdf ids} {
     global diffids blobdifffd ctext curdifftag curtagstart
     global diffnexthead diffnextnote difffilestart
@@ -3090,23 +4206,17 @@ proc getblobdiffline {bdf ids} {
        # start of a new file
        $ctext insert end "\n"
        $ctext tag add $curdifftag $curtagstart end
        # start of a new file
        $ctext insert end "\n"
        $ctext tag add $curdifftag $curtagstart end
-       set curtagstart [$ctext index "end - 1c"]
-       set header $newname
        set here [$ctext index "end - 1c"]
        set here [$ctext index "end - 1c"]
-       set i [lsearch -exact $treediffs($diffids) $fname]
+       set curtagstart $here
+       set header $newname
+       set i [lsearch -exact $treediffs($ids) $fname]
        if {$i >= 0} {
        if {$i >= 0} {
-           set difffilestart($i) $here
-           incr i
-           $ctext mark set fmark.$i $here
-           $ctext mark gravity fmark.$i left
+           setinlist difffilestart $i $here
        }
        }
-       if {$newname != $fname} {
-           set i [lsearch -exact $treediffs($diffids) $newname]
+       if {$newname ne $fname} {
+           set i [lsearch -exact $treediffs($ids) $newname]
            if {$i >= 0} {
            if {$i >= 0} {
-               set difffilestart($i) $here
-               incr i
-               $ctext mark set fmark.$i $here
-               $ctext mark gravity fmark.$i left
+               setinlist difffilestart $i $here
            }
        }
        set curdifftag "f:$fname"
            }
        }
        set curdifftag "f:$fname"
@@ -3156,26 +4266,143 @@ proc getblobdiffline {bdf ids} {
 proc nextfile {} {
     global difffilestart ctext
     set here [$ctext index @0,0]
 proc nextfile {} {
     global difffilestart ctext
     set here [$ctext index @0,0]
-    for {set i 0} {[info exists difffilestart($i)]} {incr i} {
-       if {[$ctext compare $difffilestart($i) > $here]} {
-           if {![info exists pos]
-               || [$ctext compare $difffilestart($i) < $pos]} {
-               set pos $difffilestart($i)
-           }
+    foreach loc $difffilestart {
+       if {[$ctext compare $loc > $here]} {
+           $ctext yview $loc
        }
     }
        }
     }
-    if {[info exists pos]} {
-       $ctext yview $pos
+}
+
+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 listboxsel {} {
-    global ctext cflist currentid
-    if {![info exists currentid]} return
-    set sel [lsort [$cflist curselection]]
-    if {$sel eq {}} return
-    set first [lindex $sel 0]
-    catch {$ctext yview fmark.$first}
+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 {} {
 }
 
 proc setcoords {} {
@@ -3208,11 +4435,10 @@ proc redisplay {} {
 }
 
 proc incrfont {inc} {
 }
 
 proc incrfont {inc} {
-    global mainfont namefont textfont ctext canv phase
+    global mainfont textfont ctext canv phase
     global stopped entries
     unmarkmatches
     set mainfont [lreplace $mainfont 1 1 [expr {[lindex $mainfont 1] + $inc}]]
     global stopped entries
     unmarkmatches
     set mainfont [lreplace $mainfont 1 1 [expr {[lindex $mainfont 1] + $inc}]]
-    set namefont [lreplace $namefont 1 1 [expr {[lindex $namefont 1] + $inc}]]
     set textfont [lreplace $textfont 1 1 [expr {[lindex $textfont 1] + $inc}]]
     setcoords
     $ctext conf -font $textfont
     set textfont [lreplace $textfont 1 1 [expr {[lindex $textfont 1] + $inc}]]
     setcoords
     $ctext conf -font $textfont
@@ -3251,7 +4477,7 @@ proc sha1change {n1 n2 op} {
 
 proc gotocommit {} {
     global sha1string currentid commitrow tagids headids
 
 proc gotocommit {} {
     global sha1string currentid commitrow tagids headids
-    global displayorder numcommits
+    global displayorder numcommits curview
 
     if {$sha1string == {}
        || ([info exists currentid] && $sha1string == $currentid)} return
 
     if {$sha1string == {}
        || ([info exists currentid] && $sha1string == $currentid)} return
@@ -3277,8 +4503,8 @@ proc gotocommit {} {
            }
        }
     }
            }
        }
     }
-    if {[info exists commitrow($id)]} {
-       selectline $commitrow($id) 1
+    if {[info exists commitrow($curview,$id)]} {
+       selectline $commitrow($curview,$id) 1
        return
     }
     if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
        return
     }
     if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
@@ -3386,7 +4612,7 @@ proc arrowjump {id n y} {
 }
 
 proc lineclick {x y id isnew} {
 }
 
 proc lineclick {x y id isnew} {
-    global ctext commitinfo childlist commitrow cflist canv thickerline
+    global ctext commitinfo children canv thickerline curview
 
     if {![info exists commitinfo($id)] && ![getcommit $id]} return
     unmarkmatches
 
     if {![info exists commitinfo($id)] && ![getcommit $id]} return
     unmarkmatches
@@ -3413,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 }
@@ -3425,7 +4651,7 @@ proc lineclick {x y id isnew} {
     $ctext insert end "\tAuthor:\t[lindex $info 1]\n"
     set date [formatdate [lindex $info 2]]
     $ctext insert end "\tDate:\t$date\n"
     $ctext insert end "\tAuthor:\t[lindex $info 1]\n"
     set date [formatdate [lindex $info 2]]
     $ctext insert end "\tDate:\t$date\n"
-    set kids [lindex $childlist $commitrow($id)]
+    set kids $children($curview,$id)
     if {$kids ne {}} {
        $ctext insert end "\nChildren:"
        set i 0
     if {$kids ne {}} {
        $ctext insert end "\nChildren:"
        set i 0
@@ -3443,8 +4669,7 @@ proc lineclick {x y id isnew} {
        }
     }
     $ctext conf -state disabled
        }
     }
     $ctext conf -state disabled
-
-    $cflist delete 0 end
+    init_flist {}
 }
 
 proc normalline {} {
 }
 
 proc normalline {} {
@@ -3457,9 +4682,9 @@ proc normalline {} {
 }
 
 proc selbyid {id} {
 }
 
 proc selbyid {id} {
-    global commitrow
-    if {[info exists commitrow($id)]} {
-       selectline $commitrow($id) 1
+    global commitrow curview
+    if {[info exists commitrow($curview,$id)]} {
+       selectline $commitrow($curview,$id) 1
     }
 }
 
     }
 }
 
@@ -3472,9 +4697,10 @@ proc mstime {} {
 }
 
 proc rowmenu {x y id} {
 }
 
 proc rowmenu {x y id} {
-    global rowctxmenu commitrow selectedline rowmenuid
+    global rowctxmenu commitrow selectedline rowmenuid curview
 
 
-    if {![info exists selectedline] || $commitrow($id) eq $selectedline} {
+    if {![info exists selectedline]
+       || $commitrow($curview,$id) eq $selectedline} {
        set state disabled
     } else {
        set state normal
        set state disabled
     } else {
        set state normal
@@ -3502,15 +4728,12 @@ proc diffvssel {dirn} {
 }
 
 proc doseldiff {oldid newid} {
 }
 
 proc doseldiff {oldid newid} {
-    global ctext cflist
+    global ctext
     global commitinfo
 
     $ctext conf -state normal
     global commitinfo
 
     $ctext conf -state normal
-    $ctext delete 0.0 end
-    $ctext mark set fmark.0 0.0
-    $ctext mark gravity fmark.0 left
-    $cflist delete 0 end
-    $cflist insert end "Top"
+    clear_ctext
+    init_flist "Top"
     $ctext insert end "From "
     $ctext tag conf link -foreground blue -underline 1
     $ctext tag bind link <Enter> { %W configure -cursor hand2 }
     $ctext insert end "From "
     $ctext tag conf link -foreground blue -underline 1
     $ctext tag bind link <Enter> { %W configure -cursor hand2 }
@@ -3602,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}
@@ -3677,14 +4900,22 @@ proc domktag {} {
 }
 
 proc redrawtags {id} {
 }
 
 proc redrawtags {id} {
-    global canv linehtag commitrow idpos selectedline
+    global canv linehtag commitrow idpos selectedline curview
+    global mainfont
 
 
-    if {![info exists commitrow($id)]} return
-    drawcmitrow $commitrow($id)
+    if {![info exists commitrow($curview,$id)]} return
+    drawcmitrow $commitrow($curview,$id)
     $canv delete tag.$id
     set xt [eval drawtags $id $idpos($id)]
     $canv delete tag.$id
     set xt [eval drawtags $id $idpos($id)]
-    $canv coords $linehtag($commitrow($id)) $xt [lindex $idpos($id) 2]
-    if {[info exists selectedline] && $selectedline == $commitrow($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
     }
 }
        selectline $selectedline 0
     }
 }
@@ -3756,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 $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
+       }
     }
     }
-    return [list $x $y $z]
+    set allcommits "done"
+    notbusy allcommits
+    dispneartags
 }
 
 proc rereadrefs {} {
 }
 
 proc rereadrefs {} {
@@ -3796,22 +5197,22 @@ proc rereadrefs {} {
 }
 
 proc showtag {tag isnew} {
 }
 
 proc showtag {tag isnew} {
-    global ctext cflist tagcontents tagids linknum
+    global ctext tagcontents tagids linknum
 
     if {$isnew} {
        addtohistory [list showtag $tag 0]
     }
     $ctext conf -state normal
 
     if {$isnew} {
        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
     $ctext conf -state disabled
-    $cflist delete 0 end
+    init_flist {}
 }
 
 proc doquit {} {
 }
 
 proc doquit {} {
@@ -3821,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
@@ -3830,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
@@ -3846,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
@@ -3866,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}
@@ -3878,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
     }
 }
 
     }
 }
 
@@ -4168,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"
@@ -4193,13 +5597,14 @@ set fastdate 0
 set uparrowlen 7
 set downarrowlen 7
 set mingaplen 30
 set uparrowlen 7
 set downarrowlen 7
 set mingaplen 30
+set cmitmode "patch"
+set wrapcomment "none"
+set showneartags 1
 
 set colors {green red blue magenta darkgrey brown orange}
 
 catch {source ~/.gitk}
 
 
 set colors {green red blue magenta darkgrey brown orange}
 
 catch {source ~/.gitk}
 
-set namefont $mainfont
-
 font create optionfont -family sans-serif -size -12
 
 set revtreeargs {}
 font create optionfont -family sans-serif -size -12
 
 set revtreeargs {}
@@ -4216,21 +5621,53 @@ 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]} {
-    error_popup "Cannot find the git directory \"$gitdir\"."
+    show_error {} . "Cannot find the git directory \"$gitdir\"."
     exit 1
 }
 
     exit 1
 }
 
+set cmdline_files {}
+set i [lsearch -exact $revtreeargs "--"]
+if {$i >= 0} {
+    set cmdline_files [lrange $revtreeargs [expr {$i + 1}] end]
+    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 cmdline_files [split $f "\n"]
+       set n [llength $cmdline_files]
+       set revtreeargs [lrange $revtreeargs 0 end-$n]
+    } err]} {
+       # unfortunately we get both stdout and stderr in $err,
+       # so look for "fatal:".
+       set i [string first "fatal:" $err]
+       if {$i > 0} {
+           set err [string range $err [expr {$i + 6}] end]
+       }
+       show_error {} . "Bad arguments to gitk:\n$err"
+       exit 1
+    }
+}
+
 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 None
 set viewfiles(0) {}
 set viewperm(0) 0
 set viewfiles(0) {}
 set viewperm(0) 0
+set viewargs(0) {}
 
 
+set cmdlineok 0
 set stopped 0
 set stuffsaved 0
 set patchnum 0
 set stopped 0
 set stuffsaved 0
 set patchnum 0
@@ -4238,28 +5675,18 @@ setcoords
 makewindow
 readrefs
 
 makewindow
 readrefs
 
-set cmdline_files {}
-catch {
-    set fileargs [eval exec git-rev-parse --no-revs --no-flags $revtreeargs]
-    set cmdline_files [split $fileargs "\n"]
-    set n [llength $cmdline_files]
-    set revtreeargs [lrange $revtreeargs 0 end-$n]
-}
-if {[lindex $revtreeargs end] eq "--"} {
-    set revtreeargs [lrange $revtreeargs 0 end-1]
-}
-
-if {$cmdline_files ne {}} {
+if {$cmdline_files ne {} || $revtreeargs ne {}} {
     # create a view for the files/dirs specified on the command line
     set curview 1
     set selectedview 1
     set nextviewnum 2
     set viewname(1) "Command line"
     set viewfiles(1) $cmdline_files
     # create a view for the files/dirs specified on the command line
     set curview 1
     set selectedview 1
     set nextviewnum 2
     set viewname(1) "Command line"
     set viewfiles(1) $cmdline_files
+    set viewargs(1) $revtreeargs
     set viewperm(1) 0
     set viewperm(1) 0
-    .bar.view add radiobutton -label $viewname(1) -command {showview 1} \
-       -variable selectedview -value 1
+    addviewmenu 1
     .bar.view entryconf 2 -state normal
     .bar.view entryconf 2 -state normal
+    .bar.view entryconf 3 -state normal
 }
 
 if {[info exists permviews]} {
 }
 
 if {[info exists permviews]} {
@@ -4268,9 +5695,9 @@ if {[info exists permviews]} {
        incr nextviewnum
        set viewname($n) [lindex $v 0]
        set viewfiles($n) [lindex $v 1]
        incr nextviewnum
        set viewname($n) [lindex $v 0]
        set viewfiles($n) [lindex $v 1]
+       set viewargs($n) [lindex $v 2]
        set viewperm($n) 1
        set viewperm($n) 1
-       .bar.view add radiobutton -label $viewname($n) \
-           -command [list showview $n] -variable selectedview -value $n
+       addviewmenu $n
     }
 }
 getcommits
     }
 }
 getcommits