Skip to content

Fix orderdict, improve types #1304

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jul 31, 2021
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
11 changes: 6 additions & 5 deletions git/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]

# -------------------------------------------------------------

Expand Down
5 changes: 5 additions & 0 deletions git/objects/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down
15 changes: 8 additions & 7 deletions git/refs/head.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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:
Expand All @@ -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"""
Expand Down
4 changes: 3 additions & 1 deletion git/refs/reference.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 11 additions & 5 deletions git/refs/tag.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


# ------------------------------------------------------------------------------
Expand Down Expand Up @@ -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.
Expand All @@ -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:
Expand All @@ -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']:
Expand All @@ -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)

Expand Down
8 changes: 4 additions & 4 deletions git/repo/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"""
Expand Down Expand Up @@ -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']]:
Expand Down
10 changes: 7 additions & 3 deletions test/test_refs.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down