@@ -104,6 +104,7 @@ def write(self):
104
104
return rval
105
105
# END overridden methods
106
106
107
+
107
108
class Submodule (base .IndexObject , Iterable , Traversable ):
108
109
"""Implements access to a git submodule. They are special in that their sha
109
110
represents a commit in the submodule's repository which is to be checked out
@@ -172,7 +173,10 @@ def _get_intermediate_items(self, item):
172
173
173
174
def __eq__ (self , other ):
174
175
"""Compare with another submodule"""
175
- return self .path == other .path and self .url == other .url and super (Submodule , self ).__eq__ (other )
176
+ # we may only compare by name as this should be the ID they are hashed with
177
+ # Otherwise this type wouldn't be hashable
178
+ # return self.path == other.path and self.url == other.url and super(Submodule, self).__eq__(other)
179
+ return self ._name == other ._name
176
180
177
181
def __ne__ (self , other ):
178
182
"""Compare with another submodule for inequality"""
@@ -185,6 +189,9 @@ def __hash__(self):
185
189
def __str__ (self ):
186
190
return self ._name
187
191
192
+ def __repr__ (self ):
193
+ return "git.%s(name=%s, path=%s, url=%s, branch=%s)" % (type (self ).__name__ , self ._name , self .path , self .url , self .branch )
194
+
188
195
@classmethod
189
196
def _config_parser (cls , repo , parent_commit , read_only ):
190
197
""":return: Config Parser constrained to our submodule in read or write mode
@@ -459,23 +466,29 @@ def update(self, recursive=False, init=True, to_latest_revision=False):
459
466
return self
460
467
461
468
@unbare_repo
462
- def move (self , module_path , module_only = False ):
469
+ def move (self , module_path , configuration = True , module = True ):
463
470
"""Move the submodule to a another module path. This involves physically moving
464
471
the repository at our current path, changing the configuration, as well as
465
472
adjusting our index entry accordingly.
466
473
:param module_path: the path to which to move our module, given as
467
474
repository-relative path. Intermediate directories will be created
468
475
accordingly. If the path already exists, it must be empty.
469
476
Trailling (back)slashes are removed automatically
470
- :param module_only: if True, only the repository managed by this submodule
477
+ :param configuration: if True, the configuration will be adjusted to let
478
+ the submodule point to the given path.
479
+ :param module: if True, the repository managed by this submodule
471
480
will be moved, not the configuration. This will effectively
472
481
leave your repository in an inconsistent state unless the configuration
473
- and index already point to the target location.
482
+ and index already point to the target location.
474
483
:return: self
475
484
:raise ValueError: if the module path existed and was not empty, or was a file
476
485
:note: Currently the method is not atomic, and it could leave the repository
477
486
in an inconsistent state if a sub-step fails for some reason
478
487
"""
488
+ if module + configuration < 1 :
489
+ raise ValueError ("You must specify to move at least the module or the configuration of the submodule" )
490
+ #END handle input
491
+
479
492
module_path = to_native_path_linux (module_path )
480
493
if module_path .endswith ('/' ):
481
494
module_path = module_path [:- 1 ]
@@ -494,54 +507,64 @@ def move(self, module_path, module_only=False):
494
507
index = self .repo .index
495
508
tekey = index .entry_key (module_path , 0 )
496
509
# if the target item already exists, fail
497
- if not module_only and tekey in index .entries :
510
+ if configuration and tekey in index .entries :
498
511
raise ValueError ("Index entry for target path did alredy exist" )
499
512
#END handle index key already there
500
513
501
514
# remove existing destination
502
- if os .path .exists (dest_path ):
503
- if len (os .listdir (dest_path )):
504
- raise ValueError ("Destination module directory was not empty" )
505
- #END handle non-emptyness
506
-
507
- if os .path .islink (dest_path ):
508
- os .remove (dest_path )
515
+ if module :
516
+ if os .path .exists (dest_path ):
517
+ if len (os .listdir (dest_path )):
518
+ raise ValueError ("Destination module directory was not empty" )
519
+ #END handle non-emptyness
520
+
521
+ if os .path .islink (dest_path ):
522
+ os .remove (dest_path )
523
+ else :
524
+ os .rmdir (dest_path )
525
+ #END handle link
509
526
else :
510
- os .rmdir (dest_path )
511
- #END handle link
512
- else :
513
- # recreate parent directories
514
- # NOTE: renames() does that now
515
- pass
516
- #END handle existance
527
+ # recreate parent directories
528
+ # NOTE: renames() does that now
529
+ pass
530
+ #END handle existance
531
+ # END handle module
517
532
518
533
# move the module into place if possible
519
534
cur_path = self .abspath
520
- if os .path .exists (cur_path ):
535
+ renamed_module = False
536
+ if module and os .path .exists (cur_path ):
521
537
os .renames (cur_path , dest_path )
538
+ renamed_module = True
522
539
#END move physical module
523
540
524
- # NOTE: from now on, we would have to undo the rename !
525
541
526
542
# rename the index entry - have to manipulate the index directly as
527
543
# git-mv cannot be used on submodules ... yeah
528
- if not module_only :
529
- try :
530
- ekey = index .entry_key (self .path , 0 )
531
- entry = index .entries [ekey ]
532
- del (index .entries [ekey ])
533
- nentry = git .IndexEntry (entry [:3 ]+ (module_path ,)+ entry [4 :])
534
- index .entries [tekey ] = nentry
535
- except KeyError :
536
- raise ValueError ("Submodule's entry at %r did not exist" % (self .path ))
537
- #END handle submodule doesn't exist
538
-
539
- # update configuration
540
- writer = self .config_writer (index = index ) # auto-write
541
- writer .set_value ('path' , module_path )
542
- self .path = module_path
543
- del (writer )
544
- # END handle module_only
544
+ try :
545
+ if configuration :
546
+ try :
547
+ ekey = index .entry_key (self .path , 0 )
548
+ entry = index .entries [ekey ]
549
+ del (index .entries [ekey ])
550
+ nentry = git .IndexEntry (entry [:3 ]+ (module_path ,)+ entry [4 :])
551
+ index .entries [tekey ] = nentry
552
+ except KeyError :
553
+ raise InvalidGitRepositoryError ("Submodule's entry at %r did not exist" % (self .path ))
554
+ #END handle submodule doesn't exist
555
+
556
+ # update configuration
557
+ writer = self .config_writer (index = index ) # auto-write
558
+ writer .set_value ('path' , module_path )
559
+ self .path = module_path
560
+ del (writer )
561
+ # END handle configuration flag
562
+ except Exception :
563
+ if renamed_module :
564
+ os .renames (dest_path , cur_path )
565
+ # END undo module renaming
566
+ raise
567
+ #END handle undo rename
545
568
546
569
return self
547
570
@@ -917,7 +940,7 @@ def update(self, previous_commit=None, recursive=True, force_remove=False, init=
917
940
##################
918
941
cur_commit = repo .head .commit
919
942
if previous_commit is None :
920
- symref = SymbolicReference ( repo , SymbolicReference . to_full_path ( 'ORIG_HEAD' ) )
943
+ symref = repo . head . orig_head ( )
921
944
try :
922
945
previous_commit = symref .commit
923
946
except Exception :
@@ -936,8 +959,8 @@ def update(self, previous_commit=None, recursive=True, force_remove=False, init=
936
959
937
960
938
961
# HANDLE REMOVALS
939
- psms = type ( self ) .list_items (repo , parent_commit = previous_commit )
940
- sms = self .children ( )
962
+ psms = self .list_items (repo , parent_commit = previous_commit )
963
+ sms = self .list_items ( self . module () )
941
964
spsms = set (psms )
942
965
ssms = set (sms )
943
966
@@ -958,7 +981,7 @@ def update(self, previous_commit=None, recursive=True, force_remove=False, init=
958
981
959
982
if sm .path != psm .path and psm .module_exists ():
960
983
# move the module to the new path
961
- psm .move (sm .path , module_only = True )
984
+ psm .move (sm .path , module = True , configuration = False )
962
985
# END handle path changes
963
986
964
987
if sm .module_exists ():
@@ -970,26 +993,39 @@ def update(self, previous_commit=None, recursive=True, force_remove=False, init=
970
993
nn = '__new_origin__'
971
994
smm = sm .module ()
972
995
rmts = smm .remotes
973
- assert nn not in rmts
974
- smr = smm .create_remote (nn , sm .url )
975
- srm .fetch ()
976
-
977
- # now delete the changed one
978
- orig_name = None
979
- for remote in rmts :
980
- if remote .url == psm .url :
981
- orig_name = remote .name
982
- smm .delete_remote (remote )
983
- break
984
- # END if urls match
985
- # END for each remote
986
996
987
- # rename the new remote back to what it was
988
- # if we have not found any remote with the original url
989
- # we may not have a name. This is a special case,
990
- # and its okay to fail her
991
- assert orig_name is not None , "Couldn't find original remote-repo at url %r" % psm .url
992
- smr .rename (orig_name )
997
+ # don't do anything if we already have the url we search in place
998
+ if len ([r for r in rmts if r .url == sm .url ]) == 0 :
999
+ assert nn not in [r .name for r in rmts ]
1000
+ smr = smm .create_remote (nn , sm .url )
1001
+ smr .fetch ()
1002
+
1003
+ # now delete the changed one
1004
+ orig_name = None
1005
+ for remote in rmts :
1006
+ if remote .url == psm .url :
1007
+ orig_name = remote .name
1008
+ smm .delete_remote (remote )
1009
+ break
1010
+ # END if urls match
1011
+ # END for each remote
1012
+
1013
+ # if we didn't find a matching remote, but have exactly one,
1014
+ # we can safely use this one
1015
+ if len (rmts ) == 1 :
1016
+ orig_name = rmts [0 ].name
1017
+ smm .delete_remote (rmts [0 ])
1018
+ else :
1019
+ # if we have not found any remote with the original url
1020
+ # we may not have a name. This is a special case,
1021
+ # and its okay to fail here
1022
+ # Alternatively we could just generate a unique name
1023
+ raise InvalidGitRepositoryError ("Couldn't find original remote-repo at url %r" % psm .url )
1024
+ # END only one remove
1025
+
1026
+ # rename the new remote back to what it was
1027
+ smr .rename (orig_name )
1028
+ # END skip remote handling if new url already exists in module
993
1029
# END handle url
994
1030
995
1031
if sm .branch != psm .branch :
@@ -1020,11 +1056,13 @@ def update(self, previous_commit=None, recursive=True, force_remove=False, init=
1020
1056
# FINALLY UPDATE ALL ACTUAL SUBMODULES
1021
1057
##########################################
1022
1058
for sm in sms :
1059
+ # update the submodule using the default method
1023
1060
sm .update (recursive = True , init = init , to_latest_revision = to_latest_revision )
1024
1061
1025
1062
# update recursively depth first - question is which inconsitent
1026
1063
# state will be better in case it fails somewhere. Defective branch
1027
- # or defective depth
1064
+ # or defective depth. The RootSubmodule type will never process itself,
1065
+ # which was done in the previous expression
1028
1066
if recursive :
1029
1067
type (cls )(sm .module ()).update (recursive = True , force_remove = force_remove ,
1030
1068
init = init , to_latest_revision = to_latest_revision )
0 commit comments