diff options
author | Tim Harder <radhermit@gmail.com> | 2021-03-10 20:07:58 -0700 |
---|---|---|
committer | Tim Harder <radhermit@gmail.com> | 2021-03-10 20:07:58 -0700 |
commit | 418c664943e8fc2cf297f242a5440bd66b4a8774 (patch) | |
tree | d94382cbdab684e9e82c4953078f8f6f82b3dfb7 /src/snakeoil/contexts.py | |
parent | mappings: OrderedFrozenSet: support slice notation (diff) | |
download | snakeoil-418c664943e8fc2cf297f242a5440bd66b4a8774.tar.gz snakeoil-418c664943e8fc2cf297f242a5440bd66b4a8774.tar.bz2 snakeoil-418c664943e8fc2cf297f242a5440bd66b4a8774.zip |
contexts: add GitStash context manager
Diffstat (limited to 'src/snakeoil/contexts.py')
-rw-r--r-- | src/snakeoil/contexts.py | 58 |
1 files changed, 57 insertions, 1 deletions
diff --git a/src/snakeoil/contexts.py b/src/snakeoil/contexts.py index 66d3397f..d6123171 100644 --- a/src/snakeoil/contexts.py +++ b/src/snakeoil/contexts.py @@ -1,6 +1,6 @@ """Various with-statement context utilities.""" -from contextlib import contextmanager +from contextlib import AbstractContextManager, contextmanager from importlib import import_module from multiprocessing.connection import Pipe import errno @@ -8,11 +8,14 @@ import inspect import os import pickle import signal +import subprocess import sys import threading import traceback +from .cli.exceptions import UserException from .process import namespaces +from .sequences import predicate_split # Ideas and code for SplitExec have been borrowed from withhacks @@ -272,6 +275,59 @@ class Namespace(SplitExec): namespaces.simple_unshare(hostname=self._hostname, **self._namespaces) +class GitStash(AbstractContextManager): + """Context manager for stashing untracked or modified/uncommitted files.""" + + def __init__(self, path, staged=False): + self.path = path + self._staged = ['--keep-index'] if staged else [] + self._stashed = False + + def __enter__(self): + """Stash all untracked or modified files in working tree.""" + # check for untracked or modified/uncommitted files + try: + p = subprocess.run( + ['git', 'status', '--porcelain=1', '-u'], + stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, + cwd=self.path, encoding='utf8', check=True) + except subprocess.CalledProcessError: + raise ValueError(f'not a git repo: {self.path}') + + # split file changes into unstaged vs staged + unstaged, staged = predicate_split(lambda x: x[1] == ' ', p.stdout.splitlines()) + + # don't stash when no relevant changes exist + if self._staged: + if not unstaged: + return + elif not p.stdout: + return + + # stash all existing untracked or modified/uncommitted files + try: + subprocess.run( + ['git', 'stash', 'push', '-u', '-m', 'pkgcheck scan --commits'] + self._staged, + stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, + cwd=self.path, check=True, encoding='utf8') + except subprocess.CalledProcessError as e: + error = e.stderr.splitlines()[0] + raise UserException(f'git failed stashing files: {error}') + self._stashed = True + + def __exit__(self, _exc_type, _exc_value, _traceback): + """Apply any previously stashed files back to the working tree.""" + if self._stashed: + try: + subprocess.run( + ['git', 'stash', 'pop'], + stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, + cwd=self.path, check=True, encoding='utf8') + except subprocess.CalledProcessError as e: + error = e.stderr.splitlines()[0] + raise UserException(f'git failed applying stash: {error}') + + @contextmanager def chdir(path): """Context manager that changes the current working directory. |