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