diff options
author | Harmen Stoppels <harmenstoppels@gmail.com> | 2023-08-02 17:47:08 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-08-02 17:47:08 +0200 |
commit | e7fa6d99bf9fb38ad90c1fce6649e90c4b862218 (patch) | |
tree | f3dd185a2bf064e95f657a323c26551a3f2e1bf9 /lib | |
parent | 03c0d7413907fc46b82bbc77dd9bd2276b5e070e (diff) | |
download | spack-e7fa6d99bf9fb38ad90c1fce6649e90c4b862218.tar.gz spack-e7fa6d99bf9fb38ad90c1fce6649e90c4b862218.tar.bz2 spack-e7fa6d99bf9fb38ad90c1fce6649e90c4b862218.tar.xz spack-e7fa6d99bf9fb38ad90c1fce6649e90c4b862218.zip |
version: move to module, avoid circular imports (#39077)
Diffstat (limited to 'lib')
-rw-r--r-- | lib/spack/spack/fetch_strategy.py | 3 | ||||
-rw-r--r-- | lib/spack/spack/solver/asp.py | 5 | ||||
-rw-r--r-- | lib/spack/spack/spec.py | 3 | ||||
-rw-r--r-- | lib/spack/spack/version/__init__.py | 58 | ||||
-rw-r--r-- | lib/spack/spack/version/common.py | 37 | ||||
-rw-r--r-- | lib/spack/spack/version/git_ref_lookup.py | 222 | ||||
-rw-r--r-- | lib/spack/spack/version/lookup.py | 17 | ||||
-rw-r--r-- | lib/spack/spack/version/version_types.py (renamed from lib/spack/spack/version.py) | 281 |
8 files changed, 358 insertions, 268 deletions
diff --git a/lib/spack/spack/fetch_strategy.py b/lib/spack/spack/fetch_strategy.py index f440ea94b7..1f99c4ce9e 100644 --- a/lib/spack/spack/fetch_strategy.py +++ b/lib/spack/spack/fetch_strategy.py @@ -45,6 +45,7 @@ import spack.util.git import spack.util.url as url_util import spack.util.web as web_util import spack.version +import spack.version.git_ref_lookup from spack.util.compression import decompressor_for, extension_from_path from spack.util.executable import CommandNotFoundError, which from spack.util.string import comma_and, quote @@ -1540,7 +1541,7 @@ def for_package_version(pkg, version=None): f"Cannot fetch git version for {pkg.name}. Package has no 'git' attribute" ) # Populate the version with comparisons to other commits - version.attach_git_lookup_from_package(pkg.name) + version.attach_lookup(spack.version.git_ref_lookup.GitRefLookup(pkg.name)) # For GitVersion, we have no way to determine whether a ref is a branch or tag # Fortunately, we handle branches and tags identically, except tags are diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py index 4612f3aeb3..7a4117667b 100644 --- a/lib/spack/spack/solver/asp.py +++ b/lib/spack/spack/solver/asp.py @@ -48,6 +48,7 @@ import spack.util.path import spack.util.timer import spack.variant import spack.version as vn +import spack.version.git_ref_lookup # these are from clingo.ast and bootstrapped later ASTType = None @@ -2672,7 +2673,9 @@ class SpecBuilder: for root in self._specs.values(): for spec in root.traverse(): if isinstance(spec.version, vn.GitVersion): - spec.version.attach_git_lookup_from_package(spec.fullname) + spec.version.attach_lookup( + spack.version.git_ref_lookup.GitRefLookup(spec.fullname) + ) return self._specs diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py index 4a8ba6ae5b..6238bb8c65 100644 --- a/lib/spack/spack/spec.py +++ b/lib/spack/spack/spec.py @@ -88,6 +88,7 @@ import spack.util.spack_yaml as syaml import spack.util.string import spack.variant as vt import spack.version as vn +import spack.version.git_ref_lookup __all__ = [ "CompilerSpec", @@ -4801,7 +4802,7 @@ class Spec: return for v in self.versions: if isinstance(v, vn.GitVersion) and v._ref_version is None: - v.attach_git_lookup_from_package(self.fullname) + v.attach_lookup(spack.version.git_ref_lookup.GitRefLookup(self.fullname)) def parse_with_version_concrete(string: str, compiler: bool = False): diff --git a/lib/spack/spack/version/__init__.py b/lib/spack/spack/version/__init__.py new file mode 100644 index 0000000000..25745a94fd --- /dev/null +++ b/lib/spack/spack/version/__init__.py @@ -0,0 +1,58 @@ +# Copyright 2013-2023 Lawrence Livermore National Security, LLC and other +# Spack Project Developers. See the top-level COPYRIGHT file for details. +# +# SPDX-License-Identifier: (Apache-2.0 OR MIT) + +""" +This module implements Version and version-ish objects. These are: + +StandardVersion: A single version of a package. +ClosedOpenRange: A range of versions of a package. +VersionList: A ordered list of Version and VersionRange elements. + +The set of Version and ClosedOpenRange is totally ordered wiht < +defined as Version(x) < VersionRange(Version(y), Version(x)) +if Version(x) <= Version(y). +""" + +from .common import ( + VersionChecksumError, + VersionError, + VersionLookupError, + infinity_versions, + is_git_version, +) +from .version_types import ( + ClosedOpenRange, + GitVersion, + StandardVersion, + Version, + VersionList, + VersionRange, + from_string, + next_version, + prev_version, + ver, +) + +#: This version contains all possible versions. +any_version: VersionList = VersionList([":"]) + +__all__ = [ + "Version", + "VersionRange", + "ver", + "from_string", + "is_git_version", + "infinity_versions", + "prev_version", + "next_version", + "VersionList", + "ClosedOpenRange", + "StandardVersion", + "GitVersion", + "VersionError", + "VersionChecksumError", + "VersionLookupError", + "any_version", +] diff --git a/lib/spack/spack/version/common.py b/lib/spack/spack/version/common.py new file mode 100644 index 0000000000..e26339d132 --- /dev/null +++ b/lib/spack/spack/version/common.py @@ -0,0 +1,37 @@ +# Copyright 2013-2023 Lawrence Livermore National Security, LLC and other +# Spack Project Developers. See the top-level COPYRIGHT file for details. +# +# SPDX-License-Identifier: (Apache-2.0 OR MIT) + +import re + +import spack.error + +# regex for a commit version +COMMIT_VERSION = re.compile(r"^[a-f0-9]{40}$") + +# Infinity-like versions. The order in the list implies the comparison rules +infinity_versions = ["stable", "trunk", "head", "master", "main", "develop"] + +iv_min_len = min(len(s) for s in infinity_versions) + + +def is_git_version(string: str) -> bool: + return ( + string.startswith("git.") + or len(string) == 40 + and bool(COMMIT_VERSION.match(string)) + or "=" in string[1:] + ) + + +class VersionError(spack.error.SpackError): + """This is raised when something is wrong with a version.""" + + +class VersionChecksumError(VersionError): + """Raised for version checksum errors.""" + + +class VersionLookupError(VersionError): + """Raised for errors looking up git commits as versions.""" diff --git a/lib/spack/spack/version/git_ref_lookup.py b/lib/spack/spack/version/git_ref_lookup.py new file mode 100644 index 0000000000..93f1dfeba8 --- /dev/null +++ b/lib/spack/spack/version/git_ref_lookup.py @@ -0,0 +1,222 @@ +# Copyright 2013-2023 Lawrence Livermore National Security, LLC and other +# Spack Project Developers. See the top-level COPYRIGHT file for details. +# +# SPDX-License-Identifier: (Apache-2.0 OR MIT) + +import os +import re +from typing import Dict, Optional, Tuple + +from llnl.util.filesystem import mkdirp, working_dir + +import spack.caches +import spack.fetch_strategy +import spack.paths +import spack.repo +import spack.util.executable +import spack.util.spack_json as sjson +import spack.util.url +import spack.version + +from .common import VersionLookupError +from .lookup import AbstractRefLookup + +# regular expression for semantic versioning +SEMVER_REGEX = re.compile( + ".+(?P<semver>([0-9]+)[.]([0-9]+)[.]([0-9]+)" + "(?:-([0-9A-Za-z-]+(?:[.][0-9A-Za-z-]+)*))?" + "(?:[+][0-9A-Za-z-]+)?)" +) + + +class GitRefLookup(AbstractRefLookup): + """An object for cached lookups of git refs + + GitRefLookup objects delegate to the misc_cache for locking. GitRefLookup objects may + be attached to a GitVersion to allow for comparisons between git refs and versions as + represented by tags in the git repository. + """ + + def __init__(self, pkg_name): + self.pkg_name = pkg_name + + self.data: Dict[str, Tuple[Optional[str], int]] = {} + + self._pkg = None + self._fetcher = None + self._cache_key = None + self._cache_path = None + + # The following properties are used as part of a lazy reference scheme + # to avoid querying the package repository until it is necessary (and + # in particular to wait until after the configuration has been + # assembled) + @property + def cache_key(self): + if not self._cache_key: + key_base = "git_metadata" + if not self.repository_uri.startswith("/"): + key_base += "/" + self._cache_key = key_base + self.repository_uri + + # Cache data in misc_cache + # If this is the first lazy access, initialize the cache as well + spack.caches.misc_cache.init_entry(self.cache_key) + return self._cache_key + + @property + def cache_path(self): + if not self._cache_path: + self._cache_path = spack.caches.misc_cache.cache_path(self.cache_key) + return self._cache_path + + @property + def pkg(self): + if not self._pkg: + try: + pkg = spack.repo.path.get_pkg_class(self.pkg_name) + pkg.git + except (spack.repo.RepoError, AttributeError) as e: + raise VersionLookupError(f"Couldn't get the git repo for {self.pkg_name}") from e + self._pkg = pkg + return self._pkg + + @property + def fetcher(self): + if not self._fetcher: + # We require the full git repository history + fetcher = spack.fetch_strategy.GitFetchStrategy(git=self.pkg.git) + fetcher.get_full_repo = True + self._fetcher = fetcher + return self._fetcher + + @property + def repository_uri(self): + """Identifier for git repos used within the repo and metadata caches.""" + try: + components = [ + str(c).lstrip("/") for c in spack.util.url.parse_git_url(self.pkg.git) if c + ] + return os.path.join(*components) + except ValueError: + # If it's not a git url, it's a local path + return os.path.abspath(self.pkg.git) + + def save(self): + """Save the data to file""" + with spack.caches.misc_cache.write_transaction(self.cache_key) as (old, new): + sjson.dump(self.data, new) + + def load_data(self): + """Load data if the path already exists.""" + if os.path.isfile(self.cache_path): + with spack.caches.misc_cache.read_transaction(self.cache_key) as cache_file: + self.data = sjson.load(cache_file) + + def get(self, ref) -> Tuple[Optional[str], int]: + if not self.data: + self.load_data() + + if ref not in self.data: + self.data[ref] = self.lookup_ref(ref) + self.save() + + return self.data[ref] + + def lookup_ref(self, ref) -> Tuple[Optional[str], int]: + """Lookup the previous version and distance for a given commit. + + We use git to compare the known versions from package to the git tags, + as well as any git tags that are SEMVER versions, and find the latest + known version prior to the commit, as well as the distance from that version + to the commit in the git repo. Those values are used to compare Version objects. + """ + dest = os.path.join(spack.paths.user_repos_cache_path, self.repository_uri) + if dest.endswith(".git"): + dest = dest[:-4] + + # prepare a cache for the repository + dest_parent = os.path.dirname(dest) + if not os.path.exists(dest_parent): + mkdirp(dest_parent) + + # Only clone if we don't have it! + if not os.path.exists(dest): + self.fetcher.clone(dest, bare=True) + + # Lookup commit info + with working_dir(dest): + # TODO: we need to update the local tags if they changed on the + # remote instance, simply adding '-f' may not be sufficient + # (if commits are deleted on the remote, this command alone + # won't properly update the local rev-list) + self.fetcher.git("fetch", "--tags", output=os.devnull, error=os.devnull) + + # Ensure ref is a commit object known to git + # Note the brackets are literals, the ref replaces the format string + try: + self.fetcher.git( + "cat-file", "-e", "%s^{commit}" % ref, output=os.devnull, error=os.devnull + ) + except spack.util.executable.ProcessError: + raise VersionLookupError("%s is not a valid git ref for %s" % (ref, self.pkg_name)) + + # List tags (refs) by date, so last reference of a tag is newest + tag_info = self.fetcher.git( + "for-each-ref", + "--sort=creatordate", + "--format", + "%(objectname) %(refname)", + "refs/tags", + output=str, + ).split("\n") + + # Lookup of commits to spack versions + commit_to_version = {} + + for entry in tag_info: + if not entry: + continue + tag_commit, tag = entry.split() + tag = tag.replace("refs/tags/", "", 1) + + # For each tag, try to match to a version + for v in [v.string for v in self.pkg.versions]: + if v == tag or "v" + v == tag: + commit_to_version[tag_commit] = v + break + else: + # try to parse tag to copare versions spack does not know + match = SEMVER_REGEX.match(tag) + if match: + semver = match.groupdict()["semver"] + commit_to_version[tag_commit] = semver + + ancestor_commits = [] + for tag_commit in commit_to_version: + self.fetcher.git("merge-base", "--is-ancestor", tag_commit, ref, ignore_errors=[1]) + if self.fetcher.git.returncode == 0: + distance = self.fetcher.git( + "rev-list", "%s..%s" % (tag_commit, ref), "--count", output=str, error=str + ).strip() + ancestor_commits.append((tag_commit, int(distance))) + + if ancestor_commits: + # Get nearest ancestor that is a known version + prev_version_commit, distance = min(ancestor_commits, key=lambda x: x[1]) + prev_version = commit_to_version[prev_version_commit] + else: + # Get list of all commits, this is in reverse order + # We use this to get the first commit below + ref_info = self.fetcher.git("log", "--all", "--pretty=format:%H", output=str) + commits = [c for c in ref_info.split("\n") if c] + + # No previous version and distance from first commit + prev_version = None + distance = int( + self.fetcher.git( + "rev-list", "%s..%s" % (commits[-1], ref), "--count", output=str, error=str + ).strip() + ) + + return prev_version, distance diff --git a/lib/spack/spack/version/lookup.py b/lib/spack/spack/version/lookup.py new file mode 100644 index 0000000000..e33b5c2176 --- /dev/null +++ b/lib/spack/spack/version/lookup.py @@ -0,0 +1,17 @@ +# Copyright 2013-2023 Lawrence Livermore National Security, LLC and other +# Spack Project Developers. See the top-level COPYRIGHT file for details. +# +# SPDX-License-Identifier: (Apache-2.0 OR MIT) + +from typing import Optional, Tuple + + +class AbstractRefLookup: + def get(self, ref) -> Tuple[Optional[str], int]: + """Get the version string and distance for a given git ref. + + Args: + ref (str): git ref to lookup + + Returns: optional version string and distance""" + return None, 0 diff --git a/lib/spack/spack/version.py b/lib/spack/spack/version/version_types.py index 4be1afd90c..223cad40d6 100644 --- a/lib/spack/spack/version.py +++ b/lib/spack/spack/version/version_types.py @@ -3,54 +3,28 @@ # # SPDX-License-Identifier: (Apache-2.0 OR MIT) -""" -This module implements Version and version-ish objects. These are: - -StandardVersion: A single version of a package. -ClosedOpenRange: A range of versions of a package. -VersionList: A ordered list of Version and VersionRange elements. - -The set of Version and ClosedOpenRange is totally ordered wiht < -defined as Version(x) < VersionRange(Version(y), Version(x)) -if Version(x) <= Version(y). -""" import numbers -import os import re from bisect import bisect_left -from typing import Dict, List, Optional, Tuple, Union - -from llnl.util.filesystem import mkdirp, working_dir +from typing import List, Optional, Tuple, Union -import spack.caches -import spack.error -import spack.paths -import spack.util.executable -import spack.util.spack_json as sjson -import spack.util.url from spack.util.spack_yaml import syaml_dict +from .common import ( + COMMIT_VERSION, + VersionLookupError, + infinity_versions, + is_git_version, + iv_min_len, +) +from .lookup import AbstractRefLookup + # Valid version characters VALID_VERSION = re.compile(r"^[A-Za-z0-9_.-][=A-Za-z0-9_.-]*$") -# regex for a commit version -COMMIT_VERSION = re.compile(r"^[a-f0-9]{40}$") - # regex for version segments SEGMENT_REGEX = re.compile(r"(?:(?P<num>[0-9]+)|(?P<str>[a-zA-Z]+))(?P<sep>[_.-]*)") -# regular expression for semantic versioning -SEMVER_REGEX = re.compile( - ".+(?P<semver>([0-9]+)[.]([0-9]+)[.]([0-9]+)" - "(?:-([0-9A-Za-z-]+(?:[.][0-9A-Za-z-]+)*))?" - "(?:[+][0-9A-Za-z-]+)?)" -) - -# Infinity-like versions. The order in the list implies the comparison rules -infinity_versions = ["stable", "trunk", "head", "master", "main", "develop"] - -iv_min_len = min(len(s) for s in infinity_versions) - class VersionStrComponent: __slots__ = ["data"] @@ -407,7 +381,7 @@ class GitVersion(ConcreteVersion): def __init__(self, string: str): # An object that can lookup git refs to compare them to versions - self._ref_lookup: Optional[CommitLookup] = None + self._ref_lookup: Optional[AbstractRefLookup] = None # This is the effective version. self._ref_version: Optional[StandardVersion] @@ -440,8 +414,7 @@ class GitVersion(ConcreteVersion): if self.ref_lookup is None: raise VersionLookupError( - f"git ref '{self.ref}' cannot be looked up: " - "call attach_git_lookup_from_package first" + f"git ref '{self.ref}' cannot be looked up: " "call attach_lookup first" ) version_string, distance = self.ref_lookup.get(self.ref) @@ -573,7 +546,7 @@ class GitVersion(ConcreteVersion): self._ref_lookup.get(self.ref) return self._ref_lookup - def attach_git_lookup_from_package(self, pkg_name): + def attach_lookup(self, lookup: AbstractRefLookup): """ Use the git fetcher to look up a version for a commit. @@ -585,7 +558,7 @@ class GitVersion(ConcreteVersion): alongside the GitFetcher because eventually the git repos cache will be one and the same with the source cache. """ - self._ref_lookup = self._ref_lookup or CommitLookup(pkg_name) + self._ref_lookup = lookup def __iter__(self): return self.ref_version.__iter__() @@ -758,7 +731,7 @@ class VersionList: if vlist is not None: if isinstance(vlist, str): vlist = from_string(vlist) - if type(vlist) == VersionList: + if isinstance(vlist, VersionList): self.versions = vlist.versions else: self.versions = [vlist] @@ -792,7 +765,7 @@ class VersionList: self.versions.insert(i, item) - elif type(item) == VersionList: + elif isinstance(item, VersionList): for v in item: self.add(v) @@ -1073,15 +1046,6 @@ def prev_version(v: StandardVersion) -> StandardVersion: return StandardVersion("".join(string_components), v.version[:-1] + (prev,), v.separators) -def is_git_version(string: str) -> bool: - return ( - string.startswith("git.") - or len(string) == 40 - and bool(COMMIT_VERSION.match(string)) - or "=" in string[1:] - ) - - def Version(string: Union[str, int]) -> Union[GitVersion, StandardVersion]: if not isinstance(string, (str, int)): raise ValueError(f"Cannot construct a version from {type(string)}") @@ -1140,216 +1104,3 @@ def ver(obj) -> Union[VersionList, ClosedOpenRange, StandardVersion, GitVersion] return obj else: raise TypeError("ver() can't convert %s to version!" % type(obj)) - - -#: This version contains all possible versions. -any_version: VersionList = VersionList([":"]) - - -class VersionError(spack.error.SpackError): - """This is raised when something is wrong with a version.""" - - -class VersionChecksumError(VersionError): - """Raised for version checksum errors.""" - - -class VersionLookupError(VersionError): - """Raised for errors looking up git commits as versions.""" - - -class CommitLookup: - """An object for cached lookups of git commits - - CommitLookup objects delegate to the misc_cache for locking. CommitLookup objects may - be attached to a GitVersion to allow for comparisons between git refs and versions as - represented by tags in the git repository. - """ - - def __init__(self, pkg_name): - self.pkg_name = pkg_name - - self.data: Dict[str, Tuple[Optional[str], int]] = {} - - self._pkg = None - self._fetcher = None - self._cache_key = None - self._cache_path = None - - # The following properties are used as part of a lazy reference scheme - # to avoid querying the package repository until it is necessary (and - # in particular to wait until after the configuration has been - # assembled) - @property - def cache_key(self): - if not self._cache_key: - key_base = "git_metadata" - if not self.repository_uri.startswith("/"): - key_base += "/" - self._cache_key = key_base + self.repository_uri - - # Cache data in misc_cache - # If this is the first lazy access, initialize the cache as well - spack.caches.misc_cache.init_entry(self.cache_key) - return self._cache_key - - @property - def cache_path(self): - if not self._cache_path: - self._cache_path = spack.caches.misc_cache.cache_path(self.cache_key) - return self._cache_path - - @property - def pkg(self): - if not self._pkg: - import spack.repo # break cycle - - try: - pkg = spack.repo.path.get_pkg_class(self.pkg_name) - pkg.git - except (spack.repo.RepoError, AttributeError) as e: - raise VersionLookupError(f"Couldn't get the git repo for {self.pkg_name}") from e - self._pkg = pkg - return self._pkg - - @property - def fetcher(self): - if not self._fetcher: - # We require the full git repository history - import spack.fetch_strategy # break cycle - - fetcher = spack.fetch_strategy.GitFetchStrategy(git=self.pkg.git) - fetcher.get_full_repo = True - self._fetcher = fetcher - return self._fetcher - - @property - def repository_uri(self): - """Identifier for git repos used within the repo and metadata caches.""" - try: - components = [ - str(c).lstrip("/") for c in spack.util.url.parse_git_url(self.pkg.git) if c - ] - return os.path.join(*components) - except ValueError: - # If it's not a git url, it's a local path - return os.path.abspath(self.pkg.git) - - def save(self): - """Save the data to file""" - with spack.caches.misc_cache.write_transaction(self.cache_key) as (old, new): - sjson.dump(self.data, new) - - def load_data(self): - """Load data if the path already exists.""" - if os.path.isfile(self.cache_path): - with spack.caches.misc_cache.read_transaction(self.cache_key) as cache_file: - self.data = sjson.load(cache_file) - - def get(self, ref) -> Tuple[Optional[str], int]: - if not self.data: - self.load_data() - - if ref not in self.data: - self.data[ref] = self.lookup_ref(ref) - self.save() - - return self.data[ref] - - def lookup_ref(self, ref) -> Tuple[Optional[str], int]: - """Lookup the previous version and distance for a given commit. - - We use git to compare the known versions from package to the git tags, - as well as any git tags that are SEMVER versions, and find the latest - known version prior to the commit, as well as the distance from that version - to the commit in the git repo. Those values are used to compare Version objects. - """ - dest = os.path.join(spack.paths.user_repos_cache_path, self.repository_uri) - if dest.endswith(".git"): - dest = dest[:-4] - - # prepare a cache for the repository - dest_parent = os.path.dirname(dest) - if not os.path.exists(dest_parent): - mkdirp(dest_parent) - - # Only clone if we don't have it! - if not os.path.exists(dest): - self.fetcher.clone(dest, bare=True) - - # Lookup commit info - with working_dir(dest): - # TODO: we need to update the local tags if they changed on the - # remote instance, simply adding '-f' may not be sufficient - # (if commits are deleted on the remote, this command alone - # won't properly update the local rev-list) - self.fetcher.git("fetch", "--tags", output=os.devnull, error=os.devnull) - - # Ensure ref is a commit object known to git - # Note the brackets are literals, the ref replaces the format string - try: - self.fetcher.git( - "cat-file", "-e", "%s^{commit}" % ref, output=os.devnull, error=os.devnull - ) - except spack.util.executable.ProcessError: - raise VersionLookupError("%s is not a valid git ref for %s" % (ref, self.pkg_name)) - - # List tags (refs) by date, so last reference of a tag is newest - tag_info = self.fetcher.git( - "for-each-ref", - "--sort=creatordate", - "--format", - "%(objectname) %(refname)", - "refs/tags", - output=str, - ).split("\n") - - # Lookup of commits to spack versions - commit_to_version = {} - - for entry in tag_info: - if not entry: - continue - tag_commit, tag = entry.split() - tag = tag.replace("refs/tags/", "", 1) - - # For each tag, try to match to a version - for v in [v.string for v in self.pkg.versions]: - if v == tag or "v" + v == tag: - commit_to_version[tag_commit] = v - break - else: - # try to parse tag to copare versions spack does not know - match = SEMVER_REGEX.match(tag) - if match: - semver = match.groupdict()["semver"] - commit_to_version[tag_commit] = semver - - ancestor_commits = [] - for tag_commit in commit_to_version: - self.fetcher.git("merge-base", "--is-ancestor", tag_commit, ref, ignore_errors=[1]) - if self.fetcher.git.returncode == 0: - distance = self.fetcher.git( - "rev-list", "%s..%s" % (tag_commit, ref), "--count", output=str, error=str - ).strip() - ancestor_commits.append((tag_commit, int(distance))) - - if ancestor_commits: - # Get nearest ancestor that is a known version - prev_version_commit, distance = min(ancestor_commits, key=lambda x: x[1]) - prev_version = commit_to_version[prev_version_commit] - else: - # Get list of all commits, this is in reverse order - # We use this to get the first commit below - ref_info = self.fetcher.git("log", "--all", "--pretty=format:%H", output=str) - commits = [c for c in ref_info.split("\n") if c] - - # No previous version and distance from first commit - prev_version = None - distance = int( - self.fetcher.git( - "rev-list", "%s..%s" % (commits[-1], ref), "--count", output=str, error=str - ).strip() - ) - - return prev_version, distance |