From bee866fa6b5a77f65d9915f8778919ef76d9d6c1 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 8 Oct 2008 11:05:35 +0400 Subject: [PATCH 01/18] gitk: Allow forcing branch creation if it already exists If gitk knows that the branch the user tries to create exists, it should ask whether it should overwrite it. This way the user can either decide to choose a new name, or move the head while preserving the reflog. Signed-off-by: Alexander Gavrilov Signed-off-by: Paul Mackerras --- gitk | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/gitk b/gitk index bcebc87f7d..5830941b7a 100755 --- a/gitk +++ b/gitk @@ -7727,24 +7727,42 @@ proc mkbrgo {top} { set name [$top.name get] set id [$top.sha1 get] + set cmdargs {} + set old_id {} if {$name eq {}} { error_popup [mc "Please specify a name for the new branch"] return } + if {[info exists headids($name)]} { + if {![confirm_popup [mc \ + "Branch '%s' already exists. Overwrite?" $name]]} { + return + } + set old_id $headids($name) + lappend cmdargs -f + } catch {destroy $top} + lappend cmdargs $name $id nowbusy newbranch update if {[catch { - exec git branch $name $id + eval exec git branch $cmdargs } err]} { notbusy newbranch error_popup $err } else { - set headids($name) $id - lappend idheads($id) $name - addedhead $id $name notbusy newbranch - redrawtags $id + if {$old_id ne {}} { + movehead $id $name + movedhead $id $name + redrawtags $old_id + redrawtags $id + } else { + set headids($name) $id + lappend idheads($id) $name + addedhead $id $name + redrawtags $id + } dispneartags 0 run refill_reflist } From 2df6442f7dc0d6cd5636e6ac413d06f92eb04029 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 8 Oct 2008 11:05:37 +0400 Subject: [PATCH 02/18] gitk: Fix file list context menu for merge commits Currently it displays an ugly error box, because the treediffs array is not filled for such commits. This fixes it by making getmergediffline add the filenames it sees to the treediffs array like gettreediffline does. Signed-off-by: Alexander Gavrilov Signed-off-by: Paul Mackerras --- gitk | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/gitk b/gitk index 5830941b7a..f6a3e10d8e 100755 --- a/gitk +++ b/gitk @@ -6418,7 +6418,7 @@ proc getblobline {bf id} { proc mergediff {id} { global diffmergeid mdifffd - global diffids + global diffids treediffs global parents global diffcontext global diffencoding @@ -6426,6 +6426,7 @@ proc mergediff {id} { set diffmergeid $id set diffids $id + set treediffs($id) {} # 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 {}} { @@ -6445,7 +6446,7 @@ proc mergediff {id} { proc getmergediffline {mdf id np} { global diffmergeid ctext cflist mergemax - global difffilestart mdifffd + global difffilestart mdifffd treediffs global diffencoding $ctext conf -state normal @@ -6462,6 +6463,7 @@ proc getmergediffline {mdf id np} { $ctext insert end "\n" set here [$ctext index "end - 1c"] lappend difffilestart $here + lappend treediffs($id) $fname add_flist [list $fname] set diffencoding [get_path_encoding $fname] set l [expr {(78 - [string length $fname]) / 2}] From 7cdc3556d1cec11582ae2a018d4052767c02e1ef Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 24 Oct 2008 12:13:01 +0400 Subject: [PATCH 03/18] gitk: Allow starting gui blame for a specific line This adds a context menu item to the diff viewer pane that calls git gui blame, focusing it on the clicked line. In case of combined diffs, it also automatically deduces which parent is to be blamed. Lines added by the diff are blamed on the current commit itself. The context menu itself is added by this patch. It would be possible to add the commands from the flist menu to it. Signed-off-by: Alexander Gavrilov Signed-off-by: Paul Mackerras --- gitk | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 146 insertions(+), 2 deletions(-) diff --git a/gitk b/gitk index f6a3e10d8e..0003797697 100755 --- a/gitk +++ b/gitk @@ -2249,6 +2249,7 @@ proc makewindow {} { bind $cflist {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} set maincursor [. cget -cursor] set textcursor [$ctext cget -cursor] @@ -2291,6 +2292,13 @@ proc makewindow {} { {mc "Blame parent commit" command {external_blame 1}} } $flist_menu configure -tearoff 0 + + global diff_menu + set diff_menu .diffctxmenu + makemenu $diff_menu { + {mc "Run git gui blame on this line" command {external_blame_diff}} + } + $diff_menu configure -tearoff 0 } # Windows sends all mouse wheel events to the current focused window, not @@ -2993,6 +3001,34 @@ proc pop_flist_menu {w X Y x y} { tk_popup $flist_menu $X $Y } +proc find_ctext_fileinfo {line} { + global ctext_file_names ctext_file_lines + + set ok [bsearch $ctext_file_lines $line] + set tline [lindex $ctext_file_lines $ok] + + if {$ok >= [llength $ctext_file_lines] || $line < $tline} { + return {} + } else { + return [list [lindex $ctext_file_names $ok] $tline] + } +} + +proc pop_diff_menu {w X Y x y} { + global ctext diff_menu flist_menu_file + 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] + set f [find_ctext_fileinfo $diff_menu_line] + if {$f eq {}} return + set flist_menu_file [lindex $f 0] + set diff_menu_filebase [lindex $f 1] + tk_popup $diff_menu $X $Y +} + proc flist_hl {only} { global flist_menu_file findstring gdttype @@ -3099,7 +3135,96 @@ proc external_diff {} { } } -proc external_blame {parent_idx} { +proc find_hunk_blamespec {base line} { + global ctext + + # Find and parse the hunk header + set s_lix [$ctext search -backwards -regexp ^@@ "$line.0 lineend" $base.0] + if {$s_lix eq {}} return + + set s_line [$ctext get $s_lix "$s_lix + 1 lines"] + if {![regexp {^@@@*(( -\d+(,\d+)?)+) \+(\d+)(,\d+)? @@} $s_line \ + s_line old_specs osz osz1 new_line nsz]} { + return + } + + # base lines for the parents + set base_lines [list $new_line] + foreach old_spec [lrange [split $old_specs " "] 1 end] { + if {![regexp -- {-(\d+)(,\d+)?} $old_spec \ + old_spec old_line osz]} { + return + } + lappend base_lines $old_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] + set removed_idx [string first "-" $chunk] + # Choose a parent index + if {$parent eq {}} { + if {$removed_idx >= 0} { + set parent $removed_idx + } else { + set unchanged_idx [string first " " $chunk] + if {$unchanged_idx >= 0} { + set parent $unchanged_idx + } else { + # 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 + } + } + } + + if {$parent eq {}} { set parent -1 } + incr parent + incr dline [lindex $base_lines $parent] + return [list $parent $dline] +} + +proc external_blame_diff {} { + global currentid diffmergeid cmitmode + global diff_menu_txtpos diff_menu_line + global diff_menu_filebase flist_menu_file + + if {$cmitmode eq "tree"} { + set parent_idx 0 + set line [expr {$diff_menu_line - $diff_menu_filebase - 1}] + } else { + set hinfo [find_hunk_blamespec $diff_menu_filebase $diff_menu_line] + if {$hinfo ne {}} { + set parent_idx [lindex $hinfo 0] + set line [lindex $hinfo 1] + } else { + set parent_idx 0 + set line 0 + } + } + + external_blame $parent_idx $line +} + +proc external_blame {parent_idx {line {}}} { global flist_menu_file global nullid nullid2 global parentlist selectedline currentid @@ -3115,7 +3240,12 @@ proc external_blame {parent_idx} { return } - if {[catch {exec git gui blame $base_commit $flist_menu_file &} err]} { + set cmdline [list git gui blame] + if {$line ne {} && $line > 1} { + lappend cmdline "--line=$line" + } + lappend cmdline $base_commit $flist_menu_file + if {[catch {eval exec $cmdline &} err]} { error_popup "[mc "git gui blame: command failed:"] $err" } } @@ -6364,6 +6494,7 @@ proc gettreeline {gtf id} { proc showfile {f} { global treefilelist treeidlist diffids nullid nullid2 + global ctext_file_names ctext_file_lines global ctext commentend set i [lsearch -exact $treefilelist($diffids) $f] @@ -6387,6 +6518,8 @@ proc showfile {f} { filerun $bf [list getblobline $bf $diffids] $ctext config -state normal clear_ctext $commentend + lappend ctext_file_names $f + lappend ctext_file_lines [lindex [split $commentend "."] 0] $ctext insert end "\n" $ctext insert end "$f\n" filesep $ctext config -state disabled @@ -6447,6 +6580,7 @@ proc mergediff {id} { proc getmergediffline {mdf id np} { global diffmergeid ctext cflist mergemax global difffilestart mdifffd treediffs + global ctext_file_names ctext_file_lines global diffencoding $ctext conf -state normal @@ -6465,6 +6599,8 @@ proc getmergediffline {mdf id np} { lappend difffilestart $here lappend treediffs($id) $fname add_flist [list $fname] + lappend ctext_file_names $fname + lappend ctext_file_lines [lindex [split $here "."] 0] set diffencoding [get_path_encoding $fname] set l [expr {(78 - [string length $fname]) / 2}] set pad [string range "----------------------------------------" 1 $l] @@ -6733,11 +6869,13 @@ proc setinlist {var i val} { proc makediffhdr {fname ids} { global ctext curdiffstart treediffs + global ctext_file_names set i [lsearch -exact $treediffs($ids) $fname] if {$i >= 0} { setinlist difffilestart $i $curdiffstart } + set ctext_file_names [lreplace $ctext_file_names end end $fname] set l [expr {(78 - [string length $fname]) / 2}] set pad [string range "----------------------------------------" 1 $l] $ctext insert $curdiffstart "$pad $fname $pad" filesep @@ -6746,6 +6884,7 @@ proc makediffhdr {fname ids} { proc getblobdiffline {bdf ids} { global diffids blobdifffd ctext curdiffstart global diffnexthead diffnextnote difffilestart + global ctext_file_names ctext_file_lines global diffinhdr treediffs global diffencoding @@ -6763,6 +6902,8 @@ proc getblobdiffline {bdf ids} { # start of a new file $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 the name hasn't changed the length will be odd, # the middle char will be a space, and the two bits either @@ -6899,6 +7040,7 @@ proc nextfile {} { proc clear_ctext {{first 1.0}} { global ctext smarktop smarkbot + global ctext_file_names ctext_file_lines global pendinglinks set l [lindex [split $first .] 0] @@ -6912,6 +7054,8 @@ proc clear_ctext {{first 1.0}} { if {$first eq "1.0"} { catch {unset pendinglinks} } + set ctext_file_names {} + set ctext_file_lines {} } proc settabs {{firstab {}}} { From 190ec52c901b1e003f73ad97e88f4626d20a9e4e Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Mon, 27 Oct 2008 21:13:37 +1100 Subject: [PATCH 04/18] gitk: Fix some off-by-one errors in computing which line to blame When walking back from the line where a right-click happened to the previous hunk separator line to calculate the line number to work on, we were counting every line including the one clicked on. That isn't right; if the user clicked on the line immediately after the hunk separator then the correct line number would be the one from the hunk separator. Therefore this looks at the clicked-on line to work out which parent to blame (or whether to blame the current commit), and then looks only at the preceding lines to work out the offset from the line number in the hunk separator. This also fixes an off-by-one error when we are showing files rather than diffs. In this case diff_menu_filebase is the line number of the banner showing the file name, so the first line of the file is at line $diff_menu_filebase + 1. This also simplifies the code in find_hunk_blamespec a bit and arranges that we don't pop up the context menu if the user clicks on a file separator line or a hunk separator line. Signed-off-by: Paul Mackerras --- gitk | 60 +++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/gitk b/gitk index 0003797697..477590eed6 100755 --- a/gitk +++ b/gitk @@ -3019,9 +3019,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 +3163,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 +3215,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 {}} { From 8a8977425e2697029414c3bcf4b627b074934bbc Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Mon, 27 Oct 2008 21:36:25 +1100 Subject: [PATCH 05/18] gitk: Add a menu item to show where a given line comes from This adds a menu item to the pop-up menu for the diff display window which makes gitk find which commit added the line (via git blame) and show that commit, with the line highlighted with a light-blue background. Signed-off-by: Paul Mackerras --- gitk | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 153 insertions(+), 7 deletions(-) diff --git a/gitk b/gitk index 477590eed6..7b02efb4fa 100755 --- a/gitk +++ b/gitk @@ -2296,6 +2296,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 @@ -2830,9 +2831,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 { @@ -3256,6 +3263,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} {} @@ -5748,6 +5840,7 @@ proc stopfinding {} { set fprogcoord 0 adjustprogress } + stopblaming } proc findmore {} { @@ -6152,7 +6245,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 @@ -6160,7 +6253,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 @@ -6299,6 +6392,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 @@ -6546,15 +6640,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 + + $ctext tag delete omark + $ctext tag add omark $lnum.0 "$lnum.0 + 1 line" + $ctext tag conf omark -background "#e0e0ff" + $ctext see $lnum.0 +} + proc mergediff {id} { global diffmergeid mdifffd global diffids treediffs @@ -6562,10 +6673,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 {}} { @@ -6587,7 +6700,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 @@ -6611,9 +6724,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 { @@ -6653,6 +6774,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 @@ -6840,7 +6970,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} { @@ -6853,6 +6983,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 @@ -6875,7 +7006,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} { @@ -6885,6 +7016,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} { @@ -6892,7 +7028,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 @@ -6941,6 +7077,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]} { @@ -6974,6 +7111,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 @@ -6984,6 +7122,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 From e3e901bece93417392faa3dd7833d69271cf74f6 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Mon, 27 Oct 2008 22:37:21 +1100 Subject: [PATCH 06/18] gitk: Make the background color of marked lines configurable This makes it possible for the user to configure the background color of lines that are "marked". At the moment only the "show the origin of this line" function marks lines. This also makes the user's choice persistent by saving it in ~/.gitk. Signed-off-by: Paul Mackerras --- gitk | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/gitk b/gitk index 7b02efb4fa..3fe1b479b2 100755 --- a/gitk +++ b/gitk @@ -2415,7 +2415,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 @@ -2439,6 +2439,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] @@ -6658,11 +6659,11 @@ proc getblobline {bf id} { } proc mark_ctext_line {lnum} { - global ctext + global ctext markbgcolor $ctext tag delete omark $ctext tag add omark $lnum.0 "$lnum.0 + 1 line" - $ctext tag conf omark -background "#e0e0ff" + $ctext tag conf omark -background $markbgcolor $ctext see $lnum.0 } @@ -9731,7 +9732,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 @@ -9824,6 +9825,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] @@ -10369,6 +10376,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} From 8b07dca18a498c5900edc717d216e841d6c15dc9 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sun, 2 Nov 2008 22:34:47 +1100 Subject: [PATCH 07/18] gitk: Unify handling of merge diffs with normal 2-way diffs This adds code to getblobdiffline to make it able to recognize and display merge diffs (i.e. N-way diffs for N >= 3) as well as normal two-way diffs. This means that it can also correctly display the 3-way diff of the local changes when the git repository is in the middle of a merge with conflicts. This also removes getmergediffline and changes mergediff to invoke getblobdiffline rather than getmergediffline. Signed-off-by: Paul Mackerras --- gitk | 270 ++++++++++++++++++++++------------------------------------- 1 file changed, 99 insertions(+), 171 deletions(-) diff --git a/gitk b/gitk index 3fe1b479b2..e6e49fc5cc 100755 --- a/gitk +++ b/gitk @@ -2098,7 +2098,7 @@ proc makewindow {} { $ctext tag conf filesep -font textfontbold -back "#aaaaaa" $ctext tag conf hunksep -fore [lindex $diffcolors 2] $ctext tag conf d0 -fore [lindex $diffcolors 0] - $ctext tag conf d1 -fore [lindex $diffcolors 1] + $ctext tag conf dresult -fore [lindex $diffcolors 1] $ctext tag conf m0 -fore red $ctext tag conf m1 -fore blue $ctext tag conf m2 -fore green @@ -3217,7 +3217,7 @@ proc find_hunk_blamespec {base line} { } proc external_blame_diff {} { - global currentid diffmergeid cmitmode + global currentid cmitmode global diff_menu_txtpos diff_menu_line global diff_menu_filebase flist_menu_file @@ -6668,130 +6668,16 @@ proc mark_ctext_line {lnum} { } proc mergediff {id} { - global diffmergeid mdifffd + global diffmergeid global diffids treediffs - global parents - global diffcontext - global diffencoding - global limitdiffs vfilelimit curview - global targetline + global parents curview 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 {}} { - set cmd [concat $cmd -- $vfilelimit($curview)] - } - if {[catch {set mdf [open $cmd r]} err]} { - error_popup "[mc "Error getting merge diffs:"] $err" - return - } - fconfigure $mdf -blocking 0 -encoding binary - set mdifffd($id) $mdf set np [llength $parents($curview,$id)] - set diffencoding [get_path_encoding {}] settabs $np - filerun $mdf [list getmergediffline $mdf $id $np] -} - -proc getmergediffline {mdf id np} { - global diffmergeid ctext cflist mergemax - global difffilestart mdifffd treediffs - global ctext_file_names ctext_file_lines - global diffencoding jump_to_here targetline diffline - - $ctext conf -state normal - set nr 0 - while {[incr nr] <= 1000 && [gets $mdf line] >= 0} { - if {![info exists diffmergeid] || $id != $diffmergeid - || $mdf != $mdifffd($id)} { - close $mdf - return 0 - } - if {[regexp {^diff --cc (.*)} $line match fname]} { - # start of a new file - set fname [encoding convertfrom $fname] - $ctext insert end "\n" - set here [$ctext index "end - 1c"] - lappend difffilestart $here - lappend treediffs($id) $fname - add_flist [list $fname] - lappend ctext_file_names $fname - lappend ctext_file_lines [lindex [split $here "."] 0] - set diffencoding [get_path_encoding $fname] - 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 { - set line [encoding convertfrom $diffencoding $line] - # parse the prefix - one ' ', '-' or '+' for each parent - set spaces {} - set minuses {} - set pluses {} - set isbad 0 - for {set j 0} {$j < $np} {incr j} { - set c [string range $line $j $j] - if {$c == " "} { - lappend spaces $j - } elseif {$c == "-"} { - lappend minuses $j - } elseif {$c == "+"} { - lappend pluses $j - } else { - set isbad 1 - break - } - } - set tags {} - set num {} - if {!$isbad && $minuses ne {} && $pluses eq {}} { - # line doesn't appear in result, parents in $minuses have the line - set num [lindex $minuses 0] - } elseif {!$isbad && $pluses ne {} && $minuses eq {}} { - # line appears in result, parents in $pluses don't have the line - lappend tags mresult - set num [lindex $spaces 0] - } - if {$num ne {}} { - if {$num >= $mergemax} { - set num "max" - } - 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 - if {[eof $mdf]} { - close $mdf - return 0 - } - return [expr {$nr >= 1000? 2: 1}] + getblobdiffs $id } proc startdiff {ids} { @@ -6971,9 +6857,9 @@ proc getblobdiffs {ids} { global diffcontext global ignorespace global limitdiffs vfilelimit curview - global diffencoding targetline + global diffencoding targetline diffnparents - set cmd [diffcmd $ids "-p -C --no-commit-id -U$diffcontext"] + set cmd [diffcmd $ids "-p -C --cc --no-commit-id -U$diffcontext"] if {$ignorespace} { append cmd " -w" } @@ -6981,10 +6867,11 @@ proc getblobdiffs {ids} { set cmd [concat $cmd -- $vfilelimit($curview)] } if {[catch {set bdf [open $cmd r]} err]} { - puts "error getting diffs: $err" + 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 @@ -7006,9 +6893,11 @@ proc setinlist {var i val} { } proc makediffhdr {fname ids} { - global ctext curdiffstart treediffs + global ctext curdiffstart treediffs diffencoding global ctext_file_names jump_to_here targetline diffline + set fname [encoding convertfrom $fname] + set diffencoding [get_path_encoding $fname] set i [lsearch -exact $treediffs($ids) $fname] if {$i >= 0} { setinlist difffilestart $i $curdiffstart @@ -7028,7 +6917,7 @@ proc getblobdiffline {bdf ids} { global diffids blobdifffd ctext curdiffstart global diffnexthead diffnextnote difffilestart global ctext_file_names ctext_file_lines - global diffinhdr treediffs + global diffinhdr treediffs mergemax diffnparents global diffencoding jump_to_here targetline diffline set nr 0 @@ -7038,47 +6927,63 @@ proc getblobdiffline {bdf ids} { close $bdf return 0 } - if {![string compare -length 11 "diff --git " $line]} { - # trim off "diff --git " - set line [string range $line 11 end] - set diffinhdr 1 + 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 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] + + 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 fname [string range $line 2 [expr {$i - 1}]] + 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 [encoding convertfrom $fname] - set diffencoding [get_path_encoding $fname] makediffhdr $fname $ids - } elseif {[regexp {^@@ -([0-9]+)(,[0-9]+)? \+([0-9]+)(,[0-9]+)? @@(.*)} \ - $line match f1l f1c f2l f2c rest]} { + } 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 - set diffline $f2l } elseif {$diffinhdr} { if {![string compare -length 12 "rename from " $line]} { @@ -7097,8 +7002,6 @@ proc getblobdiffline {bdf ids} { if {[string index $fname 0] eq "\""} { set fname [lindex $fname 0] } - set fname [encoding convertfrom $fname] - set diffencoding [get_path_encoding $fname] makediffhdr $fname $ids } elseif {[string compare -length 3 $line "---"] == 0} { # do nothing @@ -7111,28 +7014,53 @@ 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 - } elseif {$x == " "} { - $ctext insert end "$line\n" + # 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 + } + } + } + if {$targetline ne {}} { + if {$diffline == $targetline} { + set seehere [$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 {$targetline ne {} && ($x eq " " || $x eq "+")} { - if {$diffline == $targetline} { - mark_ctext_line [lindex [split $here .] 0] - set targetline {} - } else { - incr diffline - } - } } } + if {[info exists seehere]} { + mark_ctext_line [lindex [split $seehere .] 0] + } $ctext conf -state disabled if {[eof $bdf]} { close $bdf @@ -7145,7 +7073,7 @@ proc changediffdisp {} { global ctext diffelide $ctext tag conf d0 -elide [lindex $diffelide 0] - $ctext tag conf d1 -elide [lindex $diffelide 1] + $ctext tag conf dresult -elide [lindex $diffelide 1] } proc highlightfile {loc cline} { @@ -9817,7 +9745,7 @@ proc doprefs {} { 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 "diff new lines" \ - [list $ctext tag conf d1 -foreground]] + [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 \ From fc4977e1b989503c6dcfbc527cda2458d63d0400 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 4 Nov 2008 12:57:44 +1100 Subject: [PATCH 08/18] gitk: Make "show origin of this line" work on fake commits This makes the "Show origin of this line" menu item work correctly on the fake commits that gitk shows for local uncommitted changes. With the fake commit for changes that aren't checked in to the index, we can actually get a 3-way diff shown, which means we might have to blame either the parent or the commit being merged in (which we get from .git/MERGE_HEAD). If the parent is the fake commit which shows the changes that have been checked in to the index, then we need to get the SHA1 of the blob for the version of the file that is in the index, then use git cat-file blob to get the contents of the blob, and give that to git blame with --contents - so that git blame will do the blame on the index version of the file. In that case, we might get the all-zeroes SHA1 back from git blame, meaning that the line is new in the index version of the file, so then we have to use $nullid2 (the pseudo-SHA1 of the fake commit for the checked-in changes). Signed-off-by: Paul Mackerras --- gitk | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/gitk b/gitk index e6e49fc5cc..f386981cc9 100755 --- a/gitk +++ b/gitk @@ -3238,6 +3238,22 @@ proc external_blame_diff {} { external_blame $parent_idx $line } +# Find the SHA1 ID of the blob for file $fname in the index +# at stage 0 or 2 +proc index_sha1 {fname} { + set f [open [list | git ls-files -s $fname] r] + while {[gets $f line] >= 0} { + set info [lindex [split $line "\t"] 0] + set stage [lindex $info 2] + if {$stage eq "0" || $stage eq "2"} { + close $f + return [lindex $info 1] + } + } + close $f + return {} +} + proc external_blame {parent_idx {line {}}} { global flist_menu_file global nullid nullid2 @@ -3267,7 +3283,9 @@ 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 + set from_index {} if {$cmitmode eq "tree"} { set id $currentid set line [expr {$diff_menu_line - $diff_menu_filebase}] @@ -3279,11 +3297,46 @@ proc show_line_source {} { mark_ctext_line $diff_menu_line return } - set id [lindex $parents($curview,$currentid) [expr {$pi - 1}]] + incr pi -1 + if {$currentid eq $nullid} { + if {$pi > 0} { + # must be a merge in progress... + if {[catch { + # get the last line from .git/MERGE_HEAD + set f [open [file join $gitdir MERGE_HEAD] r] + set id [lindex [split [read $f] "\n"] end-1] + close $f + } err]} { + error_popup [mc "Couldn't read merge head: %s" $err] + return + } + } elseif {$parents($curview,$currentid) eq $nullid2} { + # need to do the blame from the index + if {[catch { + set from_index [index_sha1 $flist_menu_file] + } err]} { + error_popup [mc "Error reading index: %s" $err] + return + } + } + } else { + set id [lindex $parents($curview,$currentid) $pi] + } set line [lindex $h 1] } + set blameargs {} + if {$from_index ne {}} { + lappend blameargs | git cat-file blob $from_index + } + lappend blameargs | git blame -p -L$line,+1 + if {$from_index ne {}} { + lappend blameargs --contents - + } else { + lappend blameargs $id + } + lappend blameargs -- $flist_menu_file if {[catch { - set f [open [list | git blame -p -L$line,+1 $id -- $flist_menu_file] r] + set f [open $blameargs r] } err]} { error_popup [mc "Couldn't start git blame: %s" $err] return @@ -3305,7 +3358,7 @@ proc stopblaming {} { } proc read_line_source {fd inst} { - global blamestuff curview commfd blameinst + global blamestuff curview commfd blameinst nullid nullid2 while {[gets $fd line] >= 0} { lappend blamestuff($inst) $line @@ -3337,6 +3390,11 @@ proc read_line_source {fd inst} { } if {$fname ne {}} { # all looks good, select it + if {$id eq $nullid} { + # blame uses all-zeroes to mean not committed, + # which would mean a change in the index + set id $nullid2 + } if {[commitinview $id $curview]} { selectline [rowofcommit $id] 1 [list $fname $lnum] } else { From 48a81b7cda1e58d8cc4d9bcadce76bbf1680ab7d Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 4 Nov 2008 21:09:00 +1100 Subject: [PATCH 09/18] gitk: Cope with unmerged files in local changes This modifies gettreediffline so that it when we get both a "U" line and an "M" line for the same file in the output from git diff-files or git diff-index --cached (used when the user clicks on a fake commit) we don't add the same filename to the treediff list twice. This also makes getblobdiffline recognize the "* Unmerged path ..." lines we get when we ask for the actual diffs, and makes a tiny optimization in makediffhdr. Signed-off-by: Paul Mackerras --- gitk | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/gitk b/gitk index f386981cc9..bea5026f62 100755 --- a/gitk +++ b/gitk @@ -6854,8 +6854,10 @@ proc gettreediffline {gdtf ids} { set file [lindex $file 0] } set file [encoding convertfrom $file] - lappend treediff $file - lappend sublist $file + if {$file ne [lindex $treediff end]} { + lappend treediff $file + lappend sublist $file + } } } if {$perfile_attrs} { @@ -6960,7 +6962,7 @@ proc makediffhdr {fname ids} { if {$i >= 0} { setinlist difffilestart $i $curdiffstart } - set ctext_file_names [lreplace $ctext_file_names end end $fname] + lset ctext_file_names end $fname set l [expr {(78 - [string length $fname]) / 2}] set pad [string range "----------------------------------------" 1 $l] $ctext insert $curdiffstart "$pad $fname $pad" filesep @@ -7033,6 +7035,18 @@ proc getblobdiffline {bdf 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] From 76f15947af78f526925810487f69fed35f6a7daf Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 2 Nov 2008 21:59:44 +0300 Subject: [PATCH 10/18] gitk: Add Return and Escape bindings to dialogs It is often more convenient to dismiss or accept a dialog using the keyboard, than by clicking buttons on the screen. This commit adds key binding to make it possible with gitk's dialogs. Signed-off-by: Alexander Gavrilov Signed-off-by: Paul Mackerras --- gitk | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/gitk b/gitk index 3fe1b479b2..edef9e224e 100755 --- a/gitk +++ b/gitk @@ -1746,6 +1746,8 @@ proc show_error {w top msg} { pack $w.ok -side bottom -fill x bind $top "grab $top; focus $top" bind $top "destroy $top" + bind $top "destroy $top" + bind $top "destroy $top" tkwait window $top } @@ -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 "grab $w; focus $w" + bind $w "set confirm_ok 1; destroy $w" + bind $w "set confirm_ok 1; destroy $w" + bind $w "destroy $w" tkwait window $w return $confirm_ok } @@ -2611,6 +2616,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 [list destroy $w] pack $w.ok -side bottom bind $w "focus $w.ok" bind $w "destroy $w" @@ -3533,6 +3539,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 [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 @@ -7793,6 +7800,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 mkpatchgo + bind $top 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 @@ -7864,6 +7873,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 mktaggo + bind $top 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 @@ -7967,6 +7978,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 wrcomgo + bind $top 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 @@ -8014,6 +8027,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 [list mkbrgo $top] + bind $top "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 @@ -8138,6 +8153,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 [list destroy $w] pack $w.cancel -side right -fill x -padx 20 -pady 20 bind $w "grab $w; focus $w" tkwait window $w @@ -8315,6 +8331,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 [list destroy $top] grid $top.close - grid columnconfigure $top 0 -weight 1 grid rowconfigure $top 0 -weight 1 @@ -9666,6 +9683,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 fontok + bind $top 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 @@ -9845,6 +9864,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 prefsok + bind $top 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 From 84a76f18f0313832ccc9c2f0171ace3da64ffa4f Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 2 Nov 2008 21:59:45 +0300 Subject: [PATCH 11/18] gitk: Make gitk dialog windows transient Transient windows are always kept above their parent, and don't occupy any space in the taskbar, which is useful for dialogs. Also, when transient is used, it is important to bind windows to the correct parent. This commit adds transient annotations to all dialogs, ensures usage of the correct parent for error and confirmation popups, and, as a side job, makes gitk preserve the create tag dialog window in case of errors. Signed-off-by: Alexander Gavrilov Signed-off-by: Paul Mackerras --- gitk | 46 ++++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/gitk b/gitk index edef9e224e..41d3d2d98b 100755 --- a/gitk +++ b/gitk @@ -1751,19 +1751,19 @@ proc show_error {w top msg} { 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" @@ -2546,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 @@ -2574,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:"] @@ -3503,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 @@ -3572,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 {} @@ -7770,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:"] @@ -7836,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 @@ -7856,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:"] @@ -7888,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 @@ -7908,6 +7911,7 @@ proc domktag {} { addedtag $id dispneartags 0 run refill_reflist + return 1 } proc redrawtags {id} { @@ -7946,7 +7950,7 @@ proc mktagcan {} { } proc mktaggo {} { - domktag + if {![domktag]} return mktagcan } @@ -7957,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:"] @@ -7994,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 @@ -8013,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:"] @@ -8044,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) @@ -8310,6 +8316,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" \ @@ -9637,6 +9644,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 @@ -9650,6 +9658,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 @@ -9766,6 +9775,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 " " From 15e350552db1c0e8ede0af4f1f07dbbc9090e420 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 2 Nov 2008 21:59:47 +0300 Subject: [PATCH 12/18] gitk: Make cherry-pick call git-citool on conflicts Now that git-gui has facilities to help users resolve conflicts, it makes sense to launch it from other GUI tools when they happen. Signed-off-by: Alexander Gavrilov Signed-off-by: Paul Mackerras --- gitk | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/gitk b/gitk index 41d3d2d98b..4168648b52 100755 --- a/gitk +++ b/gitk @@ -8088,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 @@ -8106,7 +8132,19 @@ 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} $err msg fname]} { + error_popup [mc "Cherry-pick failed: +file '%s' had local modifications. + +Please commit, reset or stash your changes." $fname] + } elseif {[regexp -line {^CONFLICT \(.*\):} $err msg]} { + # Force citool to read MERGE_MSG + file delete [file join [gitdir] "GITGUI_MSG"] + exec_citool {} $rowmenuid + } else { + error_popup $err + } return } set newhead [exec git rev-parse HEAD] From 887a791f113a09a56bb4916243463481a51d86a4 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sat, 8 Nov 2008 21:37:09 +1100 Subject: [PATCH 13/18] gitk: Improve cherry-pick error handling This adds to the regexps that are used to work out what sort of error we encountered in trying to do a cherry-pick so that it recognizes some additional common error messages. It adds a confirmation dialog when the error is a merge conflict so the user can choose whether or not to run git citool. Finally, it arranges to update the display after a cherry-pick failed so that any local changes made by the cherry-pick become visible. Signed-off-by: Paul Mackerras --- gitk | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/gitk b/gitk index 4168648b52..58ad47d183 100755 --- a/gitk +++ b/gitk @@ -8133,18 +8133,25 @@ proc cherrypick {} { if {[catch {exec sh -c "git cherry-pick -r $rowmenuid 2>&1"} err]} { notbusy cherrypick if {[regexp -line \ - {Entry '(.*)' would be overwritten by merge} $err msg fname]} { - error_popup [mc "Cherry-pick failed: -file '%s' had local modifications. - -Please commit, reset or stash your changes." $fname] - } elseif {[regexp -line {^CONFLICT \(.*\):} $err msg]} { - # Force citool to read MERGE_MSG - file delete [file join [gitdir] "GITGUI_MSG"] - exec_citool {} $rowmenuid + {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] From 218a900bd8efd0d49f8a0d9491aa4786a998d4f4 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 2 Nov 2008 21:59:48 +0300 Subject: [PATCH 14/18] gitk: Implement a user-friendly Edit View dialog Originally gitk required the user to specify all limiting options manually in the same field with the list of commits. It is rather unfriendly for new users, who may not know which options can be used, or, indeed, that it is possible to specify them at all. This commit modifies the dialog to present the most useful options as individual fields. Note that options that may be useful to an extent, but produce a severely broken view, are deliberately not included. It is still possible to specify options in the commit list field, but when the dialog is reopened, they will be extracted into their own separate fields. Signed-off-by: Alexander Gavrilov Signed-off-by: Paul Mackerras --- gitk | 203 ++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 161 insertions(+), 42 deletions(-) diff --git a/gitk b/gitk index 58ad47d183..2048798bcf 100755 --- a/gitk +++ b/gitk @@ -3467,8 +3467,8 @@ proc shellsplit {str} { # Code to implement multiple views proc newview {ishighlight} { - global nextviewnum newviewname newviewperm newishighlight - global newviewargs revtreeargs viewargscmd newviewargscmd curview + global nextviewnum newviewname newishighlight + global revtreeargs viewargscmd newviewopts curview set newishighlight $ishighlight set top .gitkview @@ -3477,59 +3477,173 @@ proc newview {ishighlight} { return } set newviewname($nextviewnum) "[mc "View"] $nextviewnum" - set newviewperm($nextviewnum) 0 - set newviewargs($nextviewnum) [shellarglist $revtreeargs] - set newviewargscmd($nextviewnum) $viewargscmd($curview) + set newviewopts($nextviewnum,perm) 0 + set newviewopts($nextviewnum,cmd) $viewargscmd($curview) + decode_view_opts $nextviewnum $revtreeargs vieweditor $top $nextviewnum [mc "Gitk view definition"] } +set known_view_options { + {perm b . {} {mc "Remember this view"}} + {args t50= + {} {mc "Commits to include (arguments to git log):"}} + {all b * "--all" {mc "Use all refs"}} + {dorder b . {"--date-order" "-d"} {mc "Strictly sort by date"}} + {lright b . "--left-right" {mc "Mark branch sides"}} + {since t15 + {"--since=*" "--after=*"} {mc "Since date:"}} + {until t15 . {"--until=*" "--before=*"} {mc "Until date:"}} + {limit t10 + "--max-count=*" {mc "Max count:"}} + {skip t10 . "--skip=*" {mc "Skip:"}} + {first b . "--first-parent" {mc "Limit to first parent"}} + {cmd t50= + {} {mc "Command to generate more commits to include:"}} + } + +proc encode_view_opts {n} { + global known_view_options newviewopts + + set rargs [list] + foreach opt $known_view_options { + set patterns [lindex $opt 3] + if {$patterns eq {}} continue + set pattern [lindex $patterns 0] + + set val $newviewopts($n,[lindex $opt 0]) + + if {[lindex $opt 1] eq "b"} { + if {$val} { + lappend rargs $pattern + } + } else { + set val [string trim $val] + if {$val ne {}} { + set pfix [string range $pattern 0 end-1] + lappend rargs $pfix$val + } + } + } + return [concat $rargs [shellsplit $newviewopts($n,args)]] +} + +proc decode_view_opts {n view_args} { + global known_view_options newviewopts + + foreach opt $known_view_options { + if {[lindex $opt 1] eq "b"} { + set val 0 + } else { + set val {} + } + set newviewopts($n,[lindex $opt 0]) $val + } + set oargs [list] + foreach arg $view_args { + if {[regexp -- {^-([0-9]+)$} $arg arg cnt] + && ![info exists found(limit)]} { + set newviewopts($n,limit) $cnt + set found(limit) 1 + continue + } + catch { unset val } + foreach opt $known_view_options { + set id [lindex $opt 0] + if {[info exists found($id)]} continue + foreach pattern [lindex $opt 3] { + if {![string match $pattern $arg]} continue + if {[lindex $opt 1] ne "b"} { + set size [string length $pattern] + set val [string range $arg [expr {$size-1}] end] + } else { + set val 1 + } + set newviewopts($n,$id) $val + set found($id) 1 + break + } + if {[info exists val]} break + } + if {[info exists val]} continue + lappend oargs $arg + } + set newviewopts($n,args) [shellarglist $oargs] +} + proc editview {} { global curview - global viewname viewperm newviewname newviewperm - global viewargs newviewargs viewargscmd newviewargscmd + global viewname viewperm newviewname newviewopts + global viewargs viewargscmd set top .gitkvedit-$curview if {[winfo exists $top]} { raise $top return } - set newviewname($curview) $viewname($curview) - set newviewperm($curview) $viewperm($curview) - set newviewargs($curview) [shellarglist $viewargs($curview)] - set newviewargscmd($curview) $viewargscmd($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 "Gitk: edit view $viewname($curview)" } proc vieweditor {top n title} { - global newviewname newviewperm viewfiles bgcolor + global newviewname newviewopts viewfiles bgcolor + global known_view_options toplevel $top wm title $top $title wm transient $top . + + # View name + frame $top.nfr label $top.nl -text [mc "Name"] entry $top.name -width 20 -textvariable newviewname($n) - grid $top.nl $top.name -sticky w -pady 5 - checkbutton $top.perm -text [mc "Remember this view"] \ - -variable newviewperm($n) - grid $top.perm - -pady 5 -sticky w - message $top.al -aspect 1000 \ - -text [mc "Commits to include (arguments to git log):"] - grid $top.al - -sticky w -pady 5 - entry $top.args -width 50 -textvariable newviewargs($n) \ - -background $bgcolor - grid $top.args - -sticky ew -padx 5 + pack $top.nfr -in $top -fill x -pady 5 -padx 3 + pack $top.nl -in $top.nfr -side left -padx {0 30} + pack $top.name -in $top.nfr -side left - message $top.ac -aspect 1000 \ - -text [mc "Command to generate more commits to include:"] - grid $top.ac - -sticky w -pady 5 - entry $top.argscmd -width 50 -textvariable newviewargscmd($n) \ - -background white - grid $top.argscmd - -sticky ew -padx 5 + # View options + set cframe $top.nfr + set cexpand 0 + set cnt 0 + foreach opt $known_view_options { + set id [lindex $opt 0] + set type [lindex $opt 1] + set flags [lindex $opt 2] + set title [eval [lindex $opt 4]] + set lxpad 0 - message $top.l -aspect 1000 \ + if {$flags eq "+" || $flags eq "*"} { + set cframe $top.fr$cnt + incr cnt + frame $cframe + pack $cframe -in $top -fill x -pady 3 -padx 3 + set cexpand [expr {$flags eq "*"}] + } else { + set lxpad 5 + } + + if {$type eq "b"} { + 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 {^t(\d+)$} $type type sz]} { + message $cframe.l_$id -aspect 1500 -text $title + 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 \ + -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 + } + } + + # Path list + message $top.l -aspect 1500 \ -text [mc "Enter files and directories to include, one per line:"] - grid $top.l - -sticky w - text $top.t -width 40 -height 10 -background $bgcolor -font uifont + pack $top.l -in $top -side top -pady [list 7 0] -anchor w -padx 3 + text $top.t -width 40 -height 5 -background $bgcolor -font uifont if {[info exists viewfiles($n)]} { foreach f $viewfiles($n) { $top.t insert end $f @@ -3538,15 +3652,19 @@ proc vieweditor {top n title} { $top.t delete {end - 1c} end $top.t mark set insert 0.0 } - grid $top.t - -sticky ew -padx 5 + pack $top.t -in $top -side top -pady [list 0 5] -fill both -expand 1 -padx 3 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] + bind $top [list newviewok $top $n] + bind $top [list newviewok $top $n 1] bind $top [list destroy $top] - grid $top.buts.ok $top.buts.can + grid $top.buts.ok $top.buts.apply $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 + grid columnconfigure $top.buts 2 -weight 1 -uniform a + pack $top.buts -in $top -side top -fill x focus $top.t } @@ -3567,13 +3685,13 @@ proc allviewmenus {n op args} { # doviewmenu $viewhlmenu 1 [list addvhighlight $n] $op $args } -proc newviewok {top n} { +proc newviewok {top n {apply 0}} { global nextviewnum newviewperm newviewname newishighlight global viewname viewfiles viewperm selectedview curview - global viewargs newviewargs viewargscmd newviewargscmd viewhlmenu + global viewargs viewargscmd newviewopts viewhlmenu if {[catch { - set newargs [shellsplit $newviewargs($n)] + set newargs [encode_view_opts $n] } err]} { error_popup "[mc "Error in commit selection arguments:"] $err" $top return @@ -3589,10 +3707,10 @@ proc newviewok {top n} { # creating a new view incr nextviewnum set viewname($n) $newviewname($n) - set viewperm($n) $newviewperm($n) + set viewperm($n) $newviewopts($n,perm) set viewfiles($n) $files set viewargs($n) $newargs - set viewargscmd($n) $newviewargscmd($n) + set viewargscmd($n) $newviewopts($n,cmd) addviewmenu $n if {!$newishighlight} { run showview $n @@ -3601,7 +3719,7 @@ proc newviewok {top n} { } } else { # editing an existing view - set viewperm($n) $newviewperm($n) + set viewperm($n) $newviewopts($n,perm) if {$newviewname($n) ne $viewname($n)} { set viewname($n) $newviewname($n) doviewmenu .bar.view 5 [list showview $n] \ @@ -3610,15 +3728,16 @@ proc newviewok {top n} { # entryconf [list -label $viewname($n) -value $viewname($n)] } if {$files ne $viewfiles($n) || $newargs ne $viewargs($n) || \ - $newviewargscmd($n) ne $viewargscmd($n)} { + $newviewopts($n,cmd) ne $viewargscmd($n)} { set viewfiles($n) $files set viewargs($n) $newargs - set viewargscmd($n) $newviewargscmd($n) + set viewargscmd($n) $newviewopts($n,cmd) if {$curview == $n} { run reloadcommits } } } + if {$apply} return catch {destroy $top} } From cea07cf8dc9b3677e0c50433c0d72bce83adbdc7 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 9 Nov 2008 13:00:45 +0300 Subject: [PATCH 15/18] gitk: Add accelerators to frequently used menu commands This commit documents keyboard accelerators used for menu commands in the menu, as it is usually done, and adds some more, e.g. F4 to invoke Edit View (or New View if the current view is the un-editable "All files" view). The changes include a workaround for handling Shift-F4 on systems where XKB binds special XF86_Switch_VT_* symbols to Ctrl-Alt-F* combinations. Tk often receives these codes when Shift-F* is pressed, so it is necessary to bind the relevant actions to them as well. Signed-off-by: Alexander Gavrilov Signed-off-by: Paul Mackerras --- gitk | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/gitk b/gitk index 18d153e569..9b2a6e5445 100755 --- a/gitk +++ b/gitk @@ -1801,6 +1801,11 @@ proc setoptions {} { # command to invoke for command, or {variable value} for radiobutton proc makemenu {m items} { menu $m + if {[tk windowingsystem] eq {aqua}} { + set Meta1 Cmd + } else { + set Meta1 Ctrl + } foreach i $items { set name [mc [lindex $i 1]] set type [lindex $i 2] @@ -1826,7 +1831,9 @@ proc makemenu {m items} { -value [lindex $thing 1] } } - eval $m add $params [lrange $i 4 end] + set tail [lrange $i 4 end] + regsub -all {\yMeta1\y} $tail $Meta1 tail + eval $m add $params $tail if {$type eq "cascade"} { makemenu $m.$submenu $thing } @@ -1860,17 +1867,17 @@ proc makewindow {} { makemenu .bar { {mc "File" cascade { {mc "Update" command updatecommits -accelerator F5} - {mc "Reload" command reloadcommits} + {mc "Reload" command reloadcommits -accelerator Meta1-F5} {mc "Reread references" command rereadrefs} - {mc "List references" command showrefs} - {mc "Quit" command doquit} + {mc "List references" command showrefs -accelerator F2} + {mc "Quit" command doquit -accelerator Meta1-Q} }} {mc "Edit" cascade { {mc "Preferences" command doprefs} }} {mc "View" cascade { - {mc "New view..." command {newview 0}} - {mc "Edit view..." command editview -state disabled} + {mc "New view..." command {newview 0} -accelerator Shift-F4} + {mc "Edit view..." command editview -state disabled -accelerator F4} {mc "Delete view" command delview -state disabled} {xx "" separator} {mc "All files" radiobutton {selectedview 0} -command {showview 0}} @@ -2232,7 +2239,12 @@ proc makewindow {} { bindkey {dofind 1 1} bindkey ? {dofind -1 1} bindkey f nextfile - bindkey updatecommits + bind . updatecommits + bind . <$M1B-F5> reloadcommits + bind . showrefs + bind . {newview 0} + catch { bind . {newview 0} } + bind . edit_or_newview bind . <$M1B-q> doquit bind . <$M1B-f> {dofind 1 1} bind . <$M1B-g> {dofind 1 0} @@ -3624,6 +3636,16 @@ proc decode_view_opts {n view_args} { set newviewopts($n,args) [shellarglist $oargs] } +proc edit_or_newview {} { + global curview + + if {$curview > 0} { + editview + } else { + newview 0 + } +} + proc editview {} { global curview global viewname viewperm newviewname newviewopts From e7d640086e0d3eb332542da287c40262c6eae207 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 11 Nov 2008 23:55:42 +0300 Subject: [PATCH 16/18] gitk: Fix transient windows on Win32 and MacOS Transient windows cause problems on these platforms: - On Win32 the windows appear in the top left corner of the screen. In order to fix it, this patch causes them to be explicitly centered on their parents by an idle handler. - On MacOS with Tk 8.4 they appear without a title bar. Since it is clearly unacceptable, this patch disables transient on that platform. Signed-off-by: Alexander Gavrilov Signed-off-by: Paul Mackerras --- gitk | 44 +++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/gitk b/gitk index 9b2a6e5445..e6aafe8a68 100755 --- a/gitk +++ b/gitk @@ -1739,6 +1739,24 @@ proc removehead {id name} { unset headids($name) } +proc make_transient {window origin} { + global have_tk85 + + # In MacOS Tk 8.4 transient appears to work by setting + # overrideredirect, which is utterly useless, since the + # windows get no border, and are not even kept above + # the parent. + if {!$have_tk85 && [tk windowingsystem] eq {aqua}} return + + wm transient $window $origin + + # Windows fails to place transient windows normally, so + # schedule a callback to center them on the parent. + if {[tk windowingsystem] eq {win32}} { + after idle [list tk::PlaceWindow $window widget $origin] + } +} + proc show_error {w top msg} { message $w.m -text $msg -justify center -aspect 400 pack $w.m -side top -fill x -padx 20 -pady 20 @@ -1754,7 +1772,7 @@ proc show_error {w top msg} { proc error_popup {msg {owner .}} { set w .error toplevel $w - wm transient $w $owner + make_transient $w $owner show_error $w $w $msg } @@ -1763,7 +1781,7 @@ proc confirm_popup {msg {owner .}} { set confirm_ok 0 set w .confirm toplevel $w - wm transient $w $owner + 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" @@ -2558,7 +2576,7 @@ proc about {} { } toplevel $w wm title $w [mc "About gitk"] - wm transient $w . + make_transient $w . message $w.m -text [mc " Gitk - a commit viewer for git @@ -2587,7 +2605,7 @@ proc keys {} { } toplevel $w wm title $w [mc "Gitk key bindings"] - wm transient $w . + make_transient $w . message $w.m -text " [mc "Gitk key bindings:"] @@ -3669,7 +3687,7 @@ proc vieweditor {top n title} { toplevel $top wm title $top $title - wm transient $top . + make_transient $top . # View name frame $top.nfr @@ -7912,7 +7930,7 @@ proc mkpatch {} { set patchtop $top catch {destroy $top} toplevel $top - wm transient $top . + make_transient $top . label $top.title -text [mc "Generate patch"] grid $top.title - -pady 10 label $top.from -text [mc "From:"] @@ -7999,7 +8017,7 @@ proc mktag {} { set mktagtop $top catch {destroy $top} toplevel $top - wm transient $top . + make_transient $top . label $top.title -text [mc "Create tag"] grid $top.title - -pady 10 label $top.id -text [mc "ID:"] @@ -8102,7 +8120,7 @@ proc writecommit {} { set wrcomtop $top catch {destroy $top} toplevel $top - wm transient $top . + make_transient $top . label $top.title -text [mc "Write commit to file"] grid $top.title - -pady 10 label $top.id -text [mc "ID:"] @@ -8159,7 +8177,7 @@ proc mkbranch {} { set top .makebranch catch {destroy $top} toplevel $top - wm transient $top . + make_transient $top . label $top.title -text [mc "Create new branch"] grid $top.title - -pady 10 label $top.id -text [mc "ID:"] @@ -8322,7 +8340,7 @@ proc resethead {} { set confirm_ok 0 set w ".confirmreset" toplevel $w - wm transient $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]] \ @@ -8502,7 +8520,7 @@ proc showrefs {} { } toplevel $top wm title $top [mc "Tags and heads: %s" [file tail [pwd]]] - wm transient $top . + make_transient $top . text $top.list -background $bgcolor -foreground $fgcolor \ -selectbackground $selectbgcolor -font mainfont \ -xscrollcommand "$top.xsb set" -yscrollcommand "$top.ysb set" \ @@ -9844,7 +9862,7 @@ proc choosefont {font which} { font create sample eval font config sample [font actual $font] toplevel $top - wm transient $top $prefstop + make_transient $top $prefstop wm title $top [mc "Gitk font chooser"] label $top.l -textvariable fontparam(which) pack $top.l -side top @@ -9961,7 +9979,7 @@ proc doprefs {} { } toplevel $top wm title $top [mc "Gitk preferences"] - wm transient $top . + make_transient $top . label $top.ldisp -text [mc "Commit list display options"] grid $top.ldisp - -sticky w -pady 10 label $top.spacer -text " " From 590915dab8aaf464f5819c4ccec7941c3490e3ce Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 9 Nov 2008 18:06:07 +0300 Subject: [PATCH 17/18] gitk: Fix commit encoding support This commit fixes two problems with commit encodings: 1) git-log actually uses i18n.logoutputencoding to generate its output, and falls back to i18n.commitencoding only when that option is not set. Thus, gitk should use its value to read the results, if available. 2) The readcommit function did not process encodings at all. This led to randomly appearing misconverted commits if the commit encoding differed from the current locale. Now commit messages should be displayed correctly, except when logoutputencoding is set to an encoding that cannot represent charecters in the message. For example, it is impossible to convert Japanese characters from Shift-JIS to CP-1251 (although the reverse conversion works). The reason for using git log to read the commit and then getting Tcl to convert its output is that is essentially what happens in the normal path through getcommitlines, hence there is less chance for unintended differences in how commits are processed in getcommitlines and do_readcommit. Signed-off-by: Alexander Gavrilov Signed-off-by: Paul Mackerras --- gitk | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/gitk b/gitk index e6aafe8a68..3864d3826d 100755 --- a/gitk +++ b/gitk @@ -1555,9 +1555,27 @@ proc chewcommits {} { return 0 } +proc do_readcommit {id} { + global tclencoding + + # Invoke git-log to handle automatic encoding conversion + set fd [open [concat | git log --no-color --pretty=raw -1 $id] r] + # Read the results using i18n.logoutputencoding + fconfigure $fd -translation lf -eofchar {} + if {$tclencoding != {}} { + fconfigure $fd -encoding $tclencoding + } + set contents [read $fd] + close $fd + # Remove the heading line + regsub {^commit [0-9a-f]+\n} $contents {} contents + + return $contents +} + proc readcommit {id} { - if {[catch {set contents [exec git cat-file commit $id]}]} return - parsecommit $id $contents 0 + if {[catch {set contents [do_readcommit $id]}]} return + parsecommit $id $contents 1 } proc parsecommit {id contents listed} { @@ -10558,6 +10576,9 @@ set gitencoding {} catch { set gitencoding [exec git config --get i18n.commitencoding] } +catch { + set gitencoding [exec git config --get i18n.logoutputencoding] +} if {$gitencoding == ""} { set gitencoding "utf-8" } From d98d50e214643379734ac3ffe891770457f1c419 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Thu, 13 Nov 2008 22:39:00 +1100 Subject: [PATCH 18/18] gitk: Fix linehtag undefined error with file highlighting Occasionally gitk will throw a Tcl error complaining that linehtag(n) is undefined when. It happens when the commit list is still growing (e.g. when updating the commit list) and gitk is set to highlight commits that affect certain file(s). What happens is that the changes to the commit list set need_redisplay to indicate that the display needs to be redrawn. That causes the next call to drawcommits to call clear_display, which unsets iddrawn and thus ensures that readfhighlight won't call bolden on any rows that have moved. However, it is possible for readfhighlight to be called after the commit list has changed but before drawcommits has run, meaning that readfhighlight will potentially think that rows have been drawn when they haven't, because of the change in the id -> row mapping (and the fact that iddrawn is indexed by id but line[hnd]tag are indexed by row number). This fixes it (and also optimizes things a little) by making bolden and bolden_name check need_redisplay before doing anything. If need_redisplay is set, then there is no point doing anything because the whole display is about to get cleared and redrawn, and it avoids looking up line[hn]tag using stale row numbers. Signed-off-by: Paul Mackerras --- gitk | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/gitk b/gitk index 3864d3826d..3353f4a271 100755 --- a/gitk +++ b/gitk @@ -4005,8 +4005,10 @@ proc ishighlighted {id} { } proc bolden {row font} { - global canv linehtag selectedline boldrows + global canv linehtag selectedline boldrows need_redisplay + # need_redisplay = 1 means the display is stale and about to be redrawn + if {$need_redisplay} return lappend boldrows $row $canv itemconf $linehtag($row) -font $font if {$row == $selectedline} { @@ -4019,8 +4021,9 @@ proc bolden {row font} { } proc bolden_name {row font} { - global canv2 linentag selectedline boldnamerows + global canv2 linentag selectedline boldnamerows need_redisplay + if {$need_redisplay} return lappend boldnamerows $row $canv2 itemconf $linentag($row) -font $font if {$row == $selectedline} {