Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 4c34d5c

Browse files
committedNov 18, 2010
Split big submodule file into smaller files. Tried to manually get imports right, but its not yet tested
1 parent ebe8f64 commit 4c34d5c

File tree

4 files changed

+369
-345
lines changed

4 files changed

+369
-345
lines changed
 

‎lib/git/objects/submodule/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
from base import *
3+
from root import *

‎lib/git/objects/submodule.py renamed to ‎lib/git/objects/submodule/base.py

Lines changed: 6 additions & 345 deletions
Original file line numberDiff line numberDiff line change
@@ -1,111 +1,23 @@
1-
import base
2-
from util import Traversable
1+
import git.objects.base
2+
from util import *
3+
from git.objects.util import Traversable
34
from StringIO import StringIO # need a dict to set bloody .name field
45
from git.util import Iterable, join_path_native, to_native_path_linux
5-
from git.config import GitConfigParser, SectionConstraint
6+
from git.config import SectionConstraint
67
from git.exc import InvalidGitRepositoryError, NoSuchPathError
78
import stat
89
import git
910

1011
import os
1112
import sys
12-
import weakref
13+
1314
import shutil
1415

1516
__all__ = ("Submodule", "RootModule")
1617

17-
#{ Utilities
18-
19-
def sm_section(name):
20-
""":return: section title used in .gitmodules configuration file"""
21-
return 'submodule "%s"' % name
22-
23-
def sm_name(section):
24-
""":return: name of the submodule as parsed from the section name"""
25-
section = section.strip()
26-
return section[11:-1]
27-
28-
def mkhead(repo, path):
29-
""":return: New branch/head instance"""
30-
return git.Head(repo, git.Head.to_full_path(path))
31-
32-
def unbare_repo(func):
33-
"""Methods with this decorator raise InvalidGitRepositoryError if they
34-
encounter a bare repository"""
35-
def wrapper(self, *args, **kwargs):
36-
if self.repo.bare:
37-
raise InvalidGitRepositoryError("Method '%s' cannot operate on bare repositories" % func.__name__)
38-
#END bare method
39-
return func(self, *args, **kwargs)
40-
# END wrapper
41-
wrapper.__name__ = func.__name__
42-
return wrapper
43-
44-
def find_first_remote_branch(remotes, branch):
45-
"""Find the remote branch matching the name of the given branch or raise InvalidGitRepositoryError"""
46-
for remote in remotes:
47-
try:
48-
return remote.refs[branch.name]
49-
except IndexError:
50-
continue
51-
# END exception handling
52-
#END for remote
53-
raise InvalidGitRepositoryError("Didn't find remote branch %r in any of the given remotes", branch)
54-
55-
#} END utilities
56-
57-
58-
#{ Classes
59-
60-
class SubmoduleConfigParser(GitConfigParser):
61-
"""
62-
Catches calls to _write, and updates the .gitmodules blob in the index
63-
with the new data, if we have written into a stream. Otherwise it will
64-
add the local file to the index to make it correspond with the working tree.
65-
Additionally, the cache must be cleared
66-
67-
Please note that no mutating method will work in bare mode
68-
"""
69-
70-
def __init__(self, *args, **kwargs):
71-
self._smref = None
72-
self._index = None
73-
self._auto_write = True
74-
super(SubmoduleConfigParser, self).__init__(*args, **kwargs)
75-
76-
#{ Interface
77-
def set_submodule(self, submodule):
78-
"""Set this instance's submodule. It must be called before
79-
the first write operation begins"""
80-
self._smref = weakref.ref(submodule)
81-
82-
def flush_to_index(self):
83-
"""Flush changes in our configuration file to the index"""
84-
assert self._smref is not None
85-
# should always have a file here
86-
assert not isinstance(self._file_or_files, StringIO)
87-
88-
sm = self._smref()
89-
if sm is not None:
90-
index = self._index
91-
if index is None:
92-
index = sm.repo.index
93-
# END handle index
94-
index.add([sm.k_modules_file], write=self._auto_write)
95-
sm._clear_cache()
96-
# END handle weakref
97-
98-
#} END interface
99-
100-
#{ Overridden Methods
101-
def write(self):
102-
rval = super(SubmoduleConfigParser, self).write()
103-
self.flush_to_index()
104-
return rval
105-
# END overridden methods
10618

10719

108-
class Submodule(base.IndexObject, Iterable, Traversable):
20+
class Submodule(git.objects.base.IndexObject, Iterable, Traversable):
10921
"""Implements access to a git submodule. They are special in that their sha
11022
represents a commit in the submodule's repository which is to be checked out
11123
at the path of this instance.
@@ -879,255 +791,4 @@ def iter_items(cls, repo, parent_commit='HEAD'):
879791
# END for each section
880792

881793
#} END iterable interface
882-
883-
884-
class RootModule(Submodule):
885-
"""A (virtual) Root of all submodules in the given repository. It can be used
886-
to more easily traverse all submodules of the master repository"""
887-
888-
__slots__ = tuple()
889-
890-
k_root_name = '__ROOT__'
891-
892-
def __init__(self, repo):
893-
# repo, binsha, mode=None, path=None, name = None, parent_commit=None, url=None, ref=None)
894-
super(RootModule, self).__init__(
895-
repo,
896-
binsha = self.NULL_BIN_SHA,
897-
mode = self.k_default_mode,
898-
path = '',
899-
name = self.k_root_name,
900-
parent_commit = repo.head.commit,
901-
url = '',
902-
branch = mkhead(repo, self.k_head_default)
903-
)
904-
905-
906-
def _clear_cache(self):
907-
"""May not do anything"""
908-
pass
909-
910-
#{ Interface
911-
912-
def update(self, previous_commit=None, recursive=True, force_remove=False, init=True, to_latest_revision=False):
913-
"""Update the submodules of this repository to the current HEAD commit.
914-
This method behaves smartly by determining changes of the path of a submodules
915-
repository, next to changes to the to-be-checked-out commit or the branch to be
916-
checked out. This works if the submodules ID does not change.
917-
Additionally it will detect addition and removal of submodules, which will be handled
918-
gracefully.
919-
920-
:param previous_commit: If set to a commit'ish, the commit we should use
921-
as the previous commit the HEAD pointed to before it was set to the commit it points to now.
922-
If None, it defaults to ORIG_HEAD otherwise, or the parent of the current
923-
commit if it is not given
924-
:param recursive: if True, the children of submodules will be updated as well
925-
using the same technique
926-
:param force_remove: If submodules have been deleted, they will be forcibly removed.
927-
Otherwise the update may fail if a submodule's repository cannot be deleted as
928-
changes have been made to it (see Submodule.update() for more information)
929-
:param init: If we encounter a new module which would need to be initialized, then do it.
930-
:param to_latest_revision: If True, instead of checking out the revision pointed to
931-
by this submodule's sha, the checked out tracking branch will be merged with the
932-
newest remote branch fetched from the repository's origin"""
933-
if self.repo.bare:
934-
raise InvalidGitRepositoryError("Cannot update submodules in bare repositories")
935-
# END handle bare
936-
937-
repo = self.repo
938-
939-
# HANDLE COMMITS
940-
##################
941-
cur_commit = repo.head.commit
942-
if previous_commit is None:
943-
symref = repo.head.orig_head()
944-
try:
945-
previous_commit = symref.commit
946-
except Exception:
947-
pcommits = cur_commit.parents
948-
if pcommits:
949-
previous_commit = pcommits[0]
950-
else:
951-
# in this special case, we just diff against ourselve, which
952-
# means exactly no change
953-
previous_commit = cur_commit
954-
# END handle initial commit
955-
# END no ORIG_HEAD
956-
else:
957-
previous_commit = repo.commit(previous_commit) # obtain commit object
958-
# END handle previous commit
959-
960-
961-
psms = self.list_items(repo, parent_commit=previous_commit)
962-
sms = self.list_items(self.module())
963-
spsms = set(psms)
964-
ssms = set(sms)
965-
966-
# HANDLE REMOVALS
967-
###################
968-
for rsm in (spsms - ssms):
969-
# fake it into thinking its at the current commit to allow deletion
970-
# of previous module. Trigger the cache to be updated before that
971-
#rsm.url
972-
rsm._parent_commit = repo.head.commit
973-
rsm.remove(configuration=False, module=True, force=force_remove)
974-
# END for each removed submodule
975-
976-
# HANDLE PATH RENAMES
977-
#####################
978-
# url changes + branch changes
979-
for csm in (spsms & ssms):
980-
psm = psms[csm.name]
981-
sm = sms[csm.name]
982-
983-
if sm.path != psm.path and psm.module_exists():
984-
# move the module to the new path
985-
psm.move(sm.path, module=True, configuration=False)
986-
# END handle path changes
987-
988-
if sm.module_exists():
989-
# handle url change
990-
if sm.url != psm.url:
991-
# Add the new remote, remove the old one
992-
# This way, if the url just changes, the commits will not
993-
# have to be re-retrieved
994-
nn = '__new_origin__'
995-
smm = sm.module()
996-
rmts = smm.remotes
997-
998-
# don't do anything if we already have the url we search in place
999-
if len([r for r in rmts if r.url == sm.url]) == 0:
1000-
1001-
1002-
assert nn not in [r.name for r in rmts]
1003-
smr = smm.create_remote(nn, sm.url)
1004-
smr.fetch()
1005-
1006-
# If we have a tracking branch, it should be available
1007-
# in the new remote as well.
1008-
if len([r for r in smr.refs if r.remote_head == sm.branch.name]) == 0:
1009-
raise ValueError("Submodule branch named %r was not available in new submodule remote at %r" % (sm.branch.name, sm.url))
1010-
# END head is not detached
1011-
1012-
# now delete the changed one
1013-
rmt_for_deletion = None
1014-
for remote in rmts:
1015-
if remote.url == psm.url:
1016-
rmt_for_deletion = remote
1017-
break
1018-
# END if urls match
1019-
# END for each remote
1020-
1021-
# if we didn't find a matching remote, but have exactly one,
1022-
# we can safely use this one
1023-
if rmt_for_deletion is None:
1024-
if len(rmts) == 1:
1025-
rmt_for_deletion = rmts[0]
1026-
else:
1027-
# if we have not found any remote with the original url
1028-
# we may not have a name. This is a special case,
1029-
# and its okay to fail here
1030-
# Alternatively we could just generate a unique name and leave all
1031-
# existing ones in place
1032-
raise InvalidGitRepositoryError("Couldn't find original remote-repo at url %r" % psm.url)
1033-
#END handle one single remote
1034-
# END handle check we found a remote
1035-
1036-
orig_name = rmt_for_deletion.name
1037-
smm.delete_remote(rmt_for_deletion)
1038-
# NOTE: Currently we leave tags from the deleted remotes
1039-
# as well as separate tracking branches in the possibly totally
1040-
# changed repository ( someone could have changed the url to
1041-
# another project ). At some point, one might want to clean
1042-
# it up, but the danger is high to remove stuff the user
1043-
# has added explicitly
1044-
1045-
# rename the new remote back to what it was
1046-
smr.rename(orig_name)
1047-
1048-
# early on, we verified that the our current tracking branch
1049-
# exists in the remote. Now we have to assure that the
1050-
# sha we point to is still contained in the new remote
1051-
# tracking branch.
1052-
smsha = sm.binsha
1053-
found = False
1054-
rref = smr.refs[self.branch.name]
1055-
for c in rref.commit.traverse():
1056-
if c.binsha == smsha:
1057-
found = True
1058-
break
1059-
# END traverse all commits in search for sha
1060-
# END for each commit
1061-
1062-
if not found:
1063-
# adjust our internal binsha to use the one of the remote
1064-
# this way, it will be checked out in the next step
1065-
# This will change the submodule relative to us, so
1066-
# the user will be able to commit the change easily
1067-
print >> sys.stderr, "WARNING: Current sha %s was not contained in the tracking branch at the new remote, setting it the the remote's tracking branch" % sm.hexsha
1068-
sm.binsha = rref.commit.binsha
1069-
#END reset binsha
1070-
1071-
#NOTE: All checkout is performed by the base implementation of update
1072-
1073-
# END skip remote handling if new url already exists in module
1074-
# END handle url
1075-
1076-
if sm.branch != psm.branch:
1077-
# finally, create a new tracking branch which tracks the
1078-
# new remote branch
1079-
smm = sm.module()
1080-
smmr = smm.remotes
1081-
try:
1082-
tbr = git.Head.create(smm, sm.branch.name)
1083-
except git.GitCommandError, e:
1084-
if e.status != 128:
1085-
raise
1086-
#END handle something unexpected
1087-
1088-
# ... or reuse the existing one
1089-
tbr = git.Head(smm, git.Head.to_full_path(sm.branch.name))
1090-
#END assure tracking branch exists
1091-
1092-
tbr.set_tracking_branch(find_first_remote_branch(smmr, sm.branch))
1093-
# figure out whether the previous tracking branch contains
1094-
# new commits compared to the other one, if not we can
1095-
# delete it.
1096-
try:
1097-
tbr = find_first_remote_branch(smmr, psm.branch)
1098-
if len(smm.git.cherry(tbr, psm.branch)) == 0:
1099-
psm.branch.delete(smm, psm.branch)
1100-
#END delete original tracking branch if there are no changes
1101-
except InvalidGitRepositoryError:
1102-
# ignore it if the previous branch couldn't be found in the
1103-
# current remotes, this just means we can't handle it
1104-
pass
1105-
# END exception handling
1106-
1107-
#NOTE: All checkout is done in the base implementation of update
1108-
1109-
#END handle branch
1110-
#END handle
1111-
# END for each common submodule
1112-
1113-
# FINALLY UPDATE ALL ACTUAL SUBMODULES
1114-
######################################
1115-
for sm in sms:
1116-
# update the submodule using the default method
1117-
sm.update(recursive=True, init=init, to_latest_revision=to_latest_revision)
1118-
1119-
# update recursively depth first - question is which inconsitent
1120-
# state will be better in case it fails somewhere. Defective branch
1121-
# or defective depth. The RootSubmodule type will never process itself,
1122-
# which was done in the previous expression
1123-
if recursive:
1124-
type(self)(sm.module()).update(recursive=True, force_remove=force_remove,
1125-
init=init, to_latest_revision=to_latest_revision)
1126-
#END handle recursive
1127-
# END for each submodule to update
1128794

1129-
def module(self):
1130-
""":return: the actual repository containing the submodules"""
1131-
return self.repo
1132-
#} END interface
1133-
#} END classes

‎lib/git/objects/submodule/root.py

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
from base import Submodule
2+
from git.exc import InvalidGitRepositoryError
3+
import git
4+
5+
import sys
6+
7+
__all__ = ["RootModule"]
8+
9+
10+
class RootModule(Submodule):
11+
"""A (virtual) Root of all submodules in the given repository. It can be used
12+
to more easily traverse all submodules of the master repository"""
13+
14+
__slots__ = tuple()
15+
16+
k_root_name = '__ROOT__'
17+
18+
def __init__(self, repo):
19+
# repo, binsha, mode=None, path=None, name = None, parent_commit=None, url=None, ref=None)
20+
super(RootModule, self).__init__(
21+
repo,
22+
binsha = self.NULL_BIN_SHA,
23+
mode = self.k_default_mode,
24+
path = '',
25+
name = self.k_root_name,
26+
parent_commit = repo.head.commit,
27+
url = '',
28+
branch = mkhead(repo, self.k_head_default)
29+
)
30+
31+
32+
def _clear_cache(self):
33+
"""May not do anything"""
34+
pass
35+
36+
#{ Interface
37+
38+
def update(self, previous_commit=None, recursive=True, force_remove=False, init=True, to_latest_revision=False):
39+
"""Update the submodules of this repository to the current HEAD commit.
40+
This method behaves smartly by determining changes of the path of a submodules
41+
repository, next to changes to the to-be-checked-out commit or the branch to be
42+
checked out. This works if the submodules ID does not change.
43+
Additionally it will detect addition and removal of submodules, which will be handled
44+
gracefully.
45+
46+
:param previous_commit: If set to a commit'ish, the commit we should use
47+
as the previous commit the HEAD pointed to before it was set to the commit it points to now.
48+
If None, it defaults to ORIG_HEAD otherwise, or the parent of the current
49+
commit if it is not given
50+
:param recursive: if True, the children of submodules will be updated as well
51+
using the same technique
52+
:param force_remove: If submodules have been deleted, they will be forcibly removed.
53+
Otherwise the update may fail if a submodule's repository cannot be deleted as
54+
changes have been made to it (see Submodule.update() for more information)
55+
:param init: If we encounter a new module which would need to be initialized, then do it.
56+
:param to_latest_revision: If True, instead of checking out the revision pointed to
57+
by this submodule's sha, the checked out tracking branch will be merged with the
58+
newest remote branch fetched from the repository's origin"""
59+
if self.repo.bare:
60+
raise InvalidGitRepositoryError("Cannot update submodules in bare repositories")
61+
# END handle bare
62+
63+
repo = self.repo
64+
65+
# HANDLE COMMITS
66+
##################
67+
cur_commit = repo.head.commit
68+
if previous_commit is None:
69+
symref = repo.head.orig_head()
70+
try:
71+
previous_commit = symref.commit
72+
except Exception:
73+
pcommits = cur_commit.parents
74+
if pcommits:
75+
previous_commit = pcommits[0]
76+
else:
77+
# in this special case, we just diff against ourselve, which
78+
# means exactly no change
79+
previous_commit = cur_commit
80+
# END handle initial commit
81+
# END no ORIG_HEAD
82+
else:
83+
previous_commit = repo.commit(previous_commit) # obtain commit object
84+
# END handle previous commit
85+
86+
87+
psms = self.list_items(repo, parent_commit=previous_commit)
88+
sms = self.list_items(self.module())
89+
spsms = set(psms)
90+
ssms = set(sms)
91+
92+
# HANDLE REMOVALS
93+
###################
94+
for rsm in (spsms - ssms):
95+
# fake it into thinking its at the current commit to allow deletion
96+
# of previous module. Trigger the cache to be updated before that
97+
#rsm.url
98+
rsm._parent_commit = repo.head.commit
99+
rsm.remove(configuration=False, module=True, force=force_remove)
100+
# END for each removed submodule
101+
102+
# HANDLE PATH RENAMES
103+
#####################
104+
# url changes + branch changes
105+
for csm in (spsms & ssms):
106+
psm = psms[csm.name]
107+
sm = sms[csm.name]
108+
109+
if sm.path != psm.path and psm.module_exists():
110+
# move the module to the new path
111+
psm.move(sm.path, module=True, configuration=False)
112+
# END handle path changes
113+
114+
if sm.module_exists():
115+
# handle url change
116+
if sm.url != psm.url:
117+
# Add the new remote, remove the old one
118+
# This way, if the url just changes, the commits will not
119+
# have to be re-retrieved
120+
nn = '__new_origin__'
121+
smm = sm.module()
122+
rmts = smm.remotes
123+
124+
# don't do anything if we already have the url we search in place
125+
if len([r for r in rmts if r.url == sm.url]) == 0:
126+
127+
128+
assert nn not in [r.name for r in rmts]
129+
smr = smm.create_remote(nn, sm.url)
130+
smr.fetch()
131+
132+
# If we have a tracking branch, it should be available
133+
# in the new remote as well.
134+
if len([r for r in smr.refs if r.remote_head == sm.branch.name]) == 0:
135+
raise ValueError("Submodule branch named %r was not available in new submodule remote at %r" % (sm.branch.name, sm.url))
136+
# END head is not detached
137+
138+
# now delete the changed one
139+
rmt_for_deletion = None
140+
for remote in rmts:
141+
if remote.url == psm.url:
142+
rmt_for_deletion = remote
143+
break
144+
# END if urls match
145+
# END for each remote
146+
147+
# if we didn't find a matching remote, but have exactly one,
148+
# we can safely use this one
149+
if rmt_for_deletion is None:
150+
if len(rmts) == 1:
151+
rmt_for_deletion = rmts[0]
152+
else:
153+
# if we have not found any remote with the original url
154+
# we may not have a name. This is a special case,
155+
# and its okay to fail here
156+
# Alternatively we could just generate a unique name and leave all
157+
# existing ones in place
158+
raise InvalidGitRepositoryError("Couldn't find original remote-repo at url %r" % psm.url)
159+
#END handle one single remote
160+
# END handle check we found a remote
161+
162+
orig_name = rmt_for_deletion.name
163+
smm.delete_remote(rmt_for_deletion)
164+
# NOTE: Currently we leave tags from the deleted remotes
165+
# as well as separate tracking branches in the possibly totally
166+
# changed repository ( someone could have changed the url to
167+
# another project ). At some point, one might want to clean
168+
# it up, but the danger is high to remove stuff the user
169+
# has added explicitly
170+
171+
# rename the new remote back to what it was
172+
smr.rename(orig_name)
173+
174+
# early on, we verified that the our current tracking branch
175+
# exists in the remote. Now we have to assure that the
176+
# sha we point to is still contained in the new remote
177+
# tracking branch.
178+
smsha = sm.binsha
179+
found = False
180+
rref = smr.refs[self.branch.name]
181+
for c in rref.commit.traverse():
182+
if c.binsha == smsha:
183+
found = True
184+
break
185+
# END traverse all commits in search for sha
186+
# END for each commit
187+
188+
if not found:
189+
# adjust our internal binsha to use the one of the remote
190+
# this way, it will be checked out in the next step
191+
# This will change the submodule relative to us, so
192+
# the user will be able to commit the change easily
193+
print >> sys.stderr, "WARNING: Current sha %s was not contained in the tracking branch at the new remote, setting it the the remote's tracking branch" % sm.hexsha
194+
sm.binsha = rref.commit.binsha
195+
#END reset binsha
196+
197+
#NOTE: All checkout is performed by the base implementation of update
198+
199+
# END skip remote handling if new url already exists in module
200+
# END handle url
201+
202+
if sm.branch != psm.branch:
203+
# finally, create a new tracking branch which tracks the
204+
# new remote branch
205+
smm = sm.module()
206+
smmr = smm.remotes
207+
try:
208+
tbr = git.Head.create(smm, sm.branch.name)
209+
except git.GitCommandError, e:
210+
if e.status != 128:
211+
raise
212+
#END handle something unexpected
213+
214+
# ... or reuse the existing one
215+
tbr = git.Head(smm, git.Head.to_full_path(sm.branch.name))
216+
#END assure tracking branch exists
217+
218+
tbr.set_tracking_branch(find_first_remote_branch(smmr, sm.branch))
219+
# figure out whether the previous tracking branch contains
220+
# new commits compared to the other one, if not we can
221+
# delete it.
222+
try:
223+
tbr = find_first_remote_branch(smmr, psm.branch)
224+
if len(smm.git.cherry(tbr, psm.branch)) == 0:
225+
psm.branch.delete(smm, psm.branch)
226+
#END delete original tracking branch if there are no changes
227+
except InvalidGitRepositoryError:
228+
# ignore it if the previous branch couldn't be found in the
229+
# current remotes, this just means we can't handle it
230+
pass
231+
# END exception handling
232+
233+
#NOTE: All checkout is done in the base implementation of update
234+
235+
#END handle branch
236+
#END handle
237+
# END for each common submodule
238+
239+
# FINALLY UPDATE ALL ACTUAL SUBMODULES
240+
######################################
241+
for sm in sms:
242+
# update the submodule using the default method
243+
sm.update(recursive=True, init=init, to_latest_revision=to_latest_revision)
244+
245+
# update recursively depth first - question is which inconsitent
246+
# state will be better in case it fails somewhere. Defective branch
247+
# or defective depth. The RootSubmodule type will never process itself,
248+
# which was done in the previous expression
249+
if recursive:
250+
type(self)(sm.module()).update(recursive=True, force_remove=force_remove,
251+
init=init, to_latest_revision=to_latest_revision)
252+
#END handle recursive
253+
# END for each submodule to update
254+
255+
def module(self):
256+
""":return: the actual repository containing the submodules"""
257+
return self.repo
258+
#} END interface
259+
#} END classes

‎lib/git/objects/submodule/util.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import git
2+
from git.exc import InvalidGitRepositoryError
3+
from git.config import GitConfigParser
4+
from StringIO import StringIO
5+
import weakref
6+
7+
__all__ = ( 'sm_section', 'sm_name', 'mkhead', 'unbare_repo', 'find_first_remote_branch',
8+
'SubmoduleConfigParser')
9+
10+
#{ Utilities
11+
12+
def sm_section(name):
13+
""":return: section title used in .gitmodules configuration file"""
14+
return 'submodule "%s"' % name
15+
16+
def sm_name(section):
17+
""":return: name of the submodule as parsed from the section name"""
18+
section = section.strip()
19+
return section[11:-1]
20+
21+
def mkhead(repo, path):
22+
""":return: New branch/head instance"""
23+
return git.Head(repo, git.Head.to_full_path(path))
24+
25+
def unbare_repo(func):
26+
"""Methods with this decorator raise InvalidGitRepositoryError if they
27+
encounter a bare repository"""
28+
def wrapper(self, *args, **kwargs):
29+
if self.repo.bare:
30+
raise InvalidGitRepositoryError("Method '%s' cannot operate on bare repositories" % func.__name__)
31+
#END bare method
32+
return func(self, *args, **kwargs)
33+
# END wrapper
34+
wrapper.__name__ = func.__name__
35+
return wrapper
36+
37+
def find_first_remote_branch(remotes, branch):
38+
"""Find the remote branch matching the name of the given branch or raise InvalidGitRepositoryError"""
39+
for remote in remotes:
40+
try:
41+
return remote.refs[branch.name]
42+
except IndexError:
43+
continue
44+
# END exception handling
45+
#END for remote
46+
raise InvalidGitRepositoryError("Didn't find remote branch %r in any of the given remotes", branch)
47+
48+
#} END utilities
49+
50+
51+
#{ Classes
52+
53+
class SubmoduleConfigParser(GitConfigParser):
54+
"""
55+
Catches calls to _write, and updates the .gitmodules blob in the index
56+
with the new data, if we have written into a stream. Otherwise it will
57+
add the local file to the index to make it correspond with the working tree.
58+
Additionally, the cache must be cleared
59+
60+
Please note that no mutating method will work in bare mode
61+
"""
62+
63+
def __init__(self, *args, **kwargs):
64+
self._smref = None
65+
self._index = None
66+
self._auto_write = True
67+
super(SubmoduleConfigParser, self).__init__(*args, **kwargs)
68+
69+
#{ Interface
70+
def set_submodule(self, submodule):
71+
"""Set this instance's submodule. It must be called before
72+
the first write operation begins"""
73+
self._smref = weakref.ref(submodule)
74+
75+
def flush_to_index(self):
76+
"""Flush changes in our configuration file to the index"""
77+
assert self._smref is not None
78+
# should always have a file here
79+
assert not isinstance(self._file_or_files, StringIO)
80+
81+
sm = self._smref()
82+
if sm is not None:
83+
index = self._index
84+
if index is None:
85+
index = sm.repo.index
86+
# END handle index
87+
index.add([sm.k_modules_file], write=self._auto_write)
88+
sm._clear_cache()
89+
# END handle weakref
90+
91+
#} END interface
92+
93+
#{ Overridden Methods
94+
def write(self):
95+
rval = super(SubmoduleConfigParser, self).write()
96+
self.flush_to_index()
97+
return rval
98+
# END overridden methods
99+
100+
101+
#} END classes

0 commit comments

Comments
 (0)
Please sign in to comment.