47
47
find_first_remote_branch
48
48
)
49
49
50
+ from git .repo import Repo
50
51
51
52
# typing ----------------------------------------------------------------------
52
- from typing import Dict , TYPE_CHECKING
53
+ from typing import Callable , Dict , Mapping , Sequence , TYPE_CHECKING
53
54
from typing import Any , Iterator , Union
54
55
55
- from git .types import Commit_ish , PathLike
56
+ from git .types import Commit_ish , PathLike , TBD
56
57
57
58
if TYPE_CHECKING :
58
- from git .repo import Repo
59
+ from git .index import IndexFile
59
60
60
61
61
62
# -----------------------------------------------------------------------------
@@ -131,14 +132,14 @@ def __init__(self, repo: 'Repo', binsha: bytes,
131
132
if url is not None :
132
133
self ._url = url
133
134
if branch_path is not None :
134
- assert isinstance (branch_path , str )
135
+ # assert isinstance(branch_path, str)
135
136
self ._branch_path = branch_path
136
137
if name is not None :
137
138
self ._name = name
138
139
139
140
def _set_cache_ (self , attr : str ) -> None :
140
141
if attr in ('path' , '_url' , '_branch_path' ):
141
- reader = self .config_reader ()
142
+ reader : SectionConstraint = self .config_reader ()
142
143
# default submodule values
143
144
try :
144
145
self .path = reader .get ('path' )
@@ -226,7 +227,7 @@ def _config_parser(cls, repo: 'Repo',
226
227
227
228
return SubmoduleConfigParser (fp_module , read_only = read_only )
228
229
229
- def _clear_cache (self ):
230
+ def _clear_cache (self ) -> None :
230
231
# clear the possibly changed values
231
232
for name in self ._cache_attrs :
232
233
try :
@@ -246,7 +247,7 @@ def _sio_modules(cls, parent_commit: Commit_ish) -> BytesIO:
246
247
def _config_parser_constrained (self , read_only : bool ) -> SectionConstraint :
247
248
""":return: Config Parser constrained to our submodule in read or write mode"""
248
249
try :
249
- pc = self .parent_commit
250
+ pc : Union [ 'Commit_ish' , None ] = self .parent_commit
250
251
except ValueError :
251
252
pc = None
252
253
# end handle empty parent repository
@@ -255,10 +256,12 @@ def _config_parser_constrained(self, read_only: bool) -> SectionConstraint:
255
256
return SectionConstraint (parser , sm_section (self .name ))
256
257
257
258
@classmethod
258
- def _module_abspath (cls , parent_repo , path , name ) :
259
+ def _module_abspath (cls , parent_repo : 'Repo' , path : PathLike , name : str ) -> PathLike :
259
260
if cls ._need_gitfile_submodules (parent_repo .git ):
260
261
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 ()
262
265
# end
263
266
264
267
@classmethod
@@ -286,15 +289,15 @@ def _clone_repo(cls, repo, url, path, name, **kwargs):
286
289
return clone
287
290
288
291
@classmethod
289
- def _to_relative_path (cls , parent_repo , path ) :
292
+ def _to_relative_path (cls , parent_repo : 'Repo' , path : PathLike ) -> PathLike :
290
293
""":return: a path guaranteed to be relative to the given parent - repository
291
294
:raise ValueError: if path is not contained in the parent repository's working tree"""
292
295
path = to_native_path_linux (path )
293
296
if path .endswith ('/' ):
294
297
path = path [:- 1 ]
295
298
# END handle trailing slash
296
299
297
- if osp .isabs (path ):
300
+ if osp .isabs (path ) and parent_repo . working_tree_dir :
298
301
working_tree_linux = to_native_path_linux (parent_repo .working_tree_dir )
299
302
if not path .startswith (working_tree_linux ):
300
303
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):
308
311
return path
309
312
310
313
@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 :
312
315
"""Writes a .git file containing a(preferably) relative path to the actual git module repository.
313
316
It is an error if the module_abspath cannot be made into a relative path, relative to the working_tree_dir
314
317
:note: will overwrite existing files !
@@ -335,7 +338,8 @@ def _write_git_file_and_module_config(cls, working_tree_dir, module_abspath):
335
338
336
339
@classmethod
337
340
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
339
343
) -> 'Submodule' :
340
344
"""Add a new submodule to the given repository. This will alter the index
341
345
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
391
395
if sm .exists ():
392
396
# reretrieve submodule from tree
393
397
try :
394
- sm = repo .head .commit .tree [path ] # type: ignore
398
+ sm = repo .head .commit .tree [str ( path )]
395
399
sm ._name = name
396
400
return sm
397
401
except KeyError :
@@ -414,7 +418,8 @@ def add(cls, repo: 'Repo', name: str, path: PathLike, url: Union[str, None] = No
414
418
# END check url
415
419
# END verify urls match
416
420
417
- mrepo = None
421
+ mrepo : Union [Repo , None ] = None
422
+
418
423
if url is None :
419
424
if not has_module :
420
425
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
427
432
url = urls [0 ]
428
433
else :
429
434
# 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 }
431
436
if not branch_is_default :
432
437
kwargs ['b' ] = br .name
433
438
# END setup checkout-branch
@@ -451,6 +456,8 @@ def add(cls, repo: 'Repo', name: str, path: PathLike, url: Union[str, None] = No
451
456
# otherwise there is a '-' character in front of the submodule listing
452
457
# a38efa84daef914e4de58d1905a500d8d14aaf45 mymodule (v0.9.0-1-ga38efa8)
453
458
# -a38efa84daef914e4de58d1905a500d8d14aaf45 submodules/intermediate/one
459
+ writer : Union [GitConfigParser , SectionConstraint ]
460
+
454
461
with sm .repo .config_writer () as writer :
455
462
writer .set_value (sm_section (name ), 'url' , url )
456
463
@@ -467,13 +474,15 @@ def add(cls, repo: 'Repo', name: str, path: PathLike, url: Union[str, None] = No
467
474
sm ._branch_path = br .path
468
475
469
476
# 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
471
478
index .add ([sm ], write = True )
472
479
473
480
return sm
474
481
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 ):
477
486
"""Update the repository of this submodule to point to the checkout
478
487
we point at with the binsha of this instance.
479
488
@@ -580,6 +589,7 @@ def update(self, recursive=False, init=True, to_latest_revision=False, progress=
580
589
if not dry_run :
581
590
# see whether we have a valid branch to checkout
582
591
try :
592
+ assert isinstance (mrepo , Repo )
583
593
# find a remote which has our branch - we try to be flexible
584
594
remote_branch = find_first_remote_branch (mrepo .remotes , self .branch_name )
585
595
local_branch = mkhead (mrepo , self .branch_path )
@@ -640,7 +650,7 @@ def update(self, recursive=False, init=True, to_latest_revision=False, progress=
640
650
may_reset = True
641
651
if mrepo .head .commit .binsha != self .NULL_BIN_SHA :
642
652
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 ) :
644
654
if force :
645
655
msg = "Will force checkout or reset on local branch that is possibly in the future of"
646
656
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):
807
817
return self
808
818
809
819
@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' :
811
822
"""Remove this submodule from the repository. This will remove our entry
812
823
from the .gitmodules file and the entry in the .git / config file.
813
824
@@ -861,7 +872,7 @@ def remove(self, module=True, force=False, configuration=True, dry_run=False):
861
872
# TODO: If we run into permission problems, we have a highly inconsistent
862
873
# state. Delete the .git folders last, start with the submodules first
863
874
mp = self .abspath
864
- method = None
875
+ method : Union [ None , Callable [[ PathLike ], None ]] = None
865
876
if osp .islink (mp ):
866
877
method = os .remove
867
878
elif osp .isdir (mp ):
@@ -914,7 +925,7 @@ def remove(self, module=True, force=False, configuration=True, dry_run=False):
914
925
import gc
915
926
gc .collect ()
916
927
try :
917
- rmtree (wtd )
928
+ rmtree (str ( wtd ) )
918
929
except Exception as ex :
919
930
if HIDE_WINDOWS_KNOWN_ERRORS :
920
931
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):
928
939
rmtree (git_dir )
929
940
except Exception as ex :
930
941
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
932
943
else :
933
944
raise
934
945
# end handle separate bare repository
@@ -952,6 +963,8 @@ def remove(self, module=True, force=False, configuration=True, dry_run=False):
952
963
953
964
# now git config - need the config intact, otherwise we can't query
954
965
# information anymore
966
+ writer : Union [GitConfigParser , SectionConstraint ]
967
+
955
968
with self .repo .config_writer () as writer :
956
969
writer .remove_section (sm_section (self .name ))
957
970
@@ -961,7 +974,7 @@ def remove(self, module=True, force=False, configuration=True, dry_run=False):
961
974
962
975
return self
963
976
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' :
965
978
"""Set this instance to use the given commit whose tree is supposed to
966
979
contain the .gitmodules blob.
967
980
@@ -1009,7 +1022,7 @@ def set_parent_commit(self, commit: Union[Commit_ish, None], check=True):
1009
1022
return self
1010
1023
1011
1024
@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 :
1013
1026
""":return: a config writer instance allowing you to read and write the data
1014
1027
belonging to this submodule into the .gitmodules file.
1015
1028
@@ -1030,7 +1043,7 @@ def config_writer(self, index=None, write=True):
1030
1043
return writer
1031
1044
1032
1045
@unbare_repo
1033
- def rename (self , new_name ) :
1046
+ def rename (self , new_name : str ) -> 'Submodule' :
1034
1047
"""Rename this submodule
1035
1048
:note: This method takes care of renaming the submodule in various places, such as
1036
1049
@@ -1065,13 +1078,14 @@ def rename(self, new_name):
1065
1078
destination_module_abspath = self ._module_abspath (self .repo , self .path , new_name )
1066
1079
source_dir = mod .git_dir
1067
1080
# 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 ) ):
1069
1082
tmp_dir = self ._module_abspath (self .repo , self .path , str (uuid .uuid4 ()))
1070
1083
os .renames (source_dir , tmp_dir )
1071
1084
source_dir = tmp_dir
1072
1085
# end handle self-containment
1073
1086
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 )
1075
1089
# end move separate git repository
1076
1090
1077
1091
return self
@@ -1081,7 +1095,7 @@ def rename(self, new_name):
1081
1095
#{ Query Interface
1082
1096
1083
1097
@unbare_repo
1084
- def module (self ):
1098
+ def module (self ) -> 'Repo' :
1085
1099
""":return: Repo instance initialized from the repository at our submodule path
1086
1100
:raise InvalidGitRepositoryError: if a repository was not available. This could
1087
1101
also mean that it was not yet initialized"""
@@ -1098,7 +1112,7 @@ def module(self):
1098
1112
raise InvalidGitRepositoryError ("Repository at %r was not yet checked out" % module_checkout_abspath )
1099
1113
# END handle exceptions
1100
1114
1101
- def module_exists (self ):
1115
+ def module_exists (self ) -> bool :
1102
1116
""":return: True if our module exists and is a valid git repository. See module() method"""
1103
1117
try :
1104
1118
self .module ()
@@ -1107,7 +1121,7 @@ def module_exists(self):
1107
1121
return False
1108
1122
# END handle exception
1109
1123
1110
- def exists (self ):
1124
+ def exists (self ) -> bool :
1111
1125
"""
1112
1126
:return: True if the submodule exists, False otherwise. Please note that
1113
1127
a submodule may exist ( in the .gitmodules file) even though its module
@@ -1148,34 +1162,34 @@ def branch(self):
1148
1162
return mkhead (self .module (), self ._branch_path )
1149
1163
1150
1164
@property
1151
- def branch_path (self ):
1165
+ def branch_path (self ) -> PathLike :
1152
1166
"""
1153
1167
:return: full(relative) path as string to the branch we would checkout
1154
1168
from the remote and track"""
1155
1169
return self ._branch_path
1156
1170
1157
1171
@property
1158
- def branch_name (self ):
1172
+ def branch_name (self ) -> str :
1159
1173
""":return: the name of the branch, which is the shortest possible branch name"""
1160
1174
# use an instance method, for this we create a temporary Head instance
1161
1175
# which uses a repository that is available at least ( it makes no difference )
1162
1176
return git .Head (self .repo , self ._branch_path ).name
1163
1177
1164
1178
@property
1165
- def url (self ):
1179
+ def url (self ) -> str :
1166
1180
""":return: The url to the repository which our module - repository refers to"""
1167
1181
return self ._url
1168
1182
1169
1183
@property
1170
- def parent_commit (self ):
1184
+ def parent_commit (self ) -> 'Commit_ish' :
1171
1185
""":return: Commit instance with the tree containing the .gitmodules file
1172
1186
:note: will always point to the current head's commit if it was not set explicitly"""
1173
1187
if self ._parent_commit is None :
1174
1188
return self .repo .commit ()
1175
1189
return self ._parent_commit
1176
1190
1177
1191
@property
1178
- def name (self ):
1192
+ def name (self ) -> str :
1179
1193
""":return: The name of this submodule. It is used to identify it within the
1180
1194
.gitmodules file.
1181
1195
:note: by default, the name is the path at which to find the submodule, but
0 commit comments