Skip to content

Commit 436d18f

Browse files
committed
Merge branch 'pw/add-p-recount'
"git add -p" has been lazy in coalescing split patches before passing the result to underlying "git apply", leading to corner case bugs; the logic to prepare the patch to be applied after hunk selections has been tightened. * pw/add-p-recount: add -p: don't rely on apply's '--recount' option add -p: fix counting when splitting and coalescing add -p: calculate offset delta for edited patches add -p: adjust offsets of subsequent hunks when one is skipped t3701: add failing test for pathological context lines t3701: don't hard code sha1 hash values t3701: use test_write_lines and write_script t3701: indent here documents add -i: add function to format hunk header
2 parents b423234 + 3a8522f commit 436d18f

File tree

2 files changed

+251
-152
lines changed

2 files changed

+251
-152
lines changed

git-add--interactive.perl

Lines changed: 86 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -677,7 +677,7 @@ sub add_untracked_cmd {
677677
sub run_git_apply {
678678
my $cmd = shift;
679679
my $fh;
680-
open $fh, '| git ' . $cmd . " --recount --allow-overlap";
680+
open $fh, '| git ' . $cmd . " --allow-overlap";
681681
print $fh @_;
682682
return close $fh;
683683
}
@@ -751,6 +751,15 @@ sub parse_hunk_header {
751751
return ($o_ofs, $o_cnt, $n_ofs, $n_cnt);
752752
}
753753

754+
sub format_hunk_header {
755+
my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) = @_;
756+
return ("@@ -$o_ofs" .
757+
(($o_cnt != 1) ? ",$o_cnt" : '') .
758+
" +$n_ofs" .
759+
(($n_cnt != 1) ? ",$n_cnt" : '') .
760+
" @@\n");
761+
}
762+
754763
sub split_hunk {
755764
my ($text, $display) = @_;
756765
my @split = ();
@@ -784,6 +793,11 @@ sub split_hunk {
784793
while (++$i < @$text) {
785794
my $line = $text->[$i];
786795
my $display = $display->[$i];
796+
if ($line =~ /^\\/) {
797+
push @{$this->{TEXT}}, $line;
798+
push @{$this->{DISPLAY}}, $display;
799+
next;
800+
}
787801
if ($line =~ /^ /) {
788802
if ($this->{ADDDEL} &&
789803
!defined $next_hunk_start) {
@@ -838,11 +852,7 @@ sub split_hunk {
838852
my $o_cnt = $hunk->{OCNT};
839853
my $n_cnt = $hunk->{NCNT};
840854

841-
my $head = ("@@ -$o_ofs" .
842-
(($o_cnt != 1) ? ",$o_cnt" : '') .
843-
" +$n_ofs" .
844-
(($n_cnt != 1) ? ",$n_cnt" : '') .
845-
" @@\n");
855+
my $head = format_hunk_header($o_ofs, $o_cnt, $n_ofs, $n_cnt);
846856
my $display_head = $head;
847857
unshift @{$hunk->{TEXT}}, $head;
848858
if ($diff_use_color) {
@@ -886,6 +896,9 @@ sub merge_hunk {
886896
$n_cnt++;
887897
push @line, $line;
888898
next;
899+
} elsif ($line =~ /^\\/) {
900+
push @line, $line;
901+
next;
889902
}
890903

891904
last if ($o1_ofs <= $ofs);
@@ -904,6 +917,9 @@ sub merge_hunk {
904917
$n_cnt++;
905918
push @line, $line;
906919
next;
920+
} elsif ($line =~ /^\\/) {
921+
push @line, $line;
922+
next;
907923
}
908924
$ofs++;
909925
$o_cnt++;
@@ -912,11 +928,7 @@ sub merge_hunk {
912928
}
913929
push @line, $line;
914930
}
915-
my $head = ("@@ -$o0_ofs" .
916-
(($o_cnt != 1) ? ",$o_cnt" : '') .
917-
" +$n0_ofs" .
918-
(($n_cnt != 1) ? ",$n_cnt" : '') .
919-
" @@\n");
931+
my $head = format_hunk_header($o0_ofs, $o_cnt, $n0_ofs, $n_cnt);
920932
@{$prev->{TEXT}} = ($head, @line);
921933
}
922934

@@ -925,14 +937,35 @@ sub coalesce_overlapping_hunks {
925937
my @out = ();
926938

927939
my ($last_o_ctx, $last_was_dirty);
940+
my $ofs_delta = 0;
928941

929-
for (grep { $_->{USE} } @in) {
942+
for (@in) {
930943
if ($_->{TYPE} ne 'hunk') {
931944
push @out, $_;
932945
next;
933946
}
934947
my $text = $_->{TEXT};
935-
my ($o_ofs) = parse_hunk_header($text->[0]);
948+
my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) =
949+
parse_hunk_header($text->[0]);
950+
unless ($_->{USE}) {
951+
$ofs_delta += $o_cnt - $n_cnt;
952+
# If this hunk has been edited then subtract
953+
# the delta that is due to the edit.
954+
if ($_->{OFS_DELTA}) {
955+
$ofs_delta -= $_->{OFS_DELTA};
956+
}
957+
next;
958+
}
959+
if ($ofs_delta) {
960+
$n_ofs += $ofs_delta;
961+
$_->{TEXT}->[0] = format_hunk_header($o_ofs, $o_cnt,
962+
$n_ofs, $n_cnt);
963+
}
964+
# If this hunk was edited then adjust the offset delta
965+
# to reflect the edit.
966+
if ($_->{OFS_DELTA}) {
967+
$ofs_delta += $_->{OFS_DELTA};
968+
}
936969
if (defined $last_o_ctx &&
937970
$o_ofs <= $last_o_ctx &&
938971
!$_->{DIRTY} &&
@@ -1004,6 +1037,30 @@ sub color_diff {
10041037
marked for applying."),
10051038
);
10061039

1040+
sub recount_edited_hunk {
1041+
local $_;
1042+
my ($oldtext, $newtext) = @_;
1043+
my ($o_cnt, $n_cnt) = (0, 0);
1044+
for (@{$newtext}[1..$#{$newtext}]) {
1045+
my $mode = substr($_, 0, 1);
1046+
if ($mode eq '-') {
1047+
$o_cnt++;
1048+
} elsif ($mode eq '+') {
1049+
$n_cnt++;
1050+
} elsif ($mode eq ' ') {
1051+
$o_cnt++;
1052+
$n_cnt++;
1053+
}
1054+
}
1055+
my ($o_ofs, undef, $n_ofs, undef) =
1056+
parse_hunk_header($newtext->[0]);
1057+
$newtext->[0] = format_hunk_header($o_ofs, $o_cnt, $n_ofs, $n_cnt);
1058+
my (undef, $orig_o_cnt, undef, $orig_n_cnt) =
1059+
parse_hunk_header($oldtext->[0]);
1060+
# Return the change in the number of lines inserted by this hunk
1061+
return $orig_o_cnt - $orig_n_cnt - $o_cnt + $n_cnt;
1062+
}
1063+
10071064
sub edit_hunk_manually {
10081065
my ($oldtext) = @_;
10091066

@@ -1102,25 +1159,32 @@ sub prompt_yesno {
11021159
}
11031160

11041161
sub edit_hunk_loop {
1105-
my ($head, $hunk, $ix) = @_;
1106-
my $text = $hunk->[$ix]->{TEXT};
1162+
my ($head, $hunks, $ix) = @_;
1163+
my $hunk = $hunks->[$ix];
1164+
my $text = $hunk->{TEXT};
11071165

11081166
while (1) {
1109-
$text = edit_hunk_manually($text);
1110-
if (!defined $text) {
1167+
my $newtext = edit_hunk_manually($text);
1168+
if (!defined $newtext) {
11111169
return undef;
11121170
}
11131171
my $newhunk = {
1114-
TEXT => $text,
1115-
TYPE => $hunk->[$ix]->{TYPE},
1172+
TEXT => $newtext,
1173+
TYPE => $hunk->{TYPE},
11161174
USE => 1,
11171175
DIRTY => 1,
11181176
};
1177+
$newhunk->{OFS_DELTA} = recount_edited_hunk($text, $newtext);
1178+
# If this hunk has already been edited then add the
1179+
# offset delta of the previous edit to get the real
1180+
# delta from the original unedited hunk.
1181+
$hunk->{OFS_DELTA} and
1182+
$newhunk->{OFS_DELTA} += $hunk->{OFS_DELTA};
11191183
if (diff_applies($head,
1120-
@{$hunk}[0..$ix-1],
1184+
@{$hunks}[0..$ix-1],
11211185
$newhunk,
1122-
@{$hunk}[$ix+1..$#{$hunk}])) {
1123-
$newhunk->{DISPLAY} = [color_diff(@{$text})];
1186+
@{$hunks}[$ix+1..$#{$hunks}])) {
1187+
$newhunk->{DISPLAY} = [color_diff(@{$newtext})];
11241188
return $newhunk;
11251189
}
11261190
else {

0 commit comments

Comments
 (0)