Skip to content

Commit 98e6edb

Browse files
committed
Initial implementation of submodule.add without any tests. These are to come next
1 parent 3d061a1 commit 98e6edb

File tree

3 files changed

+110
-14
lines changed

3 files changed

+110
-14
lines changed

Diff for: lib/git/index/base.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@
3535
)
3636

3737
from git.objects import (
38-
Blob,
38+
Blob,
39+
Submodule,
3940
Tree,
4041
Object,
4142
Commit,
@@ -553,7 +554,7 @@ def _preprocess_add_items(self, items):
553554
for item in items:
554555
if isinstance(item, basestring):
555556
paths.append(self._to_relative_path(item))
556-
elif isinstance(item, Blob):
557+
elif isinstance(item, (Blob, Submodule)):
557558
entries.append(BaseIndexEntry.from_blob(item))
558559
elif isinstance(item, BaseIndexEntry):
559560
entries.append(item)
@@ -588,7 +589,7 @@ def add(self, items, force=True, fprogress=lambda *args: None, path_rewriter=Non
588589
589590
They are added at stage 0
590591
591-
- Blob object
592+
- Blob or Submodule object
592593
Blobs are added as they are assuming a valid mode is set.
593594
The file they refer to may or may not exist in the file system, but
594595
must be a path relative to our repository.
@@ -612,6 +613,7 @@ def add(self, items, force=True, fprogress=lambda *args: None, path_rewriter=Non
612613
explicitly set. Please note that Index Entries require binary sha's.
613614
614615
:param force:
616+
**CURRENTLY INEFFECTIVE**
615617
If True, otherwise ignored or excluded files will be
616618
added anyway.
617619
As opposed to the git-add command, we enable this flag by default

Diff for: lib/git/objects/submodule.py

+90-4
Original file line numberDiff line numberDiff line change
@@ -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

Diff for: test/git/test_submodule.py

+15-7
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,18 @@ def _do_base_tests(self, rwrepo):
5050

5151
# test config_reader/writer methods
5252
sm.config_reader()
53+
new_smclone_path = None # keep custom paths for later
54+
new_csmclone_path = None #
5355
if rwrepo.bare:
5456
self.failUnlessRaises(InvalidGitRepositoryError, sm.config_writer)
5557
else:
5658
writer = sm.config_writer()
5759
# for faster checkout, set the url to the local path
58-
new_path = to_native_path_linux(join_path_native(self.rorepo.working_tree_dir, sm.path))
59-
writer.set_value('url', new_path)
60+
new_smclone_path = to_native_path_linux(join_path_native(self.rorepo.working_tree_dir, sm.path))
61+
writer.set_value('url', new_smclone_path)
6062
del(writer)
61-
assert sm.config_reader().get_value('url') == new_path
62-
assert sm.url == new_path
63+
assert sm.config_reader().get_value('url') == new_smclone_path
64+
assert sm.url == new_smclone_path
6365
# END handle bare repo
6466
smold.config_reader()
6567

@@ -88,6 +90,7 @@ def _do_base_tests(self, rwrepo):
8890
if rwrepo.bare:
8991
self.failUnlessRaises(InvalidGitRepositoryError, sm.module)
9092
self.failUnlessRaises(InvalidGitRepositoryError, sm.remove)
93+
self.failUnlessRaises(InvalidGitRepositoryError, sm.add, rwrepo, 'here', 'there')
9194
else:
9295
# its not checked out in our case
9396
self.failUnlessRaises(InvalidGitRepositoryError, sm.module)
@@ -121,9 +124,9 @@ def _do_base_tests(self, rwrepo):
121124
assert not csm.module_exists()
122125

123126
# adjust the path of the submodules module to point to the local destination
124-
new_csm_path = to_native_path_linux(join_path_native(self.rorepo.working_tree_dir, sm.path, csm.path))
125-
csm.config_writer().set_value('url', new_csm_path)
126-
assert csm.url == new_csm_path
127+
new_csmclone_path = to_native_path_linux(join_path_native(self.rorepo.working_tree_dir, sm.path, csm.path))
128+
csm.config_writer().set_value('url', new_csmclone_path)
129+
assert csm.url == new_csmclone_path
127130

128131
# update recuesively again
129132
sm.update(recursive=True)
@@ -205,6 +208,11 @@ def _do_base_tests(self, rwrepo):
205208
sm.remove()
206209
assert not sm.exists()
207210
assert not sm.module_exists()
211+
212+
# ADD NEW SUBMODULE
213+
###################
214+
# raise if url does not match remote url of existing repo
215+
208216
# END handle bare mode
209217

210218

0 commit comments

Comments
 (0)