diff options
-rw-r--r-- | lib/git/objects/submodule.py | 46 | ||||
-rw-r--r-- | lib/git/objects/util.py | 8 | ||||
-rw-r--r-- | lib/git/repo/base.py | 22 | ||||
-rw-r--r-- | lib/git/util.py | 3 | ||||
-rw-r--r-- | test/git/test_repo.py | 10 | ||||
-rw-r--r-- | test/git/test_submodule.py | 33 |
6 files changed, 113 insertions, 9 deletions
diff --git a/lib/git/objects/submodule.py b/lib/git/objects/submodule.py index 1aa0cfb5..eda95115 100644 --- a/lib/git/objects/submodule.py +++ b/lib/git/objects/submodule.py @@ -1,13 +1,15 @@ import base +from util import Traversable from StringIO import StringIO # need a dict to set bloody .name field from git.util import Iterable from git.config import GitConfigParser, SectionConstraint from git.util import join_path_native from git.exc import InvalidGitRepositoryError, NoSuchPathError +import stat import os -__all__ = ("Submodule", ) +__all__ = ("Submodule", "RootModule") #{ Utilities @@ -31,7 +33,7 @@ class SubmoduleConfigParser(GitConfigParser): _mutating_methods_ = tuple() -class Submodule(base.IndexObject, Iterable): +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 at the path of this instance. @@ -40,10 +42,11 @@ class Submodule(base.IndexObject, Iterable): All methods work in bare and non-bare repositories.""" - _id_attribute_ = "path" + _id_attribute_ = "name" k_modules_file = '.gitmodules' k_ref_option = 'ref' k_ref_default = 'master' + k_def_mode = stat.S_IFDIR | stat.S_IFLNK # submodules are directories with link-status # this is a bogus type for base class compatability type = 'submodule' @@ -86,6 +89,14 @@ class Submodule(base.IndexObject, Iterable): super(Submodule, self)._set_cache_(attr) # END handle attribute name + def _get_intermediate_items(self, item): + """:return: all the submodules of our module repository""" + try: + return type(self).list_items(item.module()) + except InvalidGitRepositoryError: + return list() + # END handle intermeditate items + def __eq__(self, other): """Compare with another submodule""" return self.path == other.path and self.url == other.url and super(Submodule, self).__eq__(other) @@ -107,6 +118,7 @@ class Submodule(base.IndexObject, Iterable): if not os.path.isfile(fp_module_path): raise IOError("%s file was not accessible" % fp_module_path) # END handle existance + fp_module = fp_module_path else: try: fp_module = cls._sio_modules(parent_commit) @@ -300,4 +312,32 @@ class Submodule(base.IndexObject, Iterable): #} END iterable interface + +class RootModule(Submodule): + """A (virtual) Root of all submodules in the given repository. It can be used + to more easily traverse all submodules of the master repository""" + + __slots__ = tuple() + + k_root_name = '__ROOT__' + + def __init__(self, repo): + # repo, binsha, mode=None, path=None, name = None, parent_commit=None, url=None, ref=None) + super(RootModule, self).__init__( + repo, + binsha = self.NULL_BIN_SHA, + mode = self.k_def_mode, + path = '', + name = self.k_root_name, + parent_commit = repo.head.commit, + url = '', + ref = self.k_ref_default + ) + + + #{ Interface + def module(self): + """:return: the actual repository containing the submodules""" + return self.repo + #} END interface #} END classes diff --git a/lib/git/objects/util.py b/lib/git/objects/util.py index 21833080..9a54e031 100644 --- a/lib/git/objects/util.py +++ b/lib/git/objects/util.py @@ -4,6 +4,8 @@ # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php """Module for general utility functions""" +from git.util import IterableList + import re from collections import deque as Deque import platform @@ -273,6 +275,12 @@ class Traversable(object): """ raise NotImplementedError("To be implemented in subclass") + def list_traverse(self, *args, **kwargs): + """:return: IterableList with the results of the traversal as produced by + traverse()""" + out = IterableList(self._id_attribute_) + out.extend(self.traverse(*args, **kwargs)) + return out def traverse( self, predicate = lambda i,d: True, prune = lambda i,d: False, depth = -1, branch_first=True, diff --git a/lib/git/repo/base.py b/lib/git/repo/base.py index 790b1283..3a395af0 100644 --- a/lib/git/repo/base.py +++ b/lib/git/repo/base.py @@ -6,7 +6,6 @@ from git.exc import InvalidGitRepositoryError, NoSuchPathError from git.cmd import Git -from git.objects import Actor from git.refs import * from git.index import IndexFile from git.objects import * @@ -222,6 +221,27 @@ class Repo(object): """:return: Remote with the specified name :raise ValueError: if no remote with such a name exists""" return Remote(self, name) + + @property + def submodules(self): + """:return: git.IterableList(Submodule, ...) of direct submodules""" + return self.list_submodules(recursive=False) + + def submodule(self, name): + """:return: Submodule with the given name + :raise ValueError: If no such submodule exists""" + try: + return self.submodules[name] + except IndexError: + raise ValueError("Didn't find submodule named %r" % name) + # END exception handling + + def list_submodules(self, recursive=False): + """A list if Submodule objects available in this repository + :param recursive: If True, submodules of submodules (and so forth) will be + returned as well as part of a depth-first traversal + :return: ``git.IterableList(Submodule, ...)""" + return RootModule(self).list_traverse(ignore_self=1, depth = recursive and -1 or 1) @property def tags(self): diff --git a/lib/git/util.py b/lib/git/util.py index fcb50585..b77e7904 100644 --- a/lib/git/util.py +++ b/lib/git/util.py @@ -296,6 +296,9 @@ class IterableList(list): def __init__(self, id_attr, prefix=''): self._id_attr = id_attr self._prefix = prefix + if not isinstance(id_attr, basestring): + raise ValueError("First parameter must be a string identifying the name-property. Extend the list after initialization") + # END help debugging ! def __getattr__(self, attr): attr = self._prefix + attr diff --git a/test/git/test_repo.py b/test/git/test_repo.py index 65dce590..063b5dff 100644 --- a/test/git/test_repo.py +++ b/test/git/test_repo.py @@ -208,8 +208,10 @@ class TestRepo(TestBase): assert_equal('<git.Repo "%s">' % path, repr(self.rorepo)) def test_is_dirty_with_bare_repository(self): + orig_value = self.rorepo._bare self.rorepo._bare = True assert_false(self.rorepo.is_dirty()) + self.rorepo._bare = orig_value def test_is_dirty(self): self.rorepo._bare = False @@ -220,8 +222,10 @@ class TestRepo(TestBase): # END untracked files # END working tree # END index + orig_val = self.rorepo._bare self.rorepo._bare = True assert self.rorepo.is_dirty() == False + self.rorepo._bare = orig_val def test_head(self): assert self.rorepo.head.reference.object == self.rorepo.active_branch.object @@ -552,3 +556,9 @@ class TestRepo(TestBase): target_type = GitCmdObjectDB assert isinstance(self.rorepo.odb, target_type) + def test_submodules(self): + assert len(self.rorepo.submodules) == 1 # non-recursive + assert len(self.rorepo.list_submodules(recursive=True)) == 2 + + assert isinstance(self.rorepo.submodule("lib/git/ext/gitdb"), Submodule) + self.failUnlessRaises(ValueError, self.rorepo.submodule, "doesn't exist") diff --git a/test/git/test_submodule.py b/test/git/test_submodule.py index f2bc43b5..2ca0b269 100644 --- a/test/git/test_submodule.py +++ b/test/git/test_submodule.py @@ -7,6 +7,7 @@ from git.objects.submodule import * class TestSubmodule(TestBase): + k_subm_current = "00ce31ad308ff4c7ef874d2fa64374f47980c85c" k_subm_changed = "394ed7006ee5dc8bddfd132b64001d5dfc0ffdd3" k_no_subm_tag = "0.1.6" @@ -19,7 +20,7 @@ class TestSubmodule(TestBase): self.failUnlessRaises(AttributeError, getattr, smm, 'name') # iterate - 1 submodule - sms = Submodule.list_items(rwrepo) + sms = Submodule.list_items(rwrepo, self.k_subm_current) assert len(sms) == 1 sm = sms[0] @@ -27,6 +28,7 @@ class TestSubmodule(TestBase): assert len(Submodule.list_items(rwrepo, self.k_no_subm_tag)) == 0 assert sm.path == 'lib/git/ext/gitdb' + assert sm.path == sm.name # for now, this is True assert sm.url == 'git://gitorious.org/git-python/gitdb.git' assert sm.ref == 'master' # its unset in this case assert sm.parent_commit == rwrepo.head.commit @@ -47,10 +49,9 @@ class TestSubmodule(TestBase): # cannot get a writer on historical submodules self.failUnlessRaises(ValueError, smold.config_writer) - # make the old into a new prev_parent_commit = smold.parent_commit - smold.set_parent_commit('HEAD') + smold.set_parent_commit(self.k_subm_current) assert smold.parent_commit != prev_parent_commit assert smold.binsha == sm.binsha smold.set_parent_commit(prev_parent_commit) @@ -100,7 +101,7 @@ class TestSubmodule(TestBase): # Writing of historical submodule configurations must not work - @with_rw_repo('HEAD') + @with_rw_repo(k_subm_current) def test_base_rw(self, rwrepo): self._do_base_tests(rwrepo) @@ -108,4 +109,26 @@ class TestSubmodule(TestBase): def test_base_bare(self, rwrepo): self._do_base_tests(rwrepo) - + def test_root_module(self): + # Can query everything without problems + rm = RootModule(self.rorepo) + assert rm.module() is self.rorepo + + rm.binsha + rm.mode + rm.path + assert rm.name == rm.k_root_name + assert rm.parent_commit == self.rorepo.head.commit + rm.url + rm.ref + + assert len(rm.list_items(rm.module())) == 1 + rm.config_reader() + rm.config_writer() + + # deep traversal gitdb / async + assert len(list(rm.traverse())) == 2 + + # cannot set the parent commit as repo name doesn't exist + self.failUnlessRaises(ValueError, rm.set_parent_commit, 'HEAD') + |