Re: [PATCH v3] git-add--interactive: manual hunk editing mode
From: Jeff King <hidden>
Date: 2016-06-15 22:44:43
Subsystem:
the rest · Maintainer:
Linus Torvalds
On Mon, Jun 09, 2008 at 11:19:23PM +0200, Johan Herland wrote:
On Monday 09 June 2008, Jeff King wrote:quoted
But honestly, I don't really see a use case that isn't covered by "manually edit the diff and apply the hunk". And the rationale in your "side note" indicates that you think the same way.
Below is a simple patch to build interactive edit support on top of
Dscho's "git apply --recount" patch. Rather than create new hunks, the
action is just "edit and apply": if the apply is successful, the hunk is
removed from further consideration. This is just for playing with, and
not for commit:
- it's not very well tested :)
- I think the recount stuff may have flaws; I didn't follow the thread
too closely, but Junio seemed to have a lot of comments (I am
working under the assumption that Dscho will get that part right,
and we can just build on top of it)
- I tried one or two simple tests where I edited and applied an early
hunk, and then resumed the loop for later hunks. Everything seemed
to work, but I am not convinced that it isn't possible to make the
rest of the hunk selection loop useless by applying early and
invalidating all of the other hunks. That might be a flaw with this
approach. A more sane interface might be to simply jump from the
hunk selection loop into manually editing _all_ hunks and then
applying, ending the hunk selection loop.
This should be applied on top of "allow git-apply to ignore the hunk
headers" (v3) which can be found on the list. Obviously significant
portions of the patch below are based on Thomas' version.
-- >8 --
git-add--interactive: manual hunk editing mode
Adds a new option 'e' to the 'add -p' command loop that lets you
edit the current hunk in your favourite editor, applying the
result.
---
git-add--interactive.perl | 81 +++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 81 insertions(+), 0 deletions(-)
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 903953e..62b5aed 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl@@ -2,6 +2,7 @@ use strict; use Git; +use File::Temp; my $repo = Git->repository();
@@ -770,6 +771,76 @@ sub coalesce_overlapping_hunks { return @out; } +sub edit_hunk_manually { + my ($oldtext) = @_; + + my $t = File::Temp->new( + TEMPLATE => $repo->repo_path . "/git-hunk-edit.XXXXXX", + SUFFIX => '.diff' + ); + print $t "# Manual hunk edit mode -- see bottom for a quick guide\n"; + print $t @$oldtext; + print $t <<EOF; +# --- +# To remove '-' lines, make them ' ' lines (context). +# To remove '+' lines, delete them. +# Empty lines and lines starting with # will be removed. +# +# Lines starting with @ start a new hunk. Line counts will be adjusted +# according to contents. If the line numbers are missing altogether, +# they will be inferred from the previous hunk. +# +# You can change the hunk to your heart's content, but it will be +# refused if the end result (the entire patch including your edited +# hunk) does not apply cleanly. +EOF + close $t; + + my $editor = $ENV{GIT_EDITOR} || $repo->config("core.editor") + || $ENV{VISUAL} || $ENV{EDITOR} || "vi"; + system('sh', '-c', $editor.' "$@"', $editor, $t); + + open my $fh, '<', $t + or die "failed to open hunk edit file for reading: " . $!; + my @newtext = grep { !/^#/ } <$fh>; + close $fh; + + # Reinsert the first hunk header if the user accidentally deleted it + if ($newtext[0] !~ /^@/) { + unshift @newtext, $oldtext->[0]; + } + return \@newtext; +} + +sub apply_diff { + open(my $fh, '|-', qw(git apply --cached --recount)); + print $fh map { @$_ } @_; + return close $fh; +} + +sub prompt_yesno { + my ($prompt) = @_; + while (1) { + print colored $prompt_color, $prompt; + my $line = <STDIN>; + return 0 if $line =~ /^n/i; + return 1 if $line =~ /^y/i; + } +} + +sub edit_hunk_loop { + my ($head, $text) = @_; + + while (1) { + $text = edit_hunk_manually($text); + apply_diff($head, $text) and return 1; + prompt_yesno( + 'Your edited hunk does not apply. Edit again ' + . '(saying "no" discards!) [y/n]? ' + ) or return 0; + } +} + sub help_patch_cmd { print colored $help_color, <<\EOF ; y - stage this hunk
@@ -781,6 +852,7 @@ J - leave this hunk undecided, see next hunk k - leave this hunk undecided, see previous undecided hunk K - leave this hunk undecided, see previous hunk s - split the current hunk into smaller hunks +e - manually edit the current hunk ? - print help EOF }
@@ -885,6 +957,7 @@ sub patch_update_file { if (hunk_splittable($hunk[$ix]{TEXT})) { $other .= '/s'; } + $other .= '/e'; for (@{$hunk[$ix]{DISPLAY}}) { print; }
@@ -949,6 +1022,14 @@ sub patch_update_file { $num = scalar @hunk; next; } + elsif ($line =~ /^e/) { + if (edit_hunk_loop($head->{TEXT}, + $hunk[$ix]{TEXT})) { + splice @hunk, $ix, 1; + $num = @hunk; + } + next; + } else { help_patch_cmd($other); next;
--
1.5.6.rc2.26.gda075.dirty