Code

gitk: Improve cherry-pick error handling
[git.git] / gitk
diff --git a/gitk b/gitk
index 00037976970d57888e2db09eec77d836b2215bbb..58ad47d18370b17e837174c73bbc5b76fd2da42c 100755 (executable)
--- a/gitk
+++ b/gitk
@@ -1746,22 +1746,24 @@ proc show_error {w top msg} {
     pack $w.ok -side bottom -fill x
     bind $top <Visibility> "grab $top; focus $top"
     bind $top <Key-Return> "destroy $top"
+    bind $top <Key-space>  "destroy $top"
+    bind $top <Key-Escape> "destroy $top"
     tkwait window $top
 }
 
-proc error_popup msg {
+proc error_popup {msg {owner .}} {
     set w .error
     toplevel $w
-    wm transient $w .
+    wm transient $w $owner
     show_error $w $w $msg
 }
 
-proc confirm_popup msg {
+proc confirm_popup {msg {owner .}} {
     global confirm_ok
     set confirm_ok 0
     set w .confirm
     toplevel $w
-    wm transient $w .
+    wm 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"
@@ -1769,6 +1771,9 @@ proc confirm_popup msg {
     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"
     tkwait window $w
     return $confirm_ok
 }
@@ -2296,6 +2301,7 @@ proc makewindow {} {
     global diff_menu
     set diff_menu .diffctxmenu
     makemenu $diff_menu {
+       {mc "Show origin of this line" command show_line_source}
        {mc "Run git gui blame on this line" command {external_blame_diff}}
     }
     $diff_menu configure -tearoff 0
@@ -2414,7 +2420,7 @@ proc savestuff {w} {
     global viewname viewfiles viewargs viewargscmd viewperm nextviewnum
     global cmitmode wrapcomment datetimeformat limitdiffs
     global colors bgcolor fgcolor diffcolors diffcontext selectbgcolor
-    global autoselect extdifftool perfile_attrs
+    global autoselect extdifftool perfile_attrs markbgcolor
 
     if {$stuffsaved} return
     if {![winfo viewable .]} return
@@ -2438,6 +2444,7 @@ proc savestuff {w} {
        puts $f [list set fgcolor $fgcolor]
        puts $f [list set colors $colors]
        puts $f [list set diffcolors $diffcolors]
+       puts $f [list set markbgcolor $markbgcolor]
        puts $f [list set diffcontext $diffcontext]
        puts $f [list set selectbgcolor $selectbgcolor]
        puts $f [list set extdifftool $extdifftool]
@@ -2539,6 +2546,7 @@ proc about {} {
     }
     toplevel $w
     wm title $w [mc "About gitk"]
+    wm transient $w .
     message $w.m -text [mc "
 Gitk - a commit viewer for git
 
@@ -2567,6 +2575,7 @@ proc keys {} {
     }
     toplevel $w
     wm title $w [mc "Gitk key bindings"]
+    wm transient $w .
     message $w.m -text "
 [mc "Gitk key bindings:"]
 
@@ -2609,6 +2618,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
+    bind $w <Key-Escape> [list destroy $w]
     pack $w.ok -side bottom
     bind $w <Visibility> "focus $w.ok"
     bind $w <Key-Escape> "destroy $w"
@@ -2830,9 +2840,15 @@ proc treeclick {w x y} {
 }
 
 proc setfilelist {id} {
-    global treefilelist cflist
+    global treefilelist cflist jump_to_here
 
     treeview $cflist $treefilelist($id) 0
+    if {$jump_to_here ne {}} {
+       set f [lindex $jump_to_here 0]
+       if {[lsearch -exact $treefilelist($id) $f] >= 0} {
+           showfile $f
+       }
+    }
 }
 
 image create bitmap tri-rt -background black -foreground blue -data {
@@ -3019,9 +3035,13 @@ proc pop_diff_menu {w X Y x y} {
     global diff_menu_txtpos diff_menu_line
     global diff_menu_filebase
 
-    stopfinding
     set diff_menu_txtpos [split [$w index "@$x,$y"] "."]
     set diff_menu_line [lindex $diff_menu_txtpos 0]
+    # don't pop up the menu on hunk-separator or file-separator lines
+    if {[lsearch -glob [$ctext tag names $diff_menu_line.0] "*sep"] >= 0} {
+       return
+    }
+    stopfinding
     set f [find_ctext_fileinfo $diff_menu_line]
     if {$f eq {}} return
     set flist_menu_file [lindex $f 0]
@@ -3159,45 +3179,47 @@ proc find_hunk_blamespec {base line} {
     }
 
     # Now scan the lines to determine offset within the hunk
-    set parent {}
     set max_parent [expr {[llength $base_lines]-2}]
     set dline 0
     set s_lno [lindex [split $s_lix "."] 0]
 
-    for {set i $line} {$i > $s_lno} {incr i -1} {
-       set c_line [$ctext get $i.0 "$i.0 + 1 lines"]
-       # Determine if the line is removed
-       set chunk [string range $c_line 0 $max_parent]
+    # Determine if the line is removed
+    set chunk [$ctext get $line.0 "$line.1 + $max_parent chars"]
+    if {[string match {[-+ ]*} $chunk]} {
        set removed_idx [string first "-" $chunk]
        # Choose a parent index
-       if {$parent eq {}} {
-           if {$removed_idx >= 0} {
-               set parent $removed_idx
+       if {$removed_idx >= 0} {
+           set parent $removed_idx
+       } else {
+           set unchanged_idx [string first " " $chunk]
+           if {$unchanged_idx >= 0} {
+               set parent $unchanged_idx
            } else {
-               set unchanged_idx [string first " " $chunk]
-               if {$unchanged_idx >= 0} {
-                   set parent $unchanged_idx
-               } else {
-                   # blame the current commit
-                   set parent -1
-               }
+               # blame the current commit
+               set parent -1
            }
        }
        # then count other lines that belong to it
-       if {$parent >= 0} {
-           set code [string index $c_line $parent]
-           if {$code eq "-" || ($removed_idx < 0 && $code ne "+")} {
-               incr dline
-           }
-       } else {
-           if {$removed_idx < 0} {
-               incr dline
+       for {set i $line} {[incr i -1] > $s_lno} {} {
+           set chunk [$ctext get $i.0 "$i.1 + $max_parent chars"]
+           # Determine if the line is removed
+           set removed_idx [string first "-" $chunk]
+           if {$parent >= 0} {
+               set code [string index $chunk $parent]
+               if {$code eq "-" || ($removed_idx < 0 && $code ne "+")} {
+                   incr dline
+               }
+           } else {
+               if {$removed_idx < 0} {
+                   incr dline
+               }
            }
        }
+       incr parent
+    } else {
+       set parent 0
     }
 
-    if {$parent eq {}} { set parent -1 }
-    incr parent
     incr dline [lindex $base_lines $parent]
     return [list $parent $dline]
 }
@@ -3209,7 +3231,7 @@ proc external_blame_diff {} {
 
     if {$cmitmode eq "tree"} {
        set parent_idx 0
-       set line [expr {$diff_menu_line - $diff_menu_filebase - 1}]
+       set line [expr {$diff_menu_line - $diff_menu_filebase}]
     } else {
        set hinfo [find_hunk_blamespec $diff_menu_filebase $diff_menu_line]
        if {$hinfo ne {}} {
@@ -3250,6 +3272,91 @@ 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
+
+    if {$cmitmode eq "tree"} {
+       set id $currentid
+       set line [expr {$diff_menu_line - $diff_menu_filebase}]
+    } else {
+       set h [find_hunk_blamespec $diff_menu_filebase $diff_menu_line]
+       if {$h eq {}} return
+       set pi [lindex $h 0]
+       if {$pi == 0} {
+           mark_ctext_line $diff_menu_line
+           return
+       }
+       set id [lindex $parents($curview,$currentid) [expr {$pi - 1}]]
+       set line [lindex $h 1]
+    }
+    if {[catch {
+       set f [open [list | git blame -p -L$line,+1 $id -- $flist_menu_file] r]
+    } err]} {
+       error_popup [mc "Couldn't start git blame: %s" $err]
+       return
+    }
+    fconfigure $f -blocking 0
+    set i [reg_instance $f]
+    set blamestuff($i) {}
+    set blameinst $i
+    filerun $f [list read_line_source $f $i]
+}
+
+proc stopblaming {} {
+    global blameinst
+
+    if {[info exists blameinst]} {
+       stop_instance $blameinst
+       unset blameinst
+    }
+}
+
+proc read_line_source {fd inst} {
+    global blamestuff curview commfd blameinst
+
+    while {[gets $fd line] >= 0} {
+       lappend blamestuff($inst) $line
+    }
+    if {![eof $fd]} {
+       return 1
+    }
+    unset commfd($inst)
+    unset blameinst
+    fconfigure $fd -blocking 1
+    if {[catch {close $fd} err]} {
+       error_popup [mc "Error running git blame: %s" $err]
+       return 0
+    }
+
+    set fname {}
+    set line [split [lindex $blamestuff($inst) 0] " "]
+    set id [lindex $line 0]
+    set lnum [lindex $line 1]
+    if {[string length $id] == 40 && [string is xdigit $id] &&
+       [string is digit -strict $lnum]} {
+       # look for "filename" line
+       foreach l $blamestuff($inst) {
+           if {[string match "filename *" $l]} {
+               set fname [string range $l 9 end]
+               break
+           }
+       }
+    }
+    if {$fname ne {}} {
+       # all looks good, select it
+       if {[commitinview $id $curview]} {
+           selectline [rowofcommit $id] 1 [list $fname $lnum]
+       } else {
+           error_popup [mc "That line comes from commit %s, \
+                            which is not in this view" [shortids $id]]
+       }
+    } else {
+       puts "oops couldn't parse git blame output"
+    }
+    return 0
+}
+
 # delete $dir when we see eof on $f (presumably because the child has exited)
 proc delete_at_eof {f dir} {
     while {[gets $f line] >= 0} {}
@@ -3398,6 +3505,7 @@ proc vieweditor {top n title} {
 
     toplevel $top
     wm title $top $title
+    wm transient $top .
     label $top.nl -text [mc "Name"]
     entry $top.name -width 20 -textvariable newviewname($n)
     grid $top.nl $top.name -sticky w -pady 5
@@ -3434,6 +3542,7 @@ proc vieweditor {top n title} {
     frame $top.buts
     button $top.buts.ok -text [mc "OK"] -command [list newviewok $top $n]
     button $top.buts.can -text [mc "Cancel"] -command [list destroy $top]
+    bind $top <Escape> [list destroy $top]
     grid $top.buts.ok $top.buts.can
     grid columnconfigure $top.buts 0 -weight 1 -uniform a
     grid columnconfigure $top.buts 1 -weight 1 -uniform a
@@ -3466,9 +3575,7 @@ proc newviewok {top n} {
     if {[catch {
        set newargs [shellsplit $newviewargs($n)]
     } err]} {
-       error_popup "[mc "Error in commit selection arguments:"] $err"
-       wm raise $top
-       focus $top
+       error_popup "[mc "Error in commit selection arguments:"] $err" $top
        return
     }
     set files {}
@@ -5742,6 +5849,7 @@ proc stopfinding {} {
        set fprogcoord 0
        adjustprogress
     }
+    stopblaming
 }
 
 proc findmore {} {
@@ -6146,7 +6254,7 @@ proc make_secsel {l} {
     $canv3 lower $t
 }
 
-proc selectline {l isnew} {
+proc selectline {l isnew {desired_loc {}}} {
     global canv ctext commitinfo selectedline
     global canvy0 linespc parents children curview
     global currentid sha1entry
@@ -6154,7 +6262,7 @@ proc selectline {l isnew} {
     global mergemax numcommits pending_select
     global cmitmode showneartags allcommits
     global targetrow targetid lastscrollrows
-    global autoselect
+    global autoselect jump_to_here
 
     catch {unset pending_select}
     $canv delete hover
@@ -6293,6 +6401,7 @@ proc selectline {l isnew} {
     $ctext conf -state disabled
     set commentend [$ctext index "end - 1c"]
 
+    set jump_to_here $desired_loc
     init_flist [mc "Comments"]
     if {$cmitmode eq "tree"} {
        gettree $id
@@ -6540,15 +6649,32 @@ proc getblobline {bf id} {
        $ctext insert end "$line\n"
     }
     if {[eof $bf]} {
+       global jump_to_here ctext_file_names commentend
+
        # delete last newline
        $ctext delete "end - 2c" "end - 1c"
        close $bf
+       if {$jump_to_here ne {} &&
+           [lindex $jump_to_here 0] eq [lindex $ctext_file_names 0]} {
+           set lnum [expr {[lindex $jump_to_here 1] +
+                           [lindex [split $commentend .] 0]}]
+           mark_ctext_line $lnum
+       }
        return 0
     }
     $ctext config -state disabled
     return [expr {$nl >= 1000? 2: 1}]
 }
 
+proc mark_ctext_line {lnum} {
+    global ctext markbgcolor
+
+    $ctext tag delete omark
+    $ctext tag add omark $lnum.0 "$lnum.0 + 1 line"
+    $ctext tag conf omark -background $markbgcolor
+    $ctext see $lnum.0
+}
+
 proc mergediff {id} {
     global diffmergeid mdifffd
     global diffids treediffs
@@ -6556,10 +6682,12 @@ proc mergediff {id} {
     global diffcontext
     global diffencoding
     global limitdiffs vfilelimit curview
+    global targetline
 
     set diffmergeid $id
     set diffids $id
     set treediffs($id) {}
+    set targetline {}
     # this doesn't seem to actually affect anything...
     set cmd [concat | git diff-tree --no-commit-id --cc -U$diffcontext $id]
     if {$limitdiffs && $vfilelimit($curview) ne {}} {
@@ -6581,7 +6709,7 @@ proc getmergediffline {mdf id np} {
     global diffmergeid ctext cflist mergemax
     global difffilestart mdifffd treediffs
     global ctext_file_names ctext_file_lines
-    global diffencoding
+    global diffencoding jump_to_here targetline diffline
 
     $ctext conf -state normal
     set nr 0
@@ -6605,9 +6733,17 @@ proc getmergediffline {mdf id np} {
            set l [expr {(78 - [string length $fname]) / 2}]
            set pad [string range "----------------------------------------" 1 $l]
            $ctext insert end "$pad $fname $pad\n" filesep
+           set targetline {}
+           if {$jump_to_here ne {} && [lindex $jump_to_here 0] eq $fname} {
+               set targetline [lindex $jump_to_here 1]
+           }
+           set diffline 0
        } elseif {[regexp {^@@} $line]} {
            set line [encoding convertfrom $diffencoding $line]
            $ctext insert end "$line\n" hunksep
+           if {[regexp { \+(\d+),\d+ @@} $line m nl]} {
+               set diffline $nl
+           }
        } elseif {[regexp {^[0-9a-f]{40}$} $line] || [regexp {^index} $line]} {
            # do nothing
        } else {
@@ -6647,6 +6783,15 @@ proc getmergediffline {mdf id np} {
                lappend tags m$num
            }
            $ctext insert end "$line\n" $tags
+           if {$targetline ne {} && $minuses eq {}} {
+               if {$diffline == $targetline} {
+                   set here [$ctext index "end - 1 line"]
+                   mark_ctext_line [lindex [split $here .] 0]
+                   set targetline {}
+               } else {
+                   incr diffline
+               }
+           }
        }
     }
     $ctext conf -state disabled
@@ -6834,7 +6979,7 @@ proc getblobdiffs {ids} {
     global diffcontext
     global ignorespace
     global limitdiffs vfilelimit curview
-    global diffencoding
+    global diffencoding targetline
 
     set cmd [diffcmd $ids "-p -C --no-commit-id -U$diffcontext"]
     if {$ignorespace} {
@@ -6847,6 +6992,7 @@ proc getblobdiffs {ids} {
        puts "error getting diffs: $err"
        return
     }
+    set targetline {}
     set diffinhdr 0
     set diffencoding [get_path_encoding {}]
     fconfigure $bdf -blocking 0 -encoding binary
@@ -6869,7 +7015,7 @@ proc setinlist {var i val} {
 
 proc makediffhdr {fname ids} {
     global ctext curdiffstart treediffs
-    global ctext_file_names
+    global ctext_file_names jump_to_here targetline diffline
 
     set i [lsearch -exact $treediffs($ids) $fname]
     if {$i >= 0} {
@@ -6879,6 +7025,11 @@ proc makediffhdr {fname ids} {
     set l [expr {(78 - [string length $fname]) / 2}]
     set pad [string range "----------------------------------------" 1 $l]
     $ctext insert $curdiffstart "$pad $fname $pad" filesep
+    set targetline {}
+    if {$jump_to_here ne {} && [lindex $jump_to_here 0] eq $fname} {
+       set targetline [lindex $jump_to_here 1]
+    }
+    set diffline 0
 }
 
 proc getblobdiffline {bdf ids} {
@@ -6886,7 +7037,7 @@ proc getblobdiffline {bdf ids} {
     global diffnexthead diffnextnote difffilestart
     global ctext_file_names ctext_file_lines
     global diffinhdr treediffs
-    global diffencoding
+    global diffencoding jump_to_here targetline diffline
 
     set nr 0
     $ctext conf -state normal
@@ -6935,6 +7086,7 @@ proc getblobdiffline {bdf ids} {
            set line [encoding convertfrom $diffencoding $line]
            $ctext insert end "$line\n" hunksep
            set diffinhdr 0
+           set diffline $f2l
 
        } elseif {$diffinhdr} {
            if {![string compare -length 12 "rename from " $line]} {
@@ -6968,6 +7120,7 @@ proc getblobdiffline {bdf ids} {
        } else {
            set line [encoding convertfrom $diffencoding $line]
            set x [string range $line 0 0]
+           set here [$ctext index "end - 1 chars"]
            if {$x == "-" || $x == "+"} {
                set tag [expr {$x == "+"}]
                $ctext insert end "$line\n" d$tag
@@ -6978,6 +7131,14 @@ proc getblobdiffline {bdf ids} {
                # or something else we don't recognize
                $ctext insert end "$line\n" hunksep
            }
+           if {$targetline ne {} && ($x eq " " || $x eq "+")} {
+               if {$diffline == $targetline} {
+                   mark_ctext_line [lindex [split $here .] 0]
+                   set targetline {}
+               } else {
+                   incr diffline
+               }
+           }
        }
     }
     $ctext conf -state disabled
@@ -7610,6 +7771,7 @@ proc mkpatch {} {
     set patchtop $top
     catch {destroy $top}
     toplevel $top
+    wm transient $top .
     label $top.title -text [mc "Generate patch"]
     grid $top.title - -pady 10
     label $top.from -text [mc "From:"]
@@ -7640,6 +7802,8 @@ proc mkpatch {} {
     frame $top.buts
     button $top.buts.gen -text [mc "Generate"] -command mkpatchgo
     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
     grid columnconfigure $top.buts 0 -weight 1 -uniform a
     grid columnconfigure $top.buts 1 -weight 1 -uniform a
@@ -7674,7 +7838,7 @@ proc mkpatchgo {} {
     set cmd [lrange $cmd 1 end]
     lappend cmd >$fname &
     if {[catch {eval exec $cmd} err]} {
-       error_popup "[mc "Error creating patch:"] $err"
+       error_popup "[mc "Error creating patch:"] $err" $patchtop
     }
     catch {destroy $patchtop}
     unset patchtop
@@ -7694,6 +7858,7 @@ proc mktag {} {
     set mktagtop $top
     catch {destroy $top}
     toplevel $top
+    wm transient $top .
     label $top.title -text [mc "Create tag"]
     grid $top.title - -pady 10
     label $top.id -text [mc "ID:"]
@@ -7711,6 +7876,8 @@ proc mktag {} {
     frame $top.buts
     button $top.buts.gen -text [mc "Create"] -command mktaggo
     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
     grid columnconfigure $top.buts 0 -weight 1 -uniform a
     grid columnconfigure $top.buts 1 -weight 1 -uniform a
@@ -7724,18 +7891,18 @@ proc domktag {} {
     set id [$mktagtop.sha1 get]
     set tag [$mktagtop.tag get]
     if {$tag == {}} {
-       error_popup [mc "No tag name specified"]
-       return
+       error_popup [mc "No tag name specified"] $mktagtop
+       return 0
     }
     if {[info exists tagids($tag)]} {
-       error_popup [mc "Tag \"%s\" already exists" $tag]
-       return
+       error_popup [mc "Tag \"%s\" already exists" $tag] $mktagtop
+       return 0
     }
     if {[catch {
        exec git tag $tag $id
     } err]} {
-       error_popup "[mc "Error creating tag:"] $err"
-       return
+       error_popup "[mc "Error creating tag:"] $err" $mktagtop
+       return 0
     }
 
     set tagids($tag) $id
@@ -7744,6 +7911,7 @@ proc domktag {} {
     addedtag $id
     dispneartags 0
     run refill_reflist
+    return 1
 }
 
 proc redrawtags {id} {
@@ -7782,7 +7950,7 @@ proc mktagcan {} {
 }
 
 proc mktaggo {} {
-    domktag
+    if {![domktag]} return
     mktagcan
 }
 
@@ -7793,6 +7961,7 @@ proc writecommit {} {
     set wrcomtop $top
     catch {destroy $top}
     toplevel $top
+    wm transient $top .
     label $top.title -text [mc "Write commit to file"]
     grid $top.title - -pady 10
     label $top.id -text [mc "ID:"]
@@ -7814,6 +7983,8 @@ proc writecommit {} {
     frame $top.buts
     button $top.buts.gen -text [mc "Write"] -command wrcomgo
     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
     grid columnconfigure $top.buts 0 -weight 1 -uniform a
     grid columnconfigure $top.buts 1 -weight 1 -uniform a
@@ -7828,7 +7999,7 @@ proc wrcomgo {} {
     set cmd "echo $id | [$wrcomtop.cmd get]"
     set fname [$wrcomtop.fname get]
     if {[catch {exec sh -c $cmd >$fname &} err]} {
-       error_popup "[mc "Error writing commit:"] $err"
+       error_popup "[mc "Error writing commit:"] $err" $wrcomtop
     }
     catch {destroy $wrcomtop}
     unset wrcomtop
@@ -7847,6 +8018,7 @@ proc mkbranch {} {
     set top .makebranch
     catch {destroy $top}
     toplevel $top
+    wm transient $top .
     label $top.title -text [mc "Create new branch"]
     grid $top.title - -pady 10
     label $top.id -text [mc "ID:"]
@@ -7861,6 +8033,8 @@ proc mkbranch {} {
     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}"
+    bind $top <Key-Return> [list mkbrgo $top]
+    bind $top <Key-Escape> "catch {destroy $top}"
     grid $top.buts.go $top.buts.can
     grid columnconfigure $top.buts 0 -weight 1 -uniform a
     grid columnconfigure $top.buts 1 -weight 1 -uniform a
@@ -7876,12 +8050,12 @@ proc mkbrgo {top} {
     set cmdargs {}
     set old_id {}
     if {$name eq {}} {
-       error_popup [mc "Please specify a name for the new branch"]
+       error_popup [mc "Please specify a name for the new branch"] $top
        return
     }
     if {[info exists headids($name)]} {
        if {![confirm_popup [mc \
-               "Branch '%s' already exists. Overwrite?" $name]]} {
+               "Branch '%s' already exists. Overwrite?" $name] $top]} {
            return
        }
        set old_id $headids($name)
@@ -7914,6 +8088,32 @@ proc mkbrgo {top} {
     }
 }
 
+proc exec_citool {tool_args {baseid {}}} {
+    global commitinfo env
+
+    set save_env [array get env GIT_AUTHOR_*]
+
+    if {$baseid ne {}} {
+       if {![info exists commitinfo($baseid)]} {
+           getcommit $baseid
+       }
+       set author [lindex $commitinfo($baseid) 1]
+       set date [lindex $commitinfo($baseid) 2]
+       if {[regexp {^\s*(\S.*\S|\S)\s*<(.*)>\s*$} \
+                   $author author name email]
+           && $date ne {}} {
+           set env(GIT_AUTHOR_NAME) $name
+           set env(GIT_AUTHOR_EMAIL) $email
+           set env(GIT_AUTHOR_DATE) $date
+       }
+    }
+
+    eval exec git citool $tool_args &
+
+    array unset env GIT_AUTHOR_*
+    array set env $save_env
+}
+
 proc cherrypick {} {
     global rowmenuid curview
     global mainhead mainheadid
@@ -7932,7 +8132,26 @@ proc cherrypick {} {
     # no error occurs, and exec takes that as an indication of error...
     if {[catch {exec sh -c "git cherry-pick -r $rowmenuid 2>&1"} err]} {
        notbusy cherrypick
-       error_popup $err
+       if {[regexp -line \
+                {Entry '(.*)' (would be overwritten by merge|not uptodate)} \
+                $err msg fname]} {
+           error_popup [mc "Cherry-pick failed because of local changes\
+                       to file '%s'.\nPlease commit, reset or stash\
+                       your changes and try again." $fname]
+       } elseif {[regexp -line \
+                      {^(CONFLICT \(.*\):|Automatic cherry-pick failed)} \
+                      $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"]
+               exec_citool {} $rowmenuid
+           }
+       } else {
+           error_popup $err
+       }
+       run updatecommits
        return
     }
     set newhead [exec git rev-parse HEAD]
@@ -7985,6 +8204,7 @@ proc resethead {} {
     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"
+    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"
     tkwait window $w
@@ -8141,6 +8361,7 @@ proc showrefs {} {
     }
     toplevel $top
     wm title $top [mc "Tags and heads: %s" [file tail [pwd]]]
+    wm transient $top .
     text $top.list -background $bgcolor -foreground $fgcolor \
        -selectbackground $selectbgcolor -font mainfont \
        -xscrollcommand "$top.xsb set" -yscrollcommand "$top.ysb set" \
@@ -8162,6 +8383,7 @@ proc showrefs {} {
     pack $top.f.l -side left
     grid $top.f - -sticky ew -pady 2
     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
     grid rowconfigure $top 0 -weight 1
@@ -9467,6 +9689,7 @@ proc mkfontdisp {font top which} {
 
 proc choosefont {font which} {
     global fontparam fontlist fonttop fontattr
+    global prefstop
 
     set fontparam(which) $which
     set fontparam(font) $font
@@ -9480,6 +9703,7 @@ proc choosefont {font which} {
        font create sample
        eval font config sample [font actual $font]
        toplevel $top
+       wm transient $top $prefstop
        wm title $top [mc "Gitk font chooser"]
        label $top.l -textvariable fontparam(which)
        pack $top.l -side top
@@ -9513,6 +9737,8 @@ proc choosefont {font which} {
        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
+       bind $top <Key-Return> fontok
+       bind $top <Key-Escape> fontcan
        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
@@ -9579,7 +9805,7 @@ proc chg_fontparam {v sub op} {
 proc doprefs {} {
     global maxwidth maxgraphpct
     global oldprefs prefstop showneartags showlocalchanges
-    global bgcolor fgcolor ctext diffcolors selectbgcolor
+    global bgcolor fgcolor ctext diffcolors selectbgcolor markbgcolor
     global tabstop limitdiffs autoselect extdifftool perfile_attrs
 
     set top .gitkprefs
@@ -9594,6 +9820,7 @@ proc doprefs {} {
     }
     toplevel $top
     wm title $top [mc "Gitk preferences"]
+    wm transient $top .
     label $top.ldisp -text [mc "Commit list display options"]
     grid $top.ldisp - -sticky w -pady 10
     label $top.spacer -text " "
@@ -9672,6 +9899,12 @@ proc doprefs {} {
                      "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 background setselbg]
@@ -9686,6 +9919,8 @@ proc doprefs {} {
     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
+    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
@@ -10217,6 +10452,7 @@ set diffcolors {red "#00a000" blue}
 set diffcontext 3
 set ignorespace 0
 set selectbgcolor gray85
+set markbgcolor "#e0e0ff"
 
 set circlecolors {white blue gray blue blue}