{"thread":{"id":"21895","subject":"[PATCHv3 0/2] git-gui: (un)stage a range of changes at once","startedAt":"2009-12-08T00:22:41Z","lastAt":"2010-01-23T23:05:43Z","messageCount":4,"participants":["jepler@unpythonic.net","Shawn O. Pearce"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"129460","messageId":"1260231763-19194-1-git-send-email-jepler@unpythonic.net","threadId":"21895","inReplyTo":null,"subject":"[PATCHv3 0/2] git-gui: (un)stage a range of changes at once","fromName":"","fromEmail":"jepler@unpythonic.net","sentAt":"2009-12-08T00:22:41Z","receivedAt":"2009-12-08T00:22:41Z","isPatch":false,"sender":{"key":"jepler@unpythonic.net","avatar":"https://avatars.githubusercontent.com/u/1517291?v=4"},"body":"From: Jeff Epler <jepler@unpythonic.net>\n\nThis set of patches allows git-gui to stage multiple lines at once by\nselecting the range and then using the \"apply lines\" item in the context\nmenu.\n\nCompared to the earlier versions, I've fixed all the bugs I became aware of.\nI've also added the missing signed-off-by.\n\nThe first patch fixes an existing bug in git-gui when staging a deletion\nfollowed by another deletion followed by the end of the file.\n\nJeff Epler (2):\n  Fix applying a line when all following lines are deletions\n  Make it possible to apply a range of changes at once\n\n git-gui.sh   |   15 +++-\n lib/diff.tcl |  241 ++++++++++++++++++++++++++++++++++------------------------\n 2 files changed, 153 insertions(+), 103 deletions(-)\n"},{"id":"129461","messageId":"1260231763-19194-2-git-send-email-jepler@unpythonic.net","threadId":"21895","inReplyTo":"1260231763-19194-1-git-send-email-jepler@unpythonic.net","subject":"[PATCH 1/2] Fix applying a line when all following lines are deletions","fromName":"","fromEmail":"jepler@unpythonic.net","sentAt":"2009-12-08T00:22:42Z","receivedAt":"2009-12-08T00:22:42Z","isPatch":true,"sender":{"key":"jepler@unpythonic.net","avatar":"https://avatars.githubusercontent.com/u/1517291?v=4"},"body":"From: Jeff Epler <jepler@unpythonic.net>\n\nIf a diff looked like\n @@\n  context\n -del1\n -del2\nand you wanted to stage the deletion 'del1', the generated patch wouldn't\napply because it was missing the line 'del2' converted to context, but\nthis line was counted in the @@-line\n\nSigned-off-by: Jeff Epler <jepler@unpythonic.net>\n---\n lib/diff.tcl |    1 +\n 1 files changed, 1 insertions(+), 0 deletions(-)\n\ndiff --git a/lib/diff.tcl b/lib/diff.tcl\nindex bd5d189..066755b 100644\n--- a/lib/diff.tcl\n+++ b/lib/diff.tcl\n@@ -664,6 +664,7 @@ proc apply_line {x y} {\n \t\t}\n \t\tset i_l $next_l\n \t}\n+\tset patch \"$patch$pre_context\"\n \tset patch \"@@ -$hln,$n +$hln,[eval expr $n $sign 1] @@\\n$patch\"\n \n \tif {[catch {\n-- \n1.6.3.3\n"},{"id":"129462","messageId":"1260231763-19194-3-git-send-email-jepler@unpythonic.net","threadId":"21895","inReplyTo":"1260231763-19194-1-git-send-email-jepler@unpythonic.net","subject":"[PATCH 2/2] Make it possible to apply a range of changes at once","fromName":"","fromEmail":"jepler@unpythonic.net","sentAt":"2009-12-08T00:22:43Z","receivedAt":"2009-12-08T00:22:43Z","isPatch":true,"sender":{"key":"jepler@unpythonic.net","avatar":"https://avatars.githubusercontent.com/u/1517291?v=4"},"body":"From: Jeff Epler <jepler@unpythonic.net>\n\nSigned-off-by: Jeff Epler <jepler@unpythonic.net>\n---\n git-gui.sh   |   15 +++-\n lib/diff.tcl |  242 ++++++++++++++++++++++++++++++++++------------------------\n 2 files changed, 153 insertions(+), 104 deletions(-)\n\ndiff --git a/git-gui.sh b/git-gui.sh\nindex 718277a..803423f 100755\n--- a/git-gui.sh\n+++ b/git-gui.sh\n@@ -3199,7 +3199,7 @@ set ui_diff_applyhunk [$ctxm index last]\n lappend diff_actions [list $ctxm entryconf $ui_diff_applyhunk -state]\n $ctxm add command \\\n \t-label [mc \"Apply/Reverse Line\"] \\\n-\t-command {apply_line $cursorX $cursorY; do_rescan}\n+\t-command {apply_range_or_line $cursorX $cursorY; do_rescan}\n set ui_diff_applyline [$ctxm index last]\n lappend diff_actions [list $ctxm entryconf $ui_diff_applyline -state]\n $ctxm add separator\n@@ -3239,12 +3239,21 @@ proc popup_diff_menu {ctxm ctxmmg x y X Y} {\n \tif {[string first {U} $state] >= 0} {\n \t\ttk_popup $ctxmmg $X $Y\n \t} else {\n+\t\tset has_range [expr {[$::ui_diff tag nextrange sel 0.0] != {}}]\n \t\tif {$::ui_index eq $::current_diff_side} {\n \t\t\tset l [mc \"Unstage Hunk From Commit\"]\n-\t\t\tset t [mc \"Unstage Line From Commit\"]\n+\t\t\tif {$has_range} {\n+\t\t\t\tset t [mc \"Unstage Lines From Commit\"]\n+\t\t\t} else {\n+\t\t\t\tset t [mc \"Unstage Line From Commit\"]\n+\t\t\t}\n \t\t} else {\n \t\t\tset l [mc \"Stage Hunk For Commit\"]\n-\t\t\tset t [mc \"Stage Line For Commit\"]\n+\t\t\tif {$has_range} {\n+\t\t\t\tset t [mc \"Stage Lines For Commit\"]\n+\t\t\t} else {\n+\t\t\t\tset t [mc \"Stage Line For Commit\"]\n+\t\t\t}\n \t\t}\n \t\tif {$::is_3way_diff || $::is_submodule_diff\n \t\t\t|| $current_diff_path eq {}\ndiff --git a/lib/diff.tcl b/lib/diff.tcl\nindex 066755b..5e738e2 100644\n--- a/lib/diff.tcl\n+++ b/lib/diff.tcl\n@@ -533,10 +533,23 @@ proc apply_hunk {x y} {\n \t}\n }\n \n-proc apply_line {x y} {\n+proc apply_range_or_line {x y} {\n \tglobal current_diff_path current_diff_header current_diff_side\n \tglobal ui_diff ui_index file_states\n \n+\tset selected [$ui_diff tag nextrange sel 0.0]\n+\n+\tif {$selected == {}} {\n+\t\tset first [$ui_diff index \"@$x,$y\"]\n+\t\tset last $first\n+\t} else {\n+\t\tset first [lindex $selected 0]\n+\t\tset last [lindex $selected 1]\n+\t}\n+\n+\tset first_l [$ui_diff index \"$first linestart\"]\n+\tset last_l [$ui_diff index \"$last lineend\"]\n+\n \tif {$current_diff_path eq {} || $current_diff_header eq {}} return\n \tif {![lock_index apply_hunk]} return\n \n@@ -559,120 +572,147 @@ proc apply_line {x y} {\n \t\t}\n \t}\n \n-\tset the_l [$ui_diff index @$x,$y]\n+\tset wholepatch {}\n \n-\t# operate only on change lines\n-\tset c1 [$ui_diff get \"$the_l linestart\"]\n-\tif {$c1 ne {+} && $c1 ne {-}} {\n-\t\tunlock_index\n-\t\treturn\n-\t}\n-\tset sign $c1\n-\n-\tset i_l [$ui_diff search -backwards -regexp ^@@ $the_l 0.0]\n-\tif {$i_l eq {}} {\n-\t\tunlock_index\n-\t\treturn\n-\t}\n-\t# $i_l is now at the beginning of a line\n+\twhile {$first_l < $last_l} {\n+\t\tset i_l [$ui_diff search -backwards -regexp ^@@ $first_l 0.0]\n+\t\tif {$i_l eq {}} {\n+\t\t\t# If there's not a @@ above, then the selected range\n+\t\t\t# must have come before the first_l @@\n+\t\t\tset i_l [$ui_diff search -regexp ^@@ $first_l $last_l]\n+\t\t}\n+\t\tif {$i_l eq {}} {\n+\t\t\tunlock_index\n+\t\t\treturn\n+\t\t}\n+\t\t# $i_l is now at the beginning of a line\n \n-\t# pick start line number from hunk header\n-\tset hh [$ui_diff get $i_l \"$i_l + 1 lines\"]\n-\tset hh [lindex [split $hh ,] 0]\n-\tset hln [lindex [split $hh -] 1]\n+\t\t# pick start line number from hunk header\n+\t\tset hh [$ui_diff get $i_l \"$i_l + 1 lines\"]\n+\t\tset hh [lindex [split $hh ,] 0]\n+\t\tset hln [lindex [split $hh -] 1]\n \n-\t# There is a special situation to take care of. Consider this hunk:\n-\t#\n-\t#    @@ -10,4 +10,4 @@\n-\t#     context before\n-\t#    -old 1\n-\t#    -old 2\n-\t#    +new 1\n-\t#    +new 2\n-\t#     context after\n-\t#\n-\t# We used to keep the context lines in the order they appear in the\n-\t# hunk. But then it is not possible to correctly stage only\n-\t# \"-old 1\" and \"+new 1\" - it would result in this staged text:\n-\t#\n-\t#    context before\n-\t#    old 2\n-\t#    new 1\n-\t#    context after\n-\t#\n-\t# (By symmetry it is not possible to *un*stage \"old 2\" and \"new 2\".)\n-\t#\n-\t# We resolve the problem by introducing an asymmetry, namely, when\n-\t# a \"+\" line is *staged*, it is moved in front of the context lines\n-\t# that are generated from the \"-\" lines that are immediately before\n-\t# the \"+\" block. That is, we construct this patch:\n-\t#\n-\t#    @@ -10,4 +10,5 @@\n-\t#     context before\n-\t#    +new 1\n-\t#     old 1\n-\t#     old 2\n-\t#     context after\n-\t#\n-\t# But we do *not* treat \"-\" lines that are *un*staged in a special\n-\t# way.\n-\t#\n-\t# With this asymmetry it is possible to stage the change\n-\t# \"old 1\" -> \"new 1\" directly, and to stage the change\n-\t# \"old 2\" -> \"new 2\" by first staging the entire hunk and\n-\t# then unstaging the change \"old 1\" -> \"new 1\".\n-\n-\t# This is non-empty if and only if we are _staging_ changes;\n-\t# then it accumulates the consecutive \"-\" lines (after converting\n-\t# them to context lines) in order to be moved after the \"+\" change\n-\t# line.\n-\tset pre_context {}\n-\n-\tset n 0\n-\tset i_l [$ui_diff index \"$i_l + 1 lines\"]\n-\tset patch {}\n-\twhile {[$ui_diff compare $i_l < \"end - 1 chars\"] &&\n-\t       [$ui_diff get $i_l \"$i_l + 2 chars\"] ne {@@}} {\n-\t\tset next_l [$ui_diff index \"$i_l + 1 lines\"]\n-\t\tset c1 [$ui_diff get $i_l]\n-\t\tif {[$ui_diff compare $i_l <= $the_l] &&\n-\t\t    [$ui_diff compare $the_l < $next_l]} {\n-\t\t\t# the line to stage/unstage\n-\t\t\tset ln [$ui_diff get $i_l $next_l]\n-\t\t\tif {$c1 eq {-}} {\n-\t\t\t\tset n [expr $n+1]\n+\t\t# There is a special situation to take care of. Consider this\n+\t\t# hunk:\n+\t\t#\n+\t\t#    @@ -10,4 +10,4 @@\n+\t\t#     context before\n+\t\t#    -old 1\n+\t\t#    -old 2\n+\t\t#    +new 1\n+\t\t#    +new 2\n+\t\t#     context after\n+\t\t#\n+\t\t# We used to keep the context lines in the order they appear in\n+\t\t# the hunk. But then it is not possible to correctly stage only\n+\t\t# \"-old 1\" and \"+new 1\" - it would result in this staged text:\n+\t\t#\n+\t\t#    context before\n+\t\t#    old 2\n+\t\t#    new 1\n+\t\t#    context after\n+\t\t#\n+\t\t# (By symmetry it is not possible to *un*stage \"old 2\" and \"new\n+\t\t# 2\".)\n+\t\t#\n+\t\t# We resolve the problem by introducing an asymmetry, namely,\n+\t\t# when a \"+\" line is *staged*, it is moved in front of the\n+\t\t# context lines that are generated from the \"-\" lines that are\n+\t\t# immediately before the \"+\" block. That is, we construct this\n+\t\t# patch:\n+\t\t#\n+\t\t#    @@ -10,4 +10,5 @@\n+\t\t#     context before\n+\t\t#    +new 1\n+\t\t#     old 1\n+\t\t#     old 2\n+\t\t#     context after\n+\t\t#\n+\t\t# But we do *not* treat \"-\" lines that are *un*staged in a\n+\t\t# special way.\n+\t\t#\n+\t\t# With this asymmetry it is possible to stage the change \"old\n+\t\t# 1\" -> \"new 1\" directly, and to stage the change \"old 2\" ->\n+\t\t# \"new 2\" by first staging the entire hunk and then unstaging\n+\t\t# the change \"old 1\" -> \"new 1\".\n+\t\t#\n+\t\t# Applying multiple lines adds complexity to the special\n+\t\t# situation.  The pre_context must be moved after the entire\n+\t\t# first block of consecutive staged \"+\" lines, so that\n+\t\t# staging both additions gives the following patch:\n+\t\t#\n+\t\t#    @@ -10,4 +10,6 @@\n+\t\t#     context before\n+\t\t#    +new 1\n+\t\t#    +new 2\n+\t\t#     old 1\n+\t\t#     old 2\n+\t\t#     context after\n+\n+\t\t# This is non-empty if and only if we are _staging_ changes;\n+\t\t# then it accumulates the consecutive \"-\" lines (after\n+\t\t# converting them to context lines) in order to be moved after\n+\t\t# \"+\" change lines.\n+\t\tset pre_context {}\n+\n+\t\tset n 0\n+\t\tset m 0\n+\t\tset i_l [$ui_diff index \"$i_l + 1 lines\"]\n+\t\tset patch {}\n+\t\twhile {[$ui_diff compare $i_l < \"end - 1 chars\"] &&\n+\t\t       [$ui_diff get $i_l \"$i_l + 2 chars\"] ne {@@}} {\n+\t\t\tset next_l [$ui_diff index \"$i_l + 1 lines\"]\n+\t\t\tset c1 [$ui_diff get $i_l]\n+\t\t\tif {[$ui_diff compare $first_l <= $i_l] &&\n+\t\t\t    [$ui_diff compare $i_l < $last_l] &&\n+\t\t\t    ($c1 eq {-} || $c1 eq {+})} {\n+\t\t\t\t# a line to stage/unstage\n+\t\t\t\tset ln [$ui_diff get $i_l $next_l]\n+\t\t\t\tif {$c1 eq {-}} {\n+\t\t\t\t\tset n [expr $n+1]\n+\t\t\t\t\tset patch \"$patch$pre_context$ln\"\n+\t\t\t\t\tset pre_context {}\n+\t\t\t\t} else {\n+\t\t\t\t\tset m [expr $m+1]\n+\t\t\t\t\tset patch \"$patch$ln\"\n+\t\t\t\t}\n+\t\t\t} elseif {$c1 ne {-} && $c1 ne {+}} {\n+\t\t\t\t# context line\n+\t\t\t\tset ln [$ui_diff get $i_l $next_l]\n \t\t\t\tset patch \"$patch$pre_context$ln\"\n+\t\t\t\tset n [expr $n+1]\n+\t\t\t\tset m [expr $m+1]\n+\t\t\t\tset pre_context {}\n+\t\t\t} elseif {$c1 eq $to_context} {\n+\t\t\t\t# turn change line into context line\n+\t\t\t\tset ln [$ui_diff get \"$i_l + 1 chars\" $next_l]\n+\t\t\t\tif {$c1 eq {-}} {\n+\t\t\t\t\tset pre_context \"$pre_context $ln\"\n+\t\t\t\t} else {\n+\t\t\t\t\tset patch \"$patch $ln\"\n+\t\t\t\t}\n+\t\t\t\tset n [expr $n+1]\n+\t\t\t\tset m [expr $m+1]\n \t\t\t} else {\n-\t\t\t\tset patch \"$patch$ln$pre_context\"\n-\t\t\t}\n-\t\t\tset pre_context {}\n-\t\t} elseif {$c1 ne {-} && $c1 ne {+}} {\n-\t\t\t# context line\n-\t\t\tset ln [$ui_diff get $i_l $next_l]\n-\t\t\tset patch \"$patch$pre_context$ln\"\n-\t\t\tset n [expr $n+1]\n-\t\t\tset pre_context {}\n-\t\t} elseif {$c1 eq $to_context} {\n-\t\t\t# turn change line into context line\n-\t\t\tset ln [$ui_diff get \"$i_l + 1 chars\" $next_l]\n-\t\t\tif {$c1 eq {-}} {\n-\t\t\t\tset pre_context \"$pre_context $ln\"\n-\t\t\t} else {\n-\t\t\t\tset patch \"$patch $ln\"\n+\t\t\t\t# a change in the opposite direction of\n+\t\t\t\t# to_context which is outside the range of\n+\t\t\t\t# lines to apply.\n+\t\t\t\tset patch \"$patch$pre_context\"\n+\t\t\t\tset pre_context {}\n \t\t\t}\n-\t\t\tset n [expr $n+1]\n+\t\t\tset i_l $next_l\n \t\t}\n-\t\tset i_l $next_l\n+\t\tset patch \"$patch$pre_context\"\n+\t\tset wholepatch \"$wholepatch@@ -$hln,$n +$hln,$m @@\\n$patch\"\n+\t\tset first_l [$ui_diff index \"$next_l + 1 lines\"]\n \t}\n-\tset patch \"$patch$pre_context\"\n-\tset patch \"@@ -$hln,$n +$hln,[eval expr $n $sign 1] @@\\n$patch\"\n \n \tif {[catch {\n \t\tset enc [get_path_encoding $current_diff_path]\n \t\tset p [eval git_write $apply_cmd]\n \t\tfconfigure $p -translation binary -encoding $enc\n \t\tputs -nonewline $p $current_diff_header\n-\t\tputs -nonewline $p $patch\n+\t\tputs -nonewline $p $wholepatch\n \t\tclose $p} err]} {\n \t\terror_popup [append $failed_msg \"\\n\\n$err\"]\n \t}\n-- \n1.6.3.3\n"},{"id":"132517","messageId":"20100123230543.GG12679@spearce.org","threadId":"21895","inReplyTo":"1260231763-19194-3-git-send-email-jepler@unpythonic.net","subject":"Re: [PATCH 2/2] Make it possible to apply a range of changes at once","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2010-01-23T23:05:43Z","receivedAt":"2010-01-23T23:05:43Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"jepler@unpythonic.net wrote:\n> From: Jeff Epler <jepler@unpythonic.net>\n> \n> Signed-off-by: Jeff Epler <jepler@unpythonic.net>\n> ---\n>  git-gui.sh   |   15 +++-\n>  lib/diff.tcl |  242 ++++++++++++++++++++++++++++++++++------------------------\n>  2 files changed, 153 insertions(+), 104 deletions(-)\n\nThanks, applied.\n\n-- \nShawn.\n"}]}