diff options
Diffstat (limited to 'git/objects/submodule/base.py')
-rw-r--r-- | git/objects/submodule/base.py | 117 |
1 files changed, 68 insertions, 49 deletions
diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index f0b8babc..68e16b5f 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -3,6 +3,7 @@ from io import BytesIO import logging import os import stat + from unittest import SkipTest import uuid @@ -24,9 +25,9 @@ from git.exc import ( BadName ) from git.objects.base import IndexObject, Object -from git.objects.util import Traversable +from git.objects.util import TraversableIterableObj + from git.util import ( - IterableObj, join_path_native, to_native_path_linux, RemoteProgress, @@ -48,6 +49,13 @@ from .util import ( # typing ---------------------------------------------------------------------- +from typing import Dict, TYPE_CHECKING +from typing import Any, Iterator, Union + +from git.types import Commit_ish, PathLike + +if TYPE_CHECKING: + from git.repo import Repo # ----------------------------------------------------------------------------- @@ -64,7 +72,7 @@ class UpdateProgress(RemoteProgress): """Class providing detailed progress information to the caller who should derive from it and implement the ``update(...)`` message""" CLONE, FETCH, UPDWKTREE = [1 << x for x in range(RemoteProgress._num_op_codes, RemoteProgress._num_op_codes + 3)] - _num_op_codes = RemoteProgress._num_op_codes + 3 + _num_op_codes: int = RemoteProgress._num_op_codes + 3 __slots__ = () @@ -79,7 +87,7 @@ UPDWKTREE = UpdateProgress.UPDWKTREE # IndexObject comes via util module, its a 'hacky' fix thanks to pythons import # mechanism which cause plenty of trouble of the only reason for packages and # modules is refactoring - subpackages shouldn't depend on parent packages -class Submodule(IndexObject, IterableObj, Traversable): +class Submodule(IndexObject, TraversableIterableObj): """Implements access to a git submodule. They are special in that their sha represents a commit in the submodule's repository which is to be checked out @@ -101,7 +109,14 @@ class Submodule(IndexObject, IterableObj, Traversable): __slots__ = ('_parent_commit', '_url', '_branch_path', '_name', '__weakref__') _cache_attrs = ('path', '_url', '_branch_path') - def __init__(self, repo, binsha, mode=None, path=None, name=None, parent_commit=None, url=None, branch_path=None): + def __init__(self, repo: 'Repo', binsha: bytes, + mode: Union[int, None] = None, + path: Union[PathLike, None] = None, + name: Union[str, None] = None, + parent_commit: Union[Commit_ish, None] = None, + url: str = None, + branch_path: Union[PathLike, None] = None + ) -> None: """Initialize this instance with its attributes. We only document the ones that differ from ``IndexObject`` @@ -121,15 +136,16 @@ class Submodule(IndexObject, IterableObj, Traversable): if name is not None: self._name = name - def _set_cache_(self, attr): + def _set_cache_(self, attr: str) -> None: if attr in ('path', '_url', '_branch_path'): reader = self.config_reader() # default submodule values try: self.path = reader.get('path') except cp.NoSectionError as e: - raise ValueError("This submodule instance does not exist anymore in '%s' file" - % osp.join(self.repo.working_tree_dir, '.gitmodules')) from e + if self.repo.working_tree_dir is not None: + raise ValueError("This submodule instance does not exist anymore in '%s' file" + % osp.join(self.repo.working_tree_dir, '.gitmodules')) from e # end self._url = reader.get('url') # git-python extension values - optional @@ -150,33 +166,35 @@ class Submodule(IndexObject, IterableObj, Traversable): # END handle intermediate items @classmethod - def _need_gitfile_submodules(cls, git): + def _need_gitfile_submodules(cls, git: Git) -> bool: return git.version_info[:3] >= (1, 7, 5) - def __eq__(self, other): + def __eq__(self, other: Any) -> bool: """Compare with another submodule""" # we may only compare by name as this should be the ID they are hashed with # Otherwise this type wouldn't be hashable # return self.path == other.path and self.url == other.url and super(Submodule, self).__eq__(other) return self._name == other._name - def __ne__(self, other): + def __ne__(self, other: object) -> bool: """Compare with another submodule for inequality""" return not (self == other) - def __hash__(self): + def __hash__(self) -> int: """Hash this instance using its logical id, not the sha""" return hash(self._name) - def __str__(self): + def __str__(self) -> str: return self._name - def __repr__(self): + def __repr__(self) -> str: return "git.%s(name=%s, path=%s, url=%s, branch_path=%s)"\ % (type(self).__name__, self._name, self.path, self.url, self.branch_path) @classmethod - def _config_parser(cls, repo, parent_commit, read_only): + def _config_parser(cls, repo: 'Repo', + parent_commit: Union[Commit_ish, None], + read_only: bool) -> SubmoduleConfigParser: """:return: Config Parser constrained to our submodule in read or write mode :raise IOError: If the .gitmodules file cannot be found, either locally or in the repository at the given parent commit. Otherwise the exception would be delayed until the first @@ -189,8 +207,8 @@ class Submodule(IndexObject, IterableObj, Traversable): # We are most likely in an empty repository, so the HEAD doesn't point to a valid ref pass # end handle parent_commit - - if not repo.bare and parent_matches_head: + fp_module: Union[str, BytesIO] + if not repo.bare and parent_matches_head and repo.working_tree_dir: fp_module = osp.join(repo.working_tree_dir, cls.k_modules_file) else: assert parent_commit is not None, "need valid parent_commit in bare repositories" @@ -219,13 +237,13 @@ class Submodule(IndexObject, IterableObj, Traversable): # END for each name to delete @classmethod - def _sio_modules(cls, parent_commit): + def _sio_modules(cls, parent_commit: Commit_ish) -> BytesIO: """:return: Configuration file as BytesIO - we only access it through the respective blob's data""" sio = BytesIO(parent_commit.tree[cls.k_modules_file].data_stream.read()) sio.name = cls.k_modules_file return sio - def _config_parser_constrained(self, read_only): + def _config_parser_constrained(self, read_only: bool) -> SectionConstraint: """:return: Config Parser constrained to our submodule in read or write mode""" try: pc = self.parent_commit @@ -248,7 +266,7 @@ class Submodule(IndexObject, IterableObj, Traversable): """:return: Repo instance of newly cloned repository :param repo: our parent repository :param url: url to clone from - :param path: repository-relative path to the submodule checkout location + :param path: repository - relative path to the submodule checkout location :param name: canonical of the submodule :param kwrags: additinoal arguments given to git.clone""" module_abspath = cls._module_abspath(repo, path, name) @@ -269,7 +287,7 @@ class Submodule(IndexObject, IterableObj, Traversable): @classmethod def _to_relative_path(cls, parent_repo, path): - """:return: a path guaranteed to be relative to the given parent-repository + """:return: a path guaranteed to be relative to the given parent - repository :raise ValueError: if path is not contained in the parent repository's working tree""" path = to_native_path_linux(path) if path.endswith('/'): @@ -291,11 +309,11 @@ class Submodule(IndexObject, IterableObj, Traversable): @classmethod def _write_git_file_and_module_config(cls, working_tree_dir, module_abspath): - """Writes a .git file containing a (preferably) relative path to the actual git module repository. + """Writes a .git file containing a(preferably) relative path to the actual git module repository. It is an error if the module_abspath cannot be made into a relative path, relative to the working_tree_dir :note: will overwrite existing files ! :note: as we rewrite both the git file as well as the module configuration, we might fail on the configuration - and will not roll back changes done to the git file. This should be a non-issue, but may easily be fixed + and will not roll back changes done to the git file. This should be a non - issue, but may easily be fixed if it becomes one :param working_tree_dir: directory to write the .git file into :param module_abspath: absolute path to the bare repository @@ -316,8 +334,9 @@ class Submodule(IndexObject, IterableObj, Traversable): #{ Edit Interface @classmethod - def add(cls, repo, name, path, url=None, branch=None, no_checkout=False, depth=None, env=None, - clone_multi_options=None): + def add(cls, repo: 'Repo', name: str, path: PathLike, url: Union[str, None] = None, + branch=None, no_checkout: bool = False, depth=None, env=None, clone_multi_options=None + ) -> 'Submodule': """Add a new submodule to the given repository. This will alter the index as well as the .gitmodules file, but will not create a new commit. If the submodule already exists, no matter if the configuration differs @@ -363,8 +382,8 @@ class Submodule(IndexObject, IterableObj, Traversable): # assure we never put backslashes into the url, as some operating systems # like it ... - if url is not None: - url = to_native_path_linux(url) + # if url is not None: + # url = to_native_path_linux(url) to_native_path_linux does nothing?? # END assure url correctness # INSTANTIATE INTERMEDIATE SM @@ -408,7 +427,7 @@ class Submodule(IndexObject, IterableObj, Traversable): url = urls[0] else: # clone new repo - kwargs = {'n': no_checkout} + kwargs: Dict[str, Union[bool, int]] = {'n': no_checkout} if not branch_is_default: kwargs['b'] = br.name # END setup checkout-branch @@ -468,7 +487,7 @@ class Submodule(IndexObject, IterableObj, Traversable): was specified for this submodule and the branch existed remotely :param progress: UpdateProgress instance or None if no progress should be shown :param dry_run: if True, the operation will only be simulated, but not performed. - All performed operations are read-only + All performed operations are read - only :param force: If True, we may reset heads even if the repository in question is dirty. Additinoally we will be allowed to set a tracking branch which is ahead of its remote branch back into the past or the location of the @@ -476,7 +495,7 @@ class Submodule(IndexObject, IterableObj, Traversable): If False, local tracking branches that are in the future of their respective remote branches will simply not be moved. :param keep_going: if True, we will ignore but log all errors, and keep going recursively. - Unless dry_run is set as well, keep_going could cause subsequent/inherited errors you wouldn't see + Unless dry_run is set as well, keep_going could cause subsequent / inherited errors you wouldn't see otherwise. In conjunction with dry_run, it can be useful to anticipate all errors when updating submodules :param env: Optional dictionary containing the desired environment variables. @@ -685,9 +704,9 @@ class Submodule(IndexObject, IterableObj, Traversable): adjusting our index entry accordingly. :param module_path: the path to which to move our module in the parent repostory's working tree, - given as repository-relative or absolute path. Intermediate directories will be created + given as repository - relative or absolute path. Intermediate directories will be created accordingly. If the path already exists, it must be empty. - Trailing (back)slashes are removed automatically + Trailing(back)slashes are removed automatically :param configuration: if True, the configuration will be adjusted to let the submodule point to the given path. :param module: if True, the repository managed by this submodule @@ -696,7 +715,7 @@ class Submodule(IndexObject, IterableObj, Traversable): :return: self :raise ValueError: if the module path existed and was not empty, or was a file :note: Currently the method is not atomic, and it could leave the repository - in an inconsistent state if a sub-step fails for some reason + in an inconsistent state if a sub - step fails for some reason """ if module + configuration < 1: raise ValueError("You must specify to move at least the module or the configuration of the submodule") @@ -790,19 +809,19 @@ class Submodule(IndexObject, IterableObj, Traversable): @unbare_repo def remove(self, module=True, force=False, configuration=True, dry_run=False): """Remove this submodule from the repository. This will remove our entry - from the .gitmodules file and the entry in the .git/config file. + from the .gitmodules file and the entry in the .git / config file. :param module: If True, the module checkout we point to will be deleted as well. If the module is currently on a commit which is not part of any branch in the remote, if the currently checked out branch working tree, or untracked files, - is ahead of its tracking branch, if you have modifications in the + is ahead of its tracking branch, if you have modifications in the In case the removal of the repository fails for these reasons, the submodule status will not have been altered. - If this submodule has child-modules on its own, these will be deleted + If this submodule has child - modules on its own, these will be deleted prior to touching the own module. :param force: Enforces the deletion of the module even though it contains - modifications. This basically enforces a brute-force file system based + modifications. This basically enforces a brute - force file system based deletion. :param configuration: if True, the submodule is deleted from the configuration, otherwise it isn't. Although this should be enabled most of the times, @@ -942,7 +961,7 @@ class Submodule(IndexObject, IterableObj, Traversable): return self - def set_parent_commit(self, commit, check=True): + def set_parent_commit(self, commit: Union[Commit_ish, None], check=True): """Set this instance to use the given commit whose tree is supposed to contain the .gitmodules blob. @@ -1015,9 +1034,9 @@ class Submodule(IndexObject, IterableObj, Traversable): """Rename this submodule :note: This method takes care of renaming the submodule in various places, such as - * $parent_git_dir/config - * $working_tree_dir/.gitmodules - * (git >=v1.8.0: move submodule repository to new name) + * $parent_git_dir / config + * $working_tree_dir / .gitmodules + * (git >= v1.8.0: move submodule repository to new name) As .gitmodules will be changed, you would need to make a commit afterwards. The changed .gitmodules file will already be added to the index @@ -1091,7 +1110,7 @@ class Submodule(IndexObject, IterableObj, Traversable): def exists(self): """ :return: True if the submodule exists, False otherwise. Please note that - a submodule may exist (in the .gitmodules file) even though its module + a submodule may exist ( in the .gitmodules file) even though its module doesn't exist on disk""" # keep attributes for later, and restore them if we have no valid data # this way we do not actually alter the state of the object @@ -1131,7 +1150,7 @@ class Submodule(IndexObject, IterableObj, Traversable): @property def branch_path(self): """ - :return: full (relative) path as string to the branch we would checkout + :return: full(relative) path as string to the branch we would checkout from the remote and track""" return self._branch_path @@ -1144,7 +1163,7 @@ class Submodule(IndexObject, IterableObj, Traversable): @property def url(self): - """:return: The url to the repository which our module-repository refers to""" + """:return: The url to the repository which our module - repository refers to""" return self._url @property @@ -1160,7 +1179,7 @@ class Submodule(IndexObject, IterableObj, Traversable): """:return: The name of this submodule. It is used to identify it within the .gitmodules file. :note: by default, the name is the path at which to find the submodule, but - in git-python it should be a unique identifier similar to the identifiers + in git - python it should be a unique identifier similar to the identifiers used for remotes, which allows to change the path of the submodule easily """ @@ -1187,17 +1206,16 @@ class Submodule(IndexObject, IterableObj, Traversable): #{ Iterable Interface @classmethod - def iter_items(cls, repo, parent_commit='HEAD'): + def iter_items(cls, repo: 'Repo', parent_commit: Union[Commit_ish, str] = 'HEAD', *Args: Any, **kwargs: Any + ) -> Iterator['Submodule']: """:return: iterator yielding Submodule instances available in the given repository""" try: pc = repo.commit(parent_commit) # parent commit instance parser = cls._config_parser(repo, pc, read_only=True) except (IOError, BadName): - return + return iter([]) # END handle empty iterator - rt = pc.tree # root tree - for sms in parser.sections(): n = sm_name(sms) p = parser.get(sms, 'path') @@ -1210,6 +1228,7 @@ class Submodule(IndexObject, IterableObj, Traversable): # get the binsha index = repo.index try: + rt = pc.tree # root tree sm = rt[p] except KeyError: # try the index, maybe it was just added |