Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Release 0.12.0 (unreleased)
* Fallback and warn if patch is not UTF-8 encoded (#941)
* Skip patches outside manifest dir (#942)
* Make patch path in metadata platform independent (#937)
* Fix extra newlines in patch for new files (#945)

Release 0.11.0 (released 2026-01-03)
====================================
Expand Down
20 changes: 11 additions & 9 deletions dfetch/vcs/patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,37 +82,39 @@ def apply_patch(patch_path: str, root: str = ".") -> None:

def create_svn_patch_for_new_file(file_path: str) -> str:
"""Create a svn patch for a new file."""
diff = unified_diff_new_file(Path(file_path))
return "" if not diff else "\n".join([f"Index: {file_path}", "=" * 67] + diff)
diff = _unified_diff_new_file(Path(file_path))
return (
"" if not diff else "".join([f"Index: {file_path}\n", "=" * 67 + "\n"] + diff)
)


def create_git_patch_for_new_file(file_path: str) -> str:
"""Create a Git patch for a new untracked file, preserving file mode."""
path = Path(file_path)
diff = unified_diff_new_file(path)
diff = _unified_diff_new_file(path)

return (
""
if not diff
else "\n".join(
else "".join(
[
f"diff --git a/{file_path} b/{file_path}",
f"new file mode {_git_mode(path)}",
f"index 0000000..{_git_blob_sha1(path)[:7]}",
f"diff --git a/{file_path} b/{file_path}\n",
f"new file mode {_git_mode(path)}\n",
f"index 0000000..{_git_blob_sha1(path)[:7]}\n",
]
+ diff
)
)


def unified_diff_new_file(path: Path) -> list[str]:
def _unified_diff_new_file(path: Path) -> list[str]:
"""Create a unified diff for a new file."""
with path.open("r", encoding="utf-8", errors="replace") as new_file:
lines = new_file.readlines()

return list(
difflib.unified_diff(
[], lines, fromfile="/dev/null", tofile=str(path), lineterm=""
[], lines, fromfile="/dev/null", tofile=str(path), lineterm="\n"
)
)

Expand Down
60 changes: 60 additions & 0 deletions tests/test_patch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""Test the patch module."""

# mypy: ignore-errors
# flake8: noqa

import pytest

from dfetch.vcs.patch import (
create_git_patch_for_new_file,
create_svn_patch_for_new_file,
)


def test_create_git_patch_for_new_file(tmp_path):
"""Check basic patch generation for new files."""
test_file = tmp_path / "test.txt"
test_file.write_text("Hello World\n\nLine above is empty\n")

actual_patch = create_git_patch_for_new_file(str(test_file))

expected_patch = "\n".join(
[
f"diff --git a/{test_file} b/{test_file}",
"new file mode 100644",
"index 0000000..8029fa5",
"--- /dev/null",
f"+++ {test_file}",
"@@ -0,0 +1,3 @@",
"+Hello World",
"+",
"+Line above is empty",
"",
]
)

assert actual_patch == expected_patch


def test_create_svn_patch_for_new_file(tmp_path):
"""Check basic patch generation for new files."""
test_file = tmp_path / "test.txt"
test_file.write_text("Hello World\n\nLine above is empty\n")

actual_patch = create_svn_patch_for_new_file(str(test_file))

expected_patch = "\n".join(
[
f"Index: {test_file}",
"===================================================================",
"--- /dev/null",
f"+++ {test_file}",
"@@ -0,0 +1,3 @@",
"+Hello World",
"+",
"+Line above is empty",
"",
]
)

assert actual_patch == expected_patch
Loading