gitk: Use mktemp -d to avoid predictable temporary directories
[gitweb.git] / gitk
diff --git a/gitk b/gitk
index db5ec54be879e3ad194c2718468a499882c75733..41e5071c4bdce3767f7f2782dd97b448fb153022 100755 (executable)
--- a/gitk
+++ b/gitk
@@ -2,18 +2,45 @@
 # Tcl ignores the next line -*- tcl -*- \
 exec wish "$0" -- "$@"
 
-# Copyright © 2005-2008 Paul Mackerras.  All rights reserved.
+# Copyright © 2005-2014 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.
 
-proc gitdir {} {
-    global env
-    if {[info exists env(GIT_DIR)]} {
-       return $env(GIT_DIR)
-    } else {
-       return [exec git rev-parse --git-dir]
+package require Tk
+
+proc hasworktree {} {
+    return [expr {[exec git rev-parse --is-bare-repository] == "false" &&
+                 [exec git rev-parse --is-inside-git-dir] == "false"}]
+}
+
+proc reponame {} {
+    global gitdir
+    set n [file normalize $gitdir]
+    if {[string match "*/.git" $n]} {
+       set n [string range $n 0 end-5]
+    }
+    return [file tail $n]
+}
+
+proc gitworktree {} {
+    variable _gitworktree
+    if {[info exists _gitworktree]} {
+       return $_gitworktree
     }
+    # v1.7.0 introduced --show-toplevel to return the canonical work-tree
+    if {[catch {set _gitworktree [exec git rev-parse --show-toplevel]}]} {
+        # try to set work tree from environment, core.worktree or use
+        # cdup to obtain a relative path to the top of the worktree. If
+        # run from the top, the ./ prefix ensures normalize expands pwd.
+        if {[catch { set _gitworktree $env(GIT_WORK_TREE) }]} {
+           catch {set _gitworktree [exec git config --get core.worktree]}
+           if {$_gitworktree eq ""} {
+               set _gitworktree [file normalize ./[exec git rev-parse --show-cdup]]
+           }
+        }
+    }
+    return $_gitworktree
 }
 
 # A simple scheduler for compute-intensive stuff.
@@ -128,10 +155,13 @@ proc unmerged_files {files} {
 }
 
 proc parseviewargs {n arglist} {
-    global vdatemode vmergeonly vflags vdflags vrevs vfiltered vorigargs
+    global vdatemode vmergeonly vflags vdflags vrevs vfiltered vorigargs env
+    global vinlinediff
+    global worddiff git_version
 
     set vdatemode($n) 0
     set vmergeonly($n) 0
+    set vinlinediff($n) 0
     set glflags {}
     set diffargs {}
     set nextisval 0
@@ -166,7 +196,7 @@ proc parseviewargs {n arglist} {
                lappend diffargs $arg
            }
            "--raw" - "--patch-with-raw" - "--patch-with-stat" -
-           "--name-only" - "--name-status" - "--color" - "--color-words" -
+           "--name-only" - "--name-status" - "--color" -
            "--log-size" - "--pretty=*" - "--decorate" - "--abbrev-commit" -
            "--cc" - "-z" - "--header" - "--parents" - "--boundary" -
            "--no-color" - "-g" - "--walk-reflogs" - "--no-walk" -
@@ -175,6 +205,18 @@ proc parseviewargs {n arglist} {
                # These cause our parsing of git log's output to fail, or else
                # they're options we want to set ourselves, so ignore them.
            }
+           "--color-words*" - "--word-diff=color" {
+               # These trigger a word diff in the console interface,
+               # so help the user by enabling our own support
+               if {[package vcompare $git_version "1.7.2"] >= 0} {
+                   set worddiff [mc "Color words"]
+               }
+           }
+           "--word-diff*" {
+               if {[package vcompare $git_version "1.7.2"] >= 0} {
+                   set worddiff [mc "Markup words"]
+               }
+           }
            "--stat=*" - "--numstat" - "--shortstat" - "--summary" -
            "--check" - "--exit-code" - "--quiet" - "--topo-order" -
            "--full-history" - "--dense" - "--sparse" -
@@ -187,12 +229,20 @@ proc parseviewargs {n arglist} {
            "--until=*" - "--before=*" - "--max-age=*" - "--min-age=*" -
            "--author=*" - "--committer=*" - "--grep=*" - "-[iE]" -
            "--remove-empty" - "--first-parent" - "--cherry-pick" -
-           "-S*" - "--pickaxe-all" - "--pickaxe-regex" -
+           "-S*" - "-G*" - "--pickaxe-all" - "--pickaxe-regex" -
            "--simplify-by-decoration" {
                # These mean that we get a subset of the commits
                set filtered 1
                lappend glflags $arg
            }
+           "-L*" {
+               # Line-log with 'stuck' argument (unstuck form is
+               # not supported)
+               set filtered 1
+               set vinlinediff($n) 1
+               set allknown 0
+               lappend glflags $arg
+           }
            "-n" {
                # This appears to be the only one that has a value as a
                # separate word following it
@@ -208,6 +258,9 @@ proc parseviewargs {n arglist} {
                # git rev-parse doesn't understand --merge
                lappend revargs --gitk-symmetric-diff-marker MERGE_HEAD...HEAD
            }
+           "--no-replace-objects" {
+               set env(GIT_NO_REPLACE_OBJECTS) "1"
+           }
            "-*" {
                # Other flag arguments including -<n>
                if {[string is digit -strict [string range $arg 1 end]]} {
@@ -265,7 +318,7 @@ proc parseviewrevs {view revs} {
                }
                lappend badrev $line
            }
-       }                   
+       }
        error_popup "[mc "Error parsing revisions:"] $err"
        return {}
     }
@@ -308,6 +361,7 @@ proc start_rev_list {view} {
     global viewactive viewinstances vmergeonly
     global mainheadid viewmainheadid viewmainheadid_orig
     global vcanopt vflags vrevs vorigargs
+    global show_notes
 
     set startmsecs [clock clicks -milliseconds]
     set commitidx($view) 0
@@ -356,8 +410,8 @@ proc start_rev_list {view} {
     }
 
     if {[catch {
-       set fd [open [concat | git log --no-color -z --pretty=raw --parents \
-                        --boundary $args "--" $files] r]
+       set fd [open [concat | git log --no-color -z --pretty=raw $show_notes \
+                       --parents --boundary $args "--" $files] r]
     } err]} {
        error_popup "[mc "Error executing git log:"] $err"
        return 0
@@ -449,10 +503,11 @@ proc updatecommits {} {
     global viewactive viewcomplete tclencoding
     global startmsecs showneartags showlocalchanges
     global mainheadid viewmainheadid viewmainheadid_orig pending_select
-    global isworktree
+    global hasworktree
     global varcid vposids vnegids vflags vrevs
+    global show_notes
 
-    set isworktree [expr {[exec git rev-parse --is-inside-work-tree] == "true"}]
+    set hasworktree [hasworktree]
     rereadrefs
     set view $curview
     if {$mainheadid ne $viewmainheadid_orig($view)} {
@@ -503,8 +558,8 @@ proc updatecommits {} {
        set args $vorigargs($view)
     }
     if {[catch {
-       set fd [open [concat | git log --no-color -z --pretty=raw --parents \
-                         --boundary $args "--" $vfilelimit($view)] r]
+       set fd [open [concat | git log --no-color -z --pretty=raw $show_notes \
+                       --parents --boundary $args "--" $vfilelimit($view)] r]
     } err]} {
        error_popup "[mc "Error executing git log:"] $err"
        return
@@ -596,12 +651,16 @@ proc varcinit {view} {
 
 proc resetvarcs {view} {
     global varcid varccommits parents children vseedcount ordertok
+    global vshortids
 
     foreach vid [array names varcid $view,*] {
        unset varcid($vid)
        unset children($vid)
        unset parents($vid)
     }
+    foreach vid [array names vshortids $view,*] {
+       unset vshortids($vid)
+    }
     # some commits might have children but haven't been seen yet
     foreach vid [array names children $view,*] {
        unset children($vid)
@@ -639,7 +698,7 @@ proc newvarc {view id} {
        if {![info exists commitinfo($id)]} {
            parsecommit $id $commitdata($id) 1
        }
-       set cdate [lindex $commitinfo($id) 4]
+       set cdate [lindex [lindex $commitinfo($id) 4] 0]
        if {![string is integer -strict $cdate]} {
            set cdate 0
        }
@@ -888,7 +947,7 @@ proc fix_reversal {p a v} {
 proc insertrow {id p v} {
     global cmitlisted children parents varcid varctok vtokmod
     global varccommits ordertok commitidx numcommits curview
-    global targetid targetrow
+    global targetid targetrow vshortids
 
     readcommit $id
     set vid $v,$id
@@ -897,6 +956,7 @@ proc insertrow {id p v} {
     set parents($vid) [list $p]
     set a [newvarc $v $id]
     set varcid($vid) $a
+    lappend vshortids($v,[string range $id 0 3]) $id
     if {[string compare [lindex $varctok($v) $a] $vtokmod($v)] < 0} {
        modify_arc $v $a
     }
@@ -987,6 +1047,18 @@ proc removefakerow {id} {
     drawvisible
 }
 
+proc real_children {vp} {
+    global children nullid nullid2
+
+    set kids {}
+    foreach id $children($vp) {
+       if {$id ne $nullid && $id ne $nullid2} {
+           lappend kids $id
+       }
+    }
+    return $kids
+}
+
 proc first_real_child {vp} {
     global children nullid nullid2
 
@@ -1340,7 +1412,7 @@ proc getcommitlines {fd inst view updating}  {
     global commitidx commitdata vdatemode
     global parents children curview hlview
     global idpending ordertok
-    global varccommits varcid varctok vtokmod vfilelimit
+    global varccommits varcid varctok vtokmod vfilelimit vshortids
 
     set stuff [read $fd 500000]
     # git log doesn't terminate the last commit with a null...
@@ -1440,6 +1512,8 @@ proc getcommitlines {fd inst view updating}  {
        set id [lindex $ids 0]
        set vid $view,$id
 
+       lappend vshortids($view,[string range $id 0 3]) $id
+
        if {!$listed && $updating && ![info exists varcid($vid)] &&
            $vfilelimit($view) ne {}} {
            # git log doesn't rewrite parents for unlisted commits
@@ -1589,7 +1663,7 @@ proc readcommit {id} {
 }
 
 proc parsecommit {id contents listed} {
-    global commitinfo cdate
+    global commitinfo
 
     set inhdr 1
     set comment {}
@@ -1609,10 +1683,10 @@ proc parsecommit {id contents listed} {
        set line [split $line " "]
        set tag [lindex $line 0]
        if {$tag == "author"} {
-           set audate [lindex $line end-1]
+           set audate [lrange $line end-1 end]
            set auname [join [lrange $line 1 end-2] " "]
        } elseif {$tag == "committer"} {
-           set comdate [lindex $line end-1]
+           set comdate [lrange $line end-1 end]
            set comname [join [lrange $line 1 end-2] " "]
        }
     }
@@ -1639,11 +1713,18 @@ proc parsecommit {id contents listed} {
        }
        set comment $newcomment
     }
-    if {$comdate != {}} {
-       set cdate($id) $comdate
+    set hasnote [string first "\nNotes:\n" $contents]
+    set diff ""
+    # If there is diff output shown in the git-log stream, split it
+    # out.  But get rid of the empty line that always precedes the
+    # diff.
+    set i [string first "\n\ndiff" $comment]
+    if {$i >= 0} {
+       set diff [string range $comment $i+1 end]
+       set comment [string range $comment 0 $i-1]
     }
     set commitinfo($id) [list $headline $auname $audate \
-                            $comname $comdate $comment]
+                            $comname $comdate $comment $hasnote $diff]
 }
 
 proc getcommit {id} {
@@ -1664,11 +1745,26 @@ proc getcommit {id} {
 # and are present in the current view.
 # This is fairly slow...
 proc longid {prefix} {
-    global varcid curview
+    global varcid curview vshortids
 
     set ids {}
-    foreach match [array names varcid "$curview,$prefix*"] {
-       lappend ids [lindex [split $match ","] 1]
+    if {[string length $prefix] >= 4} {
+       set vshortid $curview,[string range $prefix 0 3]
+       if {[info exists vshortids($vshortid)]} {
+           foreach id $vshortids($vshortid) {
+               if {[string match "$prefix*" $id]} {
+                   if {[lsearch -exact $ids $id] < 0} {
+                       lappend ids $id
+                       if {[llength $ids] >= 2} break
+                   }
+               }
+           }
+       }
+    } else {
+       foreach match [array names varcid "$curview,$prefix*"] {
+           lappend ids [lindex [split $match ","] 1]
+           if {[llength $ids] >= 2} break
+       }
     }
     return $ids
 }
@@ -1769,6 +1865,15 @@ proc removehead {id name} {
     unset headids($name)
 }
 
+proc ttk_toplevel {w args} {
+    global use_ttk
+    eval [linsert $args 0 ::toplevel $w]
+    if {$use_ttk} {
+        place [ttk::frame $w._toplevel_background] -x 0 -y 0 -relwidth 1 -relheight 1
+    }
+    return $w
+}
+
 proc make_transient {window origin} {
     global have_tk85
 
@@ -1788,9 +1893,12 @@ proc make_transient {window origin} {
 }
 
 proc show_error {w top msg {mc mc}} {
+    global NS
+    if {![info exists NS]} {set NS ""}
+    if {[wm state $top] eq "withdrawn"} { wm deiconify $top }
     message $w.m -text $msg -justify center -aspect 400
     pack $w.m -side top -fill x -padx 20 -pady 20
-    button $w.ok -text [$mc OK] -command "destroy $top"
+    ${NS}::button $w.ok -default active -text [$mc OK] -command "destroy $top"
     pack $w.ok -side bottom -fill x
     bind $top <Visibility> "grab $top; focus $top"
     bind $top <Key-Return> "destroy $top"
@@ -1800,45 +1908,59 @@ proc show_error {w top msg {mc mc}} {
 }
 
 proc error_popup {msg {owner .}} {
-    set w .error
-    toplevel $w
-    make_transient $w $owner
-    show_error $w $w $msg
+    if {[tk windowingsystem] eq "win32"} {
+        tk_messageBox -icon error -type ok -title [wm title .] \
+            -parent $owner -message $msg
+    } else {
+        set w .error
+        ttk_toplevel $w
+        make_transient $w $owner
+        show_error $w $w $msg
+    }
 }
 
 proc confirm_popup {msg {owner .}} {
-    global confirm_ok
+    global confirm_ok NS
     set confirm_ok 0
     set w .confirm
-    toplevel $w
+    ttk_toplevel $w
     make_transient $w $owner
     message $w.m -text $msg -justify center -aspect 400
     pack $w.m -side top -fill x -padx 20 -pady 20
-    button $w.ok -text [mc OK] -command "set confirm_ok 1; destroy $w"
+    ${NS}::button $w.ok -text [mc OK] -command "set confirm_ok 1; destroy $w"
     pack $w.ok -side left -fill x
-    button $w.cancel -text [mc Cancel] -command "destroy $w"
+    ${NS}::button $w.cancel -text [mc Cancel] -command "destroy $w"
     pack $w.cancel -side right -fill x
     bind $w <Visibility> "grab $w; focus $w"
     bind $w <Key-Return> "set confirm_ok 1; destroy $w"
     bind $w <Key-space>  "set confirm_ok 1; destroy $w"
     bind $w <Key-Escape> "destroy $w"
+    tk::PlaceWindow $w widget $owner
     tkwait window $w
     return $confirm_ok
 }
 
 proc setoptions {} {
-    option add *Panedwindow.showHandle 1 startupFile
-    option add *Panedwindow.sashRelief raised startupFile
+    if {[tk windowingsystem] ne "win32"} {
+        option add *Panedwindow.showHandle 1 startupFile
+        option add *Panedwindow.sashRelief raised startupFile
+        if {[tk windowingsystem] ne "aqua"} {
+            option add *Menu.font uifont startupFile
+        }
+    } else {
+        option add *Menu.TearOff 0 startupFile
+    }
     option add *Button.font uifont startupFile
     option add *Checkbutton.font uifont startupFile
     option add *Radiobutton.font uifont startupFile
-    if {[tk windowingsystem] ne "aqua"} {
-       option add *Menu.font uifont startupFile
-    }
     option add *Menubutton.font uifont startupFile
     option add *Label.font uifont startupFile
     option add *Message.font uifont startupFile
-    option add *Entry.font uifont startupFile
+    option add *Entry.font textfont startupFile
+    option add *Text.font textfont startupFile
+    option add *Labelframe.font uifont startupFile
+    option add *Spinbox.font textfont startupFile
+    option add *Listbox.font mainfont startupFile
 }
 
 # Make a menu and submenus.
@@ -1895,6 +2017,27 @@ proc mca {str} {
     return [string map {&& & & {}} [mc $str]]
 }
 
+proc cleardropsel {w} {
+    $w selection clear
+}
+proc makedroplist {w varname args} {
+    global use_ttk
+    if {$use_ttk} {
+        set width 0
+        foreach label $args {
+            set cx [string length $label]
+            if {$cx > $width} {set width $cx}
+        }
+       set gm [ttk::combobox $w -width $width -state readonly\
+                   -textvariable $varname -values $args \
+                   -exportselection false]
+       bind $gm <<ComboboxSelected>> [list $gm selection clear]
+    } else {
+       set gm [eval [linsert $args 0 tk_optionMenu $w $varname]]
+    }
+    return $gm
+}
+
 proc makewindow {} {
     global canv canv2 canv3 linespc charspc ctext cflist cscroll
     global tabstop
@@ -1907,17 +2050,22 @@ proc makewindow {} {
     global highlight_files gdttype
     global searchstring sstring
     global bgcolor fgcolor bglist fglist diffcolors selectbgcolor
+    global uifgcolor uifgdisabledcolor
+    global filesepbgcolor filesepfgcolor
+    global mergecolors foundbgcolor currentsearchhitbgcolor
     global headctxmenu progresscanv progressitem progresscoords statusw
     global fprogitem fprogcoord lastprogupdate progupdatepending
     global rprogitem rprogcoord rownumsel numcommits
-    global have_tk85
+    global have_tk85 use_ttk NS
+    global git_version
+    global worddiff
 
     # The "mc" arguments here are purely so that xgettext
     # sees the following string as needing to be translated
     set file {
        mc "File" cascade {
            {mc "Update" command updatecommits -accelerator F5}
-           {mc "Reload" command reloadcommits -accelerator Meta1-F5}
+           {mc "Reload" command reloadcommits -accelerator Shift-F5}
            {mc "Reread references" command rereadrefs}
            {mc "List references" command showrefs -accelerator F2}
            {xx "" separator}
@@ -1962,8 +2110,13 @@ proc makewindow {} {
     makemenu .bar $bar
     . configure -menu .bar
 
+    if {$use_ttk} {
+        # cover the non-themed toplevel with a themed frame.
+        place [ttk::frame ._main_background] -x 0 -y 0 -relwidth 1 -relheight 1
+    }
+
     # the gui has upper and lower half, parts of a paned window.
-    panedwindow .ctop -orient vertical
+    ${NS}::panedwindow .ctop -orient vertical
 
     # possibly use assumed geometry
     if {![info exists geometry(pwsash0)]} {
@@ -1971,14 +2124,17 @@ proc makewindow {} {
         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"
+        set geometry(pwsash0) [list [expr {40 * $charspc}] 2]
+        set geometry(pwsash1) [list [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
+    ${NS}::frame .tf -height $geometry(topheight) -width $geometry(topwidth)
+    ${NS}::frame .tf.histframe
+    ${NS}::panedwindow .tf.histframe.pwclist -orient horizontal
+    if {!$use_ttk} {
+       .tf.histframe.pwclist configure -sashpad 0 -handlesize 4
+    }
 
     # create three canvases
     set cscroll .tf.histframe.csb
@@ -1998,32 +2154,41 @@ proc makewindow {} {
        -selectbackground $selectbgcolor \
        -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)
+    if {$use_ttk} {
+       bind .tf.histframe.pwclist <Map> {
+           bind %W <Map> {}
+           .tf.histframe.pwclist sashpos 1 [lindex $::geometry(pwsash1) 0]
+           .tf.histframe.pwclist sashpos 0 [lindex $::geometry(pwsash0) 0]
+       }
+    } else {
+       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
+    ${NS}::scrollbar $cscroll -command {allcanvs yview}
+    if {!$use_ttk} {$cscroll configure -highlightthickness 0}
     pack $cscroll -side right -fill y
     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
+    ${NS}::frame .tf.bar
+    ${NS}::frame .tf.lbar -height 15
 
     set sha1entry .tf.bar.sha1
     set entries $sha1entry
     set sha1but .tf.bar.sha1label
-    button $sha1but -text [mc "SHA1 ID: "] -state disabled -relief flat \
+    button $sha1but -text "[mc "SHA1 ID:"] " -state disabled -relief flat \
        -command gotocommit -width 8
     $sha1but conf -disabledforeground [$sha1but cget -foreground]
     pack .tf.bar.sha1label -side left
-    entry $sha1entry -width 40 -font textfont -textvariable sha1string
+    ${NS}::entry $sha1entry -width 40 -font textfont -textvariable sha1string
     trace add variable sha1string write sha1change
     pack $sha1entry -side left -pady 2
 
-    image create bitmap bm-left -data {
+    set bm_left_data {
        #define left_width 16
        #define left_height 16
        static unsigned char left_bits[] = {
@@ -2031,7 +2196,7 @@ proc makewindow {} {
        0x0e, 0x00, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x0e, 0x00, 0x1c, 0x00,
        0x38, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xc0, 0x01};
     }
-    image create bitmap bm-right -data {
+    set bm_right_data {
        #define right_width 16
        #define right_height 16
        static unsigned char right_bits[] = {
@@ -2039,36 +2204,56 @@ proc makewindow {} {
        0x00, 0x38, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x00, 0x38, 0x00, 0x1c,
        0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01};
     }
-    button .tf.bar.leftbut -image bm-left -command goback \
-       -state disabled -width 26
+    image create bitmap bm-left -data $bm_left_data -foreground $uifgcolor
+    image create bitmap bm-left-gray -data $bm_left_data -foreground $uifgdisabledcolor
+    image create bitmap bm-right -data $bm_right_data -foreground $uifgcolor
+    image create bitmap bm-right-gray -data $bm_right_data -foreground $uifgdisabledcolor
+
+    ${NS}::button .tf.bar.leftbut -command goback -state disabled -width 26
+    if {$use_ttk} {
+       .tf.bar.leftbut configure -image [list bm-left disabled bm-left-gray]
+    } else {
+       .tf.bar.leftbut configure -image bm-left
+    }
     pack .tf.bar.leftbut -side left -fill y
-    button .tf.bar.rightbut -image bm-right -command goforw \
-       -state disabled -width 26
+    ${NS}::button .tf.bar.rightbut -command goforw -state disabled -width 26
+    if {$use_ttk} {
+       .tf.bar.rightbut configure -image [list bm-right disabled bm-right-gray]
+    } else {
+       .tf.bar.rightbut configure -image bm-right
+    }
     pack .tf.bar.rightbut -side left -fill y
 
-    label .tf.bar.rowlabel -text [mc "Row"]
+    ${NS}::label .tf.bar.rowlabel -text [mc "Row"]
     set rownumsel {}
-    label .tf.bar.rownum -width 7 -font textfont -textvariable rownumsel \
+    ${NS}::label .tf.bar.rownum -width 7 -textvariable rownumsel \
        -relief sunken -anchor e
-    label .tf.bar.rowlabel2 -text "/"
-    label .tf.bar.numcommits -width 7 -font textfont -textvariable numcommits \
+    ${NS}::label .tf.bar.rowlabel2 -text "/"
+    ${NS}::label .tf.bar.numcommits -width 7 -textvariable numcommits \
        -relief sunken -anchor e
     pack .tf.bar.rowlabel .tf.bar.rownum .tf.bar.rowlabel2 .tf.bar.numcommits \
        -side left
+    if {!$use_ttk} {
+        foreach w {rownum numcommits} {.tf.bar.$w configure -font textfont}
+    }
     global selectedline
     trace add variable selectedline write selectedline_change
 
     # Status label and progress bar
     set statusw .tf.bar.status
-    label $statusw -width 15 -relief sunken
+    ${NS}::label $statusw -width 15 -relief sunken
     pack $statusw -side left -padx 5
-    set h [expr {[font metrics uifont -linespace] + 2}]
-    set progresscanv .tf.bar.progress
-    canvas $progresscanv -relief sunken -height $h -borderwidth 2
-    set progressitem [$progresscanv create rect -1 0 0 $h -fill green]
-    set fprogitem [$progresscanv create rect -1 0 0 $h -fill yellow]
-    set rprogitem [$progresscanv create rect -1 0 0 $h -fill red]
-    pack $progresscanv -side right -expand 1 -fill x
+    if {$use_ttk} {
+       set progresscanv [ttk::progressbar .tf.bar.progress]
+    } else {
+       set h [expr {[font metrics uifont -linespace] + 2}]
+       set progresscanv .tf.bar.progress
+       canvas $progresscanv -relief sunken -height $h -borderwidth 2
+       set progressitem [$progresscanv create rect -1 0 0 $h -fill green]
+       set fprogitem [$progresscanv create rect -1 0 0 $h -fill yellow]
+       set rprogitem [$progresscanv create rect -1 0 0 $h -fill red]
+    }
+    pack $progresscanv -side right -expand 1 -fill x -padx {0 2}
     set progresscoords {0 0}
     set fprogcoord 0
     set rprogcoord 0
@@ -2077,31 +2262,58 @@ proc makewindow {} {
     set progupdatepending 0
 
     # build up the bottom bar of upper window
-    label .tf.lbar.flabel -text "[mc "Find"] "
-    button .tf.lbar.fnext -text [mc "next"] -command {dofind 1 1}
-    button .tf.lbar.fprev -text [mc "prev"] -command {dofind -1 1}
-    label .tf.lbar.flab2 -text " [mc "commit"] "
+    ${NS}::label .tf.lbar.flabel -text "[mc "Find"] "
+
+    set bm_down_data {
+       #define down_width 16
+       #define down_height 16
+       static unsigned char down_bits[] = {
+       0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01,
+       0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01,
+       0x87, 0xe1, 0x8e, 0x71, 0x9c, 0x39, 0xb8, 0x1d,
+       0xf0, 0x0f, 0xe0, 0x07, 0xc0, 0x03, 0x80, 0x01};
+    }
+    image create bitmap bm-down -data $bm_down_data -foreground $uifgcolor
+    ${NS}::button .tf.lbar.fnext -width 26 -command {dofind 1 1}
+    .tf.lbar.fnext configure -image bm-down
+
+    set bm_up_data {
+       #define up_width 16
+       #define up_height 16
+       static unsigned char up_bits[] = {
+       0x80, 0x01, 0xc0, 0x03, 0xe0, 0x07, 0xf0, 0x0f,
+       0xb8, 0x1d, 0x9c, 0x39, 0x8e, 0x71, 0x87, 0xe1,
+       0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01,
+       0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01};
+    }
+    image create bitmap bm-up -data $bm_up_data -foreground $uifgcolor
+    ${NS}::button .tf.lbar.fprev -width 26 -command {dofind -1 1}
+    .tf.lbar.fprev configure -image bm-up
+
+    ${NS}::label .tf.lbar.flab2 -text " [mc "commit"] "
+
     pack .tf.lbar.flabel .tf.lbar.fnext .tf.lbar.fprev .tf.lbar.flab2 \
        -side left -fill y
     set gdttype [mc "containing:"]
-    set gm [tk_optionMenu .tf.lbar.gdttype gdttype \
+    set gm [makedroplist .tf.lbar.gdttype gdttype \
                [mc "containing:"] \
                [mc "touching paths:"] \
-               [mc "adding/removing string:"]]
+               [mc "adding/removing string:"] \
+               [mc "changing lines matching:"]]
     trace add variable gdttype write gdttype_change
     pack .tf.lbar.gdttype -side left -fill y
 
     set findstring {}
     set fstring .tf.lbar.findstring
     lappend entries $fstring
-    entry $fstring -width 30 -font textfont -textvariable findstring
+    ${NS}::entry $fstring -width 30 -textvariable findstring
     trace add variable findstring write find_change
     set findtype [mc "Exact"]
-    set findtypemenu [tk_optionMenu .tf.lbar.findtype \
-                     findtype [mc "Exact"] [mc "IgnCase"] [mc "Regexp"]]
+    set findtypemenu [makedroplist .tf.lbar.findtype \
+                         findtype [mc "Exact"] [mc "IgnCase"] [mc "Regexp"]]
     trace add variable findtype write findcom_change
     set findloc [mc "All fields"]
-    tk_optionMenu .tf.lbar.findloc findloc [mc "All fields"] [mc "Headline"] \
+    makedroplist .tf.lbar.findloc findloc [mc "All fields"] [mc "Headline"] \
        [mc "Comments"] [mc "Author"] [mc "Committer"]
     trace add variable findloc write find_change
     pack .tf.lbar.findloc -side right
@@ -2113,40 +2325,43 @@ proc makewindow {} {
     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)
+    if {!$use_ttk} {
+       .ctop paneconfigure .tf -height $geometry(topheight)
+       .ctop paneconfigure .tf -width $geometry(topwidth)
+    }
 
     # now build up the bottom
-    panedwindow .pwbottom -orient horizontal
+    ${NS}::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)
+       ${NS}::frame .bleft -width $geometry(botwidth)
     } else {
-        frame .bleft -width $geometry(botwidth) -height $geometry(botheight)
+       ${NS}::frame .bleft -width $geometry(botwidth) -height $geometry(botheight)
     }
-    frame .bleft.top
-    frame .bleft.mid
-    frame .bleft.bottom
+    ${NS}::frame .bleft.top
+    ${NS}::frame .bleft.mid
+    ${NS}::frame .bleft.bottom
 
-    button .bleft.top.search -text [mc "Search"] -command dosearch
+    ${NS}::button .bleft.top.search -text [mc "Search"] -command dosearch
     pack .bleft.top.search -side left -padx 5
     set sstring .bleft.top.sstring
-    entry $sstring -width 20 -font textfont -textvariable searchstring
+    set searchstring ""
+    ${NS}::entry $sstring -width 20 -textvariable searchstring
     lappend entries $sstring
     trace add variable searchstring write incrsearch
     pack $sstring -side left -expand 1 -fill x
-    radiobutton .bleft.mid.diff -text [mc "Diff"] \
+    ${NS}::radiobutton .bleft.mid.diff -text [mc "Diff"] \
        -command changediffdisp -variable diffelide -value {0 0}
-    radiobutton .bleft.mid.old -text [mc "Old version"] \
+    ${NS}::radiobutton .bleft.mid.old -text [mc "Old version"] \
        -command changediffdisp -variable diffelide -value {0 1}
-    radiobutton .bleft.mid.new -text [mc "New version"] \
+    ${NS}::radiobutton .bleft.mid.new -text [mc "New version"] \
        -command changediffdisp -variable diffelide -value {1 0}
-    label .bleft.mid.labeldiffcontext -text "      [mc "Lines of context"]: "
+    ${NS}::label .bleft.mid.labeldiffcontext -text "      [mc "Lines of context"]: "
     pack .bleft.mid.diff .bleft.mid.old .bleft.mid.new -side left
-    spinbox .bleft.mid.diffcontext -width 5 -font textfont \
+    spinbox .bleft.mid.diffcontext -width 5 \
        -from 0 -increment 1 -to 10000000 \
        -validate all -validatecommand "diffcontextvalidate %P" \
        -textvariable diffcontextstring
@@ -2154,9 +2369,18 @@ proc makewindow {} {
     trace add variable diffcontextstring write diffcontextchange
     lappend entries .bleft.mid.diffcontext
     pack .bleft.mid.labeldiffcontext .bleft.mid.diffcontext -side left
-    checkbutton .bleft.mid.ignspace -text [mc "Ignore space change"] \
+    ${NS}::checkbutton .bleft.mid.ignspace -text [mc "Ignore space change"] \
        -command changeignorespace -variable ignorespace
     pack .bleft.mid.ignspace -side left -padx 5
+
+    set worddiff [mc "Line diff"]
+    if {[package vcompare $git_version "1.7.2"] >= 0} {
+       makedroplist .bleft.mid.worddiff worddiff [mc "Line diff"] \
+           [mc "Markup words"] [mc "Color words"]
+       trace add variable worddiff write changeworddiff
+       pack .bleft.mid.worddiff -side left -padx 5
+    }
+
     set ctext .bleft.bottom.ctext
     text $ctext -background $bgcolor -foreground $fgcolor \
        -state disabled -font textfont \
@@ -2165,9 +2389,8 @@ proc makewindow {} {
     if {$have_tk85} {
        $ctext conf -tabstyle wordprocessor
     }
-    scrollbar .bleft.bottom.sb -command "$ctext yview"
-    scrollbar .bleft.bottom.sbhorizontal -command "$ctext xview" -orient h \
-       -width 10
+    ${NS}::scrollbar .bleft.bottom.sb -command "$ctext yview"
+    ${NS}::scrollbar .bleft.bottom.sbhorizontal -command "$ctext xview" -orient h
     pack .bleft.top -side top -fill x
     pack .bleft.mid -side top -fill x
     grid $ctext .bleft.bottom.sb -sticky nsew
@@ -2180,41 +2403,46 @@ proc makewindow {} {
     lappend fglist $ctext
 
     $ctext tag conf comment -wrap $wrapcomment
-    $ctext tag conf filesep -font textfontbold -back "#aaaaaa"
+    $ctext tag conf filesep -font textfontbold -fore $filesepfgcolor -back $filesepbgcolor
     $ctext tag conf hunksep -fore [lindex $diffcolors 2]
     $ctext tag conf d0 -fore [lindex $diffcolors 0]
     $ctext tag conf dresult -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 m3 -fore purple
-    $ctext tag conf m4 -fore brown
-    $ctext tag conf m5 -fore "#009090"
-    $ctext tag conf m6 -fore magenta
-    $ctext tag conf m7 -fore "#808000"
-    $ctext tag conf m8 -fore "#009000"
-    $ctext tag conf m9 -fore "#ff0080"
-    $ctext tag conf m10 -fore cyan
-    $ctext tag conf m11 -fore "#b07070"
-    $ctext tag conf m12 -fore "#70b0f0"
-    $ctext tag conf m13 -fore "#70f0b0"
-    $ctext tag conf m14 -fore "#f0b070"
-    $ctext tag conf m15 -fore "#ff70b0"
+    $ctext tag conf m0 -fore [lindex $mergecolors 0]
+    $ctext tag conf m1 -fore [lindex $mergecolors 1]
+    $ctext tag conf m2 -fore [lindex $mergecolors 2]
+    $ctext tag conf m3 -fore [lindex $mergecolors 3]
+    $ctext tag conf m4 -fore [lindex $mergecolors 4]
+    $ctext tag conf m5 -fore [lindex $mergecolors 5]
+    $ctext tag conf m6 -fore [lindex $mergecolors 6]
+    $ctext tag conf m7 -fore [lindex $mergecolors 7]
+    $ctext tag conf m8 -fore [lindex $mergecolors 8]
+    $ctext tag conf m9 -fore [lindex $mergecolors 9]
+    $ctext tag conf m10 -fore [lindex $mergecolors 10]
+    $ctext tag conf m11 -fore [lindex $mergecolors 11]
+    $ctext tag conf m12 -fore [lindex $mergecolors 12]
+    $ctext tag conf m13 -fore [lindex $mergecolors 13]
+    $ctext tag conf m14 -fore [lindex $mergecolors 14]
+    $ctext tag conf m15 -fore [lindex $mergecolors 15]
     $ctext tag conf mmax -fore darkgrey
     set mergemax 16
     $ctext tag conf mresult -font textfontbold
     $ctext tag conf msep -font textfontbold
-    $ctext tag conf found -back yellow
+    $ctext tag conf found -back $foundbgcolor
+    $ctext tag conf currentsearchhit -back $currentsearchhitbgcolor
+    $ctext tag conf wwrap -wrap word -lmargin2 1c
+    $ctext tag conf bold -font textfontbold
 
     .pwbottom add .bleft
-    .pwbottom paneconfigure .bleft -width $geometry(botwidth)
+    if {!$use_ttk} {
+       .pwbottom paneconfigure .bleft -width $geometry(botwidth)
+    }
 
     # lower right
-    frame .bright
-    frame .bright.mode
-    radiobutton .bright.mode.patch -text [mc "Patch"] \
+    ${NS}::frame .bright
+    ${NS}::frame .bright.mode
+    ${NS}::radiobutton .bright.mode.patch -text [mc "Patch"] \
        -command reselectline -variable cmitmode -value "patch"
-    radiobutton .bright.mode.tree -text [mc "Tree"] \
+    ${NS}::radiobutton .bright.mode.tree -text [mc "Tree"] \
        -command reselectline -variable cmitmode -value "tree"
     grid .bright.mode.patch .bright.mode.tree -sticky ew
     pack .bright.mode -side top -fill x
@@ -2230,7 +2458,7 @@ proc makewindow {} {
        -spacing1 1 -spacing3 1
     lappend bglist $cflist
     lappend fglist $cflist
-    scrollbar .bright.sb -command "$cflist yview"
+    ${NS}::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 \
@@ -2265,6 +2493,17 @@ proc makewindow {} {
         set ::BM "2"
     }
 
+    if {$use_ttk} {
+        bind .ctop <Map> {
+            bind %W <Map> {}
+            %W sashpos 0 $::geometry(topheight)
+        }
+        bind .pwbottom <Map> {
+            bind %W <Map> {}
+            %W sashpos 0 $::geometry(botwidth)
+        }
+    }
+
     bind .pwbottom <Configure> {resizecdetpanes %W %w}
     pack .ctop -fill both -expand 1
     bindall <1> {selcanvline %W %x %y}
@@ -2288,6 +2527,8 @@ proc makewindow {} {
     }
     bindall <$::BM> "canvscan mark %W %x %y"
     bindall <B$::BM-Motion> "canvscan dragto %W %x %y"
+    bind all <$M1B-Key-w> {destroy [winfo toplevel %W]}
+    bind . <$M1B-Key-w> doquit
     bindkey <Home> selfirstline
     bindkey <End> sellastline
     bind . <Key-Up> "selnextline -1"
@@ -2311,9 +2552,9 @@ proc makewindow {} {
     bindkey n "selnextline 1"
     bindkey z "goback"
     bindkey x "goforw"
-    bindkey i "selnextline -1"
-    bindkey k "selnextline 1"
-    bindkey j "goback"
+    bindkey k "selnextline -1"
+    bindkey j "selnextline 1"
+    bindkey h "goback"
     bindkey l "goforw"
     bindkey b prevfile
     bindkey d "$ctext yview scroll 18 units"
@@ -2324,10 +2565,9 @@ proc makewindow {} {
     bindkey ? {dofind -1 1}
     bindkey f nextfile
     bind . <F5> updatecommits
-    bind . <$M1B-F5> reloadcommits
+    bindmodfunctionkey Shift 5 reloadcommits
     bind . <F2> showrefs
-    bind . <Shift-F4> {newview 0}
-    catch { bind . <Shift-Key-XF86_Switch_VT_4> {newview 0} }
+    bindmodfunctionkey Shift 4 {newview 0}
     bind . <F4> edit_or_newview
     bind . <$M1B-q> doquit
     bind . <$M1B-f> {dofind 1 1}
@@ -2345,12 +2585,15 @@ proc makewindow {} {
     bind $fstring <Key-Return> {dofind 1 1}
     bind $sha1entry <Key-Return> {gotocommit; break}
     bind $sha1entry <<PasteSelection>> clearsha1
+    bind $sha1entry <<Paste>> 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}
     global ctxbut
     bind $cflist $ctxbut {pop_flist_menu %W %X %Y %x %y}
     bind $ctext $ctxbut {pop_diff_menu %W %X %Y %x %y}
+    bind $ctext <Button-1> {focus %W}
+    bind $ctext <<Selection>> rehighlight_search_results
 
     set maincursor [. cget -cursor]
     set textcursor [$ctext cget -cursor]
@@ -2370,6 +2613,9 @@ proc makewindow {} {
        {mc "Return to mark" command gotomark}
        {mc "Find descendant of this and mark" command find_common_desc}
        {mc "Compare with marked commit" command compare_commits}
+       {mc "Diff this -> marked commit" command {diffvsmark 0}}
+       {mc "Diff marked commit -> this" command {diffvsmark 1}}
+       {mc "Revert this commit" command revert}
     }
     $rowctxmenu configure -tearoff 0
 
@@ -2378,6 +2624,8 @@ proc makewindow {} {
        {mc "Diff this -> selected" command {diffvssel 0}}
        {mc "Diff selected -> this" command {diffvssel 1}}
        {mc "Make patch" command mkpatch}
+       {mc "Diff this -> marked commit" command {diffvsmark 0}}
+       {mc "Diff marked commit -> this" command {diffvsmark 1}}
     }
     $fakerowmenu configure -tearoff 0
 
@@ -2470,6 +2718,11 @@ proc bindkey {ev script} {
     }
 }
 
+proc bindmodfunctionkey {mod n script} {
+    bind . <$mod-F$n> $script
+    catch { bind . <$mod-XF86_Switch_VT_$n> $script }
+}
+
 # set the focus back to the toplevel for any click outside
 # the entry widgets
 proc click {w} {
@@ -2484,7 +2737,12 @@ proc click {w} {
 proc adjustprogress {} {
     global progresscanv progressitem progresscoords
     global fprogitem fprogcoord lastprogupdate progupdatepending
-    global rprogitem rprogcoord
+    global rprogitem rprogcoord use_ttk
+
+    if {$use_ttk} {
+       $progresscanv configure -value [expr {int($fprogcoord * 100)}]
+       return
+    }
 
     set w [expr {[winfo width $progresscanv] - 4}]
     set x0 [expr {$w * [lindex $progresscoords 0]}]
@@ -2520,16 +2778,27 @@ proc savestuff {w} {
     global viewname viewfiles viewargs viewargscmd viewperm nextviewnum
     global cmitmode wrapcomment datetimeformat limitdiffs
     global colors uicolor bgcolor fgcolor diffcolors diffcontext selectbgcolor
-    global autoselect extdifftool perfile_attrs markbgcolor
-    global hideremotes
+    global uifgcolor uifgdisabledcolor
+    global headbgcolor headfgcolor headoutlinecolor remotebgcolor
+    global tagbgcolor tagfgcolor tagoutlinecolor
+    global reflinecolor filesepbgcolor filesepfgcolor
+    global mergecolors foundbgcolor currentsearchhitbgcolor
+    global linehoverbgcolor linehoverfgcolor linehoveroutlinecolor circlecolors
+    global mainheadcirclecolor workingfilescirclecolor indexcirclecolor
+    global linkfgcolor circleoutlinecolor
+    global autoselect autosellen extdifftool perfile_attrs markbgcolor use_ttk
+    global hideremotes want_ttk maxrefs
+    global config_file config_file_tmp
 
     if {$stuffsaved} return
     if {![winfo viewable .]} return
     catch {
-       if {[file exists ~/.gitk-new]} {file delete -force ~/.gitk-new}
-       set f [open "~/.gitk-new" w]
+       if {[file exists $config_file_tmp]} {
+           file delete -force $config_file_tmp
+       }
+       set f [open $config_file_tmp w]
        if {$::tcl_platform(platform) eq {windows}} {
-           file attributes "~/.gitk-new" -hidden true
+           file attributes $config_file_tmp -hidden true
        }
        puts $f [list set mainfont $mainfont]
        puts $f [list set textfont $textfont]
@@ -2541,28 +2810,60 @@ proc savestuff {w} {
        puts $f [list set cmitmode $cmitmode]
        puts $f [list set wrapcomment $wrapcomment]
        puts $f [list set autoselect $autoselect]
+       puts $f [list set autosellen $autosellen]
        puts $f [list set showneartags $showneartags]
+       puts $f [list set maxrefs $maxrefs]
        puts $f [list set hideremotes $hideremotes]
        puts $f [list set showlocalchanges $showlocalchanges]
        puts $f [list set datetimeformat $datetimeformat]
        puts $f [list set limitdiffs $limitdiffs]
        puts $f [list set uicolor $uicolor]
+       puts $f [list set want_ttk $want_ttk]
        puts $f [list set bgcolor $bgcolor]
        puts $f [list set fgcolor $fgcolor]
+       puts $f [list set uifgcolor $uifgcolor]
+       puts $f [list set uifgdisabledcolor $uifgdisabledcolor]
        puts $f [list set colors $colors]
        puts $f [list set diffcolors $diffcolors]
+       puts $f [list set mergecolors $mergecolors]
        puts $f [list set markbgcolor $markbgcolor]
        puts $f [list set diffcontext $diffcontext]
        puts $f [list set selectbgcolor $selectbgcolor]
+       puts $f [list set foundbgcolor $foundbgcolor]
+       puts $f [list set currentsearchhitbgcolor $currentsearchhitbgcolor]
        puts $f [list set extdifftool $extdifftool]
        puts $f [list set perfile_attrs $perfile_attrs]
+       puts $f [list set headbgcolor $headbgcolor]
+       puts $f [list set headfgcolor $headfgcolor]
+       puts $f [list set headoutlinecolor $headoutlinecolor]
+       puts $f [list set remotebgcolor $remotebgcolor]
+       puts $f [list set tagbgcolor $tagbgcolor]
+       puts $f [list set tagfgcolor $tagfgcolor]
+       puts $f [list set tagoutlinecolor $tagoutlinecolor]
+       puts $f [list set reflinecolor $reflinecolor]
+       puts $f [list set filesepbgcolor $filesepbgcolor]
+       puts $f [list set filesepfgcolor $filesepfgcolor]
+       puts $f [list set linehoverbgcolor $linehoverbgcolor]
+       puts $f [list set linehoverfgcolor $linehoverfgcolor]
+       puts $f [list set linehoveroutlinecolor $linehoveroutlinecolor]
+       puts $f [list set mainheadcirclecolor $mainheadcirclecolor]
+       puts $f [list set workingfilescirclecolor $workingfilescirclecolor]
+       puts $f [list set indexcirclecolor $indexcirclecolor]
+       puts $f [list set circlecolors $circlecolors]
+       puts $f [list set linkfgcolor $linkfgcolor]
+       puts $f [list set circleoutlinecolor $circleoutlinecolor]
 
        puts $f "set geometry(main) [wm geometry .]"
        puts $f "set geometry(state) [wm state .]"
        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]\""
+       if {$use_ttk} {
+           puts $f "set geometry(pwsash0) \"[.tf.histframe.pwclist sashpos 0] 1\""
+           puts $f "set geometry(pwsash1) \"[.tf.histframe.pwclist sashpos 1] 1\""
+       } else {
+           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]"
 
@@ -2574,16 +2875,21 @@ proc savestuff {w} {
        }
        puts $f "}"
        close $f
-       file rename -force "~/.gitk-new" "~/.gitk"
+       file rename -force $config_file_tmp $config_file
     }
     set stuffsaved 1
 }
 
 proc resizeclistpanes {win w} {
-    global oldwidth
+    global oldwidth use_ttk
     if {[info exists oldwidth($win)]} {
-       set s0 [$win sash coord 0]
-       set s1 [$win sash coord 1]
+       if {$use_ttk} {
+           set s0 [$win sashpos 0]
+           set s1 [$win sashpos 1]
+       } else {
+           set s0 [$win sash coord 0]
+           set s1 [$win sash coord 1]
+       }
        if {$w < 60} {
            set sash0 [expr {int($w/2 - 2)}]
            set sash1 [expr {int($w*5/6 - 2)}]
@@ -2604,16 +2910,25 @@ proc resizeclistpanes {win w} {
                }
            }
        }
-       $win sash place 0 $sash0 [lindex $s0 1]
-       $win sash place 1 $sash1 [lindex $s1 1]
+       if {$use_ttk} {
+           $win sashpos 0 $sash0
+           $win sashpos 1 $sash1
+       } else {
+           $win sash place 0 $sash0 [lindex $s0 1]
+           $win sash place 1 $sash1 [lindex $s1 1]
+       }
     }
     set oldwidth($win) $w
 }
 
 proc resizecdetpanes {win w} {
-    global oldwidth
+    global oldwidth use_ttk
     if {[info exists oldwidth($win)]} {
-       set s0 [$win sash coord 0]
+       if {$use_ttk} {
+           set s0 [$win sashpos 0]
+       } else {
+           set s0 [$win sash coord 0]
+       }
        if {$w < 60} {
            set sash0 [expr {int($w*3/4 - 2)}]
        } else {
@@ -2626,7 +2941,11 @@ proc resizecdetpanes {win w} {
                set sash0 [expr {$w - 15}]
            }
        }
-       $win sash place 0 $sash0 [lindex $s0 1]
+       if {$use_ttk} {
+           $win sashpos 0 $sash0
+       } else {
+           $win sash place 0 $sash0 [lindex $s0 1]
+       }
     }
     set oldwidth($win) $w
 }
@@ -2646,31 +2965,33 @@ proc bindall {event action} {
 }
 
 proc about {} {
-    global uifont
+    global uifont NS
     set w .about
     if {[winfo exists $w]} {
        raise $w
        return
     }
-    toplevel $w
+    ttk_toplevel $w
     wm title $w [mc "About gitk"]
     make_transient $w .
     message $w.m -text [mc "
 Gitk - a commit viewer for git
 
-Copyright © 2005-2008 Paul Mackerras
+Copyright \u00a9 2005-2014 Paul Mackerras
 
 Use and redistribute under the terms of the GNU General Public License"] \
            -justify center -aspect 400 -border 2 -bg white -relief groove
     pack $w.m -side top -fill x -padx 2 -pady 2
-    button $w.ok -text [mc "Close"] -command "destroy $w" -default active
+    ${NS}::button $w.ok -text [mc "Close"] -command "destroy $w" -default active
     pack $w.ok -side bottom
     bind $w <Visibility> "focus $w.ok"
     bind $w <Key-Escape> "destroy $w"
     bind $w <Key-Return> "destroy $w"
+    tk::PlaceWindow $w widget .
 }
 
 proc keys {} {
+    global NS
     set w .keys
     if {[winfo exists $w]} {
        raise $w
@@ -2681,18 +3002,19 @@ proc keys {} {
     } else {
        set M1T Ctrl
     }
-    toplevel $w
+    ttk_toplevel $w
     wm title $w [mc "Gitk key bindings"]
     make_transient $w .
     message $w.m -text "
 [mc "Gitk key bindings:"]
 
 [mc "<%s-Q>            Quit" $M1T]
+[mc "<%s-W>            Close window" $M1T]
 [mc "<Home>            Move to first commit"]
 [mc "<End>             Move to last commit"]
-[mc "<Up>, p, i        Move up one commit"]
-[mc "<Down>, n, k      Move down one commit"]
-[mc "<Left>, z, j      Go back in history list"]
+[mc "<Up>, p, k        Move up one commit"]
+[mc "<Down>, n, j      Move down one commit"]
+[mc "<Left>, z, h      Go back in history list"]
 [mc "<Right>, x, l     Go forward in history list"]
 [mc "<PageUp>  Move up one page in commit list"]
 [mc "<PageDown>        Move down one page in commit list"]
@@ -2725,7 +3047,7 @@ proc keys {} {
 " \
            -justify left -bg white -border 2 -relief groove
     pack $w.m -side top -fill both -padx 2 -pady 2
-    button $w.ok -text [mc "Close"] -command "destroy $w" -default active
+    ${NS}::button $w.ok -text [mc "Close"] -command "destroy $w" -default active
     bind $w <Key-Escape> [list destroy $w]
     pack $w.ok -side bottom
     bind $w <Visibility> "focus $w.ok"
@@ -3100,6 +3422,7 @@ proc sel_flist {w x y} {
     } else {
        catch {$ctext yview [lindex $difffilestart [expr {$l - 2}]]}
     }
+    suppress_highlighting_file_for_current_scrollpos
 }
 
 proc pop_flist_menu {w X Y x y} {
@@ -3170,11 +3493,18 @@ proc flist_hl {only} {
 }
 
 proc gitknewtmpdir {} {
-    global diffnum gitktmpdir gitdir
+    global diffnum gitktmpdir gitdir env
 
     if {![info exists gitktmpdir]} {
-       set gitktmpdir [file join [file dirname $gitdir] \
-                           [format ".gitk-tmp.%s" [pid]]]
+       if {[info exists env(GITK_TMPDIR)]} {
+           set tmpdir $env(GITK_TMPDIR)
+       } elseif {[info exists env(TMPDIR)]} {
+           set tmpdir $env(TMPDIR)
+       } else {
+           set tmpdir $gitdir
+       }
+       set gitktmpformat [file join $tmpdir ".gitk-tmp.XXXXXX"]
+       set gitktmpdir [exec mktemp -d $gitktmpformat]
        if {[catch {file mkdir $gitktmpdir} err]} {
            error_popup "[mc "Error creating temporary directory %s:" $gitktmpdir] $err"
            unset gitktmpdir
@@ -3206,10 +3536,10 @@ proc save_file_from_commit {filename output what} {
 
 proc external_diff_get_one_file {diffid filename diffdir} {
     global nullid nullid2 nullfile
-    global gitdir
+    global worktree
 
     if {$diffid == $nullid} {
-        set difffile [file join [file dirname $gitdir] $filename]
+        set difffile [file join $worktree $filename]
        if {[file exists $difffile]} {
            return $difffile
        }
@@ -3378,6 +3708,9 @@ proc index_sha1 {fname} {
 
 # Turn an absolute path into one relative to the current directory
 proc make_relative {f} {
+    if {[file pathtype $f] eq "relative"} {
+       return $f
+    }
     set elts [file split $f]
     set here [file split [pwd]]
     set ei 0
@@ -3396,7 +3729,7 @@ proc make_relative {f} {
 }
 
 proc external_blame {parent_idx {line {}}} {
-    global flist_menu_file gitdir
+    global flist_menu_file cdup
     global nullid nullid2
     global parentlist selectedline currentid
 
@@ -3415,7 +3748,7 @@ proc external_blame {parent_idx {line {}}} {
     if {$line ne {} && $line > 1} {
        lappend cmdline "--line=$line"
     }
-    set f [file join [file dirname $gitdir] $flist_menu_file]
+    set f [file join $cdup $flist_menu_file]
     # Unfortunately it seems git gui blame doesn't like
     # being given an absolute path...
     set f [make_relative $f]
@@ -3428,7 +3761,7 @@ proc external_blame {parent_idx {line {}}} {
 proc show_line_source {} {
     global cmitmode currentid parents curview blamestuff blameinst
     global diff_menu_line diff_menu_filebase flist_menu_file
-    global nullid nullid2 gitdir
+    global nullid nullid2 gitdir cdup
 
     set from_index {}
     if {$cmitmode eq "tree"} {
@@ -3481,7 +3814,7 @@ proc show_line_source {} {
     } else {
        lappend blameargs $id
     }
-    lappend blameargs -- [file join [file dirname $gitdir] $flist_menu_file]
+    lappend blameargs -- [file join $cdup $flist_menu_file]
     if {[catch {
        set f [open $blameargs r]
     } err]} {
@@ -3546,7 +3879,7 @@ proc read_line_source {fd inst} {
            set id $nullid2
        }
        if {[commitinview $id $curview]} {
-           selectline [rowofcommit $id] 1 [list $fname $lnum]
+           selectline [rowofcommit $id] 1 [list $fname $lnum] 1
        } else {
            error_popup [mc "That line comes from commit %s, \
                             which is not in this view" [shortids $id]]
@@ -3676,10 +4009,10 @@ proc newview {ishighlight} {
        raise $top
        return
     }
+    decode_view_opts $nextviewnum $revtreeargs
     set newviewname($nextviewnum) "[mc "View"] $nextviewnum"
     set newviewopts($nextviewnum,perm) 0
     set newviewopts($nextviewnum,cmd)  $viewargscmd($curview)
-    decode_view_opts $nextviewnum $revtreeargs
     vieweditor $top $nextviewnum [mc "Gitk view definition"]
 }
 
@@ -3716,6 +4049,7 @@ set known_view_options {
     {cmd       t50= +  {}               {mc "Command to generate more commits to include:"}}
     }
 
+# Convert $newviewopts($n, ...) into args for git log.
 proc encode_view_opts {n} {
     global known_view_options newviewopts
 
@@ -3749,6 +4083,7 @@ proc encode_view_opts {n} {
     return [concat $rargs [shellsplit $newviewopts($n,args)]]
 }
 
+# Fill $newviewopts($n, ...) based on args for git log.
 proc decode_view_opts {n view_args} {
     global known_view_options newviewopts
 
@@ -3831,25 +4166,25 @@ proc editview {} {
        raise $top
        return
     }
+    decode_view_opts $curview $viewargs($curview)
     set newviewname($curview)      $viewname($curview)
     set newviewopts($curview,perm) $viewperm($curview)
     set newviewopts($curview,cmd)  $viewargscmd($curview)
-    decode_view_opts $curview $viewargs($curview)
     vieweditor $top $curview "[mc "Gitk: edit view"] $viewname($curview)"
 }
 
 proc vieweditor {top n title} {
     global newviewname newviewopts viewfiles bgcolor
-    global known_view_options
+    global known_view_options NS
 
-    toplevel $top
+    ttk_toplevel $top
     wm title $top [concat $title [mc "-- criteria for selecting revisions"]]
     make_transient $top .
 
     # View name
-    frame $top.nfr
-    label $top.nl -text [mc "View Name:"]
-    entry $top.name -width 20 -textvariable newviewname($n)
+    ${NS}::frame $top.nfr
+    ${NS}::label $top.nl -text [mc "View Name"]
+    ${NS}::entry $top.name -width 20 -textvariable newviewname($n)
     pack $top.nfr -in $top -fill x -pady 5 -padx 3
     pack $top.nl -in $top.nfr -side left -padx {0 5}
     pack $top.name -in $top.nfr -side left -padx {0 25}
@@ -3868,13 +4203,13 @@ proc vieweditor {top n title} {
        if {$flags eq "+" || $flags eq "*"} {
            set cframe $top.fr$cnt
            incr cnt
-           frame $cframe
+           ${NS}::frame $cframe
            pack $cframe -in $top -fill x -pady 3 -padx 3
            set cexpand [expr {$flags eq "*"}]
         } elseif {$flags eq ".." || $flags eq "*."} {
            set cframe $top.fr$cnt
            incr cnt
-           frame $cframe
+           ${NS}::frame $cframe
            pack $cframe -in $top -fill x -pady 3 -padx [list 15 3]
            set cexpand [expr {$flags eq "*."}]
        } else {
@@ -3882,33 +4217,33 @@ proc vieweditor {top n title} {
        }
 
        if {$type eq "l"} {
-            label $cframe.l_$id -text $title
+            ${NS}::label $cframe.l_$id -text $title
             pack $cframe.l_$id -in $cframe -side left -pady [list 3 0] -anchor w
        } elseif {$type eq "b"} {
-           checkbutton $cframe.c_$id -text $title -variable newviewopts($n,$id)
+           ${NS}::checkbutton $cframe.c_$id -text $title -variable newviewopts($n,$id)
            pack $cframe.c_$id -in $cframe -side left \
                -padx [list $lxpad 0] -expand $cexpand -anchor w
        } elseif {[regexp {^r(\d+)$} $type type sz]} {
            regexp {^(.*_)} $id uselessvar button_id
-           radiobutton $cframe.c_$id -text $title -variable newviewopts($n,$button_id) -value $sz
+           ${NS}::radiobutton $cframe.c_$id -text $title -variable newviewopts($n,$button_id) -value $sz
            pack $cframe.c_$id -in $cframe -side left \
                -padx [list $lxpad 0] -expand $cexpand -anchor w
        } elseif {[regexp {^t(\d+)$} $type type sz]} {
-           message $cframe.l_$id -aspect 1500 -text $title
-           entry $cframe.e_$id -width $sz -background $bgcolor \
+           ${NS}::label $cframe.l_$id -text $title
+           ${NS}::entry $cframe.e_$id -width $sz -background $bgcolor \
                -textvariable newviewopts($n,$id)
            pack $cframe.l_$id -in $cframe -side left -padx [list $lxpad 0]
            pack $cframe.e_$id -in $cframe -side left -expand 1 -fill x
        } elseif {[regexp {^t(\d+)=$} $type type sz]} {
-           message $cframe.l_$id -aspect 1500 -text $title
-           entry $cframe.e_$id -width $sz -background $bgcolor \
+           ${NS}::label $cframe.l_$id -text $title
+           ${NS}::entry $cframe.e_$id -width $sz -background $bgcolor \
                -textvariable newviewopts($n,$id)
            pack $cframe.l_$id -in $cframe -side top -pady [list 3 0] -anchor w
            pack $cframe.e_$id -in $cframe -side top -fill x
        } elseif {$type eq "path"} {
-           message $top.l -aspect 1500 -text $title
+           ${NS}::label $top.l -text $title
            pack $top.l -in $top -side top -pady [list 3 0] -anchor w -padx 3
-           text $top.t -width 40 -height 5 -background $bgcolor -font uifont
+           text $top.t -width 40 -height 5 -background $bgcolor
            if {[info exists viewfiles($n)]} {
                foreach f $viewfiles($n) {
                    $top.t insert end $f
@@ -3921,10 +4256,10 @@ proc vieweditor {top n title} {
        }
     }
 
-    frame $top.buts
-    button $top.buts.ok -text [mc "OK"] -command [list newviewok $top $n]
-    button $top.buts.apply -text [mc "Apply (F5)"] -command [list newviewok $top $n 1]
-    button $top.buts.can -text [mc "Cancel"] -command [list destroy $top]
+    ${NS}::frame $top.buts
+    ${NS}::button $top.buts.ok -text [mc "OK"] -command [list newviewok $top $n]
+    ${NS}::button $top.buts.apply -text [mc "Apply (F5)"] -command [list newviewok $top $n 1]
+    ${NS}::button $top.buts.can -text [mc "Cancel"] -command [list destroy $top]
     bind $top <Control-Return> [list newviewok $top $n]
     bind $top <F5> [list newviewok $top $n 1]
     bind $top <Escape> [list destroy $top]
@@ -4364,14 +4699,26 @@ proc makepatterns {l} {
 
 proc do_file_hl {serial} {
     global highlight_files filehighlight highlight_paths gdttype fhl_list
+    global cdup findtype
 
     if {$gdttype eq [mc "touching paths:"]} {
+       # If "exact" match then convert backslashes to forward slashes.
+       # Most useful to support Windows-flavoured file paths.
+       if {$findtype eq [mc "Exact"]} {
+           set highlight_files [string map {"\\" "/"} $highlight_files]
+       }
        if {[catch {set paths [shellsplit $highlight_files]}]} return
        set highlight_paths [makepatterns $paths]
        highlight_filelist
-       set gdtargs [concat -- $paths]
+       set relative_paths {}
+       foreach path $paths {
+           lappend relative_paths [file join $cdup $path]
+       }
+       set gdtargs [concat -- $relative_paths]
     } elseif {$gdttype eq [mc "adding/removing string:"]} {
        set gdtargs [list "-S$highlight_files"]
+    } elseif {$gdttype eq [mc "changing lines matching:"]} {
+       set gdtargs [list "-G$highlight_files"]
     } else {
        # must be "containing:", i.e. we're searching commit info
        return
@@ -4462,8 +4809,9 @@ proc askfindhighlight {row id} {
     }
     set info $commitinfo($id)
     set isbold 0
-    set fldtypes [list [mc Headline] [mc Author] [mc Date] [mc Committer] [mc CDate] [mc Comments]]
+    set fldtypes [list [mc Headline] [mc Author] "" [mc Committer] "" [mc Comments]]
     foreach f $info ty $fldtypes {
+       if {$ty eq ""} continue
        if {($findloc eq [mc "All fields"] || $findloc eq $ty) &&
            [doesmatch $f]} {
            if {$ty eq [mc "Author"]} {
@@ -4866,11 +5214,15 @@ proc dohidelocalchanges {} {
 # spawn off a process to do git diff-index --cached HEAD
 proc dodiffindex {} {
     global lserial showlocalchanges vfilelimit curview
-    global isworktree
+    global hasworktree git_version
 
-    if {!$showlocalchanges || !$isworktree} return
+    if {!$showlocalchanges || !$hasworktree} return
     incr lserial
-    set cmd "|git diff-index --cached HEAD"
+    if {[package vcompare $git_version "1.7.2"] >= 0} {
+       set cmd "|git diff-index --cached --ignore-submodules=dirty HEAD"
+    } else {
+       set cmd "|git diff-index --cached HEAD"
+    }
     if {$vfilelimit($curview) ne {}} {
        set cmd [concat $cmd -- $vfilelimit($curview)]
     }
@@ -5679,15 +6031,17 @@ proc drawcmittext {id row col} {
     global linehtag linentag linedtag selectedline
     global canvxmax boldids boldnameids fgcolor markedid
     global mainheadid nullid nullid2 circleitem circlecolors ctxbut
+    global mainheadcirclecolor workingfilescirclecolor indexcirclecolor
+    global circleoutlinecolor
 
     # listed is 0 for boundary, 1 for normal, 2 for negative, 3 for left, 4 for right
     set listed $cmitlisted($curview,$id)
     if {$id eq $nullid} {
-       set ofill red
+       set ofill $workingfilescirclecolor
     } elseif {$id eq $nullid2} {
-       set ofill green
+       set ofill $indexcirclecolor
     } elseif {$id eq $mainheadid} {
-       set ofill yellow
+       set ofill $mainheadcirclecolor
     } else {
        set ofill [lindex $circlecolors $listed]
     }
@@ -5697,21 +6051,21 @@ proc drawcmittext {id row col} {
     if {$listed <= 2} {
        set t [$canv create oval [expr {$x - $orad}] [expr {$y - $orad}] \
                   [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
-                  -fill $ofill -outline $fgcolor -width 1 -tags circle]
+                  -fill $ofill -outline $circleoutlinecolor -width 1 -tags circle]
     } elseif {$listed == 3} {
        # triangle pointing left for left-side commits
        set t [$canv create polygon \
                   [expr {$x - $orad}] $y \
                   [expr {$x + $orad - 1}] [expr {$y - $orad}] \
                   [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
-                  -fill $ofill -outline $fgcolor -width 1 -tags circle]
+                  -fill $ofill -outline $circleoutlinecolor -width 1 -tags circle]
     } else {
        # triangle pointing right for right-side commits
        set t [$canv create polygon \
                   [expr {$x + $orad - 1}] $y \
                   [expr {$x - $orad}] [expr {$y - $orad}] \
                   [expr {$x - $orad}] [expr {$y + $orad - 1}] \
-                  -fill $ofill -outline $fgcolor -width 1 -tags circle]
+                  -fill $ofill -outline $circleoutlinecolor -width 1 -tags circle]
     }
     set circleitem($row) $t
     $canv raise $t
@@ -5734,6 +6088,9 @@ proc drawcmittext {id row col} {
        || [info exists idotherrefs($id)]} {
        set xt [drawtags $id $x $xt $y]
     }
+    if {[lindex $commitinfo($id) 6] > 0} {
+       set xt [drawnotesign $xt $y]
+    }
     set headline [lindex $commitinfo($id) 0]
     set name [lindex $commitinfo($id) 1]
     set date [lindex $commitinfo($id) 2]
@@ -6092,17 +6449,57 @@ proc bindline {t id} {
     $canv bind $t <Button-1> "lineclick %x %y $id 1"
 }
 
+proc graph_pane_width {} {
+    global use_ttk
+
+    if {$use_ttk} {
+       set g [.tf.histframe.pwclist sashpos 0]
+    } else {
+       set g [.tf.histframe.pwclist sash coord 0]
+    }
+    return [lindex $g 0]
+}
+
+proc totalwidth {l font extra} {
+    set tot 0
+    foreach str $l {
+       set tot [expr {$tot + [font measure $font $str] + $extra}]
+    }
+    return $tot
+}
+
 proc drawtags {id x xt y1} {
     global idtags idheads idotherrefs mainhead
     global linespc lthickness
     global canv rowtextx curview fgcolor bgcolor ctxbut
+    global headbgcolor headfgcolor headoutlinecolor remotebgcolor
+    global tagbgcolor tagfgcolor tagoutlinecolor
+    global reflinecolor
 
     set marks {}
     set ntags 0
     set nheads 0
+    set singletag 0
+    set maxtags 3
+    set maxtagpct 25
+    set maxwidth [expr {[graph_pane_width] * $maxtagpct / 100}]
+    set delta [expr {int(0.5 * ($linespc - $lthickness))}]
+    set extra [expr {$delta + $lthickness + $linespc}]
+
     if {[info exists idtags($id)]} {
        set marks $idtags($id)
        set ntags [llength $marks]
+       if {$ntags > $maxtags ||
+           [totalwidth $marks mainfont $extra] > $maxwidth} {
+           # show just a single "n tags..." tag
+           set singletag 1
+           if {$ntags == 1} {
+               set marks [list "tag..."]
+           } else {
+               set marks [list [format "%d tags..." $ntags]]
+           }
+           set ntags 1
+       }
     }
     if {[info exists idheads($id)]} {
        set marks [concat $marks $idheads($id)]
@@ -6115,7 +6512,6 @@ proc drawtags {id x xt y1} {
        return $xt
     }
 
-    set delta [expr {int(0.5 * ($linespc - $lthickness))}]
     set yt [expr {$y1 - 0.5 * $linespc}]
     set yb [expr {$yt + $linespc - 1}]
     set xvals {}
@@ -6130,12 +6526,13 @@ proc drawtags {id x xt y1} {
        }
        lappend xvals $xt
        lappend wvals $wid
-       set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}]
+       set xt [expr {$xt + $wid + $extra}]
     }
     set t [$canv create line $x $y1 [lindex $xvals end] $y1 \
-              -width $lthickness -fill black -tags tag.$id]
+              -width $lthickness -fill $reflinecolor -tags tag.$id]
     $canv lower $t
     foreach tag $marks x $xvals wid $wvals {
+       set tag_quoted [string map {% %%} $tag]
        set xl [expr {$x + $delta}]
        set xr [expr {$x + $delta + $wid + $lthickness}]
        set font mainfont
@@ -6143,13 +6540,19 @@ proc drawtags {id x xt y1} {
            # draw a tag
            set t [$canv create polygon $x [expr {$yt + $delta}] $xl $yt \
                       $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]
+                      -width 1 -outline $tagoutlinecolor -fill $tagbgcolor \
+                      -tags tag.$id]
+           if {$singletag} {
+               set tagclick [list showtags $id 1]
+           } else {
+               set tagclick [list showtag $tag_quoted 1]
+           }
+           $canv bind $t <1> $tagclick
            set rowtextx([rowofcommit $id]) [expr {$xr + $linespc}]
        } else {
            # draw a head or other ref
            if {[incr nheads -1] >= 0} {
-               set col green
+               set col $headbgcolor
                if {$tag eq $mainhead} {
                    set font mainfontbold
                }
@@ -6165,20 +6568,31 @@ proc drawtags {id x xt y1} {
                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
+                       -width 0 -fill $remotebgcolor -tags tag.$id
            }
        }
-       set t [$canv create text $xl $y1 -anchor w -text $tag -fill $fgcolor \
+       set t [$canv create text $xl $y1 -anchor w -text $tag -fill $headfgcolor \
                   -font $font -tags [list tag.$id text]]
        if {$ntags >= 0} {
-           $canv bind $t <1> [list showtag $tag 1]
+           $canv bind $t <1> $tagclick
        } elseif {$nheads >= 0} {
-           $canv bind $t $ctxbut [list headmenu %X %Y $id $tag]
+           $canv bind $t $ctxbut [list headmenu %X %Y $id $tag_quoted]
        }
     }
     return $xt
 }
 
+proc drawnotesign {xt y} {
+    global linespc canv fgcolor
+
+    set orad [expr {$linespc / 3}]
+    set t [$canv create rectangle [expr {$xt - $orad}] [expr {$y - $orad}] \
+              [expr {$xt + $orad - 1}] [expr {$y + $orad - 1}] \
+              -fill yellow -outline $fgcolor -width 1 -tags circle]
+    set xt [expr {$xt + $orad * 3}]
+    return $xt
+}
+
 proc xcoord {i level ln} {
     global canvx0 xspc1 xspc2
 
@@ -6309,7 +6723,7 @@ proc findmore {} {
     if {![info exists find_dirn]} {
        return 0
     }
-    set fldtypes [list [mc "Headline"] [mc "Author"] [mc "Date"] [mc "Committer"] [mc "CDate"] [mc "Comments"]]
+    set fldtypes [list [mc "Headline"] [mc "Author"] "" [mc "Committer"] "" [mc "Comments"]]
     set l $findcurline
     set moretodo 0
     if {$find_dirn > 0} {
@@ -6370,6 +6784,7 @@ proc findmore {} {
            }
            set info $commitinfo($id)
            foreach f $info ty $fldtypes {
+               if {$ty eq ""} continue
                if {($findloc eq [mc "All fields"] || $findloc eq $ty) &&
                    [doesmatch $f]} {
                    set found 1
@@ -6522,7 +6937,7 @@ proc appendwithlinks {text tags} {
 
     set start [$ctext index "end - 1c"]
     $ctext insert end $text $tags
-    set links [regexp -indices -all -inline {\m[0-9a-f]{6,40}\M} $text]
+    set links [regexp -indices -all -inline {(?:\m|-g)[0-9a-f]{6,40}\M} $text]
     foreach l $links {
        set s [lindex $l 0]
        set e [lindex $l 1]
@@ -6537,6 +6952,11 @@ proc appendwithlinks {text tags} {
 
 proc setlink {id lk} {
     global curview ctext pendinglinks
+    global linkfgcolor
+
+    if {[string range $id 0 1] eq "-g"} {
+      set id [string range $id 2 end]
+    }
 
     set known 0
     if {[string length $id] < 40} {
@@ -6550,7 +6970,7 @@ proc setlink {id lk} {
        set known [commitinview $id $curview]
     }
     if {$known} {
-       $ctext tag conf $lk -foreground blue -underline 1
+       $ctext tag conf $lk -foreground $linkfgcolor -underline 1
        $ctext tag bind $lk <1> [list selbyid $id]
        $ctext tag bind $lk <Enter> {linkcursor %W 1}
        $ctext tag bind $lk <Leave> {linkcursor %W -1}
@@ -6613,7 +7033,7 @@ proc viewnextline {dir} {
 # add a list of tag or branch names at position pos
 # returns the number of names inserted
 proc appendrefs {pos ids var} {
-    global ctext linknum curview $var maxrefs
+    global ctext linknum curview $var maxrefs mainheadid
 
     if {[catch {$ctext index $pos}]} {
        return 0
@@ -6626,24 +7046,54 @@ proc appendrefs {pos ids var} {
            lappend tags [list $tag $id]
        }
     }
+
+    set sep {}
+    set tags [lsort -index 0 -decreasing $tags]
+    set nutags 0
+
     if {[llength $tags] > $maxrefs} {
-       $ctext insert $pos "[mc "many"] ([llength $tags])"
-    } else {
-       set tags [lsort -index 0 -decreasing $tags]
-       set sep {}
-       foreach ti $tags {
-           set id [lindex $ti 1]
-           set lk link$linknum
-           incr linknum
-           $ctext tag delete $lk
-           $ctext insert $pos $sep
-           $ctext insert $pos [lindex $ti 0] $lk
-           setlink $id $lk
-           set sep ", "
+       # If we are displaying heads, and there are too many,
+       # see if there are some important heads to display.
+       # Currently this means "master" and the current head.
+       set itags {}
+       if {$var eq "idheads"} {
+           set utags {}
+           foreach ti $tags {
+               set hname [lindex $ti 0]
+               set id [lindex $ti 1]
+               if {($hname eq "master" || $id eq $mainheadid) &&
+                   [llength $itags] < $maxrefs} {
+                   lappend itags $ti
+               } else {
+                   lappend utags $ti
+               }
+           }
+           set tags $utags
+       }
+       if {$itags ne {}} {
+           set str [mc "and many more"]
+           set sep " "
+       } else {
+           set str [mc "many"]
        }
+       $ctext insert $pos "$str ([llength $tags])"
+       set nutags [llength $tags]
+       set tags $itags
     }
+
+    foreach ti $tags {
+       set id [lindex $ti 1]
+       set lk link$linknum
+       incr linknum
+       $ctext tag delete $lk
+       $ctext insert $pos $sep
+       $ctext insert $pos [lindex $ti 0] $lk
+       setlink $id $lk
+       set sep ", "
+    }
+    $ctext tag add wwrap "$pos linestart" "$pos lineend"
     $ctext conf -state disabled
-    return [llength $tags]
+    return [expr {[llength $tags] + $nutags}]
 }
 
 # called when we have finished computing the nearby tags
@@ -6724,7 +7174,7 @@ proc make_idmark {id} {
     $canv raise $t
 }
 
-proc selectline {l isnew {desired_loc {}}} {
+proc selectline {l isnew {desired_loc {}} {switch_to_patch 0}} {
     global canv ctext commitinfo selectedline
     global canvy0 linespc parents children curview
     global currentid sha1entry
@@ -6732,7 +7182,8 @@ proc selectline {l isnew {desired_loc {}}} {
     global mergemax numcommits pending_select
     global cmitmode showneartags allcommits
     global targetrow targetid lastscrollrows
-    global autoselect jump_to_here
+    global autoselect autosellen jump_to_here
+    global vinlinediff
 
     catch {unset pending_select}
     $canv delete hover
@@ -6749,6 +7200,10 @@ proc selectline {l isnew {desired_loc {}}} {
        setcanvscroll
     }
 
+    if {$cmitmode ne "patch" && $switch_to_patch} {
+        set cmitmode "patch"
+    }
+
     set y [expr {$canvy0 + $l * $linespc}]
     set ymax [lindex [$canv cget -scrollregion] 3]
     set ytop [expr {$y - $linespc - 1}]
@@ -6788,14 +7243,13 @@ proc selectline {l isnew {desired_loc {}}} {
     make_secsel $id
 
     if {$isnew} {
-       addtohistory [list selbyid $id]
+       addtohistory [list selbyid $id 0] savecmitpos
     }
 
     $sha1entry delete 0 end
     $sha1entry insert 0 $id
     if {$autoselect} {
-       $sha1entry selection from 0
-       $sha1entry selection to end
+       $sha1entry selection range 0 $autosellen
     }
     rhighlight_sel $id
 
@@ -6875,6 +7329,8 @@ proc selectline {l isnew {desired_loc {}}} {
     init_flist [mc "Comments"]
     if {$cmitmode eq "tree"} {
        gettree $id
+    } elseif {$vinlinediff($curview) == 1} {
+       showinlinediff $id
     } elseif {[llength $olds] <= 1} {
        startdiff $id
     } else {
@@ -6940,10 +7396,12 @@ proc reselectline {} {
     }
 }
 
-proc addtohistory {cmd} {
+proc addtohistory {cmd {saveproc {}}} {
     global history historyindex curview
 
-    set elt [list $curview $cmd]
+    unset_posvars
+    save_position
+    set elt [list $curview $cmd $saveproc {}]
     if {$historyindex > 0
        && [lindex $history [expr {$historyindex - 1}]] == $elt} {
        return
@@ -6963,14 +7421,45 @@ proc addtohistory {cmd} {
     .tf.bar.rightbut conf -state disabled
 }
 
+# save the scrolling position of the diff display pane
+proc save_position {} {
+    global historyindex history
+
+    if {$historyindex < 1} return
+    set hi [expr {$historyindex - 1}]
+    set fn [lindex $history $hi 2]
+    if {$fn ne {}} {
+       lset history $hi 3 [eval $fn]
+    }
+}
+
+proc unset_posvars {} {
+    global last_posvars
+
+    if {[info exists last_posvars]} {
+       foreach {var val} $last_posvars {
+           global $var
+           catch {unset $var}
+       }
+       unset last_posvars
+    }
+}
+
 proc godo {elt} {
-    global curview
+    global curview last_posvars
 
     set view [lindex $elt 0]
     set cmd [lindex $elt 1]
+    set pv [lindex $elt 3]
     if {$curview != $view} {
        showview $view
     }
+    unset_posvars
+    foreach {var val} $pv {
+       global $var
+       set $var $val
+    }
+    set last_posvars $pv
     eval $cmd
 }
 
@@ -6979,6 +7468,7 @@ proc goback {} {
     focus .
 
     if {$historyindex > 1} {
+       save_position
        incr historyindex -1
        godo [lindex $history [expr {$historyindex - 1}]]
        .tf.bar.rightbut conf -state normal
@@ -6993,6 +7483,7 @@ proc goforw {} {
     focus .
 
     if {$historyindex < [llength $history]} {
+       save_position
        set cmd [lindex $history $historyindex]
        incr historyindex
        godo $cmd
@@ -7130,6 +7621,7 @@ proc getblobline {bf id} {
                            [lindex [split $commentend .] 0]}]
            mark_ctext_line $lnum
        }
+       $ctext config -state disabled
        return 0
     }
     $ctext config -state disabled
@@ -7175,19 +7667,48 @@ proc startdiff {ids} {
     }
 }
 
+proc showinlinediff {ids} {
+    global commitinfo commitdata ctext
+    global treediffs
+
+    set info $commitinfo($ids)
+    set diff [lindex $info 7]
+    set difflines [split $diff "\n"]
+
+    initblobdiffvars
+    set treediff {}
+
+    set inhdr 0
+    foreach line $difflines {
+       if {![string compare -length 5 "diff " $line]} {
+           set inhdr 1
+       } elseif {$inhdr && ![string compare -length 4 "+++ " $line]} {
+           # offset also accounts for the b/ prefix
+           lappend treediff [string range $line 6 end]
+           set inhdr 0
+       }
+    }
+
+    set treediffs($ids) $treediff
+    add_flist $treediff
+
+    $ctext conf -state normal
+    foreach line $difflines {
+       parseblobdiffline $ids $line
+    }
+    maybe_scroll_ctext 1
+    $ctext conf -state disabled
+}
+
+# If the filename (name) is under any of the passed filter paths
+# then return true to include the file in the listing.
 proc path_filter {filter name} {
+    set worktree [gitworktree]
     foreach p $filter {
-       set l [string length $p]
-       if {[string index $p end] eq "/"} {
-           if {[string compare -length $l $p $name] == 0} {
-               return 1
-           }
-       } else {
-           if {[string compare -length $l $p $name] == 0 &&
-               ([string length $name] == $l ||
-                [string index $name $l] eq "/")} {
-               return 1
-           }
+       set fq_p [file normalize $p]
+       set fq_n [file normalize [file join $worktree $name]]
+       if {[string match [file normalize $fq_p]* $fq_n]} {
+           return 1
        }
     }
     return 0
@@ -7201,7 +7722,7 @@ proc addtocflist {ids} {
 }
 
 proc diffcmd {ids flags} {
-    global nullid nullid2
+    global log_showroot nullid nullid2 git_version
 
     set i [lsearch -exact $ids $nullid]
     set j [lsearch -exact $ids $nullid2]
@@ -7222,6 +7743,9 @@ proc diffcmd {ids flags} {
            }
        }
     } elseif {$j >= 0} {
+       if {[package vcompare $git_version "1.7.2"] >= 0} {
+           set flags "$flags --ignore-submodules=dirty"
+       }
        set cmd [concat | git diff-index --cached $flags]
        if {[llength $ids] > 1} {
            # comparing index with specific revision
@@ -7235,15 +7759,22 @@ proc diffcmd {ids flags} {
            lappend cmd HEAD
        }
     } else {
+       if {$log_showroot} {
+           lappend flags --root
+       }
        set cmd [concat | git diff-tree -r $flags $ids]
     }
     return $cmd
 }
 
 proc gettreediffs {ids} {
-    global treediff treepending
+    global treediff treepending limitdiffs vfilelimit curview
 
-    if {[catch {set gdtf [open [diffcmd $ids {--no-commit-id}] r]}]} return
+    set cmd [diffcmd $ids {--no-commit-id}]
+    if {$limitdiffs && $vfilelimit($curview) ne {}} {
+           set cmd [concat $cmd -- $vfilelimit($curview)]
+    }
+    if {[catch {set gdtf [open $cmd r]}]} return
 
     set treepending $ids
     set treediff {}
@@ -7287,17 +7818,7 @@ proc gettreediffline {gdtf ids} {
        return [expr {$nr >= $max? 2: 1}]
     }
     close $gdtf
-    if {$limitdiffs && $vfilelimit($curview) ne {}} {
-       set flist {}
-       foreach f $treediff {
-           if {[path_filter $vfilelimit($curview) $f]} {
-               lappend flist $f
-           }
-       }
-       set treediffs($ids) $flist
-    } else {
-       set treediffs($ids) $treediff
-    }
+    set treediffs($ids) $treediff
     unset treepending
     if {$cmitmode eq "tree" && [llength $diffids] == 1} {
        gettree $diffids
@@ -7331,13 +7852,28 @@ proc changeignorespace {} {
     reselectline
 }
 
+proc changeworddiff {name ix op} {
+    reselectline
+}
+
+proc initblobdiffvars {} {
+    global diffencoding targetline diffnparents
+    global diffinhdr currdiffsubmod diffseehere
+    set targetline {}
+    set diffnparents 0
+    set diffinhdr 0
+    set diffencoding [get_path_encoding {}]
+    set currdiffsubmod ""
+    set diffseehere -1
+}
+
 proc getblobdiffs {ids} {
     global blobdifffd diffids env
-    global diffinhdr treediffs
+    global treediffs
     global diffcontext
     global ignorespace
+    global worddiff
     global limitdiffs vfilelimit curview
-    global diffencoding targetline diffnparents
     global git_version
 
     set textconv {}
@@ -7352,6 +7888,9 @@ proc getblobdiffs {ids} {
     if {$ignorespace} {
        append cmd " -w"
     }
+    if {$worddiff ne [mc "Line diff"]} {
+       append cmd " --word-diff=porcelain"
+    }
     if {$limitdiffs && $vfilelimit($curview) ne {}} {
        set cmd [concat $cmd -- $vfilelimit($curview)]
     }
@@ -7359,15 +7898,40 @@ proc getblobdiffs {ids} {
        error_popup [mc "Error getting diffs: %s" $err]
        return
     }
-    set targetline {}
-    set diffnparents 0
-    set diffinhdr 0
-    set diffencoding [get_path_encoding {}]
     fconfigure $bdf -blocking 0 -encoding binary -eofchar {}
     set blobdifffd($ids) $bdf
+    initblobdiffvars
     filerun $bdf [list getblobdiffline $bdf $diffids]
 }
 
+proc savecmitpos {} {
+    global ctext cmitmode
+
+    if {$cmitmode eq "tree"} {
+       return {}
+    }
+    return [list target_scrollpos [$ctext index @0,0]]
+}
+
+proc savectextpos {} {
+    global ctext
+
+    return [list target_scrollpos [$ctext index @0,0]]
+}
+
+proc maybe_scroll_ctext {ateof} {
+    global ctext target_scrollpos
+
+    if {![info exists target_scrollpos]} return
+    if {!$ateof} {
+       set nlines [expr {[winfo height $ctext]
+                         / [font metrics textfont -linespace]}]
+       if {[$ctext compare "$target_scrollpos + $nlines lines" <= end]} return
+    }
+    $ctext yview $target_scrollpos
+    unset target_scrollpos
+}
+
 proc setinlist {var i val} {
     global $var
 
@@ -7402,12 +7966,17 @@ proc makediffhdr {fname ids} {
     set diffline 0
 }
 
+proc blobdiffmaybeseehere {ateof} {
+    global diffseehere
+    if {$diffseehere >= 0} {
+       mark_ctext_line [lindex [split $diffseehere .] 0]
+    }
+    maybe_scroll_ctext $ateof
+}
+
 proc getblobdiffline {bdf ids} {
-    global diffids blobdifffd ctext curdiffstart
-    global diffnexthead diffnextnote difffilestart
-    global ctext_file_names ctext_file_lines
-    global diffinhdr treediffs mergemax diffnparents
-    global diffencoding jump_to_here targetline diffline
+    global diffids blobdifffd
+    global ctext
 
     set nr 0
     $ctext conf -state normal
@@ -7416,174 +7985,220 @@ proc getblobdiffline {bdf ids} {
            catch {close $bdf}
            return 0
        }
-       if {![string compare -length 5 "diff " $line]} {
-           if {![regexp {^diff (--cc|--git) } $line m type]} {
-               set line [encoding convertfrom $line]
-               $ctext insert end "$line\n" hunksep
-               continue
+       parseblobdiffline $ids $line
+    }
+    $ctext conf -state disabled
+    blobdiffmaybeseehere [eof $bdf]
+    if {[eof $bdf]} {
+       catch {close $bdf}
+       return 0
+    }
+    return [expr {$nr >= 1000? 2: 1}]
+}
+
+proc parseblobdiffline {ids line} {
+    global ctext curdiffstart
+    global diffnexthead diffnextnote difffilestart
+    global ctext_file_names ctext_file_lines
+    global diffinhdr treediffs mergemax diffnparents
+    global diffencoding jump_to_here targetline diffline currdiffsubmod
+    global worddiff diffseehere
+
+    if {![string compare -length 5 "diff " $line]} {
+       if {![regexp {^diff (--cc|--git) } $line m type]} {
+           set line [encoding convertfrom $line]
+           $ctext insert end "$line\n" hunksep
+           continue
+       }
+       # start of a new file
+       set diffinhdr 1
+       $ctext insert end "\n"
+       set curdiffstart [$ctext index "end - 1c"]
+       lappend ctext_file_names ""
+       lappend ctext_file_lines [lindex [split $curdiffstart "."] 0]
+       $ctext insert end "\n" filesep
+
+       if {$type eq "--cc"} {
+           # start of a new file in a merge diff
+           set fname [string range $line 10 end]
+           if {[lsearch -exact $treediffs($ids) $fname] < 0} {
+               lappend treediffs($ids) $fname
+               add_flist [list $fname]
            }
-           # start of a new file
-           set diffinhdr 1
-           $ctext insert end "\n"
-           set curdiffstart [$ctext index "end - 1c"]
-           lappend ctext_file_names ""
-           lappend ctext_file_lines [lindex [split $curdiffstart "."] 0]
-           $ctext insert end "\n" filesep
-
-           if {$type eq "--cc"} {
-               # start of a new file in a merge diff
-               set fname [string range $line 10 end]
-               if {[lsearch -exact $treediffs($ids) $fname] < 0} {
-                   lappend treediffs($ids) $fname
-                   add_flist [list $fname]
-               }
 
+       } else {
+           set line [string range $line 11 end]
+           # If the name hasn't changed the length will be odd,
+           # the middle char will be a space, and the two bits either
+           # side will be a/name and b/name, or "a/name" and "b/name".
+           # If the name has changed we'll get "rename from" and
+           # "rename to" or "copy from" and "copy to" lines following
+           # this, and we'll use them to get the filenames.
+           # This complexity is necessary because spaces in the
+           # filename(s) don't get escaped.
+           set l [string length $line]
+           set i [expr {$l / 2}]
+           if {!(($l & 1) && [string index $line $i] eq " " &&
+                 [string range $line 2 [expr {$i - 1}]] eq \
+                     [string range $line [expr {$i + 3}] end])} {
+               return
+           }
+           # unescape if quoted and chop off the a/ from the front
+           if {[string index $line 0] eq "\""} {
+               set fname [string range [lindex $line 0] 2 end]
            } else {
-               set line [string range $line 11 end]
-               # If the name hasn't changed the length will be odd,
-               # the middle char will be a space, and the two bits either
-               # side will be a/name and b/name, or "a/name" and "b/name".
-               # If the name has changed we'll get "rename from" and
-               # "rename to" or "copy from" and "copy to" lines following
-               # this, and we'll use them to get the filenames.
-               # This complexity is necessary because spaces in the
-               # filename(s) don't get escaped.
-               set l [string length $line]
-               set i [expr {$l / 2}]
-               if {!(($l & 1) && [string index $line $i] eq " " &&
-                     [string range $line 2 [expr {$i - 1}]] eq \
-                         [string range $line [expr {$i + 3}] end])} {
-                   continue
-               }
-               # unescape if quoted and chop off the a/ from the front
-               if {[string index $line 0] eq "\""} {
-                   set fname [string range [lindex $line 0] 2 end]
-               } else {
-                   set fname [string range $line 2 [expr {$i - 1}]]
-               }
+               set fname [string range $line 2 [expr {$i - 1}]]
            }
-           makediffhdr $fname $ids
+       }
+       makediffhdr $fname $ids
+
+    } elseif {![string compare -length 16 "* Unmerged path " $line]} {
+       set fname [encoding convertfrom [string range $line 16 end]]
+       $ctext insert end "\n"
+       set curdiffstart [$ctext index "end - 1c"]
+       lappend ctext_file_names $fname
+       lappend ctext_file_lines [lindex [split $curdiffstart "."] 0]
+       $ctext insert end "$line\n" filesep
+       set i [lsearch -exact $treediffs($ids) $fname]
+       if {$i >= 0} {
+           setinlist difffilestart $i $curdiffstart
+       }
+
+    } elseif {![string compare -length 2 "@@" $line]} {
+       regexp {^@@+} $line ats
+       set line [encoding convertfrom $diffencoding $line]
+       $ctext insert end "$line\n" hunksep
+       if {[regexp { \+(\d+),\d+ @@} $line m nl]} {
+           set diffline $nl
+       }
+       set diffnparents [expr {[string length $ats] - 1}]
+       set diffinhdr 0
 
-       } elseif {![string compare -length 16 "* Unmerged path " $line]} {
-           set fname [encoding convertfrom [string range $line 16 end]]
-           $ctext insert end "\n"
-           set curdiffstart [$ctext index "end - 1c"]
-           lappend ctext_file_names $fname
-           lappend ctext_file_lines [lindex [split $curdiffstart "."] 0]
+    } elseif {![string compare -length 10 "Submodule " $line]} {
+       # start of a new submodule
+       if {[regexp -indices "\[0-9a-f\]+\\.\\." $line nameend]} {
+           set fname [string range $line 10 [expr [lindex $nameend 0] - 2]]
+       } else {
+           set fname [string range $line 10 [expr [string first "contains " $line] - 2]]
+       }
+       if {$currdiffsubmod != $fname} {
+           $ctext insert end "\n";     # Add newline after commit message
+       }
+       set curdiffstart [$ctext index "end - 1c"]
+       lappend ctext_file_names ""
+       if {$currdiffsubmod != $fname} {
+           lappend ctext_file_lines $fname
+           makediffhdr $fname $ids
+           set currdiffsubmod $fname
+           $ctext insert end "\n$line\n" filesep
+       } else {
            $ctext insert end "$line\n" filesep
+       }
+    } elseif {![string compare -length 3 "  >" $line]} {
+       set $currdiffsubmod ""
+       set line [encoding convertfrom $diffencoding $line]
+       $ctext insert end "$line\n" dresult
+    } elseif {![string compare -length 3 "  <" $line]} {
+       set $currdiffsubmod ""
+       set line [encoding convertfrom $diffencoding $line]
+       $ctext insert end "$line\n" d0
+    } elseif {$diffinhdr} {
+       if {![string compare -length 12 "rename from " $line]} {
+           set fname [string range $line [expr 6 + [string first " from " $line] ] end]
+           if {[string index $fname 0] eq "\""} {
+               set fname [lindex $fname 0]
+           }
+           set fname [encoding convertfrom $fname]
            set i [lsearch -exact $treediffs($ids) $fname]
            if {$i >= 0} {
                setinlist difffilestart $i $curdiffstart
            }
-
-       } elseif {![string compare -length 2 "@@" $line]} {
-           regexp {^@@+} $line ats
-           set line [encoding convertfrom $diffencoding $line]
-           $ctext insert end "$line\n" hunksep
-           if {[regexp { \+(\d+),\d+ @@} $line m nl]} {
-               set diffline $nl
+       } elseif {![string compare -length 10 $line "rename to "] ||
+                 ![string compare -length 8 $line "copy to "]} {
+           set fname [string range $line [expr 4 + [string first " to " $line] ] end]
+           if {[string index $fname 0] eq "\""} {
+               set fname [lindex $fname 0]
            }
-           set diffnparents [expr {[string length $ats] - 1}]
+           makediffhdr $fname $ids
+       } elseif {[string compare -length 3 $line "---"] == 0} {
+           # do nothing
+           return
+       } elseif {[string compare -length 3 $line "+++"] == 0} {
            set diffinhdr 0
+           return
+       }
+       $ctext insert end "$line\n" filesep
 
-       } elseif {![string compare -length 10 "Submodule " $line]} {
-           # start of a new submodule
-           if {[string compare [$ctext get "end - 4c" end] "\n \n\n"]} {
-               $ctext insert end "\n";     # Add newline after commit message
-           }
-           set curdiffstart [$ctext index "end - 1c"]
-           lappend ctext_file_names ""
-           set fname [string range $line 10 [expr [string last " " $line] - 1]]
-           lappend ctext_file_lines $fname
-           makediffhdr $fname $ids
-           $ctext insert end "\n$line\n" filesep
-       } elseif {![string compare -length 3 "  >" $line]} {
-           $ctext insert end "$line\n" dresult
-       } elseif {![string compare -length 3 "  <" $line]} {
-           $ctext insert end "$line\n" d0
-       } elseif {$diffinhdr} {
-           if {![string compare -length 12 "rename from " $line]} {
-               set fname [string range $line [expr 6 + [string first " from " $line] ] end]
-               if {[string index $fname 0] eq "\""} {
-                   set fname [lindex $fname 0]
+    } else {
+       set line [string map {\x1A ^Z} \
+                     [encoding convertfrom $diffencoding $line]]
+       # parse the prefix - one ' ', '-' or '+' for each parent
+       set prefix [string range $line 0 [expr {$diffnparents - 1}]]
+       set tag [expr {$diffnparents > 1? "m": "d"}]
+       set dowords [expr {$worddiff ne [mc "Line diff"] && $diffnparents == 1}]
+       set words_pre_markup ""
+       set words_post_markup ""
+       if {[string trim $prefix " -+"] eq {}} {
+           # prefix only has " ", "-" and "+" in it: normal diff line
+           set num [string first "-" $prefix]
+           if {$dowords} {
+               set line [string range $line 1 end]
+           }
+           if {$num >= 0} {
+               # removed line, first parent with line is $num
+               if {$num >= $mergemax} {
+                   set num "max"
                }
-               set fname [encoding convertfrom $fname]
-               set i [lsearch -exact $treediffs($ids) $fname]
-               if {$i >= 0} {
-                   setinlist difffilestart $i $curdiffstart
+               if {$dowords && $worddiff eq [mc "Markup words"]} {
+                   $ctext insert end "\[-$line-\]" $tag$num
+               } else {
+                   $ctext insert end "$line" $tag$num
                }
-           } elseif {![string compare -length 10 $line "rename to "] ||
-                     ![string compare -length 8 $line "copy to "]} {
-               set fname [string range $line [expr 4 + [string first " to " $line] ] end]
-               if {[string index $fname 0] eq "\""} {
-                   set fname [lindex $fname 0]
+               if {!$dowords} {
+                   $ctext insert end "\n" $tag$num
                }
-               makediffhdr $fname $ids
-           } elseif {[string compare -length 3 $line "---"] == 0} {
-               # do nothing
-               continue
-           } elseif {[string compare -length 3 $line "+++"] == 0} {
-               set diffinhdr 0
-               continue
-           }
-           $ctext insert end "$line\n" filesep
-
-       } else {
-           set line [string map {\x1A ^Z} \
-                          [encoding convertfrom $diffencoding $line]]
-           # parse the prefix - one ' ', '-' or '+' for each parent
-           set prefix [string range $line 0 [expr {$diffnparents - 1}]]
-           set tag [expr {$diffnparents > 1? "m": "d"}]
-           if {[string trim $prefix " -+"] eq {}} {
-               # prefix only has " ", "-" and "+" in it: normal diff line
-               set num [string first "-" $prefix]
-               if {$num >= 0} {
-                   # removed line, first parent with line is $num
-                   if {$num >= $mergemax} {
-                       set num "max"
-                   }
-                   $ctext insert end "$line\n" $tag$num
-               } else {
-                   set tags {}
-                   if {[string first "+" $prefix] >= 0} {
-                       # added line
-                       lappend tags ${tag}result
-                       if {$diffnparents > 1} {
-                           set num [string first " " $prefix]
-                           if {$num >= 0} {
-                               if {$num >= $mergemax} {
-                                   set num "max"
-                               }
-                               lappend tags m$num
+           } else {
+               set tags {}
+               if {[string first "+" $prefix] >= 0} {
+                   # added line
+                   lappend tags ${tag}result
+                   if {$diffnparents > 1} {
+                       set num [string first " " $prefix]
+                       if {$num >= 0} {
+                           if {$num >= $mergemax} {
+                               set num "max"
                            }
+                           lappend tags m$num
                        }
                    }
-                   if {$targetline ne {}} {
-                       if {$diffline == $targetline} {
-                           set seehere [$ctext index "end - 1 chars"]
-                           set targetline {}
-                       } else {
-                           incr diffline
-                       }
+                   set words_pre_markup "{+"
+                   set words_post_markup "+}"
+               }
+               if {$targetline ne {}} {
+                   if {$diffline == $targetline} {
+                       set diffseehere [$ctext index "end - 1 chars"]
+                       set targetline {}
+                   } else {
+                       incr diffline
                    }
-                   $ctext insert end "$line\n" $tags
                }
-           } else {
-               # "\ No newline at end of file",
-               # or something else we don't recognize
-               $ctext insert end "$line\n" hunksep
+               if {$dowords && $worddiff eq [mc "Markup words"]} {
+                   $ctext insert end "$words_pre_markup$line$words_post_markup" $tags
+               } else {
+                   $ctext insert end "$line" $tags
+               }
+               if {!$dowords} {
+                   $ctext insert end "\n" $tags
+               }
            }
+       } elseif {$dowords && $prefix eq "~"} {
+           $ctext insert end "\n" {}
+       } else {
+           # "\ No newline at end of file",
+           # or something else we don't recognize
+           $ctext insert end "$line\n" hunksep
        }
     }
-    if {[info exists seehere]} {
-       mark_ctext_line [lindex [split $seehere .] 0]
-    }
-    $ctext conf -state disabled
-    if {[eof $bdf]} {
-       catch {close $bdf}
-       return 0
-    }
-    return [expr {$nr >= 1000? 2: 1}]
 }
 
 proc changediffdisp {} {
@@ -7593,32 +8208,45 @@ proc changediffdisp {} {
     $ctext tag conf dresult -elide [lindex $diffelide 1]
 }
 
-proc highlightfile {loc cline} {
-    global ctext cflist cflist_top
+proc highlightfile {cline} {
+    global cflist cflist_top
+
+    if {![info exists cflist_top]} return
 
-    $ctext yview $loc
     $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
     $cflist tag add highlight $cline.0 "$cline.0 lineend"
     $cflist see $cline.0
     set cflist_top $cline
 }
 
+proc highlightfile_for_scrollpos {topidx} {
+    global cmitmode difffilestart
+
+    if {$cmitmode eq "tree"} return
+    if {![info exists difffilestart]} return
+
+    set top [lindex [split $topidx .] 0]
+    if {$difffilestart eq {} || $top < [lindex $difffilestart 0]} {
+       highlightfile 0
+    } else {
+       highlightfile [expr {[bsearch $difffilestart $top] + 2}]
+    }
+}
+
 proc prevfile {} {
     global difffilestart ctext cmitmode
 
     if {$cmitmode eq "tree"} return
     set prev 0.0
-    set prevline 1
     set here [$ctext index @0,0]
     foreach loc $difffilestart {
        if {[$ctext compare $loc >= $here]} {
-           highlightfile $prev $prevline
+           $ctext yview $prev
            return
        }
        set prev $loc
-       incr prevline
     }
-    highlightfile $prev $prevline
+    $ctext yview $prev
 }
 
 proc nextfile {} {
@@ -7626,11 +8254,9 @@ proc nextfile {} {
 
     if {$cmitmode eq "tree"} return
     set here [$ctext index @0,0]
-    set line 1
     foreach loc $difffilestart {
-       incr line
        if {[$ctext compare $loc > $here]} {
-           highlightfile $loc $line
+           $ctext yview $loc
            return
        }
     }
@@ -7676,7 +8302,6 @@ proc settabs {{firstab {}}} {
 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]
@@ -7689,12 +8314,17 @@ proc incrsearch {name ix op} {
        }
     }
     if {$searchstring ne {}} {
-       set here [$ctext search $searchdirn -- $searchstring anchor]
+       set here [$ctext search -count mlen $searchdirn -- $searchstring anchor]
        if {$here ne {}} {
            $ctext see $here
+           set mend "$here + $mlen c"
+           $ctext tag remove sel 1.0 end
+           $ctext tag add sel $here $mend
+           suppress_highlighting_file_for_current_scrollpos
+           highlightfile_for_scrollpos $here
        }
-       searchmarkvisible 1
     }
+    rehighlight_search_results
 }
 
 proc dosearch {} {
@@ -7717,9 +8347,12 @@ proc dosearch {} {
            return
        }
        $ctext see $match
+       suppress_highlighting_file_for_current_scrollpos
+       highlightfile_for_scrollpos $match
        set mend "$match + $mlen c"
        $ctext tag add sel $match $mend
        $ctext mark unset anchor
+       rehighlight_search_results
     }
 }
 
@@ -7743,21 +8376,41 @@ proc dosearchback {} {
            return
        }
        $ctext see $match
+       suppress_highlighting_file_for_current_scrollpos
+       highlightfile_for_scrollpos $match
        set mend "$match + $ml c"
        $ctext tag add sel $match $mend
        $ctext mark unset anchor
+       rehighlight_search_results
+    }
+}
+
+proc rehighlight_search_results {} {
+    global ctext searchstring
+
+    $ctext tag remove found 1.0 end
+    $ctext tag remove currentsearchhit 1.0 end
+
+    if {$searchstring ne {}} {
+       searchmarkvisible 1
     }
 }
 
 proc searchmark {first last} {
     global ctext searchstring
 
+    set sel [$ctext tag ranges sel]
+
     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
+       if {$sel ne {} && [$ctext compare $match == [lindex $sel 0]]} {
+           $ctext tag add currentsearchhit $match $mend
+       } else {
+           $ctext tag add found $match $mend
+       }
     }
 }
 
@@ -7783,8 +8436,23 @@ proc searchmarkvisible {doall} {
     }
 }
 
+proc suppress_highlighting_file_for_current_scrollpos {} {
+    global ctext suppress_highlighting_file_for_this_scrollpos
+
+    set suppress_highlighting_file_for_this_scrollpos [$ctext index @0,0]
+}
+
 proc scrolltext {f0 f1} {
-    global searchstring
+    global searchstring cmitmode ctext
+    global suppress_highlighting_file_for_this_scrollpos
+
+    set topidx [$ctext index @0,0]
+    if {![info exists suppress_highlighting_file_for_this_scrollpos]
+       || $topidx ne $suppress_highlighting_file_for_this_scrollpos} {
+       highlightfile_for_scrollpos $topidx
+    }
+
+    catch {unset suppress_highlighting_file_for_this_scrollpos}
 
     .bleft.bottom.sb set $f0 $f1
     if {$searchstring ne {}} {
@@ -8000,6 +8668,8 @@ proc lineleave {id} {
 proc linehover {} {
     global hoverx hovery hoverid hovertimer
     global canv linespc lthickness
+    global linehoverbgcolor linehoverfgcolor linehoveroutlinecolor
+
     global commitinfo
 
     set text [lindex $commitinfo($hoverid) 0]
@@ -8013,10 +8683,11 @@ proc linehover {} {
     set x1 [expr {$x + [font measure mainfont $text] + 2 * $lthickness}]
     set y1 [expr {$y + $linespc + 2 * $lthickness}]
     set t [$canv create rectangle $x0 $y0 $x1 $y1 \
-              -fill \#ffff80 -outline black -width 1 -tags hover]
+              -fill $linehoverbgcolor -outline $linehoveroutlinecolor \
+              -width 1 -tags hover]
     $canv raise $t
     set t [$canv create text $x $y -anchor nw -text $text -tags hover \
-              -font mainfont]
+              -font mainfont -fill $linehoverfgcolor]
     $canv raise $t
 }
 
@@ -8077,7 +8748,7 @@ proc lineclick {x y id isnew} {
     }
 
     if {$isnew} {
-       addtohistory [list lineclick $x $y $id 0]
+       addtohistory [list lineclick $x $y $id 0] savectextpos
     }
     # fill the details pane with info about this line
     $ctext conf -state normal
@@ -8108,6 +8779,7 @@ proc lineclick {x y id isnew} {
            $ctext insert end "\n\t[mc "Date"]:\t$date\n"
        }
     }
+    maybe_scroll_ctext 1
     $ctext conf -state disabled
     init_flist {}
 }
@@ -8121,10 +8793,10 @@ proc normalline {} {
     }
 }
 
-proc selbyid {id} {
+proc selbyid {id {isnew 1}} {
     global curview
     if {[commitinview $id $curview]} {
-       selectline [rowofcommit $id] 1
+       selectline [rowofcommit $id] $isnew
     }
 }
 
@@ -8147,6 +8819,11 @@ proc rowmenu {x y id} {
     } else {
        set state normal
     }
+    if {[info exists markedid] && $markedid ne $id} {
+       set mstate normal
+    } else {
+       set mstate disabled
+    }
     if {$id ne $nullid && $id ne $nullid2} {
        set menu $rowctxmenu
        if {$mainhead ne {}} {
@@ -8154,21 +8831,17 @@ proc rowmenu {x y id} {
        } else {
            $menu entryconfigure 7 -label [mc "Detached head: can't reset" $mainhead] -state disabled
        }
-       if {[info exists markedid] && $markedid ne $id} {
-           $menu entryconfigure 9 -state normal
-           $menu entryconfigure 10 -state normal
-           $menu entryconfigure 11 -state normal
-       } else {
-           $menu entryconfigure 9 -state disabled
-           $menu entryconfigure 10 -state disabled
-           $menu entryconfigure 11 -state disabled
-       }
+       $menu entryconfigure 9 -state $mstate
+       $menu entryconfigure 10 -state $mstate
+       $menu entryconfigure 11 -state $mstate
     } else {
        set menu $fakerowmenu
     }
     $menu entryconfigure [mca "Diff this -> selected"] -state $state
     $menu entryconfigure [mca "Diff selected -> this"] -state $state
     $menu entryconfigure [mca "Make patch"] -state $state
+    $menu entryconfigure [mca "Diff this -> marked commit"] -state $mstate
+    $menu entryconfigure [mca "Diff marked commit -> this"] -state $mstate
     tk_popup $menu $x $y
 }
 
@@ -8309,30 +8982,30 @@ proc do_cmp_commits {a b} {
            }
        }
        if {$skipa} {
-           if {[llength $children($curview,$a)] != 1} {
+           set kids [real_children $curview,$a]
+           if {[llength $kids] != 1} {
                $ctext insert end "\n"
                appendshortlink $a [mc "Commit "] \
-                   [mc " has %s children - stopping\n" \
-                        [llength $children($curview,$a)]]
+                   [mc " has %s children - stopping\n" [llength $kids]]
                break
            }
-           set a [lindex $children($curview,$a) 0]
+           set a [lindex $kids 0]
        }
        if {$skipb} {
-           if {[llength $children($curview,$b)] != 1} {
+           set kids [real_children $curview,$b]
+           if {[llength $kids] != 1} {
                appendshortlink $b [mc "Commit "] \
-                   [mc " has %s children - stopping\n" \
-                        [llength $children($curview,$b)]]
+                   [mc " has %s children - stopping\n" [llength $kids]]
                break
            }
-           set b [lindex $children($curview,$b) 0]
+           set b [lindex $kids 0]
        }
     }
     $ctext conf -state disabled
 }
 
 proc diffcommits {a b} {
-    global diffcontext diffids blobdifffd diffinhdr
+    global diffcontext diffids blobdifffd diffinhdr currdiffsubmod
 
     set tmpdir [gitknewtmpdir]
     set fna [file join $tmpdir "commit-[string range $a 0 7]"]
@@ -8353,6 +9026,7 @@ proc diffcommits {a b} {
     set diffids [list commits $a $b]
     set blobdifffd($diffids) $fd
     set diffinhdr 0
+    set currdiffsubmod ""
     filerun $fd [list getblobdiffline $fd $diffids]
 }
 
@@ -8367,7 +9041,22 @@ proc diffvssel {dirn} {
        set oldid $rowmenuid
        set newid [commitonrow $selectedline]
     }
-    addtohistory [list doseldiff $oldid $newid]
+    addtohistory [list doseldiff $oldid $newid] savectextpos
+    doseldiff $oldid $newid
+}
+
+proc diffvsmark {dirn} {
+    global rowmenuid markedid
+
+    if {![info exists markedid]} return
+    if {$dirn} {
+       set oldid $markedid
+       set newid $rowmenuid
+    } else {
+       set oldid $rowmenuid
+       set newid $markedid
+    }
+    addtohistory [list doseldiff $oldid $newid] savectextpos
     doseldiff $oldid $newid
 }
 
@@ -8395,7 +9084,7 @@ proc doseldiff {oldid newid} {
 }
 
 proc mkpatch {} {
-    global rowmenuid currentid commitinfo patchtop patchnum
+    global rowmenuid currentid commitinfo patchtop patchnum NS
 
     if {![info exists currentid]} return
     set oldid $currentid
@@ -8405,38 +9094,38 @@ proc mkpatch {} {
     set top .patch
     set patchtop $top
     catch {destroy $top}
-    toplevel $top
+    ttk_toplevel $top
     make_transient $top .
-    label $top.title -text [mc "Generate patch"]
+    ${NS}::label $top.title -text [mc "Generate patch"]
     grid $top.title - -pady 10
-    label $top.from -text [mc "From:"]
-    entry $top.fromsha1 -width 40 -relief flat
+    ${NS}::label $top.from -text [mc "From:"]
+    ${NS}::entry $top.fromsha1 -width 40
     $top.fromsha1 insert 0 $oldid
     $top.fromsha1 conf -state readonly
     grid $top.from $top.fromsha1 -sticky w
-    entry $top.fromhead -width 60 -relief flat
+    ${NS}::entry $top.fromhead -width 60
     $top.fromhead insert 0 $oldhead
     $top.fromhead conf -state readonly
     grid x $top.fromhead -sticky w
-    label $top.to -text [mc "To:"]
-    entry $top.tosha1 -width 40 -relief flat
+    ${NS}::label $top.to -text [mc "To:"]
+    ${NS}::entry $top.tosha1 -width 40
     $top.tosha1 insert 0 $newid
     $top.tosha1 conf -state readonly
     grid $top.to $top.tosha1 -sticky w
-    entry $top.tohead -width 60 -relief flat
+    ${NS}::entry $top.tohead -width 60
     $top.tohead insert 0 $newhead
     $top.tohead conf -state readonly
     grid x $top.tohead -sticky w
-    button $top.rev -text [mc "Reverse"] -command mkpatchrev -padx 5
-    grid $top.rev x -pady 10
-    label $top.flab -text [mc "Output file:"]
-    entry $top.fname -width 60
+    ${NS}::button $top.rev -text [mc "Reverse"] -command mkpatchrev
+    grid $top.rev x -pady 10 -padx 5
+    ${NS}::label $top.flab -text [mc "Output file:"]
+    ${NS}::entry $top.fname -width 60
     $top.fname insert 0 [file normalize "patch$patchnum.patch"]
     incr patchnum
     grid $top.flab $top.fname -sticky w
-    frame $top.buts
-    button $top.buts.gen -text [mc "Generate"] -command mkpatchgo
-    button $top.buts.can -text [mc "Cancel"] -command mkpatchcan
+    ${NS}::frame $top.buts
+    ${NS}::button $top.buts.gen -text [mc "Generate"] -command mkpatchgo
+    ${NS}::button $top.buts.can -text [mc "Cancel"] -command mkpatchcan
     bind $top <Key-Return> mkpatchgo
     bind $top <Key-Escape> mkpatchcan
     grid $top.buts.gen $top.buts.can
@@ -8487,30 +9176,35 @@ proc mkpatchcan {} {
 }
 
 proc mktag {} {
-    global rowmenuid mktagtop commitinfo
+    global rowmenuid mktagtop commitinfo NS
 
     set top .maketag
     set mktagtop $top
     catch {destroy $top}
-    toplevel $top
+    ttk_toplevel $top
     make_transient $top .
-    label $top.title -text [mc "Create tag"]
+    ${NS}::label $top.title -text [mc "Create tag"]
     grid $top.title - -pady 10
-    label $top.id -text [mc "ID:"]
-    entry $top.sha1 -width 40 -relief flat
+    ${NS}::label $top.id -text [mc "ID:"]
+    ${NS}::entry $top.sha1 -width 40
     $top.sha1 insert 0 $rowmenuid
     $top.sha1 conf -state readonly
     grid $top.id $top.sha1 -sticky w
-    entry $top.head -width 60 -relief flat
+    ${NS}::entry $top.head -width 60
     $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
     $top.head conf -state readonly
     grid x $top.head -sticky w
-    label $top.tlab -text [mc "Tag name:"]
-    entry $top.tag -width 60
+    ${NS}::label $top.tlab -text [mc "Tag name:"]
+    ${NS}::entry $top.tag -width 60
     grid $top.tlab $top.tag -sticky w
-    frame $top.buts
-    button $top.buts.gen -text [mc "Create"] -command mktaggo
-    button $top.buts.can -text [mc "Cancel"] -command mktagcan
+    ${NS}::label $top.op -text [mc "Tag message is optional"]
+    grid $top.op -columnspan 2 -sticky we
+    ${NS}::label $top.mlab -text [mc "Tag message:"]
+    ${NS}::entry $top.msg -width 60
+    grid $top.mlab $top.msg -sticky w
+    ${NS}::frame $top.buts
+    ${NS}::button $top.buts.gen -text [mc "Create"] -command mktaggo
+    ${NS}::button $top.buts.can -text [mc "Cancel"] -command mktagcan
     bind $top <Key-Return> mktaggo
     bind $top <Key-Escape> mktagcan
     grid $top.buts.gen $top.buts.can
@@ -8525,6 +9219,7 @@ proc domktag {} {
 
     set id [$mktagtop.sha1 get]
     set tag [$mktagtop.tag get]
+    set msg [$mktagtop.msg get]
     if {$tag == {}} {
        error_popup [mc "No tag name specified"] $mktagtop
        return 0
@@ -8534,7 +9229,11 @@ proc domktag {} {
        return 0
     }
     if {[catch {
-       exec git tag $tag $id
+       if {$msg != {}} {
+           exec git tag -a -m $msg $tag $id
+       } else {
+           exec git tag $tag $id
+       }
     } err]} {
        error_popup "[mc "Error creating tag:"] $err" $mktagtop
        return 0
@@ -8552,12 +9251,13 @@ proc domktag {} {
 proc redrawtags {id} {
     global canv linehtag idpos currentid curview cmitlisted markedid
     global canvxmax iddrawn circleitem mainheadid circlecolors
+    global mainheadcirclecolor
 
     if {![commitinview $id $curview]} return
     if {![info exists iddrawn($id)]} return
     set row [rowofcommit $id]
     if {$id eq $mainheadid} {
-       set ofill yellow
+       set ofill $mainheadcirclecolor
     } else {
        set ofill [lindex $circlecolors $cmitlisted($curview,$id)]
     }
@@ -8593,34 +9293,34 @@ proc mktaggo {} {
 }
 
 proc writecommit {} {
-    global rowmenuid wrcomtop commitinfo wrcomcmd
+    global rowmenuid wrcomtop commitinfo wrcomcmd NS
 
     set top .writecommit
     set wrcomtop $top
     catch {destroy $top}
-    toplevel $top
+    ttk_toplevel $top
     make_transient $top .
-    label $top.title -text [mc "Write commit to file"]
+    ${NS}::label $top.title -text [mc "Write commit to file"]
     grid $top.title - -pady 10
-    label $top.id -text [mc "ID:"]
-    entry $top.sha1 -width 40 -relief flat
+    ${NS}::label $top.id -text [mc "ID:"]
+    ${NS}::entry $top.sha1 -width 40
     $top.sha1 insert 0 $rowmenuid
     $top.sha1 conf -state readonly
     grid $top.id $top.sha1 -sticky w
-    entry $top.head -width 60 -relief flat
+    ${NS}::entry $top.head -width 60
     $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
     $top.head conf -state readonly
     grid x $top.head -sticky w
-    label $top.clab -text [mc "Command:"]
-    entry $top.cmd -width 60 -textvariable wrcomcmd
+    ${NS}::label $top.clab -text [mc "Command:"]
+    ${NS}::entry $top.cmd -width 60 -textvariable wrcomcmd
     grid $top.clab $top.cmd -sticky w -pady 10
-    label $top.flab -text [mc "Output file:"]
-    entry $top.fname -width 60
+    ${NS}::label $top.flab -text [mc "Output file:"]
+    ${NS}::entry $top.fname -width 60
     $top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6]"]
     grid $top.flab $top.fname -sticky w
-    frame $top.buts
-    button $top.buts.gen -text [mc "Write"] -command wrcomgo
-    button $top.buts.can -text [mc "Cancel"] -command wrcomcan
+    ${NS}::frame $top.buts
+    ${NS}::button $top.buts.gen -text [mc "Write"] -command wrcomgo
+    ${NS}::button $top.buts.can -text [mc "Cancel"] -command wrcomcan
     bind $top <Key-Return> wrcomgo
     bind $top <Key-Escape> wrcomcan
     grid $top.buts.gen $top.buts.can
@@ -8651,25 +9351,25 @@ proc wrcomcan {} {
 }
 
 proc mkbranch {} {
-    global rowmenuid mkbrtop
+    global rowmenuid mkbrtop NS
 
     set top .makebranch
     catch {destroy $top}
-    toplevel $top
+    ttk_toplevel $top
     make_transient $top .
-    label $top.title -text [mc "Create new branch"]
+    ${NS}::label $top.title -text [mc "Create new branch"]
     grid $top.title - -pady 10
-    label $top.id -text [mc "ID:"]
-    entry $top.sha1 -width 40 -relief flat
+    ${NS}::label $top.id -text [mc "ID:"]
+    ${NS}::entry $top.sha1 -width 40
     $top.sha1 insert 0 $rowmenuid
     $top.sha1 conf -state readonly
     grid $top.id $top.sha1 -sticky w
-    label $top.nlab -text [mc "Name:"]
-    entry $top.name -width 40
+    ${NS}::label $top.nlab -text [mc "Name:"]
+    ${NS}::entry $top.name -width 40
     grid $top.nlab $top.name -sticky w
-    frame $top.buts
-    button $top.buts.go -text [mc "Create"] -command [list mkbrgo $top]
-    button $top.buts.can -text [mc "Cancel"] -command "catch {destroy $top}"
+    ${NS}::frame $top.buts
+    ${NS}::button $top.buts.go -text [mc "Create"] -command [list mkbrgo $top]
+    ${NS}::button $top.buts.can -text [mc "Cancel"] -command "catch {destroy $top}"
     bind $top <Key-Return> [list mkbrgo $top]
     bind $top <Key-Escape> "catch {destroy $top}"
     grid $top.buts.go $top.buts.can
@@ -8754,6 +9454,7 @@ proc exec_citool {tool_args {baseid {}}} {
 proc cherrypick {} {
     global rowmenuid curview
     global mainhead mainheadid
+    global gitdir
 
     set oldhead [exec git rev-parse HEAD]
     set dheads [descheads $rowmenuid]
@@ -8776,13 +9477,13 @@ proc cherrypick {} {
                        to file '%s'.\nPlease commit, reset or stash\
                        your changes and try again." $fname]
        } elseif {[regexp -line \
-                      {^(CONFLICT \(.*\):|Automatic cherry-pick failed)} \
+                      {^(CONFLICT \(.*\):|Automatic cherry-pick failed|error: could not apply)} \
                       $err]} {
            if {[confirm_popup [mc "Cherry-pick failed because of merge\
                        conflict.\nDo you wish to run git citool to\
                        resolve it?"]]} {
                # Force citool to read MERGE_MSG
-               file delete [file join [gitdir] "GITGUI_MSG"]
+               file delete [file join $gitdir "GITGUI_MSG"]
                exec_citool {} $rowmenuid
            }
        } else {
@@ -8813,35 +9514,93 @@ proc cherrypick {} {
     notbusy cherrypick
 }
 
+proc revert {} {
+    global rowmenuid curview
+    global mainhead mainheadid
+    global gitdir
+
+    set oldhead [exec git rev-parse HEAD]
+    set dheads [descheads $rowmenuid]
+    if { $dheads eq {} || [lsearch -exact $dheads $oldhead] == -1 } {
+       set ok [confirm_popup [mc "Commit %s is not\
+           included in branch %s -- really revert it?" \
+                      [string range $rowmenuid 0 7] $mainhead]]
+       if {!$ok} return
+    }
+    nowbusy revert [mc "Reverting"]
+    update
+
+    if [catch {exec git revert --no-edit $rowmenuid} err] {
+        notbusy revert
+        if [regexp {files would be overwritten by merge:(\n(( |\t)+[^\n]+\n)+)}\
+                $err match files] {
+            regsub {\n( |\t)+} $files "\n" files
+            error_popup [mc "Revert failed because of local changes to\
+                the following files:%s Please commit, reset or stash \
+                your changes and try again." $files]
+        } elseif [regexp {error: could not revert} $err] {
+            if [confirm_popup [mc "Revert failed because of merge conflict.\n\
+                Do you wish to run git citool to resolve it?"]] {
+                # Force citool to read MERGE_MSG
+                file delete [file join $gitdir "GITGUI_MSG"]
+                exec_citool {} $rowmenuid
+            }
+        } else { error_popup $err }
+        run updatecommits
+        return
+    }
+
+    set newhead [exec git rev-parse HEAD]
+    if { $newhead eq $oldhead } {
+        notbusy revert
+        error_popup [mc "No changes committed"]
+        return
+    }
+
+    addnewchild $newhead $oldhead
+
+    if [commitinview $oldhead $curview] {
+        # XXX this isn't right if we have a path limit...
+        insertrow $newhead $oldhead $curview
+        if {$mainhead ne {}} {
+            movehead $newhead $mainhead
+            movedhead $newhead $mainhead
+        }
+        set mainheadid $newhead
+        redrawtags $oldhead
+        redrawtags $newhead
+        selbyid $newhead
+    }
+
+    notbusy revert
+}
+
 proc resethead {} {
-    global mainhead rowmenuid confirm_ok resettype
+    global mainhead rowmenuid confirm_ok resettype NS
 
     set confirm_ok 0
     set w ".confirmreset"
-    toplevel $w
+    ttk_toplevel $w
     make_transient $w .
     wm title $w [mc "Confirm reset"]
-    message $w.m -text \
-       [mc "Reset branch %s to %s?" $mainhead [string range $rowmenuid 0 7]] \
-       -justify center -aspect 1000
+    ${NS}::label $w.m -text \
+       [mc "Reset branch %s to %s?" $mainhead [string range $rowmenuid 0 7]]
     pack $w.m -side top -fill x -padx 20 -pady 20
-    frame $w.f -relief sunken -border 2
-    message $w.f.rt -text [mc "Reset type:"] -aspect 1000
-    grid $w.f.rt -sticky w
+    ${NS}::labelframe $w.f -text [mc "Reset type:"]
     set resettype mixed
-    radiobutton $w.f.soft -value soft -variable resettype -justify left \
+    ${NS}::radiobutton $w.f.soft -value soft -variable resettype \
        -text [mc "Soft: Leave working tree and index untouched"]
     grid $w.f.soft -sticky w
-    radiobutton $w.f.mixed -value mixed -variable resettype -justify left \
+    ${NS}::radiobutton $w.f.mixed -value mixed -variable resettype \
        -text [mc "Mixed: Leave working tree untouched, reset index"]
     grid $w.f.mixed -sticky w
-    radiobutton $w.f.hard -value hard -variable resettype -justify left \
+    ${NS}::radiobutton $w.f.hard -value hard -variable resettype \
        -text [mc "Hard: Reset working tree and index\n(discard ALL local changes)"]
     grid $w.f.hard -sticky w
-    pack $w.f -side top -fill x
-    button $w.ok -text [mc OK] -command "set confirm_ok 1; destroy $w"
+    pack $w.f -side top -fill x -padx 4
+    ${NS}::button $w.ok -text [mc OK] -command "set confirm_ok 1; destroy $w"
     pack $w.ok -side left -fill x -padx 20 -pady 20
-    button $w.cancel -text [mc Cancel] -command "destroy $w"
+    ${NS}::button $w.cancel -text [mc Cancel] -command "destroy $w"
     bind $w <Key-Escape> [list destroy $w]
     pack $w.cancel -side right -fill x -padx 20 -pady 20
     bind $w <Visibility> "grab $w; focus $w"
@@ -8897,6 +9656,9 @@ proc headmenu {x y id head} {
     set headmenuid $id
     set headmenuhead $head
     set state normal
+    if {[string match "remotes/*" $head]} {
+       set state disabled
+    }
     if {$head eq $mainhead} {
        set state disabled
     }
@@ -8989,7 +9751,7 @@ proc rmbranch {} {
 
 # Display a list of tags and heads
 proc showrefs {} {
-    global showrefstop bgcolor fgcolor selectbgcolor
+    global showrefstop bgcolor fgcolor selectbgcolor NS
     global bglist fglist reflistfilter reflist maincursor
 
     set top .showrefs
@@ -8999,7 +9761,7 @@ proc showrefs {} {
        refill_reflist
        return
     }
-    toplevel $top
+    ttk_toplevel $top
     wm title $top [mc "Tags and heads: %s" [file tail [pwd]]]
     make_transient $top .
     text $top.list -background $bgcolor -foreground $fgcolor \
@@ -9010,19 +9772,19 @@ proc showrefs {} {
     $top.list tag configure highlight -background $selectbgcolor
     lappend bglist $top.list
     lappend fglist $top.list
-    scrollbar $top.ysb -command "$top.list yview" -orient vertical
-    scrollbar $top.xsb -command "$top.list xview" -orient horizontal
+    ${NS}::scrollbar $top.ysb -command "$top.list yview" -orient vertical
+    ${NS}::scrollbar $top.xsb -command "$top.list xview" -orient horizontal
     grid $top.list $top.ysb -sticky nsew
     grid $top.xsb x -sticky ew
-    frame $top.f
-    label $top.f.l -text "[mc "Filter"]: "
-    entry $top.f.e -width 20 -textvariable reflistfilter
+    ${NS}::frame $top.f
+    ${NS}::label $top.f.l -text "[mc "Filter"]: "
+    ${NS}::entry $top.f.e -width 20 -textvariable reflistfilter
     set reflistfilter "*"
     trace add variable reflistfilter write reflistfilter_change
     pack $top.f.e -side right -fill x -expand 1
     pack $top.f.l -side left
     grid $top.f - -sticky ew -pady 2
-    button $top.close -command [list destroy $top] -text [mc "Close"]
+    ${NS}::button $top.close -command [list destroy $top] -text [mc "Close"]
     bind $top <Key-Escape> [list destroy $top]
     grid $top.close -
     grid columnconfigure $top 0 -weight 1
@@ -9148,6 +9910,7 @@ proc refill_reflist {} {
 proc getallcommits {} {
     global allcommits nextarc seeds allccache allcwait cachedarcs allcupdate
     global idheads idtags idotherrefs allparents tagobjid
+    global gitdir
 
     if {![info exists allcommits]} {
        set nextarc 0
@@ -9155,7 +9918,7 @@ proc getallcommits {} {
        set seeds {}
        set allcwait 0
        set cachedarcs 0
-       set allccache [file join [gitdir] "gitk.cache"]
+       set allccache [file join $gitdir "gitk.cache"]
        if {![catch {
            set f [open $allccache r]
            set allcwait 1
@@ -9220,7 +9983,7 @@ proc getallclines {fd} {
     global allparents allchildren idtags idheads nextarc
     global arcnos arcids arctags arcout arcend arcstart archeads growing
     global seeds allcommits cachedarcs allcupdate
-    
+
     set nid 0
     while {[incr nid] <= 1000 && [gets $fd line] >= 0} {
        set id [lindex $line 0]
@@ -10125,13 +10888,13 @@ proc anctags {id} {
 # including id itself if it has a head.
 proc descheads {id} {
     global arcnos arcstart arcids archeads idheads cached_dheads
-    global allparents
+    global allparents arcout
 
     if {![info exists allparents($id)]} {
        return {}
     }
     set aret {}
-    if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
+    if {![info exists arcout($id)]} {
        # part-way along an arc; check it first
        set a [lindex $arcnos($id) 0]
        if {$archeads($a) ne {}} {
@@ -10215,7 +10978,7 @@ proc movedhead {hid head} {
 }
 
 proc changedrefs {} {
-    global cached_dheads cached_dtags cached_atags
+    global cached_dheads cached_dtags cached_atags cached_tagcontent
     global arctags archeads arcnos arcout idheads idtags
 
     foreach id [concat [array names idheads] [array names idtags]] {
@@ -10227,6 +10990,7 @@ proc changedrefs {} {
            }
        }
     }
+    catch {unset cached_tagcontent}
     catch {unset cached_dtags}
     catch {unset cached_atags}
     catch {unset cached_dheads}
@@ -10278,27 +11042,56 @@ proc listrefs {id} {
     return [list $x $y $z]
 }
 
+proc add_tag_ctext {tag} {
+    global ctext cached_tagcontent tagids
+
+    if {![info exists cached_tagcontent($tag)]} {
+       catch {
+           set cached_tagcontent($tag) [exec git cat-file -p $tag]
+       }
+    }
+    $ctext insert end "[mc "Tag"]: $tag\n" bold
+    if {[info exists cached_tagcontent($tag)]} {
+       set text $cached_tagcontent($tag)
+    } else {
+       set text "[mc "Id"]:  $tagids($tag)"
+    }
+    appendwithlinks $text {}
+}
+
 proc showtag {tag isnew} {
-    global ctext tagcontents tagids linknum tagobjid
+    global ctext cached_tagcontent tagids linknum tagobjid
 
     if {$isnew} {
-       addtohistory [list showtag $tag 0]
+       addtohistory [list showtag $tag 0] savectextpos
     }
     $ctext conf -state normal
     clear_ctext
     settabs 0
     set linknum 0
-    if {![info exists tagcontents($tag)]} {
-       catch {
-           set tagcontents($tag) [exec git cat-file tag $tagobjid($tag)]
-       }
+    add_tag_ctext $tag
+    maybe_scroll_ctext 1
+    $ctext conf -state disabled
+    init_flist {}
+}
+
+proc showtags {id isnew} {
+    global idtags ctext linknum
+
+    if {$isnew} {
+       addtohistory [list showtags $id 0] savectextpos
     }
-    if {[info exists tagcontents($tag)]} {
-       set text $tagcontents($tag)
-    } else {
-       set text "[mc "Tag"]: $tag\n[mc "Id"]:  $tagids($tag)"
+    $ctext conf -state normal
+    clear_ctext
+    settabs 0
+    set linknum 0
+    set sep {}
+    foreach tag $idtags($id) {
+       $ctext insert end $sep
+       add_tag_ctext $tag
+       set sep "\n\n"
     }
-    appendwithlinks $text {}
+    maybe_scroll_ctext 1
     $ctext conf -state disabled
     init_flist {}
 }
@@ -10317,19 +11110,19 @@ proc doquit {} {
 }
 
 proc mkfontdisp {font top which} {
-    global fontattr fontpref $font
+    global fontattr fontpref $font NS use_ttk
 
     set fontpref($font) [set $font]
-    button $top.${font}but -text $which -font optionfont \
+    ${NS}::button $top.${font}but -text $which \
        -command [list choosefont $font $which]
-    label $top.$font -relief flat -font $font \
+    ${NS}::label $top.$font -relief flat -font $font \
        -text $fontattr($font,family) -justify left
     grid x $top.${font}but $top.$font -sticky w
 }
 
 proc choosefont {font which} {
     global fontparam fontlist fonttop fontattr
-    global prefstop
+    global prefstop NS
 
     set fontparam(which) $which
     set fontparam(font) $font
@@ -10342,21 +11135,21 @@ proc choosefont {font which} {
     if {![winfo exists $top]} {
        font create sample
        eval font config sample [font actual $font]
-       toplevel $top
+       ttk_toplevel $top
        make_transient $top $prefstop
        wm title $top [mc "Gitk font chooser"]
-       label $top.l -textvariable fontparam(which)
+       ${NS}::label $top.l -textvariable fontparam(which)
        pack $top.l -side top
        set fontlist [lsort [font families]]
-       frame $top.f
+       ${NS}::frame $top.f
        listbox $top.f.fam -listvariable fontlist \
            -yscrollcommand [list $top.f.sb set]
        bind $top.f.fam <<ListboxSelect>> selfontfam
-       scrollbar $top.f.sb -command [list $top.f.fam yview]
+       ${NS}::scrollbar $top.f.sb -command [list $top.f.fam yview]
        pack $top.f.sb -side right -fill y
        pack $top.f.fam -side left -fill both -expand 1
        pack $top.f -side top -fill both -expand 1
-       frame $top.g
+       ${NS}::frame $top.g
        spinbox $top.g.size -from 4 -to 40 -width 4 \
            -textvariable fontparam(size) \
            -validatecommand {string is integer -strict %s}
@@ -10374,9 +11167,9 @@ proc choosefont {font which} {
            -fill black -tags text
        bind $top.c <Configure> [list centertext $top.c]
        pack $top.c -side top -fill x
-       frame $top.buts
-       button $top.buts.ok -text [mc "OK"] -command fontok -default active
-       button $top.buts.can -text [mc "Cancel"] -command fontcan -default normal
+       ${NS}::frame $top.buts
+       ${NS}::button $top.buts.ok -text [mc "OK"] -command fontok -default active
+       ${NS}::button $top.buts.can -text [mc "Cancel"] -command fontcan -default normal
        bind $top <Key-Return> fontok
        bind $top <Key-Escape> fontcan
        grid $top.buts.ok $top.buts.can
@@ -10410,9 +11203,9 @@ proc fontok {} {
     if {$fontparam(slant) eq "italic"} {
        lappend fontpref($f) "italic"
     }
-    set w $prefstop.$f
+    set w $prefstop.notebook.fonts.$f
     $w conf -text $fontparam(family) -font $fontpref($f)
-       
+
     fontcan
 }
 
@@ -10427,6 +11220,28 @@ proc fontcan {} {
     }
 }
 
+if {[package vsatisfies [package provide Tk] 8.6]} {
+    # In Tk 8.6 we have a native font chooser dialog. Overwrite the above
+    # function to make use of it.
+    proc choosefont {font which} {
+       tk fontchooser configure -title $which -font $font \
+           -command [list on_choosefont $font $which]
+       tk fontchooser show
+    }
+    proc on_choosefont {font which newfont} {
+       global fontparam
+       puts stderr "$font $newfont"
+       array set f [font actual $newfont]
+       set fontparam(which) $which
+       set fontparam(font) $font
+       set fontparam(family) $f(-family)
+       set fontparam(size) $f(-size)
+       set fontparam(weight) $f(-weight)
+       set fontparam(slant) $f(-slant)
+       fontok
+    }
+}
+
 proc selfontfam {} {
     global fonttop fontparam
 
@@ -10442,12 +11257,148 @@ proc chg_fontparam {v sub op} {
     font config sample -$sub $fontparam($sub)
 }
 
+# Create a property sheet tab page
+proc create_prefs_page {w} {
+    global NS
+    set parent [join [lrange [split $w .] 0 end-1] .]
+    if {[winfo class $parent] eq "TNotebook"} {
+       ${NS}::frame $w
+    } else {
+       ${NS}::labelframe $w
+    }
+}
+
+proc prefspage_general {notebook} {
+    global NS maxwidth maxgraphpct showneartags showlocalchanges
+    global tabstop limitdiffs autoselect autosellen extdifftool perfile_attrs
+    global hideremotes want_ttk have_ttk maxrefs
+
+    set page [create_prefs_page $notebook.general]
+
+    ${NS}::label $page.ldisp -text [mc "Commit list display options"]
+    grid $page.ldisp - -sticky w -pady 10
+    ${NS}::label $page.spacer -text " "
+    ${NS}::label $page.maxwidthl -text [mc "Maximum graph width (lines)"]
+    spinbox $page.maxwidth -from 0 -to 100 -width 4 -textvariable maxwidth
+    grid $page.spacer $page.maxwidthl $page.maxwidth -sticky w
+    ${NS}::label $page.maxpctl -text [mc "Maximum graph width (% of pane)"]
+    spinbox $page.maxpct -from 1 -to 100 -width 4 -textvariable maxgraphpct
+    grid x $page.maxpctl $page.maxpct -sticky w
+    ${NS}::checkbutton $page.showlocal -text [mc "Show local changes"] \
+       -variable showlocalchanges
+    grid x $page.showlocal -sticky w
+    ${NS}::checkbutton $page.autoselect -text [mc "Auto-select SHA1 (length)"] \
+       -variable autoselect
+    spinbox $page.autosellen -from 1 -to 40 -width 4 -textvariable autosellen
+    grid x $page.autoselect $page.autosellen -sticky w
+    ${NS}::checkbutton $page.hideremotes -text [mc "Hide remote refs"] \
+       -variable hideremotes
+    grid x $page.hideremotes -sticky w
+
+    ${NS}::label $page.ddisp -text [mc "Diff display options"]
+    grid $page.ddisp - -sticky w -pady 10
+    ${NS}::label $page.tabstopl -text [mc "Tab spacing"]
+    spinbox $page.tabstop -from 1 -to 20 -width 4 -textvariable tabstop
+    grid x $page.tabstopl $page.tabstop -sticky w
+    ${NS}::checkbutton $page.ntag -text [mc "Display nearby tags/heads"] \
+       -variable showneartags
+    grid x $page.ntag -sticky w
+    ${NS}::label $page.maxrefsl -text [mc "Maximum # tags/heads to show"]
+    spinbox $page.maxrefs -from 1 -to 1000 -width 4 -textvariable maxrefs
+    grid x $page.maxrefsl $page.maxrefs -sticky w
+    ${NS}::checkbutton $page.ldiff -text [mc "Limit diffs to listed paths"] \
+       -variable limitdiffs
+    grid x $page.ldiff -sticky w
+    ${NS}::checkbutton $page.lattr -text [mc "Support per-file encodings"] \
+       -variable perfile_attrs
+    grid x $page.lattr -sticky w
+
+    ${NS}::entry $page.extdifft -textvariable extdifftool
+    ${NS}::frame $page.extdifff
+    ${NS}::label $page.extdifff.l -text [mc "External diff tool" ]
+    ${NS}::button $page.extdifff.b -text [mc "Choose..."] -command choose_extdiff
+    pack $page.extdifff.l $page.extdifff.b -side left
+    pack configure $page.extdifff.l -padx 10
+    grid x $page.extdifff $page.extdifft -sticky ew
+
+    ${NS}::label $page.lgen -text [mc "General options"]
+    grid $page.lgen - -sticky w -pady 10
+    ${NS}::checkbutton $page.want_ttk -variable want_ttk \
+       -text [mc "Use themed widgets"]
+    if {$have_ttk} {
+       ${NS}::label $page.ttk_note -text [mc "(change requires restart)"]
+    } else {
+       ${NS}::label $page.ttk_note -text [mc "(currently unavailable)"]
+    }
+    grid x $page.want_ttk $page.ttk_note -sticky w
+    return $page
+}
+
+proc prefspage_colors {notebook} {
+    global NS uicolor bgcolor fgcolor ctext diffcolors selectbgcolor markbgcolor
+
+    set page [create_prefs_page $notebook.colors]
+
+    ${NS}::label $page.cdisp -text [mc "Colors: press to choose"]
+    grid $page.cdisp - -sticky w -pady 10
+    label $page.ui -padx 40 -relief sunk -background $uicolor
+    ${NS}::button $page.uibut -text [mc "Interface"] \
+       -command [list choosecolor uicolor {} $page.ui [mc "interface"] setui]
+    grid x $page.uibut $page.ui -sticky w
+    label $page.bg -padx 40 -relief sunk -background $bgcolor
+    ${NS}::button $page.bgbut -text [mc "Background"] \
+       -command [list choosecolor bgcolor {} $page.bg [mc "background"] setbg]
+    grid x $page.bgbut $page.bg -sticky w
+    label $page.fg -padx 40 -relief sunk -background $fgcolor
+    ${NS}::button $page.fgbut -text [mc "Foreground"] \
+       -command [list choosecolor fgcolor {} $page.fg [mc "foreground"] setfg]
+    grid x $page.fgbut $page.fg -sticky w
+    label $page.diffold -padx 40 -relief sunk -background [lindex $diffcolors 0]
+    ${NS}::button $page.diffoldbut -text [mc "Diff: old lines"] \
+       -command [list choosecolor diffcolors 0 $page.diffold [mc "diff old lines"] \
+                     [list $ctext tag conf d0 -foreground]]
+    grid x $page.diffoldbut $page.diffold -sticky w
+    label $page.diffnew -padx 40 -relief sunk -background [lindex $diffcolors 1]
+    ${NS}::button $page.diffnewbut -text [mc "Diff: new lines"] \
+       -command [list choosecolor diffcolors 1 $page.diffnew [mc "diff new lines"] \
+                     [list $ctext tag conf dresult -foreground]]
+    grid x $page.diffnewbut $page.diffnew -sticky w
+    label $page.hunksep -padx 40 -relief sunk -background [lindex $diffcolors 2]
+    ${NS}::button $page.hunksepbut -text [mc "Diff: hunk header"] \
+       -command [list choosecolor diffcolors 2 $page.hunksep \
+                     [mc "diff hunk header"] \
+                     [list $ctext tag conf hunksep -foreground]]
+    grid x $page.hunksepbut $page.hunksep -sticky w
+    label $page.markbgsep -padx 40 -relief sunk -background $markbgcolor
+    ${NS}::button $page.markbgbut -text [mc "Marked line bg"] \
+       -command [list choosecolor markbgcolor {} $page.markbgsep \
+                     [mc "marked line background"] \
+                     [list $ctext tag conf omark -background]]
+    grid x $page.markbgbut $page.markbgsep -sticky w
+    label $page.selbgsep -padx 40 -relief sunk -background $selectbgcolor
+    ${NS}::button $page.selbgbut -text [mc "Select bg"] \
+       -command [list choosecolor selectbgcolor {} $page.selbgsep [mc "background"] setselbg]
+    grid x $page.selbgbut $page.selbgsep -sticky w
+    return $page
+}
+
+proc prefspage_fonts {notebook} {
+    global NS
+    set page [create_prefs_page $notebook.fonts]
+    ${NS}::label $page.cfont -text [mc "Fonts: press to choose"]
+    grid $page.cfont - -sticky w -pady 10
+    mkfontdisp mainfont $page [mc "Main font"]
+    mkfontdisp textfont $page [mc "Diff display font"]
+    mkfontdisp uifont $page [mc "User interface font"]
+    return $page
+}
+
 proc doprefs {} {
-    global maxwidth maxgraphpct
+    global maxwidth maxgraphpct use_ttk NS
     global oldprefs prefstop showneartags showlocalchanges
     global uicolor bgcolor fgcolor ctext diffcolors selectbgcolor markbgcolor
-    global tabstop limitdiffs autoselect extdifftool perfile_attrs
-    global hideremotes
+    global tabstop limitdiffs autoselect autosellen extdifftool perfile_attrs
+    global hideremotes want_ttk have_ttk
 
     set top .gitkprefs
     set prefstop $top
@@ -10456,114 +11407,56 @@ proc doprefs {} {
        return
     }
     foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
-                  limitdiffs tabstop perfile_attrs hideremotes} {
+                  limitdiffs tabstop perfile_attrs hideremotes want_ttk} {
        set oldprefs($v) [set $v]
     }
-    toplevel $top
+    ttk_toplevel $top
     wm title $top [mc "Gitk preferences"]
     make_transient $top .
-    label $top.ldisp -text [mc "Commit list display options"]
-    grid $top.ldisp - -sticky w -pady 10
-    label $top.spacer -text " "
-    label $top.maxwidthl -text [mc "Maximum graph width (lines)"] \
-       -font optionfont
-    spinbox $top.maxwidth -from 0 -to 100 -width 4 -textvariable maxwidth
-    grid $top.spacer $top.maxwidthl $top.maxwidth -sticky w
-    label $top.maxpctl -text [mc "Maximum graph width (% of pane)"] \
-       -font optionfont
-    spinbox $top.maxpct -from 1 -to 100 -width 4 -textvariable maxgraphpct
-    grid x $top.maxpctl $top.maxpct -sticky w
-    checkbutton $top.showlocal -text [mc "Show local changes"] \
-       -font optionfont -variable showlocalchanges
-    grid x $top.showlocal -sticky w
-    checkbutton $top.autoselect -text [mc "Auto-select SHA1"] \
-       -font optionfont -variable autoselect
-    grid x $top.autoselect -sticky w
-
-    label $top.ddisp -text [mc "Diff display options"]
-    grid $top.ddisp - -sticky w -pady 10
-    label $top.tabstopl -text [mc "Tab spacing"] -font optionfont
-    spinbox $top.tabstop -from 1 -to 20 -width 4 -textvariable tabstop
-    grid x $top.tabstopl $top.tabstop -sticky w
-    checkbutton $top.ntag -text [mc "Display nearby tags"] \
-       -font optionfont -variable showneartags
-    grid x $top.ntag -sticky w
-    checkbutton $top.hideremotes -text [mc "Hide remote refs"] \
-       -font optionfont -variable hideremotes
-    grid x $top.hideremotes -sticky w
-    checkbutton $top.ldiff -text [mc "Limit diffs to listed paths"] \
-       -font optionfont -variable limitdiffs
-    grid x $top.ldiff -sticky w
-    checkbutton $top.lattr -text [mc "Support per-file encodings"] \
-       -font optionfont -variable perfile_attrs
-    grid x $top.lattr -sticky w
-
-    entry $top.extdifft -textvariable extdifftool
-    frame $top.extdifff
-    label $top.extdifff.l -text [mc "External diff tool" ] -font optionfont \
-       -padx 10
-    button $top.extdifff.b -text [mc "Choose..."] -font optionfont \
-       -command choose_extdiff
-    pack $top.extdifff.l $top.extdifff.b -side left
-    grid x $top.extdifff $top.extdifft -sticky w
-
-    label $top.cdisp -text [mc "Colors: press to choose"]
-    grid $top.cdisp - -sticky w -pady 10
-    label $top.ui -padx 40 -relief sunk -background $uicolor
-    button $top.uibut -text [mc "Interface"] -font optionfont \
-       -command [list choosecolor uicolor {} $top.ui [mc "interface"] setui]
-    grid x $top.uibut $top.ui -sticky w
-    label $top.bg -padx 40 -relief sunk -background $bgcolor
-    button $top.bgbut -text [mc "Background"] -font optionfont \
-       -command [list choosecolor bgcolor {} $top.bg [mc "background"] setbg]
-    grid x $top.bgbut $top.bg -sticky w
-    label $top.fg -padx 40 -relief sunk -background $fgcolor
-    button $top.fgbut -text [mc "Foreground"] -font optionfont \
-       -command [list choosecolor fgcolor {} $top.fg [mc "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 [mc "Diff: old lines"] -font optionfont \
-       -command [list choosecolor diffcolors 0 $top.diffold [mc "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 [mc "Diff: new lines"] -font optionfont \
-       -command [list choosecolor diffcolors 1 $top.diffnew [mc "diff new lines"] \
-                     [list $ctext tag conf dresult -foreground]]
-    grid x $top.diffnewbut $top.diffnew -sticky w
-    label $top.hunksep -padx 40 -relief sunk -background [lindex $diffcolors 2]
-    button $top.hunksepbut -text [mc "Diff: hunk header"] -font optionfont \
-       -command [list choosecolor diffcolors 2 $top.hunksep \
-                     [mc "diff hunk header"] \
-                     [list $ctext tag conf hunksep -foreground]]
-    grid x $top.hunksepbut $top.hunksep -sticky w
-    label $top.markbgsep -padx 40 -relief sunk -background $markbgcolor
-    button $top.markbgbut -text [mc "Marked line bg"] -font optionfont \
-       -command [list choosecolor markbgcolor {} $top.markbgsep \
-                     [mc "marked line background"] \
-                     [list $ctext tag conf omark -background]]
-    grid x $top.markbgbut $top.markbgsep -sticky w
-    label $top.selbgsep -padx 40 -relief sunk -background $selectbgcolor
-    button $top.selbgbut -text [mc "Select bg"] -font optionfont \
-       -command [list choosecolor selectbgcolor {} $top.selbgsep [mc "background"] setselbg]
-    grid x $top.selbgbut $top.selbgsep -sticky w
-
-    label $top.cfont -text [mc "Fonts: press to choose"]
-    grid $top.cfont - -sticky w -pady 10
-    mkfontdisp mainfont $top [mc "Main font"]
-    mkfontdisp textfont $top [mc "Diff display font"]
-    mkfontdisp uifont $top [mc "User interface font"]
-
-    frame $top.buts
-    button $top.buts.ok -text [mc "OK"] -command prefsok -default active
-    button $top.buts.can -text [mc "Cancel"] -command prefscan -default normal
+
+    if {[set use_notebook [expr {$use_ttk && [info command ::ttk::notebook] ne ""}]]} {
+       set notebook [ttk::notebook $top.notebook]
+    } else {
+       set notebook [${NS}::frame $top.notebook -borderwidth 0 -relief flat]
+    }
+
+    lappend pages [prefspage_general $notebook] [mc "General"]
+    lappend pages [prefspage_colors $notebook] [mc "Colors"]
+    lappend pages [prefspage_fonts $notebook] [mc "Fonts"]
+    set col 0
+    foreach {page title} $pages {
+       if {$use_notebook} {
+           $notebook add $page -text $title
+       } else {
+           set btn [${NS}::button $notebook.b_[string map {. X} $page] \
+                        -text $title -command [list raise $page]]
+           $page configure -text $title
+           grid $btn -row 0 -column [incr col] -sticky w
+           grid $page -row 1 -column 0 -sticky news -columnspan 100
+       }
+    }
+
+    if {!$use_notebook} {
+       grid columnconfigure $notebook 0 -weight 1
+       grid rowconfigure $notebook 1 -weight 1
+       raise [lindex $pages 0]
+    }
+
+    grid $notebook -sticky news -padx 2 -pady 2
+    grid rowconfigure $top 0 -weight 1
+    grid columnconfigure $top 0 -weight 1
+
+    ${NS}::frame $top.buts
+    ${NS}::button $top.buts.ok -text [mc "OK"] -command prefsok -default active
+    ${NS}::button $top.buts.can -text [mc "Cancel"] -command prefscan -default normal
     bind $top <Key-Return> prefsok
     bind $top <Key-Escape> prefscan
     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 - - -pady 10 -sticky ew
-    bind $top <Visibility> "focus $top.buts.ok"
+    grid columnconfigure $top 2 -weight 1
+    bind $top <Visibility> [list focus $top.buts.ok]
 }
 
 proc choose_extdiff {} {
@@ -10596,8 +11489,19 @@ proc setselbg {c} {
     allcanvs itemconf secsel -fill $c
 }
 
+# This sets the background color and the color scheme for the whole UI.
+# For some reason, tk_setPalette chooses a nasty dark red for selectColor
+# if we don't specify one ourselves, which makes the checkbuttons and
+# radiobuttons look bad.  This chooses white for selectColor if the
+# background color is light, or black if it is dark.
 proc setui {c} {
-    tk_setPalette $c
+    if {[tk windowingsystem] eq "win32"} { return }
+    set bg [winfo rgb . $c]
+    set selc black
+    if {[lindex $bg 0] + 1.5 * [lindex $bg 1] + 0.5 * [lindex $bg 2] > 100000} {
+       set selc white
+    }
+    tk_setPalette background $c selectColor $selc
 }
 
 proc setbg {c} {
@@ -10623,7 +11527,7 @@ proc prefscan {} {
     global oldprefs prefstop
 
     foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
-                  limitdiffs tabstop perfile_attrs hideremotes} {
+                  limitdiffs tabstop perfile_attrs hideremotes want_ttk} {
        global $v
        set $v $oldprefs($v)
     }
@@ -10691,7 +11595,29 @@ proc prefsok {} {
 proc formatdate {d} {
     global datetimeformat
     if {$d ne {}} {
-       set d [clock format $d -format $datetimeformat]
+       # If $datetimeformat includes a timezone, display in the
+       # timezone of the argument.  Otherwise, display in local time.
+       if {[string match {*%[zZ]*} $datetimeformat]} {
+           if {[catch {set d [clock format [lindex $d 0] -timezone [lindex $d 1] -format $datetimeformat]}]} {
+               # Tcl < 8.5 does not support -timezone.  Emulate it by
+               # setting TZ (e.g. TZ=<-0430>+04:30).
+               global env
+               if {[info exists env(TZ)]} {
+                   set savedTZ $env(TZ)
+               }
+               set zone [lindex $d 1]
+               set sign [string map {+ - - +} [string index $zone 0]]
+               set env(TZ) <$zone>$sign[string range $zone 1 2]:[string range $zone 3 4]
+               set d [clock format [lindex $d 0] -format $datetimeformat]
+               if {[info exists savedTZ]} {
+                   set env(TZ) $savedTZ
+               } else {
+                   unset env(TZ)
+               }
+           }
+       } else {
+           set d [clock format [lindex $d 0] -format $datetimeformat]
+       }
     }
     return $d
 }
@@ -11039,6 +11965,20 @@ if {[catch {package require Tk 8.4} err]} {
     exit 1
 }
 
+# on OSX bring the current Wish process window to front
+if {[tk windowingsystem] eq "aqua"} {
+    exec osascript -e [format {
+        tell application "System Events"
+            set frontmost of processes whose unix id is %d to true
+        end tell
+    } [pid] ]
+}
+
+# Unset GIT_TRACE var if set
+if { [info exists ::env(GIT_TRACE)] } {
+    unset ::env(GIT_TRACE)
+}
+
 # defaults...
 set wrcomcmd "git diff-tree --stdin -p --pretty"
 
@@ -11070,10 +12010,20 @@ catch {
     }
 }
 
+set log_showroot true
+catch {
+    set log_showroot [exec git config --bool --get log.showroot]
+}
+
 if {[tk windowingsystem] eq "aqua"} {
     set mainfont {{Lucida Grande} 9}
     set textfont {Monaco 9}
     set uifont {{Lucida Grande} 9 bold}
+} elseif {![catch {::tk::pkgconfig get fontsystem} xft] && $xft eq "xft"} {
+    # fontconfig!
+    set mainfont {sans 9}
+    set textfont {monospace 9}
+    set uifont {sans 9 bold}
 } else {
     set mainfont {Helvetica 9}
     set textfont {Courier 9}
@@ -11098,7 +12048,9 @@ set showlocalchanges 1
 set limitdiffs 1
 set datetimeformat "%Y-%m-%d %H:%M:%S"
 set autoselect 1
+set autosellen 40
 set perfile_attrs 0
+set want_ttk 1
 
 if {[tk windowingsystem] eq "aqua"} {
     set extdifftool "opendiff"
@@ -11107,16 +12059,49 @@ if {[tk windowingsystem] eq "aqua"} {
 }
 
 set colors {green red blue magenta darkgrey brown orange}
-set uicolor grey85
-set bgcolor white
-set fgcolor black
+if {[tk windowingsystem] eq "win32"} {
+    set uicolor SystemButtonFace
+    set uifgcolor SystemButtonText
+    set uifgdisabledcolor SystemDisabledText
+    set bgcolor SystemWindow
+    set fgcolor SystemWindowText
+    set selectbgcolor SystemHighlight
+} else {
+    set uicolor grey85
+    set uifgcolor black
+    set uifgdisabledcolor "#999"
+    set bgcolor white
+    set fgcolor black
+    set selectbgcolor gray85
+}
 set diffcolors {red "#00a000" blue}
 set diffcontext 3
+set mergecolors {red blue green purple brown "#009090" magenta "#808000" "#009000" "#ff0080" cyan "#b07070" "#70b0f0" "#70f0b0" "#f0b070" "#ff70b0"}
 set ignorespace 0
-set selectbgcolor gray85
+set worddiff ""
 set markbgcolor "#e0e0ff"
 
+set headbgcolor green
+set headfgcolor black
+set headoutlinecolor black
+set remotebgcolor #ffddaa
+set tagbgcolor yellow
+set tagfgcolor black
+set tagoutlinecolor black
+set reflinecolor black
+set filesepbgcolor #aaaaaa
+set filesepfgcolor black
+set linehoverbgcolor #ffff80
+set linehoverfgcolor black
+set linehoveroutlinecolor black
+set mainheadcirclecolor yellow
+set workingfilescirclecolor red
+set indexcirclecolor green
 set circlecolors {white blue gray blue blue}
+set linkfgcolor blue
+set circleoutlinecolor $fgcolor
+set foundbgcolor yellow
+set currentsearchhitbgcolor orange
 
 # button for popping up context menus
 if {[tk windowingsystem] eq "aqua"} {
@@ -11144,9 +12129,29 @@ namespace import ::msgcat::mc
 ## And eventually load the actual message catalog
 ::msgcat::mcload $gitk_msgsdir
 
-catch {source ~/.gitk}
-
-font create optionfont -family sans-serif -size -12
+catch {
+    # follow the XDG base directory specification by default. See
+    # http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
+    if {[info exists env(XDG_CONFIG_HOME)] && $env(XDG_CONFIG_HOME) ne ""} {
+       # XDG_CONFIG_HOME environment variable is set
+       set config_file [file join $env(XDG_CONFIG_HOME) git gitk]
+       set config_file_tmp [file join $env(XDG_CONFIG_HOME) git gitk-tmp]
+    } else {
+       # default XDG_CONFIG_HOME
+       set config_file "~/.config/git/gitk"
+       set config_file_tmp "~/.config/git/gitk-tmp"
+    }
+    if {![file exists $config_file]} {
+       # for backward compatibility use the old config file if it exists
+       if {[file exists "~/.gitk"]} {
+           set config_file "~/.gitk"
+           set config_file_tmp "~/.gitk-tmp"
+       } elseif {![file exists [file dirname $config_file]]} {
+           file mkdir [file dirname $config_file]
+       }
+    }
+    source $config_file
+}
 
 parsefont mainfont $mainfont
 eval font create mainfont [fontflags mainfont]
@@ -11159,19 +12164,15 @@ eval font create textfontbold [fontflags textfont 1]
 parsefont uifont $uifont
 eval font create uifont [fontflags uifont]
 
-tk_setPalette $uicolor
+setui $uicolor
 
 setoptions
 
 # check that we can find a .git directory somewhere...
-if {[catch {set gitdir [gitdir]}]} {
+if {[catch {set gitdir [exec git rev-parse --git-dir]}]} {
     show_error {} . [mc "Cannot find a git repository here."]
     exit 1
 }
-if {![file isdirectory $gitdir]} {
-    show_error {} . [mc "Cannot find the git directory \"%s\"." $gitdir]
-    exit 1
-}
 
 set selecthead {}
 set selectheadid {}
@@ -11238,7 +12239,20 @@ set nullid2 "0000000000000000000000000000000000000001"
 set nullfile "/dev/null"
 
 set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}]
-set git_version [join [lrange [split [lindex [exec git version] end] .] 0 2] .]
+if {![info exists have_ttk]} {
+    set have_ttk [llength [info commands ::ttk::style]]
+}
+set use_ttk [expr {$have_ttk && $want_ttk}]
+set NS [expr {$use_ttk ? "ttk" : ""}]
+
+regexp {^git version ([\d.]*\d)} [exec git version] _ git_version
+
+set show_notes {}
+if {[package vcompare $git_version "1.6.6.2"] >= 0} {
+    set show_notes "--show-notes"
+}
+
+set appname "gitk"
 
 set runq {}
 set history {}
@@ -11276,7 +12290,12 @@ set stopped 0
 set stuffsaved 0
 set patchnum 0
 set lserial 0
-set isworktree [expr {[exec git rev-parse --is-inside-work-tree] == "true"}]
+set hasworktree [hasworktree]
+set cdup {}
+if {[expr {[exec git rev-parse --is-inside-work-tree] == "true"}]} {
+    set cdup [exec git rev-parse --show-cdup]
+}
+set worktree [exec git rev-parse --show-toplevel]
 setcoords
 makewindow
 catch {
@@ -11304,7 +12323,7 @@ catch {
 }
 # wait for the window to become visible
 tkwait visibility .
-wm title . "[file tail $argv0]: [file tail [pwd]]"
+wm title . "$appname: [reponame]"
 update
 readrefs
 
@@ -11342,3 +12361,9 @@ if {[tk windowingsystem] eq "win32"} {
 }
 
 getcommits {}
+
+# Local variables:
+# mode: tcl
+# indent-tabs-mode: t
+# tab-width: 8
+# End: