##// END OF EJS Templates
subrepos: prompt on conflicts on update with dirty subrepos...
Erik Zielke -
r13417:0748e18b default
parent child Browse files
Show More
@@ -1,961 +1,997 b''
1 1 # subrepo.py - sub-repository handling for Mercurial
2 2 #
3 3 # Copyright 2009-2010 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 import errno, os, re, xml.dom.minidom, shutil, urlparse, posixpath
9 9 import stat, subprocess, tarfile
10 10 from i18n import _
11 11 import config, util, node, error, cmdutil
12 12 hg = None
13 13
14 14 nullstate = ('', '', 'empty')
15 15
16 16 def state(ctx, ui):
17 17 """return a state dict, mapping subrepo paths configured in .hgsub
18 18 to tuple: (source from .hgsub, revision from .hgsubstate, kind
19 19 (key in types dict))
20 20 """
21 21 p = config.config()
22 22 def read(f, sections=None, remap=None):
23 23 if f in ctx:
24 24 try:
25 25 data = ctx[f].data()
26 26 except IOError, err:
27 27 if err.errno != errno.ENOENT:
28 28 raise
29 29 # handle missing subrepo spec files as removed
30 30 ui.warn(_("warning: subrepo spec file %s not found\n") % f)
31 31 return
32 32 p.parse(f, data, sections, remap, read)
33 33 else:
34 34 raise util.Abort(_("subrepo spec file %s not found") % f)
35 35
36 36 if '.hgsub' in ctx:
37 37 read('.hgsub')
38 38
39 39 for path, src in ui.configitems('subpaths'):
40 40 p.set('subpaths', path, src, ui.configsource('subpaths', path))
41 41
42 42 rev = {}
43 43 if '.hgsubstate' in ctx:
44 44 try:
45 45 for l in ctx['.hgsubstate'].data().splitlines():
46 46 revision, path = l.split(" ", 1)
47 47 rev[path] = revision
48 48 except IOError, err:
49 49 if err.errno != errno.ENOENT:
50 50 raise
51 51
52 52 state = {}
53 53 for path, src in p[''].items():
54 54 kind = 'hg'
55 55 if src.startswith('['):
56 56 if ']' not in src:
57 57 raise util.Abort(_('missing ] in subrepo source'))
58 58 kind, src = src.split(']', 1)
59 59 kind = kind[1:]
60 60
61 61 for pattern, repl in p.items('subpaths'):
62 62 # Turn r'C:\foo\bar' into r'C:\\foo\\bar' since re.sub
63 63 # does a string decode.
64 64 repl = repl.encode('string-escape')
65 65 # However, we still want to allow back references to go
66 66 # through unharmed, so we turn r'\\1' into r'\1'. Again,
67 67 # extra escapes are needed because re.sub string decodes.
68 68 repl = re.sub(r'\\\\([0-9]+)', r'\\\1', repl)
69 69 try:
70 70 src = re.sub(pattern, repl, src, 1)
71 71 except re.error, e:
72 72 raise util.Abort(_("bad subrepository pattern in %s: %s")
73 73 % (p.source('subpaths', pattern), e))
74 74
75 75 state[path] = (src.strip(), rev.get(path, ''), kind)
76 76
77 77 return state
78 78
79 79 def writestate(repo, state):
80 80 """rewrite .hgsubstate in (outer) repo with these subrepo states"""
81 81 repo.wwrite('.hgsubstate',
82 82 ''.join(['%s %s\n' % (state[s][1], s)
83 83 for s in sorted(state)]), '')
84 84
85 85 def submerge(repo, wctx, mctx, actx, overwrite):
86 86 """delegated from merge.applyupdates: merging of .hgsubstate file
87 87 in working context, merging context and ancestor context"""
88 88 if mctx == actx: # backwards?
89 89 actx = wctx.p1()
90 90 s1 = wctx.substate
91 91 s2 = mctx.substate
92 92 sa = actx.substate
93 93 sm = {}
94 94
95 95 repo.ui.debug("subrepo merge %s %s %s\n" % (wctx, mctx, actx))
96 96
97 97 def debug(s, msg, r=""):
98 98 if r:
99 99 r = "%s:%s:%s" % r
100 100 repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r))
101 101
102 102 for s, l in s1.items():
103 103 a = sa.get(s, nullstate)
104 104 ld = l # local state with possible dirty flag for compares
105 105 if wctx.sub(s).dirty():
106 106 ld = (l[0], l[1] + "+")
107 107 if wctx == actx: # overwrite
108 108 a = ld
109 109
110 110 if s in s2:
111 111 r = s2[s]
112 112 if ld == r or r == a: # no change or local is newer
113 113 sm[s] = l
114 114 continue
115 115 elif ld == a: # other side changed
116 116 debug(s, "other changed, get", r)
117 117 wctx.sub(s).get(r, overwrite)
118 118 sm[s] = r
119 119 elif ld[0] != r[0]: # sources differ
120 120 if repo.ui.promptchoice(
121 121 _(' subrepository sources for %s differ\n'
122 122 'use (l)ocal source (%s) or (r)emote source (%s)?')
123 123 % (s, l[0], r[0]),
124 124 (_('&Local'), _('&Remote')), 0):
125 125 debug(s, "prompt changed, get", r)
126 126 wctx.sub(s).get(r, overwrite)
127 127 sm[s] = r
128 128 elif ld[1] == a[1]: # local side is unchanged
129 129 debug(s, "other side changed, get", r)
130 130 wctx.sub(s).get(r, overwrite)
131 131 sm[s] = r
132 132 else:
133 133 debug(s, "both sides changed, merge with", r)
134 134 wctx.sub(s).merge(r)
135 135 sm[s] = l
136 136 elif ld == a: # remote removed, local unchanged
137 137 debug(s, "remote removed, remove")
138 138 wctx.sub(s).remove()
139 139 else:
140 140 if repo.ui.promptchoice(
141 141 _(' local changed subrepository %s which remote removed\n'
142 142 'use (c)hanged version or (d)elete?') % s,
143 143 (_('&Changed'), _('&Delete')), 0):
144 144 debug(s, "prompt remove")
145 145 wctx.sub(s).remove()
146 146
147 147 for s, r in s2.items():
148 148 if s in s1:
149 149 continue
150 150 elif s not in sa:
151 151 debug(s, "remote added, get", r)
152 152 mctx.sub(s).get(r)
153 153 sm[s] = r
154 154 elif r != sa[s]:
155 155 if repo.ui.promptchoice(
156 156 _(' remote changed subrepository %s which local removed\n'
157 157 'use (c)hanged version or (d)elete?') % s,
158 158 (_('&Changed'), _('&Delete')), 0) == 0:
159 159 debug(s, "prompt recreate", r)
160 160 wctx.sub(s).get(r)
161 161 sm[s] = r
162 162
163 163 # record merged .hgsubstate
164 164 writestate(repo, sm)
165 165
166 def _updateprompt(ui, sub, dirty, local, remote):
167 if dirty:
168 msg = (_(' subrepository sources for %s differ\n'
169 'use (l)ocal source (%s) or (r)emote source (%s)?\n')
170 % (subrelpath(sub), local, remote))
171 else:
172 msg = (_(' subrepository sources for %s differ (in checked out version)\n'
173 'use (l)ocal source (%s) or (r)emote source (%s)?\n')
174 % (subrelpath(sub), local, remote))
175 return ui.promptchoice(msg, (_('&Local'), _('&Remote')), 0)
176
166 177 def reporelpath(repo):
167 178 """return path to this (sub)repo as seen from outermost repo"""
168 179 parent = repo
169 180 while hasattr(parent, '_subparent'):
170 181 parent = parent._subparent
171 182 return repo.root[len(parent.root)+1:]
172 183
173 184 def subrelpath(sub):
174 185 """return path to this subrepo as seen from outermost repo"""
175 186 if hasattr(sub, '_relpath'):
176 187 return sub._relpath
177 188 if not hasattr(sub, '_repo'):
178 189 return sub._path
179 190 return reporelpath(sub._repo)
180 191
181 192 def _abssource(repo, push=False, abort=True):
182 193 """return pull/push path of repo - either based on parent repo .hgsub info
183 194 or on the top repo config. Abort or return None if no source found."""
184 195 if hasattr(repo, '_subparent'):
185 196 source = repo._subsource
186 197 if source.startswith('/') or '://' in source:
187 198 return source
188 199 parent = _abssource(repo._subparent, push, abort=False)
189 200 if parent:
190 201 if '://' in parent:
191 202 if parent[-1] == '/':
192 203 parent = parent[:-1]
193 204 r = urlparse.urlparse(parent + '/' + source)
194 205 r = urlparse.urlunparse((r[0], r[1],
195 206 posixpath.normpath(r[2]),
196 207 r[3], r[4], r[5]))
197 208 return r
198 209 else: # plain file system path
199 210 return posixpath.normpath(os.path.join(parent, repo._subsource))
200 211 else: # recursion reached top repo
201 212 if hasattr(repo, '_subtoppath'):
202 213 return repo._subtoppath
203 214 if push and repo.ui.config('paths', 'default-push'):
204 215 return repo.ui.config('paths', 'default-push')
205 216 if repo.ui.config('paths', 'default'):
206 217 return repo.ui.config('paths', 'default')
207 218 if abort:
208 219 raise util.Abort(_("default path for subrepository %s not found") %
209 220 reporelpath(repo))
210 221
211 222 def itersubrepos(ctx1, ctx2):
212 223 """find subrepos in ctx1 or ctx2"""
213 224 # Create a (subpath, ctx) mapping where we prefer subpaths from
214 225 # ctx1. The subpaths from ctx2 are important when the .hgsub file
215 226 # has been modified (in ctx2) but not yet committed (in ctx1).
216 227 subpaths = dict.fromkeys(ctx2.substate, ctx2)
217 228 subpaths.update(dict.fromkeys(ctx1.substate, ctx1))
218 229 for subpath, ctx in sorted(subpaths.iteritems()):
219 230 yield subpath, ctx.sub(subpath)
220 231
221 232 def subrepo(ctx, path):
222 233 """return instance of the right subrepo class for subrepo in path"""
223 234 # subrepo inherently violates our import layering rules
224 235 # because it wants to make repo objects from deep inside the stack
225 236 # so we manually delay the circular imports to not break
226 237 # scripts that don't use our demand-loading
227 238 global hg
228 239 import hg as h
229 240 hg = h
230 241
231 242 util.path_auditor(ctx._repo.root)(path)
232 243 state = ctx.substate.get(path, nullstate)
233 244 if state[2] not in types:
234 245 raise util.Abort(_('unknown subrepo type %s') % state[2])
235 246 return types[state[2]](ctx, path, state[:2])
236 247
237 248 # subrepo classes need to implement the following abstract class:
238 249
239 250 class abstractsubrepo(object):
240 251
241 252 def dirty(self, ignoreupdate=False):
242 253 """returns true if the dirstate of the subrepo is dirty or does not
243 254 match current stored state. If ignoreupdate is true, only check
244 255 whether the subrepo has uncommitted changes in its dirstate.
245 256 """
246 257 raise NotImplementedError
247 258
248 259 def checknested(self, path):
249 260 """check if path is a subrepository within this repository"""
250 261 return False
251 262
252 263 def commit(self, text, user, date):
253 264 """commit the current changes to the subrepo with the given
254 265 log message. Use given user and date if possible. Return the
255 266 new state of the subrepo.
256 267 """
257 268 raise NotImplementedError
258 269
259 270 def remove(self):
260 271 """remove the subrepo
261 272
262 273 (should verify the dirstate is not dirty first)
263 274 """
264 275 raise NotImplementedError
265 276
266 277 def get(self, state, overwrite=False):
267 278 """run whatever commands are needed to put the subrepo into
268 279 this state
269 280 """
270 281 raise NotImplementedError
271 282
272 283 def merge(self, state):
273 284 """merge currently-saved state with the new state."""
274 285 raise NotImplementedError
275 286
276 287 def push(self, force):
277 288 """perform whatever action is analogous to 'hg push'
278 289
279 290 This may be a no-op on some systems.
280 291 """
281 292 raise NotImplementedError
282 293
283 294 def add(self, ui, match, dryrun, prefix):
284 295 return []
285 296
286 297 def status(self, rev2, **opts):
287 298 return [], [], [], [], [], [], []
288 299
289 300 def diff(self, diffopts, node2, match, prefix, **opts):
290 301 pass
291 302
292 303 def outgoing(self, ui, dest, opts):
293 304 return 1
294 305
295 306 def incoming(self, ui, source, opts):
296 307 return 1
297 308
298 309 def files(self):
299 310 """return filename iterator"""
300 311 raise NotImplementedError
301 312
302 313 def filedata(self, name):
303 314 """return file data"""
304 315 raise NotImplementedError
305 316
306 317 def fileflags(self, name):
307 318 """return file flags"""
308 319 return ''
309 320
310 321 def archive(self, ui, archiver, prefix):
311 322 files = self.files()
312 323 total = len(files)
313 324 relpath = subrelpath(self)
314 325 ui.progress(_('archiving (%s)') % relpath, 0,
315 326 unit=_('files'), total=total)
316 327 for i, name in enumerate(files):
317 328 flags = self.fileflags(name)
318 329 mode = 'x' in flags and 0755 or 0644
319 330 symlink = 'l' in flags
320 331 archiver.addfile(os.path.join(prefix, self._path, name),
321 332 mode, symlink, self.filedata(name))
322 333 ui.progress(_('archiving (%s)') % relpath, i + 1,
323 334 unit=_('files'), total=total)
324 335 ui.progress(_('archiving (%s)') % relpath, None)
325 336
326 337
327 338 class hgsubrepo(abstractsubrepo):
328 339 def __init__(self, ctx, path, state):
329 340 self._path = path
330 341 self._state = state
331 342 r = ctx._repo
332 343 root = r.wjoin(path)
333 344 create = False
334 345 if not os.path.exists(os.path.join(root, '.hg')):
335 346 create = True
336 347 util.makedirs(root)
337 348 self._repo = hg.repository(r.ui, root, create=create)
338 349 self._repo._subparent = r
339 350 self._repo._subsource = state[0]
340 351
341 352 if create:
342 353 fp = self._repo.opener("hgrc", "w", text=True)
343 354 fp.write('[paths]\n')
344 355
345 356 def addpathconfig(key, value):
346 357 if value:
347 358 fp.write('%s = %s\n' % (key, value))
348 359 self._repo.ui.setconfig('paths', key, value)
349 360
350 361 defpath = _abssource(self._repo, abort=False)
351 362 defpushpath = _abssource(self._repo, True, abort=False)
352 363 addpathconfig('default', defpath)
353 364 if defpath != defpushpath:
354 365 addpathconfig('default-push', defpushpath)
355 366 fp.close()
356 367
357 368 def add(self, ui, match, dryrun, prefix):
358 369 return cmdutil.add(ui, self._repo, match, dryrun, True,
359 370 os.path.join(prefix, self._path))
360 371
361 372 def status(self, rev2, **opts):
362 373 try:
363 374 rev1 = self._state[1]
364 375 ctx1 = self._repo[rev1]
365 376 ctx2 = self._repo[rev2]
366 377 return self._repo.status(ctx1, ctx2, **opts)
367 378 except error.RepoLookupError, inst:
368 379 self._repo.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
369 380 % (inst, subrelpath(self)))
370 381 return [], [], [], [], [], [], []
371 382
372 383 def diff(self, diffopts, node2, match, prefix, **opts):
373 384 try:
374 385 node1 = node.bin(self._state[1])
375 386 # We currently expect node2 to come from substate and be
376 387 # in hex format
377 388 if node2 is not None:
378 389 node2 = node.bin(node2)
379 390 cmdutil.diffordiffstat(self._repo.ui, self._repo, diffopts,
380 391 node1, node2, match,
381 392 prefix=os.path.join(prefix, self._path),
382 393 listsubrepos=True, **opts)
383 394 except error.RepoLookupError, inst:
384 395 self._repo.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
385 396 % (inst, subrelpath(self)))
386 397
387 398 def archive(self, ui, archiver, prefix):
388 399 abstractsubrepo.archive(self, ui, archiver, prefix)
389 400
390 401 rev = self._state[1]
391 402 ctx = self._repo[rev]
392 403 for subpath in ctx.substate:
393 404 s = subrepo(ctx, subpath)
394 405 s.archive(ui, archiver, os.path.join(prefix, self._path))
395 406
396 407 def dirty(self, ignoreupdate=False):
397 408 r = self._state[1]
398 409 if r == '' and not ignoreupdate: # no state recorded
399 410 return True
400 411 w = self._repo[None]
401 412 if w.p1() != self._repo[r] and not ignoreupdate:
402 413 # different version checked out
403 414 return True
404 415 return w.dirty() # working directory changed
405 416
406 417 def checknested(self, path):
407 418 return self._repo._checknested(self._repo.wjoin(path))
408 419
409 420 def commit(self, text, user, date):
410 421 self._repo.ui.debug("committing subrepo %s\n" % subrelpath(self))
411 422 n = self._repo.commit(text, user, date)
412 423 if not n:
413 424 return self._repo['.'].hex() # different version checked out
414 425 return node.hex(n)
415 426
416 427 def remove(self):
417 428 # we can't fully delete the repository as it may contain
418 429 # local-only history
419 430 self._repo.ui.note(_('removing subrepo %s\n') % subrelpath(self))
420 431 hg.clean(self._repo, node.nullid, False)
421 432
422 433 def _get(self, state):
423 434 source, revision, kind = state
424 435 try:
425 436 self._repo.lookup(revision)
426 437 except error.RepoError:
427 438 self._repo._subsource = source
428 439 srcurl = _abssource(self._repo)
429 440 self._repo.ui.status(_('pulling subrepo %s from %s\n')
430 441 % (subrelpath(self), srcurl))
431 442 other = hg.repository(self._repo.ui, srcurl)
432 443 self._repo.pull(other)
433 444
434 445 def get(self, state, overwrite=False):
435 446 self._get(state)
436 447 source, revision, kind = state
437 448 self._repo.ui.debug("getting subrepo %s\n" % self._path)
438 449 hg.clean(self._repo, revision, False)
439 450
440 451 def merge(self, state):
441 452 self._get(state)
442 453 cur = self._repo['.']
443 454 dst = self._repo[state[1]]
444 455 anc = dst.ancestor(cur)
445 if anc == cur:
446 self._repo.ui.debug("updating subrepo %s\n" % subrelpath(self))
447 hg.update(self._repo, state[1])
448 elif anc == dst:
449 self._repo.ui.debug("skipping subrepo %s\n" % subrelpath(self))
456
457 def mergefunc():
458 if anc == cur:
459 self._repo.ui.debug("updating subrepo %s\n" % subrelpath(self))
460 hg.update(self._repo, state[1])
461 elif anc == dst:
462 self._repo.ui.debug("skipping subrepo %s\n" % subrelpath(self))
463 else:
464 self._repo.ui.debug("merging subrepo %s\n" % subrelpath(self))
465 hg.merge(self._repo, state[1], remind=False)
466
467 wctx = self._repo[None]
468 if self.dirty():
469 if anc != dst:
470 if _updateprompt(self._repo.ui, self, wctx.dirty(), cur, dst):
471 mergefunc()
472 else:
473 mergefunc()
450 474 else:
451 self._repo.ui.debug("merging subrepo %s\n" % subrelpath(self))
452 hg.merge(self._repo, state[1], remind=False)
475 mergefunc()
453 476
454 477 def push(self, force):
455 478 # push subrepos depth-first for coherent ordering
456 479 c = self._repo['']
457 480 subs = c.substate # only repos that are committed
458 481 for s in sorted(subs):
459 482 if not c.sub(s).push(force):
460 483 return False
461 484
462 485 dsturl = _abssource(self._repo, True)
463 486 self._repo.ui.status(_('pushing subrepo %s to %s\n') %
464 487 (subrelpath(self), dsturl))
465 488 other = hg.repository(self._repo.ui, dsturl)
466 489 return self._repo.push(other, force)
467 490
468 491 def outgoing(self, ui, dest, opts):
469 492 return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts)
470 493
471 494 def incoming(self, ui, source, opts):
472 495 return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts)
473 496
474 497 def files(self):
475 498 rev = self._state[1]
476 499 ctx = self._repo[rev]
477 500 return ctx.manifest()
478 501
479 502 def filedata(self, name):
480 503 rev = self._state[1]
481 504 return self._repo[rev][name].data()
482 505
483 506 def fileflags(self, name):
484 507 rev = self._state[1]
485 508 ctx = self._repo[rev]
486 509 return ctx.flags(name)
487 510
488 511
489 512 class svnsubrepo(abstractsubrepo):
490 513 def __init__(self, ctx, path, state):
491 514 self._path = path
492 515 self._state = state
493 516 self._ctx = ctx
494 517 self._ui = ctx._repo.ui
495 518
496 519 def _svncommand(self, commands, filename=''):
497 520 path = os.path.join(self._ctx._repo.origroot, self._path, filename)
498 521 cmd = ['svn'] + commands + [path]
499 522 env = dict(os.environ)
500 523 # Avoid localized output, preserve current locale for everything else.
501 524 env['LC_MESSAGES'] = 'C'
502 525 p = subprocess.Popen(cmd, bufsize=-1, close_fds=util.closefds,
503 526 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
504 527 universal_newlines=True, env=env)
505 528 stdout, stderr = p.communicate()
506 529 stderr = stderr.strip()
507 530 if stderr:
508 531 raise util.Abort(stderr)
509 532 return stdout
510 533
511 534 def _wcrevs(self):
512 535 # Get the working directory revision as well as the last
513 536 # commit revision so we can compare the subrepo state with
514 537 # both. We used to store the working directory one.
515 538 output = self._svncommand(['info', '--xml'])
516 539 doc = xml.dom.minidom.parseString(output)
517 540 entries = doc.getElementsByTagName('entry')
518 541 lastrev, rev = '0', '0'
519 542 if entries:
520 543 rev = str(entries[0].getAttribute('revision')) or '0'
521 544 commits = entries[0].getElementsByTagName('commit')
522 545 if commits:
523 546 lastrev = str(commits[0].getAttribute('revision')) or '0'
524 547 return (lastrev, rev)
525 548
526 549 def _wcrev(self):
527 550 return self._wcrevs()[0]
528 551
529 552 def _wcchanged(self):
530 553 """Return (changes, extchanges) where changes is True
531 554 if the working directory was changed, and extchanges is
532 555 True if any of these changes concern an external entry.
533 556 """
534 557 output = self._svncommand(['status', '--xml'])
535 558 externals, changes = [], []
536 559 doc = xml.dom.minidom.parseString(output)
537 560 for e in doc.getElementsByTagName('entry'):
538 561 s = e.getElementsByTagName('wc-status')
539 562 if not s:
540 563 continue
541 564 item = s[0].getAttribute('item')
542 565 props = s[0].getAttribute('props')
543 566 path = e.getAttribute('path')
544 567 if item == 'external':
545 568 externals.append(path)
546 569 if (item not in ('', 'normal', 'unversioned', 'external')
547 570 or props not in ('', 'none')):
548 571 changes.append(path)
549 572 for path in changes:
550 573 for ext in externals:
551 574 if path == ext or path.startswith(ext + os.sep):
552 575 return True, True
553 576 return bool(changes), False
554 577
555 578 def dirty(self, ignoreupdate=False):
556 579 if not self._wcchanged()[0]:
557 580 if self._state[1] in self._wcrevs() or ignoreupdate:
558 581 return False
559 582 return True
560 583
561 584 def commit(self, text, user, date):
562 585 # user and date are out of our hands since svn is centralized
563 586 changed, extchanged = self._wcchanged()
564 587 if not changed:
565 588 return self._wcrev()
566 589 if extchanged:
567 590 # Do not try to commit externals
568 591 raise util.Abort(_('cannot commit svn externals'))
569 592 commitinfo = self._svncommand(['commit', '-m', text])
570 593 self._ui.status(commitinfo)
571 594 newrev = re.search('Committed revision ([0-9]+).', commitinfo)
572 595 if not newrev:
573 596 raise util.Abort(commitinfo.splitlines()[-1])
574 597 newrev = newrev.groups()[0]
575 598 self._ui.status(self._svncommand(['update', '-r', newrev]))
576 599 return newrev
577 600
578 601 def remove(self):
579 602 if self.dirty():
580 603 self._ui.warn(_('not removing repo %s because '
581 604 'it has changes.\n' % self._path))
582 605 return
583 606 self._ui.note(_('removing subrepo %s\n') % self._path)
584 607
585 608 def onerror(function, path, excinfo):
586 609 if function is not os.remove:
587 610 raise
588 611 # read-only files cannot be unlinked under Windows
589 612 s = os.stat(path)
590 613 if (s.st_mode & stat.S_IWRITE) != 0:
591 614 raise
592 615 os.chmod(path, stat.S_IMODE(s.st_mode) | stat.S_IWRITE)
593 616 os.remove(path)
594 617
595 618 path = self._ctx._repo.wjoin(self._path)
596 619 shutil.rmtree(path, onerror=onerror)
597 620 try:
598 621 os.removedirs(os.path.dirname(path))
599 622 except OSError:
600 623 pass
601 624
602 625 def get(self, state, overwrite=False):
603 626 if overwrite:
604 627 self._svncommand(['revert', '--recursive'])
605 628 status = self._svncommand(['checkout', state[0], '--revision', state[1]])
606 629 if not re.search('Checked out revision [0-9]+.', status):
607 630 raise util.Abort(status.splitlines()[-1])
608 631 self._ui.status(status)
609 632
610 633 def merge(self, state):
611 old = int(self._state[1])
612 new = int(state[1])
613 if new > old:
614 self.get(state)
634 old = self._state[1]
635 new = state[1]
636 if new != self._wcrev():
637 dirty = old == self._wcrev() or self._wcchanged()[0]
638 if _updateprompt(self._ui, self, dirty, self._wcrev(), new):
639 self.get(state, False)
615 640
616 641 def push(self, force):
617 642 # push is a no-op for SVN
618 643 return True
619 644
620 645 def files(self):
621 646 output = self._svncommand(['list'])
622 647 # This works because svn forbids \n in filenames.
623 648 return output.splitlines()
624 649
625 650 def filedata(self, name):
626 651 return self._svncommand(['cat'], name)
627 652
628 653
629 654 class gitsubrepo(abstractsubrepo):
630 655 def __init__(self, ctx, path, state):
631 656 # TODO add git version check.
632 657 self._state = state
633 658 self._ctx = ctx
634 659 self._path = path
635 660 self._relpath = os.path.join(reporelpath(ctx._repo), path)
636 661 self._abspath = ctx._repo.wjoin(path)
637 662 self._ui = ctx._repo.ui
638 663
639 664 def _gitcommand(self, commands, env=None, stream=False):
640 665 return self._gitdir(commands, env=env, stream=stream)[0]
641 666
642 667 def _gitdir(self, commands, env=None, stream=False):
643 668 return self._gitnodir(commands, env=env, stream=stream,
644 669 cwd=self._abspath)
645 670
646 671 def _gitnodir(self, commands, env=None, stream=False, cwd=None):
647 672 """Calls the git command
648 673
649 674 The methods tries to call the git command. versions previor to 1.6.0
650 675 are not supported and very probably fail.
651 676 """
652 677 self._ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands)))
653 678 # unless ui.quiet is set, print git's stderr,
654 679 # which is mostly progress and useful info
655 680 errpipe = None
656 681 if self._ui.quiet:
657 682 errpipe = open(os.devnull, 'w')
658 683 p = subprocess.Popen(['git'] + commands, bufsize=-1, cwd=cwd, env=env,
659 684 close_fds=util.closefds,
660 685 stdout=subprocess.PIPE, stderr=errpipe)
661 686 if stream:
662 687 return p.stdout, None
663 688
664 689 retdata = p.stdout.read().strip()
665 690 # wait for the child to exit to avoid race condition.
666 691 p.wait()
667 692
668 693 if p.returncode != 0 and p.returncode != 1:
669 694 # there are certain error codes that are ok
670 695 command = commands[0]
671 696 if command in ('cat-file', 'symbolic-ref'):
672 697 return retdata, p.returncode
673 698 # for all others, abort
674 699 raise util.Abort('git %s error %d in %s' %
675 700 (command, p.returncode, self._relpath))
676 701
677 702 return retdata, p.returncode
678 703
679 704 def _gitstate(self):
680 705 return self._gitcommand(['rev-parse', 'HEAD'])
681 706
682 707 def _gitcurrentbranch(self):
683 708 current, err = self._gitdir(['symbolic-ref', 'HEAD', '--quiet'])
684 709 if err:
685 710 current = None
686 711 return current
687 712
688 713 def _githavelocally(self, revision):
689 714 out, code = self._gitdir(['cat-file', '-e', revision])
690 715 return code == 0
691 716
692 717 def _gitisancestor(self, r1, r2):
693 718 base = self._gitcommand(['merge-base', r1, r2])
694 719 return base == r1
695 720
696 721 def _gitbranchmap(self):
697 722 '''returns 2 things:
698 723 a map from git branch to revision
699 724 a map from revision to branches'''
700 725 branch2rev = {}
701 726 rev2branch = {}
702 727
703 728 out = self._gitcommand(['for-each-ref', '--format',
704 729 '%(objectname) %(refname)'])
705 730 for line in out.split('\n'):
706 731 revision, ref = line.split(' ')
707 732 if ref.startswith('refs/tags/'):
708 733 continue
709 734 if ref.startswith('refs/remotes/') and ref.endswith('/HEAD'):
710 735 continue # ignore remote/HEAD redirects
711 736 branch2rev[ref] = revision
712 737 rev2branch.setdefault(revision, []).append(ref)
713 738 return branch2rev, rev2branch
714 739
715 740 def _gittracking(self, branches):
716 741 'return map of remote branch to local tracking branch'
717 742 # assumes no more than one local tracking branch for each remote
718 743 tracking = {}
719 744 for b in branches:
720 745 if b.startswith('refs/remotes/'):
721 746 continue
722 747 remote = self._gitcommand(['config', 'branch.%s.remote' % b])
723 748 if remote:
724 749 ref = self._gitcommand(['config', 'branch.%s.merge' % b])
725 750 tracking['refs/remotes/%s/%s' %
726 751 (remote, ref.split('/', 2)[2])] = b
727 752 return tracking
728 753
729 754 def _fetch(self, source, revision):
730 755 if not os.path.exists(os.path.join(self._abspath, '.git')):
731 756 self._ui.status(_('cloning subrepo %s\n') % self._relpath)
732 757 self._gitnodir(['clone', source, self._abspath])
733 758 if self._githavelocally(revision):
734 759 return
735 760 self._ui.status(_('pulling subrepo %s\n') % self._relpath)
736 761 # first try from origin
737 762 self._gitcommand(['fetch'])
738 763 if self._githavelocally(revision):
739 764 return
740 765 # then try from known subrepo source
741 766 self._gitcommand(['fetch', source])
742 767 if not self._githavelocally(revision):
743 768 raise util.Abort(_("revision %s does not exist in subrepo %s\n") %
744 769 (revision, self._relpath))
745 770
746 771 def dirty(self, ignoreupdate=False):
747 772 if not ignoreupdate and self._state[1] != self._gitstate():
748 773 # different version checked out
749 774 return True
750 775 # check for staged changes or modified files; ignore untracked files
751 776 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
752 777 return code == 1
753 778
754 779 def get(self, state, overwrite=False):
755 780 source, revision, kind = state
756 781 self._fetch(source, revision)
757 782 # if the repo was set to be bare, unbare it
758 783 if self._gitcommand(['config', '--bool', 'core.bare']) == 'true':
759 784 self._gitcommand(['config', 'core.bare', 'false'])
760 785 if self._gitstate() == revision:
761 786 self._gitcommand(['reset', '--hard', 'HEAD'])
762 787 return
763 788 elif self._gitstate() == revision:
764 789 if overwrite:
765 790 # first reset the index to unmark new files for commit, because
766 791 # reset --hard will otherwise throw away files added for commit,
767 792 # not just unmark them.
768 793 self._gitcommand(['reset', 'HEAD'])
769 794 self._gitcommand(['reset', '--hard', 'HEAD'])
770 795 return
771 796 branch2rev, rev2branch = self._gitbranchmap()
772 797
773 798 def checkout(args):
774 799 cmd = ['checkout']
775 800 if overwrite:
776 801 # first reset the index to unmark new files for commit, because
777 802 # the -f option will otherwise throw away files added for
778 803 # commit, not just unmark them.
779 804 self._gitcommand(['reset', 'HEAD'])
780 805 cmd.append('-f')
781 806 self._gitcommand(cmd + args)
782 807
783 808 def rawcheckout():
784 809 # no branch to checkout, check it out with no branch
785 810 self._ui.warn(_('checking out detached HEAD in subrepo %s\n') %
786 811 self._relpath)
787 812 self._ui.warn(_('check out a git branch if you intend '
788 813 'to make changes\n'))
789 814 checkout(['-q', revision])
790 815
791 816 if revision not in rev2branch:
792 817 rawcheckout()
793 818 return
794 819 branches = rev2branch[revision]
795 820 firstlocalbranch = None
796 821 for b in branches:
797 822 if b == 'refs/heads/master':
798 823 # master trumps all other branches
799 824 checkout(['refs/heads/master'])
800 825 return
801 826 if not firstlocalbranch and not b.startswith('refs/remotes/'):
802 827 firstlocalbranch = b
803 828 if firstlocalbranch:
804 829 checkout([firstlocalbranch])
805 830 return
806 831
807 832 tracking = self._gittracking(branch2rev.keys())
808 833 # choose a remote branch already tracked if possible
809 834 remote = branches[0]
810 835 if remote not in tracking:
811 836 for b in branches:
812 837 if b in tracking:
813 838 remote = b
814 839 break
815 840
816 841 if remote not in tracking:
817 842 # create a new local tracking branch
818 843 local = remote.split('/', 2)[2]
819 844 checkout(['-b', local, remote])
820 845 elif self._gitisancestor(branch2rev[tracking[remote]], remote):
821 846 # When updating to a tracked remote branch,
822 847 # if the local tracking branch is downstream of it,
823 848 # a normal `git pull` would have performed a "fast-forward merge"
824 849 # which is equivalent to updating the local branch to the remote.
825 850 # Since we are only looking at branching at update, we need to
826 851 # detect this situation and perform this action lazily.
827 852 if tracking[remote] != self._gitcurrentbranch():
828 853 checkout([tracking[remote]])
829 854 self._gitcommand(['merge', '--ff', remote])
830 855 else:
831 856 # a real merge would be required, just checkout the revision
832 857 rawcheckout()
833 858
834 859 def commit(self, text, user, date):
835 860 cmd = ['commit', '-a', '-m', text]
836 861 env = os.environ.copy()
837 862 if user:
838 863 cmd += ['--author', user]
839 864 if date:
840 865 # git's date parser silently ignores when seconds < 1e9
841 866 # convert to ISO8601
842 867 env['GIT_AUTHOR_DATE'] = util.datestr(date,
843 868 '%Y-%m-%dT%H:%M:%S %1%2')
844 869 self._gitcommand(cmd, env=env)
845 870 # make sure commit works otherwise HEAD might not exist under certain
846 871 # circumstances
847 872 return self._gitstate()
848 873
849 874 def merge(self, state):
850 875 source, revision, kind = state
851 876 self._fetch(source, revision)
852 877 base = self._gitcommand(['merge-base', revision, self._state[1]])
853 if base == revision:
854 self.get(state) # fast forward merge
855 elif base != self._state[1]:
856 self._gitcommand(['merge', '--no-commit', revision])
878 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
879
880 def mergefunc():
881 if base == revision:
882 self.get(state) # fast forward merge
883 elif base != self._state[1]:
884 self._gitcommand(['merge', '--no-commit', revision])
885
886 if self.dirty():
887 if self._gitstate() != revision:
888 dirty = self._gitstate() == self._state[1] or code != 0
889 if _updateprompt(self._ui, self, dirty, self._state[1], revision):
890 mergefunc()
891 else:
892 mergefunc()
857 893
858 894 def push(self, force):
859 895 # if a branch in origin contains the revision, nothing to do
860 896 branch2rev, rev2branch = self._gitbranchmap()
861 897 if self._state[1] in rev2branch:
862 898 for b in rev2branch[self._state[1]]:
863 899 if b.startswith('refs/remotes/origin/'):
864 900 return True
865 901 for b, revision in branch2rev.iteritems():
866 902 if b.startswith('refs/remotes/origin/'):
867 903 if self._gitisancestor(self._state[1], revision):
868 904 return True
869 905 # otherwise, try to push the currently checked out branch
870 906 cmd = ['push']
871 907 if force:
872 908 cmd.append('--force')
873 909
874 910 current = self._gitcurrentbranch()
875 911 if current:
876 912 # determine if the current branch is even useful
877 913 if not self._gitisancestor(self._state[1], current):
878 914 self._ui.warn(_('unrelated git branch checked out '
879 915 'in subrepo %s\n') % self._relpath)
880 916 return False
881 917 self._ui.status(_('pushing branch %s of subrepo %s\n') %
882 918 (current.split('/', 2)[2], self._relpath))
883 919 self._gitcommand(cmd + ['origin', current])
884 920 return True
885 921 else:
886 922 self._ui.warn(_('no branch checked out in subrepo %s\n'
887 923 'cannot push revision %s') %
888 924 (self._relpath, self._state[1]))
889 925 return False
890 926
891 927 def remove(self):
892 928 if self.dirty():
893 929 self._ui.warn(_('not removing repo %s because '
894 930 'it has changes.\n') % self._relpath)
895 931 return
896 932 # we can't fully delete the repository as it may contain
897 933 # local-only history
898 934 self._ui.note(_('removing subrepo %s\n') % self._relpath)
899 935 self._gitcommand(['config', 'core.bare', 'true'])
900 936 for f in os.listdir(self._abspath):
901 937 if f == '.git':
902 938 continue
903 939 path = os.path.join(self._abspath, f)
904 940 if os.path.isdir(path) and not os.path.islink(path):
905 941 shutil.rmtree(path)
906 942 else:
907 943 os.remove(path)
908 944
909 945 def archive(self, ui, archiver, prefix):
910 946 source, revision = self._state
911 947 self._fetch(source, revision)
912 948
913 949 # Parse git's native archive command.
914 950 # This should be much faster than manually traversing the trees
915 951 # and objects with many subprocess calls.
916 952 tarstream = self._gitcommand(['archive', revision], stream=True)
917 953 tar = tarfile.open(fileobj=tarstream, mode='r|')
918 954 relpath = subrelpath(self)
919 955 ui.progress(_('archiving (%s)') % relpath, 0, unit=_('files'))
920 956 for i, info in enumerate(tar):
921 957 if info.isdir():
922 958 continue
923 959 if info.issym():
924 960 data = info.linkname
925 961 else:
926 962 data = tar.extractfile(info).read()
927 963 archiver.addfile(os.path.join(prefix, self._path, info.name),
928 964 info.mode, info.issym(), data)
929 965 ui.progress(_('archiving (%s)') % relpath, i + 1,
930 966 unit=_('files'))
931 967 ui.progress(_('archiving (%s)') % relpath, None)
932 968
933 969
934 970 def status(self, rev2, **opts):
935 971 rev1 = self._state[1]
936 972 modified, added, removed = [], [], []
937 973 if rev2:
938 974 command = ['diff-tree', rev1, rev2]
939 975 else:
940 976 command = ['diff-index', rev1]
941 977 out = self._gitcommand(command)
942 978 for line in out.split('\n'):
943 979 tab = line.find('\t')
944 980 if tab == -1:
945 981 continue
946 982 status, f = line[tab - 1], line[tab + 1:]
947 983 if status == 'M':
948 984 modified.append(f)
949 985 elif status == 'A':
950 986 added.append(f)
951 987 elif status == 'D':
952 988 removed.append(f)
953 989
954 990 deleted = unknown = ignored = clean = []
955 991 return modified, added, removed, deleted, unknown, ignored, clean
956 992
957 993 types = {
958 994 'hg': hgsubrepo,
959 995 'svn': svnsubrepo,
960 996 'git': gitsubrepo,
961 997 }
@@ -1,331 +1,445 b''
1 1 $ "$TESTDIR/hghave" git || exit 80
2 2
3 3 make git commits repeatable
4 4
5 5 $ GIT_AUTHOR_NAME='test'; export GIT_AUTHOR_NAME
6 6 $ GIT_AUTHOR_EMAIL='test@example.org'; export GIT_AUTHOR_EMAIL
7 7 $ GIT_AUTHOR_DATE='1234567891 +0000'; export GIT_AUTHOR_DATE
8 8 $ GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; export GIT_COMMITTER_NAME
9 9 $ GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"; export GIT_COMMITTER_EMAIL
10 10 $ GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"; export GIT_COMMITTER_DATE
11 11
12 12 root hg repo
13 13
14 14 $ hg init t
15 15 $ cd t
16 16 $ echo a > a
17 17 $ hg add a
18 18 $ hg commit -m a
19 19 $ cd ..
20 20
21 21 new external git repo
22 22
23 23 $ mkdir gitroot
24 24 $ cd gitroot
25 25 $ git init -q
26 26 $ echo g > g
27 27 $ git add g
28 28 $ git commit -q -m g
29 29
30 30 add subrepo clone
31 31
32 32 $ cd ../t
33 33 $ echo 's = [git]../gitroot' > .hgsub
34 34 $ git clone -q ../gitroot s
35 35 $ hg add .hgsub
36 36 $ hg commit -m 'new git subrepo'
37 37 committing subrepository s
38 38 $ hg debugsub
39 39 path s
40 40 source ../gitroot
41 41 revision da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
42 42
43 43 record a new commit from upstream from a different branch
44 44
45 45 $ cd ../gitroot
46 46 $ git checkout -q -b testing
47 47 $ echo gg >> g
48 48 $ git commit -q -a -m gg
49 49
50 50 $ cd ../t/s
51 51 $ git pull -q >/dev/null 2>/dev/null
52 52 $ git checkout -q -b testing origin/testing >/dev/null
53 53
54 54 $ cd ..
55 55 $ hg status --subrepos
56 56 M s/g
57 57 $ hg commit -m 'update git subrepo'
58 58 committing subrepository s
59 59 $ hg debugsub
60 60 path s
61 61 source ../gitroot
62 62 revision 126f2a14290cd5ce061fdedc430170e8d39e1c5a
63 63
64 64 make $GITROOT pushable, by replacing it with a clone with nothing checked out
65 65
66 66 $ cd ..
67 67 $ git clone gitroot gitrootbare --bare -q
68 68 $ rm -rf gitroot
69 69 $ mv gitrootbare gitroot
70 70
71 71 clone root
72 72
73 73 $ cd t
74 74 $ hg clone . ../tc
75 75 updating to branch default
76 76 cloning subrepo s
77 77 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
78 78 $ cd ../tc
79 79 $ hg debugsub
80 80 path s
81 81 source ../gitroot
82 82 revision 126f2a14290cd5ce061fdedc430170e8d39e1c5a
83 83
84 84 update to previous substate
85 85
86 86 $ hg update 1 -q
87 87 $ cat s/g
88 88 g
89 89 $ hg debugsub
90 90 path s
91 91 source ../gitroot
92 92 revision da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
93 93
94 94 clone root, make local change
95 95
96 96 $ cd ../t
97 97 $ hg clone . ../ta
98 98 updating to branch default
99 99 cloning subrepo s
100 100 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
101 101
102 102 $ cd ../ta
103 103 $ echo ggg >> s/g
104 104 $ hg status --subrepos
105 105 M s/g
106 106 $ hg commit -m ggg
107 107 committing subrepository s
108 108 $ hg debugsub
109 109 path s
110 110 source ../gitroot
111 111 revision 79695940086840c99328513acbe35f90fcd55e57
112 112
113 113 clone root separately, make different local change
114 114
115 115 $ cd ../t
116 116 $ hg clone . ../tb
117 117 updating to branch default
118 118 cloning subrepo s
119 119 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
120 120
121 121 $ cd ../tb/s
122 122 $ echo f > f
123 123 $ git add f
124 124 $ cd ..
125 125
126 126 $ hg status --subrepos
127 127 A s/f
128 128 $ hg commit -m f
129 129 committing subrepository s
130 130 $ hg debugsub
131 131 path s
132 132 source ../gitroot
133 133 revision aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
134 134
135 135 user b push changes
136 136
137 137 $ hg push 2>/dev/null
138 138 pushing to $TESTTMP/t
139 139 pushing branch testing of subrepo s
140 140 searching for changes
141 141 adding changesets
142 142 adding manifests
143 143 adding file changes
144 144 added 1 changesets with 1 changes to 1 files
145 145
146 146 user a pulls, merges, commits
147 147
148 148 $ cd ../ta
149 149 $ hg pull
150 150 pulling from $TESTTMP/t
151 151 searching for changes
152 152 adding changesets
153 153 adding manifests
154 154 adding file changes
155 155 added 1 changesets with 1 changes to 1 files (+1 heads)
156 156 (run 'hg heads' to see heads, 'hg merge' to merge)
157 157 $ hg merge 2>/dev/null
158 158 pulling subrepo s
159 159 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
160 160 (branch merge, don't forget to commit)
161 161 $ cat s/f
162 162 f
163 163 $ cat s/g
164 164 g
165 165 gg
166 166 ggg
167 167 $ hg commit -m 'merge'
168 168 committing subrepository s
169 169 $ hg status --subrepos --rev 1:5
170 170 M .hgsubstate
171 171 M s/g
172 172 A s/f
173 173 $ hg debugsub
174 174 path s
175 175 source ../gitroot
176 176 revision f47b465e1bce645dbf37232a00574aa1546ca8d3
177 177 $ hg push 2>/dev/null
178 178 pushing to $TESTTMP/t
179 179 pushing branch testing of subrepo s
180 180 searching for changes
181 181 adding changesets
182 182 adding manifests
183 183 adding file changes
184 184 added 2 changesets with 2 changes to 1 files
185 185
186 186 make upstream git changes
187 187
188 188 $ cd ..
189 189 $ git clone -q gitroot gitclone
190 190 $ cd gitclone
191 191 $ echo ff >> f
192 192 $ git commit -q -a -m ff
193 193 $ echo fff >> f
194 194 $ git commit -q -a -m fff
195 195 $ git push origin testing 2>/dev/null
196 196
197 197 make and push changes to hg without updating the subrepo
198 198
199 199 $ cd ../t
200 200 $ hg clone . ../td
201 201 updating to branch default
202 202 cloning subrepo s
203 203 checking out detached HEAD in subrepo s
204 204 check out a git branch if you intend to make changes
205 205 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
206 206 $ cd ../td
207 207 $ echo aa >> a
208 208 $ hg commit -m aa
209 209 $ hg push
210 210 pushing to $TESTTMP/t
211 211 searching for changes
212 212 adding changesets
213 213 adding manifests
214 214 adding file changes
215 215 added 1 changesets with 1 changes to 1 files
216 216
217 217 sync to upstream git, distribute changes
218 218
219 219 $ cd ../ta
220 220 $ hg pull -u -q
221 221 $ cd s
222 222 $ git pull -q >/dev/null 2>/dev/null
223 223 $ cd ..
224 224 $ hg commit -m 'git upstream sync'
225 225 committing subrepository s
226 226 $ hg debugsub
227 227 path s
228 228 source ../gitroot
229 229 revision 32a343883b74769118bb1d3b4b1fbf9156f4dddc
230 230 $ hg push -q
231 231
232 232 $ cd ../tb
233 233 $ hg pull -q
234 234 $ hg update 2>/dev/null
235 235 pulling subrepo s
236 236 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
237 237 $ hg debugsub
238 238 path s
239 239 source ../gitroot
240 240 revision 32a343883b74769118bb1d3b4b1fbf9156f4dddc
241 241
242 242 update to a revision without the subrepo, keeping the local git repository
243 243
244 244 $ cd ../t
245 245 $ hg up 0
246 246 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
247 247 $ ls -a s
248 248 .
249 249 ..
250 250 .git
251 251
252 252 $ hg up 2
253 253 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
254 254 $ ls -a s
255 255 .
256 256 ..
257 257 .git
258 258 g
259 259
260 260 archive subrepos
261 261
262 262 $ cd ../tc
263 263 $ hg pull -q
264 264 $ hg archive --subrepos -r 5 ../archive 2>/dev/null
265 265 pulling subrepo s
266 266 $ cd ../archive
267 267 $ cat s/f
268 268 f
269 269 $ cat s/g
270 270 g
271 271 gg
272 272 ggg
273 273
274 274 create nested repo
275 275
276 276 $ cd ..
277 277 $ hg init outer
278 278 $ cd outer
279 279 $ echo b>b
280 280 $ hg add b
281 281 $ hg commit -m b
282 282
283 283 $ hg clone ../t inner
284 284 updating to branch default
285 285 cloning subrepo s
286 286 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
287 287 $ echo inner = inner > .hgsub
288 288 $ hg add .hgsub
289 289 $ hg commit -m 'nested sub'
290 290 committing subrepository inner
291 291
292 292 nested commit
293 293
294 294 $ echo ffff >> inner/s/f
295 295 $ hg status --subrepos
296 296 M inner/s/f
297 297 $ hg commit -m nested
298 298 committing subrepository inner
299 299 committing subrepository inner/s
300 300
301 301 nested archive
302 302
303 303 $ hg archive --subrepos ../narchive
304 304 $ ls ../narchive/inner/s | grep -v pax_global_header
305 305 f
306 306 g
307 307
308 308 Check hg update --clean
309 309 $ cd $TESTTMP/ta
310 310 $ echo > s/g
311 311 $ cd s
312 312 $ echo c1 > f1
313 313 $ echo c1 > f2
314 314 $ git add f1
315 315 $ cd ..
316 316 $ hg status -S
317 317 M s/g
318 318 A s/f1
319 319 $ ls s
320 320 f
321 321 f1
322 322 f2
323 323 g
324 324 $ hg update --clean
325 325 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
326 326 $ hg status -S
327 327 $ ls s
328 328 f
329 329 f1
330 330 f2
331 331 g
332
333 Sticky subrepositories, no changes
334 $ cd $TESTTMP/ta
335 $ hg id -n
336 7
337 $ cd s
338 $ git rev-parse HEAD
339 32a343883b74769118bb1d3b4b1fbf9156f4dddc
340 $ cd ..
341 $ hg update 1 > /dev/null 2>&1
342 $ hg id -n
343 1
344 $ cd s
345 $ git rev-parse HEAD
346 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
347 $ cd ..
348
349 Sticky subrepositorys, file changes
350 $ touch s/f1
351 $ cd s
352 $ git add f1
353 $ cd ..
354 $ hg id -n
355 1
356 $ cd s
357 $ git rev-parse HEAD
358 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
359 $ cd ..
360 $ hg update 4
361 subrepository sources for s differ
362 use (l)ocal source (da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7) or (r)emote source (aa84837ccfbdfedcdcdeeedc309d73e6eb069edc)?
363 l
364 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
365 $ hg id -n
366 4+
367 $ cd s
368 $ git rev-parse HEAD
369 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
370 $ cd ..
371 $ hg update --clean tip > /dev/null 2>&1
372
373 Sticky subrepository, revision updates
374 $ hg id -n
375 7
376 $ cd s
377 $ git rev-parse HEAD
378 32a343883b74769118bb1d3b4b1fbf9156f4dddc
379 $ cd ..
380 $ cd s
381 $ git checkout aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
382 Previous HEAD position was 32a3438... fff
383 HEAD is now at aa84837... f
384 $ cd ..
385 $ hg update 1
386 subrepository sources for s differ (in checked out version)
387 use (l)ocal source (32a343883b74769118bb1d3b4b1fbf9156f4dddc) or (r)emote source (da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7)?
388 l
389 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
390 $ hg id -n
391 1+
392 $ cd s
393 $ git rev-parse HEAD
394 aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
395 $ cd ..
396
397 Sticky subrepository, file changes and revision updates
398 $ touch s/f1
399 $ cd s
400 $ git add f1
401 $ git rev-parse HEAD
402 aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
403 $ cd ..
404 $ hg id -n
405 1+
406 $ hg update 7
407 subrepository sources for s differ
408 use (l)ocal source (32a343883b74769118bb1d3b4b1fbf9156f4dddc) or (r)emote source (32a343883b74769118bb1d3b4b1fbf9156f4dddc)?
409 l
410 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
411 $ hg id -n
412 7
413 $ cd s
414 $ git rev-parse HEAD
415 aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
416 $ cd ..
417
418 Sticky repository, update --clean
419 $ hg update --clean tip
420 Previous HEAD position was aa84837... f
421 HEAD is now at 32a3438... fff
422 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
423 $ hg id -n
424 7
425 $ cd s
426 $ git rev-parse HEAD
427 32a343883b74769118bb1d3b4b1fbf9156f4dddc
428 $ cd ..
429
430 Test subrepo already at intended revision:
431 $ cd s
432 $ git checkout 32a343883b74769118bb1d3b4b1fbf9156f4dddc
433 HEAD is now at 32a3438... fff
434 $ cd ..
435 $ hg update 1
436 Previous HEAD position was 32a3438... fff
437 HEAD is now at da5f5b1... g
438 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
439 $ hg id -n
440 1
441 $ cd s
442 $ git rev-parse HEAD
443 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
444 $ cd ..
445
@@ -1,298 +1,440 b''
1 1 $ "$TESTDIR/hghave" svn || exit 80
2 2
3 3 $ fix_path()
4 4 > {
5 5 > tr '\\' /
6 6 > }
7 7
8 8 SVN wants all paths to start with a slash. Unfortunately, Windows ones
9 9 don't. Handle that.
10 10
11 11 $ escapedwd=`pwd | fix_path`
12 12 $ expr "$escapedwd" : '\/' > /dev/null || escapedwd="/$escapedwd"
13 13 $ escapedwd=`python -c "import urllib, sys; sys.stdout.write(urllib.quote(sys.argv[1]))" "$escapedwd"`
14 14
15 15 create subversion repo
16 16
17 17 $ SVNREPO="file://$escapedwd/svn-repo"
18 18 $ WCROOT="`pwd`/svn-wc"
19 19 $ svnadmin create svn-repo
20 20 $ svn co "$SVNREPO" svn-wc
21 21 Checked out revision 0.
22 22 $ cd svn-wc
23 23 $ mkdir src
24 24 $ echo alpha > src/alpha
25 25 $ svn add src
26 26 A src
27 27 A src/alpha
28 28 $ mkdir externals
29 29 $ echo other > externals/other
30 30 $ svn add externals
31 31 A externals
32 32 A externals/other
33 33 $ svn ci -m 'Add alpha'
34 34 Adding externals
35 35 Adding externals/other
36 36 Adding src
37 37 Adding src/alpha
38 38 Transmitting file data ..
39 39 Committed revision 1.
40 40 $ svn up
41 41 At revision 1.
42 42 $ echo "externals -r1 $SVNREPO/externals" > extdef
43 43 $ svn propset -F extdef svn:externals src
44 44 property 'svn:externals' set on 'src'
45 45 $ svn ci -m 'Setting externals'
46 46 Sending src
47 47
48 48 Committed revision 2.
49 49 $ cd ..
50 50
51 51 create hg repo
52 52
53 53 $ mkdir sub
54 54 $ cd sub
55 55 $ hg init t
56 56 $ cd t
57 57
58 58 first revision, no sub
59 59
60 60 $ echo a > a
61 61 $ hg ci -Am0
62 62 adding a
63 63
64 64 add first svn sub with leading whitespaces
65 65
66 66 $ echo "s = [svn] $SVNREPO/src" >> .hgsub
67 67 $ echo "subdir/s = [svn] $SVNREPO/src" >> .hgsub
68 68 $ svn co --quiet "$SVNREPO"/src s
69 69 $ mkdir subdir
70 70 $ svn co --quiet "$SVNREPO"/src subdir/s
71 71 $ hg add .hgsub
72 72 $ hg ci -m1
73 73 committing subrepository s
74 74 committing subrepository subdir/s
75 75
76 76 make sure we avoid empty commits (issue2445)
77 77
78 78 $ hg sum
79 79 parent: 1:* tip (glob)
80 80 1
81 81 branch: default
82 82 commit: (clean)
83 83 update: (current)
84 84 $ hg ci -moops
85 85 nothing changed
86 86 [1]
87 87
88 88 debugsub
89 89
90 90 $ hg debugsub
91 91 path s
92 92 source file://*/svn-repo/src (glob)
93 93 revision 2
94 94 path subdir/s
95 95 source file://*/svn-repo/src (glob)
96 96 revision 2
97 97
98 98 change file in svn and hg, commit
99 99
100 100 $ echo a >> a
101 101 $ echo alpha >> s/alpha
102 102 $ hg sum
103 103 parent: 1:* tip (glob)
104 104 1
105 105 branch: default
106 106 commit: 1 modified, 1 subrepos
107 107 update: (current)
108 108 $ hg commit -m 'Message!'
109 109 committing subrepository s
110 110 Sending*s/alpha (glob)
111 111 Transmitting file data .
112 112 Committed revision 3.
113 113
114 114 Fetching external item into '$TESTTMP/sub/t/s/externals'
115 115 External at revision 1.
116 116
117 117 At revision 3.
118 118 $ hg debugsub
119 119 path s
120 120 source file://*/svn-repo/src (glob)
121 121 revision 3
122 122 path subdir/s
123 123 source file://*/svn-repo/src (glob)
124 124 revision 2
125 125
126 126 add an unrelated revision in svn and update the subrepo to without
127 127 bringing any changes.
128 128
129 129 $ svn mkdir "$SVNREPO/unrelated" -m 'create unrelated'
130 130
131 131 Committed revision 4.
132 132 $ svn up s
133 133
134 134 Fetching external item into 's/externals'
135 135 External at revision 1.
136 136
137 137 At revision 4.
138 138 $ hg sum
139 139 parent: 2:* tip (glob)
140 140 Message!
141 141 branch: default
142 142 commit: (clean)
143 143 update: (current)
144 144
145 145 $ echo a > s/a
146 146
147 147 should be empty despite change to s/a
148 148
149 149 $ hg st
150 150
151 151 add a commit from svn
152 152
153 153 $ cd "$WCROOT"/src
154 154 $ svn up
155 155 U alpha
156 156
157 157 Fetching external item into 'externals'
158 158 A externals/other
159 159 Updated external to revision 1.
160 160
161 161 Updated to revision 4.
162 162 $ echo xyz >> alpha
163 163 $ svn propset svn:mime-type 'text/xml' alpha
164 164 property 'svn:mime-type' set on 'alpha'
165 165 $ svn ci -m 'amend a from svn'
166 166 Sending src/alpha
167 167 Transmitting file data .
168 168 Committed revision 5.
169 169 $ cd ../../sub/t
170 170
171 171 this commit from hg will fail
172 172
173 173 $ echo zzz >> s/alpha
174 174 $ hg ci -m 'amend alpha from hg'
175 175 committing subrepository s
176 176 abort: svn: Commit failed (details follow):
177 177 svn: (Out of date)?.*/src/alpha.*(is out of date)? (re)
178 178 [255]
179 179 $ svn revert -q s/alpha
180 180
181 181 this commit fails because of meta changes
182 182
183 183 $ svn propset svn:mime-type 'text/html' s/alpha
184 184 property 'svn:mime-type' set on 's/alpha'
185 185 $ hg ci -m 'amend alpha from hg'
186 186 committing subrepository s
187 187 abort: svn: Commit failed (details follow):
188 188 svn: (Out of date)?.*/src/alpha.*(is out of date)? (re)
189 189 [255]
190 190 $ svn revert -q s/alpha
191 191
192 192 this commit fails because of externals changes
193 193
194 194 $ echo zzz > s/externals/other
195 195 $ hg ci -m 'amend externals from hg'
196 196 committing subrepository s
197 197 abort: cannot commit svn externals
198 198 [255]
199 199 $ hg diff --subrepos -r 1:2 | grep -v diff
200 200 --- a/.hgsubstate Thu Jan 01 00:00:00 1970 +0000
201 201 +++ b/.hgsubstate Thu Jan 01 00:00:00 1970 +0000
202 202 @@ -1,2 +1,2 @@
203 203 -2 s
204 204 +3 s
205 205 2 subdir/s
206 206 --- a/a Thu Jan 01 00:00:00 1970 +0000
207 207 +++ b/a Thu Jan 01 00:00:00 1970 +0000
208 208 @@ -1,1 +1,2 @@
209 209 a
210 210 +a
211 211 $ svn revert -q s/externals/other
212 212
213 213 this commit fails because of externals meta changes
214 214
215 215 $ svn propset svn:mime-type 'text/html' s/externals/other
216 216 property 'svn:mime-type' set on 's/externals/other'
217 217 $ hg ci -m 'amend externals from hg'
218 218 committing subrepository s
219 219 abort: cannot commit svn externals
220 220 [255]
221 221 $ svn revert -q s/externals/other
222 222
223 223 clone
224 224
225 225 $ cd ..
226 226 $ hg clone t tc | fix_path
227 227 updating to branch default
228 228 A tc/subdir/s/alpha
229 229 U tc/subdir/s
230 230
231 231 Fetching external item into 'tc/subdir/s/externals'
232 232 A tc/subdir/s/externals/other
233 233 Checked out external at revision 1.
234 234
235 235 Checked out revision 2.
236 236 A tc/s/alpha
237 237 U tc/s
238 238
239 239 Fetching external item into 'tc/s/externals'
240 240 A tc/s/externals/other
241 241 Checked out external at revision 1.
242 242
243 243 Checked out revision 3.
244 244 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
245 245 $ cd tc
246 246
247 247 debugsub in clone
248 248
249 249 $ hg debugsub
250 250 path s
251 251 source file://*/svn-repo/src (glob)
252 252 revision 3
253 253 path subdir/s
254 254 source file://*/svn-repo/src (glob)
255 255 revision 2
256 256
257 257 verify subrepo is contained within the repo directory
258 258
259 259 $ python -c "import os.path; print os.path.exists('s')"
260 260 True
261 261
262 262 update to nullrev (must delete the subrepo)
263 263
264 264 $ hg up null
265 265 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
266 266 $ ls
267 267
268 268 Check hg update --clean
269 269 $ cd $TESTTMP/sub/t
270 270 $ cd s
271 271 $ echo c0 > alpha
272 272 $ echo c1 > f1
273 273 $ echo c1 > f2
274 274 $ svn add f1 -q
275 275 $ svn status
276 276 ? * a (glob)
277 277 X * externals (glob)
278 278 ? * f2 (glob)
279 279 M * alpha (glob)
280 280 A * f1 (glob)
281 281
282 282 Performing status on external item at 'externals'
283 283 $ cd ../..
284 284 $ hg -R t update -C
285 285
286 286 Fetching external item into 't/s/externals'
287 287 Checked out external at revision 1.
288 288
289 289 Checked out revision 3.
290 290 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
291 291 $ cd t/s
292 292 $ svn status
293 293 ? * a (glob)
294 294 X * externals (glob)
295 295 ? * f1 (glob)
296 296 ? * f2 (glob)
297 297
298 298 Performing status on external item at 'externals'
299
300 Sticky subrepositories, no changes
301 $ cd $TESTTMP/sub/t
302 $ hg id -n
303 2
304 $ cd s
305 $ svnversion
306 3
307 $ cd ..
308 $ hg update 1
309 U $TESTTMP/sub/t/s/alpha
310
311 Fetching external item into '$TESTTMP/sub/t/s/externals'
312 Checked out external at revision 1.
313
314 Checked out revision 2.
315 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
316 $ hg id -n
317 1
318 $ cd s
319 $ svnversion
320 2
321 $ cd ..
322
323 Sticky subrepositorys, file changes
324 $ touch s/f1
325 $ cd s
326 $ svn add f1
327 A f1
328 $ cd ..
329 $ hg id -n
330 1
331 $ cd s
332 $ svnversion
333 2M
334 $ cd ..
335 $ hg update tip
336 subrepository sources for s differ
337 use (l)ocal source (2) or (r)emote source (3)?
338 l
339 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
340 $ hg id -n
341 2+
342 $ cd s
343 $ svnversion
344 2M
345 $ cd ..
346 $ hg update --clean tip
347 U $TESTTMP/sub/t/s/alpha
348
349 Fetching external item into '$TESTTMP/sub/t/s/externals'
350 Checked out external at revision 1.
351
352 Checked out revision 3.
353 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
354
355 Sticky subrepository, revision updates
356 $ hg id -n
357 2
358 $ cd s
359 $ svnversion
360 3
361 $ cd ..
362 $ cd s
363 $ svn update -r 1
364 U alpha
365 U .
366
367 Fetching external item into 'externals'
368 Updated external to revision 1.
369
370 Updated to revision 1.
371 $ cd ..
372 $ hg update 1
373 subrepository sources for s differ (in checked out version)
374 use (l)ocal source (1) or (r)emote source (2)?
375 l
376 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
377 $ hg id -n
378 1+
379 $ cd s
380 $ svnversion
381 1
382 $ cd ..
383
384 Sticky subrepository, file changes and revision updates
385 $ touch s/f1
386 $ cd s
387 $ svn add f1
388 A f1
389 $ svnversion
390 1M
391 $ cd ..
392 $ hg id -n
393 1+
394 $ hg update tip
395 subrepository sources for s differ
396 use (l)ocal source (1) or (r)emote source (3)?
397 l
398 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
399 $ hg id -n
400 2
401 $ cd s
402 $ svnversion
403 1M
404 $ cd ..
405
406 Sticky repository, update --clean
407 $ hg update --clean tip
408 U $TESTTMP/sub/t/s/alpha
409 U $TESTTMP/sub/t/s
410
411 Fetching external item into '$TESTTMP/sub/t/s/externals'
412 Checked out external at revision 1.
413
414 Checked out revision 3.
415 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
416 $ hg id -n
417 2
418 $ cd s
419 $ svnversion
420 3
421 $ cd ..
422
423 Test subrepo already at intended revision:
424 $ cd s
425 $ svn update -r 2
426 U alpha
427
428 Fetching external item into 'externals'
429 Updated external to revision 1.
430
431 Updated to revision 2.
432 $ cd ..
433 $ hg update 1
434 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
435 $ hg id -n
436 1+
437 $ cd s
438 $ svnversion
439 2
440 $ cd ..
@@ -1,708 +1,830 b''
1 1 $ rm -rf sub
2 2 $ mkdir sub
3 3 $ cd sub
4 4 $ hg init t
5 5 $ cd t
6 6
7 7 first revision, no sub
8 8
9 9 $ echo a > a
10 10 $ hg ci -Am0
11 11 adding a
12 12
13 13 add first sub
14 14
15 15 $ echo s = s > .hgsub
16 16 $ hg add .hgsub
17 17 $ hg init s
18 18 $ echo a > s/a
19 19
20 20 Issue2232: committing a subrepo without .hgsub
21 21
22 22 $ hg ci -mbad s
23 23 abort: can't commit subrepos without .hgsub
24 24 [255]
25 25
26 26 $ hg -R s ci -Ams0
27 27 adding a
28 28 $ hg sum
29 29 parent: 0:f7b1eb17ad24 tip
30 30 0
31 31 branch: default
32 32 commit: 1 added, 1 subrepos
33 33 update: (current)
34 34 $ hg ci -m1
35 35 committing subrepository s
36 36
37 37 Issue2022: update -C
38 38
39 39 $ echo b > s/a
40 40 $ hg sum
41 41 parent: 1:7cf8cfea66e4 tip
42 42 1
43 43 branch: default
44 44 commit: 1 subrepos
45 45 update: (current)
46 46 $ hg co -C 1
47 47 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
48 48 $ hg sum
49 49 parent: 1:7cf8cfea66e4 tip
50 50 1
51 51 branch: default
52 52 commit: (clean)
53 53 update: (current)
54 54
55 55 add sub sub
56 56
57 57 $ echo ss = ss > s/.hgsub
58 58 $ hg init s/ss
59 59 $ echo a > s/ss/a
60 60 $ hg -R s add s/.hgsub
61 61 $ hg -R s/ss add s/ss/a
62 62 $ hg sum
63 63 parent: 1:7cf8cfea66e4 tip
64 64 1
65 65 branch: default
66 66 commit: 1 subrepos
67 67 update: (current)
68 68 $ hg ci -m2
69 69 committing subrepository s
70 70 committing subrepository s/ss
71 71 $ hg sum
72 72 parent: 2:df30734270ae tip
73 73 2
74 74 branch: default
75 75 commit: (clean)
76 76 update: (current)
77 77
78 78 bump sub rev (and check it is ignored by ui.commitsubrepos)
79 79
80 80 $ echo b > s/a
81 81 $ hg -R s ci -ms1
82 82 $ hg --config ui.commitsubrepos=no ci -m3
83 83 committing subrepository s
84 84
85 85 leave sub dirty (and check ui.commitsubrepos=no aborts the commit)
86 86
87 87 $ echo c > s/a
88 88 $ hg --config ui.commitsubrepos=no ci -m4
89 89 abort: uncommitted changes in subrepo s
90 90 [255]
91 91 $ hg ci -m4
92 92 committing subrepository s
93 93 $ hg tip -R s
94 94 changeset: 3:1c833a7a9e3a
95 95 tag: tip
96 96 user: test
97 97 date: Thu Jan 01 00:00:00 1970 +0000
98 98 summary: 4
99 99
100 100
101 101 check caching
102 102
103 103 $ hg co 0
104 104 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
105 105 $ hg debugsub
106 106
107 107 restore
108 108
109 109 $ hg co
110 110 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
111 111 $ hg debugsub
112 112 path s
113 113 source s
114 114 revision 1c833a7a9e3a4445c711aaf0f012379cd0d4034e
115 115
116 116 new branch for merge tests
117 117
118 118 $ hg co 1
119 119 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
120 120 $ echo t = t >> .hgsub
121 121 $ hg init t
122 122 $ echo t > t/t
123 123 $ hg -R t add t
124 124 adding t/t
125 125
126 126 5
127 127
128 128 $ hg ci -m5 # add sub
129 129 committing subrepository t
130 130 created new head
131 131 $ echo t2 > t/t
132 132
133 133 6
134 134
135 135 $ hg st -R s
136 136 $ hg ci -m6 # change sub
137 137 committing subrepository t
138 138 $ hg debugsub
139 139 path s
140 140 source s
141 141 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
142 142 path t
143 143 source t
144 144 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
145 145 $ echo t3 > t/t
146 146
147 147 7
148 148
149 149 $ hg ci -m7 # change sub again for conflict test
150 150 committing subrepository t
151 151 $ hg rm .hgsub
152 152
153 153 8
154 154
155 155 $ hg ci -m8 # remove sub
156 156
157 157 merge tests
158 158
159 159 $ hg co -C 3
160 160 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
161 161 $ hg merge 5 # test adding
162 162 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
163 163 (branch merge, don't forget to commit)
164 164 $ hg debugsub
165 165 path s
166 166 source s
167 167 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
168 168 path t
169 169 source t
170 170 revision 60ca1237c19474e7a3978b0dc1ca4e6f36d51382
171 171 $ hg ci -m9
172 172 created new head
173 173 $ hg merge 6 --debug # test change
174 174 searching for copies back to rev 2
175 175 resolving manifests
176 176 overwrite None partial False
177 177 ancestor 1f14a2e2d3ec local f0d2028bf86d+ remote 1831e14459c4
178 178 .hgsubstate: versions differ -> m
179 179 updating: .hgsubstate 1/1 files (100.00%)
180 180 subrepo merge f0d2028bf86d+ 1831e14459c4 1f14a2e2d3ec
181 181 subrepo t: other changed, get t:6747d179aa9a688023c4b0cad32e4c92bb7f34ad:hg
182 182 getting subrepo t
183 183 resolving manifests
184 184 overwrite True partial False
185 185 ancestor 60ca1237c194+ local 60ca1237c194+ remote 6747d179aa9a
186 186 t: remote is newer -> g
187 187 updating: t 1/1 files (100.00%)
188 188 getting t
189 189 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
190 190 (branch merge, don't forget to commit)
191 191 $ hg debugsub
192 192 path s
193 193 source s
194 194 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
195 195 path t
196 196 source t
197 197 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
198 198 $ echo conflict > t/t
199 199 $ hg ci -m10
200 200 committing subrepository t
201 201 $ HGMERGE=internal:merge hg merge --debug 7 # test conflict
202 202 searching for copies back to rev 2
203 203 resolving manifests
204 204 overwrite None partial False
205 205 ancestor 1831e14459c4 local e45c8b14af55+ remote f94576341bcf
206 206 .hgsubstate: versions differ -> m
207 207 updating: .hgsubstate 1/1 files (100.00%)
208 208 subrepo merge e45c8b14af55+ f94576341bcf 1831e14459c4
209 209 subrepo t: both sides changed, merge with t:7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4:hg
210 210 merging subrepo t
211 211 searching for copies back to rev 2
212 212 resolving manifests
213 213 overwrite None partial False
214 214 ancestor 6747d179aa9a local 20a0db6fbf6c+ remote 7af322bc1198
215 215 t: versions differ -> m
216 216 preserving t for resolve of t
217 217 updating: t 1/1 files (100.00%)
218 218 picked tool 'internal:merge' for t (binary False symlink False)
219 219 merging t
220 220 my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
221 221 warning: conflicts during merge.
222 222 merging t failed!
223 223 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
224 224 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
225 225 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
226 226 (branch merge, don't forget to commit)
227 227
228 228 should conflict
229 229
230 230 $ cat t/t
231 231 <<<<<<< local
232 232 conflict
233 233 =======
234 234 t3
235 235 >>>>>>> other
236 236
237 237 clone
238 238
239 239 $ cd ..
240 240 $ hg clone t tc
241 241 updating to branch default
242 242 pulling subrepo s from $TESTTMP/sub/t/s
243 243 requesting all changes
244 244 adding changesets
245 245 adding manifests
246 246 adding file changes
247 247 added 4 changesets with 5 changes to 3 files
248 248 pulling subrepo s/ss from $TESTTMP/sub/t/s/ss
249 249 requesting all changes
250 250 adding changesets
251 251 adding manifests
252 252 adding file changes
253 253 added 1 changesets with 1 changes to 1 files
254 254 pulling subrepo t from $TESTTMP/sub/t/t
255 255 requesting all changes
256 256 adding changesets
257 257 adding manifests
258 258 adding file changes
259 259 added 4 changesets with 4 changes to 1 files (+1 heads)
260 260 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
261 261 $ cd tc
262 262 $ hg debugsub
263 263 path s
264 264 source s
265 265 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
266 266 path t
267 267 source t
268 268 revision 20a0db6fbf6c3d2836e6519a642ae929bfc67c0e
269 269
270 270 push
271 271
272 272 $ echo bah > t/t
273 273 $ hg ci -m11
274 274 committing subrepository t
275 275 $ hg push
276 276 pushing to $TESTTMP/sub/t
277 277 pushing subrepo s/ss to $TESTTMP/sub/t/s/ss
278 278 searching for changes
279 279 no changes found
280 280 pushing subrepo s to $TESTTMP/sub/t/s
281 281 searching for changes
282 282 no changes found
283 283 pushing subrepo t to $TESTTMP/sub/t/t
284 284 searching for changes
285 285 adding changesets
286 286 adding manifests
287 287 adding file changes
288 288 added 1 changesets with 1 changes to 1 files
289 289 searching for changes
290 290 adding changesets
291 291 adding manifests
292 292 adding file changes
293 293 added 1 changesets with 1 changes to 1 files
294 294
295 295 push -f
296 296
297 297 $ echo bah > s/a
298 298 $ hg ci -m12
299 299 committing subrepository s
300 300 $ hg push
301 301 pushing to $TESTTMP/sub/t
302 302 pushing subrepo s/ss to $TESTTMP/sub/t/s/ss
303 303 searching for changes
304 304 no changes found
305 305 pushing subrepo s to $TESTTMP/sub/t/s
306 306 searching for changes
307 307 abort: push creates new remote heads on branch 'default'!
308 308 (did you forget to merge? use push -f to force)
309 309 [255]
310 310 $ hg push -f
311 311 pushing to $TESTTMP/sub/t
312 312 pushing subrepo s/ss to $TESTTMP/sub/t/s/ss
313 313 searching for changes
314 314 no changes found
315 315 pushing subrepo s to $TESTTMP/sub/t/s
316 316 searching for changes
317 317 adding changesets
318 318 adding manifests
319 319 adding file changes
320 320 added 1 changesets with 1 changes to 1 files (+1 heads)
321 321 pushing subrepo t to $TESTTMP/sub/t/t
322 322 searching for changes
323 323 no changes found
324 324 searching for changes
325 325 adding changesets
326 326 adding manifests
327 327 adding file changes
328 328 added 1 changesets with 1 changes to 1 files
329 329
330 330 update
331 331
332 332 $ cd ../t
333 333 $ hg up -C # discard our earlier merge
334 334 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
335 335 $ echo blah > t/t
336 336 $ hg ci -m13
337 337 committing subrepository t
338 338
339 339 pull
340 340
341 341 $ cd ../tc
342 342 $ hg pull
343 343 pulling from $TESTTMP/sub/t
344 344 searching for changes
345 345 adding changesets
346 346 adding manifests
347 347 adding file changes
348 348 added 1 changesets with 1 changes to 1 files
349 349 (run 'hg update' to get a working copy)
350 350
351 351 should pull t
352 352
353 353 $ hg up
354 354 pulling subrepo t from $TESTTMP/sub/t/t
355 355 searching for changes
356 356 adding changesets
357 357 adding manifests
358 358 adding file changes
359 359 added 1 changesets with 1 changes to 1 files
360 360 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
361 361 $ cat t/t
362 362 blah
363 363
364 364 bogus subrepo path aborts
365 365
366 366 $ echo 'bogus=[boguspath' >> .hgsub
367 367 $ hg ci -m 'bogus subrepo path'
368 368 abort: missing ] in subrepo source
369 369 [255]
370 370
371 371 Issue1986: merge aborts when trying to merge a subrepo that
372 372 shouldn't need merging
373 373
374 374 # subrepo layout
375 375 #
376 376 # o 5 br
377 377 # /|
378 378 # o | 4 default
379 379 # | |
380 380 # | o 3 br
381 381 # |/|
382 382 # o | 2 default
383 383 # | |
384 384 # | o 1 br
385 385 # |/
386 386 # o 0 default
387 387
388 388 $ cd ..
389 389 $ rm -rf sub
390 390 $ hg init main
391 391 $ cd main
392 392 $ hg init s
393 393 $ cd s
394 394 $ echo a > a
395 395 $ hg ci -Am1
396 396 adding a
397 397 $ hg branch br
398 398 marked working directory as branch br
399 399 $ echo a >> a
400 400 $ hg ci -m1
401 401 $ hg up default
402 402 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
403 403 $ echo b > b
404 404 $ hg ci -Am1
405 405 adding b
406 406 $ hg up br
407 407 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
408 408 $ hg merge tip
409 409 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
410 410 (branch merge, don't forget to commit)
411 411 $ hg ci -m1
412 412 $ hg up 2
413 413 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
414 414 $ echo c > c
415 415 $ hg ci -Am1
416 416 adding c
417 417 $ hg up 3
418 418 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
419 419 $ hg merge 4
420 420 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
421 421 (branch merge, don't forget to commit)
422 422 $ hg ci -m1
423 423
424 424 # main repo layout:
425 425 #
426 426 # * <-- try to merge default into br again
427 427 # .`|
428 428 # . o 5 br --> substate = 5
429 429 # . |
430 430 # o | 4 default --> substate = 4
431 431 # | |
432 432 # | o 3 br --> substate = 2
433 433 # |/|
434 434 # o | 2 default --> substate = 2
435 435 # | |
436 436 # | o 1 br --> substate = 3
437 437 # |/
438 438 # o 0 default --> substate = 2
439 439
440 440 $ cd ..
441 441 $ echo 's = s' > .hgsub
442 442 $ hg -R s up 2
443 443 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
444 444 $ hg ci -Am1
445 445 adding .hgsub
446 446 committing subrepository s
447 447 $ hg branch br
448 448 marked working directory as branch br
449 449 $ echo b > b
450 450 $ hg -R s up 3
451 451 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
452 452 $ hg ci -Am1
453 453 adding b
454 454 committing subrepository s
455 455 $ hg up default
456 456 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
457 457 $ echo c > c
458 458 $ hg ci -Am1
459 459 adding c
460 460 $ hg up 1
461 461 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
462 462 $ hg merge 2
463 463 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
464 464 (branch merge, don't forget to commit)
465 465 $ hg ci -m1
466 466 $ hg up 2
467 467 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
468 468 $ hg -R s up 4
469 469 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
470 470 $ echo d > d
471 471 $ hg ci -Am1
472 472 adding d
473 473 committing subrepository s
474 474 $ hg up 3
475 475 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
476 476 $ hg -R s up 5
477 477 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
478 478 $ echo e > e
479 479 $ hg ci -Am1
480 480 adding e
481 481 committing subrepository s
482 482
483 483 $ hg up 5
484 484 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
485 485 $ hg merge 4 # try to merge default into br again
486 486 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
487 487 (branch merge, don't forget to commit)
488 488 $ cd ..
489 489
490 490 test subrepo delete from .hgsubstate
491 491
492 492 $ hg init testdelete
493 493 $ mkdir testdelete/nested testdelete/nested2
494 494 $ hg init testdelete/nested
495 495 $ hg init testdelete/nested2
496 496 $ echo test > testdelete/nested/foo
497 497 $ echo test > testdelete/nested2/foo
498 498 $ hg -R testdelete/nested add
499 499 adding testdelete/nested/foo
500 500 $ hg -R testdelete/nested2 add
501 501 adding testdelete/nested2/foo
502 502 $ hg -R testdelete/nested ci -m test
503 503 $ hg -R testdelete/nested2 ci -m test
504 504 $ echo nested = nested > testdelete/.hgsub
505 505 $ echo nested2 = nested2 >> testdelete/.hgsub
506 506 $ hg -R testdelete add
507 507 adding testdelete/.hgsub
508 508 $ hg -R testdelete ci -m "nested 1 & 2 added"
509 509 committing subrepository nested
510 510 committing subrepository nested2
511 511 $ echo nested = nested > testdelete/.hgsub
512 512 $ hg -R testdelete ci -m "nested 2 deleted"
513 513 $ cat testdelete/.hgsubstate
514 514 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
515 515 $ hg -R testdelete remove testdelete/.hgsub
516 516 $ hg -R testdelete ci -m ".hgsub deleted"
517 517 $ cat testdelete/.hgsubstate
518 518
519 519 test repository cloning
520 520
521 521 $ mkdir mercurial mercurial2
522 522 $ hg init nested_absolute
523 523 $ echo test > nested_absolute/foo
524 524 $ hg -R nested_absolute add
525 525 adding nested_absolute/foo
526 526 $ hg -R nested_absolute ci -mtest
527 527 $ cd mercurial
528 528 $ hg init nested_relative
529 529 $ echo test2 > nested_relative/foo2
530 530 $ hg -R nested_relative add
531 531 adding nested_relative/foo2
532 532 $ hg -R nested_relative ci -mtest2
533 533 $ hg init main
534 534 $ echo "nested_relative = ../nested_relative" > main/.hgsub
535 535 $ echo "nested_absolute = `pwd`/nested_absolute" >> main/.hgsub
536 536 $ hg -R main add
537 537 adding main/.hgsub
538 538 $ hg -R main ci -m "add subrepos"
539 539 committing subrepository nested_absolute
540 540 committing subrepository nested_relative
541 541 $ cd ..
542 542 $ hg clone mercurial/main mercurial2/main
543 543 updating to branch default
544 544 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
545 545 $ cat mercurial2/main/nested_absolute/.hg/hgrc \
546 546 > mercurial2/main/nested_relative/.hg/hgrc
547 547 [paths]
548 548 default = $TESTTMP/sub/mercurial/nested_absolute
549 549 [paths]
550 550 default = $TESTTMP/sub/mercurial/nested_relative
551 551 $ rm -rf mercurial mercurial2
552 552
553 553 Issue1977: multirepo push should fail if subrepo push fails
554 554
555 555 $ hg init repo
556 556 $ hg init repo/s
557 557 $ echo a > repo/s/a
558 558 $ hg -R repo/s ci -Am0
559 559 adding a
560 560 $ echo s = s > repo/.hgsub
561 561 $ hg -R repo ci -Am1
562 562 adding .hgsub
563 563 committing subrepository s
564 564 $ hg clone repo repo2
565 565 updating to branch default
566 566 pulling subrepo s from $TESTTMP/sub/repo/s
567 567 requesting all changes
568 568 adding changesets
569 569 adding manifests
570 570 adding file changes
571 571 added 1 changesets with 1 changes to 1 files
572 572 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
573 573 $ hg -q -R repo2 pull -u
574 574 $ echo 1 > repo2/s/a
575 575 $ hg -R repo2/s ci -m2
576 576 $ hg -q -R repo2/s push
577 577 $ hg -R repo2/s up -C 0
578 578 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
579 579 $ echo 2 > repo2/s/a
580 580 $ hg -R repo2/s ci -m3
581 581 created new head
582 582 $ hg -R repo2 ci -m3
583 583 committing subrepository s
584 584 $ hg -q -R repo2 push
585 585 abort: push creates new remote heads on branch 'default'!
586 586 (did you forget to merge? use push -f to force)
587 587 [255]
588 588 $ hg -R repo update
589 589 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
590 590 $ rm -rf repo2 repo
591 591
592 592
593 593 Issue1852 subrepos with relative paths always push/pull relative to default
594 594
595 595 Prepare a repo with subrepo
596 596
597 597 $ hg init issue1852a
598 598 $ cd issue1852a
599 599 $ hg init sub/repo
600 600 $ echo test > sub/repo/foo
601 601 $ hg -R sub/repo add sub/repo/foo
602 602 $ echo sub/repo = sub/repo > .hgsub
603 603 $ hg add .hgsub
604 604 $ hg ci -mtest
605 605 committing subrepository sub/repo
606 606 $ echo test >> sub/repo/foo
607 607 $ hg ci -mtest
608 608 committing subrepository sub/repo
609 609 $ cd ..
610 610
611 611 Create repo without default path, pull top repo, and see what happens on update
612 612
613 613 $ hg init issue1852b
614 614 $ hg -R issue1852b pull issue1852a
615 615 pulling from issue1852a
616 616 requesting all changes
617 617 adding changesets
618 618 adding manifests
619 619 adding file changes
620 620 added 2 changesets with 3 changes to 2 files
621 621 (run 'hg update' to get a working copy)
622 622 $ hg -R issue1852b update
623 623 abort: default path for subrepository sub/repo not found
624 624 [255]
625 625
626 626 Pull -u now doesn't help
627 627
628 628 $ hg -R issue1852b pull -u issue1852a
629 629 pulling from issue1852a
630 630 searching for changes
631 631 no changes found
632 632
633 633 Try the same, but with pull -u
634 634
635 635 $ hg init issue1852c
636 636 $ hg -R issue1852c pull -r0 -u issue1852a
637 637 pulling from issue1852a
638 638 adding changesets
639 639 adding manifests
640 640 adding file changes
641 641 added 1 changesets with 2 changes to 2 files
642 642 pulling subrepo sub/repo from issue1852a/sub/repo
643 643 requesting all changes
644 644 adding changesets
645 645 adding manifests
646 646 adding file changes
647 647 added 2 changesets with 2 changes to 1 files
648 648 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
649 649
650 650 Try to push from the other side
651 651
652 652 $ hg -R issue1852a push `pwd`/issue1852c
653 653 pushing to $TESTTMP/sub/issue1852c
654 654 pushing subrepo sub/repo to $TESTTMP/sub/issue1852c/sub/repo
655 655 searching for changes
656 656 no changes found
657 657 searching for changes
658 658 adding changesets
659 659 adding manifests
660 660 adding file changes
661 661 added 1 changesets with 1 changes to 1 files
662 662
663 663 Check status of files when none of them belong to the first
664 664 subrepository:
665 665
666 666 $ hg init subrepo-status
667 667 $ cd subrepo-status
668 668 $ hg init subrepo-1
669 669 $ hg init subrepo-2
670 670 $ cd subrepo-2
671 671 $ touch file
672 672 $ hg add file
673 673 $ cd ..
674 674 $ echo subrepo-1 = subrepo-1 > .hgsub
675 675 $ echo subrepo-2 = subrepo-2 >> .hgsub
676 676 $ hg add .hgsub
677 677 $ hg ci -m 'Added subrepos'
678 678 committing subrepository subrepo-1
679 679 committing subrepository subrepo-2
680 680 $ hg st subrepo-2/file
681 681
682 682 Check hg update --clean
683 683 $ cd $TESTTMP/sub/t
684 684 $ rm -r t/t.orig
685 685 $ hg status -S --all
686 686 C .hgsub
687 687 C .hgsubstate
688 688 C a
689 689 C s/.hgsub
690 690 C s/.hgsubstate
691 691 C s/a
692 692 C s/ss/a
693 693 C t/t
694 694 $ echo c1 > s/a
695 695 $ cd s
696 696 $ echo c1 > b
697 697 $ echo c1 > c
698 698 $ hg add b
699 699 $ cd ..
700 700 $ hg status -S
701 701 M s/a
702 702 A s/b
703 703 ? s/c
704 704 $ hg update -C
705 705 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
706 706 $ hg status -S
707 707 ? s/b
708 708 ? s/c
709
710 Sticky subrepositories, no changes
711 $ cd $TESTTMP/sub/t
712 $ hg id
713 925c17564ef8 tip
714 $ hg -R s id
715 12a213df6fa9 tip
716 $ hg -R t id
717 52c0adc0515a tip
718 $ hg update 11
719 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
720 $ hg id
721 365661e5936a
722 $ hg -R s id
723 fc627a69481f
724 $ hg -R t id
725 e95bcfa18a35
726
727 Sticky subrepositorys, file changes
728 $ touch s/f1
729 $ touch t/f1
730 $ hg add -S s/f1
731 $ hg add -S t/f1
732 $ hg id
733 365661e5936a
734 $ hg -R s id
735 fc627a69481f+
736 $ hg -R t id
737 e95bcfa18a35+
738 $ hg update tip
739 subrepository sources for s differ
740 use (l)ocal source (fc627a69481f) or (r)emote source (12a213df6fa9)?
741 l
742 subrepository sources for t differ
743 use (l)ocal source (e95bcfa18a35) or (r)emote source (52c0adc0515a)?
744 l
745 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
746 $ hg id
747 925c17564ef8+ tip
748 $ hg -R s id
749 fc627a69481f+
750 $ hg -R t id
751 e95bcfa18a35+
752 $ hg update --clean tip
753 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
754
755 Sticky subrepository, revision updates
756 $ hg id
757 925c17564ef8 tip
758 $ hg -R s id
759 12a213df6fa9 tip
760 $ hg -R t id
761 52c0adc0515a tip
762 $ cd s
763 $ hg update -r -2
764 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
765 $ cd ../t
766 $ hg update -r 2
767 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
768 $ cd ..
769 $ hg update 10
770 subrepository sources for t differ (in checked out version)
771 use (l)ocal source (7af322bc1198) or (r)emote source (20a0db6fbf6c)?
772 l
773 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
774 $ hg id
775 e45c8b14af55+
776 $ hg -R s id
777 1c833a7a9e3a
778 $ hg -R t id
779 7af322bc1198
780
781 Sticky subrepository, file changes and revision updates
782 $ touch s/f1
783 $ touch t/f1
784 $ hg add -S s/f1
785 $ hg add -S t/f1
786 $ hg id
787 e45c8b14af55+
788 $ hg -R s id
789 1c833a7a9e3a+
790 $ hg -R t id
791 7af322bc1198+
792 $ hg update tip
793 subrepository sources for s differ
794 use (l)ocal source (1c833a7a9e3a) or (r)emote source (12a213df6fa9)?
795 l
796 subrepository sources for t differ
797 use (l)ocal source (7af322bc1198) or (r)emote source (52c0adc0515a)?
798 l
799 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
800 $ hg id
801 925c17564ef8 tip
802 $ hg -R s id
803 1c833a7a9e3a+
804 $ hg -R t id
805 7af322bc1198+
806
807 Sticky repository, update --clean
808 $ hg update --clean tip
809 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
810 $ hg id
811 925c17564ef8 tip
812 $ hg -R s id
813 12a213df6fa9 tip
814 $ hg -R t id
815 52c0adc0515a tip
816
817 Test subrepo already at intended revision:
818 $ cd s
819 $ hg update fc627a69481f
820 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
821 $ cd ..
822 $ hg update 11
823 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
824 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
825 $ hg id -n
826 11+
827 $ hg -R s id
828 fc627a69481f
829 $ hg -R t id
830 e95bcfa18a35
General Comments 0
You need to be logged in to leave comments. Login now