| |
@@ -1,6 +1,7 @@
|
| |
import datetime as dt
|
| |
import logging
|
| |
import re
|
| |
+ import string
|
| |
import subprocess
|
| |
import sys
|
| |
from collections import defaultdict
|
| |
@@ -19,6 +20,25 @@
|
| |
log = logging.getLogger(__name__)
|
| |
|
| |
|
| |
+ class ShallowCloneError(KeyError):
|
| |
+ "We tried to access a commit which is not available in the shallow clone"
|
| |
+
|
| |
+ @classmethod
|
| |
+ def maybe_convert(
|
| |
+ cls, key_error: KeyError, shallow: Optional[str]
|
| |
+ ) -> Optional["ShallowCloneError"]:
|
| |
+ "Return ShallowCloneError or None if it seems the reason was different"
|
| |
+ if not shallow:
|
| |
+ return None
|
| |
+ key = key_error.args[0]
|
| |
+ if isinstance(key, str) and len(key) >= 40 and not (set(key) - set(string.hexdigits)):
|
| |
+ # OK, this looks looks like a sha1 or sha256 hash
|
| |
+ return cls(key)
|
| |
+
|
| |
+ # Some other reason for KeyError?
|
| |
+ return None
|
| |
+
|
| |
+
|
| |
class PkgHistoryProcessor:
|
| |
|
| |
autorelease_flags_re = re.compile(
|
| |
@@ -446,10 +466,21 @@
|
| |
# Unfortunately, pygit2 only tells us what the parents of a commit are, not what other
|
| |
# commits a commit is parent to (its children). Fortunately, Repository.walk() is quick.
|
| |
# This maps parent commits to their children.
|
| |
+
|
| |
+ # libgit2 doesn't support shallow clones. Handle .git/shallow manually.
|
| |
+ # https://github.com/libgit2/libgit2/issues/3058
|
| |
+ try:
|
| |
+ shallow = open(self.repo.path + "shallow", "rt").read().strip()
|
| |
+ except FileNotFoundError:
|
| |
+ shallow = None
|
| |
+
|
| |
commit_children = defaultdict(list)
|
| |
for commit in self.repo.walk(head.id):
|
| |
for parent in commit.parents:
|
| |
commit_children[parent].append(commit)
|
| |
+ if commit.parents and commit.parents[0].id.hex == shallow:
|
| |
+ log.debug("Commit {} is marked as shallow root, stopping iteration", commit.id)
|
| |
+ break
|
| |
|
| |
##########################################################################################
|
| |
# To process, first walk the tree from the head commit downward, following all branches.
|
| |
@@ -542,7 +573,19 @@
|
| |
|
| |
# Consult all visitors for the commit on whether we should continue and store
|
| |
# the results.
|
| |
- commit_coroutines_info[commit] = [next(c) for c in coroutines]
|
| |
+ try:
|
| |
+ commit_coroutines_info[commit] = [next(c) for c in coroutines]
|
| |
+ except KeyError as e:
|
| |
+ # If we encountered a KeyError and we are in a shallow clone, then most
|
| |
+ # likely the visitor tried to access a commit which is not available. Let's
|
| |
+ # generate a nicer error message in that case. But let's be careful not
|
| |
+ # to convert an unrelated exception: we check if the key looks like a
|
| |
+ # hash. This is ugly, but pygit2/libgit2 have this terrible interface.
|
| |
+ e2 = ShallowCloneError.maybe_convert(e, shallow)
|
| |
+ if e2:
|
| |
+ raise e2 from e
|
| |
+ raise
|
| |
+
|
| |
else:
|
| |
# Only traverse this commit. Traversal is important if parent commits are the
|
| |
# root of branches that affect the results (computed release number and
|
| |
@@ -552,6 +595,10 @@
|
| |
{"child_must_continue": False} for v in visitors
|
| |
]
|
| |
|
| |
+ if commit.id.hex == shallow:
|
| |
+ log.debug("\tshallow root, bailing out")
|
| |
+ break
|
| |
+
|
| |
if not commit.parents:
|
| |
log.debug("\tno parents, bailing out")
|
| |
break
|
| |
The way that pygit2 (libgit2?) reacts to a shallow clone is very unfortunate:
it throws a KeyError in the iterator so it's hard to handle such cases nicely.
Let's hope that libgit2 and pygit2 get native support for shallow clones at some
point. For now, let's just check if .git/shallow is present and cut short the loop walking
over the commits.
This is enough for 'rpmautospec calculate-release' and 'rpmautospec generate-changelog'
to work, as long as all the commits that are necessary to get a result are present.
I.e. if e.g. conversion to rpmautospec happened n commits ago, and the repo is shallow
clone with at least n+1 commits, things should work. But if it's shallower than that,
it'll fail with KeyError, the same as before.
This is only a partial solution. But I don't think it makes sense to do a full
solution here. It can only be solved nicely in pygit2/libgit2, so let's do the bare
minimum to get some common cases working without complicating rpmautospec code too
much.
Fixes #227.