4747 find_first_remote_branch
4848)
4949
50+ from git .repo import Repo
5051
5152# typing ----------------------------------------------------------------------
52- from typing import Dict , TYPE_CHECKING
53+ from typing import Callable , Dict , Mapping , Sequence , TYPE_CHECKING
5354from typing import Any , Iterator , Union
5455
55- from git .types import Commit_ish , PathLike
56+ from git .types import Commit_ish , PathLike , TBD
5657
5758if TYPE_CHECKING :
58- from git .repo import Repo
59+ from git .index import IndexFile
5960
6061
6162# -----------------------------------------------------------------------------
@@ -131,14 +132,14 @@ def __init__(self, repo: 'Repo', binsha: bytes,
131132 if url is not None :
132133 self ._url = url
133134 if branch_path is not None :
134- assert isinstance (branch_path , str )
135+ # assert isinstance(branch_path, str)
135136 self ._branch_path = branch_path
136137 if name is not None :
137138 self ._name = name
138139
139140 def _set_cache_ (self , attr : str ) -> None :
140141 if attr in ('path' , '_url' , '_branch_path' ):
141- reader = self .config_reader ()
142+ reader : SectionConstraint = self .config_reader ()
142143 # default submodule values
143144 try :
144145 self .path = reader .get ('path' )
@@ -226,7 +227,7 @@ def _config_parser(cls, repo: 'Repo',
226227
227228 return SubmoduleConfigParser (fp_module , read_only = read_only )
228229
229- def _clear_cache (self ):
230+ def _clear_cache (self ) -> None :
230231 # clear the possibly changed values
231232 for name in self ._cache_attrs :
232233 try :
@@ -246,7 +247,7 @@ def _sio_modules(cls, parent_commit: Commit_ish) -> BytesIO:
246247 def _config_parser_constrained (self , read_only : bool ) -> SectionConstraint :
247248 """:return: Config Parser constrained to our submodule in read or write mode"""
248249 try :
249- pc = self .parent_commit
250+ pc : Union [ 'Commit_ish' , None ] = self .parent_commit
250251 except ValueError :
251252 pc = None
252253 # end handle empty parent repository
@@ -255,10 +256,12 @@ def _config_parser_constrained(self, read_only: bool) -> SectionConstraint:
255256 return SectionConstraint (parser , sm_section (self .name ))
256257
257258 @classmethod
258- def _module_abspath (cls , parent_repo , path , name ) :
259+ def _module_abspath (cls , parent_repo : 'Repo' , path : PathLike , name : str ) -> PathLike :
259260 if cls ._need_gitfile_submodules (parent_repo .git ):
260261 return osp .join (parent_repo .git_dir , 'modules' , name )
261- return osp .join (parent_repo .working_tree_dir , path )
262+ if parent_repo .working_tree_dir :
263+ return osp .join (parent_repo .working_tree_dir , path )
264+ raise NotADirectoryError ()
262265 # end
263266
264267 @classmethod
@@ -286,15 +289,15 @@ def _clone_repo(cls, repo, url, path, name, **kwargs):
286289 return clone
287290
288291 @classmethod
289- def _to_relative_path (cls , parent_repo , path ) :
292+ def _to_relative_path (cls , parent_repo : 'Repo' , path : PathLike ) -> PathLike :
290293 """:return: a path guaranteed to be relative to the given parent - repository
291294 :raise ValueError: if path is not contained in the parent repository's working tree"""
292295 path = to_native_path_linux (path )
293296 if path .endswith ('/' ):
294297 path = path [:- 1 ]
295298 # END handle trailing slash
296299
297- if osp .isabs (path ):
300+ if osp .isabs (path ) and parent_repo . working_tree_dir :
298301 working_tree_linux = to_native_path_linux (parent_repo .working_tree_dir )
299302 if not path .startswith (working_tree_linux ):
300303 raise ValueError ("Submodule checkout path '%s' needs to be within the parents repository at '%s'"
@@ -308,7 +311,7 @@ def _to_relative_path(cls, parent_repo, path):
308311 return path
309312
310313 @classmethod
311- def _write_git_file_and_module_config (cls , working_tree_dir , module_abspath ) :
314+ def _write_git_file_and_module_config (cls , working_tree_dir : PathLike , module_abspath : PathLike ) -> None :
312315 """Writes a .git file containing a(preferably) relative path to the actual git module repository.
313316 It is an error if the module_abspath cannot be made into a relative path, relative to the working_tree_dir
314317 :note: will overwrite existing files !
@@ -335,7 +338,8 @@ def _write_git_file_and_module_config(cls, working_tree_dir, module_abspath):
335338
336339 @classmethod
337340 def add (cls , repo : 'Repo' , name : str , path : PathLike , url : Union [str , None ] = None ,
338- branch = None , no_checkout : bool = False , depth = None , env = None , clone_multi_options = None
341+ branch : Union [str , None ] = None , no_checkout : bool = False , depth : Union [int , None ] = None ,
342+ env : Mapping [str , str ] = None , clone_multi_options : Union [Sequence [TBD ], None ] = None
339343 ) -> 'Submodule' :
340344 """Add a new submodule to the given repository. This will alter the index
341345 as well as the .gitmodules file, but will not create a new commit.
@@ -391,7 +395,7 @@ def add(cls, repo: 'Repo', name: str, path: PathLike, url: Union[str, None] = No
391395 if sm .exists ():
392396 # reretrieve submodule from tree
393397 try :
394- sm = repo .head .commit .tree [path ] # type: ignore
398+ sm = repo .head .commit .tree [str ( path )]
395399 sm ._name = name
396400 return sm
397401 except KeyError :
@@ -414,7 +418,8 @@ def add(cls, repo: 'Repo', name: str, path: PathLike, url: Union[str, None] = No
414418 # END check url
415419 # END verify urls match
416420
417- mrepo = None
421+ mrepo : Union [Repo , None ] = None
422+
418423 if url is None :
419424 if not has_module :
420425 raise ValueError ("A URL was not given and a repository did not exist at %s" % path )
@@ -427,7 +432,7 @@ def add(cls, repo: 'Repo', name: str, path: PathLike, url: Union[str, None] = No
427432 url = urls [0 ]
428433 else :
429434 # clone new repo
430- kwargs : Dict [str , Union [bool , int ]] = {'n' : no_checkout }
435+ kwargs : Dict [str , Union [bool , int , Sequence [ TBD ] ]] = {'n' : no_checkout }
431436 if not branch_is_default :
432437 kwargs ['b' ] = br .name
433438 # END setup checkout-branch
@@ -451,6 +456,8 @@ def add(cls, repo: 'Repo', name: str, path: PathLike, url: Union[str, None] = No
451456 # otherwise there is a '-' character in front of the submodule listing
452457 # a38efa84daef914e4de58d1905a500d8d14aaf45 mymodule (v0.9.0-1-ga38efa8)
453458 # -a38efa84daef914e4de58d1905a500d8d14aaf45 submodules/intermediate/one
459+ writer : Union [GitConfigParser , SectionConstraint ]
460+
454461 with sm .repo .config_writer () as writer :
455462 writer .set_value (sm_section (name ), 'url' , url )
456463
@@ -467,13 +474,15 @@ def add(cls, repo: 'Repo', name: str, path: PathLike, url: Union[str, None] = No
467474 sm ._branch_path = br .path
468475
469476 # we deliberately assume that our head matches our index !
470- sm .binsha = mrepo .head .commit .binsha
477+ sm .binsha = mrepo .head .commit .binsha # type: ignore
471478 index .add ([sm ], write = True )
472479
473480 return sm
474481
475- def update (self , recursive = False , init = True , to_latest_revision = False , progress = None , dry_run = False ,
476- force = False , keep_going = False , env = None , clone_multi_options = None ):
482+ def update (self , recursive : bool = False , init : bool = True , to_latest_revision : bool = False ,
483+ progress : Union ['UpdateProgress' , None ] = None , dry_run : bool = False ,
484+ force : bool = False , keep_going : bool = False , env : Mapping [str , str ] = None ,
485+ clone_multi_options : Union [Sequence [TBD ], None ] = None ):
477486 """Update the repository of this submodule to point to the checkout
478487 we point at with the binsha of this instance.
479488
@@ -580,6 +589,7 @@ def update(self, recursive=False, init=True, to_latest_revision=False, progress=
580589 if not dry_run :
581590 # see whether we have a valid branch to checkout
582591 try :
592+ assert isinstance (mrepo , Repo )
583593 # find a remote which has our branch - we try to be flexible
584594 remote_branch = find_first_remote_branch (mrepo .remotes , self .branch_name )
585595 local_branch = mkhead (mrepo , self .branch_path )
@@ -640,7 +650,7 @@ def update(self, recursive=False, init=True, to_latest_revision=False, progress=
640650 may_reset = True
641651 if mrepo .head .commit .binsha != self .NULL_BIN_SHA :
642652 base_commit = mrepo .merge_base (mrepo .head .commit , hexsha )
643- if len (base_commit ) == 0 or base_commit [0 ].hexsha == hexsha :
653+ if len (base_commit ) == 0 or ( base_commit [0 ] is not None and base_commit [ 0 ] .hexsha == hexsha ) :
644654 if force :
645655 msg = "Will force checkout or reset on local branch that is possibly in the future of"
646656 msg += "the commit it will be checked out to, effectively 'forgetting' new commits"
@@ -807,7 +817,8 @@ def move(self, module_path, configuration=True, module=True):
807817 return self
808818
809819 @unbare_repo
810- def remove (self , module = True , force = False , configuration = True , dry_run = False ):
820+ def remove (self , module : bool = True , force : bool = False ,
821+ configuration : bool = True , dry_run : bool = False ) -> 'Submodule' :
811822 """Remove this submodule from the repository. This will remove our entry
812823 from the .gitmodules file and the entry in the .git / config file.
813824
@@ -861,7 +872,7 @@ def remove(self, module=True, force=False, configuration=True, dry_run=False):
861872 # TODO: If we run into permission problems, we have a highly inconsistent
862873 # state. Delete the .git folders last, start with the submodules first
863874 mp = self .abspath
864- method = None
875+ method : Union [ None , Callable [[ PathLike ], None ]] = None
865876 if osp .islink (mp ):
866877 method = os .remove
867878 elif osp .isdir (mp ):
@@ -914,7 +925,7 @@ def remove(self, module=True, force=False, configuration=True, dry_run=False):
914925 import gc
915926 gc .collect ()
916927 try :
917- rmtree (wtd )
928+ rmtree (str ( wtd ) )
918929 except Exception as ex :
919930 if HIDE_WINDOWS_KNOWN_ERRORS :
920931 raise SkipTest ("FIXME: fails with: PermissionError\n {}" .format (ex )) from ex
@@ -928,7 +939,7 @@ def remove(self, module=True, force=False, configuration=True, dry_run=False):
928939 rmtree (git_dir )
929940 except Exception as ex :
930941 if HIDE_WINDOWS_KNOWN_ERRORS :
931- raise SkipTest ("FIXME: fails with: PermissionError\n %s" , ex ) from ex
942+ raise SkipTest (f "FIXME: fails with: PermissionError\n { ex } " ) from ex
932943 else :
933944 raise
934945 # end handle separate bare repository
@@ -952,6 +963,8 @@ def remove(self, module=True, force=False, configuration=True, dry_run=False):
952963
953964 # now git config - need the config intact, otherwise we can't query
954965 # information anymore
966+ writer : Union [GitConfigParser , SectionConstraint ]
967+
955968 with self .repo .config_writer () as writer :
956969 writer .remove_section (sm_section (self .name ))
957970
@@ -961,7 +974,7 @@ def remove(self, module=True, force=False, configuration=True, dry_run=False):
961974
962975 return self
963976
964- def set_parent_commit (self , commit : Union [Commit_ish , None ], check = True ):
977+ def set_parent_commit (self , commit : Union [Commit_ish , None ], check : bool = True ) -> 'Submodule' :
965978 """Set this instance to use the given commit whose tree is supposed to
966979 contain the .gitmodules blob.
967980
@@ -1009,7 +1022,7 @@ def set_parent_commit(self, commit: Union[Commit_ish, None], check=True):
10091022 return self
10101023
10111024 @unbare_repo
1012- def config_writer (self , index = None , write = True ):
1025+ def config_writer (self , index : Union [ 'IndexFile' , None ] = None , write : bool = True ) -> SectionConstraint :
10131026 """:return: a config writer instance allowing you to read and write the data
10141027 belonging to this submodule into the .gitmodules file.
10151028
@@ -1030,7 +1043,7 @@ def config_writer(self, index=None, write=True):
10301043 return writer
10311044
10321045 @unbare_repo
1033- def rename (self , new_name ) :
1046+ def rename (self , new_name : str ) -> 'Submodule' :
10341047 """Rename this submodule
10351048 :note: This method takes care of renaming the submodule in various places, such as
10361049
@@ -1065,13 +1078,14 @@ def rename(self, new_name):
10651078 destination_module_abspath = self ._module_abspath (self .repo , self .path , new_name )
10661079 source_dir = mod .git_dir
10671080 # Let's be sure the submodule name is not so obviously tied to a directory
1068- if destination_module_abspath .startswith (mod .git_dir ):
1081+ if str ( destination_module_abspath ) .startswith (str ( mod .git_dir ) ):
10691082 tmp_dir = self ._module_abspath (self .repo , self .path , str (uuid .uuid4 ()))
10701083 os .renames (source_dir , tmp_dir )
10711084 source_dir = tmp_dir
10721085 # end handle self-containment
10731086 os .renames (source_dir , destination_module_abspath )
1074- self ._write_git_file_and_module_config (mod .working_tree_dir , destination_module_abspath )
1087+ if mod .working_tree_dir :
1088+ self ._write_git_file_and_module_config (mod .working_tree_dir , destination_module_abspath )
10751089 # end move separate git repository
10761090
10771091 return self
@@ -1081,7 +1095,7 @@ def rename(self, new_name):
10811095 #{ Query Interface
10821096
10831097 @unbare_repo
1084- def module (self ):
1098+ def module (self ) -> 'Repo' :
10851099 """:return: Repo instance initialized from the repository at our submodule path
10861100 :raise InvalidGitRepositoryError: if a repository was not available. This could
10871101 also mean that it was not yet initialized"""
@@ -1098,7 +1112,7 @@ def module(self):
10981112 raise InvalidGitRepositoryError ("Repository at %r was not yet checked out" % module_checkout_abspath )
10991113 # END handle exceptions
11001114
1101- def module_exists (self ):
1115+ def module_exists (self ) -> bool :
11021116 """:return: True if our module exists and is a valid git repository. See module() method"""
11031117 try :
11041118 self .module ()
@@ -1107,7 +1121,7 @@ def module_exists(self):
11071121 return False
11081122 # END handle exception
11091123
1110- def exists (self ):
1124+ def exists (self ) -> bool :
11111125 """
11121126 :return: True if the submodule exists, False otherwise. Please note that
11131127 a submodule may exist ( in the .gitmodules file) even though its module
@@ -1148,34 +1162,34 @@ def branch(self):
11481162 return mkhead (self .module (), self ._branch_path )
11491163
11501164 @property
1151- def branch_path (self ):
1165+ def branch_path (self ) -> PathLike :
11521166 """
11531167 :return: full(relative) path as string to the branch we would checkout
11541168 from the remote and track"""
11551169 return self ._branch_path
11561170
11571171 @property
1158- def branch_name (self ):
1172+ def branch_name (self ) -> str :
11591173 """:return: the name of the branch, which is the shortest possible branch name"""
11601174 # use an instance method, for this we create a temporary Head instance
11611175 # which uses a repository that is available at least ( it makes no difference )
11621176 return git .Head (self .repo , self ._branch_path ).name
11631177
11641178 @property
1165- def url (self ):
1179+ def url (self ) -> str :
11661180 """:return: The url to the repository which our module - repository refers to"""
11671181 return self ._url
11681182
11691183 @property
1170- def parent_commit (self ):
1184+ def parent_commit (self ) -> 'Commit_ish' :
11711185 """:return: Commit instance with the tree containing the .gitmodules file
11721186 :note: will always point to the current head's commit if it was not set explicitly"""
11731187 if self ._parent_commit is None :
11741188 return self .repo .commit ()
11751189 return self ._parent_commit
11761190
11771191 @property
1178- def name (self ):
1192+ def name (self ) -> str :
11791193 """:return: The name of this submodule. It is used to identify it within the
11801194 .gitmodules file.
11811195 :note: by default, the name is the path at which to find the submodule, but
0 commit comments