From f066c7adb5f5773dc1d0dd986c786bc0c5094e31 Mon Sep 17 00:00:00 2001
From: Yobmod <yobmod@gmail.com>
Date: Sat, 31 Jul 2021 16:52:42 +0100
Subject: [PATCH 01/23] Add type to symbolicreference.is_remote()

---
 git/refs/symbolic.py | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/git/refs/symbolic.py b/git/refs/symbolic.py
index bffcfea5f..b072f142c 100644
--- a/git/refs/symbolic.py
+++ b/git/refs/symbolic.py
@@ -526,8 +526,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['SymbolicReference', str] = 'HEAD',
+               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:
@@ -689,6 +690,6 @@ def from_path(cls, repo: 'Repo', path: PathLike) -> Union['Head', 'TagReference'
         # END for each type to try
         raise ValueError("Could not find reference type suitable to handle path %r" % path)
 
-    def is_remote(self):
+    def is_remote(self) -> bool:
         """:return: True if this symbolic reference points to a remote branch"""
-        return self.path.startswith(self._remote_common_path_default + "/")
+        return str(self.path).startswith(self._remote_common_path_default + "/")

From a8ee94b1998589085ae2b8a6de310d0a5dfd0ffd Mon Sep 17 00:00:00 2001
From: Yobmod <yobmod@gmail.com>
Date: Sat, 31 Jul 2021 16:57:56 +0100
Subject: [PATCH 02/23] Add type to symbolicreference._create()

---
 git/refs/symbolic.py | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/git/refs/symbolic.py b/git/refs/symbolic.py
index b072f142c..1000204fb 100644
--- a/git/refs/symbolic.py
+++ b/git/refs/symbolic.py
@@ -493,7 +493,9 @@ def delete(cls, repo, path):
         # END remove reflog
 
     @classmethod
-    def _create(cls, repo, path, resolve, reference, force, logmsg=None):
+    def _create(cls: Type[T_References], repo: 'Repo', path: PathLike, resolve: bool,
+                reference: Union['SymbolicReference', str], 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
@@ -511,7 +513,7 @@ def _create(cls, repo, path, resolve, reference, force, logmsg=None):
         if not force and os.path.isfile(abs_ref_path):
             target_data = str(target)
             if isinstance(target, SymbolicReference):
-                target_data = target.path
+                target_data = str(target.path)
             if not resolve:
                 target_data = "ref: " + target_data
             with open(abs_ref_path, 'rb') as fd:

From afd2ec5251479f48044408c7fcb7dab0494cd4c1 Mon Sep 17 00:00:00 2001
From: Yobmod <yobmod@gmail.com>
Date: Sat, 31 Jul 2021 17:02:53 +0100
Subject: [PATCH 03/23] Add type to symbolicreference.delete()

---
 git/refs/symbolic.py | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/git/refs/symbolic.py b/git/refs/symbolic.py
index 1000204fb..89999eda6 100644
--- a/git/refs/symbolic.py
+++ b/git/refs/symbolic.py
@@ -421,7 +421,7 @@ def log_entry(self, index):
         return RefLog.entry_at(RefLog.path(self), index)
 
     @classmethod
-    def to_full_path(cls, path) -> PathLike:
+    def to_full_path(cls, path: Union[PathLike, 'SymbolicReference']) -> 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``"""
@@ -430,12 +430,12 @@ def to_full_path(cls, path) -> PathLike:
         full_ref_path = 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):
+    def delete(cls, repo: 'Repo', path: PathLike) -> None:
         """Delete the reference at the given path
 
         :param repo:
@@ -457,8 +457,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

From 581d40d957fd3d7ee85c28c4dde4d6f28e104433 Mon Sep 17 00:00:00 2001
From: Yobmod <yobmod@gmail.com>
Date: Sat, 31 Jul 2021 17:06:22 +0100
Subject: [PATCH 04/23] Add type to symbolicreference.log_append()

---
 git/refs/symbolic.py | 21 +++++++++++++++------
 1 file changed, 15 insertions(+), 6 deletions(-)

diff --git a/git/refs/symbolic.py b/git/refs/symbolic.py
index 89999eda6..5d3a6a0da 100644
--- a/git/refs/symbolic.py
+++ b/git/refs/symbolic.py
@@ -27,6 +27,10 @@
 if TYPE_CHECKING:
     from git.repo import Repo
     from git.refs import Head, TagReference, Reference
+    from .log import RefLogEntry
+    from git.config import GitConfigParser
+    from git.objects.commit import Actor
+
 
 T_References = TypeVar('T_References', bound='SymbolicReference')
 
@@ -391,7 +395,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
@@ -403,15 +408,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 = ''
+
+        return RefLog.append_entry(committer_or_reader, RefLog.path(self), oldbinsha, newbinsha, message)
 
-    def log_entry(self, index):
+    def log_entry(self, index: int) -> 'RefLogEntry':
         """:return: RefLogEntry at the given index
         :param index: python list compatible positive or negative index
 

From 7f401fc659a7f9c84a9c88675aba498357a2916d Mon Sep 17 00:00:00 2001
From: Yobmod <yobmod@gmail.com>
Date: Sat, 31 Jul 2021 17:09:54 +0100
Subject: [PATCH 05/23] Add type to symbolicreference.is_valid()

---
 git/refs/symbolic.py | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/git/refs/symbolic.py b/git/refs/symbolic.py
index 5d3a6a0da..65bad6e48 100644
--- a/git/refs/symbolic.py
+++ b/git/refs/symbolic.py
@@ -360,9 +360,9 @@ def set_reference(self, ref, logmsg=None):
 
     # 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]
+    ref: Union['Reference'] = reference     # type: ignore
 
-    def is_valid(self):
+    def is_valid(self) -> bool:
         """
         :return:
             True if the reference is valid, hence it can be read and points to
@@ -375,7 +375,7 @@ def is_valid(self):
             return True
 
     @property
-    def is_detached(self):
+    def is_detached(self) -> bool:
         """
         :return:
             True if we are a detached reference, hence we point to a specific commit
@@ -386,7 +386,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

From e8442eead72bfc2a547234d0289d0f90642167fd Mon Sep 17 00:00:00 2001
From: Yobmod <yobmod@gmail.com>
Date: Sat, 31 Jul 2021 17:15:33 +0100
Subject: [PATCH 06/23] Add type to symbolicreference.set_reference()

---
 git/refs/symbolic.py | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/git/refs/symbolic.py b/git/refs/symbolic.py
index 65bad6e48..4713e0c42 100644
--- a/git/refs/symbolic.py
+++ b/git/refs/symbolic.py
@@ -289,7 +289,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[Commit_ish, 'SymbolicReference', str],
+                      logmsg: Union[str, None] = None) -> Union[Commit_ish, '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
@@ -330,7 +331,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
@@ -359,8 +360,8 @@ 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['Reference'] = reference     # type: ignore
+    reference = property(_get_reference, set_reference, doc="Returns the Reference we point to")  # type: ignore
+    ref: Union['Reference'] = reference             # type: ignore
 
     def is_valid(self) -> bool:
         """

From f2012e599e388580bf8823a23ff63a201e0bf4c4 Mon Sep 17 00:00:00 2001
From: Yobmod <yobmod@gmail.com>
Date: Sat, 31 Jul 2021 17:25:46 +0100
Subject: [PATCH 07/23] Add type to symbolicreference.set_object()

---
 git/refs/reference.py |  4 ++--
 git/refs/symbolic.py  | 14 ++++++++------
 2 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/git/refs/reference.py b/git/refs/reference.py
index bc2c6e807..539691e72 100644
--- a/git/refs/reference.py
+++ b/git/refs/reference.py
@@ -63,8 +63,8 @@ def __str__(self) -> str:
     #{ Interface
 
     # @ReservedAssignment
-    def set_object(self, object: Union[Commit_ish, 'SymbolicReference'], logmsg: Union[str, None] = None
-                   ) -> 'SymbolicReference':
+    def set_object(self, object: Union[Commit_ish, 'SymbolicReference', str], logmsg: Union[str, None] = None
+                   ) -> 'Reference':
         """Special version which checks if the head-log needs an update as well
         :return: self"""
         oldbinsha = None
diff --git a/git/refs/symbolic.py b/git/refs/symbolic.py
index 4713e0c42..0d2c98297 100644
--- a/git/refs/symbolic.py
+++ b/git/refs/symbolic.py
@@ -221,7 +221,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
+                   ) -> 'SymbolicReference':
         """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
@@ -250,7 +251,8 @@ def set_commit(self, commit: Union[Commit, 'SymbolicReference', str], logmsg=Non
 
         return self
 
-    def set_object(self, object, logmsg=None):  # @ReservedAssignment
+    def set_object(self, object: Union[Commit_ish, 'SymbolicReference', str], logmsg: Union[str, None] = None
+                   ) -> 'SymbolicReference':
         """Set the object we point to, possibly dereference our symbolic reference first.
         If the reference does not exist, it will be created
 
@@ -277,10 +279,10 @@ 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 = property(_get_commit, set_commit, doc="Query or set commits directly")       # type: ignore
+    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) -> 'SymbolicReference':
         """: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"""
@@ -290,7 +292,7 @@ def _get_reference(self):
         return self.from_path(self.repo, target_ref_path)
 
     def set_reference(self, ref: Union[Commit_ish, 'SymbolicReference', str],
-                      logmsg: Union[str, None] = None) -> Union[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

From d4d46697a6ce23edba8e22030916dad28d42abc2 Mon Sep 17 00:00:00 2001
From: Yobmod <yobmod@gmail.com>
Date: Sat, 31 Jul 2021 17:29:06 +0100
Subject: [PATCH 08/23] cleanup

---
 git/refs/reference.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/git/refs/reference.py b/git/refs/reference.py
index 539691e72..a3647fb3b 100644
--- a/git/refs/reference.py
+++ b/git/refs/reference.py
@@ -7,7 +7,7 @@
 
 # typing ------------------------------------------------------------------
 
-from typing import Any, Callable, Iterator, List, Match, Optional, Tuple, Type, TypeVar, Union, TYPE_CHECKING  # NOQA
+from typing import Any, Callable, Iterator, Type, Union, TYPE_CHECKING  # NOQA
 from git.types import Commit_ish, PathLike, TBD, Literal, _T                                  # NOQA
 
 if TYPE_CHECKING:

From 13b38ce012dc1bf84d9dca8d141dcab86c0d4e09 Mon Sep 17 00:00:00 2001
From: Yobmod <yobmod@gmail.com>
Date: Sat, 31 Jul 2021 17:49:18 +0100
Subject: [PATCH 09/23] Add type to symbolicreference.reference()

---
 git/refs/head.py     |  1 +
 git/refs/symbolic.py | 14 +++++++++++---
 2 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/git/refs/head.py b/git/refs/head.py
index 260bf5e7e..160272049 100644
--- a/git/refs/head.py
+++ b/git/refs/head.py
@@ -40,6 +40,7 @@ def __init__(self, repo: 'Repo', path: PathLike = _HEAD_NAME):
             raise ValueError("HEAD instance must point to %r, got %r" % (self._HEAD_NAME, path))
         super(HEAD, self).__init__(repo, path)
         self.commit: 'Commit'
+        self.ref: 'Head'
 
     def orig_head(self) -> SymbolicReference:
         """
diff --git a/git/refs/symbolic.py b/git/refs/symbolic.py
index 0d2c98297..5ce74938c 100644
--- a/git/refs/symbolic.py
+++ b/git/refs/symbolic.py
@@ -65,6 +65,7 @@ class SymbolicReference(object):
     def __init__(self, repo: 'Repo', path: PathLike, check_path: bool = False):
         self.repo = repo
         self.path = path
+        self.ref = self.reference
 
     def __str__(self) -> str:
         return str(self.path)
@@ -282,7 +283,7 @@ def set_object(self, object: Union[Commit_ish, 'SymbolicReference', str], logmsg
     commit = property(_get_commit, set_commit, doc="Query or set commits directly")       # type: ignore
     object = property(_get_object, set_object, doc="Return the object our ref currently refers to")  # type: ignore
 
-    def _get_reference(self) -> 'SymbolicReference':
+    def _get_reference(self) -> '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"""
@@ -362,8 +363,15 @@ def set_reference(self, ref: Union[Commit_ish, 'SymbolicReference', str],
         return self
 
     # aliased reference
-    reference = property(_get_reference, set_reference, doc="Returns the Reference we point to")  # type: ignore
-    ref: Union['Reference'] = reference             # type: ignore
+    # reference = property(_get_reference, set_reference, doc="Returns the Reference we point to")  # type: ignore
+
+    @property
+    def reference(self) -> 'Reference':
+        return self._get_reference()
+
+    @reference.setter
+    def reference(self, *args, **kwargs):
+        return self.set_reference(*args, **kwargs)
 
     def is_valid(self) -> bool:
         """

From 3be955e5adc09d20a7e2e919ee1e95a7a0f5fb0e Mon Sep 17 00:00:00 2001
From: Yobmod <yobmod@gmail.com>
Date: Sat, 31 Jul 2021 17:53:44 +0100
Subject: [PATCH 10/23] Add type to symbolicreference.references()

---
 git/refs/head.py     |  1 -
 git/refs/symbolic.py | 14 +++-----------
 2 files changed, 3 insertions(+), 12 deletions(-)

diff --git a/git/refs/head.py b/git/refs/head.py
index 160272049..260bf5e7e 100644
--- a/git/refs/head.py
+++ b/git/refs/head.py
@@ -40,7 +40,6 @@ def __init__(self, repo: 'Repo', path: PathLike = _HEAD_NAME):
             raise ValueError("HEAD instance must point to %r, got %r" % (self._HEAD_NAME, path))
         super(HEAD, self).__init__(repo, path)
         self.commit: 'Commit'
-        self.ref: 'Head'
 
     def orig_head(self) -> SymbolicReference:
         """
diff --git a/git/refs/symbolic.py b/git/refs/symbolic.py
index 5ce74938c..ae391c1e2 100644
--- a/git/refs/symbolic.py
+++ b/git/refs/symbolic.py
@@ -26,7 +26,7 @@
 
 if TYPE_CHECKING:
     from git.repo import Repo
-    from git.refs import Head, TagReference, Reference
+    from git.refs import Head, TagReference, RemoteReference, Reference
     from .log import RefLogEntry
     from git.config import GitConfigParser
     from git.objects.commit import Actor
@@ -65,7 +65,6 @@ class SymbolicReference(object):
     def __init__(self, repo: 'Repo', path: PathLike, check_path: bool = False):
         self.repo = repo
         self.path = path
-        self.ref = self.reference
 
     def __str__(self) -> str:
         return str(self.path)
@@ -363,15 +362,8 @@ def set_reference(self, ref: Union[Commit_ish, 'SymbolicReference', str],
         return self
 
     # aliased reference
-    # reference = property(_get_reference, set_reference, doc="Returns the Reference we point to")  # type: ignore
-
-    @property
-    def reference(self) -> 'Reference':
-        return self._get_reference()
-
-    @reference.setter
-    def reference(self, *args, **kwargs):
-        return self.set_reference(*args, **kwargs)
+    reference = property(_get_reference, set_reference, doc="Returns the Reference we point to")  # type: ignore
+    ref: Union['Head', 'TagReference', 'RemoteReference', 'Reference'] = reference             # type: ignore
 
     def is_valid(self) -> bool:
         """

From 62f78814206a99fafeedab1d4f2ee6f4c6b70ef1 Mon Sep 17 00:00:00 2001
From: Yobmod <yobmod@gmail.com>
Date: Sat, 31 Jul 2021 17:59:14 +0100
Subject: [PATCH 11/23] Add type to repo.base._to_full_tag_path

---
 git/repo/base.py | 13 +++++++------
 pyproject.toml   |  2 +-
 2 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/git/repo/base.py b/git/repo/base.py
index 355f93999..5581233ba 100644
--- a/git/repo/base.py
+++ b/git/repo/base.py
@@ -412,13 +412,14 @@ def tag(self, path: PathLike) -> TagReference:
         return TagReference(self, full_path)
 
     @staticmethod
-    def _to_full_tag_path(path):
-        if path.startswith(TagReference._common_path_default + '/'):
-            return path
-        if path.startswith(TagReference._common_default + '/'):
-            return Reference._common_path_default + '/' + path
+    def _to_full_tag_path(path: PathLike) -> str:
+        path_str = str(path)
+        if path_str.startswith(TagReference._common_path_default + '/'):
+            return path_str
+        if path_str.startswith(TagReference._common_default + '/'):
+            return Reference._common_path_default + '/' + path_str
         else:
-            return TagReference._common_path_default + '/' + path
+            return TagReference._common_path_default + '/' + path_str
 
     def create_head(self, path: PathLike, commit: str = 'HEAD',
                     force: bool = False, logmsg: Optional[str] = None
diff --git a/pyproject.toml b/pyproject.toml
index 6437a7199..4751ffcb9 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -19,7 +19,7 @@ filterwarnings = 'ignore::DeprecationWarning'
 # filterwarnings ignore::WarningType  # ignores those warnings
 
 [tool.mypy]
-# disallow_untyped_defs = true
+disallow_untyped_defs = true
 no_implicit_optional = true
 warn_redundant_casts = true
 # warn_unused_ignores = True

From ef48a3513d7a9456fd57f4da248a9c73f9e668bd Mon Sep 17 00:00:00 2001
From: Yobmod <yobmod@gmail.com>
Date: Sat, 31 Jul 2021 18:01:01 +0100
Subject: [PATCH 12/23] Add type to refs.head.delete()

---
 git/refs/head.py | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/git/refs/head.py b/git/refs/head.py
index 260bf5e7e..56a87182f 100644
--- a/git/refs/head.py
+++ b/git/refs/head.py
@@ -21,7 +21,7 @@
 __all__ = ["HEAD", "Head"]
 
 
-def strip_quotes(string):
+def strip_quotes(string: str) -> str:
     if string.startswith('"') and string.endswith('"'):
         return string[1:-1]
     return string
@@ -129,14 +129,13 @@ class Head(Reference):
     k_config_remote_ref = "merge"           # branch to merge from remote
 
     @classmethod
-    def delete(cls, repo: 'Repo', *heads: 'Head', **kwargs: Any):
+    def delete(cls, repo: 'Repo', *heads: 'Head', force: bool = False, **kwargs: Any) -> None:
         """Delete the given heads
 
         :param force:
             If True, the heads will be deleted even if they are not yet merged into
             the main development stream.
             Default False"""
-        force = kwargs.get("force", False)
         flag = "-d"
         if force:
             flag = "-D"

From e364c5e327f916366e5936aa2c9f3f4065aec034 Mon Sep 17 00:00:00 2001
From: Yobmod <yobmod@gmail.com>
Date: Sat, 31 Jul 2021 18:02:06 +0100
Subject: [PATCH 13/23] Add type to refs.log._read_from_file()

---
 git/refs/log.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/git/refs/log.py b/git/refs/log.py
index 643b41140..ddd78bc76 100644
--- a/git/refs/log.py
+++ b/git/refs/log.py
@@ -157,7 +157,7 @@ def __init__(self, filepath: Union[PathLike, None] = None):
             self._read_from_file()
         # END handle filepath
 
-    def _read_from_file(self):
+    def _read_from_file(self) -> None:
         try:
             fmap = file_contents_ro_filepath(
                 self._path, stream=True, allow_mmap=True)

From 35231dba2f12ef4d19eabc409e72f773a19a3c43 Mon Sep 17 00:00:00 2001
From: Yobmod <yobmod@gmail.com>
Date: Sat, 31 Jul 2021 18:04:28 +0100
Subject: [PATCH 14/23] Add type to objects.base.new()

---
 git/objects/base.py | 3 ++-
 pyproject.toml      | 2 +-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/git/objects/base.py b/git/objects/base.py
index 64f105ca5..a3b0f230a 100644
--- a/git/objects/base.py
+++ b/git/objects/base.py
@@ -25,6 +25,7 @@
     from .tree import Tree
     from .blob import Blob
     from .submodule.base import Submodule
+    from git.refs.reference import Reference
 
 IndexObjUnion = Union['Tree', 'Blob', 'Submodule']
 
@@ -59,7 +60,7 @@ def __init__(self, repo: 'Repo', binsha: bytes):
         assert len(binsha) == 20, "Require 20 byte binary sha, got %r, len = %i" % (binsha, len(binsha))
 
     @classmethod
-    def new(cls, repo: 'Repo', id):  # @ReservedAssignment
+    def new(cls, repo: 'Repo', id: Union[str, 'Reference']) -> Commit_ish:
         """
         :return: New Object instance of a type appropriate to the object type behind
             id. The id of the newly created object will be a binsha even though
diff --git a/pyproject.toml b/pyproject.toml
index 4751ffcb9..ccf5c165d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -19,7 +19,7 @@ filterwarnings = 'ignore::DeprecationWarning'
 # filterwarnings ignore::WarningType  # ignores those warnings
 
 [tool.mypy]
-disallow_untyped_defs = true
+#disallow_untyped_defs = true
 no_implicit_optional = true
 warn_redundant_casts = true
 # warn_unused_ignores = True

From d6e736922cb69cc87dd6595ef93f247ce99a960a Mon Sep 17 00:00:00 2001
From: Yobmod <yobmod@gmail.com>
Date: Sat, 31 Jul 2021 18:56:09 +0100
Subject: [PATCH 15/23] Add final types to config.py

---
 git/config.py  | 18 +++++++++++-------
 git/util.py    |  8 ++++----
 pyproject.toml |  2 +-
 3 files changed, 16 insertions(+), 12 deletions(-)

diff --git a/git/config.py b/git/config.py
index ad02b4373..a3f41c60f 100644
--- a/git/config.py
+++ b/git/config.py
@@ -40,6 +40,7 @@
     from io import BytesIO
 
 T_ConfigParser = TypeVar('T_ConfigParser', bound='GitConfigParser')
+T_OMD_value = TypeVar('T_OMD_value', str, bytes, int, float, bool)
 
 if sys.version_info[:3] < (3, 7, 2):
     # typing.Ordereddict not added until py 3.7.2
@@ -47,7 +48,7 @@
     OrderedDict_OMD = OrderedDict           # type: ignore  # until 3.6 dropped
 else:
     from typing import OrderedDict                   # type: ignore  # until 3.6 dropped
-    OrderedDict_OMD = OrderedDict[str, List[_T]]  # type: ignore[assignment, misc]
+    OrderedDict_OMD = OrderedDict[str, List[T_OMD_value]]  # type: ignore[assignment, misc]
 
 # -------------------------------------------------------------
 
@@ -97,23 +98,23 @@ def __new__(cls, name: str, bases: TBD, clsdict: Dict[str, Any]) -> TBD:
         return new_type
 
 
-def needs_values(func: Callable) -> Callable:
+def needs_values(func: Callable[..., _T]) -> Callable[..., _T]:
     """Returns method assuring we read values (on demand) before we try to access them"""
 
     @wraps(func)
-    def assure_data_present(self, *args: Any, **kwargs: Any) -> Any:
+    def assure_data_present(self: GitConfigParser, *args: Any, **kwargs: Any) -> _T:
         self.read()
         return func(self, *args, **kwargs)
     # END wrapper method
     return assure_data_present
 
 
-def set_dirty_and_flush_changes(non_const_func: Callable) -> Callable:
+def set_dirty_and_flush_changes(non_const_func: Callable[..., _T]) -> Callable[..., _T]:
     """Return method that checks whether given non constant function may be called.
     If so, the instance will be set dirty.
     Additionally, we flush the changes right to disk"""
 
-    def flush_changes(self, *args: Any, **kwargs: Any) -> Any:
+    def flush_changes(self: GitConfigParser, *args: Any, **kwargs: Any) -> _T:
         rval = non_const_func(self, *args, **kwargs)
         self._dirty = True
         self.write()
@@ -356,7 +357,7 @@ def __enter__(self) -> 'GitConfigParser':
         self._acquire_lock()
         return self
 
-    def __exit__(self, exception_type, exception_value, traceback) -> None:
+    def __exit__(self, *args: Any) -> None:
         self.release()
 
     def release(self) -> None:
@@ -613,12 +614,15 @@ def read(self) -> None:  # type: ignore[override]
     def _write(self, fp: IO) -> None:
         """Write an .ini-format representation of the configuration state in
         git compatible format"""
-        def write_section(name, section_dict):
+        def write_section(name: str, section_dict: _OMD) -> None:
             fp.write(("[%s]\n" % name).encode(defenc))
+
+            values: Sequence[Union[str, bytes, int, float, bool]]
             for (key, values) in section_dict.items_all():
                 if key == "__name__":
                     continue
 
+                v: Union[str, bytes, int, float, bool]
                 for v in values:
                     fp.write(("\t%s = %s\n" % (key, self._value_to_string(v).replace('\n', '\n\t'))).encode(defenc))
                 # END if key is not __name__
diff --git a/git/util.py b/git/util.py
index c0c0ecb73..92d95379e 100644
--- a/git/util.py
+++ b/git/util.py
@@ -408,7 +408,7 @@ def expand_path(p: Union[None, PathLike], expand_vars: bool = True) -> Optional[
         return None
 
 
-def remove_password_if_present(cmdline):
+def remove_password_if_present(cmdline: Sequence[str]) -> List[str]:
     """
     Parse any command line argument and if on of the element is an URL with a
     password, replace it by stars (in-place).
@@ -1033,7 +1033,7 @@ def __delitem__(self, index: Union[SupportsIndex, int, slice, str]) -> None:
 
 class IterableClassWatcher(type):
     """ Metaclass that watches """
-    def __init__(cls, name, bases, clsdict):
+    def __init__(cls, name: str, bases: List, clsdict: Dict) -> None:
         for base in bases:
             if type(base) == IterableClassWatcher:
                 warnings.warn(f"GitPython Iterable subclassed by {name}. "
@@ -1052,7 +1052,7 @@ class Iterable(metaclass=IterableClassWatcher):
     _id_attribute_ = "attribute that most suitably identifies your instance"
 
     @classmethod
-    def list_items(cls, repo, *args, **kwargs):
+    def list_items(cls, repo: 'Repo', *args: Any, **kwargs: Any) -> Any:
         """
         Deprecated, use IterableObj instead.
         Find all items of this type - subclasses can specify args and kwargs differently.
@@ -1062,7 +1062,7 @@ def list_items(cls, repo, *args, **kwargs):
         :note: Favor the iter_items method as it will
 
         :return:list(Item,...) list of item instances"""
-        out_list = IterableList(cls._id_attribute_)
+        out_list: Any = IterableList(cls._id_attribute_)
         out_list.extend(cls.iter_items(repo, *args, **kwargs))
         return out_list
 
diff --git a/pyproject.toml b/pyproject.toml
index ccf5c165d..6437a7199 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -19,7 +19,7 @@ filterwarnings = 'ignore::DeprecationWarning'
 # filterwarnings ignore::WarningType  # ignores those warnings
 
 [tool.mypy]
-#disallow_untyped_defs = true
+# disallow_untyped_defs = true
 no_implicit_optional = true
 warn_redundant_casts = true
 # warn_unused_ignores = True

From 476f4de6aa893bc0289c345a7d06b85256ab98bc Mon Sep 17 00:00:00 2001
From: Yobmod <yobmod@gmail.com>
Date: Sat, 31 Jul 2021 19:00:23 +0100
Subject: [PATCH 16/23] Add set_dirty_and_flush_changes() types to config.py

---
 git/config.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/git/config.py b/git/config.py
index a3f41c60f..011d0e0b1 100644
--- a/git/config.py
+++ b/git/config.py
@@ -102,7 +102,7 @@ def needs_values(func: Callable[..., _T]) -> Callable[..., _T]:
     """Returns method assuring we read values (on demand) before we try to access them"""
 
     @wraps(func)
-    def assure_data_present(self: GitConfigParser, *args: Any, **kwargs: Any) -> _T:
+    def assure_data_present(self: 'GitConfigParser', *args: Any, **kwargs: Any) -> _T:
         self.read()
         return func(self, *args, **kwargs)
     # END wrapper method
@@ -114,7 +114,7 @@ def set_dirty_and_flush_changes(non_const_func: Callable[..., _T]) -> Callable[.
     If so, the instance will be set dirty.
     Additionally, we flush the changes right to disk"""
 
-    def flush_changes(self: GitConfigParser, *args: Any, **kwargs: Any) -> _T:
+    def flush_changes(self: 'GitConfigParser', *args: Any, **kwargs: Any) -> _T:
         rval = non_const_func(self, *args, **kwargs)
         self._dirty = True
         self.write()

From e6bee43b97862182d6c30bc8200f6abd1ff759e5 Mon Sep 17 00:00:00 2001
From: Yobmod <yobmod@gmail.com>
Date: Sat, 31 Jul 2021 19:08:29 +0100
Subject: [PATCH 17/23] Add final types to commit.py

---
 git/objects/commit.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/git/objects/commit.py b/git/objects/commit.py
index 9d7096563..b689167f5 100644
--- a/git/objects/commit.py
+++ b/git/objects/commit.py
@@ -282,7 +282,7 @@ def iter_items(cls, repo: 'Repo', rev: Union[str, 'Commit', 'SymbolicReference']
         proc = repo.git.rev_list(rev, args_list, as_process=True, **kwargs)
         return cls._iter_from_process_or_stream(repo, proc)
 
-    def iter_parents(self, paths: Union[PathLike, Sequence[PathLike]] = '', **kwargs) -> Iterator['Commit']:
+    def iter_parents(self, paths: Union[PathLike, Sequence[PathLike]] = '', **kwargs: Any) -> Iterator['Commit']:
         """Iterate _all_ parents of this commit.
 
         :param paths:
@@ -362,7 +362,7 @@ def _iter_from_process_or_stream(cls, repo: 'Repo', proc_or_stream: Union[Popen,
     def create_from_tree(cls, repo: 'Repo', tree: Union[Tree, str], message: str,
                          parent_commits: Union[None, List['Commit']] = None, head: bool = False,
                          author: Union[None, Actor] = None, committer: Union[None, Actor] = None,
-                         author_date: Union[None, str] = None, commit_date: Union[None, str] = None):
+                         author_date: Union[None, str] = None, commit_date: Union[None, str] = None) -> 'Commit':
         """Commit the given tree, creating a commit object.
 
         :param repo: Repo object the commit should be part of
@@ -403,7 +403,7 @@ def create_from_tree(cls, repo: 'Repo', tree: Union[Tree, str], message: str,
         else:
             for p in parent_commits:
                 if not isinstance(p, cls):
-                    raise ValueError("Parent commit '%r' must be of type %s" % (p, cls))
+                    raise ValueError(f"Parent commit '{p!r}' must be of type {cls}")
             # end check parent commit types
         # END if parent commits are unset
 

From 2fc8a461017db70051e12746468585479c081bec Mon Sep 17 00:00:00 2001
From: Yobmod <yobmod@gmail.com>
Date: Sat, 31 Jul 2021 19:12:14 +0100
Subject: [PATCH 18/23] Add final types to tree.py

---
 git/objects/tree.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/git/objects/tree.py b/git/objects/tree.py
index 70f36af5d..0cceb59ac 100644
--- a/git/objects/tree.py
+++ b/git/objects/tree.py
@@ -375,8 +375,8 @@ def __contains__(self, item: Union[IndexObjUnion, PathLike]) -> bool:
         # END for each item
         return False
 
-    def __reversed__(self):
-        return reversed(self._iter_convert_to_object(self._cache))
+    def __reversed__(self) -> Iterator[IndexObjUnion]:
+        return reversed(self._iter_convert_to_object(self._cache))  # type: ignore
 
     def _serialize(self, stream: 'BytesIO') -> 'Tree':
         """Serialize this tree into the stream. Please note that we will assume

From 9e5574ca26b30a9b5ec1e4763a590093e3dcfc97 Mon Sep 17 00:00:00 2001
From: Yobmod <yobmod@gmail.com>
Date: Sat, 31 Jul 2021 19:25:38 +0100
Subject: [PATCH 19/23] Add final types to submodule.py

---
 git/objects/submodule/base.py | 13 +++++++------
 git/objects/util.py           | 12 ++++++------
 pyproject.toml                |  2 +-
 3 files changed, 14 insertions(+), 13 deletions(-)

diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py
index 143511909..559d2585e 100644
--- a/git/objects/submodule/base.py
+++ b/git/objects/submodule/base.py
@@ -57,6 +57,7 @@
 if TYPE_CHECKING:
     from git.index import IndexFile
     from git.repo import Repo
+    from git.refs import Head
 
 
 # -----------------------------------------------------------------------------
@@ -265,7 +266,7 @@ def _module_abspath(cls, parent_repo: 'Repo', path: PathLike, name: str) -> Path
         # end
 
     @classmethod
-    def _clone_repo(cls, repo, url, path, name, **kwargs):
+    def _clone_repo(cls, repo: 'Repo', url: str, path: PathLike, name: str, **kwargs: Any) -> 'Repo':
         """:return: Repo instance of newly cloned repository
         :param repo: our parent repository
         :param url: url to clone from
@@ -279,7 +280,7 @@ def _clone_repo(cls, repo, url, path, name, **kwargs):
             module_abspath_dir = osp.dirname(module_abspath)
             if not osp.isdir(module_abspath_dir):
                 os.makedirs(module_abspath_dir)
-            module_checkout_path = osp.join(repo.working_tree_dir, path)
+            module_checkout_path = osp.join(str(repo.working_tree_dir), path)
         # end
 
         clone = git.Repo.clone_from(url, module_checkout_path, **kwargs)
@@ -484,7 +485,7 @@ def add(cls, repo: 'Repo', name: str, path: PathLike, url: Union[str, None] = No
     def update(self, recursive: bool = False, init: bool = True, to_latest_revision: bool = False,
                progress: Union['UpdateProgress', None] = None, dry_run: bool = False,
                force: bool = False, keep_going: bool = False, env: Union[Mapping[str, str], None] = None,
-               clone_multi_options: Union[Sequence[TBD], None] = None):
+               clone_multi_options: Union[Sequence[TBD], None] = None) -> 'Submodule':
         """Update the repository of this submodule to point to the checkout
         we point at with the binsha of this instance.
 
@@ -712,7 +713,7 @@ def update(self, recursive: bool = False, init: bool = True, to_latest_revision:
         return self
 
     @unbare_repo
-    def move(self, module_path, configuration=True, module=True):
+    def move(self, module_path: PathLike, configuration: bool = True, module: bool = True) -> 'Submodule':
         """Move the submodule to a another module path. This involves physically moving
         the repository at our current path, changing the configuration, as well as
         adjusting our index entry accordingly.
@@ -742,7 +743,7 @@ def move(self, module_path, configuration=True, module=True):
             return self
         # END handle no change
 
-        module_checkout_abspath = join_path_native(self.repo.working_tree_dir, module_checkout_path)
+        module_checkout_abspath = join_path_native(str(self.repo.working_tree_dir), module_checkout_path)
         if osp.isfile(module_checkout_abspath):
             raise ValueError("Cannot move repository onto a file: %s" % module_checkout_abspath)
         # END handle target files
@@ -1160,7 +1161,7 @@ def exists(self) -> bool:
         # END handle object state consistency
 
     @property
-    def branch(self):
+    def branch(self) -> 'Head':
         """:return: The branch instance that we are to checkout
         :raise InvalidGitRepositoryError: if our module is not yet checked out"""
         return mkhead(self.module(), self._branch_path)
diff --git a/git/objects/util.py b/git/objects/util.py
index db7807c26..f627211ec 100644
--- a/git/objects/util.py
+++ b/git/objects/util.py
@@ -144,20 +144,20 @@ def __init__(self, secs_west_of_utc: float, name: Union[None, str] = None) -> No
     def __reduce__(self) -> Tuple[Type['tzoffset'], Tuple[float, str]]:
         return tzoffset, (-self._offset.total_seconds(), self._name)
 
-    def utcoffset(self, dt) -> timedelta:
+    def utcoffset(self, dt: Union[datetime, None]) -> timedelta:
         return self._offset
 
-    def tzname(self, dt) -> str:
+    def tzname(self, dt: Union[datetime, None]) -> str:
         return self._name
 
-    def dst(self, dt) -> timedelta:
+    def dst(self, dt: Union[datetime, None]) -> timedelta:
         return ZERO
 
 
 utc = tzoffset(0, 'UTC')
 
 
-def from_timestamp(timestamp, tz_offset: float) -> datetime:
+def from_timestamp(timestamp: float, tz_offset: float) -> datetime:
     """Converts a timestamp + tz_offset into an aware datetime instance."""
     utc_dt = datetime.fromtimestamp(timestamp, utc)
     try:
@@ -305,7 +305,7 @@ class Traversable(Protocol):
 
     @classmethod
     @abstractmethod
-    def _get_intermediate_items(cls, item) -> Sequence['Traversable']:
+    def _get_intermediate_items(cls, item: Any) -> Sequence['Traversable']:
         """
         Returns:
             Tuple of items connected to the given item.
@@ -327,7 +327,7 @@ def list_traverse(self, *args: Any, **kwargs: Any) -> Any:
                       stacklevel=2)
         return self._list_traverse(*args, **kwargs)
 
-    def _list_traverse(self, as_edge=False, *args: Any, **kwargs: Any
+    def _list_traverse(self, as_edge: bool = False, *args: Any, **kwargs: Any
                        ) -> IterableList[Union['Commit', 'Submodule', 'Tree', 'Blob']]:
         """
         :return: IterableList with the results of the traversal as produced by
diff --git a/pyproject.toml b/pyproject.toml
index 6437a7199..4751ffcb9 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -19,7 +19,7 @@ filterwarnings = 'ignore::DeprecationWarning'
 # filterwarnings ignore::WarningType  # ignores those warnings
 
 [tool.mypy]
-# disallow_untyped_defs = true
+disallow_untyped_defs = true
 no_implicit_optional = true
 warn_redundant_casts = true
 # warn_unused_ignores = True

From 56f8dd6a902736cb6b87329542ea6dcbf380884e Mon Sep 17 00:00:00 2001
From: Yobmod <yobmod@gmail.com>
Date: Sat, 31 Jul 2021 20:05:53 +0100
Subject: [PATCH 20/23] Add final types to cmd.py

---
 git/cmd.py | 53 ++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 34 insertions(+), 19 deletions(-)

diff --git a/git/cmd.py b/git/cmd.py
index 4404981e0..f82127453 100644
--- a/git/cmd.py
+++ b/git/cmd.py
@@ -39,10 +39,10 @@
 
 # typing ---------------------------------------------------------------------------
 
-from typing import (Any, AnyStr, BinaryIO, Callable, Dict, IO, List, Mapping,
+from typing import (Any, AnyStr, BinaryIO, Callable, Dict, IO, Iterator, List, Mapping,
                     Sequence, TYPE_CHECKING, TextIO, Tuple, Union, cast, overload)
 
-from git.types import PathLike, Literal, TBD
+from git.types import PathLike, Literal
 
 if TYPE_CHECKING:
     from git.repo.base import Repo
@@ -146,11 +146,11 @@ def dashify(string: str) -> str:
     return string.replace('_', '-')
 
 
-def slots_to_dict(self, exclude: Sequence[str] = ()) -> Dict[str, Any]:
+def slots_to_dict(self: object, exclude: Sequence[str] = ()) -> Dict[str, Any]:
     return {s: getattr(self, s) for s in self.__slots__ if s not in exclude}
 
 
-def dict_to_slots_and__excluded_are_none(self, d: Mapping[str, Any], excluded: Sequence[str] = ()) -> None:
+def dict_to_slots_and__excluded_are_none(self: object, d: Mapping[str, Any], excluded: Sequence[str] = ()) -> None:
     for k, v in d.items():
         setattr(self, k, v)
     for k in excluded:
@@ -192,7 +192,7 @@ class Git(LazyMixin):
     def __getstate__(self) -> Dict[str, Any]:
         return slots_to_dict(self, exclude=self._excluded_)
 
-    def __setstate__(self, d) -> None:
+    def __setstate__(self, d: Dict[str, Any]) -> None:
         dict_to_slots_and__excluded_are_none(self, d, excluded=self._excluded_)
 
     # CONFIGURATION
@@ -434,10 +434,13 @@ def wait(self, stderr: Union[None, bytes] = b'') -> int:
             if self.proc is not None:
                 status = self.proc.wait()
 
-                def read_all_from_possibly_closed_stream(stream):
-                    try:
-                        return stderr + force_bytes(stream.read())
-                    except ValueError:
+                def read_all_from_possibly_closed_stream(stream: Union[IO[bytes], None]) -> bytes:
+                    if stream:
+                        try:
+                            return stderr + force_bytes(stream.read())
+                        except ValueError:
+                            return stderr or b''
+                    else:
                         return stderr or b''
 
                 if status != 0:
@@ -907,7 +910,7 @@ def _kill_process(pid: int) -> None:
         if self.GIT_PYTHON_TRACE == 'full':
             cmdstr = " ".join(redacted_command)
 
-            def as_text(stdout_value):
+            def as_text(stdout_value: Union[bytes, str]) -> str:
                 return not output_stream and safe_decode(stdout_value) or '<OUTPUT_STREAM>'
             # end
 
@@ -932,10 +935,10 @@ def as_text(stdout_value):
         else:
             return stdout_value
 
-    def environment(self):
+    def environment(self) -> Dict[str, str]:
         return self._environment
 
-    def update_environment(self, **kwargs):
+    def update_environment(self, **kwargs: Any) -> Dict[str, Union[str, None]]:
         """
         Set environment variables for future git invocations. Return all changed
         values in a format that can be passed back into this function to revert
@@ -962,7 +965,7 @@ def update_environment(self, **kwargs):
         return old_env
 
     @contextmanager
-    def custom_environment(self, **kwargs):
+    def custom_environment(self, **kwargs: Any) -> Iterator[None]:
         """
         A context manager around the above ``update_environment`` method to restore the
         environment back to its previous state after operation.
@@ -1043,6 +1046,13 @@ def _call_process(self, method: str, *args: None, **kwargs: None
                       ) -> str:
         ...  # if no args given, execute called with all defaults
 
+    @overload
+    def _call_process(self, method: str,
+                      istream: int,
+                      as_process: Literal[True],
+                      *args: Any, **kwargs: Any
+                      ) -> 'Git.AutoInterrupt': ...
+
     @overload
     def _call_process(self, method: str, *args: Any, **kwargs: Any
                       ) -> Union[str, bytes, Tuple[int, Union[str, bytes], str], 'Git.AutoInterrupt']:
@@ -1156,7 +1166,7 @@ def _prepare_ref(self, ref: AnyStr) -> bytes:
         return refstr.encode(defenc)
 
     def _get_persistent_cmd(self, attr_name: str, cmd_name: str, *args: Any, **kwargs: Any
-                            ) -> Union['Git.AutoInterrupt', TBD]:
+                            ) -> 'Git.AutoInterrupt':
         cur_val = getattr(self, attr_name)
         if cur_val is not None:
             return cur_val
@@ -1166,12 +1176,16 @@ def _get_persistent_cmd(self, attr_name: str, cmd_name: str, *args: Any, **kwarg
 
         cmd = self._call_process(cmd_name, *args, **options)
         setattr(self, attr_name, cmd)
+        cmd = cast('Git.AutoInterrupt', cmd)
         return cmd
 
-    def __get_object_header(self, cmd, ref: AnyStr) -> Tuple[str, str, int]:
-        cmd.stdin.write(self._prepare_ref(ref))
-        cmd.stdin.flush()
-        return self._parse_object_header(cmd.stdout.readline())
+    def __get_object_header(self, cmd: 'Git.AutoInterrupt', ref: AnyStr) -> Tuple[str, str, int]:
+        if cmd.stdin and cmd.stdout:
+            cmd.stdin.write(self._prepare_ref(ref))
+            cmd.stdin.flush()
+            return self._parse_object_header(cmd.stdout.readline())
+        else:
+            raise ValueError("cmd stdin was empty")
 
     def get_object_header(self, ref: str) -> Tuple[str, str, int]:
         """ Use this method to quickly examine the type and size of the object behind
@@ -1200,7 +1214,8 @@ def stream_object_data(self, ref: str) -> Tuple[str, str, int, 'Git.CatFileConte
         :note: This method is not threadsafe, you need one independent Command instance per thread to be safe !"""
         cmd = self._get_persistent_cmd("cat_file_all", "cat_file", batch=True)
         hexsha, typename, size = self.__get_object_header(cmd, ref)
-        return (hexsha, typename, size, self.CatFileContentStream(size, cmd.stdout))
+        cmd_stdout = cmd.stdout if cmd.stdout is not None else io.BytesIO()
+        return (hexsha, typename, size, self.CatFileContentStream(size, cmd_stdout))
 
     def clear_cache(self) -> 'Git':
         """Clear all kinds of internal caches to release resources.

From 48f64bbdea658fd9e0bd5d3d51c54ee6be8c05bd Mon Sep 17 00:00:00 2001
From: Yobmod <yobmod@gmail.com>
Date: Sat, 31 Jul 2021 20:06:59 +0100
Subject: [PATCH 21/23] Add final types to index/fun.py

---
 git/index/fun.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/git/index/fun.py b/git/index/fun.py
index 49e3f2c52..16ec744e2 100644
--- a/git/index/fun.py
+++ b/git/index/fun.py
@@ -251,7 +251,7 @@ def read_cache(stream: IO[bytes]) -> Tuple[int, Dict[Tuple[PathLike, int], 'Inde
     return (version, entries, extension_data, content_sha)
 
 
-def write_tree_from_cache(entries: List[IndexEntry], odb, sl: slice, si: int = 0
+def write_tree_from_cache(entries: List[IndexEntry], odb: 'GitCmdObjectDB', sl: slice, si: int = 0
                           ) -> Tuple[bytes, List['TreeCacheTup']]:
     """Create a tree from the given sorted list of entries and put the respective
     trees into the given object database

From 3c2454d20ba60e3350f3da103d5c1570ccc41c6f Mon Sep 17 00:00:00 2001
From: Yobmod <yobmod@gmail.com>
Date: Sat, 31 Jul 2021 20:10:29 +0100
Subject: [PATCH 22/23] Add final types to symbolic.py

---
 git/refs/symbolic.py | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/git/refs/symbolic.py b/git/refs/symbolic.py
index ae391c1e2..bcd3d261c 100644
--- a/git/refs/symbolic.py
+++ b/git/refs/symbolic.py
@@ -146,13 +146,13 @@ def dereference_recursive(cls, repo: 'Repo', ref_path: PathLike) -> str:
             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 = cls._get_ref_info(repo, ref_path)  # type: ignore
             if hexsha is not None:
                 return hexsha
         # END recursive dereferencing
 
     @classmethod
-    def _get_ref_info_helper(cls, repo: 'Repo', ref_path: PathLike):
+    def _get_ref_info_helper(cls, repo: 'Repo', ref_path: PathLike) -> Union[Tuple[str, None], Tuple[None, str]]:
         """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"""
@@ -191,13 +191,13 @@ def _get_ref_info_helper(cls, repo: 'Repo', ref_path: PathLike):
         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, str]]:
         """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
@@ -206,7 +206,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

From 2a350b57ce79a0e1b71623d1146c52918232e074 Mon Sep 17 00:00:00 2001
From: Yobmod <yobmod@gmail.com>
Date: Sat, 31 Jul 2021 20:18:20 +0100
Subject: [PATCH 23/23] Add final final types to symbolic.py

---
 git/refs/symbolic.py | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/git/refs/symbolic.py b/git/refs/symbolic.py
index bcd3d261c..b4a933aa7 100644
--- a/git/refs/symbolic.py
+++ b/git/refs/symbolic.py
@@ -40,7 +40,7 @@
 __all__ = ["SymbolicReference"]
 
 
-def _git_dir(repo: 'Repo', path: PathLike) -> PathLike:
+def _git_dir(repo: 'Repo', path: Union[PathLike, None]) -> PathLike:
     """ Find the git dir that's appropriate for the path"""
     name = f"{path}"
     if name in ['HEAD', 'ORIG_HEAD', 'FETCH_HEAD', 'index', 'logs']:
@@ -140,26 +140,28 @@ 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: 'Repo', ref_path: Union[PathLike, None]) -> 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)  # type: ignore
+            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, str]]:
+    def _get_ref_info_helper(cls, repo: 'Repo', ref_path: Union[PathLike, None]
+                             ) -> Union[Tuple[str, None], Tuple[None, str]]:
         """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[None, List[str], Tuple[str, str]] = None
         repodir = _git_dir(repo, ref_path)
         try:
-            with open(os.path.join(repodir, ref_path), 'rt', encoding='UTF-8') as fp:
+            with open(os.path.join(repodir, str(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
@@ -191,7 +193,7 @@ 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, str]]:
+    def _get_ref_info(cls, repo: 'Repo', ref_path: Union[PathLike, None]) -> Union[Tuple[str, None], Tuple[None, str]]:
         """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"""