diff options
author | Sebastian Thiel <byronimo@gmail.com> | 2010-11-18 17:09:32 +0100 |
---|---|---|
committer | Sebastian Thiel <byronimo@gmail.com> | 2010-11-18 17:35:51 +0100 |
commit | c0990b2a6dd2e777b46c1685ddb985b3c0ef59a2 (patch) | |
tree | 6f657b31e044fd3136ddd2e013b4cf644958d60d /lib/git/objects/submodule.py | |
parent | 0c1834134ce177cdbd30a56994fcc4bf8f5be8b2 (diff) | |
download | gitpython-c0990b2a6dd2e777b46c1685ddb985b3c0ef59a2.tar.gz |
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)
Diffstat (limited to 'lib/git/objects/submodule.py')
-rw-r--r-- | lib/git/objects/submodule.py | 162 |
1 files changed, 100 insertions, 62 deletions
diff --git a/lib/git/objects/submodule.py b/lib/git/objects/submodule.py index 7ef7f590..9fb8ce8f 100644 --- a/lib/git/objects/submodule.py +++ b/lib/git/objects/submodule.py @@ -104,6 +104,7 @@ class SubmoduleConfigParser(GitConfigParser): return rval # END overridden methods + class Submodule(base.IndexObject, Iterable, Traversable): """Implements access to a git submodule. They are special in that their sha represents a commit in the submodule's repository which is to be checked out @@ -172,7 +173,10 @@ class Submodule(base.IndexObject, Iterable, Traversable): def __eq__(self, other): """Compare with another submodule""" - return self.path == other.path and self.url == other.url and super(Submodule, self).__eq__(other) + # we may only compare by name as this should be the ID they are hashed with + # Otherwise this type wouldn't be hashable + # return self.path == other.path and self.url == other.url and super(Submodule, self).__eq__(other) + return self._name == other._name def __ne__(self, other): """Compare with another submodule for inequality""" @@ -185,6 +189,9 @@ class Submodule(base.IndexObject, Iterable, Traversable): def __str__(self): return self._name + def __repr__(self): + return "git.%s(name=%s, path=%s, url=%s, branch=%s)" % (type(self).__name__, self._name, self.path, self.url, self.branch) + @classmethod def _config_parser(cls, repo, parent_commit, read_only): """:return: Config Parser constrained to our submodule in read or write mode @@ -459,7 +466,7 @@ class Submodule(base.IndexObject, Iterable, Traversable): return self @unbare_repo - def move(self, module_path, module_only=False): + def move(self, module_path, configuration=True, module=True): """Move the submodule to a another module path. This involves physically moving the repository at our current path, changing the configuration, as well as adjusting our index entry accordingly. @@ -467,15 +474,21 @@ class Submodule(base.IndexObject, Iterable, Traversable): repository-relative path. Intermediate directories will be created accordingly. If the path already exists, it must be empty. Trailling (back)slashes are removed automatically - :param module_only: if True, only the repository managed by this submodule + :param configuration: if True, the configuration will be adjusted to let + the submodule point to the given path. + :param module: if True, the repository managed by this submodule will be moved, not the configuration. This will effectively leave your repository in an inconsistent state unless the configuration - and index already point to the target location. + and index already point to the target location. :return: self :raise ValueError: if the module path existed and was not empty, or was a file :note: Currently the method is not atomic, and it could leave the repository in an inconsistent state if a sub-step fails for some reason """ + if module + configuration < 1: + raise ValueError("You must specify to move at least the module or the configuration of the submodule") + #END handle input + module_path = to_native_path_linux(module_path) if module_path.endswith('/'): module_path = module_path[:-1] @@ -494,54 +507,64 @@ class Submodule(base.IndexObject, Iterable, Traversable): index = self.repo.index tekey = index.entry_key(module_path, 0) # if the target item already exists, fail - if not module_only and tekey in index.entries: + if configuration and tekey in index.entries: raise ValueError("Index entry for target path did alredy exist") #END handle index key already there # remove existing destination - if os.path.exists(dest_path): - if len(os.listdir(dest_path)): - raise ValueError("Destination module directory was not empty") - #END handle non-emptyness - - if os.path.islink(dest_path): - os.remove(dest_path) + if module: + if os.path.exists(dest_path): + if len(os.listdir(dest_path)): + raise ValueError("Destination module directory was not empty") + #END handle non-emptyness + + if os.path.islink(dest_path): + os.remove(dest_path) + else: + os.rmdir(dest_path) + #END handle link else: - os.rmdir(dest_path) - #END handle link - else: - # recreate parent directories - # NOTE: renames() does that now - pass - #END handle existance + # recreate parent directories + # NOTE: renames() does that now + pass + #END handle existance + # END handle module # move the module into place if possible cur_path = self.abspath - if os.path.exists(cur_path): + renamed_module = False + if module and os.path.exists(cur_path): os.renames(cur_path, dest_path) + renamed_module = True #END move physical module - # NOTE: from now on, we would have to undo the rename ! # rename the index entry - have to manipulate the index directly as # git-mv cannot be used on submodules ... yeah - if not module_only: - try: - ekey = index.entry_key(self.path, 0) - entry = index.entries[ekey] - del(index.entries[ekey]) - nentry = git.IndexEntry(entry[:3]+(module_path,)+entry[4:]) - index.entries[tekey] = nentry - except KeyError: - raise ValueError("Submodule's entry at %r did not exist" % (self.path)) - #END handle submodule doesn't exist - - # update configuration - writer = self.config_writer(index=index) # auto-write - writer.set_value('path', module_path) - self.path = module_path - del(writer) - # END handle module_only + try: + if configuration: + try: + ekey = index.entry_key(self.path, 0) + entry = index.entries[ekey] + del(index.entries[ekey]) + nentry = git.IndexEntry(entry[:3]+(module_path,)+entry[4:]) + index.entries[tekey] = nentry + except KeyError: + raise InvalidGitRepositoryError("Submodule's entry at %r did not exist" % (self.path)) + #END handle submodule doesn't exist + + # update configuration + writer = self.config_writer(index=index) # auto-write + writer.set_value('path', module_path) + self.path = module_path + del(writer) + # END handle configuration flag + except Exception: + if renamed_module: + os.renames(dest_path, cur_path) + # END undo module renaming + raise + #END handle undo rename return self @@ -917,7 +940,7 @@ class RootModule(Submodule): ################## cur_commit = repo.head.commit if previous_commit is None: - symref = SymbolicReference(repo, SymbolicReference.to_full_path('ORIG_HEAD')) + symref = repo.head.orig_head() try: previous_commit = symref.commit except Exception: @@ -936,8 +959,8 @@ class RootModule(Submodule): # HANDLE REMOVALS - psms = type(self).list_items(repo, parent_commit=previous_commit) - sms = self.children() + psms = self.list_items(repo, parent_commit=previous_commit) + sms = self.list_items(self.module()) spsms = set(psms) ssms = set(sms) @@ -958,7 +981,7 @@ class RootModule(Submodule): if sm.path != psm.path and psm.module_exists(): # move the module to the new path - psm.move(sm.path, module_only=True) + psm.move(sm.path, module=True, configuration=False) # END handle path changes if sm.module_exists(): @@ -970,26 +993,39 @@ class RootModule(Submodule): nn = '__new_origin__' smm = sm.module() rmts = smm.remotes - assert nn not in rmts - smr = smm.create_remote(nn, sm.url) - srm.fetch() - - # now delete the changed one - orig_name = None - for remote in rmts: - if remote.url == psm.url: - orig_name = remote.name - smm.delete_remote(remote) - break - # END if urls match - # END for each remote - # rename the new remote back to what it was - # if we have not found any remote with the original url - # we may not have a name. This is a special case, - # and its okay to fail her - assert orig_name is not None, "Couldn't find original remote-repo at url %r" % psm.url - smr.rename(orig_name) + # don't do anything if we already have the url we search in place + if len([r for r in rmts if r.url == sm.url]) == 0: + assert nn not in [r.name for r in rmts] + smr = smm.create_remote(nn, sm.url) + smr.fetch() + + # now delete the changed one + orig_name = None + for remote in rmts: + if remote.url == psm.url: + orig_name = remote.name + smm.delete_remote(remote) + break + # END if urls match + # END for each remote + + # if we didn't find a matching remote, but have exactly one, + # we can safely use this one + if len(rmts) == 1: + orig_name = rmts[0].name + smm.delete_remote(rmts[0]) + else: + # if we have not found any remote with the original url + # we may not have a name. This is a special case, + # and its okay to fail here + # Alternatively we could just generate a unique name + raise InvalidGitRepositoryError("Couldn't find original remote-repo at url %r" % psm.url) + # END only one remove + + # rename the new remote back to what it was + smr.rename(orig_name) + # END skip remote handling if new url already exists in module # END handle url if sm.branch != psm.branch: @@ -1020,11 +1056,13 @@ class RootModule(Submodule): # FINALLY UPDATE ALL ACTUAL SUBMODULES ########################################## for sm in sms: + # update the submodule using the default method sm.update(recursive=True, init=init, to_latest_revision=to_latest_revision) # update recursively depth first - question is which inconsitent # state will be better in case it fails somewhere. Defective branch - # or defective depth + # or defective depth. The RootSubmodule type will never process itself, + # which was done in the previous expression if recursive: type(cls)(sm.module()).update(recursive=True, force_remove=force_remove, init=init, to_latest_revision=to_latest_revision) |