summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/git/objects/submodule.py46
-rw-r--r--lib/git/objects/util.py8
-rw-r--r--lib/git/repo/base.py22
-rw-r--r--lib/git/util.py3
-rw-r--r--test/git/test_repo.py10
-rw-r--r--test/git/test_submodule.py33
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')
+