Subject: Re: [PATCH v3] apply.c: fix -p argument parsing

From: Jeff King <[email-masked]>
Date: Thu, 12 Mar 2026 21:12:59 -0400

On Thu, Mar 12, 2026 at 08:16:29PM -0400, Jeff King wrote:

> > +test_expect_success 'git apply -p 1 patch' '
> > +	test_when_finished "rm -rf t" &&
> > +	git apply -p 1 $TEST_DIRECTORY/t4120/patch &&
> > +	test_path_is_dir t
> > +'
> 
> This test seems to fail on Windows. From CI:
> 
>     ++ git apply -p 1 /d/a/git/git/t/t4120/patch
>     error: git diff header lacks filename information when removing 1 leading pathname component (line 14)
>     error: last command exited with $?=128
> 
> but I can't figure out why (and don't have a local Windows machine to
> test on easily).

Ah, I figured it out. The culprit is CRLF line endings. Naturally. :-/

It looks like there is an existing bug in apply.c when reading patches
with CRLF endings. Because of complicated historical reasons, parsing
the:

  diff --git a/t/test/test b/t/test/test

line insists that we find the same "t/test/test" path at the very end of
the line. But instead, we find the extra CR. As a result, we leave
patch->def_name NULL instead of filling it in with "t/test/test".

Usually this is not too big a deal, as we can pick up the name from the
"---" and "+++" lines. But in your patch:

  diff --git a/t/test/test b/t/test/test
  new file mode 100644
  index 0000000000..e69de29bb2

since there is no content, we omit them entirely. And so Git has no idea
where to apply the patch (even without "-p" at all).

I think the fix is probably:

diff --git a/apply.c b/apply.c
index 61df3bdcd0..62d6a1f8d7 100644
--- a/apply.c
+++ b/apply.c
@@ -1295,7 +1295,7 @@ static char *git_header_name(int p_value,
 			 * (that are separated by one HT or SP we just
 			 * found) exactly match?
 			 */
-			if (second[len] == '\n' && !strncmp(name, second, len))
+			if ((second[len] == '\n' || second[len] == '\r') && !strncmp(name, second, len))
 				return xmemdupz(name, len);
 		}
 	}

but I'm not sure if there are other lurking CRLF issues, or if this
might allow malicious input to cause confusion.

Getting back to your patch: why is there a CRLF here in the first place?
Because on Windows, we check out the whole repo with CRLF conversion,
except for a few known file types listed in .gitattributes. And that
includes your t/t4120/patch file.

Coincidentally the style suggestion I made earlier, to just inline it in
the t4120 script itself, makes the problem go away. Because we check out
those scripts with bare line feeds, per .gitattributes, the file we
create will also have regular line feeds.

So I would suggest doing that as a workaround. It might be worth
addressing the CRLF header parsing problem above, too, but I think that
should be a separate topic.

-Peff