##// END OF EJS Templates
subrepo: fix recording of + in .hgsubstate (issue2217)
Matt Mackall -
r11463:f0ea9355 stable
parent child Browse files
Show More
@@ -1,388 +1,390 b''
1 # subrepo.py - sub-repository handling for Mercurial
1 # subrepo.py - sub-repository handling for Mercurial
2 #
2 #
3 # Copyright 2009-2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2009-2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import errno, os, re, xml.dom.minidom, shutil, urlparse, posixpath
8 import errno, os, re, xml.dom.minidom, shutil, urlparse, posixpath
9 from i18n import _
9 from i18n import _
10 import config, util, node, error
10 import config, util, node, error
11 hg = None
11 hg = None
12
12
13 nullstate = ('', '', 'empty')
13 nullstate = ('', '', 'empty')
14
14
15 def state(ctx):
15 def state(ctx):
16 p = config.config()
16 p = config.config()
17 def read(f, sections=None, remap=None):
17 def read(f, sections=None, remap=None):
18 if f in ctx:
18 if f in ctx:
19 p.parse(f, ctx[f].data(), sections, remap, read)
19 p.parse(f, ctx[f].data(), sections, remap, read)
20 else:
20 else:
21 raise util.Abort(_("subrepo spec file %s not found") % f)
21 raise util.Abort(_("subrepo spec file %s not found") % f)
22
22
23 if '.hgsub' in ctx:
23 if '.hgsub' in ctx:
24 read('.hgsub')
24 read('.hgsub')
25
25
26 rev = {}
26 rev = {}
27 if '.hgsubstate' in ctx:
27 if '.hgsubstate' in ctx:
28 try:
28 try:
29 for l in ctx['.hgsubstate'].data().splitlines():
29 for l in ctx['.hgsubstate'].data().splitlines():
30 revision, path = l.split(" ", 1)
30 revision, path = l.split(" ", 1)
31 rev[path] = revision
31 rev[path] = revision
32 except IOError, err:
32 except IOError, err:
33 if err.errno != errno.ENOENT:
33 if err.errno != errno.ENOENT:
34 raise
34 raise
35
35
36 state = {}
36 state = {}
37 for path, src in p[''].items():
37 for path, src in p[''].items():
38 kind = 'hg'
38 kind = 'hg'
39 if src.startswith('['):
39 if src.startswith('['):
40 if ']' not in src:
40 if ']' not in src:
41 raise util.Abort(_('missing ] in subrepo source'))
41 raise util.Abort(_('missing ] in subrepo source'))
42 kind, src = src.split(']', 1)
42 kind, src = src.split(']', 1)
43 kind = kind[1:]
43 kind = kind[1:]
44 state[path] = (src.strip(), rev.get(path, ''), kind)
44 state[path] = (src.strip(), rev.get(path, ''), kind)
45
45
46 return state
46 return state
47
47
48 def writestate(repo, state):
48 def writestate(repo, state):
49 repo.wwrite('.hgsubstate',
49 repo.wwrite('.hgsubstate',
50 ''.join(['%s %s\n' % (state[s][1], s)
50 ''.join(['%s %s\n' % (state[s][1], s)
51 for s in sorted(state)]), '')
51 for s in sorted(state)]), '')
52
52
53 def submerge(repo, wctx, mctx, actx):
53 def submerge(repo, wctx, mctx, actx):
54 # working context, merging context, ancestor context
54 # working context, merging context, ancestor context
55 if mctx == actx: # backwards?
55 if mctx == actx: # backwards?
56 actx = wctx.p1()
56 actx = wctx.p1()
57 s1 = wctx.substate
57 s1 = wctx.substate
58 s2 = mctx.substate
58 s2 = mctx.substate
59 sa = actx.substate
59 sa = actx.substate
60 sm = {}
60 sm = {}
61
61
62 repo.ui.debug("subrepo merge %s %s %s\n" % (wctx, mctx, actx))
62 repo.ui.debug("subrepo merge %s %s %s\n" % (wctx, mctx, actx))
63
63
64 def debug(s, msg, r=""):
64 def debug(s, msg, r=""):
65 if r:
65 if r:
66 r = "%s:%s:%s" % r
66 r = "%s:%s:%s" % r
67 repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r))
67 repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r))
68
68
69 for s, l in s1.items():
69 for s, l in s1.items():
70 ld = l # local state with possible dirty flag for compares
70 if wctx != actx and wctx.sub(s).dirty():
71 if wctx != actx and wctx.sub(s).dirty():
71 l = (l[0], l[1] + "+")
72 ld = (l[0], l[1] + "+")
73
72 a = sa.get(s, nullstate)
74 a = sa.get(s, nullstate)
73 if s in s2:
75 if s in s2:
74 r = s2[s]
76 r = s2[s]
75 if l == r or r == a: # no change or local is newer
77 if ld == r or r == a: # no change or local is newer
76 sm[s] = l
78 sm[s] = l
77 continue
79 continue
78 elif l == a: # other side changed
80 elif ld == a: # other side changed
79 debug(s, "other changed, get", r)
81 debug(s, "other changed, get", r)
80 wctx.sub(s).get(r)
82 wctx.sub(s).get(r)
81 sm[s] = r
83 sm[s] = r
82 elif l[0] != r[0]: # sources differ
84 elif ld[0] != r[0]: # sources differ
83 if repo.ui.promptchoice(
85 if repo.ui.promptchoice(
84 _(' subrepository sources for %s differ\n'
86 _(' subrepository sources for %s differ\n'
85 'use (l)ocal source (%s) or (r)emote source (%s)?')
87 'use (l)ocal source (%s) or (r)emote source (%s)?')
86 % (s, l[0], r[0]),
88 % (s, l[0], r[0]),
87 (_('&Local'), _('&Remote')), 0):
89 (_('&Local'), _('&Remote')), 0):
88 debug(s, "prompt changed, get", r)
90 debug(s, "prompt changed, get", r)
89 wctx.sub(s).get(r)
91 wctx.sub(s).get(r)
90 sm[s] = r
92 sm[s] = r
91 elif l[1] == a[1]: # local side is unchanged
93 elif ld[1] == a[1]: # local side is unchanged
92 debug(s, "other side changed, get", r)
94 debug(s, "other side changed, get", r)
93 wctx.sub(s).get(r)
95 wctx.sub(s).get(r)
94 sm[s] = r
96 sm[s] = r
95 else:
97 else:
96 debug(s, "both sides changed, merge with", r)
98 debug(s, "both sides changed, merge with", r)
97 wctx.sub(s).merge(r)
99 wctx.sub(s).merge(r)
98 sm[s] = l
100 sm[s] = l
99 elif l == a: # remote removed, local unchanged
101 elif ld == a: # remote removed, local unchanged
100 debug(s, "remote removed, remove")
102 debug(s, "remote removed, remove")
101 wctx.sub(s).remove()
103 wctx.sub(s).remove()
102 else:
104 else:
103 if repo.ui.promptchoice(
105 if repo.ui.promptchoice(
104 _(' local changed subrepository %s which remote removed\n'
106 _(' local changed subrepository %s which remote removed\n'
105 'use (c)hanged version or (d)elete?') % s,
107 'use (c)hanged version or (d)elete?') % s,
106 (_('&Changed'), _('&Delete')), 0):
108 (_('&Changed'), _('&Delete')), 0):
107 debug(s, "prompt remove")
109 debug(s, "prompt remove")
108 wctx.sub(s).remove()
110 wctx.sub(s).remove()
109
111
110 for s, r in s2.items():
112 for s, r in s2.items():
111 if s in s1:
113 if s in s1:
112 continue
114 continue
113 elif s not in sa:
115 elif s not in sa:
114 debug(s, "remote added, get", r)
116 debug(s, "remote added, get", r)
115 mctx.sub(s).get(r)
117 mctx.sub(s).get(r)
116 sm[s] = r
118 sm[s] = r
117 elif r != sa[s]:
119 elif r != sa[s]:
118 if repo.ui.promptchoice(
120 if repo.ui.promptchoice(
119 _(' remote changed subrepository %s which local removed\n'
121 _(' remote changed subrepository %s which local removed\n'
120 'use (c)hanged version or (d)elete?') % s,
122 'use (c)hanged version or (d)elete?') % s,
121 (_('&Changed'), _('&Delete')), 0) == 0:
123 (_('&Changed'), _('&Delete')), 0) == 0:
122 debug(s, "prompt recreate", r)
124 debug(s, "prompt recreate", r)
123 wctx.sub(s).get(r)
125 wctx.sub(s).get(r)
124 sm[s] = r
126 sm[s] = r
125
127
126 # record merged .hgsubstate
128 # record merged .hgsubstate
127 writestate(repo, sm)
129 writestate(repo, sm)
128
130
129 def relpath(sub):
131 def relpath(sub):
130 if not hasattr(sub, '_repo'):
132 if not hasattr(sub, '_repo'):
131 return sub._path
133 return sub._path
132 parent = sub._repo
134 parent = sub._repo
133 while hasattr(parent, '_subparent'):
135 while hasattr(parent, '_subparent'):
134 parent = parent._subparent
136 parent = parent._subparent
135 return sub._repo.root[len(parent.root)+1:]
137 return sub._repo.root[len(parent.root)+1:]
136
138
137 def _abssource(repo, push=False):
139 def _abssource(repo, push=False):
138 if hasattr(repo, '_subparent'):
140 if hasattr(repo, '_subparent'):
139 source = repo._subsource
141 source = repo._subsource
140 if source.startswith('/') or '://' in source:
142 if source.startswith('/') or '://' in source:
141 return source
143 return source
142 parent = _abssource(repo._subparent, push)
144 parent = _abssource(repo._subparent, push)
143 if '://' in parent:
145 if '://' in parent:
144 if parent[-1] == '/':
146 if parent[-1] == '/':
145 parent = parent[:-1]
147 parent = parent[:-1]
146 r = urlparse.urlparse(parent + '/' + source)
148 r = urlparse.urlparse(parent + '/' + source)
147 r = urlparse.urlunparse((r[0], r[1],
149 r = urlparse.urlunparse((r[0], r[1],
148 posixpath.normpath(r[2]),
150 posixpath.normpath(r[2]),
149 r[3], r[4], r[5]))
151 r[3], r[4], r[5]))
150 return r
152 return r
151 return posixpath.normpath(os.path.join(parent, repo._subsource))
153 return posixpath.normpath(os.path.join(parent, repo._subsource))
152 if push and repo.ui.config('paths', 'default-push'):
154 if push and repo.ui.config('paths', 'default-push'):
153 return repo.ui.config('paths', 'default-push', repo.root)
155 return repo.ui.config('paths', 'default-push', repo.root)
154 return repo.ui.config('paths', 'default', repo.root)
156 return repo.ui.config('paths', 'default', repo.root)
155
157
156 def subrepo(ctx, path):
158 def subrepo(ctx, path):
157 # subrepo inherently violates our import layering rules
159 # subrepo inherently violates our import layering rules
158 # because it wants to make repo objects from deep inside the stack
160 # because it wants to make repo objects from deep inside the stack
159 # so we manually delay the circular imports to not break
161 # so we manually delay the circular imports to not break
160 # scripts that don't use our demand-loading
162 # scripts that don't use our demand-loading
161 global hg
163 global hg
162 import hg as h
164 import hg as h
163 hg = h
165 hg = h
164
166
165 util.path_auditor(ctx._repo.root)(path)
167 util.path_auditor(ctx._repo.root)(path)
166 state = ctx.substate.get(path, nullstate)
168 state = ctx.substate.get(path, nullstate)
167 if state[2] not in types:
169 if state[2] not in types:
168 raise util.Abort(_('unknown subrepo type %s') % state[2])
170 raise util.Abort(_('unknown subrepo type %s') % state[2])
169 return types[state[2]](ctx, path, state[:2])
171 return types[state[2]](ctx, path, state[:2])
170
172
171 # subrepo classes need to implement the following methods:
173 # subrepo classes need to implement the following methods:
172 # __init__(self, ctx, path, state)
174 # __init__(self, ctx, path, state)
173 # dirty(self): returns true if the dirstate of the subrepo
175 # dirty(self): returns true if the dirstate of the subrepo
174 # does not match current stored state
176 # does not match current stored state
175 # commit(self, text, user, date): commit the current changes
177 # commit(self, text, user, date): commit the current changes
176 # to the subrepo with the given log message. Use given
178 # to the subrepo with the given log message. Use given
177 # user and date if possible. Return the new state of the subrepo.
179 # user and date if possible. Return the new state of the subrepo.
178 # remove(self): remove the subrepo (should verify the dirstate
180 # remove(self): remove the subrepo (should verify the dirstate
179 # is not dirty first)
181 # is not dirty first)
180 # get(self, state): run whatever commands are needed to put the
182 # get(self, state): run whatever commands are needed to put the
181 # subrepo into this state
183 # subrepo into this state
182 # merge(self, state): merge currently-saved state with the new state.
184 # merge(self, state): merge currently-saved state with the new state.
183 # push(self, force): perform whatever action is analagous to 'hg push'
185 # push(self, force): perform whatever action is analagous to 'hg push'
184 # This may be a no-op on some systems.
186 # This may be a no-op on some systems.
185
187
186 class hgsubrepo(object):
188 class hgsubrepo(object):
187 def __init__(self, ctx, path, state):
189 def __init__(self, ctx, path, state):
188 self._path = path
190 self._path = path
189 self._state = state
191 self._state = state
190 r = ctx._repo
192 r = ctx._repo
191 root = r.wjoin(path)
193 root = r.wjoin(path)
192 create = False
194 create = False
193 if not os.path.exists(os.path.join(root, '.hg')):
195 if not os.path.exists(os.path.join(root, '.hg')):
194 create = True
196 create = True
195 util.makedirs(root)
197 util.makedirs(root)
196 self._repo = hg.repository(r.ui, root, create=create)
198 self._repo = hg.repository(r.ui, root, create=create)
197 self._repo._subparent = r
199 self._repo._subparent = r
198 self._repo._subsource = state[0]
200 self._repo._subsource = state[0]
199
201
200 if create:
202 if create:
201 fp = self._repo.opener("hgrc", "w", text=True)
203 fp = self._repo.opener("hgrc", "w", text=True)
202 fp.write('[paths]\n')
204 fp.write('[paths]\n')
203
205
204 def addpathconfig(key, value):
206 def addpathconfig(key, value):
205 fp.write('%s = %s\n' % (key, value))
207 fp.write('%s = %s\n' % (key, value))
206 self._repo.ui.setconfig('paths', key, value)
208 self._repo.ui.setconfig('paths', key, value)
207
209
208 defpath = _abssource(self._repo)
210 defpath = _abssource(self._repo)
209 defpushpath = _abssource(self._repo, True)
211 defpushpath = _abssource(self._repo, True)
210 addpathconfig('default', defpath)
212 addpathconfig('default', defpath)
211 if defpath != defpushpath:
213 if defpath != defpushpath:
212 addpathconfig('default-push', defpushpath)
214 addpathconfig('default-push', defpushpath)
213 fp.close()
215 fp.close()
214
216
215 def dirty(self):
217 def dirty(self):
216 r = self._state[1]
218 r = self._state[1]
217 if r == '':
219 if r == '':
218 return True
220 return True
219 w = self._repo[None]
221 w = self._repo[None]
220 if w.p1() != self._repo[r]: # version checked out change
222 if w.p1() != self._repo[r]: # version checked out change
221 return True
223 return True
222 return w.dirty() # working directory changed
224 return w.dirty() # working directory changed
223
225
224 def commit(self, text, user, date):
226 def commit(self, text, user, date):
225 self._repo.ui.debug("committing subrepo %s\n" % relpath(self))
227 self._repo.ui.debug("committing subrepo %s\n" % relpath(self))
226 n = self._repo.commit(text, user, date)
228 n = self._repo.commit(text, user, date)
227 if not n:
229 if not n:
228 return self._repo['.'].hex() # different version checked out
230 return self._repo['.'].hex() # different version checked out
229 return node.hex(n)
231 return node.hex(n)
230
232
231 def remove(self):
233 def remove(self):
232 # we can't fully delete the repository as it may contain
234 # we can't fully delete the repository as it may contain
233 # local-only history
235 # local-only history
234 self._repo.ui.note(_('removing subrepo %s\n') % relpath(self))
236 self._repo.ui.note(_('removing subrepo %s\n') % relpath(self))
235 hg.clean(self._repo, node.nullid, False)
237 hg.clean(self._repo, node.nullid, False)
236
238
237 def _get(self, state):
239 def _get(self, state):
238 source, revision, kind = state
240 source, revision, kind = state
239 try:
241 try:
240 self._repo.lookup(revision)
242 self._repo.lookup(revision)
241 except error.RepoError:
243 except error.RepoError:
242 self._repo._subsource = source
244 self._repo._subsource = source
243 srcurl = _abssource(self._repo)
245 srcurl = _abssource(self._repo)
244 self._repo.ui.status(_('pulling subrepo %s from %s\n')
246 self._repo.ui.status(_('pulling subrepo %s from %s\n')
245 % (relpath(self), srcurl))
247 % (relpath(self), srcurl))
246 other = hg.repository(self._repo.ui, srcurl)
248 other = hg.repository(self._repo.ui, srcurl)
247 self._repo.pull(other)
249 self._repo.pull(other)
248
250
249 def get(self, state):
251 def get(self, state):
250 self._get(state)
252 self._get(state)
251 source, revision, kind = state
253 source, revision, kind = state
252 self._repo.ui.debug("getting subrepo %s\n" % self._path)
254 self._repo.ui.debug("getting subrepo %s\n" % self._path)
253 hg.clean(self._repo, revision, False)
255 hg.clean(self._repo, revision, False)
254
256
255 def merge(self, state):
257 def merge(self, state):
256 self._get(state)
258 self._get(state)
257 cur = self._repo['.']
259 cur = self._repo['.']
258 dst = self._repo[state[1]]
260 dst = self._repo[state[1]]
259 anc = dst.ancestor(cur)
261 anc = dst.ancestor(cur)
260 if anc == cur:
262 if anc == cur:
261 self._repo.ui.debug("updating subrepo %s\n" % relpath(self))
263 self._repo.ui.debug("updating subrepo %s\n" % relpath(self))
262 hg.update(self._repo, state[1])
264 hg.update(self._repo, state[1])
263 elif anc == dst:
265 elif anc == dst:
264 self._repo.ui.debug("skipping subrepo %s\n" % relpath(self))
266 self._repo.ui.debug("skipping subrepo %s\n" % relpath(self))
265 else:
267 else:
266 self._repo.ui.debug("merging subrepo %s\n" % relpath(self))
268 self._repo.ui.debug("merging subrepo %s\n" % relpath(self))
267 hg.merge(self._repo, state[1], remind=False)
269 hg.merge(self._repo, state[1], remind=False)
268
270
269 def push(self, force):
271 def push(self, force):
270 # push subrepos depth-first for coherent ordering
272 # push subrepos depth-first for coherent ordering
271 c = self._repo['']
273 c = self._repo['']
272 subs = c.substate # only repos that are committed
274 subs = c.substate # only repos that are committed
273 for s in sorted(subs):
275 for s in sorted(subs):
274 if not c.sub(s).push(force):
276 if not c.sub(s).push(force):
275 return False
277 return False
276
278
277 dsturl = _abssource(self._repo, True)
279 dsturl = _abssource(self._repo, True)
278 self._repo.ui.status(_('pushing subrepo %s to %s\n') %
280 self._repo.ui.status(_('pushing subrepo %s to %s\n') %
279 (relpath(self), dsturl))
281 (relpath(self), dsturl))
280 other = hg.repository(self._repo.ui, dsturl)
282 other = hg.repository(self._repo.ui, dsturl)
281 return self._repo.push(other, force)
283 return self._repo.push(other, force)
282
284
283 class svnsubrepo(object):
285 class svnsubrepo(object):
284 def __init__(self, ctx, path, state):
286 def __init__(self, ctx, path, state):
285 self._path = path
287 self._path = path
286 self._state = state
288 self._state = state
287 self._ctx = ctx
289 self._ctx = ctx
288 self._ui = ctx._repo.ui
290 self._ui = ctx._repo.ui
289
291
290 def _svncommand(self, commands):
292 def _svncommand(self, commands):
291 path = os.path.join(self._ctx._repo.origroot, self._path)
293 path = os.path.join(self._ctx._repo.origroot, self._path)
292 cmd = ['svn'] + commands + [path]
294 cmd = ['svn'] + commands + [path]
293 cmd = [util.shellquote(arg) for arg in cmd]
295 cmd = [util.shellquote(arg) for arg in cmd]
294 cmd = util.quotecommand(' '.join(cmd))
296 cmd = util.quotecommand(' '.join(cmd))
295 env = dict(os.environ)
297 env = dict(os.environ)
296 # Avoid localized output, preserve current locale for everything else.
298 # Avoid localized output, preserve current locale for everything else.
297 env['LC_MESSAGES'] = 'C'
299 env['LC_MESSAGES'] = 'C'
298 write, read, err = util.popen3(cmd, env=env, newlines=True)
300 write, read, err = util.popen3(cmd, env=env, newlines=True)
299 retdata = read.read()
301 retdata = read.read()
300 err = err.read().strip()
302 err = err.read().strip()
301 if err:
303 if err:
302 raise util.Abort(err)
304 raise util.Abort(err)
303 return retdata
305 return retdata
304
306
305 def _wcrev(self):
307 def _wcrev(self):
306 output = self._svncommand(['info', '--xml'])
308 output = self._svncommand(['info', '--xml'])
307 doc = xml.dom.minidom.parseString(output)
309 doc = xml.dom.minidom.parseString(output)
308 entries = doc.getElementsByTagName('entry')
310 entries = doc.getElementsByTagName('entry')
309 if not entries:
311 if not entries:
310 return 0
312 return 0
311 return int(entries[0].getAttribute('revision') or 0)
313 return int(entries[0].getAttribute('revision') or 0)
312
314
313 def _wcchanged(self):
315 def _wcchanged(self):
314 """Return (changes, extchanges) where changes is True
316 """Return (changes, extchanges) where changes is True
315 if the working directory was changed, and extchanges is
317 if the working directory was changed, and extchanges is
316 True if any of these changes concern an external entry.
318 True if any of these changes concern an external entry.
317 """
319 """
318 output = self._svncommand(['status', '--xml'])
320 output = self._svncommand(['status', '--xml'])
319 externals, changes = [], []
321 externals, changes = [], []
320 doc = xml.dom.minidom.parseString(output)
322 doc = xml.dom.minidom.parseString(output)
321 for e in doc.getElementsByTagName('entry'):
323 for e in doc.getElementsByTagName('entry'):
322 s = e.getElementsByTagName('wc-status')
324 s = e.getElementsByTagName('wc-status')
323 if not s:
325 if not s:
324 continue
326 continue
325 item = s[0].getAttribute('item')
327 item = s[0].getAttribute('item')
326 props = s[0].getAttribute('props')
328 props = s[0].getAttribute('props')
327 path = e.getAttribute('path')
329 path = e.getAttribute('path')
328 if item == 'external':
330 if item == 'external':
329 externals.append(path)
331 externals.append(path)
330 if (item not in ('', 'normal', 'unversioned', 'external')
332 if (item not in ('', 'normal', 'unversioned', 'external')
331 or props not in ('', 'none')):
333 or props not in ('', 'none')):
332 changes.append(path)
334 changes.append(path)
333 for path in changes:
335 for path in changes:
334 for ext in externals:
336 for ext in externals:
335 if path == ext or path.startswith(ext + os.sep):
337 if path == ext or path.startswith(ext + os.sep):
336 return True, True
338 return True, True
337 return bool(changes), False
339 return bool(changes), False
338
340
339 def dirty(self):
341 def dirty(self):
340 if self._wcrev() == self._state[1] and not self._wcchanged()[0]:
342 if self._wcrev() == self._state[1] and not self._wcchanged()[0]:
341 return False
343 return False
342 return True
344 return True
343
345
344 def commit(self, text, user, date):
346 def commit(self, text, user, date):
345 # user and date are out of our hands since svn is centralized
347 # user and date are out of our hands since svn is centralized
346 changed, extchanged = self._wcchanged()
348 changed, extchanged = self._wcchanged()
347 if not changed:
349 if not changed:
348 return self._wcrev()
350 return self._wcrev()
349 if extchanged:
351 if extchanged:
350 # Do not try to commit externals
352 # Do not try to commit externals
351 raise util.Abort(_('cannot commit svn externals'))
353 raise util.Abort(_('cannot commit svn externals'))
352 commitinfo = self._svncommand(['commit', '-m', text])
354 commitinfo = self._svncommand(['commit', '-m', text])
353 self._ui.status(commitinfo)
355 self._ui.status(commitinfo)
354 newrev = re.search('Committed revision ([\d]+).', commitinfo)
356 newrev = re.search('Committed revision ([\d]+).', commitinfo)
355 if not newrev:
357 if not newrev:
356 raise util.Abort(commitinfo.splitlines()[-1])
358 raise util.Abort(commitinfo.splitlines()[-1])
357 newrev = newrev.groups()[0]
359 newrev = newrev.groups()[0]
358 self._ui.status(self._svncommand(['update', '-r', newrev]))
360 self._ui.status(self._svncommand(['update', '-r', newrev]))
359 return newrev
361 return newrev
360
362
361 def remove(self):
363 def remove(self):
362 if self.dirty():
364 if self.dirty():
363 self._ui.warn(_('not removing repo %s because '
365 self._ui.warn(_('not removing repo %s because '
364 'it has changes.\n' % self._path))
366 'it has changes.\n' % self._path))
365 return
367 return
366 self._ui.note(_('removing subrepo %s\n') % self._path)
368 self._ui.note(_('removing subrepo %s\n') % self._path)
367 shutil.rmtree(self._ctx.repo.join(self._path))
369 shutil.rmtree(self._ctx.repo.join(self._path))
368
370
369 def get(self, state):
371 def get(self, state):
370 status = self._svncommand(['checkout', state[0], '--revision', state[1]])
372 status = self._svncommand(['checkout', state[0], '--revision', state[1]])
371 if not re.search('Checked out revision [\d]+.', status):
373 if not re.search('Checked out revision [\d]+.', status):
372 raise util.Abort(status.splitlines()[-1])
374 raise util.Abort(status.splitlines()[-1])
373 self._ui.status(status)
375 self._ui.status(status)
374
376
375 def merge(self, state):
377 def merge(self, state):
376 old = int(self._state[1])
378 old = int(self._state[1])
377 new = int(state[1])
379 new = int(state[1])
378 if new > old:
380 if new > old:
379 self.get(state)
381 self.get(state)
380
382
381 def push(self, force):
383 def push(self, force):
382 # push is a no-op for SVN
384 # push is a no-op for SVN
383 return True
385 return True
384
386
385 types = {
387 types = {
386 'hg': hgsubrepo,
388 'hg': hgsubrepo,
387 'svn': svnsubrepo,
389 'svn': svnsubrepo,
388 }
390 }
General Comments 0
You need to be logged in to leave comments. Login now