From c081f51b6d4ea6020a411e4ec5c2f90a48e10cad Mon Sep 17 00:00:00 2001 From: Dominic Date: Sat, 31 Jul 2021 13:27:55 +0100 Subject: [PATCH 1/5] update types commit.py --- git/objects/commit.py | 1 + 1 file changed, 1 insertion(+) diff --git a/git/objects/commit.py b/git/objects/commit.py index 884f65228..9d7096563 100644 --- a/git/objects/commit.py +++ b/git/objects/commit.py @@ -128,6 +128,7 @@ def __init__(self, repo: 'Repo', binsha: bytes, tree: Union[Tree, None] = None, as what time.altzone returns. The sign is inverted compared to git's UTC timezone.""" super(Commit, self).__init__(repo, binsha) + self.binsha = binsha if tree is not None: assert isinstance(tree, Tree), "Tree needs to be a Tree instance, was %s" % type(tree) if tree is not None: From 0affa33e449db5ba3e00e4c606e6d0c78ce228cf Mon Sep 17 00:00:00 2001 From: Dominic Date: Sat, 31 Jul 2021 13:28:39 +0100 Subject: [PATCH 2/5] update types submodule/base.py --- git/objects/submodule/base.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index 29212167c..143511909 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -563,6 +563,7 @@ def update(self, recursive: bool = False, init: bool = True, to_latest_revision: progress.update(op, i, len_rmts, prefix + "Done fetching remote of submodule %r" % self.name) # END fetch new data except InvalidGitRepositoryError: + mrepo = None if not init: return self # END early abort if init is not allowed @@ -603,7 +604,7 @@ def update(self, recursive: bool = False, init: bool = True, to_latest_revision: # make sure HEAD is not detached mrepo.head.set_reference(local_branch, logmsg="submodule: attaching head to %s" % local_branch) - mrepo.head.ref.set_tracking_branch(remote_branch) + mrepo.head.reference.set_tracking_branch(remote_branch) except (IndexError, InvalidGitRepositoryError): log.warning("Failed to checkout tracking branch %s", self.branch_path) # END handle tracking branch @@ -629,13 +630,14 @@ def update(self, recursive: bool = False, init: bool = True, to_latest_revision: if mrepo is not None and to_latest_revision: msg_base = "Cannot update to latest revision in repository at %r as " % mrepo.working_dir if not is_detached: - rref = mrepo.head.ref.tracking_branch() + rref = mrepo.head.reference.tracking_branch() if rref is not None: rcommit = rref.commit binsha = rcommit.binsha hexsha = rcommit.hexsha else: - log.error("%s a tracking branch was not set for local branch '%s'", msg_base, mrepo.head.ref) + log.error("%s a tracking branch was not set for local branch '%s'", + msg_base, mrepo.head.reference) # END handle remote ref else: log.error("%s there was no local tracking branch", msg_base) From a3cd08a402f9517583b263152dddfa0005934015 Mon Sep 17 00:00:00 2001 From: Dominic Date: Sat, 31 Jul 2021 13:29:15 +0100 Subject: [PATCH 3/5] update types submodule/root.py --- git/objects/submodule/root.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/git/objects/submodule/root.py b/git/objects/submodule/root.py index bcac5419a..5e84d1616 100644 --- a/git/objects/submodule/root.py +++ b/git/objects/submodule/root.py @@ -2,9 +2,7 @@ Submodule, UpdateProgress ) -from .util import ( - find_first_remote_branch -) +from .util import find_first_remote_branch from git.exc import InvalidGitRepositoryError import git From 03190098bdf0f4c0cbb90e39f464c3dd67b0ee1d Mon Sep 17 00:00:00 2001 From: Dominic Date: Sat, 31 Jul 2021 13:34:22 +0100 Subject: [PATCH 4/5] update types sybmbolic.py --- git/refs/symbolic.py | 172 ++++++++++++++++++++++++------------------- 1 file changed, 98 insertions(+), 74 deletions(-) diff --git a/git/refs/symbolic.py b/git/refs/symbolic.py index 0e9dad5cc..4171fe234 100644 --- a/git/refs/symbolic.py +++ b/git/refs/symbolic.py @@ -1,4 +1,3 @@ -from git.types import PathLike import os from git.compat import defenc @@ -17,17 +16,18 @@ BadName ) -import os.path as osp - -from .log import RefLog +from .log import RefLog, RefLogEntry # typing ------------------------------------------------------------------ -from typing import Any, Iterator, List, Match, Optional, Tuple, Type, TypeVar, Union, TYPE_CHECKING # NOQA +from typing import Any, Iterator, List, Match, Optional, Tuple, Type, TypeVar, Union, TYPE_CHECKING, cast # NOQA from git.types import Commit_ish, PathLike, TBD, Literal # NOQA if TYPE_CHECKING: from git.repo import Repo + from git.refs import Reference, Head, TagReference, RemoteReference + from git.config import GitConfigParser + from git.objects.commit import Actor T_References = TypeVar('T_References', bound='SymbolicReference') @@ -37,9 +37,9 @@ __all__ = ["SymbolicReference"] -def _git_dir(repo, path): +def _git_dir(repo: 'Repo', path: PathLike) -> PathLike: """ Find the git dir that's appropriate for the path""" - name = "%s" % (path,) + name = f"{path}" if name in ['HEAD', 'ORIG_HEAD', 'FETCH_HEAD', 'index', 'logs']: return repo.git_dir return repo.common_dir @@ -59,46 +59,47 @@ class SymbolicReference(object): _remote_common_path_default = "refs/remotes" _id_attribute_ = "name" - def __init__(self, repo: 'Repo', path: PathLike, check_path: bool = False): + def __init__(self, repo: 'Repo', path: PathLike, check_path: bool = False) -> None: self.repo = repo self.path = str(path) + self.ref = self._get_reference() def __str__(self) -> str: return self.path - def __repr__(self): + def __repr__(self) -> str: return '' % (self.__class__.__name__, self.path) - def __eq__(self, other): + def __eq__(self, other) -> bool: if hasattr(other, 'path'): return self.path == other.path return False - def __ne__(self, other): + def __ne__(self, other) -> bool: return not (self == other) def __hash__(self): return hash(self.path) @property - def name(self): + def name(self) -> str: """ :return: In case of symbolic references, the shortest assumable name is the path itself.""" - return self.path + return str(self.path) @property - def abspath(self): + def abspath(self) -> PathLike: return join_path_native(_git_dir(self.repo, self.path), self.path) @classmethod - def _get_packed_refs_path(cls, repo): - return osp.join(repo.common_dir, 'packed-refs') + def _get_packed_refs_path(cls, repo: 'Repo') -> str: + return os.path.join(repo.common_dir, 'packed-refs') @classmethod - def _iter_packed_refs(cls, repo): - """Returns an iterator yielding pairs of sha1/path pairs (as bytes) for the corresponding refs. + def _iter_packed_refs(cls, repo: 'Repo') -> Iterator[Tuple[str, str]]: + """Returns an iterator yielding pairs of sha1/path pairs for the corresponding refs. :note: The packed refs file will be kept open as long as we iterate""" try: with open(cls._get_packed_refs_path(repo), 'rt', encoding='UTF-8') as fp: @@ -126,7 +127,7 @@ def _iter_packed_refs(cls, repo): if line[0] == '^': continue - yield tuple(line.split(' ', 1)) + yield cast(Tuple[str, str], tuple(line.split(' ', 1))) # END for each line except OSError: return None @@ -137,26 +138,26 @@ def _iter_packed_refs(cls, repo): # alright. @classmethod - def dereference_recursive(cls, repo, ref_path): + def dereference_recursive(cls, repo: 'Repo', ref_path: PathLike) -> str: """ :return: hexsha stored in the reference at the given ref_path, recursively dereferencing all intermediate references as required :param repo: the repository containing the reference at ref_path""" while True: - hexsha, ref_path = cls._get_ref_info(repo, ref_path) + hexsha, _ref_path_out = cls._get_ref_info(repo, ref_path) if hexsha is not None: return hexsha # END recursive dereferencing @classmethod - def _get_ref_info_helper(cls, repo, ref_path): + def _get_ref_info_helper(cls, repo: 'Repo', ref_path: PathLike) -> Union[Tuple[str, None], Tuple[None, PathLike]]: """Return: (str(sha), str(target_ref_path)) if available, the sha the file at rela_path points to, or None. target_ref_path is the reference we point to, or None""" - tokens = None + tokens: Union[List[str], Tuple[str, str], None] = None repodir = _git_dir(repo, ref_path) try: - with open(osp.join(repodir, ref_path), 'rt', encoding='UTF-8') as fp: + with open(os.path.join(repodir, ref_path), 'rt', encoding='UTF-8') as fp: value = fp.read().rstrip() # Don't only split on spaces, but on whitespace, which allows to parse lines like # 60b64ef992065e2600bfef6187a97f92398a9144 branch 'master' of git-server:/path/to/repo @@ -188,13 +189,14 @@ def _get_ref_info_helper(cls, repo, ref_path): raise ValueError("Failed to parse reference information from %r" % ref_path) @classmethod - def _get_ref_info(cls, repo, ref_path): + def _get_ref_info(cls, repo: 'Repo', ref_path: PathLike + ) -> Union[Tuple[str, None], Tuple[None, PathLike]]: """Return: (str(sha), str(target_ref_path)) if available, the sha the file at rela_path points to, or None. target_ref_path is the reference we point to, or None""" return cls._get_ref_info_helper(repo, ref_path) - def _get_object(self): + def _get_object(self) -> Commit_ish: """ :return: The object our ref currently refers to. Refs can be cached, they will @@ -203,7 +205,7 @@ def _get_object(self): # Our path will be resolved to the hexsha which will be used accordingly return Object.new_from_sha(self.repo, hex_to_bin(self.dereference_recursive(self.repo, self.path))) - def _get_commit(self): + def _get_commit(self) -> 'Commit': """ :return: Commit object we point to, works for detached and non-detached @@ -218,7 +220,8 @@ def _get_commit(self): # END handle type return obj - def set_commit(self, commit: Union[Commit, 'SymbolicReference', str], logmsg=None): + def set_commit(self, commit: Union[Commit, 'SymbolicReference', str], + logmsg: Union[str, None] = None) -> None: """As set_object, but restricts the type of object to be a Commit :raise ValueError: If commit is not a Commit object or doesn't point to @@ -228,11 +231,13 @@ def set_commit(self, commit: Union[Commit, 'SymbolicReference', str], logmsg=Non invalid_type = False if isinstance(commit, Object): invalid_type = commit.type != Commit.type + commit = cast('Commit', commit) elif isinstance(commit, SymbolicReference): invalid_type = commit.object.type != Commit.type else: try: - invalid_type = self.repo.rev_parse(commit).type != Commit.type + commit = self.repo.rev_parse(commit) + invalid_type = commit.type != Commit.type except (BadObject, BadName) as e: raise ValueError("Invalid object: %s" % commit) from e # END handle exception @@ -245,9 +250,12 @@ def set_commit(self, commit: Union[Commit, 'SymbolicReference', str], logmsg=Non # we leave strings to the rev-parse method below self.set_object(commit, logmsg) - return self + # return self + return None - def set_object(self, object, logmsg=None): # @ReservedAssignment + def set_object(self, object: Union[Commit_ish, 'SymbolicReference'], + logmsg: Union[str, None] = None + ) -> 'SymbolicReference': # @ReservedAssignment """Set the object we point to, possibly dereference our symbolic reference first. If the reference does not exist, it will be created @@ -274,10 +282,11 @@ def set_object(self, object, logmsg=None): # @ReservedAssignment # set the commit on our reference return self._get_reference().set_object(object, logmsg) - commit = property(_get_commit, set_commit, doc="Query or set commits directly") - object = property(_get_object, set_object, doc="Return the object our ref currently refers to") + commit = cast('Commit', property(_get_commit, set_commit, doc="Query or set commits directly")) + object = property(_get_object, set_object, doc="Return the object our ref currently refers to") # type: ignore - def _get_reference(self): + def _get_reference(self + ) -> Union['Head', 'RemoteReference', 'TagReference', 'Reference']: """:return: Reference Object we point to :raise TypeError: If this symbolic reference is detached, hence it doesn't point to a reference, but to a commit""" @@ -286,7 +295,8 @@ def _get_reference(self): raise TypeError("%s is a detached symbolic reference as it points to %r" % (self, sha)) return self.from_path(self.repo, target_ref_path) - def set_reference(self, ref, logmsg=None): + def set_reference(self, ref: Union[str, Commit_ish, 'SymbolicReference'], logmsg: Union[str, None] = None + ) -> 'SymbolicReference': """Set ourselves to the given ref. It will stay a symbol if the ref is a Reference. Otherwise an Object, given as Object instance or refspec, is assumed and if valid, will be set which effectively detaches the refererence if it was a purely @@ -327,7 +337,7 @@ def set_reference(self, ref, logmsg=None): raise TypeError("Require commit, got %r" % obj) # END verify type - oldbinsha = None + oldbinsha: bytes = b'' if logmsg is not None: try: oldbinsha = self.commit.binsha @@ -355,11 +365,16 @@ def set_reference(self, ref, logmsg=None): return self - # aliased reference - reference = property(_get_reference, set_reference, doc="Returns the Reference we point to") - ref: Union[Commit_ish] = reference # type: ignore # Union[str, Commit_ish, SymbolicReference] + @ property + def reference(self) -> Union['Head', 'RemoteReference', 'TagReference', 'Reference']: + return self._get_reference() - def is_valid(self): + @ reference.setter + def reference(self, ref: Union[str, Commit_ish, 'SymbolicReference'], logmsg: Union[str, None] = None + ) -> 'SymbolicReference': + return self.set_reference(ref=ref, logmsg=logmsg) + + def is_valid(self) -> bool: """ :return: True if the reference is valid, hence it can be read and points to @@ -371,7 +386,7 @@ def is_valid(self): else: return True - @property + @ property def is_detached(self): """ :return: @@ -383,7 +398,7 @@ def is_detached(self): except TypeError: return True - def log(self): + def log(self) -> 'RefLog': """ :return: RefLog for this reference. Its last entry reflects the latest change applied to this reference @@ -392,7 +407,8 @@ def log(self): instead of calling this method repeatedly. It should be considered read-only.""" return RefLog.from_file(RefLog.path(self)) - def log_append(self, oldbinsha, message, newbinsha=None): + def log_append(self, oldbinsha: bytes, message: Union[str, None], + newbinsha: Union[bytes, None] = None) -> 'RefLogEntry': """Append a logentry to the logfile of this ref :param oldbinsha: binary sha this ref used to point to @@ -404,15 +420,19 @@ def log_append(self, oldbinsha, message, newbinsha=None): # correct to allow overriding the committer on a per-commit level. # See https://github.com/gitpython-developers/GitPython/pull/146 try: - committer_or_reader = self.commit.committer + committer_or_reader: Union['Actor', 'GitConfigParser'] = self.commit.committer except ValueError: committer_or_reader = self.repo.config_reader() # end handle newly cloned repositories - return RefLog.append_entry(committer_or_reader, RefLog.path(self), oldbinsha, - (newbinsha is None and self.commit.binsha) or newbinsha, - message) + if newbinsha is None: + newbinsha = self.commit.binsha + + if message is None: + message = '' - def log_entry(self, index): + return RefLog.append_entry(committer_or_reader, RefLog.path(self), oldbinsha, newbinsha, message) + + def log_entry(self, index: int) -> RefLogEntry: """:return: RefLogEntry at the given index :param index: python list compatible positive or negative index @@ -421,22 +441,23 @@ def log_entry(self, index): In that case, it will be faster than the ``log()`` method""" return RefLog.entry_at(RefLog.path(self), index) - @classmethod - def to_full_path(cls, path) -> PathLike: + @ classmethod + def to_full_path(cls, path: Union[PathLike, 'SymbolicReference']) -> str: """ :return: string with a full repository-relative path which can be used to initialize a Reference instance, for instance by using ``Reference.from_path``""" if isinstance(path, SymbolicReference): path = path.path - full_ref_path = path + full_ref_path = str(path) if not cls._common_path_default: return full_ref_path - if not path.startswith(cls._common_path_default + "/"): + + if not str(path).startswith(cls._common_path_default + "/"): full_ref_path = '%s/%s' % (cls._common_path_default, path) return full_ref_path - @classmethod - def delete(cls, repo, path): + @ classmethod + def delete(cls, repo: 'Repo', path: PathLike) -> None: """Delete the reference at the given path :param repo: @@ -447,8 +468,8 @@ def delete(cls, repo, path): or just "myreference", hence 'refs/' is implied. Alternatively the symbolic reference to be deleted""" full_ref_path = cls.to_full_path(path) - abs_path = osp.join(repo.common_dir, full_ref_path) - if osp.exists(abs_path): + abs_path = os.path.join(repo.common_dir, full_ref_path) + if os.path.exists(abs_path): os.remove(abs_path) else: # check packed refs @@ -458,8 +479,8 @@ def delete(cls, repo, path): new_lines = [] made_change = False dropped_last_line = False - for line in reader: - line = line.decode(defenc) + for line_bytes in reader: + line = line_bytes.decode(defenc) _, _, line_ref = line.partition(' ') line_ref = line_ref.strip() # keep line if it is a comment or if the ref to delete is not @@ -489,12 +510,14 @@ def delete(cls, repo, path): # delete the reflog reflog_path = RefLog.path(cls(repo, full_ref_path)) - if osp.isfile(reflog_path): + if os.path.isfile(reflog_path): os.remove(reflog_path) # END remove reflog - @classmethod - def _create(cls, repo, path, resolve, reference, force, logmsg=None): + @ classmethod + def _create(cls: Type[T_References], repo: 'Repo', path: PathLike, resolve: bool, + reference: Union[str, 'SymbolicReference'], + force: bool, logmsg: Union[str, None] = None) -> T_References: """internal method used to create a new symbolic reference. If resolve is False, the reference will be taken as is, creating a proper symbolic reference. Otherwise it will be resolved to the @@ -502,14 +525,14 @@ def _create(cls, repo, path, resolve, reference, force, logmsg=None): instead""" git_dir = _git_dir(repo, path) full_ref_path = cls.to_full_path(path) - abs_ref_path = osp.join(git_dir, full_ref_path) + abs_ref_path = os.path.join(git_dir, full_ref_path) # figure out target data target = reference if resolve: target = repo.rev_parse(str(reference)) - if not force and osp.isfile(abs_ref_path): + if not force and os.path.isfile(abs_ref_path): target_data = str(target) if isinstance(target, SymbolicReference): target_data = target.path @@ -527,8 +550,9 @@ def _create(cls, repo, path, resolve, reference, force, logmsg=None): return ref @classmethod - def create(cls, repo: 'Repo', path: PathLike, reference: Union[Commit_ish, str] = 'HEAD', - logmsg: Union[str, None] = None, force: bool = False, **kwargs: Any): + def create(cls: Type[T_References], repo: 'Repo', path: PathLike, + reference: Union[str, 'SymbolicReference'] = 'SymbolicReference', + logmsg: Union[str, None] = None, force: bool = False, **kwargs: Any) -> T_References: """Create a new symbolic reference, hence a reference pointing , to another reference. :param repo: @@ -540,7 +564,7 @@ def create(cls, repo: 'Repo', path: PathLike, reference: Union[Commit_ish, str] :param reference: The reference to which the new symbolic reference should point to. - If it is a commit'ish, the symbolic ref will be detached. + If it is a ref to a commit'ish, the symbolic ref will be detached. :param force: if True, force creation even if a symbolic reference with that name already exists. @@ -559,7 +583,7 @@ def create(cls, repo: 'Repo', path: PathLike, reference: Union[Commit_ish, str] :note: This does not alter the current HEAD, index or Working Tree""" return cls._create(repo, path, cls._resolve_ref_on_create, reference, force, logmsg) - def rename(self, new_path, force=False): + def rename(self, new_path: str, force: bool = False) -> 'SymbolicReference': """Rename self to a new path :param new_path: @@ -577,9 +601,9 @@ def rename(self, new_path, force=False): if self.path == new_path: return self - new_abs_path = osp.join(_git_dir(self.repo, new_path), new_path) - cur_abs_path = osp.join(_git_dir(self.repo, self.path), self.path) - if osp.isfile(new_abs_path): + new_abs_path = os.path.join(_git_dir(self.repo, new_path), new_path) + cur_abs_path = os.path.join(_git_dir(self.repo, self.path), self.path) + if os.path.isfile(new_abs_path): if not force: # if they point to the same file, its not an error with open(new_abs_path, 'rb') as fd1: @@ -594,8 +618,8 @@ def rename(self, new_path, force=False): os.remove(new_abs_path) # END handle existing target file - dname = osp.dirname(new_abs_path) - if not osp.isdir(dname): + dname = os.path.dirname(new_abs_path) + if not os.path.isdir(dname): os.makedirs(dname) # END create directory @@ -630,7 +654,7 @@ def _iter_items(cls: Type[T_References], repo: 'Repo', common_path: Union[PathLi # read packed refs for _sha, rela_path in cls._iter_packed_refs(repo): - if rela_path.startswith(common_path): + if rela_path.startswith(str(common_path)): rela_paths.add(rela_path) # END relative path matches common path # END packed refs reading @@ -665,7 +689,7 @@ def iter_items(cls, repo: 'Repo', common_path: Union[PathLike, None] = None, *ar return (r for r in cls._iter_items(repo, common_path) if r.__class__ == SymbolicReference or not r.is_detached) @classmethod - def from_path(cls, repo, path): + def from_path(cls, repo: 'Repo', path: PathLike) -> Union['Head', 'RemoteReference', 'TagReference', 'Reference']: """ :param path: full .git-directory-relative path name to the Reference to instantiate :note: use to_full_path() if you only have a partial path of a known Reference Type From a5a05d153ecdbd9b9bdb285a77f5b14d9c0344b0 Mon Sep 17 00:00:00 2001 From: Dominic Date: Sat, 31 Jul 2021 13:50:39 +0100 Subject: [PATCH 5/5] rvrt symbolic.py types --- git/refs/symbolic.py | 172 +++++++++++++++++++------------------------ 1 file changed, 74 insertions(+), 98 deletions(-) diff --git a/git/refs/symbolic.py b/git/refs/symbolic.py index 4171fe234..0e9dad5cc 100644 --- a/git/refs/symbolic.py +++ b/git/refs/symbolic.py @@ -1,3 +1,4 @@ +from git.types import PathLike import os from git.compat import defenc @@ -16,18 +17,17 @@ BadName ) -from .log import RefLog, RefLogEntry +import os.path as osp + +from .log import RefLog # typing ------------------------------------------------------------------ -from typing import Any, Iterator, List, Match, Optional, Tuple, Type, TypeVar, Union, TYPE_CHECKING, cast # NOQA +from typing import Any, Iterator, List, Match, Optional, Tuple, Type, TypeVar, Union, TYPE_CHECKING # NOQA from git.types import Commit_ish, PathLike, TBD, Literal # NOQA if TYPE_CHECKING: from git.repo import Repo - from git.refs import Reference, Head, TagReference, RemoteReference - from git.config import GitConfigParser - from git.objects.commit import Actor T_References = TypeVar('T_References', bound='SymbolicReference') @@ -37,9 +37,9 @@ __all__ = ["SymbolicReference"] -def _git_dir(repo: 'Repo', path: PathLike) -> PathLike: +def _git_dir(repo, path): """ Find the git dir that's appropriate for the path""" - name = f"{path}" + name = "%s" % (path,) if name in ['HEAD', 'ORIG_HEAD', 'FETCH_HEAD', 'index', 'logs']: return repo.git_dir return repo.common_dir @@ -59,47 +59,46 @@ class SymbolicReference(object): _remote_common_path_default = "refs/remotes" _id_attribute_ = "name" - def __init__(self, repo: 'Repo', path: PathLike, check_path: bool = False) -> None: + def __init__(self, repo: 'Repo', path: PathLike, check_path: bool = False): self.repo = repo self.path = str(path) - self.ref = self._get_reference() def __str__(self) -> str: return self.path - def __repr__(self) -> str: + def __repr__(self): return '' % (self.__class__.__name__, self.path) - def __eq__(self, other) -> bool: + def __eq__(self, other): if hasattr(other, 'path'): return self.path == other.path return False - def __ne__(self, other) -> bool: + def __ne__(self, other): return not (self == other) def __hash__(self): return hash(self.path) @property - def name(self) -> str: + def name(self): """ :return: In case of symbolic references, the shortest assumable name is the path itself.""" - return str(self.path) + return self.path @property - def abspath(self) -> PathLike: + def abspath(self): return join_path_native(_git_dir(self.repo, self.path), self.path) @classmethod - def _get_packed_refs_path(cls, repo: 'Repo') -> str: - return os.path.join(repo.common_dir, 'packed-refs') + def _get_packed_refs_path(cls, repo): + return osp.join(repo.common_dir, 'packed-refs') @classmethod - def _iter_packed_refs(cls, repo: 'Repo') -> Iterator[Tuple[str, str]]: - """Returns an iterator yielding pairs of sha1/path pairs for the corresponding refs. + def _iter_packed_refs(cls, repo): + """Returns an iterator yielding pairs of sha1/path pairs (as bytes) for the corresponding refs. :note: The packed refs file will be kept open as long as we iterate""" try: with open(cls._get_packed_refs_path(repo), 'rt', encoding='UTF-8') as fp: @@ -127,7 +126,7 @@ def _iter_packed_refs(cls, repo: 'Repo') -> Iterator[Tuple[str, str]]: if line[0] == '^': continue - yield cast(Tuple[str, str], tuple(line.split(' ', 1))) + yield tuple(line.split(' ', 1)) # END for each line except OSError: return None @@ -138,26 +137,26 @@ def _iter_packed_refs(cls, repo: 'Repo') -> Iterator[Tuple[str, str]]: # alright. @classmethod - def dereference_recursive(cls, repo: 'Repo', ref_path: PathLike) -> str: + def dereference_recursive(cls, repo, ref_path): """ :return: hexsha stored in the reference at the given ref_path, recursively dereferencing all intermediate references as required :param repo: the repository containing the reference at ref_path""" while True: - hexsha, _ref_path_out = cls._get_ref_info(repo, ref_path) + hexsha, ref_path = cls._get_ref_info(repo, ref_path) if hexsha is not None: return hexsha # END recursive dereferencing @classmethod - def _get_ref_info_helper(cls, repo: 'Repo', ref_path: PathLike) -> Union[Tuple[str, None], Tuple[None, PathLike]]: + def _get_ref_info_helper(cls, repo, ref_path): """Return: (str(sha), str(target_ref_path)) if available, the sha the file at rela_path points to, or None. target_ref_path is the reference we point to, or None""" - tokens: Union[List[str], Tuple[str, str], None] = None + tokens = None repodir = _git_dir(repo, ref_path) try: - with open(os.path.join(repodir, ref_path), 'rt', encoding='UTF-8') as fp: + with open(osp.join(repodir, ref_path), 'rt', encoding='UTF-8') as fp: value = fp.read().rstrip() # Don't only split on spaces, but on whitespace, which allows to parse lines like # 60b64ef992065e2600bfef6187a97f92398a9144 branch 'master' of git-server:/path/to/repo @@ -189,14 +188,13 @@ def _get_ref_info_helper(cls, repo: 'Repo', ref_path: PathLike) -> Union[Tuple[s raise ValueError("Failed to parse reference information from %r" % ref_path) @classmethod - def _get_ref_info(cls, repo: 'Repo', ref_path: PathLike - ) -> Union[Tuple[str, None], Tuple[None, PathLike]]: + def _get_ref_info(cls, repo, ref_path): """Return: (str(sha), str(target_ref_path)) if available, the sha the file at rela_path points to, or None. target_ref_path is the reference we point to, or None""" return cls._get_ref_info_helper(repo, ref_path) - def _get_object(self) -> Commit_ish: + def _get_object(self): """ :return: The object our ref currently refers to. Refs can be cached, they will @@ -205,7 +203,7 @@ def _get_object(self) -> Commit_ish: # Our path will be resolved to the hexsha which will be used accordingly return Object.new_from_sha(self.repo, hex_to_bin(self.dereference_recursive(self.repo, self.path))) - def _get_commit(self) -> 'Commit': + def _get_commit(self): """ :return: Commit object we point to, works for detached and non-detached @@ -220,8 +218,7 @@ def _get_commit(self) -> 'Commit': # END handle type return obj - def set_commit(self, commit: Union[Commit, 'SymbolicReference', str], - logmsg: Union[str, None] = None) -> None: + def set_commit(self, commit: Union[Commit, 'SymbolicReference', str], logmsg=None): """As set_object, but restricts the type of object to be a Commit :raise ValueError: If commit is not a Commit object or doesn't point to @@ -231,13 +228,11 @@ def set_commit(self, commit: Union[Commit, 'SymbolicReference', str], invalid_type = False if isinstance(commit, Object): invalid_type = commit.type != Commit.type - commit = cast('Commit', commit) elif isinstance(commit, SymbolicReference): invalid_type = commit.object.type != Commit.type else: try: - commit = self.repo.rev_parse(commit) - invalid_type = commit.type != Commit.type + invalid_type = self.repo.rev_parse(commit).type != Commit.type except (BadObject, BadName) as e: raise ValueError("Invalid object: %s" % commit) from e # END handle exception @@ -250,12 +245,9 @@ def set_commit(self, commit: Union[Commit, 'SymbolicReference', str], # we leave strings to the rev-parse method below self.set_object(commit, logmsg) - # return self - return None + return self - def set_object(self, object: Union[Commit_ish, 'SymbolicReference'], - logmsg: Union[str, None] = None - ) -> 'SymbolicReference': # @ReservedAssignment + def set_object(self, object, logmsg=None): # @ReservedAssignment """Set the object we point to, possibly dereference our symbolic reference first. If the reference does not exist, it will be created @@ -282,11 +274,10 @@ def set_object(self, object: Union[Commit_ish, 'SymbolicReference'], # set the commit on our reference return self._get_reference().set_object(object, logmsg) - commit = cast('Commit', property(_get_commit, set_commit, doc="Query or set commits directly")) - object = property(_get_object, set_object, doc="Return the object our ref currently refers to") # type: ignore + commit = property(_get_commit, set_commit, doc="Query or set commits directly") + object = property(_get_object, set_object, doc="Return the object our ref currently refers to") - def _get_reference(self - ) -> Union['Head', 'RemoteReference', 'TagReference', 'Reference']: + def _get_reference(self): """:return: Reference Object we point to :raise TypeError: If this symbolic reference is detached, hence it doesn't point to a reference, but to a commit""" @@ -295,8 +286,7 @@ def _get_reference(self raise TypeError("%s is a detached symbolic reference as it points to %r" % (self, sha)) return self.from_path(self.repo, target_ref_path) - def set_reference(self, ref: Union[str, Commit_ish, 'SymbolicReference'], logmsg: Union[str, None] = None - ) -> 'SymbolicReference': + def set_reference(self, ref, logmsg=None): """Set ourselves to the given ref. It will stay a symbol if the ref is a Reference. Otherwise an Object, given as Object instance or refspec, is assumed and if valid, will be set which effectively detaches the refererence if it was a purely @@ -337,7 +327,7 @@ def set_reference(self, ref: Union[str, Commit_ish, 'SymbolicReference'], logmsg raise TypeError("Require commit, got %r" % obj) # END verify type - oldbinsha: bytes = b'' + oldbinsha = None if logmsg is not None: try: oldbinsha = self.commit.binsha @@ -365,16 +355,11 @@ def set_reference(self, ref: Union[str, Commit_ish, 'SymbolicReference'], logmsg return self - @ property - def reference(self) -> Union['Head', 'RemoteReference', 'TagReference', 'Reference']: - return self._get_reference() + # aliased reference + reference = property(_get_reference, set_reference, doc="Returns the Reference we point to") + ref: Union[Commit_ish] = reference # type: ignore # Union[str, Commit_ish, SymbolicReference] - @ reference.setter - def reference(self, ref: Union[str, Commit_ish, 'SymbolicReference'], logmsg: Union[str, None] = None - ) -> 'SymbolicReference': - return self.set_reference(ref=ref, logmsg=logmsg) - - def is_valid(self) -> bool: + def is_valid(self): """ :return: True if the reference is valid, hence it can be read and points to @@ -386,7 +371,7 @@ def is_valid(self) -> bool: else: return True - @ property + @property def is_detached(self): """ :return: @@ -398,7 +383,7 @@ def is_detached(self): except TypeError: return True - def log(self) -> 'RefLog': + def log(self): """ :return: RefLog for this reference. Its last entry reflects the latest change applied to this reference @@ -407,8 +392,7 @@ def log(self) -> 'RefLog': instead of calling this method repeatedly. It should be considered read-only.""" return RefLog.from_file(RefLog.path(self)) - def log_append(self, oldbinsha: bytes, message: Union[str, None], - newbinsha: Union[bytes, None] = None) -> 'RefLogEntry': + def log_append(self, oldbinsha, message, newbinsha=None): """Append a logentry to the logfile of this ref :param oldbinsha: binary sha this ref used to point to @@ -420,19 +404,15 @@ def log_append(self, oldbinsha: bytes, message: Union[str, None], # correct to allow overriding the committer on a per-commit level. # See https://github.com/gitpython-developers/GitPython/pull/146 try: - committer_or_reader: Union['Actor', 'GitConfigParser'] = self.commit.committer + committer_or_reader = self.commit.committer except ValueError: committer_or_reader = self.repo.config_reader() # end handle newly cloned repositories - if newbinsha is None: - newbinsha = self.commit.binsha - - if message is None: - message = '' + return RefLog.append_entry(committer_or_reader, RefLog.path(self), oldbinsha, + (newbinsha is None and self.commit.binsha) or newbinsha, + message) - return RefLog.append_entry(committer_or_reader, RefLog.path(self), oldbinsha, newbinsha, message) - - def log_entry(self, index: int) -> RefLogEntry: + def log_entry(self, index): """:return: RefLogEntry at the given index :param index: python list compatible positive or negative index @@ -441,23 +421,22 @@ def log_entry(self, index: int) -> RefLogEntry: In that case, it will be faster than the ``log()`` method""" return RefLog.entry_at(RefLog.path(self), index) - @ classmethod - def to_full_path(cls, path: Union[PathLike, 'SymbolicReference']) -> str: + @classmethod + def to_full_path(cls, path) -> PathLike: """ :return: string with a full repository-relative path which can be used to initialize a Reference instance, for instance by using ``Reference.from_path``""" if isinstance(path, SymbolicReference): path = path.path - full_ref_path = str(path) + full_ref_path = path if not cls._common_path_default: return full_ref_path - - if not str(path).startswith(cls._common_path_default + "/"): + if not path.startswith(cls._common_path_default + "/"): full_ref_path = '%s/%s' % (cls._common_path_default, path) return full_ref_path - @ classmethod - def delete(cls, repo: 'Repo', path: PathLike) -> None: + @classmethod + def delete(cls, repo, path): """Delete the reference at the given path :param repo: @@ -468,8 +447,8 @@ def delete(cls, repo: 'Repo', path: PathLike) -> None: or just "myreference", hence 'refs/' is implied. Alternatively the symbolic reference to be deleted""" full_ref_path = cls.to_full_path(path) - abs_path = os.path.join(repo.common_dir, full_ref_path) - if os.path.exists(abs_path): + abs_path = osp.join(repo.common_dir, full_ref_path) + if osp.exists(abs_path): os.remove(abs_path) else: # check packed refs @@ -479,8 +458,8 @@ def delete(cls, repo: 'Repo', path: PathLike) -> None: new_lines = [] made_change = False dropped_last_line = False - for line_bytes in reader: - line = line_bytes.decode(defenc) + for line in reader: + line = line.decode(defenc) _, _, line_ref = line.partition(' ') line_ref = line_ref.strip() # keep line if it is a comment or if the ref to delete is not @@ -510,14 +489,12 @@ def delete(cls, repo: 'Repo', path: PathLike) -> None: # delete the reflog reflog_path = RefLog.path(cls(repo, full_ref_path)) - if os.path.isfile(reflog_path): + if osp.isfile(reflog_path): os.remove(reflog_path) # END remove reflog - @ classmethod - def _create(cls: Type[T_References], repo: 'Repo', path: PathLike, resolve: bool, - reference: Union[str, 'SymbolicReference'], - force: bool, logmsg: Union[str, None] = None) -> T_References: + @classmethod + def _create(cls, repo, path, resolve, reference, force, logmsg=None): """internal method used to create a new symbolic reference. If resolve is False, the reference will be taken as is, creating a proper symbolic reference. Otherwise it will be resolved to the @@ -525,14 +502,14 @@ def _create(cls: Type[T_References], repo: 'Repo', path: PathLike, resolve: bool instead""" git_dir = _git_dir(repo, path) full_ref_path = cls.to_full_path(path) - abs_ref_path = os.path.join(git_dir, full_ref_path) + abs_ref_path = osp.join(git_dir, full_ref_path) # figure out target data target = reference if resolve: target = repo.rev_parse(str(reference)) - if not force and os.path.isfile(abs_ref_path): + if not force and osp.isfile(abs_ref_path): target_data = str(target) if isinstance(target, SymbolicReference): target_data = target.path @@ -550,9 +527,8 @@ def _create(cls: Type[T_References], repo: 'Repo', path: PathLike, resolve: bool return ref @classmethod - def create(cls: Type[T_References], repo: 'Repo', path: PathLike, - reference: Union[str, 'SymbolicReference'] = 'SymbolicReference', - logmsg: Union[str, None] = None, force: bool = False, **kwargs: Any) -> T_References: + def create(cls, repo: 'Repo', path: PathLike, reference: Union[Commit_ish, str] = 'HEAD', + logmsg: Union[str, None] = None, force: bool = False, **kwargs: Any): """Create a new symbolic reference, hence a reference pointing , to another reference. :param repo: @@ -564,7 +540,7 @@ def create(cls: Type[T_References], repo: 'Repo', path: PathLike, :param reference: The reference to which the new symbolic reference should point to. - If it is a ref to a commit'ish, the symbolic ref will be detached. + If it is a commit'ish, the symbolic ref will be detached. :param force: if True, force creation even if a symbolic reference with that name already exists. @@ -583,7 +559,7 @@ def create(cls: Type[T_References], repo: 'Repo', path: PathLike, :note: This does not alter the current HEAD, index or Working Tree""" return cls._create(repo, path, cls._resolve_ref_on_create, reference, force, logmsg) - def rename(self, new_path: str, force: bool = False) -> 'SymbolicReference': + def rename(self, new_path, force=False): """Rename self to a new path :param new_path: @@ -601,9 +577,9 @@ def rename(self, new_path: str, force: bool = False) -> 'SymbolicReference': if self.path == new_path: return self - new_abs_path = os.path.join(_git_dir(self.repo, new_path), new_path) - cur_abs_path = os.path.join(_git_dir(self.repo, self.path), self.path) - if os.path.isfile(new_abs_path): + new_abs_path = osp.join(_git_dir(self.repo, new_path), new_path) + cur_abs_path = osp.join(_git_dir(self.repo, self.path), self.path) + if osp.isfile(new_abs_path): if not force: # if they point to the same file, its not an error with open(new_abs_path, 'rb') as fd1: @@ -618,8 +594,8 @@ def rename(self, new_path: str, force: bool = False) -> 'SymbolicReference': os.remove(new_abs_path) # END handle existing target file - dname = os.path.dirname(new_abs_path) - if not os.path.isdir(dname): + dname = osp.dirname(new_abs_path) + if not osp.isdir(dname): os.makedirs(dname) # END create directory @@ -654,7 +630,7 @@ def _iter_items(cls: Type[T_References], repo: 'Repo', common_path: Union[PathLi # read packed refs for _sha, rela_path in cls._iter_packed_refs(repo): - if rela_path.startswith(str(common_path)): + if rela_path.startswith(common_path): rela_paths.add(rela_path) # END relative path matches common path # END packed refs reading @@ -689,7 +665,7 @@ def iter_items(cls, repo: 'Repo', common_path: Union[PathLike, None] = None, *ar return (r for r in cls._iter_items(repo, common_path) if r.__class__ == SymbolicReference or not r.is_detached) @classmethod - def from_path(cls, repo: 'Repo', path: PathLike) -> Union['Head', 'RemoteReference', 'TagReference', 'Reference']: + def from_path(cls, repo, path): """ :param path: full .git-directory-relative path name to the Reference to instantiate :note: use to_full_path() if you only have a partial path of a known Reference Type