Skip to content

patch: accept empty-file diffs with no hunk - #7331

Merged
ethomson merged 1 commit into
libgit2:mainfrom
tarann26:fix-6778-empty-file-diff-parse
Aug 3, 2026
Merged

ethomson merged 1 commit into
libgit2:mainfrom
tarann26:fix-6778-empty-file-diff-parse

Conversation

@tarann26

Copy link
Copy Markdown
Contributor

What

Parsing a diff that adds an empty file fails with unexpected header line. Staging a new zero-byte file and taking a plain git diff produces a header with no hunk:

diff --git a/new_file b/new_file
new file mode 100644
index 0000000..e69de29

There is no content, so there is no ---/+++ pair and no hunk — the header simply ends at the index line. git_patch_from_buffer / git_diff_from_buffer reject this valid diff.

Why it happens

In parse_header_git() the header state machine reaches STATE_INDEX after the index line but has no accepting transition out of it when the header ends there. STATE_INDEX is not treated as a terminal state, and a following diff --git (the multi-file case, where an empty-file add is followed by another file) is not recognized from STATE_INDEX either, so parsing errors out.

#5248 previously handled empty files in git format-patch output, which terminate with a -- signature line (the { "-- ", STATE_INDEX, ... } transition). Plain git diff output has no -- line, so that path doesn't apply and the header ends directly at index — the case this change covers.

Fix

Two additive changes mirroring the existing STATE_END handling:

  1. Accept STATE_INDEX as a second terminal state (for a header that ends at end-of-buffer):
    if (state != STATE_END && state != STATE_INDEX) {
  2. Recognize a new diff --git header beginning from STATE_INDEX (for the multi-file case), mirroring the existing next-patch row for STATE_END:
    { "diff --git ", STATE_INDEX, 0, NULL },

A non-empty file diff still transitions out of STATE_INDEX via the --- row, so real modifications are unaffected. A truncated modification (bare index with no hunks) is still rejected by the existing check_patch() "patch with no hunks" guard, matching git apply.

Tests

Adds parser tests in tests/libgit2/patch/parse.c for an empty-file add (both at end-of-buffer and followed by a second file in a multi-file patch), reproducing the issue's exact input.

Fixes #6778.

When a new file is empty, git's diff for staging it consists of only
a `diff --git` line, a `new file mode` line, and an `index` line --
there is no hunk, since there is no content to show. Parsing such a
diff currently fails with "unexpected header line", because the
header state machine has no accepting transition out of STATE_INDEX
when the header ends there instead of continuing on to a `---`/`+++`
pair or a binary marker. The same gap also broke a multi-file patch
whose first file was an empty add, since a following `diff --git`
line right after STATE_INDEX was likewise unrecognized.

Fix this by accepting STATE_INDEX as a second terminal state and by
adding a transition that lets a new `diff --git` header begin from
STATE_INDEX, mirroring the existing STATE_END handling for both.

Fixes libgit2#6778.
@ethomson

ethomson commented Aug 1, 2026

Copy link
Copy Markdown
Member

Makes sense. Thanks!

@ethomson
ethomson merged commit 5adb922 into libgit2:main Aug 3, 2026
22 checks passed
@ethomson ethomson added the bug label Aug 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Failure to parse diff when it adds an empty file

2 participants