diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c75cdd62f..e199302e2 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -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) ==================================== diff --git a/dfetch/vcs/patch.py b/dfetch/vcs/patch.py index 9e95a6e87..a70e85d93 100644 --- a/dfetch/vcs/patch.py +++ b/dfetch/vcs/patch.py @@ -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" ) ) diff --git a/tests/test_patch.py b/tests/test_patch.py new file mode 100644 index 000000000..33253cf20 --- /dev/null +++ b/tests/test_patch.py @@ -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