From a1a4975824e8f9f88a8c96ae908c488a2c6047c5 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 8 May 2007 13:33:06 +0200 Subject: [PATCH 01/13] git-gui: Call changes "Staged" and "Unstaged" in file list titles. All menu entries talk about "staging" and "unstaging" changes, but the titles of the file lists use different wording, which may confuse newcomers. Signed-off-by: Johannes Sixt Signed-off-by: Shawn O. Pearce --- git-gui.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index ae881336da..4cf340dfe7 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -5496,7 +5496,7 @@ pack .vpane -anchor n -side top -fill both -expand 1 # -- Index File List # frame .vpane.files.index -height 100 -width 200 -label .vpane.files.index.title -text {Changes To Be Committed} \ +label .vpane.files.index.title -text {Staged Changes (Will Be Committed)} \ -background green \ -font font_ui text $ui_index -background white -borderwidth 0 \ @@ -5518,7 +5518,7 @@ pack $ui_index -side left -fill both -expand 1 # -- Working Directory File List # frame .vpane.files.workdir -height 100 -width 200 -label .vpane.files.workdir.title -text {Changed But Not Updated} \ +label .vpane.files.workdir.title -text {Unstaged Changes (Will Not Be Committed)} \ -background red \ -font font_ui text $ui_workdir -background white -borderwidth 0 \ From cc1f83fbdff1ae248c91ab231ab4100351e1ba0a Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 8 May 2007 21:38:46 -0400 Subject: [PATCH 02/13] git-gui: Allow shift-{k,j} to select a range of branches to merge I found it useful to be able to use j/k (vi-like keys) to move up and down the list of branches to merge and shift-j/k to do the selection, much as shift-up/down (arrow keys) would alter the selection. Signed-off-by: Shawn O. Pearce --- lib/merge.tcl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/merge.tcl b/lib/merge.tcl index 3dce856e5e..642d6eefbd 100644 --- a/lib/merge.tcl +++ b/lib/merge.tcl @@ -238,6 +238,8 @@ proc dialog {} { $subj([lindex $ref 0])] } + bind $w.source.l [list event generate %W ] + bind $w.source.l [list event generate %W ] bind $w.source.l [list event generate %W ] bind $w.source.l [list event generate %W ] bind $w.source.l [list event generate %W ] From 1f07c4e5cefec88d825045ade24eee71f6a2df47 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 8 May 2007 19:54:05 -0400 Subject: [PATCH 03/13] git-gui: Define a simple class/method system As most of the git-gui interface is based upon "meta-widgets" that need to carry around a good deal of state (e.g. console windows, browser windows, blame viewer) we have a good deal of messy code that tries to store this meta-widget state in global arrays, where keys into the array are formed from a union of a unique "object instance id" and the field name. This is a simple class system for Tcl that allows us to hide much of that mess by making Tcl do what it does best; process strings to manipulate its own code during startup. Each object instance is placed into its own namespace. The namespace is created when the object instance is created and the namespace is destroyed when the object instance is removed from the system. Within that namespace we place variables for each field within the class; these variables can themselves be scalar values or full-blown Tcl arrays. A simple class might be defined as: class map { field data field size 0 constructor {} { return $this } method set {name value} { set data($name) $value incr size } method size {} { return $size } ifdeleted { return 0 } } All fields must be declared before any constructors or methods. This allows our class to generate a list of the fields so it can properly alter the definition of the constructor and method bodies prior to passing them off to Tcl for definition with proc. A field may optionally be given a default/initial value. This can only be done for non-array type fields. Constructors are given full access to all fields of the class, so they can initialize the data values. The default values of fields (if any) are set before the constructor runs, and the implicit local variable $this is initialized to the instance identifier. Methods are given access to fields they actually use in their body. Every method has an implicit "this" argument inserted as its first parameter; callers of methods must be sure they supply this value. Some basic optimization tricks are performed (but not much). We try to only upvar (locally bind) fields that are accessed within a method, but we err on the side of caution and may upvar more than we need to. If a variable is accessed only once within a method and that access is by $foo (read) we avoid the upvar and instead use [set foo] to obtain the value. This is slightly faster as Tcl does not need to lookup the variable twice. We also offer some small syntatic sugar for interacting with Tk and the fileevent callback system in Tcl. If a field (say "foo") is used as "@foo" we insert instead the true global variable name of that variable into the body of the constructor or method. This allows easy binding to Tk textvariable options, e.g.: label $w.title -textvariable @title Proper namespace callbacks can also be setup with the special cb proc that is defined in each namespace. [cb _foo a] will invoke the method _foo in the current namespace, passing it $this as the first (implied) parameter and a as the second parameter. This makes it very simple to connect an object instance to a -command option for a Tk widget or to a fileevent readable or writable for a file channel. Signed-off-by: Shawn O. Pearce --- Makefile | 5 +- lib/class.tcl | 153 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 lib/class.tcl diff --git a/Makefile b/Makefile index ba1e33ba84..e73b6453d9 100644 --- a/Makefile +++ b/Makefile @@ -64,7 +64,10 @@ $(GITGUI_BUILT_INS): git-gui $(QUIET_BUILT_IN)rm -f $@ && ln git-gui $@ lib/tclIndex: $(ALL_LIBFILES) - $(QUIET_INDEX)echo auto_mkindex lib '*.tcl' | $(TCL_PATH) + $(QUIET_INDEX)echo \ + source lib/class.tcl \; \ + auto_mkindex lib '*.tcl' \ + | $(TCL_PATH) # These can record GITGUI_VERSION $(patsubst %.sh,%,$(SCRIPT_SH)): GIT-VERSION-FILE GIT-GUI-VARS diff --git a/lib/class.tcl b/lib/class.tcl new file mode 100644 index 0000000000..c1291989aa --- /dev/null +++ b/lib/class.tcl @@ -0,0 +1,153 @@ +# git-gui simple class/object fake-alike +# Copyright (C) 2007 Shawn Pearce + +proc class {class body} { + if {[namespace exists $class]} { + error "class $class already declared" + } + namespace eval $class { + variable __nextid 0 + variable __sealed 0 + variable __field_list {} + variable __field_array + + proc cb {name args} { + upvar this this + set args [linsert $args 0 $name $this] + return [uplevel [list namespace code $args]] + } + } + namespace eval $class $body +} + +proc field {name args} { + set class [uplevel {namespace current}] + variable ${class}::__sealed + variable ${class}::__field_array + + switch [llength $args] { + 0 { set new [list $name] } + 1 { set new [list $name [lindex $args 0]] } + default { error "wrong # args: field name value?" } + } + + if {$__sealed} { + error "class $class is sealed (cannot add new fields)" + } + + if {[catch {set old $__field_array($name)}]} { + variable ${class}::__field_list + lappend __field_list $new + set __field_array($name) 1 + } else { + error "field $name already declared" + } +} + +proc constructor {name params body} { + set class [uplevel {namespace current}] + set ${class}::__sealed 1 + variable ${class}::__field_list + set mbodyc {} + + append mbodyc {set this } $class + append mbodyc {::__o[incr } $class {::__nextid]} \; + append mbodyc {namespace eval $this {}} \; + + if {$__field_list ne {}} { + append mbodyc {upvar #0} + foreach n $__field_list { + set n [lindex $n 0] + append mbodyc { ${this}::} $n { } $n + regsub -all @$n\\M $body "\${this}::$n" body + } + append mbodyc \; + foreach n $__field_list { + if {[llength $n] == 2} { + append mbodyc \ + {set } [lindex $n 0] { } [list [lindex $n 1]] \; + } + } + } + append mbodyc $body + namespace eval $class [list proc $name $params $mbodyc] +} + +proc method {name params body {deleted {}} {del_body {}}} { + set class [uplevel {namespace current}] + set ${class}::__sealed 1 + variable ${class}::__field_list + set params [linsert $params 0 this] + set mbodyc {} + + switch $deleted { + {} {} + ifdeleted { + append mbodyc {if {![namespace exists $this]} } + append mbodyc \{ $del_body \; return \} \; + } + default { + error "wrong # args: method name args body (ifdeleted body)?" + } + } + + set decl {} + foreach n $__field_list { + set n [lindex $n 0] + if {[regexp -- $n\\M $body]} { + if { [regexp -all -- $n\\M $body] == 1 + && [regexp -all -- \\\$$n\\M $body] == 1} { + regsub -all \\\$$n\\M $body "\[set \${this}::$n\]" body + } else { + append decl { ${this}::} $n { } $n + regsub -all @$n\\M $body "\${this}::$n" body + } + } + } + if {$decl ne {}} { + append mbodyc {upvar #0} $decl \; + } + append mbodyc $body + namespace eval $class [list proc $name $params $mbodyc] +} + +proc delete_this {{t {}}} { + if {$t eq {}} { + upvar this this + set t $this + } + if {[namespace exists $t]} {namespace delete $t} +} + +proc make_toplevel {t w} { + upvar $t top $w pfx + if {[winfo ismapped .]} { + upvar this this + regsub -all {::} $this {__} w + set top .$w + set pfx $top + toplevel $top + } else { + set top . + set pfx {} + } +} + + +## auto_mkindex support for class/constructor/method +## +auto_mkindex_parser::command class {name body} { + variable parser + variable contextStack + set contextStack [linsert $contextStack 0 $name] + $parser eval [list _%@namespace eval $name] $body + set contextStack [lrange $contextStack 1 end] +} +auto_mkindex_parser::command constructor {name args} { + variable index + variable scriptFile + append index [list set auto_index([fullname $name])] \ + [format { [list source [file join $dir %s]]} \ + [file split $scriptFile]] "\n" +} + From c74b6c66f00ce51b2c01d20fc5ef0e6429811124 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 8 May 2007 20:33:47 -0400 Subject: [PATCH 04/13] git-gui: Convert browser, console to "class" format Now that we have a slightly easier method of working with per-widget data we should make use of that technique in our browser and console meta-widgets, as both have a decent amount of information that they store on a per-widget basis and our current approach of handling it is difficult to follow. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 4 +- lib/blame.tcl | 2 + lib/browser.tcl | 196 +++++++++++++++++++++-------------------------- lib/console.tcl | 97 +++++++++-------------- lib/database.tcl | 12 +-- lib/merge.tcl | 3 +- 6 files changed, 134 insertions(+), 180 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 84a3b62011..67f993aa4b 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1288,7 +1288,7 @@ menu .mbar.repository .mbar.repository add command \ -label {Browse Current Branch} \ - -command {new_browser $current_branch} + -command {browser::new $current_branch} trace add variable current_branch write ".mbar.repository entryconf [.mbar.repository index last] -label \"Browse \$current_branch\" ;#" .mbar.repository add separator @@ -1581,7 +1581,7 @@ browser { exit 1 } set current_branch [lindex $argv 0] - new_browser $current_branch + browser::new $current_branch return } blame { diff --git a/lib/blame.tcl b/lib/blame.tcl index 6d894e52d4..dc7b22f7b5 100644 --- a/lib/blame.tcl +++ b/lib/blame.tcl @@ -1,6 +1,8 @@ # git-gui blame viewer # Copyright (C) 2006, 2007 Shawn Pearce +set next_browser_id 0 + proc show_blame {commit path} { global next_browser_id blame_status blame_data diff --git a/lib/browser.tcl b/lib/browser.tcl index 631859ae75..f9a4e1d52c 100644 --- a/lib/browser.tcl +++ b/lib/browser.tcl @@ -1,28 +1,26 @@ # git-gui tree browser # Copyright (C) 2006, 2007 Shawn Pearce -set next_browser_id 0 +class browser { -proc new_browser {commit} { - global next_browser_id cursor_ptr M1B - global browser_commit browser_status browser_stack browser_path browser_busy +field w +field browser_commit +field browser_path +field browser_files {} +field browser_status {Starting...} +field browser_stack {} +field browser_busy 1 - if {[winfo ismapped .]} { - set w .browser[incr next_browser_id] - set tl $w - toplevel $w - } else { - set w {} - set tl . - } - set w_list $w.list.l - set browser_commit($w_list) $commit - set browser_status($w_list) {Starting...} - set browser_stack($w_list) {} - set browser_path($w_list) $browser_commit($w_list): - set browser_busy($w_list) 1 +constructor new {commit} { + global cursor_ptr M1B + make_toplevel top w + wm title $top "[appname] ([reponame]): File Browser" - label $w.path -textvariable browser_path($w_list) \ + set browser_commit $commit + set browser_path $browser_commit: + + label $w.path \ + -textvariable @browser_path \ -anchor w \ -justify left \ -borderwidth 1 \ @@ -31,6 +29,7 @@ proc new_browser {commit} { pack $w.path -anchor w -side top -fill x frame $w.list + set w_list $w.list.l text $w_list -background white -borderwidth 0 \ -cursor $cursor_ptr \ -state disabled \ @@ -49,176 +48,149 @@ proc new_browser {commit} { pack $w_list -side left -fill both -expand 1 pack $w.list -side top -fill both -expand 1 - label $w.status -textvariable browser_status($w_list) \ + label $w.status \ + -textvariable @browser_status \ -anchor w \ -justify left \ -borderwidth 1 \ -relief sunken pack $w.status -anchor w -side bottom -fill x - bind $w_list "browser_click 0 $w_list @%x,%y;break" - bind $w_list "browser_click 1 $w_list @%x,%y;break" - bind $w_list <$M1B-Up> "browser_parent $w_list;break" - bind $w_list <$M1B-Left> "browser_parent $w_list;break" - bind $w_list "browser_move -1 $w_list;break" - bind $w_list "browser_move 1 $w_list;break" - bind $w_list <$M1B-Right> "browser_enter $w_list;break" - bind $w_list "browser_enter $w_list;break" - bind $w_list "browser_page -1 $w_list;break" - bind $w_list "browser_page 1 $w_list;break" + bind $w_list "[cb _click 0 @%x,%y];break" + bind $w_list "[cb _click 1 @%x,%y];break" + bind $w_list <$M1B-Up> "[cb _parent] ;break" + bind $w_list <$M1B-Left> "[cb _parent] ;break" + bind $w_list "[cb _move -1] ;break" + bind $w_list "[cb _move 1] ;break" + bind $w_list <$M1B-Right> "[cb _enter] ;break" + bind $w_list "[cb _enter] ;break" + bind $w_list "[cb _page -1] ;break" + bind $w_list "[cb _page 1] ;break" bind $w_list break bind $w_list break - bind $tl "focus $w" - bind $tl " - array unset browser_buffer $w_list - array unset browser_files $w_list - array unset browser_status $w_list - array unset browser_stack $w_list - array unset browser_path $w_list - array unset browser_commit $w_list - array unset browser_busy $w_list - " - wm title $tl "[appname] ([reponame]): File Browser" - ls_tree $w_list $browser_commit($w_list) {} + bind $w_list [list focus $w_list] + bind $w_list [list delete_this $this] + set w $w_list + _ls $this $browser_commit + return $this } -proc browser_move {dir w} { - global browser_files browser_busy - - if {$browser_busy($w)} return +method _move {dir} { + if {$browser_busy} return set lno [lindex [split [$w index in_sel.first] .] 0] incr lno $dir - if {[lindex $browser_files($w) [expr {$lno - 1}]] ne {}} { + if {[lindex $browser_files [expr {$lno - 1}]] ne {}} { $w tag remove in_sel 0.0 end $w tag add in_sel $lno.0 [expr {$lno + 1}].0 $w see $lno.0 } } -proc browser_page {dir w} { - global browser_files browser_busy - - if {$browser_busy($w)} return +method _page {dir} { + if {$browser_busy} return $w yview scroll $dir pages set lno [expr {int( [lindex [$w yview] 0] - * [llength $browser_files($w)] + * [llength $browser_files] + 1)}] - if {[lindex $browser_files($w) [expr {$lno - 1}]] ne {}} { + if {[lindex $browser_files [expr {$lno - 1}]] ne {}} { $w tag remove in_sel 0.0 end $w tag add in_sel $lno.0 [expr {$lno + 1}].0 $w see $lno.0 } } -proc browser_parent {w} { - global browser_files browser_status browser_path - global browser_stack browser_busy - - if {$browser_busy($w)} return - set info [lindex $browser_files($w) 0] +method _parent {} { + if {$browser_busy} return + set info [lindex $browser_files 0] if {[lindex $info 0] eq {parent}} { - set parent [lindex $browser_stack($w) end-1] - set browser_stack($w) [lrange $browser_stack($w) 0 end-2] - if {$browser_stack($w) eq {}} { - regsub {:.*$} $browser_path($w) {:} browser_path($w) + set parent [lindex $browser_stack end-1] + set browser_stack [lrange $browser_stack 0 end-2] + if {$browser_stack eq {}} { + regsub {:.*$} $browser_path {:} browser_path } else { - regsub {/[^/]+$} $browser_path($w) {} browser_path($w) + regsub {/[^/]+$} $browser_path {} browser_path } - set browser_status($w) "Loading $browser_path($w)..." - ls_tree $w [lindex $parent 0] [lindex $parent 1] + set browser_status "Loading $browser_path..." + _ls $this [lindex $parent 0] [lindex $parent 1] } } -proc browser_enter {w} { - global browser_files browser_status browser_path - global browser_commit browser_stack browser_busy - - if {$browser_busy($w)} return +method _enter {} { + if {$browser_busy} return set lno [lindex [split [$w index in_sel.first] .] 0] - set info [lindex $browser_files($w) [expr {$lno - 1}]] + set info [lindex $browser_files [expr {$lno - 1}]] if {$info ne {}} { switch -- [lindex $info 0] { parent { - browser_parent $w + _parent $this } tree { set name [lindex $info 2] set escn [escape_path $name] - set browser_status($w) "Loading $escn..." - append browser_path($w) $escn - ls_tree $w [lindex $info 1] $name + set browser_status "Loading $escn..." + append browser_path $escn + _ls $this [lindex $info 1] $name } blob { set name [lindex $info 2] set p {} - foreach n $browser_stack($w) { + foreach n $browser_stack { append p [lindex $n 1] } append p $name - show_blame $browser_commit($w) $p + show_blame $browser_commit $p } } } } -proc browser_click {was_double_click w pos} { - global browser_files browser_busy - - if {$browser_busy($w)} return +method _click {was_double_click pos} { + if {$browser_busy} return set lno [lindex [split [$w index $pos] .] 0] focus $w - if {[lindex $browser_files($w) [expr {$lno - 1}]] ne {}} { + if {[lindex $browser_files [expr {$lno - 1}]] ne {}} { $w tag remove in_sel 0.0 end $w tag add in_sel $lno.0 [expr {$lno + 1}].0 if {$was_double_click} { - browser_enter $w + _enter $this } } } -proc ls_tree {w tree_id name} { - global browser_buffer browser_files browser_stack browser_busy - - set browser_buffer($w) {} - set browser_files($w) {} - set browser_busy($w) 1 +method _ls {tree_id {name {}}} { + set browser_buffer {} + set browser_files {} + set browser_busy 1 $w conf -state normal $w tag remove in_sel 0.0 end $w delete 0.0 end - if {$browser_stack($w) ne {}} { + if {$browser_stack ne {}} { $w image create end \ -align center -padx 5 -pady 1 \ -name icon0 \ -image file_uplevel $w insert end {[Up To Parent]} - lappend browser_files($w) parent + lappend browser_files parent } - lappend browser_stack($w) [list $tree_id $name] + lappend browser_stack [list $tree_id $name] $w conf -state disabled set cmd [list git ls-tree -z $tree_id] set fd [open "| $cmd" r] fconfigure $fd -blocking 0 -translation binary -encoding binary - fileevent $fd readable [list read_ls_tree $fd $w] + fileevent $fd readable [cb _read $fd] } -proc read_ls_tree {fd w} { - global browser_buffer browser_files browser_status browser_busy +method _read {fd} { + append browser_buffer [read $fd] + set pck [split $browser_buffer "\0"] + set browser_buffer [lindex $pck end] - if {![winfo exists $w]} { - catch {close $fd} - return - } - - append browser_buffer($w) [read $fd] - set pck [split $browser_buffer($w) "\0"] - set browser_buffer($w) [lindex $pck end] - - set n [llength $browser_files($w)] + set n [llength $browser_files] $w conf -state normal foreach p [lrange $pck 0 end-1] { set info [split $p "\t"] @@ -246,18 +218,22 @@ proc read_ls_tree {fd w} { -name icon[incr n] \ -image $image $w insert end [escape_path $path] - lappend browser_files($w) [list $type $object $path] + lappend browser_files [list $type $object $path] } $w conf -state disabled if {[eof $fd]} { close $fd - set browser_status($w) Ready. - set browser_busy($w) 0 - array unset browser_buffer $w + set browser_status Ready. + set browser_busy 0 + unset browser_buffer if {$n > 0} { $w tag add in_sel 1.0 2.0 focus -force $w } } +} ifdeleted { + catch {close $fd} +} + } diff --git a/lib/console.tcl b/lib/console.tcl index 75f3e0463b..8c112f3a89 100644 --- a/lib/console.tcl +++ b/lib/console.tcl @@ -1,30 +1,29 @@ # git-gui console support # Copyright (C) 2006, 2007 Shawn Pearce -namespace eval console { +class console { -variable next_console_id 0 -variable console_data -variable console_cr +field t_short +field t_long +field w +field console_cr -proc new {short_title long_title} { - variable next_console_id - variable console_data - - set w .console[incr next_console_id] - set console_data($w) [list $short_title $long_title] - return [_init $w] +constructor new {short_title long_title} { + set t_short $short_title + set t_long $long_title + _init $this + return $this } -proc _init {w} { +method _init {} { global M1B - variable console_cr - variable console_data + make_toplevel top w + wm title $top "[appname] ([reponame]): $t_short" + set console_cr 1.0 - set console_cr($w) 1.0 - toplevel $w frame $w.m - label $w.m.l1 -text "[lindex $console_data($w) 1]:" \ + label $w.m.l1 \ + -textvariable @t_long \ -anchor w \ -justify left \ -font font_uibold @@ -67,11 +66,9 @@ proc _init {w} { bind $w.m.t <$M1B-Key-a> "$w.m.t tag add sel 0.0 end;break" bind $w.m.t <$M1B-Key-A> "$w.m.t tag add sel 0.0 end;break" bind $w "focus $w" - wm title $w "[appname] ([reponame]): [lindex $console_data($w) 0]" - return $w } -proc exec {w cmd {after {}}} { +method exec {cmd {after {}}} { # -- Cygwin's Tcl tosses the enviroment when we exec our child. # But most users need that so we have to relogin. :-( # @@ -86,16 +83,13 @@ proc exec {w cmd {after {}}} { set fd_f [open $cmd r] fconfigure $fd_f -blocking 0 -translation binary - fileevent $fd_f readable \ - [namespace code [list _read $w $fd_f $after]] + fileevent $fd_f readable [cb _read $fd_f $after] } -proc _read {w fd after} { - variable console_cr - +method _read {fd after} { set buf [read $fd] if {$buf ne {}} { - if {![winfo exists $w]} {_init $w} + if {![winfo exists $w.m.t]} {_init $this} $w.m.t conf -state normal set c 0 set n [string length $buf] @@ -107,11 +101,11 @@ proc _read {w fd after} { if {$lf < $cr} { $w.m.t insert end [string range $buf $c $lf] - set console_cr($w) [$w.m.t index {end -1c}] + set console_cr [$w.m.t index {end -1c}] set c $lf incr c } else { - $w.m.t delete $console_cr($w) end + $w.m.t delete $console_cr end $w.m.t insert end "\n" $w.m.t insert end [string range $buf $c $cr] set c $cr @@ -130,19 +124,19 @@ proc _read {w fd after} { set ok 1 } if {$after ne {}} { - uplevel #0 $after $w $ok + uplevel #0 $after $ok } else { - done $w $ok + done $this $ok } return } fconfigure $fd -blocking 0 } -proc chain {cmdlist w {ok 1}} { +method chain {cmdlist {ok 1}} { if {$ok} { if {[llength $cmdlist] == 0} { - done $w $ok + done $this $ok return } @@ -150,52 +144,33 @@ proc chain {cmdlist w {ok 1}} { set cmdlist [lrange $cmdlist 1 end] if {[lindex $cmd 0] eq {exec}} { - exec $w \ - [lindex $cmd 1] \ - [namespace code [list chain $cmdlist]] + exec $this \ + [lrange $cmd 1 end] \ + [cb chain $cmdlist] } else { - uplevel #0 $cmd $cmdlist $w $ok + uplevel #0 $cmd [cb chain $cmdlist] } } else { - done $w $ok + done $this $ok } } -proc done {args} { - variable console_cr - variable console_data - - switch -- [llength $args] { - 2 { - set w [lindex $args 0] - set ok [lindex $args 1] - } - 3 { - set w [lindex $args 1] - set ok [lindex $args 2] - } - default { - error "wrong number of args: done ?ignored? w ok" - } - } - +method done {ok} { if {$ok} { - if {[winfo exists $w]} { + if {[winfo exists $w.m.s]} { $w.m.s conf -background green -text {Success} $w.ok conf -state normal focus $w.ok } } else { - if {![winfo exists $w]} { - _init $w + if {![winfo exists $w.m.s]} { + _init $this } $w.m.s conf -background red -text {Error: Command Failed} $w.ok conf -state normal focus $w.ok } - - array unset console_cr $w - array unset console_data $w + delete_this } } diff --git a/lib/database.tcl b/lib/database.tcl index 73058a8269..43e4a289bb 100644 --- a/lib/database.tcl +++ b/lib/database.tcl @@ -70,12 +70,12 @@ proc do_stats {} { proc do_gc {} { set w [console::new {gc} {Compressing the object database}] - console::chain { - {exec {git pack-refs --prune}} - {exec {git reflog expire --all}} - {exec {git repack -a -d -l}} - {exec {git rerere gc}} - } $w + console::chain $w { + {exec git pack-refs --prune} + {exec git reflog expire --all} + {exec git repack -a -d -l} + {exec git rerere gc} + } } proc do_fsck_objects {} { diff --git a/lib/merge.tcl b/lib/merge.tcl index 642d6eefbd..24ed24b3d0 100644 --- a/lib/merge.tcl +++ b/lib/merge.tcl @@ -123,7 +123,8 @@ Please select fewer branches. To merge more than 15 branches, merge the branche set msg "Merging $current_branch, [join $names {, }]" set ui_status_value "$msg..." set cons [console::new "Merge" $msg] - console::exec $cons $cmd [namespace code [list _finish $revcnt]] + console::exec $cons $cmd \ + [namespace code [list _finish $revcnt $cons]] bind $w {} destroy $w } From 28bf928cf81eec720bc8a31179a0c9cf688d5af3 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 8 May 2007 21:31:31 -0400 Subject: [PATCH 05/13] git-gui: Don't attempt to inline array reads in methods If a variable reference to a field is to an array, and it is the only reference to that field in that method we cannot make it an inlined [set foo] call as the regexp was converting the Tcl code wrong. We were producing "[set foo](x)" for "$foo(x)", and that isn't valid Tcl when foo is an array. So we just punt if the only occurance has a ( after it. Signed-off-by: Shawn O. Pearce --- lib/class.tcl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/class.tcl b/lib/class.tcl index c1291989aa..88b056522a 100644 --- a/lib/class.tcl +++ b/lib/class.tcl @@ -96,7 +96,8 @@ proc method {name params body {deleted {}} {del_body {}}} { set n [lindex $n 0] if {[regexp -- $n\\M $body]} { if { [regexp -all -- $n\\M $body] == 1 - && [regexp -all -- \\\$$n\\M $body] == 1} { + && [regexp -all -- \\\$$n\\M $body] == 1 + && [regexp -all -- \\\$$n\\( $body] == 0} { regsub -all \\\$$n\\M $body "\[set \${this}::$n\]" body } else { append decl { ${this}::} $n { } $n From 685caf9af672811b45ebeaaa299b8f9fbd743d82 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 8 May 2007 21:33:14 -0400 Subject: [PATCH 06/13] git-gui: Convert blame to the "class" way of doing things Our blame viewer code has historically been a mess simply because the data for multiple viewers was all crammed into a single pair of Tcl arrays. This made the code hard to read and even harder to maintain. Now that we have a slightly better way of tracking the data for our "meta-widgets" we can make use of it here in the blame viewer to cleanup the code and make it easier to work with long term. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 2 +- lib/blame.tcl | 248 +++++++++++++++++++++++------------------------- lib/browser.tcl | 2 +- 3 files changed, 121 insertions(+), 131 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 67f993aa4b..568c814c60 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1590,7 +1590,7 @@ blame { exit 1 } set current_branch [lindex $argv 0] - show_blame $current_branch [lindex $argv 1] + blame::new $current_branch [lindex $argv 1] return } citool - diff --git a/lib/blame.tcl b/lib/blame.tcl index dc7b22f7b5..37ce3e7a86 100644 --- a/lib/blame.tcl +++ b/lib/blame.tcl @@ -1,20 +1,42 @@ # git-gui blame viewer # Copyright (C) 2006, 2007 Shawn Pearce -set next_browser_id 0 +class blame { -proc show_blame {commit path} { - global next_browser_id blame_status blame_data +field commit ; # input commit to blame +field path ; # input filename to view in $commit - if {[winfo ismapped .]} { - set w .browser[incr next_browser_id] - set tl $w - toplevel $w - } else { - set w {} - set tl . - } - set blame_status($w) {Loading current file content...} +field w +field w_line +field w_load +field w_file +field w_cmit +field status + +field highlight_line -1 ; # current line selected +field highlight_commit {} ; # sha1 of commit selected + +field total_lines 0 ; # total length of file +field blame_lines 0 ; # number of lines computed +field commit_count 0 ; # number of commits in $commit_list +field commit_list {} ; # list of commit sha1 in receipt order +field order ; # array commit -> receipt order +field header ; # array commit,key -> header field +field line_commit ; # array line -> sha1 commit +field line_file ; # array line -> file name + +field r_commit ; # commit currently being parsed +field r_orig_line ; # original line number +field r_final_line ; # final line number +field r_line_count ; # lines in this region + +constructor new {i_commit i_path} { + set commit $i_commit + set path $i_path + + make_toplevel top w + wm title $top "[appname] ([reponame]): File Viewer" + set status "Loading $commit:$path..." label $w.path -text "$commit:$path" \ -anchor w \ @@ -70,7 +92,8 @@ proc show_blame {commit path} { grid rowconfigure $w.out 0 -weight 1 pack $w.out -fill both -expand 1 - label $w.status -textvariable blame_status($w) \ + label $w.status \ + -textvariable @status \ -anchor w \ -justify left \ -borderwidth 1 \ @@ -95,8 +118,14 @@ proc show_blame {commit path} { pack $w.cm -side bottom -fill x menu $w.ctxm -tearoff 0 - $w.ctxm add command -label "Copy Commit" \ - -command "blame_copycommit $w \$cursorW @\$cursorX,\$cursorY" + $w.ctxm add command \ + -label "Copy Commit" \ + -command [cb _copycommit] + + set w_line $w.out.linenumber_t + set w_load $w.out.loaded_t + set w_file $w.out.file_t + set w_cmit $w.cm.t foreach i [list \ $w.out.loaded_t \ @@ -111,14 +140,7 @@ proc show_blame {commit path} { $w.out.linenumber_t \ $w.out.file_t \ ] yview $w.out.sby] - bind $i " - blame_click {$w} \\ - $w.cm.t \\ - $w.out.linenumber_t \\ - $w.out.file_t \\ - $i @%x,%y - focus $i - " + bind $i "[cb _click $i @%x,%y]; focus $i" bind_button3 $i " set cursorX %x set cursorY %y @@ -145,157 +167,122 @@ proc show_blame {commit path} { } bind $w.cm.t "focus $w.cm.t" - bind $tl "focus $tl" - bind $tl " - array unset blame_status {$w} - array unset blame_data $w,* - " - wm title $tl "[appname] ([reponame]): File Viewer" - - set blame_data($w,commit_count) 0 - set blame_data($w,commit_list) {} - set blame_data($w,total_lines) 0 - set blame_data($w,blame_lines) 0 - set blame_data($w,highlight_commit) {} - set blame_data($w,highlight_line) -1 + bind $top "focus $top" + bind $top [list delete_this $this] set cmd [list git cat-file blob "$commit:$path"] set fd [open "| $cmd" r] fconfigure $fd -blocking 0 -translation lf -encoding binary - fileevent $fd readable [list read_blame_catfile \ - $fd $w $commit $path \ - $w.cm.t $w.out.loaded_t $w.out.linenumber_t $w.out.file_t] + fileevent $fd readable [cb _read_file $fd] } -proc read_blame_catfile {fd w commit path w_cmit w_load w_line w_file} { - global blame_status blame_data - - if {![winfo exists $w_file]} { - catch {close $fd} - return - } - - set n $blame_data($w,total_lines) +method _read_file {fd} { $w_load conf -state normal $w_line conf -state normal $w_file conf -state normal while {[gets $fd line] >= 0} { regsub "\r\$" $line {} line - incr n + incr total_lines $w_load insert end "\n" - $w_line insert end "$n\n" linenumber + $w_line insert end "$total_lines\n" linenumber $w_file insert end "$line\n" } $w_load conf -state disabled $w_line conf -state disabled $w_file conf -state disabled - set blame_data($w,total_lines) $n if {[eof $fd]} { close $fd - blame_incremental_status $w - set cmd [list git blame -M -C --incremental] - lappend cmd $commit -- $path + _status $this + set cmd [list git blame -M -C --incremental $commit -- $path] set fd [open "| $cmd" r] fconfigure $fd -blocking 0 -translation lf -encoding binary - fileevent $fd readable [list read_blame_incremental $fd $w \ - $w_load $w_cmit $w_line $w_file] - } -} - -proc read_blame_incremental {fd w w_load w_cmit w_line w_file} { - global blame_status blame_data - - if {![winfo exists $w_file]} { - catch {close $fd} - return + fileevent $fd readable [cb _read_blame $fd] } +} ifdeleted { catch {close $fd} } +method _read_blame {fd} { while {[gets $fd line] >= 0} { if {[regexp {^([a-z0-9]{40}) (\d+) (\d+) (\d+)$} $line line \ cmit original_line final_line line_count]} { - set blame_data($w,commit) $cmit - set blame_data($w,original_line) $original_line - set blame_data($w,final_line) $final_line - set blame_data($w,line_count) $line_count + set r_commit $cmit + set r_orig_line $original_line + set r_final_line $final_line + set r_line_count $line_count - if {[catch {set g $blame_data($w,$cmit,order)}]} { + if {[catch {set g $order($cmit)}]} { $w_line tag conf g$cmit $w_file tag conf g$cmit $w_line tag raise in_sel $w_file tag raise in_sel $w_file tag raise sel - set blame_data($w,$cmit,order) $blame_data($w,commit_count) - incr blame_data($w,commit_count) - lappend blame_data($w,commit_list) $cmit + set order($cmit) $commit_count + incr commit_count + lappend commit_list $cmit } } elseif {[string match {filename *} $line]} { set file [string range $line 9 end] - set n $blame_data($w,line_count) - set lno $blame_data($w,final_line) - set cmit $blame_data($w,commit) + set n $r_line_count + set lno $r_final_line + set cmit $r_commit while {$n > 0} { - if {[catch {set g g$blame_data($w,line$lno,commit)}]} { + if {[catch {set g g$line_commit($lno)}]} { $w_load tag add annotated $lno.0 "$lno.0 lineend + 1c" } else { $w_line tag remove g$g $lno.0 "$lno.0 lineend + 1c" $w_file tag remove g$g $lno.0 "$lno.0 lineend + 1c" } - set blame_data($w,line$lno,commit) $cmit - set blame_data($w,line$lno,file) $file + set line_commit($lno) $cmit + set line_file($lno) $file $w_line tag add g$cmit $lno.0 "$lno.0 lineend + 1c" $w_file tag add g$cmit $lno.0 "$lno.0 lineend + 1c" - if {$blame_data($w,highlight_line) == -1} { + if {$highlight_line == -1} { if {[lindex [$w_file yview] 0] == 0} { $w_file see $lno.0 - blame_showcommit $w $w_cmit $w_line $w_file $lno + _showcommit $this $lno } - } elseif {$blame_data($w,highlight_line) == $lno} { - blame_showcommit $w $w_cmit $w_line $w_file $lno + } elseif {$highlight_line == $lno} { + _showcommit $this $lno } incr n -1 incr lno - incr blame_data($w,blame_lines) + incr blame_lines } - set hc $blame_data($w,highlight_commit) + set hc $highlight_commit if {$hc ne {} - && [expr {$blame_data($w,$hc,order) + 1}] - == $blame_data($w,$cmit,order)} { - blame_showcommit $w $w_cmit $w_line $w_file \ - $blame_data($w,highlight_line) + && [expr {$order($hc) + 1}] == $order($cmit)} { + _showcommit $this $highlight_line } - } elseif {[regexp {^([a-z-]+) (.*)$} $line line header data]} { - set blame_data($w,$blame_data($w,commit),$header) $data + } elseif {[regexp {^([a-z-]+) (.*)$} $line line key data]} { + set header($r_commit,$key) $data } } if {[eof $fd]} { close $fd - set blame_status($w) {Annotation complete.} + set status {Annotation complete.} } else { - blame_incremental_status $w + _status $this } -} +} ifdeleted { catch {close $fd} } -proc blame_incremental_status {w} { - global blame_status blame_data - - set have $blame_data($w,blame_lines) - set total $blame_data($w,total_lines) +method _status {} { + set have $blame_lines + set total $total_lines set pdone 0 if {$total} {set pdone [expr {100 * $have / $total}]} - set blame_status($w) [format \ + set status [format \ "Loading annotations... %i of %i lines annotated (%2i%%)" \ $have $total $pdone] } -proc blame_click {w w_cmit w_line w_file cur_w pos} { +method _click {cur_w pos} { set lno [lindex [split [$cur_w index $pos] .] 0] if {$lno eq {}} return @@ -304,24 +291,24 @@ proc blame_click {w w_cmit w_line w_file cur_w pos} { $w_line tag add in_sel $lno.0 "$lno.0 + 1 line" $w_file tag add in_sel $lno.0 "$lno.0 + 1 line" - blame_showcommit $w $w_cmit $w_line $w_file $lno + _showcommit $this $lno } -set blame_colors { +variable blame_colors { #ff4040 #ff40ff #4040ff } -proc blame_showcommit {w w_cmit w_line w_file lno} { - global blame_colors blame_data repo_config +method _showcommit {lno} { + global repo_config + variable blame_colors - set cmit $blame_data($w,highlight_commit) - if {$cmit ne {}} { - set idx $blame_data($w,$cmit,order) + if {$highlight_commit ne {}} { + set idx $order($highlight_commit) set i 0 foreach c $blame_colors { - set h [lindex $blame_data($w,commit_list) [expr {$idx - 1 + $i}]] + set h [lindex $commit_list [expr {$idx - 1 + $i}]] $w_line tag conf g$h -background white $w_file tag conf g$h -background white incr i @@ -330,14 +317,15 @@ proc blame_showcommit {w w_cmit w_line w_file lno} { $w_cmit conf -state normal $w_cmit delete 0.0 end - if {[catch {set cmit $blame_data($w,line$lno,commit)}]} { + if {[catch {set cmit $line_commit($lno)} myerr]} { + puts "myerr = $myerr" set cmit {} $w_cmit insert end "Loading annotation..." } else { - set idx $blame_data($w,$cmit,order) + set idx $order($cmit) set i 0 foreach c $blame_colors { - set h [lindex $blame_data($w,commit_list) [expr {$idx - 1 + $i}]] + set h [lindex $commit_list [expr {$idx - 1 + $i}]] $w_line tag conf g$h -background $c $w_file tag conf g$h -background $c incr i @@ -346,18 +334,18 @@ proc blame_showcommit {w w_cmit w_line w_file lno} { set author_name {} set author_email {} set author_time {} - catch {set author_name $blame_data($w,$cmit,author)} - catch {set author_email $blame_data($w,$cmit,author-mail)} - catch {set author_time [clock format $blame_data($w,$cmit,author-time)]} + catch {set author_name $header($cmit,author)} + catch {set author_email $header($cmit,author-mail)} + catch {set author_time [clock format $header($cmit,author-time)]} set committer_name {} set committer_email {} set committer_time {} - catch {set committer_name $blame_data($w,$cmit,committer)} - catch {set committer_email $blame_data($w,$cmit,committer-mail)} - catch {set committer_time [clock format $blame_data($w,$cmit,committer-time)]} + catch {set committer_name $header($cmit,committer)} + catch {set committer_email $header($cmit,committer-mail)} + catch {set committer_time [clock format $header($cmit,committer-time)]} - if {[catch {set msg $blame_data($w,$cmit,message)}]} { + if {[catch {set msg $header($cmit,message)}]} { set msg {} catch { set fd [open "| git cat-file commit $cmit" r] @@ -377,29 +365,29 @@ proc blame_showcommit {w w_cmit w_line w_file lno} { set author_name [encoding convertfrom $enc $author_name] set committer_name [encoding convertfrom $enc $committer_name] - set blame_data($w,$cmit,author) $author_name - set blame_data($w,$cmit,committer) $committer_name + set header($cmit,author) $author_name + set header($cmit,committer) $committer_name } - set blame_data($w,$cmit,message) $msg + set header($cmit,message) $msg } $w_cmit insert end "commit $cmit\n" $w_cmit insert end "Author: $author_name $author_email $author_time\n" $w_cmit insert end "Committer: $committer_name $committer_email $committer_time\n" - $w_cmit insert end "Original File: [escape_path $blame_data($w,line$lno,file)]\n" + $w_cmit insert end "Original File: [escape_path $line_file($lno)]\n" $w_cmit insert end "\n" $w_cmit insert end $msg } $w_cmit conf -state disabled - set blame_data($w,highlight_line) $lno - set blame_data($w,highlight_commit) $cmit + set highlight_line $lno + set highlight_commit $cmit } -proc blame_copycommit {w i pos} { - global blame_data - set lno [lindex [split [$i index $pos] .] 0] - if {![catch {set commit $blame_data($w,line$lno,commit)}]} { +method _copycommit {} { + set pos @$::cursorX,$::cursorY + set lno [lindex [split [$::cursorW index $pos] .] 0] + if {![catch {set commit $line_commit($lno)}]} { clipboard clear clipboard append \ -format STRING \ @@ -407,3 +395,5 @@ proc blame_copycommit {w i pos} { -- $commit } } + +} diff --git a/lib/browser.tcl b/lib/browser.tcl index f9a4e1d52c..fd86b11217 100644 --- a/lib/browser.tcl +++ b/lib/browser.tcl @@ -140,7 +140,7 @@ method _enter {} { append p [lindex $n 1] } append p $name - show_blame $browser_commit $p + blame::new $browser_commit $p } } } From c6127856eb318157121b0aeab150c074e9eeb3de Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 8 May 2007 21:58:25 -0400 Subject: [PATCH 07/13] git-gui: Use prefix if blame is run in a subdirectory I think it was Andy Parkins who pointed out that git gui blame HEAD f does not work if f is in a subdirectory and we are currently running git-gui within that subdirectory. This is happening because we did not take the user's prefix into account when we computed the file path in the repository. We now assume the prefix as returned by rev-parse --show-prefix is valid and we use that during the command line blame subcommand when we apply the parameters. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 568c814c60..8f33f81c58 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -261,8 +261,14 @@ unset -nocomplain v _junk act_maj act_min req_maj req_min ## ## repository setup -if { [catch {set _gitdir $env(GIT_DIR)}] - && [catch {set _gitdir [git rev-parse --git-dir]} err]} { +if {[catch { + set _gitdir $env(GIT_DIR) + set _prefix {} + }] + && [catch { + set _gitdir [git rev-parse --git-dir] + set _prefix [git rev-parse --show-prefix] + } err]} { catch {wm withdraw .} error_popup "Cannot find the git directory:\n\n$err" exit 1 @@ -1590,7 +1596,7 @@ blame { exit 1 } set current_branch [lindex $argv 0] - blame::new $current_branch [lindex $argv 1] + blame::new $current_branch $_prefix[lindex $argv 1] return } citool - From 3e45ee1ef268c73539903f89c737af89034d391f Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 8 May 2007 22:36:01 -0400 Subject: [PATCH 08/13] git-gui: Smarter command line parsing for browser, blame The browser subcommand now optionally accepts a single revision argument; if no revision argument is supplied then we use the current branch as the tree to browse. This is very common, so its a nice option. Our blame subcommand now tries to perform the same assumptions as the command line git-blame; both the revision and the file are optional. We assume the argument is a filename if the file exists in the working directory, otherwise we assume the argument is a revision name. A -- can be supplied between the two to force parsing, or before the filename to force it to be a filename. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 56 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 8f33f81c58..e90cdde96c 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1578,25 +1578,63 @@ bind all <$M1B-Key-Q> do_quit bind all <$M1B-Key-w> {destroy [winfo toplevel %W]} bind all <$M1B-Key-W> {destroy [winfo toplevel %W]} +set subcommand_args {} +proc usage {} { + puts stderr "usage: $::argv0 $::subcommand $::subcommand_args" + exit 1 +} + # -- Not a normal commit type invocation? Do that instead! # switch -- $subcommand { browser { - if {[llength $argv] != 1} { - puts stderr "usage: $argv0 browser commit" - exit 1 + set subcommand_args {rev?} + switch [llength $argv] { + 0 { + set current_branch [git symbolic-ref HEAD] + regsub ^refs/((heads|tags|remotes)/)? \ + $current_branch {} current_branch + } + 1 { + set current_branch [lindex $argv 0] + } + default usage } - set current_branch [lindex $argv 0] browser::new $current_branch return } blame { - if {[llength $argv] != 2} { - puts stderr "usage: $argv0 blame commit path" - exit 1 + set subcommand_args {rev? path?} + set path {} + set is_path 0 + foreach a $argv { + if {$is_path || [file exists $_prefix$a]} { + if {$path ne {}} usage + set path $a + break + } elseif {$a eq {--}} { + if {$path ne {}} { + if {$current_branch ne {}} usage + set current_branch $path + set path {} + } + set is_path 1 + } elseif {$current_branch eq {}} { + if {$current_branch ne {}} usage + set current_branch $a + } else { + usage + } } - set current_branch [lindex $argv 0] - blame::new $current_branch $_prefix[lindex $argv 1] + unset is_path + + if {$current_branch eq {} && $path ne {}} { + set current_branch [git symbolic-ref HEAD] + regsub ^refs/((heads|tags|remotes)/)? \ + $current_branch {} current_branch + } + if {$current_branch eq {} || $path eq {}} usage + blame::new $current_branch $path return } citool - From a0db0d61fbc447f13cb0aadf01c83eb193b22dc9 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 8 May 2007 22:48:47 -0400 Subject: [PATCH 09/13] git-gui: Generate blame on uncommitted working tree file If the user doesn't give us a revision parameter to our blame subcommand then we can generate blame against the working tree file by passing the file path off to blame with the --contents argument. In this case we cannot obtain the contents of the file from the ODB; instead we must obtain the contents by reading the working directory file as-is. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 20 ++++++++++++-------- lib/blame.tcl | 16 +++++++++++++--- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index e90cdde96c..62b1a36a8a 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1605,6 +1605,7 @@ browser { } blame { set subcommand_args {rev? path?} + set head {} set path {} set is_path 0 foreach a $argv { @@ -1614,27 +1615,30 @@ blame { break } elseif {$a eq {--}} { if {$path ne {}} { - if {$current_branch ne {}} usage - set current_branch $path + if {$head ne {}} usage + set head $path set path {} } set is_path 1 - } elseif {$current_branch eq {}} { - if {$current_branch ne {}} usage - set current_branch $a + } elseif {$head eq {}} { + if {$head ne {}} usage + set head $a } else { usage } } unset is_path - if {$current_branch eq {} && $path ne {}} { + if {$head eq {}} { set current_branch [git symbolic-ref HEAD] regsub ^refs/((heads|tags|remotes)/)? \ $current_branch {} current_branch + } else { + set current_branch $head } - if {$current_branch eq {} || $path eq {}} usage - blame::new $current_branch $path + + if {$path eq {}} usage + blame::new $head $path return } citool - diff --git a/lib/blame.tcl b/lib/blame.tcl index 37ce3e7a86..8ac0104187 100644 --- a/lib/blame.tcl +++ b/lib/blame.tcl @@ -170,8 +170,12 @@ constructor new {i_commit i_path} { bind $top "focus $top" bind $top [list delete_this $this] - set cmd [list git cat-file blob "$commit:$path"] - set fd [open "| $cmd" r] + if {$commit eq {}} { + set fd [open $path r] + } else { + set cmd [list git cat-file blob "$commit:$path"] + set fd [open "| $cmd" r] + } fconfigure $fd -blocking 0 -translation lf -encoding binary fileevent $fd readable [cb _read_file $fd] } @@ -194,7 +198,13 @@ method _read_file {fd} { if {[eof $fd]} { close $fd _status $this - set cmd [list git blame -M -C --incremental $commit -- $path] + set cmd [list git blame -M -C --incremental] + if {$commit eq {}} { + lappend cmd --contents $path + } else { + lappend cmd $commit + } + lappend cmd -- $path set fd [open "| $cmd" r] fconfigure $fd -blocking 0 -translation lf -encoding binary fileevent $fd readable [cb _read_blame $fd] From 0511798f06d3456885682605b54bd3bc378e8fda Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 9 May 2007 00:36:25 -0400 Subject: [PATCH 10/13] git-gui: Cleanup minor nits in blame code We can use [list ...] rather than "", especially when we are talking about values as then they are properly escaped if necessary. Small nit, but probably not a huge deal as the only data being inlined here is Tk paths. Some of the lines in the parser code were longer than 80 characters wide, and they actually were all the same value on the end part of the line. Rather than keeping the mess copied-and-pasted around we can set the last argument into a local variable and reuse it many times. The commit display code was also rather difficult to read on an 80 character wide terminal, so I'm moving it all into a double quoted string that is easier to read. Signed-off-by: Shawn O. Pearce --- lib/blame.tcl | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/lib/blame.tcl b/lib/blame.tcl index 8ac0104187..d6d57d889d 100644 --- a/lib/blame.tcl +++ b/lib/blame.tcl @@ -166,8 +166,8 @@ constructor new {i_commit i_path} { bind $i {catch {%W yview scroll 1 pages};break} } - bind $w.cm.t "focus $w.cm.t" - bind $top "focus $top" + bind $w.cm.t [list focus $w.cm.t] + bind $top [list focus $top] bind $top [list delete_this $this] if {$commit eq {}} { @@ -237,17 +237,18 @@ method _read_blame {fd} { set cmit $r_commit while {$n > 0} { + set lno_e "$lno.0 lineend + 1c" if {[catch {set g g$line_commit($lno)}]} { - $w_load tag add annotated $lno.0 "$lno.0 lineend + 1c" + $w_load tag add annotated $lno.0 $lno_e } else { - $w_line tag remove g$g $lno.0 "$lno.0 lineend + 1c" - $w_file tag remove g$g $lno.0 "$lno.0 lineend + 1c" + $w_line tag remove g$g $lno.0 $lno_e + $w_file tag remove g$g $lno.0 $lno_e } set line_commit($lno) $cmit set line_file($lno) $file - $w_line tag add g$cmit $lno.0 "$lno.0 lineend + 1c" - $w_file tag add g$cmit $lno.0 "$lno.0 lineend + 1c" + $w_line tag add g$cmit $lno.0 $lno_e + $w_file tag add g$cmit $lno.0 $lno_e if {$highlight_line == -1} { if {[lindex [$w_file yview] 0] == 0} { @@ -296,10 +297,11 @@ method _click {cur_w pos} { set lno [lindex [split [$cur_w index $pos] .] 0] if {$lno eq {}} return + set lno_e "$lno.0 + 1 line" $w_line tag remove in_sel 0.0 end $w_file tag remove in_sel 0.0 end - $w_line tag add in_sel $lno.0 "$lno.0 + 1 line" - $w_file tag add in_sel $lno.0 "$lno.0 + 1 line" + $w_line tag add in_sel $lno.0 $lno_e + $w_file tag add in_sel $lno.0 $lno_e _showcommit $this $lno } @@ -327,8 +329,7 @@ method _showcommit {lno} { $w_cmit conf -state normal $w_cmit delete 0.0 end - if {[catch {set cmit $line_commit($lno)} myerr]} { - puts "myerr = $myerr" + if {[catch {set cmit $line_commit($lno)}]} { set cmit {} $w_cmit insert end "Loading annotation..." } else { @@ -381,12 +382,12 @@ method _showcommit {lno} { set header($cmit,message) $msg } - $w_cmit insert end "commit $cmit\n" - $w_cmit insert end "Author: $author_name $author_email $author_time\n" - $w_cmit insert end "Committer: $committer_name $committer_email $committer_time\n" - $w_cmit insert end "Original File: [escape_path $line_file($lno)]\n" - $w_cmit insert end "\n" - $w_cmit insert end $msg + $w_cmit insert end "commit $cmit +Author: $author_name $author_email $author_time +Committer: $committer_name $committer_email $committer_time +Original File: [escape_path $line_file($lno)] + +$msg" } $w_cmit conf -state disabled From 76486bbefbaeb606f662dadde0ca5dac59a4ec31 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 9 May 2007 00:48:27 -0400 Subject: [PATCH 11/13] git-gui: Format author/committer times in ISO format This is a simple change to match what gitk does when it shows a commit; we format using ISO dates (yyyy-mm-dd HH:MM:SS). Signed-off-by: Shawn O. Pearce --- lib/blame.tcl | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/blame.tcl b/lib/blame.tcl index d6d57d889d..8b032d9590 100644 --- a/lib/blame.tcl +++ b/lib/blame.tcl @@ -347,14 +347,20 @@ method _showcommit {lno} { set author_time {} catch {set author_name $header($cmit,author)} catch {set author_email $header($cmit,author-mail)} - catch {set author_time [clock format $header($cmit,author-time)]} + catch {set author_time [clock format \ + $header($cmit,author-time) \ + -format {%Y-%m-%d %H:%M:%S} + ]} set committer_name {} set committer_email {} set committer_time {} catch {set committer_name $header($cmit,committer)} catch {set committer_email $header($cmit,committer-mail)} - catch {set committer_time [clock format $header($cmit,committer-time)]} + catch {set committer_time [clock format \ + $header($cmit,committer-time) \ + -format {%Y-%m-%d %H:%M:%S} + ]} if {[catch {set msg $header($cmit,message)}]} { set msg {} @@ -383,8 +389,8 @@ method _showcommit {lno} { } $w_cmit insert end "commit $cmit -Author: $author_name $author_email $author_time -Committer: $committer_name $committer_email $committer_time +Author: $author_name $author_email $author_time +Committer: $committer_name $committer_email $committer_time Original File: [escape_path $line_file($lno)] $msg" From 6b3d8b97cb48bcc6e2ac020312a56e1d9a2828b2 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 9 May 2007 18:35:04 -0400 Subject: [PATCH 12/13] git-gui: Paperbag fix blame in subdirectory Signed-off-by: Shawn O. Pearce --- git-gui.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-gui.sh b/git-gui.sh index 62b1a36a8a..2fda4c2290 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1611,7 +1611,7 @@ blame { foreach a $argv { if {$is_path || [file exists $_prefix$a]} { if {$path ne {}} usage - set path $a + set path $_prefix$a break } elseif {$a eq {--}} { if {$path ne {}} { From d6da71a9d16b8cf27f9d8f90692d3625c849cbc8 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 10 May 2007 17:54:45 -0400 Subject: [PATCH 13/13] git gui 0.7.0 Signed-off-by: Shawn O. Pearce --- GIT-VERSION-GEN | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 2741c1e14c..25647c8060 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=0.6.GITGUI +DEF_VER=0.7.GITGUI LF=' '