Skip to content

Commit

Permalink
repo defer importing git until required
Browse files Browse the repository at this point in the history
Many use use cases for augpathlib do not involve RepoPath so we defer
importing git until the first time a RepoPath is instantiated. I'm sure
that there is some tradeoff that will come back to haunt me by doing
this, but forcing anyone who wants to use a library to pay the worst
case time cost for the maximal use case seems at least as bad.
  • Loading branch information
tgbugs committed Jun 27, 2020
1 parent ac1d3d4 commit 321fc8f
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 67 deletions.
88 changes: 21 additions & 67 deletions augpathlib/repo.py
Original file line number Diff line number Diff line change
@@ -1,79 +1,33 @@
import pathlib
from urllib.parse import urlparse
import git
from git import Repo
from augpathlib import AugmentedPath
from augpathlib import exceptions as exc
from augpathlib.utils import log as _log

log = _log.getChild('repo')


class _Repo(git.Repo): # FIXME should we subclass Repo for this or patch ??
""" monkey patching """

def getRef(self, ref_name):
for ref in self.refs:
if ref.name == ref_name:
return ref

else:
raise ValueError(f'No ref with name: {ref_name}')


# monkey patch git.Repo
git.Repo.getRef = _Repo.getRef


class _Reference(git.Reference):
""" monkey patching """
def __enter__(self):
""" Checkout the ref for this head.
`git stash --all` beforehand and restore during __exit__.
If the ref is the same, then the stash step still happens.
If you need to modify the uncommitted state of a repo this
is not the tool you should use. """

if not self.is_valid():
raise exc.InvalidRefError(f'Not a valid ref: {self.name}')

self.__original_branch = self.repo.active_branch
self.__stash = self.repo.git.stash('--all') # always stash
if self.__stash == 'No local changes to save':
self.__stash = None

if self == self.__original_branch:
return self

self.checkout()
return self

def __exit__(self, exc_type, exc_value, traceback):
_stash = self.repo.git.stash('--all') # always stash on the way out as well
if _stash == 'No local changes to save':
stash = 'stash@{0}'
else:
stash = "stash@{1}"

if self.__original_branch != self:
self.__original_branch.checkout()

# TODO check to make sure no other stashes were pushed on top
if self.__stash is not None:
self.repo.git.stash('pop', stash)

self.__stash = None
class RepoHelper:
_repo_class = None # set in _setup
_repos = {} # repo cache

def __new__(cls, *args, **kwargs):
return super().__new__(cls, *args, **kwargs)

# monkey patch git.Reference
git.Reference.__enter__ = _Reference.__enter__
git.Reference.__exit__ = _Reference.__exit__
_renew = __new__

def __new__(cls, *args, **kwargs):
RepoHelper._setup(*args, **kwargs)
RepoHelper.__new__ = RepoHelper._renew
return super().__new__(cls, *args, **kwargs)

class RepoHelper:
_repo_class = Repo
_repos = {} # repo cache
@staticmethod
def _setup(*args, **kwargs):
import git
from git import Repo
from . import repo_patch
RepoHelper._git = git
RepoHelper._repo_class = Repo

def clone_path(self, remote):
""" get the path to which a repo would clone
Expand Down Expand Up @@ -123,7 +77,7 @@ def init(self, remote=None, depth=None):

if repo is not None:
if not self.exists():
msg = 'how!? {self!r} != {repo.working_dir}'
msg = f'how!? {self!r} != {repo.working_dir}'
assert repo.working_dir == self.as_posix(), msg
log.warning(f'stale cache on deleted repo {self!r}')
self._repos.pop(self)
Expand Down Expand Up @@ -190,7 +144,7 @@ def repo_relative_path(self):
path_relative_repo = repo_relative_path

def _remote_uri(self, prefix, infix=None, ref=None):
if isinstance(ref, git.Commit):
if isinstance(ref, self._git.Commit):
ref = str(ref)

repo = self.repo
Expand Down Expand Up @@ -340,7 +294,7 @@ def commit(self, message, *, date=None):
# FIXME concurrent modification?!
try:
self.repo.git.checkout('stash@{0}', '--', self.as_posix())
except git.exc.GitCommandError:
except self._git.exc.GitCommandError:
pass

commit = self.repo.index.commit(message=message,
Expand All @@ -349,7 +303,7 @@ def commit(self, message, *, date=None):
finally:
try:
self.repo.git.stash('pop')
except git.exc.GitCommandError:
except self._git.exc.GitCommandError:
pass
else:
try:
Expand Down
63 changes: 63 additions & 0 deletions augpathlib/repo_patch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import git


class _Repo(git.Repo): # FIXME should we subclass Repo for this or patch ??
""" monkey patching """

def getRef(self, ref_name):
for ref in self.refs:
if ref.name == ref_name:
return ref

else:
raise ValueError(f'No ref with name: {ref_name}')


# monkey patch git.Repo
git.Repo.getRef = _Repo.getRef


class _Reference(git.Reference):
""" monkey patching """
def __enter__(self):
""" Checkout the ref for this head.
`git stash --all` beforehand and restore during __exit__.
If the ref is the same, then the stash step still happens.
If you need to modify the uncommitted state of a repo this
is not the tool you should use. """

if not self.is_valid():
raise exc.InvalidRefError(f'Not a valid ref: {self.name}')

self.__original_branch = self.repo.active_branch
self.__stash = self.repo.git.stash('--all') # always stash
if self.__stash == 'No local changes to save':
self.__stash = None

if self == self.__original_branch:
return self

self.checkout()
return self

def __exit__(self, exc_type, exc_value, traceback):
_stash = self.repo.git.stash('--all') # always stash on the way out as well
if _stash == 'No local changes to save':
stash = 'stash@{0}'
else:
stash = "stash@{1}"

if self.__original_branch != self:
self.__original_branch.checkout()

# TODO check to make sure no other stashes were pushed on top
if self.__stash is not None:
self.repo.git.stash('pop', stash)

self.__stash = None


# monkey patch git.Reference
git.Reference.__enter__ = _Reference.__enter__
git.Reference.__exit__ = _Reference.__exit__

0 comments on commit 321fc8f

Please sign in to comment.