diff --git a/dvc/scm/git/backend/base.py b/dvc/scm/git/backend/base.py index 304e96530..02808c59e 100644 --- a/dvc/scm/git/backend/base.py +++ b/dvc/scm/git/backend/base.py @@ -67,7 +67,11 @@ class BaseGitBackend(ABC): @abstractmethod def checkout( - self, branch: str, create_new: Optional[bool] = False, **kwargs, + self, + branch: str, + create_new: Optional[bool] = False, + force: bool = False, + **kwargs, ): pass diff --git a/dvc/scm/git/backend/dulwich.py b/dvc/scm/git/backend/dulwich.py index cd3af32fd..517978bd5 100644 --- a/dvc/scm/git/backend/dulwich.py +++ b/dvc/scm/git/backend/dulwich.py @@ -189,7 +189,11 @@ class DulwichBackend(BaseGitBackend): # pylint:disable=abstract-method raise SCMError("Git commit failed") from exc def checkout( - self, branch: str, create_new: Optional[bool] = False, **kwargs, + self, + branch: str, + create_new: Optional[bool] = False, + force: bool = False, + **kwargs, ): raise NotImplementedError diff --git a/dvc/scm/git/backend/gitpython.py b/dvc/scm/git/backend/gitpython.py index 19922e5c9..f7bf23a3e 100644 --- a/dvc/scm/git/backend/gitpython.py +++ b/dvc/scm/git/backend/gitpython.py @@ -217,12 +217,16 @@ class GitPythonBackend(BaseGitBackend): # pylint:disable=abstract-method raise SCMError("Git pre-commit hook failed") from exc def checkout( - self, branch: str, create_new: Optional[bool] = False, **kwargs + self, + branch: str, + create_new: Optional[bool] = False, + force: bool = False, + **kwargs, ): if create_new: - self.repo.git.checkout("HEAD", b=branch, **kwargs) + self.repo.git.checkout("HEAD", b=branch, force=force, **kwargs) else: - self.repo.git.checkout(branch, **kwargs) + self.repo.git.checkout(branch, force=force, **kwargs) def pull(self, **kwargs): infos = self.repo.remote().pull(**kwargs) diff --git a/dvc/scm/git/backend/pygit2.py b/dvc/scm/git/backend/pygit2.py index 3aa944b15..a00c3aef8 100644 --- a/dvc/scm/git/backend/pygit2.py +++ b/dvc/scm/git/backend/pygit2.py @@ -100,14 +100,20 @@ class Pygit2Backend(BaseGitBackend): # pylint:disable=abstract-method raise NotImplementedError def checkout( - self, branch: str, create_new: Optional[bool] = False, **kwargs, + self, + branch: str, + create_new: Optional[bool] = False, + force: bool = False, + **kwargs, ): - from pygit2 import GitError + from pygit2 import GIT_CHECKOUT_FORCE, GitError + + checkout_strategy = GIT_CHECKOUT_FORCE if force else None if create_new: commit = self.repo.revparse_single("HEAD") new_branch = self.repo.branches.local.create(branch, commit) - self.repo.checkout(new_branch) + self.repo.checkout(new_branch, strategy=checkout_strategy) else: if branch == "-": branch = "@{-1}" @@ -115,7 +121,7 @@ class Pygit2Backend(BaseGitBackend): # pylint:disable=abstract-method commit, ref = self.repo.resolve_refish(branch) except (KeyError, GitError): raise RevError(f"unknown Git revision '{branch}'") - self.repo.checkout_tree(commit) + self.repo.checkout_tree(commit, strategy=checkout_strategy) detach = kwargs.get("detach", False) if ref and not detach: self.repo.set_head(ref.name)