From 492e60c82456b228a24ff0021e6accee946b7a1c Mon Sep 17 00:00:00 2001 From: Phillip Wood Date: Mon, 19 Feb 2018 11:29:02 +0000 Subject: add -i: add function to format hunk header This code is duplicated in a couple of places so make it into a function as we're going to add some more callers shortly. Signed-off-by: Phillip Wood Signed-off-by: Junio C Hamano --- git-add--interactive.perl | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'git-add--interactive.perl') diff --git a/git-add--interactive.perl b/git-add--interactive.perl index 28b325d754..64f869c3b7 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -751,6 +751,15 @@ sub parse_hunk_header { return ($o_ofs, $o_cnt, $n_ofs, $n_cnt); } +sub format_hunk_header { + my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) = @_; + return ("@@ -$o_ofs" . + (($o_cnt != 1) ? ",$o_cnt" : '') . + " +$n_ofs" . + (($n_cnt != 1) ? ",$n_cnt" : '') . + " @@\n"); +} + sub split_hunk { my ($text, $display) = @_; my @split = (); @@ -838,11 +847,7 @@ sub split_hunk { my $o_cnt = $hunk->{OCNT}; my $n_cnt = $hunk->{NCNT}; - my $head = ("@@ -$o_ofs" . - (($o_cnt != 1) ? ",$o_cnt" : '') . - " +$n_ofs" . - (($n_cnt != 1) ? ",$n_cnt" : '') . - " @@\n"); + my $head = format_hunk_header($o_ofs, $o_cnt, $n_ofs, $n_cnt); my $display_head = $head; unshift @{$hunk->{TEXT}}, $head; if ($diff_use_color) { @@ -912,11 +917,7 @@ sub merge_hunk { } push @line, $line; } - my $head = ("@@ -$o0_ofs" . - (($o_cnt != 1) ? ",$o_cnt" : '') . - " +$n0_ofs" . - (($n_cnt != 1) ? ",$n_cnt" : '') . - " @@\n"); + my $head = format_hunk_header($o0_ofs, $o_cnt, $n0_ofs, $n_cnt); @{$prev->{TEXT}} = ($head, @line); } -- cgit 1.2.3-korg From fecc6f3a6862c55dee0e9a2390acaf4b23991fef Mon Sep 17 00:00:00 2001 From: Phillip Wood Date: Thu, 1 Mar 2018 10:51:00 +0000 Subject: add -p: adjust offsets of subsequent hunks when one is skipped Since commit 8cbd431082 ("git-add--interactive: replace hunk recounting with apply --recount", 2008-7-2) if a hunk is skipped then we rely on the context lines to apply subsequent hunks in the right place. While this works most of the time it is possible for hunks to end up being applied in the wrong place. To fix this adjust the offset of subsequent hunks to correct for any change in the number of insertions or deletions due to the skipped hunk. The change in offset due to edited hunks that have the number of insertions or deletions changed is ignored here, it will be fixed in the next commit. Signed-off-by: Phillip Wood Signed-off-by: Junio C Hamano --- git-add--interactive.perl | 15 +++++++++++++-- t/t3701-add-interactive.sh | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) (limited to 'git-add--interactive.perl') diff --git a/git-add--interactive.perl b/git-add--interactive.perl index 64f869c3b7..6f6a21dc11 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -926,14 +926,25 @@ sub coalesce_overlapping_hunks { my @out = (); my ($last_o_ctx, $last_was_dirty); + my $ofs_delta = 0; - for (grep { $_->{USE} } @in) { + for (@in) { if ($_->{TYPE} ne 'hunk') { push @out, $_; next; } my $text = $_->{TEXT}; - my ($o_ofs) = parse_hunk_header($text->[0]); + my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) = + parse_hunk_header($text->[0]); + unless ($_->{USE}) { + $ofs_delta += $o_cnt - $n_cnt; + next; + } + if ($ofs_delta) { + $n_ofs += $ofs_delta; + $_->{TEXT}->[0] = format_hunk_header($o_ofs, $o_cnt, + $n_ofs, $n_cnt); + } if (defined $last_o_ctx && $o_ofs <= $last_o_ctx && !$_->{DIRTY} && diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index fe7c1bef61..6f18a92486 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -496,7 +496,7 @@ test_expect_success 'set up pathological context' ' test_write_lines +b " a" >patch ' -test_expect_failure 'add -p works with pathological context lines' ' +test_expect_success 'add -p works with pathological context lines' ' git reset && printf "%s\n" n y | git add -p && -- cgit 1.2.3-korg From 2b8ea7f3c7d8dfd4f1eddaef6688b51f8106767e Mon Sep 17 00:00:00 2001 From: Phillip Wood Date: Mon, 5 Mar 2018 10:56:28 +0000 Subject: add -p: calculate offset delta for edited patches 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 Signed-off-by: Junio C Hamano --- git-add--interactive.perl | 59 +++++++++++++++++++++++++++++++++++++++------- t/t3701-add-interactive.sh | 2 +- 2 files changed, 51 insertions(+), 10 deletions(-) (limited to 'git-add--interactive.perl') diff --git a/git-add--interactive.perl b/git-add--interactive.perl index 6f6a21dc11..b5f01160c5 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -938,6 +938,11 @@ 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. + if ($_->{OFS_DELTA}) { + $ofs_delta -= $_->{OFS_DELTA}; + } next; } if ($ofs_delta) { @@ -945,6 +950,11 @@ sub coalesce_overlapping_hunks { $_->{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. + if ($_->{OFS_DELTA}) { + $ofs_delta += $_->{OFS_DELTA}; + } if (defined $last_o_ctx && $o_ofs <= $last_o_ctx && !$_->{DIRTY} && @@ -1016,6 +1026,30 @@ sub color_diff { 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 +1148,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 6f18a92486..24ed3f4a22 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -504,7 +504,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 && # n q q below is in case edit fails printf "%s\n" e y n q q | -- cgit 1.2.3-korg From b3e0fcfe429204ccdf1f6977c55127171d65a8e5 Mon Sep 17 00:00:00 2001 From: Phillip Wood Date: Mon, 5 Mar 2018 10:56:29 +0000 Subject: add -p: fix counting when splitting and coalescing When a file has no trailing new line at the end diff records this by appending "\ No newline at end of file" below the last line of the file. This line should not be counted in the hunk header. Fix the splitting and coalescing code to count files without a trailing new line properly and change one of the tests to test splitting without a trailing new line. Signed-off-by: Phillip Wood Signed-off-by: Junio C Hamano --- git-add--interactive.perl | 11 +++++++++++ t/t3701-add-interactive.sh | 31 +++++++++++++++++++++++-------- 2 files changed, 34 insertions(+), 8 deletions(-) (limited to 'git-add--interactive.perl') diff --git a/git-add--interactive.perl b/git-add--interactive.perl index b5f01160c5..4a1e71d2e4 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -793,6 +793,11 @@ sub split_hunk { while (++$i < @$text) { my $line = $text->[$i]; my $display = $display->[$i]; + if ($line =~ /^\\/) { + push @{$this->{TEXT}}, $line; + push @{$this->{DISPLAY}}, $display; + next; + } if ($line =~ /^ /) { if ($this->{ADDDEL} && !defined $next_hunk_start) { @@ -891,6 +896,9 @@ sub merge_hunk { $n_cnt++; push @line, $line; next; + } elsif ($line =~ /^\\/) { + push @line, $line; + next; } last if ($o1_ofs <= $ofs); @@ -909,6 +917,9 @@ sub merge_hunk { $n_cnt++; push @line, $line; next; + } elsif ($line =~ /^\\/) { + push @line, $line; + next; } $ofs++; $o_cnt++; diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index 24ed3f4a22..e5c66f7500 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -237,14 +237,15 @@ test_expect_success 'setup patch' ' baseline content +lastline + \ No newline at end of file EOF ' -# Expected output, similar to the patch but w/ diff at the top +# Expected output, diff is similar to the patch but w/ diff at the top test_expect_success 'setup expected' ' - cat >expected <<-\EOF - diff --git a/file b/file - index b6f2c08..61b9053 100755 + echo diff --git a/file b/file >expected && + cat patch |sed "/^index/s/ 100644/ 100755/" >>expected && + cat >expected-output <<-\EOF --- a/file +++ b/file @@ -1,2 +1,4 @@ @@ -252,16 +253,30 @@ test_expect_success 'setup expected' ' baseline content +lastline + \ No newline at end of file + @@ -1,2 +1,3 @@ + +firstline + baseline + content + @@ -1,2 +2,3 @@ + baseline + content + +lastline + \ No newline at end of file EOF ' # Test splitting the first patch, then adding both -test_expect_success 'add first line works' ' +test_expect_success C_LOCALE_OUTPUT 'add first line works' ' git commit -am "clear local changes" && git apply patch && - (echo s; echo y; echo y) | git add -p file && - git diff --cached > diff && - diff_cmp expected diff + printf "%s\n" s y y | git add -p file 2>error | + sed -n -e "s/^Stage this hunk[^@]*\(@@ .*\)/\1/" \ + -e "/^[-+@ \\\\]"/p >output && + test_must_be_empty error && + git diff --cached >diff && + diff_cmp expected diff && + test_cmp expected-output output ' test_expect_success 'setup expected' ' -- cgit 1.2.3-korg From 3a8522f41ff45b16e06888d2b164252c9aeeaa03 Mon Sep 17 00:00:00 2001 From: Phillip Wood Date: Mon, 5 Mar 2018 10:56:30 +0000 Subject: add -p: don't rely on apply's '--recount' option Now that add -p counts patches properly it should be possible to turn off the '--recount' option when invoking 'git apply' Signed-off-by: Phillip Wood Signed-off-by: Junio C Hamano --- git-add--interactive.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'git-add--interactive.perl') diff --git a/git-add--interactive.perl b/git-add--interactive.perl index 4a1e71d2e4..ab022ec073 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -677,7 +677,7 @@ sub add_untracked_cmd { sub run_git_apply { my $cmd = shift; my $fh; - open $fh, '| git ' . $cmd . " --recount --allow-overlap"; + open $fh, '| git ' . $cmd . " --allow-overlap"; print $fh @_; return close $fh; } -- cgit 1.2.3-korg