summaryrefslogtreecommitdiff
path: root/lib/git/objects/submodule.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/git/objects/submodule.py')
-rw-r--r--lib/git/objects/submodule.py70
1 files changed, 46 insertions, 24 deletions
diff --git a/lib/git/objects/submodule.py b/lib/git/objects/submodule.py
index b0fd0e35..b9bcfc07 100644
--- a/lib/git/objects/submodule.py
+++ b/lib/git/objects/submodule.py
@@ -6,6 +6,13 @@ from git.exc import InvalidGitRepositoryError, NoSuchPathError
__all__ = ("Submodule", )
+class SubmoduleConfigParser(GitConfigParser):
+ """Catches calls to _write, and updates the .gitmodules blob in the index
+ with the new data, if we have written into a stream. Otherwise it will
+ add the local file to the index to make it correspond with the working tree."""
+ _mutating_methods_ = tuple()
+
+
class Submodule(base.IndexObject):
"""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
@@ -20,14 +27,14 @@ class Submodule(base.IndexObject):
# this is a bogus type for base class compatability
type = 'submodule'
- __slots__ = ('_root_tree', '_url', '_ref')
+ __slots__ = ('_parent_commit', '_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':
+ elif attr == '_parent_commit':
# set a default value, which is the root tree of the current head
- self._root_tree = self.repo.tree()
+ self._parent_commit = self.repo.commit()
elif attr in ('path', '_url', '_ref'):
reader = self.config_reader()
# default submodule values
@@ -39,13 +46,26 @@ class Submodule(base.IndexObject):
super(Submodule, self)._set_cache_(attr)
# END handle attribute name
- def _fp_config(self):
+ def _sio_modules(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())
+ sio = StringIO(self._parent_commit.tree[self.kModulesFile].datastream.read())
+ sio.name = self.kModulesFile
+ return sio
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)
+ parent_matches_head = self.repo.head.commit == self._parent_commit
+ if not self.repo.bare and parent_matches_head:
+ fp_module = self.kModulesFile
+ else:
+ fp_module = self._sio_modules()
+ # END handle non-bare working tree
+
+ if not read_only and not parent_matches_head:
+ raise ValueError("Cannot write blobs of 'historical' submodule configurations")
+ # END handle writes of historical submodules
+
+ parser = GitConfigParser(fp_module, read_only = read_only)
return SectionConstraint(parser, 'submodule "%s"' % self.path)
#{ Edit Interface
@@ -61,21 +81,24 @@ class Submodule(base.IndexObject):
: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))
+ def set_parent_commit(self, commit):
+ """Set this instance to use the given commit whose tree is supposed to
+ contain the .gitmodules blob.
+ :param commit: Commit'ish reference pointing at the root_tree
+ :raise ValueError: if the commit's tree didn't contain the .gitmodules blob."""
+ pcommit = self.repo.commit(commit)
+ if self.kModulesFile not in pcommit.tree:
+ raise ValueError("Tree of commit %s did not contain the %s file" % (commit, self.kModulesFile))
# END handle exceptions
- self._root_tree = tree
+ self._parent_commit = pcommit
- # clear the possibly changing values
- del(self.path)
- del(self._ref)
- del(self._url)
+ # clear the possibly changed values
+ for name in ('path', '_ref', '_url'):
+ try:
+ delattr(self, name)
+ except AttributeError:
+ pass
+ # END for each name to delete
def config_writer(self):
""":return: a config writer instance allowing you to read and write the data
@@ -108,11 +131,10 @@ class Submodule(base.IndexObject):
""":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 parent_commit(self):
+ """:return: Commit instance with the tree containing the .gitmodules file
+ :note: will always point to the current head's commit if it was not set explicitly"""
+ return self._parent_commit
def config_reader(self):
""":return: ConfigReader instance which allows you to qurey the configuration values