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
7 changes: 7 additions & 0 deletions tests/samples/debdiff.diff
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
diff -u -N original/added.txt new/added.txt
--- original/added.txt 1970-01-01 01:00:00.000000000 +0100
+++ new/added.txt 2025-03-26 12:42:27.672906377 +0000
@@ -0,0 +1 @@
+new file
Binary files Binary files /t/p1/a.png and /t/p2/a.png differ
Binary files Binary files /t/p1/b.png and /t/p2/b.png differ
28 changes: 28 additions & 0 deletions tests/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,30 +284,58 @@ def test_parse_diff_with_new_and_modified_binary_files(self):
self.assertFalse(res[0].is_removed_file)
self.assertTrue(res[0].is_added_file)
self.assertFalse(res[0].is_binary_file)
self.assertEqual(res[0].diff_line_no, 1)

# second file is added
self.assertFalse(res[1].is_modified_file)
self.assertFalse(res[1].is_removed_file)
self.assertTrue(res[1].is_added_file)
self.assertTrue(res[1].is_binary_file)
self.assertEqual(res[1].diff_line_no, 4)

# third file is modified
self.assertTrue(res[2].is_modified_file)
self.assertFalse(res[2].is_removed_file)
self.assertFalse(res[2].is_added_file)
self.assertTrue(res[2].is_binary_file)
self.assertEqual(res[2].diff_line_no, 8)

# fourth file is removed
self.assertFalse(res[3].is_modified_file)
self.assertTrue(res[3].is_removed_file)
self.assertFalse(res[3].is_added_file)
self.assertTrue(res[3].is_binary_file)
self.assertEqual(res[3].diff_line_no, 11)

# fifth empty file is added
self.assertFalse(res[4].is_modified_file)
self.assertFalse(res[4].is_removed_file)
self.assertTrue(res[4].is_added_file)
self.assertFalse(res[4].is_binary_file)
self.assertEqual(res[4].diff_line_no, 15)

def test_parse_debdiff_binary_file_line_numbers(self):
# issue #122 / PR #123: a binary change without hunks should still
# expose the diff line number where its entry appears.
utf8_file = os.path.join(self.samples_dir, 'samples/debdiff.diff')
with open(utf8_file, 'r') as diff_file:
res = PatchSet(diff_file)

self.assertEqual(len(res), 3)

# first file has a hunk; entry starts at the +++ line (3)
self.assertEqual(res[0].path, 'new/added.txt')
self.assertEqual(res[0].diff_line_no, 3)
self.assertEqual(res[0][0][0].diff_line_no, 5)

# the binary entries carry the line number of their "Binary files" line
self.assertEqual(res[1].path, '/t/p2/a.png')
self.assertTrue(res[1].is_binary_file)
self.assertEqual(res[1].diff_line_no, 6)

self.assertEqual(res[2].path, '/t/p2/b.png')
self.assertTrue(res[2].is_binary_file)
self.assertEqual(res[2].diff_line_no, 7)

def test_parse_round_trip_with_binary_files_in_diff(self):
"""Parse git diff with binary files though round trip"""
Expand Down
17 changes: 12 additions & 5 deletions unidiff/patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,8 @@ def __init__(self, patch_info: Optional[PatchInfo] = None,
target_timestamp: Optional[str] = None,
is_binary_file: bool = False,
source_mode: Optional[str] = None,
target_mode: Optional[str] = None) -> None:
target_mode: Optional[str] = None,
diff_line_no: Optional[int] = None) -> None:
super(PatchedFile, self).__init__()
self.patch_info = patch_info
self.source_file = source
Expand All @@ -220,6 +221,9 @@ def __init__(self, patch_info: Optional[PatchInfo] = None,
# git file modes (e.g. '100644', '100755', '120000'); None if unknown
self.source_mode = source_mode
self.target_mode = target_mode
# 1-based line number in the diff where this file entry starts; useful
# to locate files that have no hunks (e.g. binary changes)
self.diff_line_no = diff_line_no

def __repr__(self) -> str:
return "<PatchedFile: %s>" % self.path
Expand Down Expand Up @@ -453,7 +457,7 @@ def _parse(self, diff: Iterable, encoding: Optional[str],
patch_info = None

diff_lines = enumerate(diff, 1)
for unused_diff_line_no, line in diff_lines:
for diff_line_no, line in diff_lines:
if encoding is not None:
line = line.decode(encoding)

Expand All @@ -466,7 +470,8 @@ def _parse(self, diff: Iterable, encoding: Optional[str],
source_file = is_diff_git_header.group('source')
target_file = is_diff_git_header.group('target')
current_file = PatchedFile(
patch_info, source_file, target_file, None, None)
patch_info, source_file, target_file, None, None,
diff_line_no=diff_line_no)
self.append(current_file)
patch_info.append(line)
continue
Expand Down Expand Up @@ -542,7 +547,8 @@ def _parse(self, diff: Iterable, encoding: Optional[str],
# add current file to PatchSet
current_file = PatchedFile(
patch_info, source_file, target_file,
source_timestamp, target_timestamp)
source_timestamp, target_timestamp,
diff_line_no=diff_line_no)
self.append(current_file)
patch_info = None
else:
Expand Down Expand Up @@ -585,7 +591,8 @@ def _parse(self, diff: Iterable, encoding: Optional[str],
current_file.is_binary_file = True
else:
current_file = PatchedFile(
patch_info, source_file, target_file, is_binary_file=True)
patch_info, source_file, target_file, is_binary_file=True,
diff_line_no=diff_line_no)
self.append(current_file)
patch_info = None
current_file = None
Expand Down
Loading