import pygit2
import re
import subprocess
from git_deps.errors import InvalidCommitish
from git_deps.utils import abort
class GitUtils(object):
@classmethod
def abbreviate_sha1(cls, sha1):
"""Uniquely abbreviates the given SHA1."""
# For now we invoke git-rev-parse(1), but hopefully eventually
# we will be able to do this via pygit2.
cmd = ['git', 'rev-parse', '--short', sha1]
# cls.logger.debug(" ".join(cmd))
out = subprocess.check_output(cmd).strip()
# cls.logger.debug(out)
return out
@classmethod
def describe(cls, sha1):
"""Returns a human-readable representation of the given SHA1."""
# For now we invoke git-describe(1), but eventually we will be
# able to do this via pygit2, since libgit2 already provides
# an API for this:
# https://github.com/libgit2/pygit2/pull/459#issuecomment-68866929
# https://github.com/libgit2/libgit2/pull/2592
cmd = [
'git', 'describe',
'--all', # look for tags and branches
'--long', # remotes/github/master-0-g2b6d591
# '--contains',
# '--abbrev',
sha1
]
# cls.logger.debug(" ".join(cmd))
out = None
try:
out = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
if e.output.find('No tags can describe') != -1:
return ''
raise
out = out.strip()
out = re.sub(r'^(heads|tags|remotes)/', '', out)
# We already have the abbreviated SHA1 from abbreviate_sha1()
out = re.sub(r'-g[0-9a-f]{7,}$', '', out)
# cls.logger.debug(out)
return out
@classmethod
def oneline(cls, commit):
return commit.message.split('\n', 1)[0]
@classmethod
def commit_summary(cls, commit):
return "%s %s" % (commit.hex[:8], cls.oneline(commit))
@classmethod
def refs_to(cls, sha1, repo):
"""Returns all refs pointing to the given SHA1."""
matching = []
for refname in repo.listall_references():
symref = repo.lookup_reference(refname)
dref = symref.resolve()
oid = dref.target
commit = repo.get(oid)
if commit.hex == sha1:
matching.append(symref.shorthand)
return matching
@classmethod
def rev_list(cls, rev_range):
cmd = ['git', 'rev-list', rev_range]
return subprocess.check_output(cmd).strip().split('\n')
@classmethod
def ref_commit(cls, repo, rev):
try:
commit = repo.revparse_single(rev)
except (KeyError, ValueError):
raise InvalidCommitish(rev)
if isinstance(commit, pygit2.Tag):
commit = commit.get_object()
return commit
@classmethod
def get_repo(cls, path='.'):
try:
repo_path = pygit2.discover_repository(path)
except KeyError:
abort("Couldn't find a repository in the current directory.")
return pygit2.Repository(repo_path)