66
77__all__ = ("Submodule" , )
88
9+ class SubmoduleConfigParser (GitConfigParser ):
10+ """Catches calls to _write, and updates the .gitmodules blob in the index
11+ with the new data, if we have written into a stream. Otherwise it will
12+ add the local file to the index to make it correspond with the working tree."""
13+ _mutating_methods_ = tuple ()
14+
15+
916class Submodule (base .IndexObject ):
1017 """Implements access to a git submodule. They are special in that their sha
1118 represents a commit in the submodule's repository which is to be checked out
@@ -20,14 +27,14 @@ class Submodule(base.IndexObject):
2027 # this is a bogus type for base class compatability
2128 type = 'submodule'
2229
23- __slots__ = ('_root_tree ' , '_url' , '_ref' )
30+ __slots__ = ('_parent_commit ' , '_url' , '_ref' )
2431
2532 def _set_cache_ (self , attr ):
2633 if attr == 'size' :
2734 raise ValueError ("Submodules do not have a size as they do not refer to anything in this repository" )
28- elif attr == '_root_tree ' :
35+ elif attr == '_parent_commit ' :
2936 # set a default value, which is the root tree of the current head
30- self ._root_tree = self .repo .tree ()
37+ self ._parent_commit = self .repo .commit ()
3138 elif attr in ('path' , '_url' , '_ref' ):
3239 reader = self .config_reader ()
3340 # default submodule values
@@ -39,13 +46,26 @@ def _set_cache_(self, attr):
3946 super (Submodule , self )._set_cache_ (attr )
4047 # END handle attribute name
4148
42- def _fp_config (self ):
49+ def _sio_modules (self ):
4350 """:return: Configuration file as StringIO - we only access it through the respective blob's data"""
44- return StringIO (self ._root_tree [self .kModulesFile ].datastream .read ())
51+ sio = StringIO (self ._parent_commit .tree [self .kModulesFile ].datastream .read ())
52+ sio .name = self .kModulesFile
53+ return sio
4554
4655 def _config_parser (self , read_only ):
4756 """:return: Config Parser constrained to our submodule in read or write mode"""
48- parser = GitConfigParser (self ._fp_config (), read_only = read_only )
57+ parent_matches_head = self .repo .head .commit == self ._parent_commit
58+ if not self .repo .bare and parent_matches_head :
59+ fp_module = self .kModulesFile
60+ else :
61+ fp_module = self ._sio_modules ()
62+ # END handle non-bare working tree
63+
64+ if not read_only and not parent_matches_head :
65+ raise ValueError ("Cannot write blobs of 'historical' submodule configurations" )
66+ # END handle writes of historical submodules
67+
68+ parser = GitConfigParser (fp_module , read_only = read_only )
4969 return SectionConstraint (parser , 'submodule "%s"' % self .path )
5070
5171 #{ Edit Interface
@@ -61,21 +81,24 @@ def add(cls, repo, path, url, skip_init=False):
6181 :param skip_init: if True, the new repository will not be cloned to its location.
6282 :return: The newly created submodule instance"""
6383
64- def set_root_tree (self , root_tree ):
65- """Set this instance to use the given tree which is supposed to contain the
66- .gitmodules blob.
67- :param root_tree: Tree 'ish reference pointing at the root_tree
68- :raise ValueError: if the root_tree didn't contain the .gitmodules blob."""
69- tree = self .repo .tree ( root_tree )
70- if self .kModulesFile not in tree :
71- raise ValueError ("Tree %s did not contain the %s file" % (root_tree , self .kModulesFile ))
84+ def set_parent_commit (self , commit ):
85+ """Set this instance to use the given commit whose tree is supposed to
86+ contain the .gitmodules blob.
87+ :param commit: Commit 'ish reference pointing at the root_tree
88+ :raise ValueError: if the commit's tree didn't contain the .gitmodules blob."""
89+ pcommit = self .repo .commit ( commit )
90+ if self .kModulesFile not in pcommit . tree :
91+ raise ValueError ("Tree of commit %s did not contain the %s file" % (commit , self .kModulesFile ))
7292 # END handle exceptions
73- self ._root_tree = tree
93+ self ._parent_commit = pcommit
7494
75- # clear the possibly changing values
76- del (self .path )
77- del (self ._ref )
78- del (self ._url )
95+ # clear the possibly changed values
96+ for name in ('path' , '_ref' , '_url' ):
97+ try :
98+ delattr (self , name )
99+ except AttributeError :
100+ pass
101+ # END for each name to delete
79102
80103 def config_writer (self ):
81104 """:return: a config writer instance allowing you to read and write the data
@@ -108,11 +131,10 @@ def url(self):
108131 """:return: The url to the repository which our module-repository refers to"""
109132 return self ._url
110133
111- def root_tree (self ):
112- """:return: Tree instance referring to the tree which contains the .gitmodules file
113- we are to use
114- :note: will always point to the current head's root tree if it was not set explicitly"""
115- return self ._root_tree
134+ def parent_commit (self ):
135+ """:return: Commit instance with the tree containing the .gitmodules file
136+ :note: will always point to the current head's commit if it was not set explicitly"""
137+ return self ._parent_commit
116138
117139 def config_reader (self ):
118140 """:return: ConfigReader instance which allows you to qurey the configuration values
0 commit comments