From: Phillip Wood <phillip.w...@dunelm.org.uk>

Recount the number of preimage and postimage lines in a hunk after it
has been edited so any change in the number of insertions or deletions
can be used to adjust the offsets of subsequent hunks. If an edited
hunk is subsequently split then the offset correction will be lost. It
would be possible to fix this if it is a problem, however the code
here is still an improvement on the status quo for the common case
where an edited hunk is applied without being split.

This is also a necessary step to removing '--recount' and
'--allow-overlap' from the invocation of 'git apply'. Before
'--recount' can be removed the splitting and coalescing counting needs
to be fixed to handle a missing newline at the end of a file. In order
to remove '--allow-overlap' there needs to be i) some way of verifying
the offset data in the edited hunk (probably by correlating the
preimage (or postimage if the patch is going to be applied in reverse)
lines of the edited and unedited versions to see if they are offset or
if any leading/trailing context lines have been removed) and ii) a way of
dealing with edited hunks that change context lines that are shared
with neighbouring hunks.

Signed-off-by: Phillip Wood <phillip.w...@dunelm.org.uk>
---

Notes:
    changes since v1
     - edited hunks are now recounted before seeing if they apply in
       preparation for removing '--recount' when invoking 'git apply'
     - added sentence to commit message about the offset data being lost
       if an edited hunk is split

 git-add--interactive.perl  | 55 ++++++++++++++++++++++++++++++++++++++--------
 t/t3701-add-interactive.sh |  2 +-
 2 files changed, 47 insertions(+), 10 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 
7a0a5896bb8fa063ace29b85840849c867b874f5..0df0c2aa065af88e159f8e9a2febe12f4ef8ee75
 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -938,13 +938,19 @@ sub coalesce_overlapping_hunks {
                                                parse_hunk_header($text->[0]);
                unless ($_->{USE}) {
                        $ofs_delta += $o_cnt - $n_cnt;
+                       # If this hunk has been edited then subtract
+                       # the delta that is due to the edit.
+                       $_->{OFS_DELTA} and $ofs_delta -= $_->{OFS_DELTA};
                        next;
                }
                if ($ofs_delta) {
                        $n_ofs += $ofs_delta;
                        $_->{TEXT}->[0] = format_hunk_header($o_ofs, $o_cnt,
                                                             $n_ofs, $n_cnt);
                }
+               # If this hunk was edited then adjust the offset delta
+               # to reflect the edit.
+               $_->{OFS_DELTA} and $ofs_delta += $_->{OFS_DELTA};
                if (defined $last_o_ctx &&
                    $o_ofs <= $last_o_ctx &&
                    !$_->{DIRTY} &&
@@ -1016,6 +1022,30 @@ marked for discarding."),
 marked for applying."),
 );
 
+sub recount_edited_hunk {
+       local $_;
+       my ($oldtext, $newtext) = @_;
+       my ($o_cnt, $n_cnt) = (0, 0);
+       for (@{$newtext}[1..$#{$newtext}]) {
+               my $mode = substr($_, 0, 1);
+               if ($mode eq '-') {
+                       $o_cnt++;
+               } elsif ($mode eq '+') {
+                       $n_cnt++;
+               } elsif ($mode eq ' ') {
+                       $o_cnt++;
+                       $n_cnt++;
+               }
+       }
+       my ($o_ofs, undef, $n_ofs, undef) =
+                                       parse_hunk_header($newtext->[0]);
+       $newtext->[0] = format_hunk_header($o_ofs, $o_cnt, $n_ofs, $n_cnt);
+       my (undef, $orig_o_cnt, undef, $orig_n_cnt) =
+                                       parse_hunk_header($oldtext->[0]);
+       # Return the change in the number of lines inserted by this hunk
+       return $orig_o_cnt - $orig_n_cnt - $o_cnt + $n_cnt;
+}
+
 sub edit_hunk_manually {
        my ($oldtext) = @_;
 
@@ -1114,25 +1144,32 @@ sub prompt_yesno {
 }
 
 sub edit_hunk_loop {
-       my ($head, $hunk, $ix) = @_;
-       my $text = $hunk->[$ix]->{TEXT};
+       my ($head, $hunks, $ix) = @_;
+       my $hunk = $hunks->[$ix];
+       my $text = $hunk->{TEXT};
 
        while (1) {
-               $text = edit_hunk_manually($text);
-               if (!defined $text) {
+               my $newtext = edit_hunk_manually($text);
+               if (!defined $newtext) {
                        return undef;
                }
                my $newhunk = {
-                       TEXT => $text,
-                       TYPE => $hunk->[$ix]->{TYPE},
+                       TEXT => $newtext,
+                       TYPE => $hunk->{TYPE},
                        USE => 1,
                        DIRTY => 1,
                };
+               $newhunk->{OFS_DELTA} = recount_edited_hunk($text, $newtext);
+               # If this hunk has already been edited then add the
+               # offset delta of the previous edit to get the real
+               # delta from the original unedited hunk.
+               $hunk->{OFS_DELTA} and
+                               $newhunk->{OFS_DELTA} += $hunk->{OFS_DELTA};
                if (diff_applies($head,
-                                @{$hunk}[0..$ix-1],
+                                @{$hunks}[0..$ix-1],
                                 $newhunk,
-                                @{$hunk}[$ix+1..$#{$hunk}])) {
-                       $newhunk->{DISPLAY} = [color_diff(@{$text})];
+                                @{$hunks}[$ix+1..$#{$hunks}])) {
+                       $newhunk->{DISPLAY} = [color_diff(@{$newtext})];
                        return $newhunk;
                }
                else {
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index 
e153a02605df25ea40e49dd48b7c9fd981713b9d..bbda771ba7e516aa37a204beffba7eeb0c85a2f4
 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -532,7 +532,7 @@ test_expect_success 'add -p works with pathological context 
lines' '
        test_cmp expected-1 actual
 '
 
-test_expect_failure 'add -p patch editing works with pathological context 
lines' '
+test_expect_success 'add -p patch editing works with pathological context 
lines' '
        git reset &&
        test_set_editor "$FAKE_EDITOR" &&
        # n q q below is in case edit fails
-- 
2.16.1

Reply via email to