summaryrefslogtreecommitdiff
path: root/lib/git
diff options
context:
space:
mode:
Diffstat (limited to 'lib/git')
-rw-r--r--lib/git/config.py24
m---------lib/git/ext/gitdb0
-rw-r--r--lib/git/objects/submodule.py115
-rw-r--r--lib/git/remote.py30
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())