Skip to content

Strip a/ and b/ prefixes for rename-only and mode-only git diffs - #66

Closed
afonsojanu wants to merge 1 commit into
cscorley:mainfrom
afonsojanu:fix-git-rename-mode-header-prefix
Closed

afonsojanu wants to merge 1 commit into
cscorley:mainfrom
afonsojanu:fix-git-rename-mode-header-prefix

Conversation

@afonsojanu

Copy link
Copy Markdown

A git diff that only renames a file, or only changes its mode, has no
content to show, so git omits both the index <sha>..<sha> line and the
unified ---/+++ hunk lines. For example:

diff --git a/myfile2.txt b/myfile3.txt
similarity index 100%
rename from myfile2.txt
rename to myfile3.txt

parse_header() normally relies on one of those two things being present
to recognize the diff as git-flavored and route it to parse_git_header,
which knows to strip the a//b/ prefixes. When neither is there, the
generic diffcmd_header regex ends up matching the diff --git a/x b/y
line instead, since that pattern happens to fit it too, and the raw,
prefixed paths leak straight into old_path/new_path:

>>> whatthepatch.parse_patch(text)[0].header
header(index_path=None, old_path='a/myfile2.txt', old_version=None, new_path='b/myfile3.txt', new_version=None)

This fixes it two ways:

  • parse_diff_header now checks the git-specific diff --git a/... b/...
    line ahead of the generic one, so it's always routed to
    parse_git_header.
  • parse_git_header's fallback (which uses the paths straight from the
    diff --git command line when nothing more specific was found) no
    longer requires blob hashes to be present, since a pure rename or mode
    change never has them.

Added two regression tests with fixtures for a rename-only diff and a
mode-only diff, both of which fail against the old code with the a//b/
prefixes still attached and pass with the fix. The rest of the suite
(tests/test_patch.py and tests/test_apply.py) still passes locally.

Fixes #43.

A git diff that only renames a file or changes its mode has neither
an "index <sha>..<sha>" line nor a unified hunk, since there is no
content to show. parse_header() relies on one of those to recognize
the diff as a git diff and route it to the git-aware parser that
strips the a/ and b/ prefixes from the paths. Without them, the
generic diffcmd_header regex matches the "diff --git a/x b/y" line
first (it happens to fit that pattern too) and the raw, prefixed
paths leak into old_path/new_path.

Give the git-specific "diff --git" line priority over the generic
one in parse_diff_header, and let parse_git_header fall back to the
paths from that line even when no blob hashes were found, so a plain
rename or mode change still gets a usable header.

Fixes #43.
@afonsojanu

Copy link
Copy Markdown
Author

Part of a general cleanup on my end, closing this along with the rest of what I had open.

@afonsojanu afonsojanu closed this Oct 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

not discarding "a/" and "b/" prefixes in paths when parsing git patches with no diff

1 participant