2525InvalidGitRepositoryError ,
2626NoSuchPathError
2727)
28- from git .compat import string_types
28+ from git .compat import (
29+ string_types ,
30+ defenc
31+ )
2932
3033import stat
3134import git
@@ -209,13 +212,64 @@ def _module_abspath(cls, parent_repo, path, name):
209212# end
210213
211214@classmethod
212- def _write_git_file (cls ,working_tree_dir ,module_abspath ,overwrite_existing = False ):
215+ def _clone_repo (cls ,repo ,url ,path ,name ,** kwargs ):
216+ """:return: Repo instance of newly cloned repository
217+ :param repo: our parent repository
218+ :param url: url to clone from
219+ :param path: repository-relative path to the submodule checkout location
220+ :param name: canonical of the submodule
221+ :param kwrags: additinoal arguments given to git.clone"""
222+ module_abspath = cls ._module_abspath (repo ,path ,name )
223+ module_checkout_path = module_abspath
224+ if cls ._need_gitfile_submodules (repo .git ):
225+ kwargs ['separate_git_dir' ]= module_abspath
226+ module_abspath_dir = os .path .dirname (module_abspath )
227+ if not os .path .isdir (module_abspath_dir ):
228+ os .makedirs (module_abspath_dir )
229+ module_checkout_path = os .path .join (repo .working_tree_dir ,path )
230+ # end
231+
232+ clone = git .Repo .clone_from (url ,module_checkout_path ,** kwargs )
233+ if cls ._need_gitfile_submodules (repo .git ):
234+ cls ._write_git_file (module_checkout_path ,module_abspath )
235+ # end
236+ return clone
237+
238+ @classmethod
239+ def _to_relative_path (cls ,parent_repo ,path ):
240+ """:return: a path guaranteed to be relative to the given parent-repository
241+ :raise ValueError: if path is not contained in the parent repository's working tree"""
242+ path = to_native_path_linux (path )
243+ if path .endswith ('/' ):
244+ path = path [:- 1 ]
245+ # END handle trailing slash
246+
247+ if os .path .isabs (path ):
248+ working_tree_linux = to_native_path_linux (parent_repo .working_tree_dir )
249+ if not path .startswith (working_tree_linux ):
250+ raise ValueError ("Submodule checkout path '%s' needs to be within the parents repository at '%s'"
251+ % (working_tree_linux ,path ))
252+ path = path [len (working_tree_linux )+ 1 :]
253+ if not path :
254+ raise ValueError ("Absolute submodule path '%s' didn't yield a valid relative path" % path )
255+ # end verify converted relative path makes sense
256+ # end convert to a relative path
257+
258+ return path
259+
260+ @classmethod
261+ def _write_git_file (cls ,working_tree_dir ,module_abspath ):
213262"""Writes a .git file containing a (preferably) relative path to the actual git module repository.
214263 It is an error if the module_abspath cannot be made into a relative path, relative to the working_tree_dir
264+ :note: will overwrite existing files !
215265 :param working_tree_dir: directory to write the .git file into
216266 :param module_abspath: absolute path to the bare repository
217- :param overwrite_existing: if True, we may rewrite existing .git files, otherwise we raise"""
218- raise NotImplementedError
267+ """
268+ git_file = os .path .join (working_tree_dir ,'.git' )
269+ rela_path = os .path .relpath (module_abspath ,start = working_tree_dir )
270+ fp = open (git_file ,'wb' )
271+ fp .write (("gitdir: %s" % rela_path ).encode (defenc ))
272+ fp .close ()
219273
220274#{ Edit Interface
221275
@@ -252,21 +306,7 @@ def add(cls, repo, name, path, url=None, branch=None, no_checkout=False):
252306raise InvalidGitRepositoryError ("Cannot add submodules to bare repositories" )
253307# END handle bare repos
254308
255- path = to_native_path_linux (path )
256- if path .endswith ('/' ):
257- path = path [:- 1 ]
258- # END handle trailing slash
259-
260- if os .path .isabs (path ):
261- working_tree_linux = to_native_path_linux (repo .working_tree_dir )
262- if not path .startswith (working_tree_linux ):
263- raise ValueError ("Submodule checkout path '%s' needs to be within the parents repository at '%s'"
264- % (working_tree_linux ,path ))
265- path = path [len (working_tree_linux )+ 1 :]
266- if not path :
267- raise ValueError ("Absolute submodule path '%s' didn't yield a valid relative path" % path )
268- # end verify converted relative path makes sense
269- # end convert to a relative path
309+ path = cls ._to_relative_path (repo ,path )
270310
271311# assure we never put backslashes into the url, as some operating systems
272312# like it ...
@@ -317,17 +357,9 @@ def add(cls, repo, name, path, url=None, branch=None, no_checkout=False):
317357if not branch_is_default :
318358kwargs ['b' ]= br .name
319359# END setup checkout-branch
320- module_abspath = cls ._module_abspath (repo ,path ,name )
321- module_checkout_path = module_abspath
322- if cls ._need_gitfile_submodules (repo .git ):
323- kwargs ['separate_git_dir' ]= module_abspath
324- module_abspath_dir = os .path .dirname (module_abspath )
325- if not os .path .isdir (module_abspath_dir ):
326- os .makedirs (module_abspath_dir )
327- module_checkout_path = os .path .join (repo .working_tree_dir ,path )
328- # end
329-
330- mrepo = git .Repo .clone_from (url ,module_checkout_path ,** kwargs )
360+
361+ # _clone_repo(cls, repo, url, path, name, **kwargs):
362+ mrepo = cls ._clone_repo (repo ,url ,path ,name ,** kwargs )
331363# END verify url
332364
333365# update configuration and index
@@ -416,7 +448,6 @@ def update(self, recursive=False, init=True, to_latest_revision=False, progress=
416448if not init :
417449return self
418450# END early abort if init is not allowed
419- import git
420451
421452# there is no git-repository yet - but delete empty paths
422453module_path = self ._module_abspath (self .repo ,self .path ,self .name )
@@ -433,7 +464,7 @@ def update(self, recursive=False, init=True, to_latest_revision=False, progress=
433464progress .update (BEGIN | CLONE ,0 ,1 ,prefix + "Cloning %s to %s in submodule %r" %
434465 (self .url ,module_path ,self .name ))
435466if not dry_run :
436- mrepo = git . Repo . clone_from (self .url ,module_path ,n = True )
467+ mrepo = self . _clone_repo (self .repo , self . url ,self . path , self . name ,n = True )
437468# END handle dry-run
438469progress .update (END | CLONE ,0 ,1 ,prefix + "Done cloning to %s" % module_path )
439470
@@ -533,16 +564,15 @@ def move(self, module_path, configuration=True, module=True):
533564 the repository at our current path, changing the configuration, as well as
534565 adjusting our index entry accordingly.
535566
536- :param module_path: the path to which to move our module, given as
537- repository-relative path. Intermediate directories will be created
567+ :param module_path: the path to which to move our module in the parent repostory's working tree,
568+ given as repository-relative or absolute path. Intermediate directories will be created
538569 accordingly. If the path already exists, it must be empty.
539- Trailling (back)slashes are removed automatically
570+ Trailing (back)slashes are removed automatically
540571 :param configuration: if True, the configuration will be adjusted to let
541572 the submodule point to the given path.
542573 :param module: if True, the repository managed by this submodule
543- will be moved, not the configuration. This will effectively
544- leave your repository in an inconsistent state unless the configuration
545- and index already point to the target location.
574+ will be moved as well. If False, we don't move the submodule's checkout, which may leave
575+ the parent repository in an inconsistent state.
546576 :return: self
547577 :raise ValueError: if the module path existed and was not empty, or was a file
548578 :note: Currently the method is not atomic, and it could leave the repository
@@ -552,53 +582,55 @@ def move(self, module_path, configuration=True, module=True):
552582raise ValueError ("You must specify to move at least the module or the configuration of the submodule" )
553583# END handle input
554584
555- module_path = to_native_path_linux (module_path )
556- if module_path .endswith ('/' ):
557- module_path = module_path [:- 1 ]
558- # END handle trailing slash
585+ module_checkout_path = self ._to_relative_path (self .repo ,module_path )
559586
560587# VERIFY DESTINATION
561- if module_path == self .path :
588+ if module_checkout_path == self .path :
562589return self
563590# END handle no change
564591
565- dest_path = join_path_native (self .repo .working_tree_dir ,module_path )
566- if os .path .isfile (dest_path ):
567- raise ValueError ("Cannot move repository onto a file: %s" % dest_path )
592+ module_checkout_abspath = join_path_native (self .repo .working_tree_dir ,module_checkout_path )
593+ if os .path .isfile (module_checkout_abspath ):
594+ raise ValueError ("Cannot move repository onto a file: %s" % module_checkout_abspath )
568595# END handle target files
569596
570597index = self .repo .index
571- tekey = index .entry_key (module_path ,0 )
598+ tekey = index .entry_key (module_checkout_path ,0 )
572599# if the target item already exists, fail
573600if configuration and tekey in index .entries :
574- raise ValueError ("Index entry for target path didalredy exist" )
601+ raise ValueError ("Index entry for target path didalready exist" )
575602# END handle index key already there
576603
577604# remove existing destination
578605if module :
579- if os .path .exists (dest_path ):
580- if len (os .listdir (dest_path )):
606+ if os .path .exists (module_checkout_abspath ):
607+ if len (os .listdir (module_checkout_abspath )):
581608raise ValueError ("Destination module directory was not empty" )
582- # END handle non-emptyness
609+ # END handle non-emptiness
583610
584- if os .path .islink (dest_path ):
585- os .remove (dest_path )
611+ if os .path .islink (module_checkout_abspath ):
612+ os .remove (module_checkout_abspath )
586613else :
587- os .rmdir (dest_path )
614+ os .rmdir (module_checkout_abspath )
588615# END handle link
589616else :
590617# recreate parent directories
591618# NOTE: renames() does that now
592619pass
593- # END handleexistance
620+ # END handleexistence
594621# END handle module
595622
596623# move the module into place if possible
597624cur_path = self .abspath
598625renamed_module = False
599626if module and os .path .exists (cur_path ):
600- os .renames (cur_path ,dest_path )
627+ os .renames (cur_path ,module_checkout_abspath )
601628renamed_module = True
629+
630+ if self ._need_gitfile_submodules (self .repo .git ):
631+ module_abspath = self ._module_abspath (self .repo ,self .path ,self .name )
632+ self ._write_git_file (module_checkout_abspath ,module_abspath )
633+ # end handle git file rewrite
602634# END move physical module
603635
604636# rename the index entry - have to manipulate the index directly as
@@ -609,22 +641,22 @@ def move(self, module_path, configuration=True, module=True):
609641ekey = index .entry_key (self .path ,0 )
610642entry = index .entries [ekey ]
611643del (index .entries [ekey ])
612- nentry = git .IndexEntry (entry [:3 ]+ (module_path ,)+ entry [4 :])
644+ nentry = git .IndexEntry (entry [:3 ]+ (module_checkout_path ,)+ entry [4 :])
613645index .entries [tekey ]= nentry
614646except KeyError :
615647raise InvalidGitRepositoryError ("Submodule's entry at %r did not exist" % (self .path ))
616648# END handle submodule doesn't exist
617649
618650# update configuration
619651writer = self .config_writer (index = index )# auto-write
620- writer .set_value ('path' ,module_path )
621- self .path = module_path
652+ writer .set_value ('path' ,module_checkout_path )
653+ self .path = module_checkout_path
622654writer .release ()
623655del (writer )
624656# END handle configuration flag
625657except Exception :
626658if renamed_module :
627- os .renames (dest_path ,cur_path )
659+ os .renames (module_checkout_abspath ,cur_path )
628660# END undo module renaming
629661raise
630662# END handle undo rename