@@ -222,6 +222,8 @@ def remote(self, name='origin'):
222222 :raise ValueError: if no remote with such a name exists"""
223223 return Remote (self , name )
224224
225+ #{ Submodules
226+
225227 @property
226228 def submodules (self ):
227229 """:return: git.IterableList(Submodule, ...) of direct submodules"""
@@ -240,7 +242,66 @@ def iter_submodules(self, *args, **kwargs):
240242 """An iterator yielding Submodule instances, see Traversable interface
241243 for a description of args and kwargs
242244 :return: Iterator"""
243- return RootModule (self ).traverse (* args , ** kwargs )
245+ return RootModule (self ).traverse (* args , ** kwargs )
246+
247+ def submodule_update (self , previous_commit = None , force_remove = False , to_latest_revision = False ):
248+ """Update the submodules of this repository to the current HEAD commit.
249+ This method behaves smartly by determining changes of the path of a submodules
250+ repository, next to changes to the to-be-checked-out commit or the branch to be
251+ checked out. This works if the submodules ID does not change.
252+ Additionally it will detect addition and removal of submodules, which will be handled
253+ gracefully.
254+
255+ :param previous_commit: If set to a commit'ish, the commit we should use
256+ as the previous commit the HEAD pointed to before it was set to the commit it points to now.
257+ If None, it defaults to ORIG_HEAD otherwise, or the parent of the current
258+ commit if it is not given
259+ :param force_remove: If submodules have been deleted, they will be forcibly removed.
260+ Otherwise the update may fail if a submodule's repository cannot be deleted as
261+ changes have been made to it (see Submodule.update() for more information)
262+ :param to_latest_revision: If True, instead of checking out the revision pointed to
263+ by this submodule's sha, the checked out tracking branch will be merged with the
264+ newest remote branch fetched from the repository's origin"""
265+ if self .bare :
266+ raise InvalidGitRepositoryError ("Cannot update submodules in bare repositories" )
267+ # END handle bare
268+
269+ # HANDLE COMMITS
270+ ##################
271+ cur_commit = self .head .commit
272+ if previous_commit is None :
273+ symref = SymbolicReference (self , SymbolicReference .to_full_path ('ORIG_HEAD' ))
274+ try :
275+ previous_commit = symref .commit
276+ except Exception :
277+ pcommits = cur_commit .parents
278+ if pcommits :
279+ previous_commit = pcommits [0 ]
280+ else :
281+ # in this special case, we just diff against ourselve, which
282+ # means exactly no change
283+ previous_commit = cur_commit
284+ # END handle initial commit
285+ # END no ORIG_HEAD
286+ else :
287+ previous_commit = self .commit (previous_commit ) # obtain commit object
288+ # END handle previous commit
289+
290+ sms = self .submodules ()
291+
292+ # HANDLE REMOVALS
293+
294+ # HANDLE PATH RENAMES
295+
296+ # FINALLY UPDATE ALL ACTUAL SUBMODULES
297+ ##########################################
298+ if previous_commit == cur_commit :
299+ for sm in sms :
300+ sm .update (recursive = True , init = True , to_latest_revision = to_latest_revision )
301+ # END for each submodule to update
302+ # END handle commits are equal
303+
304+ #}END submodules
244305
245306 @property
246307 def tags (self ):
0 commit comments