Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c0990b2

Browse files
committed
first update test succeeds, so it verifies that existing repositories can be moved later if the configuration changed, and actually it also verifies that the url-change is handled correctly (as we changed the url from the default to the local path)
1 parent 0c18341 commit c0990b2

File tree

2 files changed

+135
-66
lines changed

2 files changed

+135
-66
lines changed

lib/git/objects/submodule.py

Lines changed: 100 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ def write(self):
104104
return rval
105105
# END overridden methods
106106

107+
107108
class Submodule(base.IndexObject, Iterable, Traversable):
108109
"""Implements access to a git submodule. They are special in that their sha
109110
represents a commit in the submodule's repository which is to be checked out
@@ -172,7 +173,10 @@ def _get_intermediate_items(self, item):
172173

173174
def __eq__(self, other):
174175
"""Compare with another submodule"""
175-
return self.path == other.path and self.url == other.url and super(Submodule, self).__eq__(other)
176+
# we may only compare by name as this should be the ID they are hashed with
177+
# Otherwise this type wouldn't be hashable
178+
# return self.path == other.path and self.url == other.url and super(Submodule, self).__eq__(other)
179+
return self._name == other._name
176180

177181
def __ne__(self, other):
178182
"""Compare with another submodule for inequality"""
@@ -185,6 +189,9 @@ def __hash__(self):
185189
def __str__(self):
186190
return self._name
187191

192+
def __repr__(self):
193+
return "git.%s(name=%s, path=%s, url=%s, branch=%s)" % (type(self).__name__, self._name, self.path, self.url, self.branch)
194+
188195
@classmethod
189196
def _config_parser(cls, repo, parent_commit, read_only):
190197
""":return: Config Parser constrained to our submodule in read or write mode
@@ -459,23 +466,29 @@ def update(self, recursive=False, init=True, to_latest_revision=False):
459466
return self
460467

461468
@unbare_repo
462-
def move(self, module_path, module_only=False):
469+
def move(self, module_path, configuration=True, module=True):
463470
"""Move the submodule to a another module path. This involves physically moving
464471
the repository at our current path, changing the configuration, as well as
465472
adjusting our index entry accordingly.
466473
:param module_path: the path to which to move our module, given as
467474
repository-relative path. Intermediate directories will be created
468475
accordingly. If the path already exists, it must be empty.
469476
Trailling (back)slashes are removed automatically
470-
:param module_only: if True, only the repository managed by this submodule
477+
:param configuration: if True, the configuration will be adjusted to let
478+
the submodule point to the given path.
479+
:param module: if True, the repository managed by this submodule
471480
will be moved, not the configuration. This will effectively
472481
leave your repository in an inconsistent state unless the configuration
473-
and index already point to the target location.
482+
and index already point to the target location.
474483
:return: self
475484
:raise ValueError: if the module path existed and was not empty, or was a file
476485
:note: Currently the method is not atomic, and it could leave the repository
477486
in an inconsistent state if a sub-step fails for some reason
478487
"""
488+
if module + configuration < 1:
489+
raise ValueError("You must specify to move at least the module or the configuration of the submodule")
490+
#END handle input
491+
479492
module_path = to_native_path_linux(module_path)
480493
if module_path.endswith('/'):
481494
module_path = module_path[:-1]
@@ -494,54 +507,64 @@ def move(self, module_path, module_only=False):
494507
index = self.repo.index
495508
tekey = index.entry_key(module_path, 0)
496509
# if the target item already exists, fail
497-
if not module_only and tekey in index.entries:
510+
if configuration and tekey in index.entries:
498511
raise ValueError("Index entry for target path did alredy exist")
499512
#END handle index key already there
500513

501514
# remove existing destination
502-
if os.path.exists(dest_path):
503-
if len(os.listdir(dest_path)):
504-
raise ValueError("Destination module directory was not empty")
505-
#END handle non-emptyness
506-
507-
if os.path.islink(dest_path):
508-
os.remove(dest_path)
515+
if module:
516+
if os.path.exists(dest_path):
517+
if len(os.listdir(dest_path)):
518+
raise ValueError("Destination module directory was not empty")
519+
#END handle non-emptyness
520+
521+
if os.path.islink(dest_path):
522+
os.remove(dest_path)
523+
else:
524+
os.rmdir(dest_path)
525+
#END handle link
509526
else:
510-
os.rmdir(dest_path)
511-
#END handle link
512-
else:
513-
# recreate parent directories
514-
# NOTE: renames() does that now
515-
pass
516-
#END handle existance
527+
# recreate parent directories
528+
# NOTE: renames() does that now
529+
pass
530+
#END handle existance
531+
# END handle module
517532

518533
# move the module into place if possible
519534
cur_path = self.abspath
520-
if os.path.exists(cur_path):
535+
renamed_module = False
536+
if module and os.path.exists(cur_path):
521537
os.renames(cur_path, dest_path)
538+
renamed_module = True
522539
#END move physical module
523540

524-
# NOTE: from now on, we would have to undo the rename !
525541

526542
# rename the index entry - have to manipulate the index directly as
527543
# git-mv cannot be used on submodules ... yeah
528-
if not module_only:
529-
try:
530-
ekey = index.entry_key(self.path, 0)
531-
entry = index.entries[ekey]
532-
del(index.entries[ekey])
533-
nentry = git.IndexEntry(entry[:3]+(module_path,)+entry[4:])
534-
index.entries[tekey] = nentry
535-
except KeyError:
536-
raise ValueError("Submodule's entry at %r did not exist" % (self.path))
537-
#END handle submodule doesn't exist
538-
539-
# update configuration
540-
writer = self.config_writer(index=index) # auto-write
541-
writer.set_value('path', module_path)
542-
self.path = module_path
543-
del(writer)
544-
# END handle module_only
544+
try:
545+
if configuration:
546+
try:
547+
ekey = index.entry_key(self.path, 0)
548+
entry = index.entries[ekey]
549+
del(index.entries[ekey])
550+
nentry = git.IndexEntry(entry[:3]+(module_path,)+entry[4:])
551+
index.entries[tekey] = nentry
552+
except KeyError:
553+
raise InvalidGitRepositoryError("Submodule's entry at %r did not exist" % (self.path))
554+
#END handle submodule doesn't exist
555+
556+
# update configuration
557+
writer = self.config_writer(index=index) # auto-write
558+
writer.set_value('path', module_path)
559+
self.path = module_path
560+
del(writer)
561+
# END handle configuration flag
562+
except Exception:
563+
if renamed_module:
564+
os.renames(dest_path, cur_path)
565+
# END undo module renaming
566+
raise
567+
#END handle undo rename
545568

546569
return self
547570

@@ -917,7 +940,7 @@ def update(self, previous_commit=None, recursive=True, force_remove=False, init=
917940
##################
918941
cur_commit = repo.head.commit
919942
if previous_commit is None:
920-
symref = SymbolicReference(repo, SymbolicReference.to_full_path('ORIG_HEAD'))
943+
symref = repo.head.orig_head()
921944
try:
922945
previous_commit = symref.commit
923946
except Exception:
@@ -936,8 +959,8 @@ def update(self, previous_commit=None, recursive=True, force_remove=False, init=
936959

937960

938961
# HANDLE REMOVALS
939-
psms = type(self).list_items(repo, parent_commit=previous_commit)
940-
sms = self.children()
962+
psms = self.list_items(repo, parent_commit=previous_commit)
963+
sms = self.list_items(self.module())
941964
spsms = set(psms)
942965
ssms = set(sms)
943966

@@ -958,7 +981,7 @@ def update(self, previous_commit=None, recursive=True, force_remove=False, init=
958981

959982
if sm.path != psm.path and psm.module_exists():
960983
# move the module to the new path
961-
psm.move(sm.path, module_only=True)
984+
psm.move(sm.path, module=True, configuration=False)
962985
# END handle path changes
963986

964987
if sm.module_exists():
@@ -970,26 +993,39 @@ def update(self, previous_commit=None, recursive=True, force_remove=False, init=
970993
nn = '__new_origin__'
971994
smm = sm.module()
972995
rmts = smm.remotes
973-
assert nn not in rmts
974-
smr = smm.create_remote(nn, sm.url)
975-
srm.fetch()
976-
977-
# now delete the changed one
978-
orig_name = None
979-
for remote in rmts:
980-
if remote.url == psm.url:
981-
orig_name = remote.name
982-
smm.delete_remote(remote)
983-
break
984-
# END if urls match
985-
# END for each remote
986996

987-
# rename the new remote back to what it was
988-
# if we have not found any remote with the original url
989-
# we may not have a name. This is a special case,
990-
# and its okay to fail her
991-
assert orig_name is not None, "Couldn't find original remote-repo at url %r" % psm.url
992-
smr.rename(orig_name)
997+
# don't do anything if we already have the url we search in place
998+
if len([r for r in rmts if r.url == sm.url]) == 0:
999+
assert nn not in [r.name for r in rmts]
1000+
smr = smm.create_remote(nn, sm.url)
1001+
smr.fetch()
1002+
1003+
# now delete the changed one
1004+
orig_name = None
1005+
for remote in rmts:
1006+
if remote.url == psm.url:
1007+
orig_name = remote.name
1008+
smm.delete_remote(remote)
1009+
break
1010+
# END if urls match
1011+
# END for each remote
1012+
1013+
# if we didn't find a matching remote, but have exactly one,
1014+
# we can safely use this one
1015+
if len(rmts) == 1:
1016+
orig_name = rmts[0].name
1017+
smm.delete_remote(rmts[0])
1018+
else:
1019+
# if we have not found any remote with the original url
1020+
# we may not have a name. This is a special case,
1021+
# and its okay to fail here
1022+
# Alternatively we could just generate a unique name
1023+
raise InvalidGitRepositoryError("Couldn't find original remote-repo at url %r" % psm.url)
1024+
# END only one remove
1025+
1026+
# rename the new remote back to what it was
1027+
smr.rename(orig_name)
1028+
# END skip remote handling if new url already exists in module
9931029
# END handle url
9941030

9951031
if sm.branch != psm.branch:
@@ -1020,11 +1056,13 @@ def update(self, previous_commit=None, recursive=True, force_remove=False, init=
10201056
# FINALLY UPDATE ALL ACTUAL SUBMODULES
10211057
##########################################
10221058
for sm in sms:
1059+
# update the submodule using the default method
10231060
sm.update(recursive=True, init=init, to_latest_revision=to_latest_revision)
10241061

10251062
# update recursively depth first - question is which inconsitent
10261063
# state will be better in case it fails somewhere. Defective branch
1027-
# or defective depth
1064+
# or defective depth. The RootSubmodule type will never process itself,
1065+
# which was done in the previous expression
10281066
if recursive:
10291067
type(cls)(sm.module()).update(recursive=True, force_remove=force_remove,
10301068
init=init, to_latest_revision=to_latest_revision)

test/git/test_submodule.py

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def _do_base_tests(self, rwrepo):
4242
# some commits earlier we still have a submodule, but its at a different commit
4343
smold = Submodule.iter_items(rwrepo, self.k_subm_changed).next()
4444
assert smold.binsha != sm.binsha
45-
assert smold != sm
45+
assert smold == sm # the name is still the same
4646

4747
# force it to reread its information
4848
del(smold._url)
@@ -268,6 +268,9 @@ def _do_base_tests(self, rwrepo):
268268

269269
# MOVE MODULE
270270
#############
271+
# invalid inptu
272+
self.failUnlessRaises(ValueError, nsm.move, 'doesntmatter', module=False, configuration=False)
273+
271274
# renaming to the same path does nothing
272275
assert nsm.move(sm.path) is nsm
273276

@@ -353,11 +356,39 @@ def test_root_module(self, rwrepo):
353356
rm = RootModule(rwrepo)
354357
assert len(rm.children()) == 1
355358

356-
# modify path
359+
# modify path without modifying the index entry
360+
# ( which is what the move method would do properly )
357361
sm = rm.children()[0]
358362
pp = "path/prefix"
359-
sm.config_writer().set_value('path', join_path_native(pp, sm.path))
360-
cpathchange = rwrepo.index.commit("changed sm path")
363+
fp = join_path_native(pp, sm.path)
364+
prep = sm.path
365+
assert not sm.module_exists() # was never updated after rwrepo's clone
366+
367+
# assure we clone from a local source
368+
sm.config_writer().set_value('url', join_path_native(self.rorepo.working_tree_dir, sm.path))
369+
sm.update(recursive=False)
370+
assert sm.module_exists()
371+
sm.config_writer().set_value('path', fp) # change path to something with prefix AFTER url change
372+
373+
# update fails as list_items in such a situations cannot work, as it cannot
374+
# find the entry at the changed path
375+
self.failUnlessRaises(InvalidGitRepositoryError, rm.update, recursive=False)
376+
377+
# move it properly - doesn't work as it its path currently points to an indexentry
378+
# which doesn't exist ( move it to some path, it doesn't matter here )
379+
self.failUnlessRaises(InvalidGitRepositoryError, sm.move, pp)
380+
# reset the path(cache) to where it was, now it works
381+
sm.path = prep
382+
sm.move(fp, module=False) # leave it at the old location
383+
384+
assert not sm.module_exists()
385+
cpathchange = rwrepo.index.commit("changed sm path") # finally we can commit
386+
387+
# update puts the module into place
388+
rm.update(recursive=False)
389+
sm.set_parent_commit(cpathchange)
390+
assert sm.module_exists()
391+
assert False
361392

362393
# add submodule
363394
nsmn = "newsubmodule"

0 commit comments

Comments
 (0)