@@ -28,6 +28,19 @@ def sm_name(section):
28
28
def mkhead (repo , path ):
29
29
""":return: New branch/head instance"""
30
30
return git .Head (repo , git .Head .to_full_path (path ))
31
+
32
+ def unbare_repo (func ):
33
+ """Methods with this decorator raise InvalidGitRepositoryError if they
34
+ encounter a bare repository"""
35
+ def wrapper (self , * args , ** kwargs ):
36
+ if self .repo .bare :
37
+ raise InvalidGitRepositoryError ("Method '%s' cannot operate on bare repositories" % func .__name__ )
38
+ #END bare method
39
+ return func (self , * args , ** kwargs )
40
+ # END wrapper
41
+ wrapper .__name__ = func .__name__
42
+ return wrapper
43
+
31
44
#} END utilities
32
45
33
46
@@ -39,10 +52,14 @@ class SubmoduleConfigParser(GitConfigParser):
39
52
with the new data, if we have written into a stream. Otherwise it will
40
53
add the local file to the index to make it correspond with the working tree.
41
54
Additionally, the cache must be cleared
55
+
56
+ Please note that no mutating method will work in bare mode
42
57
"""
43
58
44
59
def __init__ (self , * args , ** kwargs ):
45
60
self ._smref = None
61
+ self ._index = None
62
+ self ._auto_write = True
46
63
super (SubmoduleConfigParser , self ).__init__ (* args , ** kwargs )
47
64
48
65
#{ Interface
@@ -59,7 +76,11 @@ def flush_to_index(self):
59
76
60
77
sm = self ._smref ()
61
78
if sm is not None :
62
- sm .repo .index .add ([sm .k_modules_file ])
79
+ index = self ._index
80
+ if index is None :
81
+ index = sm .repo .index
82
+ # END handle index
83
+ index .add ([sm .k_modules_file ], write = self ._auto_write )
63
84
sm ._clear_cache ()
64
85
# END handle weakref
65
86
@@ -102,6 +123,7 @@ def __init__(self, repo, binsha, mode=None, path=None, name = None, parent_commi
102
123
:param url: The url to the remote repository which is the submodule
103
124
:param branch: Head instance to checkout when cloning the remote repository"""
104
125
super (Submodule , self ).__init__ (repo , binsha , mode , path )
126
+ self .size = 0
105
127
if parent_commit is not None :
106
128
self ._parent_commit = parent_commit
107
129
if url is not None :
@@ -113,9 +135,7 @@ def __init__(self, repo, binsha, mode=None, path=None, name = None, parent_commi
113
135
self ._name = name
114
136
115
137
def _set_cache_ (self , attr ):
116
- if attr == 'size' :
117
- raise ValueError ("Submodules do not have a size as they do not refer to anything in this repository" )
118
- elif attr == '_parent_commit' :
138
+ if attr == '_parent_commit' :
119
139
# set a default value, which is the root tree of the current head
120
140
self ._parent_commit = self .repo .commit ()
121
141
elif attr in ('path' , '_url' , '_branch' ):
@@ -235,8 +255,8 @@ def add(cls, repo, name, path, url=None, branch=None, no_checkout=False):
235
255
:note: works atomically, such that no change will be done if the repository
236
256
update fails for instance"""
237
257
if repo .bare :
238
- raise InvalidGitRepositoryError ("Cannot add a submodule to bare repositories" )
239
- #END handle bare mode
258
+ raise InvalidGitRepositoryError ("Cannot add submodules to bare repositories" )
259
+ # END handle bare repos
240
260
241
261
path = to_native_path_linux (path )
242
262
if path .endswith ('/' ):
@@ -280,7 +300,8 @@ def add(cls, repo, name, path, url=None, branch=None, no_checkout=False):
280
300
# END verify url
281
301
282
302
# update configuration and index
283
- writer = sm .config_writer ()
303
+ index = sm .repo .index
304
+ writer = sm .config_writer (index = index , write = False )
284
305
writer .set_value ('url' , url )
285
306
writer .set_value ('path' , path )
286
307
@@ -302,11 +323,10 @@ def add(cls, repo, name, path, url=None, branch=None, no_checkout=False):
302
323
pcommit = repo .head .commit
303
324
sm ._parent_commit = pcommit
304
325
sm .binsha = mrepo .head .commit .binsha
305
- repo . index .add ([sm ], write = True )
326
+ index .add ([sm ], write = True )
306
327
307
328
return sm
308
329
309
-
310
330
def update (self , recursive = False , init = True , to_latest_revision = False ):
311
331
"""Update the repository of this submodule to point to the checkout
312
332
we point at with the binsha of this instance.
@@ -426,6 +446,85 @@ def update(self, recursive=False, init=True, to_latest_revision=False):
426
446
427
447
return self
428
448
449
+ @unbare_repo
450
+ def move (self , module_path ):
451
+ """Move the submodule to a another module path. This involves physically moving
452
+ the repository at our current path, changing the configuration, as well as
453
+ adjusting our index entry accordingly.
454
+ :param module_path: the path to which to move our module, given as
455
+ repository-relative path. Intermediate directories will be created
456
+ accordingly. If the path already exists, it must be empty.
457
+ Trailling (back)slashes are removed automatically
458
+ :return: self
459
+ :raise ValueError: if the module path existed and was not empty, or was a file
460
+ :note: Currently the method is not atomic, and it could leave the repository
461
+ in an inconsistent state if a sub-step fails for some reason
462
+ """
463
+ module_path = to_native_path_linux (module_path )
464
+ if module_path .endswith ('/' ):
465
+ module_path = module_path [:- 1 ]
466
+ # END handle trailing slash
467
+
468
+ # VERIFY DESTINATION
469
+ if module_path == self .path :
470
+ return self
471
+ #END handle no change
472
+
473
+ dest_path = join_path_native (self .repo .working_tree_dir , module_path )
474
+ if os .path .isfile (dest_path ):
475
+ raise ValueError ("Cannot move repository onto a file: %s" % dest_path )
476
+ # END handle target files
477
+
478
+ # remove existing destination
479
+ if os .path .exists (dest_path ):
480
+ if len (os .listdir (dest_path )):
481
+ raise ValueError ("Destination module directory was not empty" )
482
+ #END handle non-emptyness
483
+
484
+ if os .path .islink (dest_path ):
485
+ os .remove (dest_path )
486
+ else :
487
+ os .rmdir (dest_path )
488
+ #END handle link
489
+ else :
490
+ # recreate parent directories
491
+ # NOTE: renames() does that now
492
+ pass
493
+ #END handle existance
494
+
495
+ # move the module into place if possible
496
+ cur_path = self .module_path ()
497
+ if os .path .exists (cur_path ):
498
+ os .renames (cur_path , dest_path )
499
+ #END move physical module
500
+
501
+ # NOTE: from now on, we would have to undo the rename !
502
+
503
+ # rename the index entry - have to manipulate the index directly as
504
+ # git-mv cannot be used on submodules ... yeah
505
+ index = self .repo .index
506
+ try :
507
+ ekey = index .entry_key (self .path , 0 )
508
+ entry = index .entries [ekey ]
509
+ del (index .entries [ekey ])
510
+ nentry = git .IndexEntry (entry [:3 ]+ (module_path ,)+ entry [4 :])
511
+ ekey = index .entry_key (module_path , 0 )
512
+ index .entries [ekey ] = nentry
513
+ except KeyError :
514
+ raise ValueError ("Submodule's entry at %r did not exist" % (self .path ))
515
+ #END handle submodule doesn't exist
516
+
517
+ # update configuration
518
+ writer = self .config_writer (index = index ) # auto-write
519
+ writer .set_value ('path' , module_path )
520
+ self .path = module_path
521
+ del (writer )
522
+
523
+ return self
524
+
525
+
526
+
527
+ @unbare_repo
429
528
def remove (self , module = True , force = False , configuration = True , dry_run = False ):
430
529
"""Remove this submodule from the repository. This will remove our entry
431
530
from the .gitmodules file and the entry in the .git/config file.
@@ -449,10 +548,6 @@ def remove(self, module=True, force=False, configuration=True, dry_run=False):
449
548
:note: doesn't work in bare repositories
450
549
:raise InvalidGitRepositoryError: thrown if the repository cannot be deleted
451
550
:raise OSError: if directories or files could not be removed"""
452
- if self .repo .bare :
453
- raise InvalidGitRepositoryError ("Cannot delete a submodule in bare repository" )
454
- # END handle bare mode
455
-
456
551
if not (module + configuration ):
457
552
raise ValueError ("Need to specify to delete at least the module, or the configuration" )
458
553
# END handle params
@@ -565,31 +660,37 @@ def set_parent_commit(self, commit, check=True):
565
660
566
661
return self
567
662
568
- def config_writer (self ):
663
+ @unbare_repo
664
+ def config_writer (self , index = None , write = True ):
569
665
""":return: a config writer instance allowing you to read and write the data
570
666
belonging to this submodule into the .gitmodules file.
571
667
668
+ :param index: if not None, an IndexFile instance which should be written.
669
+ defaults to the index of the Submodule's parent repository.
670
+ :param write: if True, the index will be written each time a configuration
671
+ value changes.
672
+ :note: the parameters allow for a more efficient writing of the index,
673
+ as you can pass in a modified index on your own, prevent automatic writing,
674
+ and write yourself once the whole operation is complete
572
675
:raise ValueError: if trying to get a writer on a parent_commit which does not
573
676
match the current head commit
574
677
:raise IOError: If the .gitmodules file/blob could not be read"""
575
- if self .repo .bare :
576
- raise InvalidGitRepositoryError ("Cannot change submodule configuration in a bare repository" )
577
- return self ._config_parser_constrained (read_only = False )
678
+ writer = self ._config_parser_constrained (read_only = False )
679
+ if index is not None :
680
+ writer .config ._index = index
681
+ writer .config ._auto_write = write
682
+ return writer
578
683
579
684
#} END edit interface
580
685
581
686
#{ Query Interface
582
687
688
+ @unbare_repo
583
689
def module (self ):
584
690
""":return: Repo instance initialized from the repository at our submodule path
585
691
:raise InvalidGitRepositoryError: if a repository was not available. This could
586
692
also mean that it was not yet initialized"""
587
693
# late import to workaround circular dependencies
588
-
589
- if self .repo .bare :
590
- raise InvalidGitRepositoryError ("Cannot retrieve module repository in bare parent repositories" )
591
- # END handle bare mode
592
-
593
694
module_path = self .module_path ()
594
695
try :
595
696
repo = git .Repo (module_path )
0 commit comments