Skip to content

Commit

Permalink
Re-implement Git version parsing with regex
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
uranusjr committed Jul 1, 2021
1 parent bef589d commit 48fc779
Show file tree
Hide file tree
Showing 3 changed files with 17 additions and 18 deletions.
2 changes: 2 additions & 0 deletions news/10117.removal.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Git version parsing is now done with regular expression to prepare for the
pending upstream removal of non-PEP-440 version parsing logic.
30 changes: 14 additions & 16 deletions src/pip/_internal/vcs/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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. '[email protected]:foo/bar.git'
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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:
Expand Down
3 changes: 1 addition & 2 deletions tests/unit/test_vcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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', [
Expand Down

0 comments on commit 48fc779

Please sign in to comment.