Files
dvc/dvc/scm/git/backend/gitpython.py
T
Peter Rowlands (변기호) 1e52bc94a3 git: add support for working with refspecs and stashes (#4952)
* git: add support for dulwich Stash

* git: start adding support for custom refs

* git: add Stash and helper context managers for preserving
workspace state

* scm: cleanup refs/stash and add push_refspec

* tests: add tests for git refs and stash

* fix push_refspec

* tests: add tests for git push_refspec

* scm: add git fetch-refspecs

* add test for fetch refspecs

* scm: add test for git refs containing

* remove extraneous log msgs

* fix typing issues

* stash: only try to push if workspace is dirty

* update dulwich to 0.20.14

* scm: move dulwich into scm.backend

* move stash internals to backends

* scm: move git code into dvc/scm/git/... module

* git: use ABC for BaseGitBackend

* fix rebase issue

* git: fix refspec tests on windows

- git file:// URLs need posix style paths

* git: move get_refs_containing to backend

* scm: add callback for handling diverged refs during fetch
2020-12-01 16:56:50 +09:00

102 lines
3.0 KiB
Python

import logging
from typing import Callable, Iterable, Optional, Tuple
from .base import BaseGitBackend
logger = logging.getLogger(__name__)
class GitPythonBackend(BaseGitBackend): # pylint:disable=abstract-method
"""git-python Git backend."""
@property
def git(self):
return self.scm.repo.git
def is_ignored(self, path):
raise NotImplementedError
def set_ref(
self,
name: str,
new_ref: str,
old_ref: Optional[str] = None,
message: Optional[str] = None,
symbolic: Optional[bool] = False,
):
raise NotImplementedError
def get_ref(self, name, follow: Optional[bool] = True) -> Optional[str]:
raise NotImplementedError
def remove_ref(self, name: str, old_ref: Optional[str] = None):
raise NotImplementedError
def get_refs_containing(self, rev: str, pattern: Optional[str] = None):
from git.exc import GitCommandError
try:
if pattern:
args = [pattern]
else:
args = []
for line in self.git.for_each_ref(
*args, contains=rev, format=r"%(refname)"
).splitlines():
line = line.strip()
if line:
yield line
except GitCommandError:
pass
def push_refspec(self, url: str, src: Optional[str], dest: str):
raise NotImplementedError
def fetch_refspecs(
self,
url: str,
refspecs: Iterable[str],
force: Optional[bool] = False,
on_diverged: Optional[Callable[[bytes, bytes], bool]] = None,
):
raise NotImplementedError
def _stash_iter(self, ref: str):
raise NotImplementedError
def _stash_push(
self,
ref: str,
message: Optional[str] = None,
include_untracked: Optional[bool] = False,
) -> Tuple[Optional[str], bool]:
from dvc.scm.git import Stash
args = ["push"]
if message:
args.extend(["-m", message])
if include_untracked:
args.append("--include-untracked")
self.git.stash(*args)
commit = self.scm.resolve_commit("stash@{0}")
if ref != Stash.DEFAULT_STASH:
# `git stash` CLI doesn't support using custom refspecs,
# so we push a commit onto refs/stash, make our refspec
# point to the new commit, then pop it from refs/stash
# `git stash create` is intended to be used for this kind of
# behavior but it doesn't support --include-untracked so we need to
# use push
self.scm.set_ref(ref, commit.hexsha, message=commit.message)
self.git.stash("drop")
return commit.hexsha, False
def _stash_apply(self, rev: str):
self.git.stash("apply", rev)
def reflog_delete(self, ref: str, updateref: bool = False):
args = ["delete"]
if updateref:
args.append("--updateref")
args.append(ref)
self.git.reflog(*args)