{"thread":{"id":"44413","subject":"RFE: Discard hunks during `git add -p`","startedAt":"2016-11-02T13:54:40Z","lastAt":"2016-11-02T22:37:13Z","messageCount":4,"participants":["Jan Engelhardt","Konstantin Khomoutov","Jeff King"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"305292","messageId":"alpine.LSU.2.20.1611021435280.21207@nerf40.vanv.qr","threadId":"44413","inReplyTo":null,"subject":"RFE: Discard hunks during `git add -p`","fromName":"Jan Engelhardt","fromEmail":"jengelh@inai.de","sentAt":"2016-11-02T13:46:04Z","receivedAt":"2016-11-02T13:54:40Z","isPatch":false,"sender":{"key":"jengelh@inai.de","avatar":"https://avatars.githubusercontent.com/u/8861948?v=4"},"body":"\nCurrent version: 2.10.2\nExample workflow:\n\n* I would do a global substitution across a source tree, e.g. `perl -i \n  -pe 's{OLD_FOO\\(x\\)}{NEW_BAR(x, 0)}' *.c`\n* Using `git add -p`, I would verify each of the substitutions that they \n  make sense in their respective locations, and, based on that,\n  answer \"y\" or \"n\" to the interactive prompting to stage good hunks.\n* When done with add-p, I would commit the so-staged hunks,\n  and then use `git reset --hard` to discard all changes that were \n  not acknowledged during add-p.\n\nBeing able to discard hunks (reset working copy to index contents) \nduring add-p would alleviate the (quite broad) hard reset.\n\nSimilar approach:\n\n* global substitution\n* Using `git add -p`, some hunks may warrant some more editing, doable \n  with the \"e\" command. The index would be updated with the extra\n  change, but the working copy be left as-is.\n* When rerunning `git add -p` in such a state, a difference is shown \n  again for such edited spots, which I would like to discard (bring \n  the working copy into sync with index).\n"},{"id":"305293","messageId":"20161102170749.eb04d1892fdf404bb5b2851d@domain007.com","threadId":"44413","inReplyTo":"alpine.LSU.2.20.1611021435280.21207@nerf40.vanv.qr","subject":"Re: RFE: Discard hunks during `git add -p`","fromName":"Konstantin Khomoutov","fromEmail":"kostix+git@007spb.ru","sentAt":"2016-11-02T14:07:49Z","receivedAt":"2016-11-02T14:08:02Z","isPatch":false,"sender":{"key":"kostix+git@007spb.ru","avatar":null},"body":"On Wed, 2 Nov 2016 14:46:04 +0100 (CET)\nJan Engelhardt <jengelh@inai.de> wrote:\n\n> Current version: 2.10.2\n> Example workflow:\n> \n> * I would do a global substitution across a source tree, e.g. `perl\n> -i -pe 's{OLD_FOO\\(x\\)}{NEW_BAR(x, 0)}' *.c`\n> * Using `git add -p`, I would verify each of the substitutions that\n> they make sense in their respective locations, and, based on that,\n>   answer \"y\" or \"n\" to the interactive prompting to stage good hunks.\n> * When done with add-p, I would commit the so-staged hunks,\n>   and then use `git reset --hard` to discard all changes that were \n>   not acknowledged during add-p.\n> \n> Being able to discard hunks (reset working copy to index contents) \n> during add-p would alleviate the (quite broad) hard reset.\n\nCouldn't you just do\n\n  git checkout -- .\n\nafter staging your approved changes?\n\nTo selectively zap uncommitted changes from your working tree, you could\ndo\n\n  git checkout --patch -- .\n\n\nI'm not sure overloading `git add` with a \"reverse\" action is a good\nidea.  I'm actually prefer pragmatism over conceptual purity but I'm\nnot sure the prospective gain here is clear.\n"},{"id":"305322","messageId":"20161102221113.peur2zyfs66bdchm@sigill.intra.peff.net","threadId":"44413","inReplyTo":"alpine.LSU.2.20.1611021435280.21207@nerf40.vanv.qr","subject":"Re: RFE: Discard hunks during `git add -p`","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-11-02T22:11:14Z","receivedAt":"2016-11-02T22:11:20Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Nov 02, 2016 at 02:46:04PM +0100, Jan Engelhardt wrote:\n\n> Current version: 2.10.2\n> Example workflow:\n> \n> * I would do a global substitution across a source tree, e.g. `perl -i \n>   -pe 's{OLD_FOO\\(x\\)}{NEW_BAR(x, 0)}' *.c`\n> * Using `git add -p`, I would verify each of the substitutions that they \n>   make sense in their respective locations, and, based on that,\n>   answer \"y\" or \"n\" to the interactive prompting to stage good hunks.\n> * When done with add-p, I would commit the so-staged hunks,\n>   and then use `git reset --hard` to discard all changes that were \n>   not acknowledged during add-p.\n> \n> Being able to discard hunks (reset working copy to index contents) \n> during add-p would alleviate the (quite broad) hard reset.\n\nAs Konstantin pointed out, you can already discard interactively with\n\"git checkout -p\". It might be nice to be able to do both in the same\nrun, and turn the \"yes/no\" decision into \"yes/no/discard\".\n\nIn theory it should be easy, as the same code drives the hunk selector\nfor both commands. It's just a matter of which command we feed the\nselected hunks to. I don't know if there would be corner cases around\nhunk-editing and splitting, though. The \"add\" phase should never touch\nthe working tree file itself, so any hunks present from the initial list\nshould still apply cleanly during the \"discard\" phase.\n\n-Peff\n"},{"id":"305325","messageId":"20161102223705.qycdo3j2bvndi7ev@sigill.intra.peff.net","threadId":"44413","inReplyTo":"20161102221113.peur2zyfs66bdchm@sigill.intra.peff.net","subject":"Re: RFE: Discard hunks during `git add -p`","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-11-02T22:37:05Z","receivedAt":"2016-11-02T22:37:13Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Nov 02, 2016 at 06:11:14PM -0400, Jeff King wrote:\n\n> > Being able to discard hunks (reset working copy to index contents) \n> > during add-p would alleviate the (quite broad) hard reset.\n> \n> As Konstantin pointed out, you can already discard interactively with\n> \"git checkout -p\". It might be nice to be able to do both in the same\n> run, and turn the \"yes/no\" decision into \"yes/no/discard\".\n> \n> In theory it should be easy, as the same code drives the hunk selector\n> for both commands. It's just a matter of which command we feed the\n> selected hunks to. I don't know if there would be corner cases around\n> hunk-editing and splitting, though. The \"add\" phase should never touch\n> the working tree file itself, so any hunks present from the initial list\n> should still apply cleanly during the \"discard\" phase.\n\nThe patch is something like the one below, which worked for me in a very\ntrivial test. I won't be surprised if there are some corner cases it's\nmissing. At the very least, coalesce_overlapping_hunks() needs to learn\nabout the differences between \"apply\" and \"discard\" hunks (and not\ncoalesce them!).\n\nI don't have immediate plans for this, so if somebody wants to pick it\nup and run with it, be my guest.\n\n-Peff\n\ndiff --git a/git-add--interactive.perl b/git-add--interactive.perl\nindex ee3d81269..43651435a 100755\n--- a/git-add--interactive.perl\n+++ b/git-add--interactive.perl\n@@ -109,6 +109,7 @@ my %patch_modes = (\n \t\tPARTICIPLE => 'staging',\n \t\tFILTER => 'file-only',\n \t\tIS_REVERSE => 0,\n+\t\tDISCARD => sub { apply_patch 'apply -R', @_; },\n \t},\n \t'stash' => {\n \t\tDIFF => 'diff-index -p HEAD',\n@@ -1325,6 +1326,11 @@ sub patch_update_file {\n \t\tmy ($prev, $next, $other, $undecided, $i);\n \t\t$other = '';\n \n+\t\tmy $discard = exists $patch_mode_flavour{DISCARD};\n+\t\tif ($discard) {\n+\t\t\t$other .= ',D';\n+\t\t}\n+\n \t\tif ($num <= $ix) {\n \t\t\t$ix = 0;\n \t\t}\n@@ -1384,6 +1390,9 @@ sub patch_update_file {\n \t\t\telsif ($line =~ /^n/i) {\n \t\t\t\t$hunk[$ix]{USE} = 0;\n \t\t\t}\n+\t\t\telsif ($discard && $line =~ /^D/) {\n+\t\t\t\t$hunk[$ix]{USE} = -1;\n+\t\t\t}\n \t\t\telsif ($line =~ /^a/i) {\n \t\t\t\twhile ($ix < $num) {\n \t\t\t\t\tif (!defined $hunk[$ix]{USE}) {\n@@ -1539,9 +1548,12 @@ sub patch_update_file {\n \n \tmy $n_lofs = 0;\n \tmy @result = ();\n+\tmy @discard = ();\n \tfor (@hunk) {\n-\t\tif ($_->{USE}) {\n+\t\tif ($_->{USE} > 0) {\n \t\t\tpush @result, @{$_->{TEXT}};\n+\t\t} elsif ($_->{USE} < 0) {\n+\t\t\tpush @discard, @{$_->{TEXT}};\n \t\t}\n \t}\n \n@@ -1552,6 +1564,13 @@ sub patch_update_file {\n \t\trefresh();\n \t}\n \n+\tif (@discard) {\n+\t\tmy @patch = reassemble_patch($head->{TEXT}, @discard);\n+\t\tmy $apply_routine = $patch_mode_flavour{DISCARD};\n+\t\t&$apply_routine(@patch);\n+\t\trefresh();\n+\t}\n+\n \tprint \"\\n\";\n \treturn $quit;\n }\n"}]}