1
0
Fork 0
mirror of https://github.com/git/git.git synced 2024-06-03 15:46:17 +02:00

git-gui: Support applying a range of changes at once

Multiple lines can be selected in the diff viewer and applied all
at once, rather than selecting "Stage Line For Commit" on each
individual line.

Signed-off-by: Jeff Epler <jepler@unpythonic.net>
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
This commit is contained in:
Jeff Epler 2009-12-07 18:22:43 -06:00 committed by Shawn O. Pearce
parent 25476c63e7
commit ff07c3b621
2 changed files with 158 additions and 109 deletions

View File

@ -3266,7 +3266,7 @@ 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_line $cursorX $cursorY; do_rescan}
-command {apply_range_or_line $cursorX $cursorY; do_rescan}
set ui_diff_applyline [$ctxm index last]
lappend diff_actions [list $ctxm entryconf $ui_diff_applyline -state]
$ctxm add separator
@ -3348,12 +3348,21 @@ proc popup_diff_menu {ctxm ctxmmg ctxmsm x y X Y} {
} elseif {$::is_submodule_diff} {
tk_popup $ctxmsm $X $Y
} else {
set has_range [expr {[$::ui_diff tag nextrange sel 0.0] != {}}]
if {$::ui_index eq $::current_diff_side} {
set l [mc "Unstage Hunk From Commit"]
set t [mc "Unstage Line From Commit"]
if {$has_range} {
set t [mc "Unstage Lines From Commit"]
} else {
set t [mc "Unstage Line From Commit"]
}
} else {
set l [mc "Stage Hunk For Commit"]
set t [mc "Stage Line For Commit"]
if {$has_range} {
set t [mc "Stage Lines For Commit"]
} else {
set t [mc "Stage Line For Commit"]
}
}
if {$::is_3way_diff
|| $current_diff_path eq {}

View File

@ -542,10 +542,23 @@ proc apply_hunk {x y} {
}
}
proc apply_line {x y} {
proc apply_range_or_line {x y} {
global current_diff_path current_diff_header current_diff_side
global ui_diff ui_index file_states
set selected [$ui_diff tag nextrange sel 0.0]
if {$selected == {}} {
set first [$ui_diff index "@$x,$y"]
set last $first
} else {
set first [lindex $selected 0]
set last [lindex $selected 1]
}
set first_l [$ui_diff index "$first linestart"]
set last_l [$ui_diff index "$last lineend"]
if {$current_diff_path eq {} || $current_diff_header eq {}} return
if {![lock_index apply_hunk]} return
@ -568,120 +581,147 @@ proc apply_line {x y} {
}
}
set the_l [$ui_diff index @$x,$y]
set wholepatch {}
# operate only on change lines
set c1 [$ui_diff get "$the_l linestart"]
if {$c1 ne {+} && $c1 ne {-}} {
unlock_index
return
}
set sign $c1
set i_l [$ui_diff search -backwards -regexp ^@@ $the_l 0.0]
if {$i_l eq {}} {
unlock_index
return
}
# $i_l is now at the beginning of a line
# pick start line number from hunk header
set hh [$ui_diff get $i_l "$i_l + 1 lines"]
set hh [lindex [split $hh ,] 0]
set hln [lindex [split $hh -] 1]
# There is a special situation to take care of. Consider this hunk:
#
# @@ -10,4 +10,4 @@
# context before
# -old 1
# -old 2
# +new 1
# +new 2
# context after
#
# We used to keep the context lines in the order they appear in the
# hunk. But then it is not possible to correctly stage only
# "-old 1" and "+new 1" - it would result in this staged text:
#
# context before
# old 2
# new 1
# context after
#
# (By symmetry it is not possible to *un*stage "old 2" and "new 2".)
#
# We resolve the problem by introducing an asymmetry, namely, when
# a "+" line is *staged*, it is moved in front of the context lines
# that are generated from the "-" lines that are immediately before
# the "+" block. That is, we construct this patch:
#
# @@ -10,4 +10,5 @@
# context before
# +new 1
# old 1
# old 2
# context after
#
# But we do *not* treat "-" lines that are *un*staged in a special
# way.
#
# With this asymmetry it is possible to stage the change
# "old 1" -> "new 1" directly, and to stage the change
# "old 2" -> "new 2" by first staging the entire hunk and
# then unstaging the change "old 1" -> "new 1".
# This is non-empty if and only if we are _staging_ changes;
# then it accumulates the consecutive "-" lines (after converting
# them to context lines) in order to be moved after the "+" change
# line.
set pre_context {}
set n 0
set i_l [$ui_diff index "$i_l + 1 lines"]
set patch {}
while {[$ui_diff compare $i_l < "end - 1 chars"] &&
[$ui_diff get $i_l "$i_l + 2 chars"] ne {@@}} {
set next_l [$ui_diff index "$i_l + 1 lines"]
set c1 [$ui_diff get $i_l]
if {[$ui_diff compare $i_l <= $the_l] &&
[$ui_diff compare $the_l < $next_l]} {
# the line to stage/unstage
set ln [$ui_diff get $i_l $next_l]
if {$c1 eq {-}} {
set n [expr $n+1]
set patch "$patch$pre_context$ln"
} else {
set patch "$patch$ln$pre_context"
}
set pre_context {}
} elseif {$c1 ne {-} && $c1 ne {+}} {
# context line
set ln [$ui_diff get $i_l $next_l]
set patch "$patch$pre_context$ln"
set n [expr $n+1]
set pre_context {}
} elseif {$c1 eq $to_context} {
# turn change line into context line
set ln [$ui_diff get "$i_l + 1 chars" $next_l]
if {$c1 eq {-}} {
set pre_context "$pre_context $ln"
} else {
set patch "$patch $ln"
}
set n [expr $n+1]
while {$first_l < $last_l} {
set i_l [$ui_diff search -backwards -regexp ^@@ $first_l 0.0]
if {$i_l eq {}} {
# If there's not a @@ above, then the selected range
# must have come before the first_l @@
set i_l [$ui_diff search -regexp ^@@ $first_l $last_l]
}
set i_l $next_l
if {$i_l eq {}} {
unlock_index
return
}
# $i_l is now at the beginning of a line
# pick start line number from hunk header
set hh [$ui_diff get $i_l "$i_l + 1 lines"]
set hh [lindex [split $hh ,] 0]
set hln [lindex [split $hh -] 1]
# There is a special situation to take care of. Consider this
# hunk:
#
# @@ -10,4 +10,4 @@
# context before
# -old 1
# -old 2
# +new 1
# +new 2
# context after
#
# We used to keep the context lines in the order they appear in
# the hunk. But then it is not possible to correctly stage only
# "-old 1" and "+new 1" - it would result in this staged text:
#
# context before
# old 2
# new 1
# context after
#
# (By symmetry it is not possible to *un*stage "old 2" and "new
# 2".)
#
# We resolve the problem by introducing an asymmetry, namely,
# when a "+" line is *staged*, it is moved in front of the
# context lines that are generated from the "-" lines that are
# immediately before the "+" block. That is, we construct this
# patch:
#
# @@ -10,4 +10,5 @@
# context before
# +new 1
# old 1
# old 2
# context after
#
# But we do *not* treat "-" lines that are *un*staged in a
# special way.
#
# With this asymmetry it is possible to stage the change "old
# 1" -> "new 1" directly, and to stage the change "old 2" ->
# "new 2" by first staging the entire hunk and then unstaging
# the change "old 1" -> "new 1".
#
# Applying multiple lines adds complexity to the special
# situation. The pre_context must be moved after the entire
# first block of consecutive staged "+" lines, so that
# staging both additions gives the following patch:
#
# @@ -10,4 +10,6 @@
# context before
# +new 1
# +new 2
# old 1
# old 2
# context after
# This is non-empty if and only if we are _staging_ changes;
# then it accumulates the consecutive "-" lines (after
# converting them to context lines) in order to be moved after
# "+" change lines.
set pre_context {}
set n 0
set m 0
set i_l [$ui_diff index "$i_l + 1 lines"]
set patch {}
while {[$ui_diff compare $i_l < "end - 1 chars"] &&
[$ui_diff get $i_l "$i_l + 2 chars"] ne {@@}} {
set next_l [$ui_diff index "$i_l + 1 lines"]
set c1 [$ui_diff get $i_l]
if {[$ui_diff compare $first_l <= $i_l] &&
[$ui_diff compare $i_l < $last_l] &&
($c1 eq {-} || $c1 eq {+})} {
# a line to stage/unstage
set ln [$ui_diff get $i_l $next_l]
if {$c1 eq {-}} {
set n [expr $n+1]
set patch "$patch$pre_context$ln"
set pre_context {}
} else {
set m [expr $m+1]
set patch "$patch$ln"
}
} elseif {$c1 ne {-} && $c1 ne {+}} {
# context line
set ln [$ui_diff get $i_l $next_l]
set patch "$patch$pre_context$ln"
set n [expr $n+1]
set m [expr $m+1]
set pre_context {}
} elseif {$c1 eq $to_context} {
# turn change line into context line
set ln [$ui_diff get "$i_l + 1 chars" $next_l]
if {$c1 eq {-}} {
set pre_context "$pre_context $ln"
} else {
set patch "$patch $ln"
}
set n [expr $n+1]
set m [expr $m+1]
} else {
# a change in the opposite direction of
# to_context which is outside the range of
# lines to apply.
set patch "$patch$pre_context"
set pre_context {}
}
set i_l $next_l
}
set patch "$patch$pre_context"
set wholepatch "$wholepatch@@ -$hln,$n +$hln,$m @@\n$patch"
set first_l [$ui_diff index "$next_l + 1 lines"]
}
set patch "$patch$pre_context"
set patch "@@ -$hln,$n +$hln,[eval expr $n $sign 1] @@\n$patch"
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 $patch
puts -nonewline $p $wholepatch
close $p} err]} {
error_popup [append $failed_msg "\n\n$err"]
}