]> git.ipfire.org Git - thirdparty/git.git/blobdiff - git-gui.sh
git-gui: Handle Ctrl + BS/Del in the commit msg
[thirdparty/git.git] / git-gui.sh
index 5dae8da64ff0e792891b51d1aee193dc088e40de..49bd86e6353b8ae458cfd6f10106dd93d8d0b795 100755 (executable)
@@ -24,15 +24,14 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA}]
+along with this program; if not, see <http://www.gnu.org/licenses/>.}]
 
 ######################################################################
 ##
 ## Tcl/Tk sanity check
 
-if {[catch {package require Tcl 8.4} err]
- || [catch {package require Tk  8.4} err]
+if {[catch {package require Tcl 8.5} err]
+ || [catch {package require Tk  8.5} err]
 } {
        catch {wm withdraw .}
        tk_messageBox \
@@ -685,6 +684,7 @@ proc load_current_branch {} {
        global current_branch is_detached
 
        set fd [open [gitdir HEAD] r]
+       fconfigure $fd -translation binary -encoding utf-8
        if {[gets $fd ref] < 1} {
                set ref {}
        }
@@ -1341,6 +1341,7 @@ set HEAD {}
 set PARENT {}
 set MERGE_HEAD [list]
 set commit_type {}
+set commit_type_is_amend 0
 set empty_tree {}
 set current_branch {}
 set is_detached 0
@@ -1348,8 +1349,9 @@ set current_diff_path {}
 set is_3way_diff 0
 set is_submodule_diff 0
 set is_conflict_diff 0
-set selected_commit_type new
 set diff_empty_count 0
+set last_revert {}
+set last_revert_enc {}
 
 set nullid "0000000000000000000000000000000000000000"
 set nullid2 "0000000000000000000000000000000000000001"
@@ -1435,7 +1437,7 @@ proc PARENT {} {
 }
 
 proc force_amend {} {
-       global selected_commit_type
+       global commit_type_is_amend
        global HEAD PARENT MERGE_HEAD commit_type
 
        repository_state newType newHEAD newMERGE_HEAD
@@ -1444,7 +1446,7 @@ proc force_amend {} {
        set MERGE_HEAD $newMERGE_HEAD
        set commit_type $newType
 
-       set selected_commit_type amend
+       set commit_type_is_amend 1
        do_select_commit_type
 }
 
@@ -1796,10 +1798,10 @@ proc ui_status {msg} {
        }
 }
 
-proc ui_ready {{test {}}} {
+proc ui_ready {} {
        global main_status
        if {[info exists main_status]} {
-               $main_status show [mc "Ready."] $test
+               $main_status show [mc "Ready."]
        }
 }
 
@@ -2149,8 +2151,6 @@ proc incr_font_size {font {amt 1}} {
 ##
 ## ui commands
 
-set starting_gitk_msg [mc "Starting gitk... please wait..."]
-
 proc do_gitk {revs {is_submodule false}} {
        global current_diff_path file_states current_diff_side ui_index
        global _gitdir _gitworktree
@@ -2205,9 +2205,12 @@ proc do_gitk {revs {is_submodule false}} {
                set env(GIT_WORK_TREE) $_gitworktree
                cd $pwd
 
-               ui_status $::starting_gitk_msg
-               after 10000 {
-                       ui_ready $starting_gitk_msg
+               if {[info exists main_status]} {
+                       set status_operation [$::main_status \
+                               start \
+                               [mc "Starting %s... please wait..." "gitk"]]
+
+                       after 3500 [list $status_operation stop]
                }
        }
 }
@@ -2239,16 +2242,16 @@ proc do_git_gui {} {
                set env(GIT_WORK_TREE) $_gitworktree
                cd $pwd
 
-               ui_status $::starting_gitk_msg
-               after 10000 {
-                       ui_ready $starting_gitk_msg
-               }
+               set status_operation [$::main_status \
+                       start \
+                       [mc "Starting %s... please wait..." "git-gui"]]
+
+               after 3500 [list $status_operation stop]
        }
 }
 
-proc do_explore {} {
-       global _gitworktree
-       set explorer {}
+# Get the system-specific explorer app/command.
+proc get_explorer {} {
        if {[is_Cygwin] || [is_Windows]} {
                set explorer "explorer.exe"
        } elseif {[is_MacOSX]} {
@@ -2257,9 +2260,23 @@ proc do_explore {} {
                # freedesktop.org-conforming system is our best shot
                set explorer "xdg-open"
        }
+       return $explorer
+}
+
+proc do_explore {} {
+       global _gitworktree
+       set explorer [get_explorer]
        eval exec $explorer [list [file nativename $_gitworktree]] &
 }
 
+# Open file relative to the working tree by the default associated app.
+proc do_file_open {file} {
+       global _gitworktree
+       set explorer [get_explorer]
+       set full_file_path [file join $_gitworktree $file]
+       exec $explorer [file nativename $full_file_path] &
+}
+
 set is_quitting 0
 set ret_code    1
 
@@ -2502,9 +2519,28 @@ proc toggle_or_diff {mode w args} {
                set pos [split [$w index @$x,$y] .]
                foreach {lno col} $pos break
        } else {
+               if {$mode eq "toggle"} {
+                       if {$w eq $ui_workdir} {
+                               do_add_selection
+                               set last_clicked {}
+                               return
+                       }
+                       if {$w eq $ui_index} {
+                               do_unstage_selection
+                               set last_clicked {}
+                               return
+                       }
+               }
+
                if {$last_clicked ne {}} {
                        set lno [lindex $last_clicked 1]
                } else {
+                       if {![info exists file_lists]
+                               || ![info exists file_lists($w)]
+                               || [llength $file_lists($w)] == 0} {
+                               set last_clicked {}
+                               return
+                       }
                        set lno [expr {int([lindex [$w tag ranges in_diff] 0])}]
                }
                if {$mode eq "toggle"} {
@@ -2515,7 +2551,13 @@ proc toggle_or_diff {mode w args} {
                }
        }
 
-       set path [lindex $file_lists($w) [expr {$lno - 1}]]
+       if {![info exists file_lists]
+               || ![info exists file_lists($w)]
+               || [llength $file_lists($w)] < $lno - 1} {
+               set path {}
+       } else {
+               set path [lindex $file_lists($w) [expr {$lno - 1}]]
+       }
        if {$path eq {}} {
                set last_clicked {}
                return
@@ -2558,12 +2600,12 @@ proc toggle_or_diff {mode w args} {
                        update_indexinfo \
                                "Unstaging [short_path $path] from commit" \
                                [list $path] \
-                               [concat $after [list ui_ready]]
+                               [concat $after {ui_ready;}]
                } elseif {$w eq $ui_workdir} {
                        update_index \
                                "Adding [short_path $path]" \
                                [list $path] \
-                               [concat $after [list ui_ready]]
+                               [concat $after {ui_ready;}]
                }
        } else {
                set selected_paths($path) 1
@@ -2662,6 +2704,12 @@ proc focus_widget {widget} {
        }
 }
 
+proc toggle_commit_type {} {
+       global commit_type_is_amend
+       set commit_type_is_amend [expr !$commit_type_is_amend]
+       do_select_commit_type
+}
+
 ######################################################################
 ##
 ## ui construction
@@ -2704,10 +2752,18 @@ if {![is_bare]} {
 }
 
 if {[is_Windows]} {
+       # Use /git-bash.exe if available
+       set normalized [file normalize $::argv0]
+       regsub "/mingw../libexec/git-core/git-gui$" \
+               $normalized "/git-bash.exe" cmdLine
+       if {$cmdLine != $normalized && [file exists $cmdLine]} {
+               set cmdLine [list "Git Bash" $cmdLine &]
+       } else {
+               set cmdLine [list "Git Bash" bash --login -l &]
+       }
        .mbar.repository add command \
                -label [mc "Git Bash"] \
-               -command {eval exec [auto_execok start] \
-                                         [list "Git Bash" bash --login -l &]}
+               -command {eval exec [auto_execok start] $cmdLine}
 }
 
 if {[is_Windows] || ![is_bare]} {
@@ -2850,19 +2906,11 @@ if {[is_enabled multicommit] || [is_enabled singlecommit]} {
        menu .mbar.commit
 
        if {![is_enabled nocommit]} {
-               .mbar.commit add radiobutton \
-                       -label [mc "New Commit"] \
-                       -command do_select_commit_type \
-                       -variable selected_commit_type \
-                       -value new
-               lappend disable_on_lock \
-                       [list .mbar.commit entryconf [.mbar.commit index last] -state]
-
-               .mbar.commit add radiobutton \
+               .mbar.commit add checkbutton \
                        -label [mc "Amend Last Commit"] \
-                       -command do_select_commit_type \
-                       -variable selected_commit_type \
-                       -value amend
+                       -accelerator $M1T-E \
+                       -variable commit_type_is_amend \
+                       -command do_select_commit_type
                lappend disable_on_lock \
                        [list .mbar.commit entryconf [.mbar.commit index last] -state]
 
@@ -3028,8 +3076,23 @@ unset doc_path doc_url
 wm protocol . WM_DELETE_WINDOW do_quit
 bind all <$M1B-Key-q> do_quit
 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 m1b_w_script {
+       set toplvl_win [winfo toplevel %W]
+
+       # If we are destroying the main window, we should call do_quit to take
+       # care of cleanup before exiting the program.
+       if {$toplvl_win eq "."} {
+               do_quit
+       } else {
+               destroy $toplvl_win
+       }
+}
+
+bind all <$M1B-Key-w> $m1b_w_script
+bind all <$M1B-Key-W> $m1b_w_script
+
+unset m1b_w_script
 
 set subcommand_args {}
 proc usage {} {
@@ -3335,18 +3398,10 @@ set ui_comm .vpane.lower.commarea.buffer.frame.t
 set ui_coml .vpane.lower.commarea.buffer.header.l
 
 if {![is_enabled nocommit]} {
-       ${NS}::radiobutton .vpane.lower.commarea.buffer.header.new \
-               -text [mc "New Commit"] \
-               -command do_select_commit_type \
-               -variable selected_commit_type \
-               -value new
-       lappend disable_on_lock \
-               [list .vpane.lower.commarea.buffer.header.new conf -state]
-       ${NS}::radiobutton .vpane.lower.commarea.buffer.header.amend \
+       ${NS}::checkbutton .vpane.lower.commarea.buffer.header.amend \
                -text [mc "Amend Last Commit"] \
-               -command do_select_commit_type \
-               -variable selected_commit_type \
-               -value amend
+               -variable commit_type_is_amend \
+               -command do_select_commit_type
        lappend disable_on_lock \
                [list .vpane.lower.commarea.buffer.header.amend conf -state]
 }
@@ -3371,7 +3426,6 @@ pack $ui_coml -side left -fill x
 
 if {![is_enabled nocommit]} {
        pack .vpane.lower.commarea.buffer.header.amend -side right
-       pack .vpane.lower.commarea.buffer.header.new -side right
 }
 
 textframe .vpane.lower.commarea.buffer.frame
@@ -3385,10 +3439,16 @@ ttext $ui_comm -background white -foreground black \
        -relief sunken \
        -width $repo_config(gui.commitmsgwidth) -height 9 -wrap none \
        -font font_diff \
+       -xscrollcommand {.vpane.lower.commarea.buffer.frame.sbx set} \
        -yscrollcommand {.vpane.lower.commarea.buffer.frame.sby set}
+${NS}::scrollbar .vpane.lower.commarea.buffer.frame.sbx \
+       -orient horizontal \
+       -command [list $ui_comm xview]
 ${NS}::scrollbar .vpane.lower.commarea.buffer.frame.sby \
+       -orient vertical \
        -command [list $ui_comm yview]
 
+pack .vpane.lower.commarea.buffer.frame.sbx -side bottom -fill x
 pack .vpane.lower.commarea.buffer.frame.sby -side right -fill y
 pack $ui_comm -side left -fill y
 pack .vpane.lower.commarea.buffer.header -side top -fill x
@@ -3468,9 +3528,11 @@ tlabel .vpane.lower.diff.header.file \
        -justify left
 tlabel .vpane.lower.diff.header.path \
        -background gold \
-       -foreground black \
+       -foreground blue \
        -anchor w \
-       -justify left
+       -justify left \
+       -font [eval font create [font configure font_ui] -underline 1] \
+       -cursor hand2
 pack .vpane.lower.diff.header.status -side left
 pack .vpane.lower.diff.header.file -side left
 pack .vpane.lower.diff.header.path -fill x
@@ -3485,8 +3547,12 @@ $ctxm add command \
                        -type STRING \
                        -- $current_diff_path
        }
+$ctxm add command \
+       -label [mc Open] \
+       -command {do_file_open $current_diff_path}
 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
 bind_button3 .vpane.lower.diff.header.path "tk_popup $ctxm %X %Y"
+bind .vpane.lower.diff.header.path <Button-1> {do_file_open $current_diff_path}
 
 # -- Diff Body
 #
@@ -3545,6 +3611,9 @@ $ui_diff tag conf d_s- \
 $ui_diff tag conf d< \
        -foreground orange \
        -font font_diffbold
+$ui_diff tag conf d| \
+       -foreground orange \
+       -font font_diffbold
 $ui_diff tag conf d= \
        -foreground orange \
        -font font_diffbold
@@ -3604,15 +3673,31 @@ set ctxm .vpane.lower.diff.body.ctxm
 menu $ctxm -tearoff 0
 $ctxm add command \
        -label [mc "Apply/Reverse Hunk"] \
-       -command {apply_hunk $cursorX $cursorY}
+       -command {apply_or_revert_hunk $cursorX $cursorY 0}
 set ui_diff_applyhunk [$ctxm index last]
 lappend diff_actions [list $ctxm entryconf $ui_diff_applyhunk -state]
 $ctxm add command \
        -label [mc "Apply/Reverse Line"] \
-       -command {apply_range_or_line $cursorX $cursorY; do_rescan}
+       -command {apply_or_revert_range_or_line $cursorX $cursorY 0; do_rescan}
 set ui_diff_applyline [$ctxm index last]
 lappend diff_actions [list $ctxm entryconf $ui_diff_applyline -state]
 $ctxm add separator
+$ctxm add command \
+       -label [mc "Revert Hunk"] \
+       -command {apply_or_revert_hunk $cursorX $cursorY 1}
+set ui_diff_reverthunk [$ctxm index last]
+lappend diff_actions [list $ctxm entryconf $ui_diff_reverthunk -state]
+$ctxm add command \
+       -label [mc "Revert Line"] \
+       -command {apply_or_revert_range_or_line $cursorX $cursorY 1; do_rescan}
+set ui_diff_revertline [$ctxm index last]
+lappend diff_actions [list $ctxm entryconf $ui_diff_revertline -state]
+$ctxm add command \
+       -label [mc "Undo Last Revert"] \
+       -command {undo_last_revert; do_rescan}
+set ui_diff_undorevert [$ctxm index last]
+lappend diff_actions [list $ctxm entryconf $ui_diff_undorevert -state]
+$ctxm add separator
 $ctxm add command \
        -label [mc "Show Less Context"] \
        -command show_less_context
@@ -3691,7 +3776,7 @@ proc has_textconv {path} {
 }
 
 proc popup_diff_menu {ctxm ctxmmg ctxmsm x y X Y} {
-       global current_diff_path file_states
+       global current_diff_path file_states last_revert
        set ::cursorX $x
        set ::cursorY $y
        if {[info exists file_states($current_diff_path)]} {
@@ -3705,19 +3790,28 @@ proc popup_diff_menu {ctxm ctxmmg ctxmsm x y X Y} {
                tk_popup $ctxmsm $X $Y
        } else {
                set has_range [expr {[$::ui_diff tag nextrange sel 0.0] != {}}]
+               set u [mc "Undo Last Revert"]
                if {$::ui_index eq $::current_diff_side} {
                        set l [mc "Unstage Hunk From Commit"]
+                       set h [mc "Revert Hunk"]
+
                        if {$has_range} {
                                set t [mc "Unstage Lines From Commit"]
+                               set r [mc "Revert Lines"]
                        } else {
                                set t [mc "Unstage Line From Commit"]
+                               set r [mc "Revert Line"]
                        }
                } else {
                        set l [mc "Stage Hunk For Commit"]
+                       set h [mc "Revert Hunk"]
+
                        if {$has_range} {
                                set t [mc "Stage Lines For Commit"]
+                               set r [mc "Revert Lines"]
                        } else {
                                set t [mc "Stage Line For Commit"]
+                               set r [mc "Revert Line"]
                        }
                }
                if {$::is_3way_diff
@@ -3728,11 +3822,35 @@ proc popup_diff_menu {ctxm ctxmmg ctxmsm x y X Y} {
                        || [string match {T?} $state]
                        || [has_textconv $current_diff_path]} {
                        set s disabled
+                       set revert_state disabled
                } else {
                        set s normal
+
+                       # Only allow reverting changes in the working tree. If
+                       # the user wants to revert changes in the index, they
+                       # need to unstage those first.
+                       if {$::ui_workdir eq $::current_diff_side} {
+                               set revert_state normal
+                       } else {
+                               set revert_state disabled
+                       }
+               }
+
+               if {$last_revert eq {}} {
+                       set undo_state disabled
+               } else {
+                       set undo_state normal
                }
+
                $ctxm entryconf $::ui_diff_applyhunk -state $s -label $l
                $ctxm entryconf $::ui_diff_applyline -state $s -label $t
+               $ctxm entryconf $::ui_diff_revertline -state $revert_state \
+                       -label $r
+               $ctxm entryconf $::ui_diff_reverthunk -state $revert_state \
+                       -label $h
+               $ctxm entryconf $::ui_diff_undorevert -state $undo_state \
+                       -label $u
+
                tk_popup $ctxm $X $Y
        }
 }
@@ -3810,6 +3928,8 @@ bind $ui_comm <$M1B-Key-KP_Subtract> {show_less_context;break}
 bind $ui_comm <$M1B-Key-equal> {show_more_context;break}
 bind $ui_comm <$M1B-Key-plus> {show_more_context;break}
 bind $ui_comm <$M1B-Key-KP_Add> {show_more_context;break}
+bind $ui_comm <$M1B-Key-BackSpace> {event generate %W <Meta-Delete>;break}
+bind $ui_comm <$M1B-Key-Delete> {event generate %W <Meta-d>;break}
 
 bind $ui_diff <$M1B-Key-x> {tk_textCopy %W;break}
 bind $ui_diff <$M1B-Key-X> {tk_textCopy %W;break}
@@ -3859,12 +3979,15 @@ bind .   <$M1B-Key-j> do_revert_selection
 bind .   <$M1B-Key-J> do_revert_selection
 bind .   <$M1B-Key-i> do_add_all
 bind .   <$M1B-Key-I> do_add_all
+bind .   <$M1B-Key-e> toggle_commit_type
+bind .   <$M1B-Key-E> toggle_commit_type
 bind .   <$M1B-Key-minus> {show_less_context;break}
 bind .   <$M1B-Key-KP_Subtract> {show_less_context;break}
 bind .   <$M1B-Key-equal> {show_more_context;break}
 bind .   <$M1B-Key-plus> {show_more_context;break}
 bind .   <$M1B-Key-KP_Add> {show_more_context;break}
 bind .   <$M1B-Key-Return> do_commit
+bind .   <$M1B-Key-KP_Enter> do_commit
 foreach i [list $ui_index $ui_workdir] {
        bind $i <Button-1>       { toggle_or_diff click %W %x %y; break }
        bind $i <$M1B-Button-1>  { add_one_to_selection %W %x %y; break }
@@ -4060,6 +4183,9 @@ if {$picked && [is_config_true gui.autoexplore]} {
        do_explore
 }
 
+# Clear "Initializing..." status
+after 500 {$main_status show ""}
+
 # Local variables:
 # mode: tcl
 # indent-tabs-mode: t