@matheusbsbs / matheus-bagliano / commits / e1327ed5fb
Fork 0

add-interactive: fix bogus diff header line ordering

When we look at a patch for adding hunks interactively, we first split it into a header and a list of hunks. Some of the header lines, such as mode changes and deletion, however, become their own selectable hunks. Later when we reassemble the patch, we simply concatenate the header and the selected hunks. This leads to patches like this: diff --git a/file b/file index d95f3ad..0000000 --- a/file +++ /dev/null deleted file mode 100644 @@ -1 +0,0 @@ -content Notice how the deletion comes _after_ the ---/+++ lines, when it should come before. In many cases, we can get away with this as git-apply accepts the slightly bogus input. However, in the specific case of a deletion line that is being applied via "apply -R", this malformed patch triggers an assert in git-apply. This comes up when discarding a deletion via "git checkout -p". Rather than try to make git-apply accept our odd input, let's just reassemble the patch in the correct order. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Feb 22, 2010 at 20:05 UTC e1327ed5fbaca0af44db89d60c33b641e2f21ee1
2 files changed +31 -1
git-add--interactive.perl
+23 -1
@@ -957,6 +957,28 @@ sub coalesce_overlapping_hunks {
957 return @out;
958 }
959
960 +sub reassemble_patch {
961 + my $head = shift;
962 + my @patch;
963 +
964 + # Include everything in the header except the beginning of the diff.
965 + push @patch, (grep { !/^[-+]{3}/ } @$head);
966 +
967 + # Then include any headers from the hunk lines, which must
968 + # come before any actual hunk.
969 + while (@_ && $_[0] !~ /^@/) {
970 + push @patch, shift;
971 + }
972 +
973 + # Then begin the diff.
974 + push @patch, grep { /^[-+]{3}/ } @$head;
975 +
976 + # And then the actual hunks.
977 + push @patch, @_;
978 +
979 + return @patch;
980 +}
981 +
982 sub color_diff {
983 return map {
984 colored((/^@/ ? $fraginfo_color :
@@ -1454,7 +1476,7 @@ sub patch_update_file {
1476
1477 if (@result) {
1478 my $fh;
1457 - my @patch = (@{$head->{TEXT}}, @result);
1479 + my @patch = reassemble_patch($head->{TEXT}, @result);
1480 my $apply_routine = $patch_mode_flavour{APPLY};
1481 &$apply_routine(@patch);
1482 refresh();
t/t2016-checkout-patch.sh
+8
@@ -66,6 +66,14 @@ test_expect_success 'git checkout -p HEAD^' '
66 verify_state dir/foo parent parent
67 '
68
69 +test_expect_success 'git checkout -p handles deletion' '
70 + set_state dir/foo work index &&
71 + rm dir/foo &&
72 + (echo n; echo y) | git checkout -p &&
73 + verify_saved_state bar &&
74 + verify_state dir/foo index index
75 +'
76 +
77 # The idea in the rest is that bar sorts first, so we always say 'y'
78 # first and if the path limiter fails it'll apply to bar instead of
79 # dir/foo. There's always an extra 'n' to reject edits to dir/foo in