Skip to content

Commit 13eaa01

Browse files
authored
Merge pull request #409 from tardyp/gitpython
Switch data_source dependency to GitPython
2 parents cac1aa3 + 74a1afa commit 13eaa01

4 files changed

Lines changed: 100 additions & 197 deletions

File tree

etc/nix/flake.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@
7474
name = "vulnerablecode-${version}";
7575
src = vulnerablecode-src;
7676
dontConfigure = true; # do not use ./configure
77-
propagatedBuildInputs = [ pythonEnv postgresql ];
77+
propagatedBuildInputs = [ pythonEnv postgresql gitMinimal];
7878

7979
postPatch = ''
8080
# Make sure the pycodestyle binary in $PATH is used.

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ asgiref==3.2.7
33
attrs==20.3.0
44
backcall==0.1.0
55
beautifulsoup4==4.7.1
6+
binaryornot==0.4.4
67
cached-property==1.5.1
78
cffi==1.14.0
89
contextlib2==0.5.5
@@ -33,7 +34,7 @@ psycopg2==2.8.4
3334
ptyprocess==0.6.0
3435
py==1.10.0
3536
pycparser==2.20
36-
pygit2==1.5.0
37+
gitpython==3.1.14
3738
Pygments==2.7.4
3839
pyparsing==2.4.5
3940
pytest==6.2.3

vulnerabilities/data_source.py

Lines changed: 38 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
import shutil
2727
import tempfile
2828
import traceback
29+
import xml.etree.ElementTree as ET
30+
from binaryornot.helpers import is_binary_string
2931
from datetime import datetime
3032
from pathlib import Path
3133
from typing import Any
@@ -36,9 +38,7 @@
3638
from typing import Optional
3739
from typing import Set
3840
from typing import Tuple
39-
import xml.etree.ElementTree as ET
40-
41-
import pygit2
41+
from git import Repo, DiffIndex
4242
from packageurl import PackageURL
4343
from univers.version_specifier import VersionSpecifier
4444
from univers.versions import version_class_by_package_type
@@ -318,35 +318,35 @@ def _collect_file_changes(
318318
file_ext: Optional[str],
319319
) -> Tuple[Set[str], Set[str]]:
320320

321-
previous_commit = None
322321
added_files, updated_files = set(), set()
323322

324-
for commit in self._repo.walk(self._repo.head.target, pygit2.GIT_SORT_TIME):
325-
commit_time = commit.commit_time + commit.commit_time_offset # convert to UTC
326-
327-
if commit_time < self.cutoff_timestamp:
323+
# find the most ancient commit we need to diff with
324+
cutoff_commit = None
325+
for commit in self._repo.iter_commits(self._repo.head):
326+
if commit.committed_date < self.cutoff_timestamp:
328327
break
328+
cutoff_commit = commit
329329

330-
if previous_commit is None:
331-
previous_commit = commit
332-
continue
330+
if cutoff_commit is None:
331+
return added_files, updated_files
333332

334-
for d in commit.tree.diff_to_tree(previous_commit.tree).deltas:
335-
if not _include_file(d.new_file.path, subdir, recursive, file_ext) or d.is_binary:
336-
continue
333+
def _is_binary(d: DiffIndex):
334+
return is_binary_string(d.b_blob.data_stream.read(1024))
337335

338-
abspath = os.path.join(self.config.working_directory, d.new_file.path)
339-
# TODO
340-
# Just filtering on the two status values for "added" and "modified" is too
341-
# simplistic. This does not cover file renames, copies &
342-
# deletions.
343-
if d.status == pygit2.GIT_DELTA_ADDED:
336+
for d in cutoff_commit.diff(self._repo.head.commit):
337+
if not _include_file(d.b_path, subdir, recursive, file_ext) or _is_binary(d):
338+
continue
339+
340+
abspath = os.path.join(self.config.working_directory, d.b_path)
341+
if d.new_file:
342+
added_files.add(abspath)
343+
elif d.a_blob and d.b_blob:
344+
if d.a_path != d.b_path:
345+
# consider moved files as added
344346
added_files.add(abspath)
345-
elif d.status == pygit2.GIT_DELTA_MODIFIED:
347+
elif d.a_blob != d.b_blob:
346348
updated_files.add(abspath)
347349

348-
previous_commit = commit
349-
350350
# Any file that has been added and then updated inside the window of the git history we
351351
# looked at, should be considered "added", not "updated", since it does not exist in the
352352
# database yet.
@@ -363,29 +363,26 @@ def _ensure_working_directory(self) -> None:
363363
os.mkdir(self.config.working_directory)
364364

365365
def _ensure_repository(self) -> None:
366-
repodir = pygit2.discover_repository(self.config.working_directory)
367-
if repodir is None:
366+
if not os.path.exists(os.path.join(self.config.working_directory, ".git")):
368367
self._clone_repository()
369368
return
370-
371-
self._repo = pygit2.Repository(repodir)
369+
self._repo = Repo(self.config.working_directory)
372370

373371
if self.config.branch is None:
374-
self.config.branch = self._repo.head.shorthand
375-
branch = self._repo.branches[self.config.branch]
376-
377-
if not branch.is_checked_out():
378-
self._repo.checkout(branch)
372+
self.config.branch = str(self._repo.active_branch)
373+
branch = self.config.branch
374+
self._repo.head.reference = self._repo.heads[branch]
375+
self._repo.head.reset(index=True, working_tree=True)
379376

380377
remote = self._find_or_add_remote()
381378
self._update_from_remote(remote, branch)
382379

383380
def _clone_repository(self) -> None:
384381
kwargs = {}
385382
if self.config.branch:
386-
kwargs["checkout_branch"] = self.config.branch
383+
kwargs["branch"] = self.config.branch
387384

388-
self._repo = pygit2.clone_repository(
385+
self._repo = Repo.clone_from(
389386
self.config.repository_url, self.config.working_directory, **kwargs
390387
)
391388

@@ -397,20 +394,19 @@ def _find_or_add_remote(self):
397394
break
398395

399396
if remote is None:
400-
remote = self._repo.remotes.create(
401-
"added_by_vulnerablecode", self.config.repository_url
397+
remote = self._repo.create_remote(
398+
"added_by_vulnerablecode", url=self.config.repository_url
402399
)
403400

404401
return remote
405402

406403
def _update_from_remote(self, remote, branch) -> None:
407-
progress = remote.fetch()
408-
if progress.received_objects == 0:
404+
fetch_info = remote.fetch()
405+
if len(fetch_info) == 0:
409406
return
410-
411-
remote_branch = self._repo.branches[f"{remote.name}/{self.config.branch}"]
412-
branch.set_target(remote_branch.target)
413-
self._repo.checkout(branch, strategy=pygit2.GIT_CHECKOUT_FORCE)
407+
branch = self._repo.branches[branch]
408+
branch.set_reference(remote.refs[branch.name])
409+
self._repo.head.reset(index=True, working_tree=True)
414410

415411

416412
def _include_file(

0 commit comments

Comments
 (0)