Skip to content

Commit 87fb597

Browse files
committed
Merge git://repo.or.cz/git-gui
* git://repo.or.cz/git-gui: git-gui: Implement "Stage/Unstage Line" git-gui: Don't select the wrong file if the last listed file is staged. git-gui: Fix accidental staged state toggle when clicking top pixel row git-gui: Move on to the next filename after staging/unstaging a change
2 parents 44701c6 + 5821988 commit 87fb597

File tree

2 files changed

+125
-3
lines changed

2 files changed

+125
-3
lines changed

git-gui/git-gui.sh

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1774,6 +1774,11 @@ proc do_commit {} {
17741774
commit_tree
17751775
}
17761776
1777+
proc next_diff {} {
1778+
global next_diff_p next_diff_w next_diff_i
1779+
show_diff $next_diff_p $next_diff_w $next_diff_i
1780+
}
1781+
17771782
proc toggle_or_diff {w x y} {
17781783
global file_states file_lists current_diff_path ui_index ui_workdir
17791784
global last_clicked selected_paths
@@ -1792,12 +1797,34 @@ proc toggle_or_diff {w x y} {
17921797
$ui_index tag remove in_sel 0.0 end
17931798
$ui_workdir tag remove in_sel 0.0 end
17941799
1795-
if {$col == 0} {
1796-
if {$current_diff_path eq $path} {
1800+
if {$col == 0 && $y > 1} {
1801+
set i [expr {$lno-1}]
1802+
set ll [expr {[llength $file_lists($w)]-1}]
1803+
1804+
if {$i == $ll && $i == 0} {
17971805
set after {reshow_diff;}
17981806
} else {
1799-
set after {}
1807+
global next_diff_p next_diff_w next_diff_i
1808+
1809+
set next_diff_w $w
1810+
1811+
if {$i < $ll} {
1812+
set i [expr {$i + 1}]
1813+
set next_diff_i $i
1814+
} else {
1815+
set next_diff_i $i
1816+
set i [expr {$i - 1}]
1817+
}
1818+
1819+
set next_diff_p [lindex $file_lists($w) $i]
1820+
1821+
if {$next_diff_p ne {} && $current_diff_path ne {}} {
1822+
set after {next_diff;}
1823+
} else {
1824+
set after {}
1825+
}
18001826
}
1827+
18011828
if {$w eq $ui_index} {
18021829
update_indexinfo \
18031830
"Unstaging [short_path $path] from commit" \
@@ -2639,6 +2666,11 @@ $ctxm add command \
26392666
-command {apply_hunk $cursorX $cursorY}
26402667
set ui_diff_applyhunk [$ctxm index last]
26412668
lappend diff_actions [list $ctxm entryconf $ui_diff_applyhunk -state]
2669+
$ctxm add command \
2670+
-label [mc "Apply/Reverse Line"] \
2671+
-command {apply_line $cursorX $cursorY; do_rescan}
2672+
set ui_diff_applyline [$ctxm index last]
2673+
lappend diff_actions [list $ctxm entryconf $ui_diff_applyline -state]
26422674
$ctxm add separator
26432675
$ctxm add command \
26442676
-label [mc "Show Less Context"] \
@@ -2687,8 +2719,10 @@ proc popup_diff_menu {ctxm x y X Y} {
26872719
set ::cursorY $y
26882720
if {$::ui_index eq $::current_diff_side} {
26892721
set l [mc "Unstage Hunk From Commit"]
2722+
set t [mc "Unstage Line From Commit"]
26902723
} else {
26912724
set l [mc "Stage Hunk For Commit"]
2725+
set t [mc "Stage Line For Commit"]
26922726
}
26932727
if {$::is_3way_diff
26942728
|| $current_diff_path eq {}
@@ -2699,6 +2733,7 @@ proc popup_diff_menu {ctxm x y X Y} {
26992733
set s normal
27002734
}
27012735
$ctxm entryconf $::ui_diff_applyhunk -state $s -label $l
2736+
$ctxm entryconf $::ui_diff_applyline -state $s -label $t
27022737
tk_popup $ctxm $X $Y
27032738
}
27042739
bind_button3 $ui_diff [list popup_diff_menu $ctxm %x %y %X %Y]

git-gui/lib/diff.tcl

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,3 +362,90 @@ proc apply_hunk {x y} {
362362
set current_diff_path $current_diff_path
363363
}
364364
}
365+
366+
proc apply_line {x y} {
367+
global current_diff_path current_diff_header current_diff_side
368+
global ui_diff ui_index file_states
369+
370+
if {$current_diff_path eq {} || $current_diff_header eq {}} return
371+
if {![lock_index apply_hunk]} return
372+
373+
set apply_cmd {apply --cached --whitespace=nowarn}
374+
set mi [lindex $file_states($current_diff_path) 0]
375+
if {$current_diff_side eq $ui_index} {
376+
set failed_msg [mc "Failed to unstage selected line."]
377+
set to_context {+}
378+
lappend apply_cmd --reverse
379+
if {[string index $mi 0] ne {M}} {
380+
unlock_index
381+
return
382+
}
383+
} else {
384+
set failed_msg [mc "Failed to stage selected line."]
385+
set to_context {-}
386+
if {[string index $mi 1] ne {M}} {
387+
unlock_index
388+
return
389+
}
390+
}
391+
392+
set the_l [$ui_diff index @$x,$y]
393+
394+
# operate only on change lines
395+
set c1 [$ui_diff get "$the_l linestart"]
396+
if {$c1 ne {+} && $c1 ne {-}} {
397+
unlock_index
398+
return
399+
}
400+
set sign $c1
401+
402+
set i_l [$ui_diff search -backwards -regexp ^@@ $the_l 0.0]
403+
if {$i_l eq {}} {
404+
unlock_index
405+
return
406+
}
407+
# $i_l is now at the beginning of a line
408+
409+
# pick start line number from hunk header
410+
set hh [$ui_diff get $i_l "$i_l + 1 lines"]
411+
set hh [lindex [split $hh ,] 0]
412+
set hln [lindex [split $hh -] 1]
413+
414+
set n 0
415+
set i_l [$ui_diff index "$i_l + 1 lines"]
416+
set patch {}
417+
while {[$ui_diff compare $i_l < "end - 1 chars"] &&
418+
[$ui_diff get $i_l "$i_l + 2 chars"] ne {@@}} {
419+
set next_l [$ui_diff index "$i_l + 1 lines"]
420+
set c1 [$ui_diff get $i_l]
421+
if {[$ui_diff compare $i_l <= $the_l] &&
422+
[$ui_diff compare $the_l < $next_l]} {
423+
# the line to stage/unstage
424+
set ln [$ui_diff get $i_l $next_l]
425+
set patch "$patch$ln"
426+
} elseif {$c1 ne {-} && $c1 ne {+}} {
427+
# context line
428+
set ln [$ui_diff get $i_l $next_l]
429+
set patch "$patch$ln"
430+
set n [expr $n+1]
431+
} elseif {$c1 eq $to_context} {
432+
# turn change line into context line
433+
set ln [$ui_diff get "$i_l + 1 chars" $next_l]
434+
set patch "$patch $ln"
435+
set n [expr $n+1]
436+
}
437+
set i_l $next_l
438+
}
439+
set patch "@@ -$hln,$n +$hln,[eval expr $n $sign 1] @@\n$patch"
440+
441+
if {[catch {
442+
set p [eval git_write $apply_cmd]
443+
fconfigure $p -translation binary -encoding binary
444+
puts -nonewline $p $current_diff_header
445+
puts -nonewline $p $patch
446+
close $p} err]} {
447+
error_popup [append $failed_msg "\n\n$err"]
448+
}
449+
450+
unlock_index
451+
}

0 commit comments

Comments
 (0)