git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v2] add-interactive: fix bogus diff header line ordering

From
Jeff King <peff@peff.net>
Date
Feb 23, 2010, 01:05 UTC
Message-ID
<20100223010544.GC3254@coredump.intra.peff.net>
In-Reply-To
<20100223005645.GB3254@coredump.intra.peff.net>

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>
---
On Mon, Feb 22, 2010 at 07:56:45PM -0500, Jeff King wrote:
Show 6 quoted lines
> Nope, good catch. Maybe this should specifically be hoisting known git
> header lines back into the header? Or perhaps a better logic would be to
> treat each hunk individually, but just take all lines before the "@@"
> hunk header? Hmm. I am not sure we would even need to treat hunks
> individually...the misplaced header lines should always be part of the
> _first_ hunk.
Like this.
 git-add--interactive.perl |   24 +++++++++++++++++++++++-
 t/t2016-checkout-patch.sh |    8 ++++++++
 2 files changed, 31 insertions(+), 1 deletions(-)
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index cd43c34..21f1330 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -957,6 +957,28 @@ sub coalesce_overlapping_hunks {
 	return @out;
 }
 
+sub reassemble_patch {
+	my $head = shift;
+	my @patch;
+
+	# Include everything in the header except the beginning of the diff.
+	push @patch, (grep { !/^[-+]{3}/ } @$head);
+
+	# Then include any headers from the hunk lines, which must
+	# come before any actual hunk.
+	while (@_ && $_[0] !~ /^@/) {
+		push @patch, shift;
+	}
+
+	# Then begin the diff.
+	push @patch, grep { /^[-+]{3}/ } @$head;
+
+	# And then the actual hunks.
+	push @patch, @_;
+
+	return @patch;
+}
+
 sub color_diff {
 	return map {
 		colored((/^@/  ? $fraginfo_color :
@@ -1453,7 +1475,7 @@ sub patch_update_file {
 
 	if (@result) {
 		my $fh;
-		my @patch = (@{$head->{TEXT}}, @result);
+		my @patch = reassemble_patch($head->{TEXT}, @result);
 		my $apply_routine = $patch_mode_flavour{APPLY};
 		&$apply_routine(@patch);
 		refresh();
diff --git a/t/t2016-checkout-patch.sh b/t/t2016-checkout-patch.sh
index 4d1c2e9..2144184 100755
--- a/t/t2016-checkout-patch.sh
+++ b/t/t2016-checkout-patch.sh
@@ -66,6 +66,14 @@ test_expect_success 'git checkout -p HEAD^' '
 	verify_state dir/foo parent parent
 '
 
+test_expect_success 'git checkout -p handles deletion' '
+	set_state dir/foo work index &&
+	rm dir/foo &&
+	(echo n; echo y) | git checkout -p &&
+	verify_saved_state bar &&
+	verify_state dir/foo index index
+'
+
 # The idea in the rest is that bar sorts first, so we always say 'y'
 # first and if the path limiter fails it'll apply to bar instead of
 # dir/foo.  There's always an extra 'n' to reject edits to dir/foo in
-- 
1.7.0.207.g88f1
Previous: Jeff KingNext: Junio C Hamano
Message 4 of 5 in “add-interactive: fix bogus diff header line ordering”
  1. add-interactive: fix bogus diff header line orderingJeff King, Feb 22, 2010
  2. Junio C HamanoFeb 22, 2010
  3. Jeff KingFeb 23, 2010
  4. add-interactive: fix bogus diff header line orderingJeff King, Feb 23, 2010
  5. Junio C HamanoFeb 23, 2010

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.