Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 35 additions & 5 deletions src/scmrepo/git/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
import logging
import os
import re
import typing
from collections import OrderedDict
from collections.abc import Mapping
from contextlib import contextmanager
from functools import partialmethod
from typing import Dict, Iterable, Optional, Tuple, Type, Union
from typing import TYPE_CHECKING, Callable, Dict, Iterable, Optional, Tuple, Type, Union

from funcy import cached_property, first
from pathspec.patterns import GitWildMatchPattern
Expand All @@ -16,12 +17,15 @@
from scmrepo.exceptions import FileNotInRepoError, GitHookAlreadyExists, RevError
from scmrepo.utils import relpath

from .backend.base import BaseGitBackend, NoGitBackendError
from .backend.base import BaseGitBackend, NoGitBackendError, SyncStatus
from .backend.dulwich import DulwichBackend
from .backend.gitpython import GitPythonBackend
from .backend.pygit2 import Pygit2Backend
from .stash import Stash

if TYPE_CHECKING:
from scmrepo.progress import GitProgressEvent

logger = logging.getLogger(__name__)

BackendCls = Type[BaseGitBackend]
Expand Down Expand Up @@ -310,6 +314,35 @@ def add_commit(
self.add(paths)
self.commit(msg=message)

_fetch_refspecs = partialmethod(
_backend_func, "fetch_refspecs", backends=["pygit2", "dulwich"]
)

def fetch_refspecs(
self,
url: str,
refspecs: Union[str, Iterable[str]],
force: bool = False,
on_diverged: Optional[Callable[[str, str], bool]] = None,
progress: Optional[Callable[["GitProgressEvent"], None]] = None,
**kwargs,
) -> typing.Mapping[str, SyncStatus]:
from .credentials import get_matching_helper_commands

if "dulwich" in kwargs.get("backends", self.backends.backends) and any(
get_matching_helper_commands(url, self.dulwich.repo.get_config_stack())
):
kwargs["backends"] = ["dulwich"]

return self._fetch_refspecs(
url,
refspecs,
force=force,
on_diverged=on_diverged,
progress=progress,
**kwargs,
)

is_ignored = partialmethod(_backend_func, "is_ignored")
add = partialmethod(_backend_func, "add")
commit = partialmethod(_backend_func, "commit")
Expand Down Expand Up @@ -337,9 +370,6 @@ def add_commit(
iter_remote_refs = partialmethod(_backend_func, "iter_remote_refs")
get_refs_containing = partialmethod(_backend_func, "get_refs_containing")
push_refspecs = partialmethod(_backend_func, "push_refspecs")
fetch_refspecs = partialmethod(
_backend_func, "fetch_refspecs", backends=["pygit2", "dulwich"]
)
_stash_iter = partialmethod(_backend_func, "_stash_iter")
_stash_push = partialmethod(_backend_func, "_stash_push")
_stash_apply = partialmethod(_backend_func, "_stash_apply")
Expand Down
2 changes: 1 addition & 1 deletion src/scmrepo/git/backend/dulwich/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from dulwich.client import Urllib3HttpGitClient
from dulwich.config import StackedConfig

from .credentials import CredentialNotFoundError, get_credentials_from_helper
from scmrepo.git.credentials import CredentialNotFoundError, get_credentials_from_helper


class GitCredentialsHTTPClient(Urllib3HttpGitClient): # pylint: disable=abstract-method
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,7 @@ def erase(self, *args, **kwargs):
raise NotImplementedError


def get_credentials_from_helper(base_url: str, config) -> Tuple[bytes, bytes]:
"""Retrieves credentials for the given url from git credential helpers"""
def get_matching_helper_commands(base_url: str, config):
if isinstance(config, StackedConfig):
backends = config.backends
else:
Expand All @@ -175,19 +174,22 @@ def get_credentials_from_helper(base_url: str, config) -> Tuple[bytes, bytes]:
except KeyError:
# no helper configured
continue
yield command.decode(conf.encoding or sys.getdefaultencoding())

helper = CredentialHelper(
command.decode(conf.encoding or sys.getdefaultencoding())
)
parsed = urlparse(base_url)
try:
return helper.get(
protocol=parsed.scheme,
hostname=parsed.hostname,
port=parsed.port,
username=parsed.username,
)
except CredentialNotFoundError:
continue

def get_credentials_from_helper(base_url: str, config) -> Tuple[bytes, bytes]:
"""Retrieves credentials for the given url from git credential helpers"""

for command in get_matching_helper_commands(base_url, config):
helper = CredentialHelper(command)
parsed = urlparse(base_url)
try:
return helper.get(
protocol=parsed.scheme,
hostname=parsed.hostname,
port=parsed.port,
username=parsed.username,
)
except CredentialNotFoundError:
continue
raise CredentialNotFoundError