Merge branch 'master' of https://github.com/prati0100/git-gui
authorJunio C Hamano <gitster@pobox.com>
Wed, 18 Sep 2019 18:22:11 +0000 (11:22 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 18 Sep 2019 18:22:11 +0000 (11:22 -0700)
* 'master' of https://github.com/prati0100/git-gui:
  git-gui: add hotkey to toggle "Amend Last Commit"
  git-gui: add horizontal scrollbar to commit buffer
  git-gui: convert new/amend commit radiobutton to checkbutton
  git-gui: add hotkeys to set widget focus
  git-gui: allow undoing last revert
  git-gui: return early when patch fails to apply
  git-gui: allow reverting selected hunk
  git-gui: allow reverting selected lines

git-gui/git-gui.sh
git-gui/lib/checkout_op.tcl
git-gui/lib/commit.tcl
git-gui/lib/diff.tcl
git-gui/lib/index.tcl

index 6ec562d..fd476b6 100755 (executable)
@@ -1340,6 +1340,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
@@ -1347,8 +1348,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"
@@ -1434,7 +1436,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
@@ -1443,7 +1445,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
 }
 
@@ -2494,7 +2496,7 @@ proc force_first_diff {after} {
 
 proc toggle_or_diff {mode w args} {
        global file_states file_lists current_diff_path ui_index ui_workdir
-       global last_clicked selected_paths
+       global last_clicked selected_paths file_lists_last_clicked
 
        if {$mode eq "click"} {
                foreach {x y} $args break
@@ -2551,6 +2553,8 @@ proc toggle_or_diff {mode w args} {
        $ui_index tag remove in_sel 0.0 end
        $ui_workdir tag remove in_sel 0.0 end
 
+       set file_lists_last_clicked($w) $path
+
        # Determine the state of the file
        if {[info exists file_states($path)]} {
                set state [lindex $file_states($path) 0]
@@ -2664,6 +2668,32 @@ proc show_less_context {} {
        }
 }
 
+proc focus_widget {widget} {
+       global file_lists last_clicked selected_paths
+       global file_lists_last_clicked
+
+       if {[llength $file_lists($widget)] > 0} {
+               set path $file_lists_last_clicked($widget)
+               set index [lsearch -sorted -exact $file_lists($widget) $path]
+               if {$index < 0} {
+                       set index 0
+                       set path [lindex $file_lists($widget) $index]
+               }
+
+               focus $widget
+               set last_clicked [list $widget [expr $index + 1]]
+               array unset selected_paths
+               set selected_paths($path) 1
+               show_diff $path $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
@@ -2852,19 +2882,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]
 
@@ -3352,18 +3374,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]
 }
@@ -3388,7 +3402,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
@@ -3402,10 +3415,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
@@ -3621,15 +3640,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
@@ -3708,7 +3743,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)]} {
@@ -3722,19 +3757,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
@@ -3745,11 +3789,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
        }
 }
@@ -3876,6 +3944,8 @@ 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}
@@ -3892,6 +3962,14 @@ foreach i [list $ui_index $ui_workdir] {
 }
 unset i
 
+bind .   <Alt-Key-1> {focus_widget $::ui_workdir}
+bind .   <Alt-Key-2> {focus_widget $::ui_index}
+bind .   <Alt-Key-3> {focus $::ui_diff}
+bind .   <Alt-Key-4> {focus $::ui_comm}
+
+set file_lists_last_clicked($ui_index) {}
+set file_lists_last_clicked($ui_workdir) {}
+
 set file_lists($ui_index) [list]
 set file_lists($ui_workdir) [list]
 
index 9e7412c..a522829 100644 (file)
@@ -389,7 +389,7 @@ $err
 }
 
 method _after_readtree {} {
-       global selected_commit_type commit_type HEAD MERGE_HEAD PARENT
+       global commit_type HEAD MERGE_HEAD PARENT
        global current_branch is_detached
        global ui_comm
 
@@ -490,12 +490,12 @@ method _update_repo_state {} {
        #    amend mode our file lists are accurate and we can avoid
        #    the rescan.
        #
-       global selected_commit_type commit_type HEAD MERGE_HEAD PARENT
+       global commit_type_is_amend commit_type HEAD MERGE_HEAD PARENT
        global ui_comm
 
        unlock_index
        set name [_name $this]
-       set selected_commit_type new
+       set commit_type_is_amend 0
        if {[string match amend* $commit_type]} {
                $ui_comm delete 0.0 end
                $ui_comm edit reset
index 75ea965..b516aa2 100644 (file)
@@ -333,7 +333,7 @@ proc commit_writetree {curHEAD msg_p} {
 proc commit_committree {fd_wt curHEAD msg_p} {
        global HEAD PARENT MERGE_HEAD commit_type commit_author
        global current_branch
-       global ui_comm selected_commit_type
+       global ui_comm commit_type_is_amend
        global file_states selected_paths rescan_active
        global repo_config
        global env
@@ -467,8 +467,8 @@ A rescan will be automatically started now.
 
        # -- Update in memory status
        #
-       set selected_commit_type new
        set commit_type normal
+       set commit_type_is_amend 0
        set HEAD $cmt_id
        set PARENT $cmt_id
        set MERGE_HEAD [list]
index 68c4a6c..958a0fa 100644 (file)
@@ -55,7 +55,7 @@ proc reshow_diff {{after {}}} {
 
 proc force_diff_encoding {enc} {
        global current_diff_path
-       
+
        if {$current_diff_path ne {}} {
                force_path_encoding $current_diff_path $enc
                reshow_diff
@@ -567,24 +567,31 @@ proc read_diff {fd conflict_size cont_info} {
        }
 }
 
-proc apply_hunk {x y} {
+proc apply_or_revert_hunk {x y revert} {
        global current_diff_path current_diff_header current_diff_side
-       global ui_diff ui_index file_states
+       global ui_diff ui_index file_states last_revert last_revert_enc
 
        if {$current_diff_path eq {} || $current_diff_header eq {}} return
        if {![lock_index apply_hunk]} return
 
-       set apply_cmd {apply --cached --whitespace=nowarn}
+       set apply_cmd {apply --whitespace=nowarn}
        set mi [lindex $file_states($current_diff_path) 0]
        if {$current_diff_side eq $ui_index} {
                set failed_msg [mc "Failed to unstage selected hunk."]
-               lappend apply_cmd --reverse
+               lappend apply_cmd --reverse --cached
                if {[string index $mi 0] ne {M}} {
                        unlock_index
                        return
                }
        } else {
-               set failed_msg [mc "Failed to stage selected hunk."]
+               if {$revert} {
+                       set failed_msg [mc "Failed to revert selected hunk."]
+                       lappend apply_cmd --reverse
+               } else {
+                       set failed_msg [mc "Failed to stage selected hunk."]
+                       lappend apply_cmd --cached
+               }
+
                if {[string index $mi 1] ne {M}} {
                        unlock_index
                        return
@@ -603,29 +610,40 @@ proc apply_hunk {x y} {
                set e_lno end
        }
 
+       set wholepatch "$current_diff_header[$ui_diff get $s_lno $e_lno]"
+
        if {[catch {
                set enc [get_path_encoding $current_diff_path]
                set p [eval git_write $apply_cmd]
                fconfigure $p -translation binary -encoding $enc
-               puts -nonewline $p $current_diff_header
-               puts -nonewline $p [$ui_diff get $s_lno $e_lno]
+               puts -nonewline $p $wholepatch
                close $p} err]} {
                error_popup "$failed_msg\n\n$err"
                unlock_index
                return
        }
 
+       if {$revert} {
+               # Save a copy of this patch for undoing reverts.
+               set last_revert $wholepatch
+               set last_revert_enc $enc
+       }
+
        $ui_diff conf -state normal
        $ui_diff delete $s_lno $e_lno
        $ui_diff conf -state disabled
 
+       # Check if the hunk was the last one in the file.
        if {[$ui_diff get 1.0 end] eq "\n"} {
                set o _
        } else {
                set o ?
        }
 
-       if {$current_diff_side eq $ui_index} {
+       # Update the status flags.
+       if {$revert} {
+               set mi [string index $mi 0]$o
+       } elseif {$current_diff_side eq $ui_index} {
                set mi ${o}M
        } elseif {[string index $mi 0] eq {_}} {
                set mi M$o
@@ -640,9 +658,9 @@ proc apply_hunk {x y} {
        }
 }
 
-proc apply_range_or_line {x y} {
+proc apply_or_revert_range_or_line {x y revert} {
        global current_diff_path current_diff_header current_diff_side
-       global ui_diff ui_index file_states
+       global ui_diff ui_index file_states last_revert
 
        set selected [$ui_diff tag nextrange sel 0.0]
 
@@ -660,19 +678,27 @@ proc apply_range_or_line {x y} {
        if {$current_diff_path eq {} || $current_diff_header eq {}} return
        if {![lock_index apply_hunk]} return
 
-       set apply_cmd {apply --cached --whitespace=nowarn}
+       set apply_cmd {apply --whitespace=nowarn}
        set mi [lindex $file_states($current_diff_path) 0]
        if {$current_diff_side eq $ui_index} {
                set failed_msg [mc "Failed to unstage selected line."]
                set to_context {+}
-               lappend apply_cmd --reverse
+               lappend apply_cmd --reverse --cached
                if {[string index $mi 0] ne {M}} {
                        unlock_index
                        return
                }
        } else {
-               set failed_msg [mc "Failed to stage selected line."]
-               set to_context {-}
+               if {$revert} {
+                       set failed_msg [mc "Failed to revert selected line."]
+                       set to_context {+}
+                       lappend apply_cmd --reverse
+               } else {
+                       set failed_msg [mc "Failed to stage selected line."]
+                       set to_context {-}
+                       lappend apply_cmd --cached
+               }
+
                if {[string index $mi 1] ne {M}} {
                        unlock_index
                        return
@@ -830,7 +856,47 @@ proc apply_range_or_line {x y} {
                puts -nonewline $p $wholepatch
                close $p} err]} {
                error_popup "$failed_msg\n\n$err"
+               unlock_index
+               return
+       }
+
+       if {$revert} {
+               # Save a copy of this patch for undoing reverts.
+               set last_revert $current_diff_header$wholepatch
+               set last_revert_enc $enc
        }
 
        unlock_index
 }
+
+# Undo the last line/hunk reverted. When hunks and lines are reverted, a copy
+# of the diff applied is saved. Re-apply that diff to undo the revert.
+#
+# Right now, we only use a single variable to hold the copy, and not a
+# stack/deque for simplicity, so multiple undos are not possible. Maybe this
+# can be added if the need for something like this is felt in the future.
+proc undo_last_revert {} {
+       global last_revert current_diff_path current_diff_header
+       global last_revert_enc
+
+       if {$last_revert eq {}} return
+       if {![lock_index apply_hunk]} return
+
+       set apply_cmd {apply --whitespace=nowarn}
+       set failed_msg [mc "Failed to undo last revert."]
+
+       if {[catch {
+               set enc $last_revert_enc
+               set p [eval git_write $apply_cmd]
+               fconfigure $p -translation binary -encoding $enc
+               puts -nonewline $p $last_revert
+               close $p} err]} {
+               error_popup "$failed_msg\n\n$err"
+               unlock_index
+               return
+       }
+
+       set last_revert {}
+
+       unlock_index
+}
index b588db1..e07b7a3 100644 (file)
@@ -466,19 +466,19 @@ proc do_revert_selection {} {
 }
 
 proc do_select_commit_type {} {
-       global commit_type selected_commit_type
+       global commit_type commit_type_is_amend
 
-       if {$selected_commit_type eq {new}
+       if {$commit_type_is_amend == 0
                && [string match amend* $commit_type]} {
                create_new_commit
-       } elseif {$selected_commit_type eq {amend}
+       } elseif {$commit_type_is_amend == 1
                && ![string match amend* $commit_type]} {
                load_last_commit
 
                # The amend request was rejected...
                #
                if {![string match amend* $commit_type]} {
-                       set selected_commit_type new
+                       set commit_type_is_amend 0
                }
        }
 }