Code

git-gui: Always indicate the file in the diff viewer.
[git.git] / git-gui
diff --git a/git-gui b/git-gui
index ad3aa0727c55a8e62609cec658108e7ecf3dedd6..f36594eea0f8a85a5abb75f51fff7123cf6b04b0 100755 (executable)
--- a/git-gui
+++ b/git-gui
@@ -7,6 +7,86 @@ exec wish "$0" -- "$@"
 # and distributed under the terms of the GNU General Public Licence,
 # either version 2, or (at your option) any later version.
 
+######################################################################
+##
+## config
+
+proc load_repo_config {} {
+       global repo_config
+       global cfg_trust_mtime
+
+       array unset repo_config
+       catch {
+               set fd_rc [open "| git repo-config --list" r]
+               while {[gets $fd_rc line] >= 0} {
+                       if {[regexp {^([^=]+)=(.*)$} $line line name value]} {
+                               lappend repo_config($name) $value
+                       }
+               }
+               close $fd_rc
+       }
+
+       if {[catch {set cfg_trust_mtime \
+                       [lindex $repo_config(gui.trustmtime) 0]
+               }]} {
+               set cfg_trust_mtime false
+       }
+}
+
+proc save_my_config {} {
+       global repo_config
+       global cfg_trust_mtime
+
+       if {[catch {set rc_trustMTime $repo_config(gui.trustmtime)}]} {
+               set rc_trustMTime [list false]
+       }
+       if {$cfg_trust_mtime != [lindex $rc_trustMTime 0]} {
+               exec git repo-config gui.trustMTime $cfg_trust_mtime
+               set repo_config(gui.trustmtime) [list $cfg_trust_mtime]
+       }
+
+       set cfg_geometry [list \
+               [wm geometry .] \
+               [.vpane sash coord 0] \
+               [.vpane.files sash coord 0] \
+               ]
+       if {[catch {set rc_geometry $repo_config(gui.geometry)}]} {
+               set rc_geometry [list [list]]
+       }
+       if {$cfg_geometry != [lindex $rc_geometry 0]} {
+               exec git repo-config gui.geometry $cfg_geometry
+               set repo_config(gui.geometry) [list $cfg_geometry]
+       }
+}
+
+######################################################################
+##
+## repository setup
+
+set appname [lindex [file split $argv0] end]
+set gitdir {}
+set GIT_COMMITTER_IDENT {}
+
+if {[catch {set cdup [exec git rev-parse --show-cdup]} err]} {
+       show_msg {} . "Cannot find the git directory: $err"
+       exit 1
+}
+if {$cdup != ""} {
+       cd $cdup
+}
+unset cdup
+
+if {[catch {set gitdir [exec git rev-parse --git-dir]} err]} {
+       show_msg {} . "Cannot find the git directory: $err"
+       exit 1
+}
+
+if {$appname == {git-citool}} {
+       set single_commit 1
+}
+
+load_repo_config
+
 ######################################################################
 ##
 ## task management
@@ -70,7 +150,7 @@ proc repository_state {hdvar ctvar} {
 proc update_status {{final Ready.}} {
        global HEAD PARENT commit_type
        global ui_index ui_other ui_status_value ui_comm
-       global status_active file_states
+       global status_active file_states file_lists
        global cfg_trust_mtime
 
        if {$status_active || ![lock_index read]} return
@@ -86,6 +166,7 @@ proc update_status {{final Ready.}} {
        }
 
        array unset file_states
+       array unset file_lists
        foreach w [list $ui_index $ui_other] {
                $w conf -state normal
                $w delete 0.0 end
@@ -99,6 +180,7 @@ proc update_status {{final Ready.}} {
                } elseif {[load_message SQUASH_MSG]} {
                }
                $ui_comm edit modified false
+               $ui_comm edit reset
        }
 
        if {$cfg_trust_mtime == {true}} {
@@ -115,7 +197,7 @@ proc update_status {{final Ready.}} {
 proc update_status_stage2 {fd final} {
        global gitdir PARENT commit_type
        global ui_index ui_other ui_status_value ui_comm
-       global status_active file_states
+       global status_active
        global buf_rdi buf_rdf buf_rlo
 
        if {$fd != {}} {
@@ -265,22 +347,40 @@ proc status_eof {fd buf final} {
 ## diff
 
 proc clear_diff {} {
-       global ui_diff ui_fname_value ui_fstatus_value
+       global ui_diff ui_fname_value ui_fstatus_value ui_index ui_other
 
        $ui_diff conf -state normal
        $ui_diff delete 0.0 end
        $ui_diff conf -state disabled
+
        set ui_fname_value {}
        set ui_fstatus_value {}
+
+       $ui_index tag remove in_diff 0.0 end
+       $ui_other tag remove in_diff 0.0 end
 }
 
-proc show_diff {path} {
-       global file_states PARENT diff_3way diff_active
+proc show_diff {path {w {}} {lno {}}} {
+       global file_states file_lists
+       global PARENT diff_3way diff_active
        global ui_diff ui_fname_value ui_fstatus_value ui_status_value
 
        if {$diff_active || ![lock_index read]} return
 
        clear_diff
+       if {$w == {} || $lno == {}} {
+               foreach w [array names file_lists] {
+                       set lno [lsearch -sorted $file_lists($w) $path]
+                       if {$lno >= 0} {
+                               incr lno
+                               break
+                       }
+               }
+       }
+       if {$w != {} && $lno >= 1} {
+               $w tag add in_diff $lno.0 [expr $lno + 1].0
+       }
+
        set s $file_states($path)
        set m [lindex $s 0]
        set diff_3way 0
@@ -423,6 +523,7 @@ proc load_last_commit {} {
                $ui_comm delete 0.0 end
                $ui_comm insert end $msg
                $ui_comm edit modified false
+               $ui_comm edit reset
                update_status
        } else {
                error_popup {You can't amend a merge commit.}
@@ -618,6 +719,7 @@ proc commit_stage2 {fd_wt curHEAD msg} {
 
        $ui_comm delete 0.0 end
        $ui_comm edit modified false
+       $ui_comm edit reset
 
        if {$single_commit} do_quit
 
@@ -740,27 +842,6 @@ proc mapdesc {state path} {
        return $r
 }
 
-proc bsearch {w path} {
-       set hi [expr [lindex [split [$w index end] .] 0] - 2]
-       if {$hi == 0} {
-               return -1
-       }
-       set lo 0
-       while {$lo < $hi} {
-               set mi [expr [expr $lo + $hi] / 2]
-               set ti [expr $mi + 1]
-               set cmp [string compare [$w get $ti.1 $ti.end] $path]
-               if {$cmp < 0} {
-                       set lo $ti
-               } elseif {$cmp == 0} {
-                       return $mi
-               } else {
-                       set hi $mi
-               }
-       }
-       return -[expr $lo + 1]
-}
-
 set next_icon_id 0
 
 proc merge_state {path new_state} {
@@ -794,7 +875,8 @@ proc merge_state {path new_state} {
 }
 
 proc display_file {path state} {
-       global ui_index ui_other file_states status_active
+       global ui_index ui_other
+       global file_states file_lists status_active
 
        set old_m [merge_state $path $state]
        if {$status_active} return
@@ -806,7 +888,7 @@ proc display_file {path state} {
        set new_icon [mapicon $new_m $path]
 
        if {$new_w != $old_w} {
-               set lno [bsearch $old_w $path]
+               set lno [lsearch -sorted $file_lists($old_w) $path]
                if {$lno >= 0} {
                        incr lno
                        $old_w conf -state normal
@@ -814,7 +896,10 @@ proc display_file {path state} {
                        $old_w conf -state disabled
                }
 
-               set lno [expr abs([bsearch $new_w $path] + 1) + 1]
+               lappend file_lists($new_w) $path
+               set file_lists($new_w) [lsort $file_lists($new_w)]
+               set lno [lsearch -sorted $file_lists($new_w) $path]
+               incr lno
                $new_w conf -state normal
                $new_w image create $lno.0 \
                        -align center -padx 5 -pady 1 \
@@ -830,7 +915,7 @@ proc display_file {path state} {
 }
 
 proc display_all_files {} {
-       global ui_index ui_other file_states
+       global ui_index ui_other file_states file_lists
 
        $ui_index conf -state normal
        $ui_other conf -state normal
@@ -839,6 +924,7 @@ proc display_all_files {} {
                set s $file_states($path)
                set m [lindex $s 0]
                set w [mapcol $m $path]
+               lappend file_lists($w) $path
                $w image create end \
                        -align center -padx 5 -pady 1 \
                        -name [lindex $s 1] \
@@ -903,39 +989,7 @@ proc toggle_mode {path} {
 
 ######################################################################
 ##
-## config (fetch push pull)
-
-proc load_repo_config {} {
-       global repo_config
-       global cfg_trust_mtime
-
-       array unset repo_config
-       catch {
-               set fd_rc [open "| git repo-config --list" r]
-               while {[gets $fd_rc line] >= 0} {
-                       if {[regexp {^([^=]+)=(.*)$} $line line name value]} {
-                               lappend repo_config($name) $value
-                       }
-               }
-               close $fd_rc
-       }
-
-       if {[catch {set cfg_trust_mtime $repo_config(gui.trustmtime)}]} {
-               set cfg_trust_mtime false
-       }
-}
-
-proc save_my_config {} {
-       global repo_config
-       global cfg_trust_mtime
-
-       if {[catch {set rc_trustMTime $repo_config(gui.trustmtime)}]} {
-               set rc_trustMTime false
-       }
-       if {$cfg_trust_mtime != $rc_trustMTime} {
-               exec git repo-config gui.trustMTime $cfg_trust_mtime
-       }
-}
+## remote management
 
 proc load_all_remotes {} {
        global gitdir all_remotes repo_config
@@ -1398,8 +1452,6 @@ proc do_include_all {} {
        }
 }
 
-set GIT_COMMITTER_IDENT {}
-
 proc do_signoff {} {
        global ui_comm GIT_COMMITTER_IDENT
 
@@ -1417,8 +1469,9 @@ proc do_signoff {} {
 
        set str "Signed-off-by: $GIT_COMMITTER_IDENT"
        if {[$ui_comm get {end -1c linestart} {end -1c}] != $str} {
-               $ui_comm insert end "\n"
-               $ui_comm insert end $str
+               $ui_comm edit separator
+               $ui_comm insert end "\n$str"
+               $ui_comm edit separator
                $ui_comm see end
        }
 }
@@ -1434,19 +1487,16 @@ proc do_commit {} {
 # shift == 1: left click
 #          3: right click  
 proc click {w x y shift wx wy} {
-       global ui_index ui_other
+       global ui_index ui_other file_lists
 
        set pos [split [$w index @$x,$y] .]
        set lno [lindex $pos 0]
        set col [lindex $pos 1]
-       set path [$w get $lno.1 $lno.end]
+       set path [lindex $file_lists($w) [expr $lno - 1]]
        if {$path == {}} return
 
        if {$col > 0 && $shift == 1} {
-               $ui_index tag remove in_diff 0.0 end
-               $ui_other tag remove in_diff 0.0 end
-               $w tag add in_diff $lno.0 [expr $lno + 1].0
-               show_diff $path
+               show_diff $path $w $lno
        }
 }
 
@@ -1479,6 +1529,7 @@ default     {set M1B M1; set M1T M1}
 # -- Menu Bar
 menu .mbar -tearoff 0
 .mbar add cascade -label Project -menu .mbar.project
+.mbar add cascade -label Edit -menu .mbar.edit
 .mbar add cascade -label Commit -menu .mbar.commit
 .mbar add cascade -label Fetch -menu .mbar.fetch
 .mbar add cascade -label Pull -menu .mbar.pull
@@ -1499,6 +1550,40 @@ menu .mbar.project
        -accelerator $M1T-Q \
        -font $mainfont
 
+# -- Edit Menu
+#
+menu .mbar.edit
+.mbar.edit add command -label Undo \
+       -command {catch {[focus] edit undo}} \
+       -accelerator $M1T-Z \
+       -font $mainfont
+.mbar.edit add command -label Redo \
+       -command {catch {[focus] edit redo}} \
+       -accelerator $M1T-Y \
+       -font $mainfont
+.mbar.edit add separator
+.mbar.edit add command -label Cut \
+       -command {catch {tk_textCut [focus]}} \
+       -accelerator $M1T-X \
+       -font $mainfont
+.mbar.edit add command -label Copy \
+       -command {catch {tk_textCopy [focus]}} \
+       -accelerator $M1T-C \
+       -font $mainfont
+.mbar.edit add command -label Paste \
+       -command {catch {tk_textPaste [focus]; [focus] see insert}} \
+       -accelerator $M1T-V \
+       -font $mainfont
+.mbar.edit add command -label Delete \
+       -command {catch {[focus] delete sel.first sel.last}} \
+       -accelerator Del \
+       -font $mainfont
+.mbar.edit add separator
+.mbar.edit add command -label {Select All} \
+       -command {catch {[focus] tag add sel 0.0 end}} \
+       -accelerator $M1T-A \
+       -font $mainfont
+
 # -- Commit Menu
 menu .mbar.commit
 .mbar.commit add command -label Rescan \
@@ -1514,7 +1599,7 @@ lappend disable_on_lock \
        [list .mbar.commit entryconf [.mbar.commit index last] -state]
 .mbar.commit add command -label {Include All Files} \
        -command do_include_all \
-       -accelerator $M1T-U \
+       -accelerator $M1T-I \
        -font $mainfont
 lappend disable_on_lock \
        [list .mbar.commit entryconf [.mbar.commit index last] -state]
@@ -1540,7 +1625,8 @@ menu .mbar.push
 
 # -- Options Menu
 menu .mbar.options
-.mbar.options add checkbutton -label {Trust File Modification Timestamps} \
+.mbar.options add checkbutton \
+       -label {Trust File Modification Timestamps} \
        -offvalue false \
        -onvalue true \
        -variable cfg_trust_mtime
@@ -1650,6 +1736,9 @@ trace add variable commit_type write {uplevel #0 {
        *       {$ui_coml conf -text {Commit Message:}}
 }}
 text $ui_comm -background white -borderwidth 1 \
+       -undo true \
+       -maxundo 20 \
+       -autoseparators true \
        -relief sunken \
        -width 75 -height 9 -wrap none \
        -font $difffont \
@@ -1724,16 +1813,47 @@ label .status -textvariable ui_status_value \
        -font $mainfont
 pack .status -anchor w -side bottom -fill x
 
+# -- Load geometry
+catch {
+wm geometry . [lindex $repo_config(gui.geometry) 0 0]
+eval .vpane sash place 0 [lindex $repo_config(gui.geometry) 0 1]
+eval .vpane.files sash place 0 [lindex $repo_config(gui.geometry) 0 2]
+}
+
 # -- Key Bindings
 bind $ui_comm <$M1B-Key-Return> {do_commit;break}
+bind $ui_comm <$M1B-Key-i> {do_include_all;break}
+bind $ui_comm <$M1B-Key-I> {do_include_all;break}
+bind $ui_comm <$M1B-Key-x> {tk_textCut %W;break}
+bind $ui_comm <$M1B-Key-X> {tk_textCut %W;break}
+bind $ui_comm <$M1B-Key-c> {tk_textCopy %W;break}
+bind $ui_comm <$M1B-Key-C> {tk_textCopy %W;break}
+bind $ui_comm <$M1B-Key-v> {tk_textPaste %W; %W see insert; break}
+bind $ui_comm <$M1B-Key-V> {tk_textPaste %W; %W see insert; break}
+bind $ui_comm <$M1B-Key-a> {%W tag add sel 0.0 end;break}
+bind $ui_comm <$M1B-Key-A> {%W tag add sel 0.0 end;break}
+
+bind $ui_diff <$M1B-Key-x> {tk_textCopy %W;break}
+bind $ui_diff <$M1B-Key-X> {tk_textCopy %W;break}
+bind $ui_diff <$M1B-Key-c> {tk_textCopy %W;break}
+bind $ui_diff <$M1B-Key-C> {tk_textCopy %W;break}
+bind $ui_diff <$M1B-Key-v> {break}
+bind $ui_diff <$M1B-Key-V> {break}
+bind $ui_diff <$M1B-Key-a> {%W tag add sel 0.0 end;break}
+bind $ui_diff <$M1B-Key-A> {%W tag add sel 0.0 end;break}
+bind $ui_diff <Key-Up>     {catch {%W yview scroll -1 units};break}
+bind $ui_diff <Key-Down>   {catch {%W yview scroll  1 units};break}
+bind $ui_diff <Key-Left>   {catch {%W xview scroll -1 units};break}
+bind $ui_diff <Key-Right>  {catch {%W xview scroll  1 units};break}
+
 bind .   <Destroy> do_quit
 bind all <Key-F5> do_rescan
 bind all <$M1B-Key-r> do_rescan
 bind all <$M1B-Key-R> do_rescan
 bind .   <$M1B-Key-s> do_signoff
 bind .   <$M1B-Key-S> do_signoff
-bind .   <$M1B-Key-u> do_include_all
-bind .   <$M1B-Key-U> do_include_all
+bind .   <$M1B-Key-i> do_include_all
+bind .   <$M1B-Key-I> do_include_all
 bind .   <$M1B-Key-Return> do_commit
 bind all <$M1B-Key-q> do_quit
 bind all <$M1B-Key-Q> do_quit
@@ -1746,34 +1866,8 @@ foreach i [list $ui_index $ui_other] {
 }
 unset i M1B M1T
 
-######################################################################
-##
-## main
-
-set appname [lindex [file split $argv0] end]
-set gitdir {}
-
-if {[catch {set cdup [exec git rev-parse --show-cdup]} err]} {
-       show_msg {} . "Cannot find the git directory: $err"
-       exit 1
-}
-if {$cdup != ""} {
-       cd $cdup
-}
-unset cdup
-
-if {[catch {set gitdir [exec git rev-parse --git-dir]} err]} {
-       show_msg {} . "Cannot find the git directory: $err"
-       exit 1
-}
-
-if {$appname == {git-citool}} {
-       set single_commit 1
-}
-
 wm title . "$appname ([file normalize [file dirname $gitdir]])"
 focus -force $ui_comm
-load_repo_config
 load_all_remotes
 populate_remote_menu .mbar.fetch From fetch_from
 populate_remote_menu .mbar.push To push_to