Skip to content

Commit e051a14

Browse files
committed
git_patch_from_diff may return a NULL patch without an error
1 parent a85f6fb commit e051a14

File tree

3 files changed

+38
-2
lines changed

3 files changed

+38
-2
lines changed

‎pygit2/_pygit2.pyi‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -432,8 +432,8 @@ class Diff:
432432
deffrom_c(diff, repo) ->Diff: ...
433433
@staticmethod
434434
defparse_diff(git_diff: str|bytes) ->Diff: ...
435-
def__getitem__(self, index: int) ->Patch: ... # Diff_getitem
436-
def__iter__(self) ->Iterator[Patch]: ... # -> DiffIter
435+
def__getitem__(self, index: int) ->Patch|None: ... # Diff_getitem
436+
def__iter__(self) ->Iterator[Patch|None]: ... # -> DiffIter
437437
def__len__(self) ->int: ...
438438

439439
classDiffDelta:

‎src/diff.c‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,11 @@ diff_get_patch_byindex(git_diff *diff, size_t idx)
533533
if (err<0)
534534
returnError_set(err);
535535

536+
/* libgit2 may decide not to create a patch if the file is
537+
"unchanged or binary", but this isn't an error case */
538+
if (patch==NULL)
539+
Py_RETURN_NONE;
540+
536541
return (PyObject*) wrap_patch(patch, NULL, NULL);
537542
}
538543

‎test/test_diff.py‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
importtextwrap
2929
fromcollections.abcimportIterator
3030
fromitertoolsimportchain
31+
frompathlibimportPath
3132

3233
importpytest
3334

@@ -459,3 +460,33 @@ def test_diff_blobs(emptyrepo: Repository) -> None:
459460
assertdiff_one_context_line.text==PATCH_BLOBS_ONE_CONTEXT_LINE
460461
diff_all_together=repo.diff(blob1, blob2, context_lines=1, interhunk_lines=1)
461462
assertdiff_all_together.text==PATCH_BLOBS_DEFAULT
463+
464+
465+
deftest_diff_unchanged_file_no_patch(testrepo) ->None:
466+
repo=testrepo
467+
468+
# Convert hello.txt line endings to CRLF
469+
path=Path(repo.workdir) /'hello.txt'
470+
data=path.read_bytes()
471+
data=data.replace(b'\n', b'\r\n')
472+
path.write_bytes(data)
473+
474+
# Enable CRLF filter
475+
repo.config['core.autocrlf'] ='input'
476+
477+
diff=repo.diff()
478+
assertlen(diff) ==1
479+
480+
# Get patch #0 in the same diff several times.
481+
# git_patch_from_diff eventually decides that the file is "unchanged"
482+
# it returns a NULL patch in this case.
483+
# https://libgit2.org/docs/reference/main/patch/git_patch_from_diff
484+
foriinrange(10): # loop typically exits in the third iteration
485+
patch=diff[0]
486+
ifpatchisNone: # libgit2 decides the file is unchanged
487+
break
488+
assertpatch.delta.new_file.path==path.name
489+
assertpatch.text==''# no content change (just line endings)
490+
else:
491+
# Didn't find the edge case that this test is supposed to exercise.
492+
assertFalse, 'libgit2 rebuilt a new patch every time'

0 commit comments

Comments
(0)