10
10
import os
11
11
import sys
12
12
import weakref
13
+ import shutil
13
14
14
15
__all__ = ("Submodule" , "RootModule" )
15
16
16
17
#{ Utilities
17
18
18
- def sm_section (path ):
19
+ def sm_section (name ):
19
20
""":return: section title used in .gitmodules configuration file"""
20
- return 'submodule "%s"' % path
21
+ return 'submodule "%s"' % name
21
22
22
23
def sm_name (section ):
23
24
""":return: name of the submodule as parsed from the section name"""
@@ -223,6 +224,7 @@ def update(self, recursive=False, init=True, to_latest_revision=False):
223
224
if the remote repository had a master branch, or of the 'branch' option
224
225
was specified for this submodule and the branch existed remotely
225
226
:note: does nothing in bare repositories
227
+ :note: method is definitely not atomic if recurisve is True
226
228
:return: self"""
227
229
if self .repo .bare :
228
230
return self
@@ -329,6 +331,111 @@ def update(self, recursive=False, init=True, to_latest_revision=False):
329
331
330
332
return self
331
333
334
+ def remove (self , module = True , force = False , configuration = True , dry_run = False ):
335
+ """Remove this submodule from the repository. This will remove our entry
336
+ from the .gitmodules file and the entry in the .git/config file.
337
+ :param module: If True, the module we point to will be deleted
338
+ as well. If the module is currently on a commit which is not part
339
+ of any branch in the remote, if the currently checked out branch
340
+ is ahead of its tracking branch, if you have modifications in the
341
+ working tree, or untracked files,
342
+ In case the removal of the repository fails for these reasons, the
343
+ submodule status will not have been altered.
344
+ If this submodule has child-modules on its own, these will be deleted
345
+ prior to touching the own module.
346
+ :param force: Enforces the deletion of the module even though it contains
347
+ modifications. This basically enforces a brute-force file system based
348
+ deletion.
349
+ :param configuration: if True, the submodule is deleted from the configuration,
350
+ otherwise it isn't. Although this should be enabled most of the times,
351
+ this flag enables you to safely delete the repository of your submodule.
352
+ :param dry_run: if True, we will not actually do anything, but throw the errors
353
+ we would usually throw
354
+ :note: doesn't work in bare repositories
355
+ :raise InvalidGitRepositoryError: thrown if the repository cannot be deleted
356
+ :raise OSError: if directories or files could not be removed"""
357
+ if self .repo .bare :
358
+ raise InvalidGitRepositoryError ("Cannot delete a submodule in bare repository" )
359
+ # END handle bare mode
360
+
361
+ if not (module + configuration ):
362
+ raise ValueError ("Need to specify to delete at least the module, or the configuration" )
363
+ # END handle params
364
+
365
+ # DELETE MODULE REPOSITORY
366
+ ##########################
367
+ if module and self .module_exists ():
368
+ if force :
369
+ # take the fast lane and just delete everything in our module path
370
+ # TODO: If we run into permission problems, we have a highly inconsistent
371
+ # state. Delete the .git folders last, start with the submodules first
372
+ mp = self .module_path ()
373
+ method = None
374
+ if os .path .islink (mp ):
375
+ method = os .remove
376
+ elif os .path .isdir (mp ):
377
+ method = shutil .rmtree
378
+ elif os .path .exists (mp ):
379
+ raise AssertionError ("Cannot forcibly delete repository as it was neither a link, nor a directory" )
380
+ #END handle brutal deletion
381
+ if not dry_run :
382
+ assert method
383
+ method (mp )
384
+ #END apply deletion method
385
+ else :
386
+ # verify we may delete our module
387
+ mod = self .module ()
388
+ if mod .is_dirty (untracked_files = True ):
389
+ raise InvalidGitRepositoryError ("Cannot delete module at %s with any modifications, unless force is specified" % mod .working_tree_dir )
390
+ # END check for dirt
391
+
392
+ # figure out whether we have new commits compared to the remotes
393
+ # NOTE: If the user pulled all the time, the remote heads might
394
+ # not have been updated, so commits coming from the remote look
395
+ # as if they come from us. But we stay strictly read-only and
396
+ # don't fetch beforhand.
397
+ for remote in mod .remotes :
398
+ num_branches_with_new_commits = 0
399
+ rrefs = remote .refs
400
+ for rref in rrefs :
401
+ num_branches_with_new_commits = len (mod .git .cherry (rref )) != 0
402
+ # END for each remote ref
403
+ # not a single remote branch contained all our commits
404
+ if num_branches_with_new_commits == len (rrefs ):
405
+ raise InvalidGitRepositoryError ("Cannot delete module at %s as there are new commits" % mod .working_tree_dir )
406
+ # END handle new commits
407
+ # END for each remote
408
+
409
+ # gently remove all submodule repositories
410
+ for sm in self .children ():
411
+ sm .remove (module = True , force = False , configuration = False , dry_run = dry_run )
412
+ # END for each child-submodule
413
+
414
+ # finally delete our own submodule
415
+ if not dry_run :
416
+ shutil .rmtree (mod .working_tree_dir )
417
+ # END delete tree if possible
418
+ # END handle force
419
+ # END handle module deletion
420
+
421
+ # DELETE CONFIGURATION
422
+ ######################
423
+ if configuration and not dry_run :
424
+ # first the index-entry
425
+ index = self .repo .index
426
+ try :
427
+ del (index .entries [index .entry_key (self .path , 0 )])
428
+ except KeyError :
429
+ pass
430
+ #END delete entry
431
+ index .write ()
432
+
433
+ # now git config - need the config intact, otherwise we can't query
434
+ # inforamtion anymore
435
+ self .repo .config_writer ().remove_section (sm_section (self .name ))
436
+ self .config_writer ().remove_section ()
437
+ # END delete configuration
438
+
332
439
def set_parent_commit (self , commit , check = True ):
333
440
"""Set this instance to use the given commit whose tree is supposed to
334
441
contain the .gitmodules blob.
@@ -410,10 +517,23 @@ def module_exists(self):
410
517
try :
411
518
self .module ()
412
519
return True
413
- except InvalidGitRepositoryError :
520
+ except Exception :
414
521
return False
415
522
# END handle exception
416
523
524
+ def exists (self ):
525
+ """:return: True if the submodule exists, False otherwise. Please note that
526
+ a submodule may exist (in the .gitmodules file) even though its module
527
+ doesn't exist"""
528
+ self ._clear_cache ()
529
+ try :
530
+ self .path
531
+ return True
532
+ except Exception :
533
+ # we raise if the path cannot be restored from configuration
534
+ return False
535
+ # END handle exceptions
536
+
417
537
@property
418
538
def branch (self ):
419
539
""":return: The branch name that we are to checkout"""
0 commit comments