Code

Merge branch 'maint' of git://linux-nfs.org/~bfields/git into maint
[git.git] / gitk
diff --git a/gitk b/gitk
index 5acaadf495f16d2515ca406fe68b212228b7024f..9ddff3e7f7b011564c56fb619f57bed66d875f7e 100755 (executable)
--- a/gitk
+++ b/gitk
@@ -2,7 +2,7 @@
 # Tcl ignores the next line -*- tcl -*- \
 exec wish "$0" -- "$@"
 
 # Tcl ignores the next line -*- tcl -*- \
 exec wish "$0" -- "$@"
 
-# Copyright (C) 2005 Paul Mackerras.  All rights reserved.
+# Copyright (C) 2005-2006 Paul Mackerras.  All rights reserved.
 # This program is free software; it may be used, copied, modified
 # and distributed under the terms of the GNU General Public Licence,
 # either version 2, or (at your option) any later version.
 # This program is free software; it may be used, copied, modified
 # and distributed under the terms of the GNU General Public Licence,
 # either version 2, or (at your option) any later version.
@@ -12,18 +12,17 @@ proc gitdir {} {
     if {[info exists env(GIT_DIR)]} {
        return $env(GIT_DIR)
     } else {
     if {[info exists env(GIT_DIR)]} {
        return $env(GIT_DIR)
     } else {
-       return ".git"
+       return [exec git rev-parse --git-dir]
     }
 }
 
 proc start_rev_list {view} {
     }
 }
 
 proc start_rev_list {view} {
-    global startmsecs nextupdate ncmupdate
+    global startmsecs nextupdate
     global commfd leftover tclencoding datemode
     global viewargs viewfiles commitidx
 
     set startmsecs [clock clicks -milliseconds]
     set nextupdate [expr {$startmsecs + 100}]
     global commfd leftover tclencoding datemode
     global viewargs viewfiles commitidx
 
     set startmsecs [clock clicks -milliseconds]
     set nextupdate [expr {$startmsecs + 100}]
-    set ncmupdate 1
     set commitidx($view) 0
     set args $viewargs($view)
     if {$viewfiles($view) ne {}} {
     set commitidx($view) 0
     set args $viewargs($view)
     if {$viewfiles($view) ne {}} {
@@ -79,7 +78,7 @@ proc getcommitlines {fd view}  {
     global parentlist childlist children curview hlview
     global vparentlist vchildlist vdisporder vcmitlisted
 
     global parentlist childlist children curview hlview
     global vparentlist vchildlist vdisporder vcmitlisted
 
-    set stuff [read $fd]
+    set stuff [read $fd 500000]
     if {$stuff == {}} {
        if {![eof $fd]} return
        global viewname
     if {$stuff == {}} {
        if {![eof $fd]} return
        global viewname
@@ -185,7 +184,7 @@ proc getcommitlines {fd view}  {
     }
     if {$gotsome} {
        if {$view == $curview} {
     }
     if {$gotsome} {
        if {$view == $curview} {
-           layoutmore
+           while {[layoutmore $nextupdate]} doupdate
        } elseif {[info exists hlview] && $view == $hlview} {
            vhighlightmore
        }
        } elseif {[info exists hlview] && $view == $hlview} {
            vhighlightmore
        }
@@ -196,20 +195,13 @@ proc getcommitlines {fd view}  {
 }
 
 proc doupdate {} {
 }
 
 proc doupdate {} {
-    global commfd nextupdate numcommits ncmupdate
+    global commfd nextupdate numcommits
 
     foreach v [array names commfd] {
        fileevent $commfd($v) readable {}
     }
     update
     set nextupdate [expr {[clock clicks -milliseconds] + 100}]
 
     foreach v [array names commfd] {
        fileevent $commfd($v) readable {}
     }
     update
     set nextupdate [expr {[clock clicks -milliseconds] + 100}]
-    if {$numcommits < 100} {
-       set ncmupdate [expr {$numcommits + 1}]
-    } elseif {$numcommits < 10000} {
-       set ncmupdate [expr {$numcommits + 10}]
-    } else {
-       set ncmupdate [expr {$numcommits + 100}]
-    }
     foreach v [array names commfd] {
        set fd $commfd($v)
        fileevent $fd readable [list getcommitlines $fd $v]
     foreach v [array names commfd] {
        set fd $commfd($v)
        fileevent $fd readable [list getcommitlines $fd $v]
@@ -312,14 +304,14 @@ proc getcommit {id} {
 
 proc readrefs {} {
     global tagids idtags headids idheads tagcontents
 
 proc readrefs {} {
     global tagids idtags headids idheads tagcontents
-    global otherrefids idotherrefs
+    global otherrefids idotherrefs mainhead
 
     foreach v {tagids idtags headids idheads otherrefids idotherrefs} {
        catch {unset $v}
     }
 
     foreach v {tagids idtags headids idheads otherrefids idotherrefs} {
        catch {unset $v}
     }
-    set refd [open [list | git ls-remote [gitdir]] r]
+    set refd [open [list | git show-ref] r]
     while {0 <= [set n [gets $refd line]]} {
     while {0 <= [set n [gets $refd line]]} {
-       if {![regexp {^([0-9a-f]{40})   refs/([^^]*)$} $line \
+       if {![regexp {^([0-9a-f]{40}) refs/([^^]*)$} $line \
            match id path]} {
            continue
        }
            match id path]} {
            continue
        }
@@ -341,13 +333,13 @@ proc readrefs {} {
            set tag {}
            catch {
                set commit [exec git rev-parse "$id^0"]
            set tag {}
            catch {
                set commit [exec git rev-parse "$id^0"]
-               if {"$commit" != "$id"} {
+               if {$commit != $id} {
                    set tagids($name) $commit
                    lappend idtags($commit) $name
                }
            }           
            catch {
                    set tagids($name) $commit
                    lappend idtags($commit) $name
                }
            }           
            catch {
-               set tagcontents($name) [exec git cat-file tag "$id"]
+               set tagcontents($name) [exec git cat-file tag $id]
            }
        } elseif { $type == "heads" } {
            set headids($name) $id
            }
        } elseif { $type == "heads" } {
            set headids($name) $id
@@ -358,6 +350,13 @@ proc readrefs {} {
        }
     }
     close $refd
        }
     }
     close $refd
+    set mainhead {}
+    catch {
+       set thehead [exec git symbolic-ref HEAD]
+       if {[string match "refs/heads/*" $thehead]} {
+           set mainhead [string range $thehead 11 end]
+       }
+    }
 }
 
 proc show_error {w top msg} {
 }
 
 proc show_error {w top msg} {
@@ -377,6 +376,23 @@ proc error_popup msg {
     show_error $w $w $msg
 }
 
     show_error $w $w $msg
 }
 
+proc confirm_popup msg {
+    global confirm_ok
+    set confirm_ok 0
+    set w .confirm
+    toplevel $w
+    wm transient $w .
+    message $w.m -text $msg -justify center -aspect 400
+    pack $w.m -side top -fill x -padx 20 -pady 20
+    button $w.ok -text OK -command "set confirm_ok 1; destroy $w"
+    pack $w.ok -side left -fill x
+    button $w.cancel -text Cancel -command "destroy $w"
+    pack $w.cancel -side right -fill x
+    bind $w <Visibility> "grab $w; focus $w"
+    tkwait window $w
+    return $confirm_ok
+}
+
 proc makewindow {} {
     global canv canv2 canv3 linespc charspc ctext cflist
     global textfont mainfont uifont
 proc makewindow {} {
     global canv canv2 canv3 linespc charspc ctext cflist
     global textfont mainfont uifont
@@ -386,6 +402,8 @@ proc makewindow {} {
     global rowctxmenu mergemax wrapcomment
     global highlight_files gdttype
     global searchstring sstring
     global rowctxmenu mergemax wrapcomment
     global highlight_files gdttype
     global searchstring sstring
+    global bgcolor fgcolor bglist fglist diffcolors
+    global headctxmenu
 
     menu .bar
     .bar add cascade -label "File" -menu .bar.file
 
     menu .bar
     .bar add cascade -label "File" -menu .bar.file
@@ -409,7 +427,7 @@ proc makewindow {} {
     .bar.view add separator
     .bar.view add radiobutton -label "All files" -command {showview 0} \
        -variable selectedview -value 0
     .bar.view add separator
     .bar.view add radiobutton -label "All files" -command {showview 0} \
        -variable selectedview -value 0
-    
+
     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
@@ -417,55 +435,60 @@ proc makewindow {} {
     .bar.help configure -font $uifont
     . configure -menu .bar
 
     .bar.help configure -font $uifont
     . configure -menu .bar
 
-    if {![info exists geometry(canv1)]} {
-       set geometry(canv1) [expr {45 * $charspc}]
-       set geometry(canv2) [expr {30 * $charspc}]
-       set geometry(canv3) [expr {15 * $charspc}]
-       set geometry(canvh) [expr {25 * $linespc + 4}]
-       set geometry(ctextw) 80
-       set geometry(ctexth) 30
-       set geometry(cflistw) 30
-    }
+    # the gui has upper and lower half, parts of a paned window.
     panedwindow .ctop -orient vertical
     panedwindow .ctop -orient vertical
-    if {[info exists geometry(width)]} {
-       .ctop conf -width $geometry(width) -height $geometry(height)
-       set texth [expr {$geometry(height) - $geometry(canvh) - 56}]
-       set geometry(ctexth) [expr {($texth - 8) /
-                                   [font metrics $textfont -linespace]}]
-    }
-    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
+
+    # possibly use assumed geometry
+    if {![info exists geometry(pwsash0)]} {
+        set geometry(topheight) [expr {15 * $linespc}]
+        set geometry(topwidth) [expr {80 * $charspc}]
+        set geometry(botheight) [expr {15 * $linespc}]
+        set geometry(botwidth) [expr {50 * $charspc}]
+        set geometry(pwsash0) "[expr {40 * $charspc}] 2"
+        set geometry(pwsash1) "[expr {60 * $charspc}] 2"
+    }
+
+    # the upper half will have a paned window, a scroll bar to the right, and some stuff below
+    frame .tf -height $geometry(topheight) -width $geometry(topwidth)
+    frame .tf.histframe
+    panedwindow .tf.histframe.pwclist -orient horizontal -sashpad 0 -handlesize 4
+
+    # create three canvases
+    set cscroll .tf.histframe.csb
+    set canv .tf.histframe.pwclist.canv
+    canvas $canv \
+       -background $bgcolor -bd 0 \
+       -yscrollincr $linespc -yscrollcommand "scrollcanv $cscroll"
+    .tf.histframe.pwclist add $canv
+    set canv2 .tf.histframe.pwclist.canv2
+    canvas $canv2 \
+       -background $bgcolor -bd 0 -yscrollincr $linespc
+    .tf.histframe.pwclist add $canv2
+    set canv3 .tf.histframe.pwclist.canv3
+    canvas $canv3 \
+       -background $bgcolor -bd 0 -yscrollincr $linespc
+    .tf.histframe.pwclist add $canv3
+    eval .tf.histframe.pwclist sash place 0 $geometry(pwsash0)
+    eval .tf.histframe.pwclist sash place 1 $geometry(pwsash1)
+
+    # a scroll bar to rule them
     scrollbar $cscroll -command {allcanvs yview} -highlightthickness 0
     pack $cscroll -side right -fill y
     scrollbar $cscroll -command {allcanvs yview} -highlightthickness 0
     pack $cscroll -side right -fill y
-    panedwindow .ctop.top.clist -orient horizontal -sashpad 0 -handlesize 4
-    pack .ctop.top.clist -side top -fill both -expand 1
-    .ctop add .ctop.top
-    set canv .ctop.top.clist.canv
-    canvas $canv -height $geometry(canvh) -width $geometry(canv1) \
-       -bg white -bd 0 \
-       -yscrollincr $linespc -yscrollcommand "scrollcanv $cscroll"
-    .ctop.top.clist add $canv
-    set canv2 .ctop.top.clist.canv2
-    canvas $canv2 -height $geometry(canvh) -width $geometry(canv2) \
-       -bg white -bd 0 -yscrollincr $linespc
-    .ctop.top.clist add $canv2
-    set canv3 .ctop.top.clist.canv3
-    canvas $canv3 -height $geometry(canvh) -width $geometry(canv3) \
-       -bg white -bd 0 -yscrollincr $linespc
-    .ctop.top.clist add $canv3
-    bind .ctop.top.clist <Configure> {resizeclistpanes %W %w}
-
-    set sha1entry .ctop.top.bar.sha1
+    bind .tf.histframe.pwclist <Configure> {resizeclistpanes %W %w}
+    lappend bglist $canv $canv2 $canv3
+    pack .tf.histframe.pwclist -fill both -expand 1 -side left
+
+    # we have two button bars at bottom of top frame. Bar 1
+    frame .tf.bar
+    frame .tf.lbar -height 15
+
+    set sha1entry .tf.bar.sha1
     set entries $sha1entry
     set entries $sha1entry
-    set sha1but .ctop.top.bar.sha1label
+    set sha1but .tf.bar.sha1label
     button $sha1but -text "SHA1 ID: " -state disabled -relief flat \
        -command gotocommit -width 8 -font $uifont
     $sha1but conf -disabledforeground [$sha1but cget -foreground]
     button $sha1but -text "SHA1 ID: " -state disabled -relief flat \
        -command gotocommit -width 8 -font $uifont
     $sha1but conf -disabledforeground [$sha1but cget -foreground]
-    pack .ctop.top.bar.sha1label -side left
+    pack .tf.bar.sha1label -side left
     entry $sha1entry -width 40 -font $textfont -textvariable sha1string
     trace add variable sha1string write sha1change
     pack $sha1entry -side left -pady 2
     entry $sha1entry -width 40 -font $textfont -textvariable sha1string
     trace add variable sha1string write sha1change
     pack $sha1entry -side left -pady 2
@@ -486,96 +509,115 @@ proc makewindow {} {
        0x00, 0x38, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x00, 0x38, 0x00, 0x1c,
        0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01};
     }
        0x00, 0x38, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x00, 0x38, 0x00, 0x1c,
        0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01};
     }
-    button .ctop.top.bar.leftbut -image bm-left -command goback \
+    button .tf.bar.leftbut -image bm-left -command goback \
        -state disabled -width 26
        -state disabled -width 26
-    pack .ctop.top.bar.leftbut -side left -fill y
-    button .ctop.top.bar.rightbut -image bm-right -command goforw \
+    pack .tf.bar.leftbut -side left -fill y
+    button .tf.bar.rightbut -image bm-right -command goforw \
        -state disabled -width 26
        -state disabled -width 26
-    pack .ctop.top.bar.rightbut -side left -fill y
+    pack .tf.bar.rightbut -side left -fill y
 
 
-    button .ctop.top.bar.findbut -text "Find" -command dofind -font $uifont
-    pack .ctop.top.bar.findbut -side left
+    button .tf.bar.findbut -text "Find" -command dofind -font $uifont
+    pack .tf.bar.findbut -side left
     set findstring {}
     set findstring {}
-    set fstring .ctop.top.bar.findstring
+    set fstring .tf.bar.findstring
     lappend entries $fstring
     entry $fstring -width 30 -font $textfont -textvariable findstring
     trace add variable findstring write find_change
     lappend entries $fstring
     entry $fstring -width 30 -font $textfont -textvariable findstring
     trace add variable findstring write find_change
-    pack $fstring -side left -expand 1 -fill x
+    pack $fstring -side left -expand 1 -fill x -in .tf.bar
     set findtype Exact
     set findtype Exact
-    set findtypemenu [tk_optionMenu .ctop.top.bar.findtype \
-                         findtype Exact IgnCase Regexp]
+    set findtypemenu [tk_optionMenu .tf.bar.findtype \
+                     findtype Exact IgnCase Regexp]
     trace add variable findtype write find_change
     trace add variable findtype write find_change
-    .ctop.top.bar.findtype configure -font $uifont
-    .ctop.top.bar.findtype.menu configure -font $uifont
+    .tf.bar.findtype configure -font $uifont
+    .tf.bar.findtype.menu configure -font $uifont
     set findloc "All fields"
     set findloc "All fields"
-    tk_optionMenu .ctop.top.bar.findloc findloc "All fields" Headline \
+    tk_optionMenu .tf.bar.findloc findloc "All fields" Headline \
        Comments Author Committer
     trace add variable findloc write find_change
        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
-    pack .ctop.top.bar.findloc -side right
-    pack .ctop.top.bar.findtype -side right
-
-    label .ctop.top.lbar.flabel -text "Highlight:  Commits " \
-       -font $uifont
-    pack .ctop.top.lbar.flabel -side left -fill y
+    .tf.bar.findloc configure -font $uifont
+    .tf.bar.findloc.menu configure -font $uifont
+    pack .tf.bar.findloc -side right
+    pack .tf.bar.findtype -side right
+
+    # build up the bottom bar of upper window
+    label .tf.lbar.flabel -text "Highlight:  Commits " \
+    -font $uifont
+    pack .tf.lbar.flabel -side left -fill y
     set gdttype "touching paths:"
     set gdttype "touching paths:"
-    set gm [tk_optionMenu .ctop.top.lbar.gdttype gdttype "touching paths:" \
-               "adding/removing string:"]
+    set gm [tk_optionMenu .tf.lbar.gdttype gdttype "touching paths:" \
+       "adding/removing string:"]
     trace add variable gdttype write hfiles_change
     $gm conf -font $uifont
     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 \
+    .tf.lbar.gdttype conf -font $uifont
+    pack .tf.lbar.gdttype -side left -fill y
+    entry .tf.lbar.fent -width 25 -font $textfont \
        -textvariable highlight_files
     trace add variable highlight_files write hfiles_change
        -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
+    lappend entries .tf.lbar.fent
+    pack .tf.lbar.fent -side left -fill x -expand 1
+    label .tf.lbar.vlabel -text " OR in view" -font $uifont
+    pack .tf.lbar.vlabel -side left -fill y
     global viewhlmenu selectedhlview
     global viewhlmenu selectedhlview
-    set viewhlmenu [tk_optionMenu .ctop.top.lbar.vhl selectedhlview None]
-    $viewhlmenu entryconf 0 -command delvhighlight
+    set viewhlmenu [tk_optionMenu .tf.lbar.vhl selectedhlview None]
+    $viewhlmenu entryconf None -command delvhighlight
     $viewhlmenu conf -font $uifont
     $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
+    .tf.lbar.vhl conf -font $uifont
+    pack .tf.lbar.vhl -side left -fill y
+    label .tf.lbar.rlabel -text " OR " -font $uifont
+    pack .tf.lbar.rlabel -side left -fill y
     global highlight_related
     global highlight_related
-    set m [tk_optionMenu .ctop.top.lbar.relm highlight_related None \
-              "Descendent" "Not descendent" "Ancestor" "Not ancestor"]
+    set m [tk_optionMenu .tf.lbar.relm highlight_related None \
+       "Descendent" "Not descendent" "Ancestor" "Not ancestor"]
     $m conf -font $uifont
     $m conf -font $uifont
-    .ctop.top.lbar.relm conf -font $uifont
+    .tf.lbar.relm conf -font $uifont
     trace add variable highlight_related write vrel_change
     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
-    frame .ctop.cdet.left.bot
-    pack .ctop.cdet.left.bot -side bottom -fill x
-    button .ctop.cdet.left.bot.search -text "Search" -command dosearch \
+    pack .tf.lbar.relm -side left -fill y
+
+    # Finish putting the upper half of the viewer together
+    pack .tf.lbar -in .tf -side bottom -fill x
+    pack .tf.bar -in .tf -side bottom -fill x
+    pack .tf.histframe -fill both -side top -expand 1
+    .ctop add .tf
+    .ctop paneconfigure .tf -height $geometry(topheight)
+    .ctop paneconfigure .tf -width $geometry(topwidth)
+
+    # now build up the bottom
+    panedwindow .pwbottom -orient horizontal
+
+    # lower left, a text box over search bar, scroll bar to the right
+    # if we know window height, then that will set the lower text height, otherwise
+    # we set lower text height which will drive window height
+    if {[info exists geometry(main)]} {
+        frame .bleft -width $geometry(botwidth)
+    } else {
+        frame .bleft -width $geometry(botwidth) -height $geometry(botheight)
+    }
+    frame .bleft.top
+
+    button .bleft.top.search -text "Search" -command dosearch \
        -font $uifont
        -font $uifont
-    pack .ctop.cdet.left.bot.search -side left -padx 5
-    set sstring .ctop.cdet.left.bot.sstring
+    pack .bleft.top.search -side left -padx 5
+    set sstring .bleft.top.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
     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 .bleft.ctext
+    text $ctext -background $bgcolor -foreground $fgcolor \
+       -state disabled -font $textfont \
        -yscrollcommand scrolltext -wrap none
        -yscrollcommand scrolltext -wrap none
-    scrollbar .ctop.cdet.left.sb -command "$ctext yview"
-    pack .ctop.cdet.left.sb -side right -fill y
+    scrollbar .bleft.sb -command "$ctext yview"
+    pack .bleft.top -side top -fill x
+    pack .bleft.sb -side right -fill y
     pack $ctext -side left -fill both -expand 1
     pack $ctext -side left -fill both -expand 1
-    .ctop.cdet add .ctop.cdet.left
+    lappend bglist $ctext
+    lappend fglist $ctext
 
     $ctext tag conf comment -wrap $wrapcomment
     $ctext tag conf filesep -font [concat $textfont bold] -back "#aaaaaa"
 
     $ctext tag conf comment -wrap $wrapcomment
     $ctext tag conf filesep -font [concat $textfont bold] -back "#aaaaaa"
-    $ctext tag conf hunksep -fore blue
-    $ctext tag conf d0 -fore red
-    $ctext tag conf d1 -fore "#00a000"
+    $ctext tag conf hunksep -fore [lindex $diffcolors 2]
+    $ctext tag conf d0 -fore [lindex $diffcolors 0]
+    $ctext tag conf d1 -fore [lindex $diffcolors 1]
     $ctext tag conf m0 -fore red
     $ctext tag conf m1 -fore blue
     $ctext tag conf m2 -fore green
     $ctext tag conf m0 -fore red
     $ctext tag conf m1 -fore blue
     $ctext tag conf m2 -fore green
@@ -598,32 +640,46 @@ proc makewindow {} {
     $ctext tag conf msep -font [concat $textfont bold]
     $ctext tag conf found -back yellow
 
     $ctext tag conf msep -font [concat $textfont bold]
     $ctext tag conf found -back yellow
 
-    frame .ctop.cdet.right
-    frame .ctop.cdet.right.mode
-    radiobutton .ctop.cdet.right.mode.patch -text "Patch" \
+    .pwbottom add .bleft
+    .pwbottom paneconfigure .bleft -width $geometry(botwidth)
+
+    # lower right
+    frame .bright
+    frame .bright.mode
+    radiobutton .bright.mode.patch -text "Patch" \
        -command reselectline -variable cmitmode -value "patch"
        -command reselectline -variable cmitmode -value "patch"
-    radiobutton .ctop.cdet.right.mode.tree -text "Tree" \
+    radiobutton .bright.mode.tree -text "Tree" \
        -command reselectline -variable cmitmode -value "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
+    grid .bright.mode.patch .bright.mode.tree -sticky ew
+    pack .bright.mode -side top -fill x
+    set cflist .bright.cfiles
     set indent [font measure $mainfont "nn"]
     set indent [font measure $mainfont "nn"]
-    text $cflist -width $geometry(cflistw) -background white -font $mainfont \
+    text $cflist \
+       -background $bgcolor -foreground $fgcolor \
+       -font $mainfont \
        -tabs [list $indent [expr {2 * $indent}]] \
        -tabs [list $indent [expr {2 * $indent}]] \
-       -yscrollcommand ".ctop.cdet.right.sb set" \
+       -yscrollcommand ".bright.sb set" \
        -cursor [. cget -cursor] \
        -spacing1 1 -spacing3 1
        -cursor [. cget -cursor] \
        -spacing1 1 -spacing3 1
-    scrollbar .ctop.cdet.right.sb -command "$cflist yview"
-    pack .ctop.cdet.right.sb -side right -fill y
+    lappend bglist $cflist
+    lappend fglist $cflist
+    scrollbar .bright.sb -command "$cflist yview"
+    pack .bright.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]
     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}
 
 
-    pack .ctop -side top -fill both -expand 1
+    .pwbottom add .bright
+    .ctop add .pwbottom
+
+    # restore window position if known
+    if {[info exists geometry(main)]} {
+        wm geometry . "$geometry(main)"
+    }
 
 
+    bind .pwbottom <Configure> {resizecdetpanes %W %w}
+    pack .ctop -fill both -expand 1
     bindall <1> {selcanvline %W %x %y}
     #bindall <B1-Motion> {selcanvline %W %x %y}
     bindall <ButtonRelease-4> "allcanvs yview scroll -5 units"
     bindall <1> {selcanvline %W %x %y}
     #bindall <B1-Motion> {selcanvline %W %x %y}
     bindall <ButtonRelease-4> "allcanvs yview scroll -5 units"
@@ -673,7 +729,7 @@ proc makewindow {} {
     bind . <Control-KP_Add> {incrfont 1}
     bind . <Control-minus> {incrfont -1}
     bind . <Control-KP_Subtract> {incrfont -1}
     bind . <Control-KP_Add> {incrfont 1}
     bind . <Control-minus> {incrfont -1}
     bind . <Control-KP_Subtract> {incrfont -1}
-    bind . <Destroy> {savestuff %W}
+    wm protocol . WM_DELETE_WINDOW doquit
     bind . <Button-1> "click %W"
     bind $fstring <Key-Return> dofind
     bind $sha1entry <Key-Return> gotocommit
     bind . <Button-1> "click %W"
     bind $fstring <Key-Return> dofind
     bind $sha1entry <Key-Return> gotocommit
@@ -695,6 +751,16 @@ proc makewindow {} {
     $rowctxmenu add command -label "Make patch" -command mkpatch
     $rowctxmenu add command -label "Create tag" -command mktag
     $rowctxmenu add command -label "Write commit to file" -command writecommit
     $rowctxmenu add command -label "Make patch" -command mkpatch
     $rowctxmenu add command -label "Create tag" -command mktag
     $rowctxmenu add command -label "Write commit to file" -command writecommit
+    $rowctxmenu add command -label "Create new branch" -command mkbranch
+    $rowctxmenu add command -label "Cherry-pick this commit" \
+       -command cherrypick
+
+    set headctxmenu .headctxmenu
+    menu $headctxmenu -tearoff 0
+    $headctxmenu add command -label "Check out this branch" \
+       -command cobranch
+    $headctxmenu add command -label "Remove this branch" \
+       -command rmbranch
 }
 
 # mouse-2 makes all windows scan vertically, but only the one
 }
 
 # mouse-2 makes all windows scan vertically, but only the one
@@ -747,6 +813,7 @@ proc savestuff {w} {
     global maxwidth showneartags
     global viewname viewfiles viewargs viewperm nextviewnum
     global cmitmode wrapcomment
     global maxwidth showneartags
     global viewname viewfiles viewargs viewperm nextviewnum
     global cmitmode wrapcomment
+    global colors bgcolor fgcolor diffcolors
 
     if {$stuffsaved} return
     if {![winfo viewable .]} return
 
     if {$stuffsaved} return
     if {![winfo viewable .]} return
@@ -761,18 +828,19 @@ proc savestuff {w} {
        puts $f [list set cmitmode $cmitmode]
        puts $f [list set wrapcomment $wrapcomment]
        puts $f [list set showneartags $showneartags]
        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(canv2) [expr {[winfo width $canv2]-2}]"
-       puts $f "set geometry(canv3) [expr {[winfo width $canv3]-2}]"
-       puts $f "set geometry(canvh) [expr {[winfo height $canv]-2}]"
-       set wid [expr {([winfo width $ctext] - 8) \
-                          / [font measure $textfont "0"]}]
-       puts $f "set geometry(ctextw) $wid"
-       set wid [expr {([winfo width $cflist] - 11) \
-                          / [font measure [$cflist cget -font] "0"]}]
-       puts $f "set geometry(cflistw) $wid"
+       puts $f [list set bgcolor $bgcolor]
+       puts $f [list set fgcolor $fgcolor]
+       puts $f [list set colors $colors]
+       puts $f [list set diffcolors $diffcolors]
+
+       puts $f "set geometry(main) [wm geometry .]"
+       puts $f "set geometry(topwidth) [winfo width .tf]"
+       puts $f "set geometry(topheight) [winfo height .tf]"
+        puts $f "set geometry(pwsash0) \"[.tf.histframe.pwclist sash coord 0]\""
+        puts $f "set geometry(pwsash1) \"[.tf.histframe.pwclist sash coord 1]\""
+       puts $f "set geometry(botwidth) [winfo width .bleft]"
+       puts $f "set geometry(botheight) [winfo height .bleft]"
+
        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)} {
@@ -1361,7 +1429,7 @@ proc newview {ishighlight} {
     set newviewname($nextviewnum) "View $nextviewnum"
     set newviewperm($nextviewnum) 0
     set newviewargs($nextviewnum) [shellarglist $revtreeargs]
     set newviewname($nextviewnum) "View $nextviewnum"
     set newviewperm($nextviewnum) 0
     set newviewargs($nextviewnum) [shellarglist $revtreeargs]
-    vieweditor $top $nextviewnum "Gitk view definition" 
+    vieweditor $top $nextviewnum "Gitk view definition"
 }
 
 proc editview {} {
 }
 
 proc editview {} {
@@ -1433,7 +1501,7 @@ proc doviewmenu {m first cmd op argv} {
 proc allviewmenus {n op args} {
     global viewhlmenu
 
 proc allviewmenus {n op args} {
     global viewhlmenu
 
-    doviewmenu .bar.view 7 [list showview $n] $op $args
+    doviewmenu .bar.view 5 [list showview $n] $op $args
     doviewmenu $viewhlmenu 1 [list addvhighlight $n] $op $args
 }
 
     doviewmenu $viewhlmenu 1 [list addvhighlight $n] $op $args
 }
 
@@ -1475,7 +1543,7 @@ proc newviewok {top n} {
        set viewperm($n) $newviewperm($n)
        if {$newviewname($n) ne $viewname($n)} {
            set viewname($n) $newviewname($n)
        set viewperm($n) $newviewperm($n)
        if {$newviewname($n) ne $viewname($n)} {
            set viewname($n) $newviewname($n)
-           doviewmenu .bar.view 7 [list showview $n] \
+           doviewmenu .bar.view 5 [list showview $n] \
                entryconf [list -label $viewname($n)]
            doviewmenu $viewhlmenu 1 [list addvhighlight $n] \
                entryconf [list -label $viewname($n) -value $viewname($n)]
                entryconf [list -label $viewname($n)]
            doviewmenu $viewhlmenu 1 [list addvhighlight $n] \
                entryconf [list -label $viewname($n) -value $viewname($n)]
@@ -1591,8 +1659,8 @@ proc showview {n} {
 
     set curview $n
     set selectedview $n
 
     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"}]
+    .bar.view entryconf Edit* -state [expr {$n == 0? "disabled": "normal"}]
+    .bar.view entryconf Delete* -state [expr {$n == 0? "disabled": "normal"}]
 
     if {![info exists viewdata($n)]} {
        set pending_select $selid
 
     if {![info exists viewdata($n)]} {
        set pending_select $selid
@@ -1648,7 +1716,7 @@ proc showview {n} {
            show_status "Reading commits..."
        }
        if {[info exists commfd($n)]} {
            show_status "Reading commits..."
        }
        if {[info exists commfd($n)]} {
-           layoutmore
+           layoutmore {}
        } else {
            finishcommits
        }
        } else {
            finishcommits
        }
@@ -2329,20 +2397,38 @@ proc visiblerows {} {
     return [list $r0 $r1]
 }
 
     return [list $r0 $r1]
 }
 
-proc layoutmore {} {
+proc layoutmore {tmax} {
     global rowlaidout rowoptim commitidx numcommits optim_delay
     global uparrowlen curview
 
     global rowlaidout rowoptim commitidx numcommits optim_delay
     global uparrowlen curview
 
-    set row $rowlaidout
-    set rowlaidout [layoutrows $row $commitidx($curview) 0]
-    set orow [expr {$rowlaidout - $uparrowlen - 1}]
-    if {$orow > $rowoptim} {
-       optimize_rows $rowoptim 0 $orow
-       set rowoptim $orow
-    }
-    set canshow [expr {$rowoptim - $optim_delay}]
-    if {$canshow > $numcommits} {
-       showstuff $canshow
+    while {1} {
+       if {$rowoptim - $optim_delay > $numcommits} {
+           showstuff [expr {$rowoptim - $optim_delay}]
+       } elseif {$rowlaidout - $uparrowlen - 1 > $rowoptim} {
+           set nr [expr {$rowlaidout - $uparrowlen - 1 - $rowoptim}]
+           if {$nr > 100} {
+               set nr 100
+           }
+           optimize_rows $rowoptim 0 [expr {$rowoptim + $nr}]
+           incr rowoptim $nr
+       } elseif {$commitidx($curview) > $rowlaidout} {
+           set nr [expr {$commitidx($curview) - $rowlaidout}]
+           # may need to increase this threshold if uparrowlen or
+           # mingaplen are increased...
+           if {$nr > 150} {
+               set nr 150
+           }
+           set row $rowlaidout
+           set rowlaidout [layoutrows $row [expr {$row + $nr}] 0]
+           if {$rowlaidout == $row} {
+               return 0
+           }
+       } else {
+           return 0
+       }
+       if {$tmax ne {} && [clock clicks -milliseconds] >= $tmax} {
+           return 1
+       }
     }
 }
 
     }
 }
 
@@ -2870,11 +2956,11 @@ proc drawlines {id} {
 }
 
 proc drawcmittext {id row col rmx} {
 }
 
 proc drawcmittext {id row col rmx} {
-    global linespc canv canv2 canv3 canvy0
+    global linespc canv canv2 canv3 canvy0 fgcolor
     global commitlisted commitinfo rowidlist
     global rowtextx idpos idtags idheads idotherrefs
     global linehtag linentag linedtag
     global commitlisted commitinfo rowidlist
     global rowtextx idpos idtags idheads idotherrefs
     global linehtag linentag linedtag
-    global mainfont canvxmax boldrows boldnamerows
+    global mainfont canvxmax boldrows boldnamerows fgcolor
 
     set ofill [expr {[lindex $commitlisted $row]? "blue": "white"}]
     set x [xc $row $col]
 
     set ofill [expr {[lindex $commitlisted $row]? "blue": "white"}]
     set x [xc $row $col]
@@ -2882,7 +2968,7 @@ proc drawcmittext {id row col rmx} {
     set orad [expr {$linespc / 3}]
     set t [$canv create oval [expr {$x - $orad}] [expr {$y - $orad}] \
               [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
     set orad [expr {$linespc / 3}]
     set t [$canv create oval [expr {$x - $orad}] [expr {$y - $orad}] \
               [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
-              -fill $ofill -outline black -width 1]
+              -fill $ofill -outline $fgcolor -width 1 -tags circle]
     $canv raise $t
     $canv bind $t <1> {selcanvline {} %x %y}
     set xt [xc $row [llength [lindex $rowidlist $row]]]
     $canv raise $t
     $canv bind $t <1> {selcanvline {} %x %y}
     set xt [xc $row [llength [lindex $rowidlist $row]]]
@@ -2910,13 +2996,13 @@ proc drawcmittext {id row col rmx} {
            lappend nfont bold
        }
     }
            lappend nfont bold
        }
     }
-    set linehtag($row) [$canv create text $xt $y -anchor w \
-                           -text $headline -font $font]
+    set linehtag($row) [$canv create text $xt $y -anchor w -fill $fgcolor \
+                           -text $headline -font $font -tags text]
     $canv bind $linehtag($row) <Button-3> "rowmenu %X %Y $id"
     $canv bind $linehtag($row) <Button-3> "rowmenu %X %Y $id"
-    set linentag($row) [$canv2 create text 3 $y -anchor w \
-                           -text $name -font $nfont]
-    set linedtag($row) [$canv3 create text 3 $y -anchor w \
-                           -text $date -font $mainfont]
+    set linentag($row) [$canv2 create text 3 $y -anchor w -fill $fgcolor \
+                           -text $name -font $nfont -tags text]
+    set linedtag($row) [$canv3 create text 3 $y -anchor w -fill $fgcolor \
+                           -text $date -font $mainfont -tags text]
     set xr [expr {$xt + [font measure $mainfont $headline]}]
     if {$xr > $canvxmax} {
        set canvxmax $xr
     set xr [expr {$xt + [font measure $mainfont $headline]}]
     if {$xr > $canvxmax} {
        set canvxmax $xr
@@ -3136,9 +3222,9 @@ proc bindline {t id} {
 }
 
 proc drawtags {id x xt y1} {
 }
 
 proc drawtags {id x xt y1} {
-    global idtags idheads idotherrefs
+    global idtags idheads idotherrefs mainhead
     global linespc lthickness
     global linespc lthickness
-    global canv mainfont commitrow rowtextx curview
+    global canv mainfont commitrow rowtextx curview fgcolor bgcolor
 
     set marks {}
     set ntags 0
 
     set marks {}
     set ntags 0
@@ -3163,8 +3249,14 @@ proc drawtags {id x xt y1} {
     set yb [expr {$yt + $linespc - 1}]
     set xvals {}
     set wvals {}
     set yb [expr {$yt + $linespc - 1}]
     set xvals {}
     set wvals {}
+    set i -1
     foreach tag $marks {
     foreach tag $marks {
-       set wid [font measure $mainfont $tag]
+       incr i
+       if {$i >= $ntags && $i < $ntags + $nheads && $tag eq $mainhead} {
+           set wid [font measure [concat $mainfont bold] $tag]
+       } else {
+           set wid [font measure $mainfont $tag]
+       }
        lappend xvals $xt
        lappend wvals $wid
        set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}]
        lappend xvals $xt
        lappend wvals $wid
        set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}]
@@ -3175,6 +3267,7 @@ proc drawtags {id x xt y1} {
     foreach tag $marks x $xvals wid $wvals {
        set xl [expr {$x + $delta}]
        set xr [expr {$x + $delta + $wid + $lthickness}]
     foreach tag $marks x $xvals wid $wvals {
        set xl [expr {$x + $delta}]
        set xr [expr {$x + $delta + $wid + $lthickness}]
+       set font $mainfont
        if {[incr ntags -1] >= 0} {
            # draw a tag
            set t [$canv create polygon $x [expr {$yt + $delta}] $xl $yt \
        if {[incr ntags -1] >= 0} {
            # draw a tag
            set t [$canv create polygon $x [expr {$yt + $delta}] $xl $yt \
@@ -3186,6 +3279,9 @@ proc drawtags {id x xt y1} {
            # draw a head or other ref
            if {[incr nheads -1] >= 0} {
                set col green
            # draw a head or other ref
            if {[incr nheads -1] >= 0} {
                set col green
+               if {$tag eq $mainhead} {
+                   lappend font bold
+               }
            } else {
                set col "#ddddff"
            }
            } else {
                set col "#ddddff"
            }
@@ -3201,10 +3297,12 @@ proc drawtags {id x xt y1} {
                        -width 0 -fill "#ffddaa" -tags tag.$id
            }
        }
                        -width 0 -fill "#ffddaa" -tags tag.$id
            }
        }
-       set t [$canv create text $xl $y1 -anchor w -text $tag \
-                  -font $mainfont -tags tag.$id]
+       set t [$canv create text $xl $y1 -anchor w -text $tag -fill $fgcolor \
+                  -font $font -tags [list tag.$id text]]
        if {$ntags >= 0} {
            $canv bind $t <1> [list showtag $tag 1]
        if {$ntags >= 0} {
            $canv bind $t <1> [list showtag $tag 1]
+       } elseif {$nheads >= 0} {
+           $canv bind $t <Button-3> [list headmenu %X %Y $id $tag]
        }
     }
     return $xt
        }
     }
     return $xt
@@ -3223,16 +3321,16 @@ proc xcoord {i level ln} {
 }
 
 proc show_status {msg} {
 }
 
 proc show_status {msg} {
-    global canv mainfont
+    global canv mainfont fgcolor
 
     clear_display
 
     clear_display
-    $canv create text 3 3 -anchor nw -text $msg -font $mainfont -tags textitems
+    $canv create text 3 3 -anchor nw -text $msg -font $mainfont \
+       -tags text -fill $fgcolor
 }
 
 proc finishcommits {} {
     global commitidx phase curview
 }
 
 proc finishcommits {} {
     global commitidx phase curview
-    global canv mainfont ctext maincursor textcursor
-    global findinprogress pending_select
+    global pending_select
 
     if {$commitidx($curview) > 0} {
        drawrest
 
     if {$commitidx($curview) > 0} {
        drawrest
@@ -3243,6 +3341,108 @@ proc finishcommits {} {
     catch {unset pending_select}
 }
 
     catch {unset pending_select}
 }
 
+# Insert a new commit as the child of the commit on row $row.
+# The new commit will be displayed on row $row and the commits
+# on that row and below will move down one row.
+proc insertrow {row newcmit} {
+    global displayorder parentlist childlist commitlisted
+    global commitrow curview rowidlist rowoffsets numcommits
+    global rowrangelist idrowranges rowlaidout rowoptim numcommits
+    global linesegends selectedline
+
+    if {$row >= $numcommits} {
+       puts "oops, inserting new row $row but only have $numcommits rows"
+       return
+    }
+    set p [lindex $displayorder $row]
+    set displayorder [linsert $displayorder $row $newcmit]
+    set parentlist [linsert $parentlist $row $p]
+    set kids [lindex $childlist $row]
+    lappend kids $newcmit
+    lset childlist $row $kids
+    set childlist [linsert $childlist $row {}]
+    set commitlisted [linsert $commitlisted $row 1]
+    set l [llength $displayorder]
+    for {set r $row} {$r < $l} {incr r} {
+       set id [lindex $displayorder $r]
+       set commitrow($curview,$id) $r
+    }
+
+    set idlist [lindex $rowidlist $row]
+    set offs [lindex $rowoffsets $row]
+    set newoffs {}
+    foreach x $idlist {
+       if {$x eq {} || ($x eq $p && [llength $kids] == 1)} {
+           lappend newoffs {}
+       } else {
+           lappend newoffs 0
+       }
+    }
+    if {[llength $kids] == 1} {
+       set col [lsearch -exact $idlist $p]
+       lset idlist $col $newcmit
+    } else {
+       set col [llength $idlist]
+       lappend idlist $newcmit
+       lappend offs {}
+       lset rowoffsets $row $offs
+    }
+    set rowidlist [linsert $rowidlist $row $idlist]
+    set rowoffsets [linsert $rowoffsets [expr {$row+1}] $newoffs]
+
+    set rowrangelist [linsert $rowrangelist $row {}]
+    set l [llength $rowrangelist]
+    for {set r 0} {$r < $l} {incr r} {
+       set ranges [lindex $rowrangelist $r]
+       if {$ranges ne {} && [lindex $ranges end] >= $row} {
+           set newranges {}
+           foreach x $ranges {
+               if {$x >= $row} {
+                   lappend newranges [expr {$x + 1}]
+               } else {
+                   lappend newranges $x
+               }
+           }
+           lset rowrangelist $r $newranges
+       }
+    }
+    if {[llength $kids] > 1} {
+       set rp1 [expr {$row + 1}]
+       set ranges [lindex $rowrangelist $rp1]
+       if {$ranges eq {}} {
+           set ranges [list $row $rp1]
+       } elseif {[lindex $ranges end-1] == $rp1} {
+           lset ranges end-1 $row
+       }
+       lset rowrangelist $rp1 $ranges
+    }
+    foreach id [array names idrowranges] {
+       set ranges $idrowranges($id)
+       if {$ranges ne {} && [lindex $ranges end] >= $row} {
+           set newranges {}
+           foreach x $ranges {
+               if {$x >= $row} {
+                   lappend newranges [expr {$x + 1}]
+               } else {
+                   lappend newranges $x
+               }
+           }
+           set idrowranges($id) $newranges
+       }
+    }
+
+    set linesegends [linsert $linesegends $row {}]
+
+    incr rowlaidout
+    incr rowoptim
+    incr numcommits
+
+    if {[info exists selectedline] && $selectedline >= $row} {
+       incr selectedline
+    }
+    redisplay
+}
+
 # Don't change the text pane cursor if it is currently the hand cursor,
 # showing that we are over a sha1 ID link.
 proc settextcursor {c} {
 # Don't change the text pane cursor if it is currently the hand cursor,
 # showing that we are over a sha1 ID link.
 proc settextcursor {c} {
@@ -3275,9 +3475,7 @@ proc notbusy {what} {
 }
 
 proc drawrest {} {
 }
 
 proc drawrest {} {
-    global numcommits
     global startmsecs
     global startmsecs
-    global canvy0 numcommits linespc
     global rowlaidout commitidx curview
     global pending_select
 
     global rowlaidout commitidx curview
     global pending_select
 
@@ -3291,6 +3489,7 @@ proc drawrest {} {
     }
 
     set drawmsecs [expr {[clock clicks -milliseconds] - $startmsecs}]
     }
 
     set drawmsecs [expr {[clock clicks -milliseconds] - $startmsecs}]
+    #global numcommits
     #puts "overall $drawmsecs ms for $numcommits commits"
 }
 
     #puts "overall $drawmsecs ms for $numcommits commits"
 }
 
@@ -3571,27 +3770,20 @@ proc viewnextline {dir} {
 
 # add a list of tag or branch names at position pos
 # returns the number of names inserted
 
 # 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
+proc appendrefs {pos tags var} {
+    global ctext commitrow linknum curview $var
 
     if {[catch {$ctext index $pos}]} {
        return 0
     }
 
     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 tags [lsort $tags]
     set sep {}
     foreach tag $tags {
     set sep {}
     foreach tag $tags {
-       set name [lindex $tag 0]
-       set id [lindex $tag 1]
+       set id [set $var\($tag\)]
        set lk link$linknum
        incr linknum
        $ctext insert $pos $sep
        set lk link$linknum
        incr linknum
        $ctext insert $pos $sep
-       $ctext insert $pos $name $lk
+       $ctext insert $pos $tag $lk
        $ctext tag conf $lk -foreground blue
        if {[info exists commitrow($curview,$id)]} {
            $ctext tag bind $lk <1> \
        $ctext tag conf $lk -foreground blue
        if {[info exists commitrow($curview,$id)]} {
            $ctext tag bind $lk <1> \
@@ -3605,6 +3797,18 @@ proc appendrefs {pos l var} {
     return [llength $tags]
 }
 
     return [llength $tags]
 }
 
+proc taglist {ids} {
+    global idtags
+
+    set tags {}
+    foreach id $ids {
+       foreach tag $idtags($id) {
+           lappend tags $tag
+       }
+    }
+    return $tags
+}
+
 # called when we have finished computing the nearby tags
 proc dispneartags {} {
     global selectedline currentid ctext anc_tags desc_tags showneartags
 # called when we have finished computing the nearby tags
 proc dispneartags {} {
     global selectedline currentid ctext anc_tags desc_tags showneartags
@@ -3614,15 +3818,15 @@ proc dispneartags {} {
     set id $currentid
     $ctext conf -state normal
     if {[info exists desc_heads($id)]} {
     set id $currentid
     $ctext conf -state normal
     if {[info exists desc_heads($id)]} {
-       if {[appendrefs branch $desc_heads($id) idheads] > 1} {
+       if {[appendrefs branch $desc_heads($id) headids] > 1} {
            $ctext insert "branch -2c" "es"
        }
     }
     if {[info exists anc_tags($id)]} {
            $ctext insert "branch -2c" "es"
        }
     }
     if {[info exists anc_tags($id)]} {
-       appendrefs follows $anc_tags($id) idtags
+       appendrefs follows [taglist $anc_tags($id)] tagids
     }
     if {[info exists desc_tags($id)]} {
     }
     if {[info exists desc_tags($id)]} {
-       appendrefs precedes $desc_tags($id) idtags
+       appendrefs precedes [taglist $desc_tags($id)] tagids
     }
     $ctext conf -state disabled
 }
     }
     $ctext conf -state disabled
 }
@@ -3720,7 +3924,7 @@ proc selectline {l isnew} {
        }
        $ctext insert end "\n"
     }
        }
        $ctext insert end "\n"
     }
+
     set headers {}
     set olds [lindex $parentlist $l]
     if {[llength $olds] > 1} {
     set headers {}
     set olds [lindex $parentlist $l]
     if {[llength $olds] > 1} {
@@ -3755,7 +3959,7 @@ proc selectline {l isnew} {
        $ctext mark set branch "end -1c"
        $ctext mark gravity branch left
        if {[info exists desc_heads($id)]} {
        $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} {
+           if {[appendrefs branch $desc_heads($id) headids] > 1} {
                # turn "Branch" into "Branches"
                $ctext insert "branch -2c" "es"
            }
                # turn "Branch" into "Branches"
                $ctext insert "branch -2c" "es"
            }
@@ -3764,13 +3968,13 @@ proc selectline {l isnew} {
        $ctext mark set follows "end -1c"
        $ctext mark gravity follows left
        if {[info exists anc_tags($id)]} {
        $ctext mark set follows "end -1c"
        $ctext mark gravity follows left
        if {[info exists anc_tags($id)]} {
-           appendrefs follows $anc_tags($id) idtags
+           appendrefs follows [taglist $anc_tags($id)] tagids
        }
        $ctext insert end "\nPrecedes: "
        $ctext mark set precedes "end -1c"
        $ctext mark gravity precedes left
        if {[info exists desc_tags($id)]} {
        }
        $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
+           appendrefs precedes [taglist $desc_tags($id)] tagids
        }
        $ctext insert end "\n"
     }
        }
        $ctext insert end "\n"
     }
@@ -3829,7 +4033,7 @@ proc selnextpage {dir} {
         set l [expr $numcommits - 1]
     }
     unmarkmatches
         set l [expr $numcommits - 1]
     }
     unmarkmatches
-    selectline $l 1    
+    selectline $l 1
 }
 
 proc unselectline {} {
 }
 
 proc unselectline {} {
@@ -3866,11 +4070,11 @@ proc addtohistory {cmd} {
     }
     incr historyindex
     if {$historyindex > 1} {
     }
     incr historyindex
     if {$historyindex > 1} {
-       .ctop.top.bar.leftbut conf -state normal
+       .tf.bar.leftbut conf -state normal
     } else {
     } else {
-       .ctop.top.bar.leftbut conf -state disabled
+       .tf.bar.leftbut conf -state disabled
     }
     }
-    .ctop.top.bar.rightbut conf -state disabled
+    .tf.bar.rightbut conf -state disabled
 }
 
 proc godo {elt} {
 }
 
 proc godo {elt} {
@@ -3890,10 +4094,10 @@ proc goback {} {
     if {$historyindex > 1} {
        incr historyindex -1
        godo [lindex $history [expr {$historyindex - 1}]]
     if {$historyindex > 1} {
        incr historyindex -1
        godo [lindex $history [expr {$historyindex - 1}]]
-       .ctop.top.bar.rightbut conf -state normal
+       .tf.bar.rightbut conf -state normal
     }
     if {$historyindex <= 1} {
     }
     if {$historyindex <= 1} {
-       .ctop.top.bar.leftbut conf -state disabled
+       .tf.bar.leftbut conf -state disabled
     }
 }
 
     }
 }
 
@@ -3904,10 +4108,10 @@ proc goforw {} {
        set cmd [lindex $history $historyindex]
        incr historyindex
        godo $cmd
        set cmd [lindex $history $historyindex]
        incr historyindex
        godo $cmd
-       .ctop.top.bar.leftbut conf -state normal
+       .tf.bar.leftbut conf -state normal
     }
     if {$historyindex >= [llength $history]} {
     }
     if {$historyindex >= [llength $history]} {
-       .ctop.top.bar.rightbut conf -state disabled
+       .tf.bar.rightbut conf -state disabled
     }
 }
 
     }
 }
 
@@ -4263,12 +4467,27 @@ proc getblobdiffline {bdf ids} {
     }
 }
 
     }
 }
 
+proc prevfile {} {
+    global difffilestart ctext
+    set prev [lindex $difffilestart 0]
+    set here [$ctext index @0,0]
+    foreach loc $difffilestart {
+       if {[$ctext compare $loc >= $here]} {
+           $ctext yview $prev
+           return
+       }
+       set prev $loc
+    }
+    $ctext yview $prev
+}
+
 proc nextfile {} {
     global difffilestart ctext
     set here [$ctext index @0,0]
     foreach loc $difffilestart {
        if {[$ctext compare $loc > $here]} {
            $ctext yview $loc
 proc nextfile {} {
     global difffilestart ctext
     set here [$ctext index @0,0]
     foreach loc $difffilestart {
        if {[$ctext compare $loc > $here]} {
            $ctext yview $loc
+           return
        }
     }
 }
        }
     }
 }
@@ -4399,7 +4618,7 @@ proc searchmarkvisible {doall} {
 proc scrolltext {f0 f1} {
     global searchstring
 
 proc scrolltext {f0 f1} {
     global searchstring
 
-    .ctop.cdet.left.sb set $f0 $f1
+    .bleft.sb set $f0 $f1
     if {$searchstring ne {}} {
        searchmarkvisible 0
     }
     if {$searchstring ne {}} {
        searchmarkvisible 0
     }
@@ -4431,6 +4650,7 @@ proc redisplay {} {
     drawvisible
     if {[info exists selectedline]} {
        selectline $selectedline 0
     drawvisible
     if {[info exists selectedline]} {
        selectline $selectedline 0
+       allcanvs yview moveto [lindex $span 0]
     }
 }
 
     }
 }
 
@@ -4574,7 +4794,8 @@ proc linehover {} {
     set t [$canv create rectangle $x0 $y0 $x1 $y1 \
               -fill \#ffff80 -outline black -width 1 -tags hover]
     $canv raise $t
     set t [$canv create rectangle $x0 $y0 $x1 $y1 \
               -fill \#ffff80 -outline black -width 1 -tags hover]
     $canv raise $t
-    set t [$canv create text $x $y -anchor nw -text $text -tags hover -font $mainfont]
+    set t [$canv create text $x $y -anchor nw -text $text -tags hover \
+              -font $mainfont]
     $canv raise $t
 }
 
     $canv raise $t
 }
 
@@ -4705,9 +4926,9 @@ proc rowmenu {x y id} {
     } else {
        set state normal
     }
     } else {
        set state normal
     }
-    $rowctxmenu entryconfigure 0 -state $state
-    $rowctxmenu entryconfigure 1 -state $state
-    $rowctxmenu entryconfigure 2 -state $state
+    $rowctxmenu entryconfigure "Diff this*" -state $state
+    $rowctxmenu entryconfigure "Diff selected*" -state $state
+    $rowctxmenu entryconfigure "Make patch" -state $state
     set rowmenuid $id
     tk_popup $rowctxmenu $x $y
 }
     set rowmenuid $id
     tk_popup $rowctxmenu $x $y
 }
@@ -4897,6 +5118,7 @@ proc domktag {} {
     set tagids($tag) $id
     lappend idtags($id) $tag
     redrawtags $id
     set tagids($tag) $id
     lappend idtags($id) $tag
     redrawtags $id
+    addedtag $id
 }
 
 proc redrawtags {id} {
 }
 
 proc redrawtags {id} {
@@ -4987,10 +5209,164 @@ proc wrcomcan {} {
     unset wrcomtop
 }
 
     unset wrcomtop
 }
 
+proc mkbranch {} {
+    global rowmenuid mkbrtop
+
+    set top .makebranch
+    catch {destroy $top}
+    toplevel $top
+    label $top.title -text "Create new branch"
+    grid $top.title - -pady 10
+    label $top.id -text "ID:"
+    entry $top.sha1 -width 40 -relief flat
+    $top.sha1 insert 0 $rowmenuid
+    $top.sha1 conf -state readonly
+    grid $top.id $top.sha1 -sticky w
+    label $top.nlab -text "Name:"
+    entry $top.name -width 40
+    grid $top.nlab $top.name -sticky w
+    frame $top.buts
+    button $top.buts.go -text "Create" -command [list mkbrgo $top]
+    button $top.buts.can -text "Cancel" -command "catch {destroy $top}"
+    grid $top.buts.go $top.buts.can
+    grid columnconfigure $top.buts 0 -weight 1 -uniform a
+    grid columnconfigure $top.buts 1 -weight 1 -uniform a
+    grid $top.buts - -pady 10 -sticky ew
+    focus $top.name
+}
+
+proc mkbrgo {top} {
+    global headids idheads
+
+    set name [$top.name get]
+    set id [$top.sha1 get]
+    if {$name eq {}} {
+       error_popup "Please specify a name for the new branch"
+       return
+    }
+    catch {destroy $top}
+    nowbusy newbranch
+    update
+    if {[catch {
+       exec git branch $name $id
+    } err]} {
+       notbusy newbranch
+       error_popup $err
+    } else {
+       addedhead $id $name
+       # XXX should update list of heads displayed for selected commit
+       notbusy newbranch
+       redrawtags $id
+    }
+}
+
+proc cherrypick {} {
+    global rowmenuid curview commitrow
+    global mainhead desc_heads anc_tags desc_tags allparents allchildren
+
+    if {[info exists desc_heads($rowmenuid)]
+       && [lsearch -exact $desc_heads($rowmenuid) $mainhead] >= 0} {
+       set ok [confirm_popup "Commit [string range $rowmenuid 0 7] is already\
+                       included in branch $mainhead -- really re-apply it?"]
+       if {!$ok} return
+    }
+    nowbusy cherrypick
+    update
+    set oldhead [exec git rev-parse HEAD]
+    # Unfortunately git-cherry-pick writes stuff to stderr even when
+    # no error occurs, and exec takes that as an indication of error...
+    if {[catch {exec sh -c "git cherry-pick -r $rowmenuid 2>&1"} err]} {
+       notbusy cherrypick
+       error_popup $err
+       return
+    }
+    set newhead [exec git rev-parse HEAD]
+    if {$newhead eq $oldhead} {
+       notbusy cherrypick
+       error_popup "No changes committed"
+       return
+    }
+    set allparents($newhead) $oldhead
+    lappend allchildren($oldhead) $newhead
+    set desc_heads($newhead) $mainhead
+    if {[info exists anc_tags($oldhead)]} {
+       set anc_tags($newhead) $anc_tags($oldhead)
+    }
+    set desc_tags($newhead) {}
+    if {[info exists commitrow($curview,$oldhead)]} {
+       insertrow $commitrow($curview,$oldhead) $newhead
+       if {$mainhead ne {}} {
+           movedhead $newhead $mainhead
+       }
+       redrawtags $oldhead
+       redrawtags $newhead
+    }
+    notbusy cherrypick
+}
+
+# context menu for a head
+proc headmenu {x y id head} {
+    global headmenuid headmenuhead headctxmenu
+
+    set headmenuid $id
+    set headmenuhead $head
+    tk_popup $headctxmenu $x $y
+}
+
+proc cobranch {} {
+    global headmenuid headmenuhead mainhead headids
+
+    # check the tree is clean first??
+    set oldmainhead $mainhead
+    nowbusy checkout
+    update
+    if {[catch {
+       exec git checkout $headmenuhead
+    } err]} {
+       notbusy checkout
+       error_popup $err
+    } else {
+       notbusy checkout
+       set mainhead $headmenuhead
+       if {[info exists headids($oldmainhead)]} {
+           redrawtags $headids($oldmainhead)
+       }
+       redrawtags $headmenuid
+    }
+}
+
+proc rmbranch {} {
+    global desc_heads headmenuid headmenuhead mainhead
+    global headids idheads
+
+    set head $headmenuhead
+    set id $headmenuid
+    if {$head eq $mainhead} {
+       error_popup "Cannot delete the currently checked-out branch"
+       return
+    }
+    if {$desc_heads($id) eq $head} {
+       # the stuff on this branch isn't on any other branch
+       if {![confirm_popup "The commits on branch $head aren't on any other\
+                       branch.\nReally delete branch $head?"]} return
+    }
+    nowbusy rmbranch
+    update
+    if {[catch {exec git branch -D $head} err]} {
+       notbusy rmbranch
+       error_popup $err
+       return
+    }
+    removedhead $id $head
+    redrawtags $id
+    notbusy rmbranch
+}
+
 # Stuff for finding nearby tags
 proc getallcommits {} {
 # Stuff for finding nearby tags
 proc getallcommits {} {
-    global allcstart allcommits allcfd
+    global allcstart allcommits allcfd allids
 
 
+    set allids {}
     set fd [open [concat | git rev-list --all --topo-order --parents] r]
     set allcfd $fd
     fconfigure $fd -blocking 0
     set fd [open [concat | git rev-list --all --topo-order --parents] r]
     set allcfd $fd
     fconfigure $fd -blocking 0
@@ -5074,10 +5450,52 @@ proc combine_atags {l1 l2} {
     return $res
 }
 
     return $res
 }
 
+proc forward_pass {id children} {
+    global idtags desc_tags idheads desc_heads alldtags tagisdesc
+
+    set dtags {}
+    set dheads {}
+    foreach child $children {
+       if {[info exists idtags($child)]} {
+           set ctags [list $child]
+       } else {
+           set ctags $desc_tags($child)
+       }
+       if {$dtags eq {}} {
+           set dtags $ctags
+       } elseif {$ctags ne $dtags} {
+           set dtags [combine_dtags $dtags $ctags]
+       }
+       set cheads $desc_heads($child)
+       if {$dheads eq {}} {
+           set dheads $cheads
+       } elseif {$cheads ne $dheads} {
+           set dheads [lsort -unique [concat $dheads $cheads]]
+       }
+    }
+    set desc_tags($id) $dtags
+    if {[info exists idtags($id)]} {
+       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)]} {
+       set dheads [concat $dheads $idheads($id)]
+    }
+    set desc_heads($id) $dheads
+}
+
 proc getallclines {fd} {
     global allparents allchildren allcommits allcstart
 proc getallclines {fd} {
     global allparents allchildren allcommits allcstart
-    global desc_tags anc_tags idtags alldtags tagisdesc allids
-    global desc_heads idheads
+    global desc_tags anc_tags idtags tagisdesc allids
+    global idheads travindex
 
     while {[gets $fd line] >= 0} {
        set id [lindex $line 0]
 
     while {[gets $fd line] >= 0} {
        set id [lindex $line 0]
@@ -5092,43 +5510,7 @@ proc getallclines {fd} {
        }
        # compute nearest tagged descendents as we go
        # also compute descendent heads
        }
        # 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
+       forward_pass $id $allchildren($id)
        if {[clock clicks -milliseconds] - $allcstart >= 50} {
            fileevent $fd readable {}
            after idle restartgetall $fd
        if {[clock clicks -milliseconds] - $allcstart >= 50} {
            fileevent $fd readable {}
            after idle restartgetall $fd
@@ -5136,7 +5518,9 @@ proc getallclines {fd} {
        }
     }
     if {[eof $fd]} {
        }
     }
     if {[eof $fd]} {
-       after idle restartatags [llength $allids]
+       set travindex [llength $allids]
+       set allcommits "traversing"
+       after idle restartatags
        if {[catch {close $fd} err]} {
            error_popup "Error reading full commit graph: $err.\n\
                         Results may be incomplete."
        if {[catch {close $fd} err]} {
            error_popup "Error reading full commit graph: $err.\n\
                         Results may be incomplete."
@@ -5145,10 +5529,11 @@ proc getallclines {fd} {
 }
 
 # walk backward through the tree and compute nearest tagged ancestors
 }
 
 # walk backward through the tree and compute nearest tagged ancestors
-proc restartatags {i} {
-    global allids allparents idtags anc_tags t0
+proc restartatags {} {
+    global allids allparents idtags anc_tags travindex
 
     set t0 [clock clicks -milliseconds]
 
     set t0 [clock clicks -milliseconds]
+    set i $travindex
     while {[incr i -1] >= 0} {
        set id [lindex $allids $i]
        set atags {}
     while {[incr i -1] >= 0} {
        set id [lindex $allids $i]
        set atags {}
@@ -5166,17 +5551,195 @@ proc restartatags {i} {
        }
        set anc_tags($id) $atags
        if {[clock clicks -milliseconds] - $t0 >= 50} {
        }
        set anc_tags($id) $atags
        if {[clock clicks -milliseconds] - $t0 >= 50} {
-           after idle restartatags $i
+           set travindex $i
+           after idle restartatags
            return
        }
     }
     set allcommits "done"
            return
        }
     }
     set allcommits "done"
+    set travindex 0
     notbusy allcommits
     dispneartags
 }
 
     notbusy allcommits
     dispneartags
 }
 
+# update the desc_tags and anc_tags arrays for a new tag just added
+proc addedtag {id} {
+    global desc_tags anc_tags allparents allchildren allcommits
+    global idtags tagisdesc alldtags
+
+    if {![info exists desc_tags($id)]} return
+    set adt $desc_tags($id)
+    foreach t $desc_tags($id) {
+       set adt [concat $adt $alldtags($t)]
+    }
+    set adt [lsort -unique $adt]
+    set alldtags($id) $adt
+    foreach t $adt {
+       set tagisdesc($id,$t) -1
+       set tagisdesc($t,$id) 1
+    }
+    if {[info exists anc_tags($id)]} {
+       set todo $anc_tags($id)
+       while {$todo ne {}} {
+           set do [lindex $todo 0]
+           set todo [lrange $todo 1 end]
+           if {[info exists tagisdesc($id,$do)]} continue
+           set tagisdesc($do,$id) -1
+           set tagisdesc($id,$do) 1
+           if {[info exists anc_tags($do)]} {
+               set todo [concat $todo $anc_tags($do)]
+           }
+       }
+    }
+
+    set lastold $desc_tags($id)
+    set lastnew [list $id]
+    set nup 0
+    set nch 0
+    set todo $allparents($id)
+    while {$todo ne {}} {
+       set do [lindex $todo 0]
+       set todo [lrange $todo 1 end]
+       if {![info exists desc_tags($do)]} continue
+       if {$desc_tags($do) ne $lastold} {
+           set lastold $desc_tags($do)
+           set lastnew [combine_dtags $lastold [list $id]]
+           incr nch
+       }
+       if {$lastold eq $lastnew} continue
+       set desc_tags($do) $lastnew
+       incr nup
+       if {![info exists idtags($do)]} {
+           set todo [concat $todo $allparents($do)]
+       }
+    }
+
+    if {![info exists anc_tags($id)]} return
+    set lastold $anc_tags($id)
+    set lastnew [list $id]
+    set nup 0
+    set nch 0
+    set todo $allchildren($id)
+    while {$todo ne {}} {
+       set do [lindex $todo 0]
+       set todo [lrange $todo 1 end]
+       if {![info exists anc_tags($do)]} continue
+       if {$anc_tags($do) ne $lastold} {
+           set lastold $anc_tags($do)
+           set lastnew [combine_atags $lastold [list $id]]
+           incr nch
+       }
+       if {$lastold eq $lastnew} continue
+       set anc_tags($do) $lastnew
+       incr nup
+       if {![info exists idtags($do)]} {
+           set todo [concat $todo $allchildren($do)]
+       }
+    }
+}
+
+# update the desc_heads array for a new head just added
+proc addedhead {hid head} {
+    global desc_heads allparents headids idheads
+
+    set headids($head) $hid
+    lappend idheads($hid) $head
+
+    set todo [list $hid]
+    while {$todo ne {}} {
+       set do [lindex $todo 0]
+       set todo [lrange $todo 1 end]
+       if {![info exists desc_heads($do)] ||
+           [lsearch -exact $desc_heads($do) $head] >= 0} continue
+       set oldheads $desc_heads($do)
+       lappend desc_heads($do) $head
+       set heads $desc_heads($do)
+       while {1} {
+           set p $allparents($do)
+           if {[llength $p] != 1 || ![info exists desc_heads($p)] ||
+               $desc_heads($p) ne $oldheads} break
+           set do $p
+           set desc_heads($do) $heads
+       }
+       set todo [concat $todo $p]
+    }
+}
+
+# update the desc_heads array for a head just removed
+proc removedhead {hid head} {
+    global desc_heads allparents headids idheads
+
+    unset headids($head)
+    if {$idheads($hid) eq $head} {
+       unset idheads($hid)
+    } else {
+       set i [lsearch -exact $idheads($hid) $head]
+       if {$i >= 0} {
+           set idheads($hid) [lreplace $idheads($hid) $i $i]
+       }
+    }
+
+    set todo [list $hid]
+    while {$todo ne {}} {
+       set do [lindex $todo 0]
+       set todo [lrange $todo 1 end]
+       if {![info exists desc_heads($do)]} continue
+       set i [lsearch -exact $desc_heads($do) $head]
+       if {$i < 0} continue
+       set oldheads $desc_heads($do)
+       set heads [lreplace $desc_heads($do) $i $i]
+       while {1} {
+           set desc_heads($do) $heads
+           set p $allparents($do)
+           if {[llength $p] != 1 || ![info exists desc_heads($p)] ||
+               $desc_heads($p) ne $oldheads} break
+           set do $p
+       }
+       set todo [concat $todo $p]
+    }
+}
+
+# update things for a head moved to a child of its previous location
+proc movedhead {id name} {
+    global headids idheads
+
+    set oldid $headids($name)
+    set headids($name) $id
+    if {$idheads($oldid) eq $name} {
+       unset idheads($oldid)
+    } else {
+       set i [lsearch -exact $idheads($oldid) $name]
+       if {$i >= 0} {
+           set idheads($oldid) [lreplace $idheads($oldid) $i $i]
+       }
+    }
+    lappend idheads($id) $name
+}
+
+proc changedrefs {} {
+    global desc_heads desc_tags anc_tags allcommits allids
+    global allchildren allparents idtags travindex
+
+    if {![info exists allcommits]} return
+    catch {unset desc_heads}
+    catch {unset desc_tags}
+    catch {unset anc_tags}
+    catch {unset alldtags}
+    catch {unset tagisdesc}
+    foreach id $allids {
+       forward_pass $id $allchildren($id)
+    }
+    if {$allcommits ne "reading"} {
+       set travindex [llength $allids]
+       if {$allcommits ne "traversing"} {
+           set allcommits "traversing"
+           after idle restartatags
+       }
+    }
+}
+
 proc rereadrefs {} {
 proc rereadrefs {} {
-    global idtags idheads idotherrefs
+    global idtags idheads idotherrefs mainhead
 
     set refids [concat [array names idtags] \
                    [array names idheads] [array names idotherrefs]]
 
     set refids [concat [array names idtags] \
                    [array names idheads] [array names idotherrefs]]
@@ -5185,12 +5748,16 @@ proc rereadrefs {} {
            set ref($id) [listrefs $id]
        }
     }
            set ref($id) [listrefs $id]
        }
     }
+    set oldmainhead $mainhead
     readrefs
     readrefs
+    changedrefs
     set refids [lsort -unique [concat $refids [array names idtags] \
                        [array names idheads] [array names idotherrefs]]]
     foreach id $refids {
        set v [listrefs $id]
     set refids [lsort -unique [concat $refids [array names idtags] \
                        [array names idheads] [array names idotherrefs]]]
     foreach id $refids {
        set v [listrefs $id]
-       if {![info exists ref($id)] || $ref($id) != $v} {
+       if {![info exists ref($id)] || $ref($id) != $v ||
+           ($id eq $oldmainhead && $id ne $mainhead) ||
+           ($id eq $mainhead && $id ne $oldmainhead)} {
            redrawtags $id
        }
     }
            redrawtags $id
        }
     }
@@ -5236,12 +5803,14 @@ proc showtag {tag isnew} {
 proc doquit {} {
     global stopped
     set stopped 100
 proc doquit {} {
     global stopped
     set stopped 100
+    savestuff .
     destroy .
 }
 
 proc doprefs {} {
     global maxwidth maxgraphpct diffopts
     global oldprefs prefstop showneartags
     destroy .
 }
 
 proc doprefs {} {
     global maxwidth maxgraphpct diffopts
     global oldprefs prefstop showneartags
+    global bgcolor fgcolor ctext diffcolors
 
     set top .gitkprefs
     set prefstop $top
 
     set top .gitkprefs
     set prefstop $top
@@ -5265,6 +5834,7 @@ 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
+
     label $top.ddisp -text "Diff display options"
     grid $top.ddisp - -sticky w -pady 10
     label $top.diffoptl -text "Options for diff program" \
     label $top.ddisp -text "Diff display options"
     grid $top.ddisp - -sticky w -pady 10
     label $top.diffoptl -text "Options for diff program" \
@@ -5276,6 +5846,34 @@ proc doprefs {} {
     checkbutton $top.ntag.b -variable showneartags
     pack $top.ntag.b $top.ntag.l -side left
     grid x $top.ntag -sticky w
     checkbutton $top.ntag.b -variable showneartags
     pack $top.ntag.b $top.ntag.l -side left
     grid x $top.ntag -sticky w
+
+    label $top.cdisp -text "Colors: press to choose"
+    grid $top.cdisp - -sticky w -pady 10
+    label $top.bg -padx 40 -relief sunk -background $bgcolor
+    button $top.bgbut -text "Background" -font optionfont \
+       -command [list choosecolor bgcolor 0 $top.bg background setbg]
+    grid x $top.bgbut $top.bg -sticky w
+    label $top.fg -padx 40 -relief sunk -background $fgcolor
+    button $top.fgbut -text "Foreground" -font optionfont \
+       -command [list choosecolor fgcolor 0 $top.fg foreground setfg]
+    grid x $top.fgbut $top.fg -sticky w
+    label $top.diffold -padx 40 -relief sunk -background [lindex $diffcolors 0]
+    button $top.diffoldbut -text "Diff: old lines" -font optionfont \
+       -command [list choosecolor diffcolors 0 $top.diffold "diff old lines" \
+                     [list $ctext tag conf d0 -foreground]]
+    grid x $top.diffoldbut $top.diffold -sticky w
+    label $top.diffnew -padx 40 -relief sunk -background [lindex $diffcolors 1]
+    button $top.diffnewbut -text "Diff: new lines" -font optionfont \
+       -command [list choosecolor diffcolors 1 $top.diffnew "diff new lines" \
+                     [list $ctext tag conf d1 -foreground]]
+    grid x $top.diffnewbut $top.diffnew -sticky w
+    label $top.hunksep -padx 40 -relief sunk -background [lindex $diffcolors 2]
+    button $top.hunksepbut -text "Diff: hunk header" -font optionfont \
+       -command [list choosecolor diffcolors 2 $top.hunksep \
+                     "diff hunk header" \
+                     [list $ctext tag conf hunksep -foreground]]
+    grid x $top.hunksepbut $top.hunksep -sticky w
+
     frame $top.buts
     button $top.buts.ok -text "OK" -command prefsok
     button $top.buts.can -text "Cancel" -command prefscan
     frame $top.buts
     button $top.buts.ok -text "OK" -command prefsok
     button $top.buts.can -text "Cancel" -command prefscan
@@ -5285,6 +5883,35 @@ proc doprefs {} {
     grid $top.buts - - -pady 10 -sticky ew
 }
 
     grid $top.buts - - -pady 10 -sticky ew
 }
 
+proc choosecolor {v vi w x cmd} {
+    global $v
+
+    set c [tk_chooseColor -initialcolor [lindex [set $v] $vi] \
+              -title "Gitk: choose color for $x"]
+    if {$c eq {}} return
+    $w conf -background $c
+    lset $v $vi $c
+    eval $cmd $c
+}
+
+proc setbg {c} {
+    global bglist
+
+    foreach w $bglist {
+       $w conf -background $c
+    }
+}
+
+proc setfg {c} {
+    global fglist canv
+
+    foreach w $fglist {
+       $w conf -foreground $c
+    }
+    allcanvs itemconf text -fill $c
+    $canv itemconf circle -outline $c
+}
+
 proc prefscan {} {
     global maxwidth maxgraphpct diffopts
     global oldprefs prefstop showneartags
 proc prefscan {} {
     global maxwidth maxgraphpct diffopts
     global oldprefs prefstop showneartags
@@ -5594,7 +6221,7 @@ 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 config --get i18n.commitencoding]
 }
 if {$gitencoding == ""} {
     set gitencoding "utf-8"
 }
 if {$gitencoding == ""} {
     set gitencoding "utf-8"
@@ -5620,6 +6247,9 @@ set wrapcomment "none"
 set showneartags 1
 
 set colors {green red blue magenta darkgrey brown orange}
 set showneartags 1
 
 set colors {green red blue magenta darkgrey brown orange}
+set bgcolor white
+set fgcolor black
+set diffcolors {red "#00a000" blue}
 
 catch {source ~/.gitk}
 
 
 catch {source ~/.gitk}
 
@@ -5691,6 +6321,7 @@ set stuffsaved 0
 set patchnum 0
 setcoords
 makewindow
 set patchnum 0
 setcoords
 makewindow
+wm title . "[file tail $argv0]: [file tail [pwd]]"
 readrefs
 
 if {$cmdline_files ne {} || $revtreeargs ne {}} {
 readrefs
 
 if {$cmdline_files ne {} || $revtreeargs ne {}} {
@@ -5703,8 +6334,8 @@ if {$cmdline_files ne {} || $revtreeargs ne {}} {
     set viewargs(1) $revtreeargs
     set viewperm(1) 0
     addviewmenu 1
     set viewargs(1) $revtreeargs
     set viewperm(1) 0
     addviewmenu 1
-    .bar.view entryconf 2 -state normal
-    .bar.view entryconf 3 -state normal
+    .bar.view entryconf Edit* -state normal
+    .bar.view entryconf Delete* -state normal
 }
 
 if {[info exists permviews]} {
 }
 
 if {[info exists permviews]} {