diff options
author | Sebastian Thiel <byronimo@gmail.com> | 2010-11-15 11:37:14 +0100 |
---|---|---|
committer | Sebastian Thiel <byronimo@gmail.com> | 2010-11-15 11:37:14 +0100 |
commit | a1e2f63e64875a29e8c01a7ae17f5744680167a5 (patch) | |
tree | 8feb612ac5df79ec31300359b2bd28c40424bec0 /lib/git | |
parent | a1d1d2cb421f16bd277d7c4ce88398ff0f5afb29 (diff) | |
download | gitpython-a1e2f63e64875a29e8c01a7ae17f5744680167a5.tar.gz |
submodule: Fleshed out interface, and a partial test which is not yet usable. It showed that the ConfigParser needs some work. If the root is set, it also needs to refer to the root_commit instead of to the root-tree, as it will have to decide whether it works on the working tree's version of the .gitmodules file or the one in the repository
Diffstat (limited to 'lib/git')
-rw-r--r-- | lib/git/config.py | 24 | ||||
m--------- | lib/git/ext/gitdb | 0 | ||||
-rw-r--r-- | lib/git/objects/submodule.py | 115 | ||||
-rw-r--r-- | lib/git/remote.py | 30 |
4 files changed, 139 insertions, 30 deletions
diff --git a/lib/git/config.py b/lib/git/config.py index 09bad0b6..e919838b 100644 --- a/lib/git/config.py +++ b/lib/git/config.py @@ -15,7 +15,7 @@ import cStringIO from git.odict import OrderedDict from git.util import LockFile -__all__ = ('GitConfigParser', ) +__all__ = ('GitConfigParser', 'SectionConstraint') class MetaParserBuilder(type): """Utlity class wrapping base-class methods into decorators that assure read-only properties""" @@ -63,7 +63,29 @@ def set_dirty_and_flush_changes(non_const_func): flush_changes.__name__ = non_const_func.__name__ return flush_changes + +class SectionConstraint(object): + """Constrains a ConfigParser to only option commands which are constrained to + always use the section we have been initialized with. + + It supports all ConfigParser methods that operate on an option""" + __slots__ = ("_config", "_section_name") + _valid_attrs_ = ("get_value", "set_value", "get", "set", "getint", "getfloat", "getboolean", "has_option") + def __init__(self, config, section): + self._config = config + self._section_name = section + + def __getattr__(self, attr): + if attr in self._valid_attrs_: + return lambda *args, **kwargs: self._call_config(attr, *args, **kwargs) + return super(SectionConstraint,self).__getattribute__(attr) + + def _call_config(self, method, *args, **kwargs): + """Call the configuration at the given method which must take a section name + as first argument""" + return getattr(self._config, method)(self._section_name, *args, **kwargs) + class GitConfigParser(cp.RawConfigParser, object): """Implements specifics required to read git style configuration files. diff --git a/lib/git/ext/gitdb b/lib/git/ext/gitdb -Subproject 78665b13ff4125f4ce3e5311d040c027bdc92a9 +Subproject 2ddc5bad224d8f545ef3bb2ab3df98dfe063c5b diff --git a/lib/git/objects/submodule.py b/lib/git/objects/submodule.py index 1f571a48..b0fd0e35 100644 --- a/lib/git/objects/submodule.py +++ b/lib/git/objects/submodule.py @@ -1,4 +1,8 @@ import base +from cStringIO import StringIO +from git.config import GitConfigParser +from git.util import join_path_native +from git.exc import InvalidGitRepositoryError, NoSuchPathError __all__ = ("Submodule", ) @@ -7,10 +11,115 @@ class Submodule(base.IndexObject): represents a commit in the submodule's repository which is to be checked out at the path of this instance. The submodule type does not have a string type associated with it, as it exists - solely as a marker in the tree and index""" + solely as a marker in the tree and index. + + All methods work in bare and non-bare repositories.""" + + kModulesFile = '.gitmodules' # this is a bogus type for base class compatability type = 'submodule' - # TODO: Add functions to retrieve a repo for the submodule, to allow - # its initiailization and handling + __slots__ = ('_root_tree', '_url', '_ref') + + def _set_cache_(self, attr): + if attr == 'size': + raise ValueError("Submodules do not have a size as they do not refer to anything in this repository") + elif attr == '_root_tree': + # set a default value, which is the root tree of the current head + self._root_tree = self.repo.tree() + elif attr in ('path', '_url', '_ref'): + reader = self.config_reader() + # default submodule values + self._path = reader.get_value('path') + self._url = reader.get_value('url') + # git-python extension values - optional + self._ref = reader.get_value('ref', 'master') + else: + super(Submodule, self)._set_cache_(attr) + # END handle attribute name + + def _fp_config(self): + """:return: Configuration file as StringIO - we only access it through the respective blob's data""" + return StringIO(self._root_tree[self.kModulesFile].datastream.read()) + + def _config_parser(self, read_only): + """:return: Config Parser constrained to our submodule in read or write mode""" + parser = GitConfigParser(self._fp_config(), read_only = read_only) + return SectionConstraint(parser, 'submodule "%s"' % self.path) + + #{ Edit Interface + + @classmethod + def add(cls, repo, path, url, skip_init=False): + """Add a new submodule to the given repository. This will alter the index + as well as the .gitmodules file, but will not create a new commit. + :param repo: Repository instance which should receive the submodule + :param path: repository-relative path at which the submodule should be located + It will be created as required during the repository initialization. + :param url: git-clone compatible URL, see git-clone reference for more information + :param skip_init: if True, the new repository will not be cloned to its location. + :return: The newly created submodule instance""" + + def set_root_tree(self, root_tree): + """Set this instance to use the given tree which is supposed to contain the + .gitmodules blob. + :param root_tree: Tree'ish reference pointing at the root_tree + :raise ValueError: if the root_tree didn't contain the .gitmodules blob.""" + tree = self.repo.tree(root_tree) + if self.kModulesFile not in tree: + raise ValueError("Tree %s did not contain the %s file" % (root_tree, self.kModulesFile)) + # END handle exceptions + self._root_tree = tree + + # clear the possibly changing values + del(self.path) + del(self._ref) + del(self._url) + + def config_writer(self): + """:return: a config writer instance allowing you to read and write the data + belonging to this submodule into the .gitmodules file.""" + return self._config_parser(read_only=False) + + #} END edit interface + + #{ Query Interface + + def module(self): + """:return: Repo instance initialized from the repository at our submodule path + :raise InvalidGitRepositoryError: if a repository was not available""" + if self.repo.bare: + raise InvalidGitRepositoryError("Cannot retrieve module repository in bare parent repositories") + # END handle bare mode + + repo_path = join_path_native(self.repo.working_tree_dir, self.path) + try: + return Repo(repo_path) + except (InvalidGitRepositoryError, NoSuchPathError): + raise InvalidGitRepositoryError("No valid repository at %s" % self.path) + # END handle exceptions + + def ref(self): + """:return: The reference's name that we are to checkout""" + return self._ref + + def url(self): + """:return: The url to the repository which our module-repository refers to""" + return self._url + + def root_tree(self): + """:return: Tree instance referring to the tree which contains the .gitmodules file + we are to use + :note: will always point to the current head's root tree if it was not set explicitly""" + return self._root_tree + + def config_reader(self): + """:return: ConfigReader instance which allows you to qurey the configuration values + of this submodule, as provided by the .gitmodules file + :note: The config reader will actually read the data directly from the repository + and thus does not need nor care about your working tree. + :note: Should be cached by the caller and only kept as long as needed""" + return self._config_parser.read_only(read_only=True) + + #} END query interface diff --git a/lib/git/remote.py b/lib/git/remote.py index 52dd787d..135e37d7 100644 --- a/lib/git/remote.py +++ b/lib/git/remote.py @@ -7,7 +7,8 @@ from exc import GitCommandError from objects import Commit -from ConfigParser import NoOptionError +from ConfigParser import NoOptionError +from config import SectionConstraint from git.util import ( LazyMixin, @@ -30,29 +31,6 @@ import os __all__ = ('RemoteProgress', 'PushInfo', 'FetchInfo', 'Remote') -class _SectionConstraint(object): - """Constrains a ConfigParser to only option commands which are constrained to - always use the section we have been initialized with. - - It supports all ConfigParser methods that operate on an option""" - __slots__ = ("_config", "_section_name") - _valid_attrs_ = ("get_value", "set_value", "get", "set", "getint", "getfloat", "getboolean", "has_option") - - def __init__(self, config, section): - self._config = config - self._section_name = section - - def __getattr__(self, attr): - if attr in self._valid_attrs_: - return lambda *args, **kwargs: self._call_config(attr, *args, **kwargs) - return super(_SectionConstraint,self).__getattribute__(attr) - - def _call_config(self, method, *args, **kwargs): - """Call the configuration at the given method which must take a section name - as first argument""" - return getattr(self._config, method)(self._section_name, *args, **kwargs) - - class RemoteProgress(object): """ Handler providing an interface to parse progress information emitted by git-push @@ -449,7 +427,7 @@ class Remote(LazyMixin, Iterable): def _set_cache_(self, attr): if attr == "_config_reader": - self._config_reader = _SectionConstraint(self.repo.config_reader(), self._config_section_name()) + self._config_reader = SectionConstraint(self.repo.config_reader(), self._config_section_name()) else: super(Remote, self)._set_cache_(attr) @@ -735,4 +713,4 @@ class Remote(LazyMixin, Iterable): # clear our cache to assure we re-read the possibly changed configuration del(self._config_reader) - return _SectionConstraint(writer, self._config_section_name()) + return SectionConstraint(writer, self._config_section_name()) |