@@ -202,15 +202,101 @@ def _config_parser_constrained(self, read_only):
202202 #{ Edit Interface
203203
204204 @classmethod
205- def add (cls , repo , path , url , skip_init = False ):
205+ def add (cls , repo , name , path , url = None , branch = k_head_default , no_checkout = False ):
206206 """Add a new submodule to the given repository. This will alter the index
207207 as well as the .gitmodules file, but will not create a new commit.
208+ If the submodule already exists, no matter if the configuration differs
209+ from the one provided, the existing submodule will be returned.
208210 :param repo: Repository instance which should receive the submodule
209- :param path: repository-relative path at which the submodule should be located
211+ :param name: The name/identifier for the submodule
212+ :param path: repository-relative or absolute path at which the submodule
213+ should be located
210214 It will be created as required during the repository initialization.
211215 :param url: git-clone compatible URL, see git-clone reference for more information
212- :param skip_init: if True, the new repository will not be cloned to its location.
213- :return: The newly created submodule instance"""
216+ If None, the repository is assumed to exist, and the url of the first
217+ remote is taken instead. This is useful if you want to make an existing
218+ repository a submodule of anotherone.
219+ :param branch: branch at which the submodule should (later) be checked out.
220+ The given branch must exist in the remote repository, and will be checked
221+ out locally as a tracking branch.
222+ It will only be written into the configuration if it differs from the
223+ default.
224+ :param no_checkout: if True, and if the repository has to be cloned manually,
225+ no checkout will be performed
226+ :return: The newly created submodule instance
227+ :note: works atomically, such that no change will be done if the repository
228+ update fails for instance"""
229+ if repo .bare :
230+ raise InvalidGitRepositoryError ("Cannot add a submodule to bare repositories" )
231+ #END handle bare mode
232+
233+ path = to_native_path_linux (path )
234+ if path .endswith ('/' ):
235+ path = path [:- 1 ]
236+ # END handle trailing slash
237+
238+ sm = cls (repo , cls .NULL_BIN_SHA , cls .k_def_mode , path , name )
239+ if sm .exists ():
240+ # reretrieve submodule from tree
241+ return repo .head .commit .tree [path ]
242+ # END handle existing
243+
244+ branch = Head (repo , head .to_full_path (branch ))
245+ has_module = sm .module_exists ()
246+ branch_is_default = branch .name == cls .k_head_default
247+ if has_module and url is not None :
248+ if url not in [r .url for r in sm .module ().remotes ]:
249+ raise ValueError ("Specified URL %s does not match any remote url of the repository at %s" % (url , sm .module_path ()))
250+ # END check url
251+ # END verify urls match
252+
253+ mrepo = None
254+ if url is None :
255+ if not has_module :
256+ raise ValueError ("A URL was not given and existing repository did not exsit at %s" % path )
257+ # END check url
258+ mrepo = sm .module ()
259+ urls = [r .url for r in mrepo .remotes ]
260+ if not urls :
261+ raise ValueError ("Didn't find any remote url in repository at %s" % sm .module_path ())
262+ # END verify we have url
263+ url = urls [0 ]
264+ else :
265+ # clone new repo
266+ kwargs = {'n' : no_checkout }
267+ if branch_is_default :
268+ kwargs ['b' ] = str (branch )
269+ # END setup checkout-branch
270+ mrepo = git .Repo .clone_from (url , path , ** kwargs )
271+ # END verify url
272+
273+ # update configuration and index
274+ writer = sm .config_writer ()
275+ writer .set_value ('url' , url )
276+ writer .set_value ('path' , path )
277+
278+ sm ._url = url
279+ if not branch_is_default :
280+ # store full path
281+ writer .set_value (cls .k_head_option , branch .path )
282+ sm ._branch = branch
283+ # END handle path
284+ del (writer )
285+
286+ # NOTE: Have to write the repo config file as well, otherwise
287+ # the default implementation will be offended and not update the repository
288+ # Maybe this is a good way to assure it doesn't get into our way, but
289+ # we want to stay backwards compatible too ... . Its so redundant !
290+ repo .config_writer ().set_value (sm_section (sm .name ), 'url' , url )
291+
292+ # we deliberatly assume that our head matches our index !
293+ pcommit = repo .head .commit
294+ sm ._parent_commit = pcommit
295+ sm .binsha = mrepo .head .commit .binsha
296+ repo .index .add ([sm ], write = True )
297+
298+ return sm
299+
214300
215301 def update (self , recursive = False , init = True , to_latest_revision = False ):
216302 """Update the repository of this submodule to point to the checkout
0 commit comments