threads / discuss / 44413

RFE: Discard hunks during `git add -p`

Subject: RFE: Discard hunks during `git add -p`

## tl;dr

4 messages between Nov 2, 2016 and Nov 2, 2016.

replies: 3people: 3as markdown or json

Jan Engelhardt· Nov 2, 2016, 13:46 UTC · lore

Current version: 2.10.2 Example workflow:

* I would do a global substitution across a source tree, e.g. `perl -i 
  -pe 's{OLD_FOO\(x\)}{NEW_BAR(x, 0)}' *.c`
* Using `git add -p`, I would verify each of the substitutions that they 
  make sense in their respective locations, and, based on that,
  answer "y" or "n" to the interactive prompting to stage good hunks.
* When done with add-p, I would commit the so-staged hunks,
  and then use `git reset --hard` to discard all changes that were 
  not acknowledged during add-p.

Being able to discard hunks (reset working copy to index contents) during add-p would alleviate the (quite broad) hard reset.

Similar approach:
* global substitution
* Using `git add -p`, some hunks may warrant some more editing, doable 
  with the "e" command. The index would be updated with the extra
  change, but the working copy be left as-is.
* When rerunning `git add -p` in such a state, a difference is shown 
  again for such edited spots, which I would like to discard (bring 
  the working copy into sync with index).
Konstantin Khomoutov· Nov 2, 2016, 14:07 UTC · re: Jan Engelhardt · lore

Re: RFE: Discard hunks during `git add -p`

On Wed, 2 Nov 2016 14:46:04 +0100 (CET) Jan Engelhardt <jengelh@inai.de> wrote:

Show 14 quoted lines
> Current version: 2.10.2
> Example workflow:
> 
> * I would do a global substitution across a source tree, e.g. `perl
> -i -pe 's{OLD_FOO\(x\)}{NEW_BAR(x, 0)}' *.c`
> * Using `git add -p`, I would verify each of the substitutions that
> they make sense in their respective locations, and, based on that,
>   answer "y" or "n" to the interactive prompting to stage good hunks.
> * When done with add-p, I would commit the so-staged hunks,
>   and then use `git reset --hard` to discard all changes that were 
>   not acknowledged during add-p.
> 
> Being able to discard hunks (reset working copy to index contents) 
> during add-p would alleviate the (quite broad) hard reset.
Couldn't you just do
  git checkout -- .
after staging your approved changes?

To selectively zap uncommitted changes from your working tree, you could do

  git checkout --patch -- .

I'm not sure overloading `git add` with a "reverse" action is a good idea. I'm actually prefer pragmatism over conceptual purity but I'm not sure the prospective gain here is clear.

Jeff King· Nov 2, 2016, 22:11 UTC · re: Jan Engelhardt · lore

Re: RFE: Discard hunks during `git add -p`

On Wed, Nov 02, 2016 at 02:46:04PM +0100, Jan Engelhardt wrote:
Show 14 quoted lines
> Current version: 2.10.2
> Example workflow:
> 
> * I would do a global substitution across a source tree, e.g. `perl -i 
>   -pe 's{OLD_FOO\(x\)}{NEW_BAR(x, 0)}' *.c`
> * Using `git add -p`, I would verify each of the substitutions that they 
>   make sense in their respective locations, and, based on that,
>   answer "y" or "n" to the interactive prompting to stage good hunks.
> * When done with add-p, I would commit the so-staged hunks,
>   and then use `git reset --hard` to discard all changes that were 
>   not acknowledged during add-p.
> 
> Being able to discard hunks (reset working copy to index contents) 
> during add-p would alleviate the (quite broad) hard reset.

As Konstantin pointed out, you can already discard interactively with "git checkout -p". It might be nice to be able to do both in the same run, and turn the "yes/no" decision into "yes/no/discard".

In theory it should be easy, as the same code drives the hunk selector for both commands. It's just a matter of which command we feed the selected hunks to. I don't know if there would be corner cases around hunk-editing and splitting, though. The "add" phase should never touch the working tree file itself, so any hunks present from the initial list should still apply cleanly during the "discard" phase.

-Peff
Jeff King· Nov 2, 2016, 22:37 UTC · re: Jeff King · lore

Re: RFE: Discard hunks during `git add -p`

On Wed, Nov 02, 2016 at 06:11:14PM -0400, Jeff King wrote:
Show 13 quoted lines
> > Being able to discard hunks (reset working copy to index contents) 
> > during add-p would alleviate the (quite broad) hard reset.
> 
> As Konstantin pointed out, you can already discard interactively with
> "git checkout -p". It might be nice to be able to do both in the same
> run, and turn the "yes/no" decision into "yes/no/discard".
> 
> In theory it should be easy, as the same code drives the hunk selector
> for both commands. It's just a matter of which command we feed the
> selected hunks to. I don't know if there would be corner cases around
> hunk-editing and splitting, though. The "add" phase should never touch
> the working tree file itself, so any hunks present from the initial list
> should still apply cleanly during the "discard" phase.

The patch is something like the one below, which worked for me in a very trivial test. I won't be surprised if there are some corner cases it's missing. At the very least, coalesce_overlapping_hunks() needs to learn about the differences between "apply" and "discard" hunks (and not coalesce them!).

I don't have immediate plans for this, so if somebody wants to pick it up and run with it, be my guest.

-Peff
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index ee3d81269..43651435a 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -109,6 +109,7 @@ my %patch_modes = (
 		PARTICIPLE => 'staging',
 		FILTER => 'file-only',
 		IS_REVERSE => 0,
+		DISCARD => sub { apply_patch 'apply -R', @_; },
 	},
 	'stash' => {
 		DIFF => 'diff-index -p HEAD',
@@ -1325,6 +1326,11 @@ sub patch_update_file {
 		my ($prev, $next, $other, $undecided, $i);
 		$other = '';
 
+		my $discard = exists $patch_mode_flavour{DISCARD};
+		if ($discard) {
+			$other .= ',D';
+		}
+
 		if ($num <= $ix) {
 			$ix = 0;
 		}
@@ -1384,6 +1390,9 @@ sub patch_update_file {
 			elsif ($line =~ /^n/i) {
 				$hunk[$ix]{USE} = 0;
 			}
+			elsif ($discard && $line =~ /^D/) {
+				$hunk[$ix]{USE} = -1;
+			}
 			elsif ($line =~ /^a/i) {
 				while ($ix < $num) {
 					if (!defined $hunk[$ix]{USE}) {
@@ -1539,9 +1548,12 @@ sub patch_update_file {
 
 	my $n_lofs = 0;
 	my @result = ();
+	my @discard = ();
 	for (@hunk) {
-		if ($_->{USE}) {
+		if ($_->{USE} > 0) {
 			push @result, @{$_->{TEXT}};
+		} elsif ($_->{USE} < 0) {
+			push @discard, @{$_->{TEXT}};
 		}
 	}
 
@@ -1552,6 +1564,13 @@ sub patch_update_file {
 		refresh();
 	}
 
+	if (@discard) {
+		my @patch = reassemble_patch($head->{TEXT}, @discard);
+		my $apply_routine = $patch_mode_flavour{DISCARD};
+		&$apply_routine(@patch);
+		refresh();
+	}
+
 	print "\n";
 	return $quit;
 }

← back to recent threads