diff --git a/git/config.py b/git/config.py index c4b26ba63..ad02b4373 100644 --- a/git/config.py +++ b/git/config.py @@ -41,12 +41,13 @@ T_ConfigParser = TypeVar('T_ConfigParser', bound='GitConfigParser') -if sys.version_info[:2] < (3, 7): - from collections import OrderedDict - OrderedDict_OMD = OrderedDict +if sys.version_info[:3] < (3, 7, 2): + # typing.Ordereddict not added until py 3.7.2 + from collections import OrderedDict # type: ignore # until 3.6 dropped + OrderedDict_OMD = OrderedDict # type: ignore # until 3.6 dropped else: - from typing import OrderedDict - OrderedDict_OMD = OrderedDict[str, List[_T]] + from typing import OrderedDict # type: ignore # until 3.6 dropped + OrderedDict_OMD = OrderedDict[str, List[_T]] # type: ignore[assignment, misc] # ------------------------------------------------------------- diff --git a/git/objects/util.py b/git/objects/util.py index ef1ae77ba..db7807c26 100644 --- a/git/objects/util.py +++ b/git/objects/util.py @@ -493,6 +493,11 @@ def list_traverse(self: T_TIobj, *args: Any, **kwargs: Any) -> IterableList[T_TI return super(TraversableIterableObj, self)._list_traverse(* args, **kwargs) @ overload # type: ignore + def traverse(self: T_TIobj + ) -> Iterator[T_TIobj]: + ... + + @ overload def traverse(self: T_TIobj, predicate: Callable[[Union[T_TIobj, Tuple[Union[T_TIobj, None], T_TIobj]], int], bool], prune: Callable[[Union[T_TIobj, Tuple[Union[T_TIobj, None], T_TIobj]], int], bool], diff --git a/git/refs/head.py b/git/refs/head.py index 338efce9f..260bf5e7e 100644 --- a/git/refs/head.py +++ b/git/refs/head.py @@ -1,4 +1,4 @@ -from git.config import SectionConstraint +from git.config import GitConfigParser, SectionConstraint from git.util import join_path from git.exc import GitCommandError @@ -142,7 +142,7 @@ def delete(cls, repo: 'Repo', *heads: 'Head', **kwargs: Any): flag = "-D" repo.git.branch(flag, *heads) - def set_tracking_branch(self, remote_reference: 'RemoteReference') -> 'Head': + def set_tracking_branch(self, remote_reference: Union['RemoteReference', None]) -> 'Head': """ Configure this branch to track the given remote reference. This will alter this branch's configuration accordingly. @@ -203,7 +203,7 @@ def rename(self, new_path: PathLike, force: bool = False) -> 'Head': self.path = "%s/%s" % (self._common_path_default, new_path) return self - def checkout(self, force: bool = False, **kwargs: Any): + def checkout(self, force: bool = False, **kwargs: Any) -> Union['HEAD', 'Head']: """Checkout this head by setting the HEAD to this reference, by updating the index to reflect the tree we point to and by updating the working tree to reflect the latest index. @@ -235,10 +235,11 @@ def checkout(self, force: bool = False, **kwargs: Any): self.repo.git.checkout(self, **kwargs) if self.repo.head.is_detached: return self.repo.head - return self.repo.active_branch + else: + return self.repo.active_branch #{ Configuration - def _config_parser(self, read_only: bool) -> SectionConstraint: + def _config_parser(self, read_only: bool) -> SectionConstraint[GitConfigParser]: if read_only: parser = self.repo.config_reader() else: @@ -247,13 +248,13 @@ def _config_parser(self, read_only: bool) -> SectionConstraint: return SectionConstraint(parser, 'branch "%s"' % self.name) - def config_reader(self) -> SectionConstraint: + def config_reader(self) -> SectionConstraint[GitConfigParser]: """ :return: A configuration parser instance constrained to only read this instance's values""" return self._config_parser(read_only=True) - def config_writer(self) -> SectionConstraint: + def config_writer(self) -> SectionConstraint[GitConfigParser]: """ :return: A configuration writer instance with read-and write access to options of this head""" diff --git a/git/refs/reference.py b/git/refs/reference.py index 646622816..bc2c6e807 100644 --- a/git/refs/reference.py +++ b/git/refs/reference.py @@ -62,7 +62,9 @@ def __str__(self) -> str: #{ Interface - def set_object(self, object: Commit_ish, logmsg: Union[str, None] = None) -> 'Reference': # @ReservedAssignment + # @ReservedAssignment + def set_object(self, object: Union[Commit_ish, 'SymbolicReference'], logmsg: Union[str, None] = None + ) -> 'SymbolicReference': """Special version which checks if the head-log needs an update as well :return: self""" oldbinsha = None diff --git a/git/refs/tag.py b/git/refs/tag.py index 281ce09ad..edfab33d8 100644 --- a/git/refs/tag.py +++ b/git/refs/tag.py @@ -4,13 +4,14 @@ # typing ------------------------------------------------------------------ -from typing import Any, Union, TYPE_CHECKING +from typing import Any, Type, Union, TYPE_CHECKING from git.types import Commit_ish, PathLike if TYPE_CHECKING: from git.repo import Repo from git.objects import Commit from git.objects import TagObject + from git.refs import SymbolicReference # ------------------------------------------------------------------------------ @@ -68,7 +69,8 @@ def object(self) -> Commit_ish: # type: ignore[override] return Reference._get_object(self) @classmethod - def create(cls, repo: 'Repo', path: PathLike, reference: Union[Commit_ish, str] = 'HEAD', + def create(cls: Type['TagReference'], repo: 'Repo', path: PathLike, + reference: Union[str, 'SymbolicReference'] = 'HEAD', logmsg: Union[str, None] = None, force: bool = False, **kwargs: Any) -> 'TagReference': """Create a new tag reference. @@ -78,7 +80,7 @@ def create(cls, repo: 'Repo', path: PathLike, reference: Union[Commit_ish, str] The prefix refs/tags is implied :param ref: - A reference to the object you want to tag. It can be a commit, tree or + A reference to the Object you want to tag. The Object can be a commit, tree or blob. :param logmsg: @@ -98,7 +100,9 @@ def create(cls, repo: 'Repo', path: PathLike, reference: Union[Commit_ish, str] Additional keyword arguments to be passed to git-tag :return: A new TagReference""" - args = (path, reference) + if 'ref' in kwargs and kwargs['ref']: + reference = kwargs['ref'] + if logmsg: kwargs['m'] = logmsg elif 'message' in kwargs and kwargs['message']: @@ -107,11 +111,13 @@ def create(cls, repo: 'Repo', path: PathLike, reference: Union[Commit_ish, str] if force: kwargs['f'] = True + args = (path, reference) + repo.git.tag(*args, **kwargs) return TagReference(repo, "%s/%s" % (cls._common_path_default, path)) @classmethod - def delete(cls, repo: 'Repo', *tags: 'TagReference') -> None: + def delete(cls, repo: 'Repo', *tags: 'TagReference') -> None: # type: ignore[override] """Delete the given existing tag or tags""" repo.git.tag("-d", *tags) diff --git a/git/repo/base.py b/git/repo/base.py index bb8ddf135..355f93999 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -422,14 +422,14 @@ def _to_full_tag_path(path): def create_head(self, path: PathLike, commit: str = 'HEAD', force: bool = False, logmsg: Optional[str] = None - ) -> Head: + ) -> 'Head': """Create a new head within the repository. For more documentation, please see the Head.create method. :return: newly created Head Reference""" return Head.create(self, path, commit, logmsg, force) - def delete_head(self, *heads: 'SymbolicReference', **kwargs: Any) -> None: + def delete_head(self, *heads: 'Head', **kwargs: Any) -> None: """Delete the given heads :param kwargs: Additional keyword arguments to be passed to git-branch""" @@ -788,10 +788,10 @@ def ignored(self, *paths: PathLike) -> List[PathLike]: return proc.replace("\\\\", "\\").replace('"', "").split("\n") @property - def active_branch(self) -> 'SymbolicReference': + def active_branch(self) -> Head: """The name of the currently active branch. - :return: Head to the active branch""" + # reveal_type(self.head.reference) # => Reference return self.head.reference def blame_incremental(self, rev: TBD, file: TBD, **kwargs: Any) -> Optional[Iterator['BlameEntry']]: diff --git a/test/test_refs.py b/test/test_refs.py index 1315f885f..ab760a6f5 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -125,11 +125,15 @@ def test_heads(self, rwrepo): gp_tracking_branch = rwrepo.create_head('gp_tracking#123') special_name_remote_ref = rwrepo.remotes[0].refs[special_name] # get correct type gp_tracking_branch.set_tracking_branch(special_name_remote_ref) - assert gp_tracking_branch.tracking_branch().path == special_name_remote_ref.path + TBranch = gp_tracking_branch.tracking_branch() + if TBranch is not None: + assert TBranch.path == special_name_remote_ref.path git_tracking_branch = rwrepo.create_head('git_tracking#123') rwrepo.git.branch('-u', special_name_remote_ref.name, git_tracking_branch.name) - assert git_tracking_branch.tracking_branch().name == special_name_remote_ref.name + TBranch = gp_tracking_branch.tracking_branch() + if TBranch is not None: + assert TBranch.name == special_name_remote_ref.name # END for each head # verify REFLOG gets altered @@ -453,7 +457,7 @@ def test_head_reset(self, rw_repo): self.assertRaises(OSError, SymbolicReference.create, rw_repo, symref_path, cur_head.reference.commit) # it works if the new ref points to the same reference - SymbolicReference.create(rw_repo, symref.path, symref.reference).path == symref.path # @NoEffect + assert SymbolicReference.create(rw_repo, symref.path, symref.reference).path == symref.path # @NoEffect SymbolicReference.delete(rw_repo, symref) # would raise if the symref wouldn't have been deletedpbl symref = SymbolicReference.create(rw_repo, symref_path, cur_head.reference)