From 0449b5cad305b3ea669f13fdca5e5cca6e7334b9 Mon Sep 17 00:00:00 2001 From: Tzu-ping Chung Date: Thu, 1 Jul 2021 13:33:38 +0800 Subject: [PATCH] Re-implement Git version parsing with regex After packaging drops LegacyVersion, version.parse() will no longer guarantee to be able to parse Git versions, so we need to invent our own parser. Since we really only care about the major and minor segments, the logic is pretty simple. --- src/pip/_internal/vcs/git.py | 30 ++++++++++++++---------------- tests/unit/test_vcs.py | 3 +-- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/pip/_internal/vcs/git.py b/src/pip/_internal/vcs/git.py index b860e350a2d..269bf6a65b3 100644 --- a/src/pip/_internal/vcs/git.py +++ b/src/pip/_internal/vcs/git.py @@ -6,9 +6,6 @@ import urllib.request from typing import List, Optional, Tuple -from pip._vendor.packaging.version import _BaseVersion -from pip._vendor.packaging.version import parse as parse_version - from pip._internal.exceptions import BadCommand, InstallationError from pip._internal.utils.misc import HiddenText, display_path, hide_url from pip._internal.utils.subprocess import make_command @@ -29,6 +26,14 @@ logger = logging.getLogger(__name__) +GIT_VERSION_REGEX = re.compile( + r"^git version " # Prefix. + r"(\d+)" # Major. + r"\.(\d+)" # Dot, minor. + r"(?:\.(\d+))?" # Optional dot, patch. + r".*$" # Suffix, including any pre- and post-release segments we don't care about. +) + HASH_REGEX = re.compile('^[a-fA-F0-9]{40}$') # SCP (Secure copy protocol) shorthand. e.g. 'git@example.com:foo/bar.git' @@ -83,21 +88,14 @@ def is_immutable_rev_checkout(self, url, dest): ) return not is_tag_or_branch - def get_git_version(self): - # type: () -> _BaseVersion - VERSION_PFX = 'git version ' + def get_git_version(self) -> Tuple[int, ...]: version = self.run_command( ['version'], show_stdout=False, stdout_only=True ) - if version.startswith(VERSION_PFX): - version = version[len(VERSION_PFX):].split()[0] - else: - version = '' - # get first 3 positions of the git version because - # on windows it is x.y.z.windows.t, and this parses as - # LegacyVersion which always smaller than a Version. - version = '.'.join(version.split('.')[:3]) - return parse_version(version) + match = GIT_VERSION_REGEX.match(version) + if not match: + return () + return tuple(int(c) for c in match.groups()) @classmethod def get_current_branch(cls, location): @@ -301,7 +299,7 @@ def switch(self, dest, url, rev_options): def update(self, dest, url, rev_options): # type: (str, HiddenText, RevOptions) -> None # First fetch changes from the default remote - if self.get_git_version() >= parse_version('1.9.0'): + if self.get_git_version() >= (1, 9): # fetch tags in addition to everything else self.run_command(['fetch', '-q', '--tags'], cwd=dest) else: diff --git a/tests/unit/test_vcs.py b/tests/unit/test_vcs.py index 305c45fd857..403f3946673 100644 --- a/tests/unit/test_vcs.py +++ b/tests/unit/test_vcs.py @@ -4,7 +4,6 @@ from unittest.mock import patch import pytest -from pip._vendor.packaging.version import parse as parse_version from pip._internal.exceptions import BadCommand, InstallationError from pip._internal.utils.misc import hide_url, hide_value @@ -483,7 +482,7 @@ def test_subversion__get_url_rev_options(): def test_get_git_version(): git_version = Git().get_git_version() - assert git_version >= parse_version('1.0.0') + assert git_version >= (1, 0, 0) @pytest.mark.parametrize('use_interactive,is_atty,expected', [