3939from typing import Tuple
4040import xml .etree .ElementTree as ET
4141
42- import pygit2
42+ from git import Repo , DiffIndex
4343from packageurl import PackageURL
4444
4545from vulnerabilities .oval_parser import OvalParser
@@ -319,34 +319,38 @@ def _collect_file_changes(
319319 file_ext : Optional [str ],
320320 ) -> Tuple [Set [str ], Set [str ]]:
321321
322- previous_commit = None
323322 added_files , updated_files = set (), set ()
324323
325- for commit in self . _repo . walk ( self . _repo . head . target , pygit2 . GIT_SORT_TIME ):
326- commit_time = commit . commit_time + commit . commit_time_offset # convert to UTC
327-
328- if commit_time < self .cutoff_timestamp :
324+ # find the most ancient commit we need to diff with
325+ cutoff_commit = None
326+ for commit in self . _repo . iter_commits ( self . _repo . head ):
327+ if commit . committed_date < self .cutoff_timestamp :
329328 break
329+ cutoff_commit = commit
330330
331- if previous_commit is None :
332- previous_commit = commit
333- continue
331+ if cutoff_commit is None :
332+ return added_files , updated_files
334333
335- for d in commit .tree .diff_to_tree (previous_commit .tree ).deltas :
336- if not _include_file (d .new_file .path , subdir , recursive , file_ext ) or d .is_binary :
337- continue
334+ def _is_binary (d : DiffIndex ):
335+ if not d .b_blob :
336+ return False
337+ try :
338+ d .b_blob .data_stream .read ().decode ()
339+ except UnicodeDecodeError :
340+ return True
341+ return False
338342
339- abspath = os .path .join (self .config .working_directory , d .new_file .path )
340- # TODO
341- # Just filtering on the two status values for "added" and "modified" is too
342- # simplistic. This does not cover file renames, copies &
343- # deletions.
344- if d .status == pygit2 .GIT_DELTA_ADDED :
345- added_files .add (abspath )
346- elif d .status == pygit2 .GIT_DELTA_MODIFIED :
347- updated_files .add (abspath )
343+ for d in cutoff_commit .diff (self ._repo .head .commit ):
344+ if not _include_file (d .b_path , subdir , recursive , file_ext ) or _is_binary (d ):
345+ continue
348346
349- previous_commit = commit
347+ abspath = os .path .join (self .config .working_directory , d .b_path )
348+ if d .new_file :
349+ added_files .add (abspath )
350+ elif d .a_blob and d .b_blob and d .a_path != d .b_path :
351+ added_files .add (abspath ) # consider moved files as added
352+ elif d .a_blob and d .b_blob and d .a_blob != d .b_blob :
353+ updated_files .add (abspath )
350354
351355 # Any file that has been added and then updated inside the window of the git history we
352356 # looked at, should be considered "added", not "updated", since it does not exist in the
@@ -364,29 +368,26 @@ def _ensure_working_directory(self) -> None:
364368 os .mkdir (self .config .working_directory )
365369
366370 def _ensure_repository (self ) -> None :
367- repodir = pygit2 .discover_repository (self .config .working_directory )
368- if repodir is None :
371+ if not os .path .exists (os .path .join (self .config .working_directory , ".git" )):
369372 self ._clone_repository ()
370373 return
371-
372- self ._repo = pygit2 .Repository (repodir )
374+ self ._repo = Repo (self .config .working_directory )
373375
374376 if self .config .branch is None :
375- self .config .branch = self ._repo .head .shorthand
376- branch = self ._repo .branches [self .config .branch ]
377-
378- if not branch .is_checked_out ():
379- self ._repo .checkout (branch )
377+ self .config .branch = self ._repo .head .reference
378+ branch = self .config .branch
379+ self ._repo .head .reference = branch
380+ self ._repo .head .reset (index = True , working_tree = True )
380381
381382 remote = self ._find_or_add_remote ()
382383 self ._update_from_remote (remote , branch )
383384
384385 def _clone_repository (self ) -> None :
385386 kwargs = {}
386387 if self .config .branch :
387- kwargs ["checkout_branch " ] = self .config .branch
388+ kwargs ["branch " ] = self .config .branch
388389
389- self ._repo = pygit2 . clone_repository (
390+ self ._repo = Repo . clone_from (
390391 self .config .repository_url , self .config .working_directory , ** kwargs
391392 )
392393
@@ -398,20 +399,19 @@ def _find_or_add_remote(self):
398399 break
399400
400401 if remote is None :
401- remote = self ._repo .remotes . create (
402- "added_by_vulnerablecode" , self .config .repository_url
402+ remote = self ._repo .create_remote (
403+ "added_by_vulnerablecode" , url = self .config .repository_url
403404 )
404405
405406 return remote
406407
407408 def _update_from_remote (self , remote , branch ) -> None :
408- progress = remote .fetch ()
409- if progress . received_objects == 0 :
409+ fetch_info = remote .fetch ()
410+ if len ( fetch_info ) == 0 :
410411 return
411-
412- remote_branch = self ._repo .branches [f"{ remote .name } /{ self .config .branch } " ]
413- branch .set_target (remote_branch .target )
414- self ._repo .checkout (branch , strategy = pygit2 .GIT_CHECKOUT_FORCE )
412+ print (branch .name )
413+ branch .set_reference (remote .refs [branch .name ])
414+ self ._repo .head .reset (index = True , working_tree = True )
415415
416416
417417def _include_file (
0 commit comments