##// END OF EJS Templates
subrepos: make last line of prompts <40 english chars (issue6158)...
Kyle Lippincott -
r42791:f6540aba default
parent child Browse files
Show More
@@ -1,1838 +1,1840 b''
1 1 # subrepo.py - sub-repository classes and factory
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 from __future__ import absolute_import
9 9
10 10 import copy
11 11 import errno
12 12 import hashlib
13 13 import os
14 14 import re
15 15 import stat
16 16 import subprocess
17 17 import sys
18 18 import tarfile
19 19 import xml.dom.minidom
20 20
21 21 from .i18n import _
22 22 from . import (
23 23 cmdutil,
24 24 encoding,
25 25 error,
26 26 exchange,
27 27 logcmdutil,
28 28 match as matchmod,
29 29 node,
30 30 pathutil,
31 31 phases,
32 32 pycompat,
33 33 scmutil,
34 34 subrepoutil,
35 35 util,
36 36 vfs as vfsmod,
37 37 )
38 38 from .utils import (
39 39 dateutil,
40 40 procutil,
41 41 stringutil,
42 42 )
43 43
44 44 hg = None
45 45 reporelpath = subrepoutil.reporelpath
46 46 subrelpath = subrepoutil.subrelpath
47 47 _abssource = subrepoutil._abssource
48 48 propertycache = util.propertycache
49 49
50 50 def _expandedabspath(path):
51 51 '''
52 52 get a path or url and if it is a path expand it and return an absolute path
53 53 '''
54 54 expandedpath = util.urllocalpath(util.expandpath(path))
55 55 u = util.url(expandedpath)
56 56 if not u.scheme:
57 57 path = util.normpath(os.path.abspath(u.path))
58 58 return path
59 59
60 60 def _getstorehashcachename(remotepath):
61 61 '''get a unique filename for the store hash cache of a remote repository'''
62 62 return node.hex(hashlib.sha1(_expandedabspath(remotepath)).digest())[0:12]
63 63
64 64 class SubrepoAbort(error.Abort):
65 65 """Exception class used to avoid handling a subrepo error more than once"""
66 66 def __init__(self, *args, **kw):
67 67 self.subrepo = kw.pop(r'subrepo', None)
68 68 self.cause = kw.pop(r'cause', None)
69 69 error.Abort.__init__(self, *args, **kw)
70 70
71 71 def annotatesubrepoerror(func):
72 72 def decoratedmethod(self, *args, **kargs):
73 73 try:
74 74 res = func(self, *args, **kargs)
75 75 except SubrepoAbort as ex:
76 76 # This exception has already been handled
77 77 raise ex
78 78 except error.Abort as ex:
79 79 subrepo = subrelpath(self)
80 80 errormsg = (stringutil.forcebytestr(ex) + ' '
81 81 + _('(in subrepository "%s")') % subrepo)
82 82 # avoid handling this exception by raising a SubrepoAbort exception
83 83 raise SubrepoAbort(errormsg, hint=ex.hint, subrepo=subrepo,
84 84 cause=sys.exc_info())
85 85 return res
86 86 return decoratedmethod
87 87
88 88 def _updateprompt(ui, sub, dirty, local, remote):
89 89 if dirty:
90 90 msg = (_(' subrepository sources for %s differ\n'
91 'use (l)ocal source (%s) or (r)emote source (%s)?'
91 'you can use (l)ocal source (%s) or (r)emote source (%s).\n'
92 'what do you want to do?'
92 93 '$$ &Local $$ &Remote')
93 94 % (subrelpath(sub), local, remote))
94 95 else:
95 96 msg = (_(' subrepository sources for %s differ (in checked out '
96 97 'version)\n'
97 'use (l)ocal source (%s) or (r)emote source (%s)?'
98 'you can use (l)ocal source (%s) or (r)emote source (%s).\n'
99 'what do you want to do?'
98 100 '$$ &Local $$ &Remote')
99 101 % (subrelpath(sub), local, remote))
100 102 return ui.promptchoice(msg, 0)
101 103
102 104 def _sanitize(ui, vfs, ignore):
103 105 for dirname, dirs, names in vfs.walk():
104 106 for i, d in enumerate(dirs):
105 107 if d.lower() == ignore:
106 108 del dirs[i]
107 109 break
108 110 if vfs.basename(dirname).lower() != '.hg':
109 111 continue
110 112 for f in names:
111 113 if f.lower() == 'hgrc':
112 114 ui.warn(_("warning: removing potentially hostile 'hgrc' "
113 115 "in '%s'\n") % vfs.join(dirname))
114 116 vfs.unlink(vfs.reljoin(dirname, f))
115 117
116 118 def _auditsubrepopath(repo, path):
117 119 # sanity check for potentially unsafe paths such as '~' and '$FOO'
118 120 if path.startswith('~') or '$' in path or util.expandpath(path) != path:
119 121 raise error.Abort(_('subrepo path contains illegal component: %s')
120 122 % path)
121 123 # auditor doesn't check if the path itself is a symlink
122 124 pathutil.pathauditor(repo.root)(path)
123 125 if repo.wvfs.islink(path):
124 126 raise error.Abort(_("subrepo '%s' traverses symbolic link") % path)
125 127
126 128 SUBREPO_ALLOWED_DEFAULTS = {
127 129 'hg': True,
128 130 'git': False,
129 131 'svn': False,
130 132 }
131 133
132 134 def _checktype(ui, kind):
133 135 # subrepos.allowed is a master kill switch. If disabled, subrepos are
134 136 # disabled period.
135 137 if not ui.configbool('subrepos', 'allowed', True):
136 138 raise error.Abort(_('subrepos not enabled'),
137 139 hint=_("see 'hg help config.subrepos' for details"))
138 140
139 141 default = SUBREPO_ALLOWED_DEFAULTS.get(kind, False)
140 142 if not ui.configbool('subrepos', '%s:allowed' % kind, default):
141 143 raise error.Abort(_('%s subrepos not allowed') % kind,
142 144 hint=_("see 'hg help config.subrepos' for details"))
143 145
144 146 if kind not in types:
145 147 raise error.Abort(_('unknown subrepo type %s') % kind)
146 148
147 149 def subrepo(ctx, path, allowwdir=False, allowcreate=True):
148 150 """return instance of the right subrepo class for subrepo in path"""
149 151 # subrepo inherently violates our import layering rules
150 152 # because it wants to make repo objects from deep inside the stack
151 153 # so we manually delay the circular imports to not break
152 154 # scripts that don't use our demand-loading
153 155 global hg
154 156 from . import hg as h
155 157 hg = h
156 158
157 159 repo = ctx.repo()
158 160 _auditsubrepopath(repo, path)
159 161 state = ctx.substate[path]
160 162 _checktype(repo.ui, state[2])
161 163 if allowwdir:
162 164 state = (state[0], ctx.subrev(path), state[2])
163 165 return types[state[2]](ctx, path, state[:2], allowcreate)
164 166
165 167 def nullsubrepo(ctx, path, pctx):
166 168 """return an empty subrepo in pctx for the extant subrepo in ctx"""
167 169 # subrepo inherently violates our import layering rules
168 170 # because it wants to make repo objects from deep inside the stack
169 171 # so we manually delay the circular imports to not break
170 172 # scripts that don't use our demand-loading
171 173 global hg
172 174 from . import hg as h
173 175 hg = h
174 176
175 177 repo = ctx.repo()
176 178 _auditsubrepopath(repo, path)
177 179 state = ctx.substate[path]
178 180 _checktype(repo.ui, state[2])
179 181 subrev = ''
180 182 if state[2] == 'hg':
181 183 subrev = "0" * 40
182 184 return types[state[2]](pctx, path, (state[0], subrev), True)
183 185
184 186 # subrepo classes need to implement the following abstract class:
185 187
186 188 class abstractsubrepo(object):
187 189
188 190 def __init__(self, ctx, path):
189 191 """Initialize abstractsubrepo part
190 192
191 193 ``ctx`` is the context referring this subrepository in the
192 194 parent repository.
193 195
194 196 ``path`` is the path to this subrepository as seen from
195 197 innermost repository.
196 198 """
197 199 self.ui = ctx.repo().ui
198 200 self._ctx = ctx
199 201 self._path = path
200 202
201 203 def addwebdirpath(self, serverpath, webconf):
202 204 """Add the hgwebdir entries for this subrepo, and any of its subrepos.
203 205
204 206 ``serverpath`` is the path component of the URL for this repo.
205 207
206 208 ``webconf`` is the dictionary of hgwebdir entries.
207 209 """
208 210 pass
209 211
210 212 def storeclean(self, path):
211 213 """
212 214 returns true if the repository has not changed since it was last
213 215 cloned from or pushed to a given repository.
214 216 """
215 217 return False
216 218
217 219 def dirty(self, ignoreupdate=False, missing=False):
218 220 """returns true if the dirstate of the subrepo is dirty or does not
219 221 match current stored state. If ignoreupdate is true, only check
220 222 whether the subrepo has uncommitted changes in its dirstate. If missing
221 223 is true, check for deleted files.
222 224 """
223 225 raise NotImplementedError
224 226
225 227 def dirtyreason(self, ignoreupdate=False, missing=False):
226 228 """return reason string if it is ``dirty()``
227 229
228 230 Returned string should have enough information for the message
229 231 of exception.
230 232
231 233 This returns None, otherwise.
232 234 """
233 235 if self.dirty(ignoreupdate=ignoreupdate, missing=missing):
234 236 return _('uncommitted changes in subrepository "%s"'
235 237 ) % subrelpath(self)
236 238
237 239 def bailifchanged(self, ignoreupdate=False, hint=None):
238 240 """raise Abort if subrepository is ``dirty()``
239 241 """
240 242 dirtyreason = self.dirtyreason(ignoreupdate=ignoreupdate,
241 243 missing=True)
242 244 if dirtyreason:
243 245 raise error.Abort(dirtyreason, hint=hint)
244 246
245 247 def basestate(self):
246 248 """current working directory base state, disregarding .hgsubstate
247 249 state and working directory modifications"""
248 250 raise NotImplementedError
249 251
250 252 def checknested(self, path):
251 253 """check if path is a subrepository within this repository"""
252 254 return False
253 255
254 256 def commit(self, text, user, date):
255 257 """commit the current changes to the subrepo with the given
256 258 log message. Use given user and date if possible. Return the
257 259 new state of the subrepo.
258 260 """
259 261 raise NotImplementedError
260 262
261 263 def phase(self, state):
262 264 """returns phase of specified state in the subrepository.
263 265 """
264 266 return phases.public
265 267
266 268 def remove(self):
267 269 """remove the subrepo
268 270
269 271 (should verify the dirstate is not dirty first)
270 272 """
271 273 raise NotImplementedError
272 274
273 275 def get(self, state, overwrite=False):
274 276 """run whatever commands are needed to put the subrepo into
275 277 this state
276 278 """
277 279 raise NotImplementedError
278 280
279 281 def merge(self, state):
280 282 """merge currently-saved state with the new state."""
281 283 raise NotImplementedError
282 284
283 285 def push(self, opts):
284 286 """perform whatever action is analogous to 'hg push'
285 287
286 288 This may be a no-op on some systems.
287 289 """
288 290 raise NotImplementedError
289 291
290 292 def add(self, ui, match, prefix, uipathfn, explicitonly, **opts):
291 293 return []
292 294
293 295 def addremove(self, matcher, prefix, uipathfn, opts):
294 296 self.ui.warn("%s: %s" % (prefix, _("addremove is not supported")))
295 297 return 1
296 298
297 299 def cat(self, match, fm, fntemplate, prefix, **opts):
298 300 return 1
299 301
300 302 def status(self, rev2, **opts):
301 303 return scmutil.status([], [], [], [], [], [], [])
302 304
303 305 def diff(self, ui, diffopts, node2, match, prefix, **opts):
304 306 pass
305 307
306 308 def outgoing(self, ui, dest, opts):
307 309 return 1
308 310
309 311 def incoming(self, ui, source, opts):
310 312 return 1
311 313
312 314 def files(self):
313 315 """return filename iterator"""
314 316 raise NotImplementedError
315 317
316 318 def filedata(self, name, decode):
317 319 """return file data, optionally passed through repo decoders"""
318 320 raise NotImplementedError
319 321
320 322 def fileflags(self, name):
321 323 """return file flags"""
322 324 return ''
323 325
324 326 def matchfileset(self, expr, badfn=None):
325 327 """Resolve the fileset expression for this repo"""
326 328 return matchmod.never(badfn=badfn)
327 329
328 330 def printfiles(self, ui, m, uipathfn, fm, fmt, subrepos):
329 331 """handle the files command for this subrepo"""
330 332 return 1
331 333
332 334 def archive(self, archiver, prefix, match=None, decode=True):
333 335 if match is not None:
334 336 files = [f for f in self.files() if match(f)]
335 337 else:
336 338 files = self.files()
337 339 total = len(files)
338 340 relpath = subrelpath(self)
339 341 progress = self.ui.makeprogress(_('archiving (%s)') % relpath,
340 342 unit=_('files'), total=total)
341 343 progress.update(0)
342 344 for name in files:
343 345 flags = self.fileflags(name)
344 346 mode = 'x' in flags and 0o755 or 0o644
345 347 symlink = 'l' in flags
346 348 archiver.addfile(prefix + name, mode, symlink,
347 349 self.filedata(name, decode))
348 350 progress.increment()
349 351 progress.complete()
350 352 return total
351 353
352 354 def walk(self, match):
353 355 '''
354 356 walk recursively through the directory tree, finding all files
355 357 matched by the match function
356 358 '''
357 359
358 360 def forget(self, match, prefix, uipathfn, dryrun, interactive):
359 361 return ([], [])
360 362
361 363 def removefiles(self, matcher, prefix, uipathfn, after, force, subrepos,
362 364 dryrun, warnings):
363 365 """remove the matched files from the subrepository and the filesystem,
364 366 possibly by force and/or after the file has been removed from the
365 367 filesystem. Return 0 on success, 1 on any warning.
366 368 """
367 369 warnings.append(_("warning: removefiles not implemented (%s)")
368 370 % self._path)
369 371 return 1
370 372
371 373 def revert(self, substate, *pats, **opts):
372 374 self.ui.warn(_('%s: reverting %s subrepos is unsupported\n')
373 375 % (substate[0], substate[2]))
374 376 return []
375 377
376 378 def shortid(self, revid):
377 379 return revid
378 380
379 381 def unshare(self):
380 382 '''
381 383 convert this repository from shared to normal storage.
382 384 '''
383 385
384 386 def verify(self):
385 387 '''verify the integrity of the repository. Return 0 on success or
386 388 warning, 1 on any error.
387 389 '''
388 390 return 0
389 391
390 392 @propertycache
391 393 def wvfs(self):
392 394 """return vfs to access the working directory of this subrepository
393 395 """
394 396 return vfsmod.vfs(self._ctx.repo().wvfs.join(self._path))
395 397
396 398 @propertycache
397 399 def _relpath(self):
398 400 """return path to this subrepository as seen from outermost repository
399 401 """
400 402 return self.wvfs.reljoin(reporelpath(self._ctx.repo()), self._path)
401 403
402 404 class hgsubrepo(abstractsubrepo):
403 405 def __init__(self, ctx, path, state, allowcreate):
404 406 super(hgsubrepo, self).__init__(ctx, path)
405 407 self._state = state
406 408 r = ctx.repo()
407 409 root = r.wjoin(util.localpath(path))
408 410 create = allowcreate and not r.wvfs.exists('%s/.hg' % path)
409 411 # repository constructor does expand variables in path, which is
410 412 # unsafe since subrepo path might come from untrusted source.
411 413 if os.path.realpath(util.expandpath(root)) != root:
412 414 raise error.Abort(_('subrepo path contains illegal component: %s')
413 415 % path)
414 416 self._repo = hg.repository(r.baseui, root, create=create)
415 417 if self._repo.root != root:
416 418 raise error.ProgrammingError('failed to reject unsafe subrepo '
417 419 'path: %s (expanded to %s)'
418 420 % (root, self._repo.root))
419 421
420 422 # Propagate the parent's --hidden option
421 423 if r is r.unfiltered():
422 424 self._repo = self._repo.unfiltered()
423 425
424 426 self.ui = self._repo.ui
425 427 for s, k in [('ui', 'commitsubrepos')]:
426 428 v = r.ui.config(s, k)
427 429 if v:
428 430 self.ui.setconfig(s, k, v, 'subrepo')
429 431 # internal config: ui._usedassubrepo
430 432 self.ui.setconfig('ui', '_usedassubrepo', 'True', 'subrepo')
431 433 self._initrepo(r, state[0], create)
432 434
433 435 @annotatesubrepoerror
434 436 def addwebdirpath(self, serverpath, webconf):
435 437 cmdutil.addwebdirpath(self._repo, subrelpath(self), webconf)
436 438
437 439 def storeclean(self, path):
438 440 with self._repo.lock():
439 441 return self._storeclean(path)
440 442
441 443 def _storeclean(self, path):
442 444 clean = True
443 445 itercache = self._calcstorehash(path)
444 446 for filehash in self._readstorehashcache(path):
445 447 if filehash != next(itercache, None):
446 448 clean = False
447 449 break
448 450 if clean:
449 451 # if not empty:
450 452 # the cached and current pull states have a different size
451 453 clean = next(itercache, None) is None
452 454 return clean
453 455
454 456 def _calcstorehash(self, remotepath):
455 457 '''calculate a unique "store hash"
456 458
457 459 This method is used to to detect when there are changes that may
458 460 require a push to a given remote path.'''
459 461 # sort the files that will be hashed in increasing (likely) file size
460 462 filelist = ('bookmarks', 'store/phaseroots', 'store/00changelog.i')
461 463 yield '# %s\n' % _expandedabspath(remotepath)
462 464 vfs = self._repo.vfs
463 465 for relname in filelist:
464 466 filehash = node.hex(hashlib.sha1(vfs.tryread(relname)).digest())
465 467 yield '%s = %s\n' % (relname, filehash)
466 468
467 469 @propertycache
468 470 def _cachestorehashvfs(self):
469 471 return vfsmod.vfs(self._repo.vfs.join('cache/storehash'))
470 472
471 473 def _readstorehashcache(self, remotepath):
472 474 '''read the store hash cache for a given remote repository'''
473 475 cachefile = _getstorehashcachename(remotepath)
474 476 return self._cachestorehashvfs.tryreadlines(cachefile, 'r')
475 477
476 478 def _cachestorehash(self, remotepath):
477 479 '''cache the current store hash
478 480
479 481 Each remote repo requires its own store hash cache, because a subrepo
480 482 store may be "clean" versus a given remote repo, but not versus another
481 483 '''
482 484 cachefile = _getstorehashcachename(remotepath)
483 485 with self._repo.lock():
484 486 storehash = list(self._calcstorehash(remotepath))
485 487 vfs = self._cachestorehashvfs
486 488 vfs.writelines(cachefile, storehash, mode='wb', notindexed=True)
487 489
488 490 def _getctx(self):
489 491 '''fetch the context for this subrepo revision, possibly a workingctx
490 492 '''
491 493 if self._ctx.rev() is None:
492 494 return self._repo[None] # workingctx if parent is workingctx
493 495 else:
494 496 rev = self._state[1]
495 497 return self._repo[rev]
496 498
497 499 @annotatesubrepoerror
498 500 def _initrepo(self, parentrepo, source, create):
499 501 self._repo._subparent = parentrepo
500 502 self._repo._subsource = source
501 503
502 504 if create:
503 505 lines = ['[paths]\n']
504 506
505 507 def addpathconfig(key, value):
506 508 if value:
507 509 lines.append('%s = %s\n' % (key, value))
508 510 self.ui.setconfig('paths', key, value, 'subrepo')
509 511
510 512 defpath = _abssource(self._repo, abort=False)
511 513 defpushpath = _abssource(self._repo, True, abort=False)
512 514 addpathconfig('default', defpath)
513 515 if defpath != defpushpath:
514 516 addpathconfig('default-push', defpushpath)
515 517
516 518 self._repo.vfs.write('hgrc', util.tonativeeol(''.join(lines)))
517 519
518 520 @annotatesubrepoerror
519 521 def add(self, ui, match, prefix, uipathfn, explicitonly, **opts):
520 522 return cmdutil.add(ui, self._repo, match, prefix, uipathfn,
521 523 explicitonly, **opts)
522 524
523 525 @annotatesubrepoerror
524 526 def addremove(self, m, prefix, uipathfn, opts):
525 527 # In the same way as sub directories are processed, once in a subrepo,
526 528 # always entry any of its subrepos. Don't corrupt the options that will
527 529 # be used to process sibling subrepos however.
528 530 opts = copy.copy(opts)
529 531 opts['subrepos'] = True
530 532 return scmutil.addremove(self._repo, m, prefix, uipathfn, opts)
531 533
532 534 @annotatesubrepoerror
533 535 def cat(self, match, fm, fntemplate, prefix, **opts):
534 536 rev = self._state[1]
535 537 ctx = self._repo[rev]
536 538 return cmdutil.cat(self.ui, self._repo, ctx, match, fm, fntemplate,
537 539 prefix, **opts)
538 540
539 541 @annotatesubrepoerror
540 542 def status(self, rev2, **opts):
541 543 try:
542 544 rev1 = self._state[1]
543 545 ctx1 = self._repo[rev1]
544 546 ctx2 = self._repo[rev2]
545 547 return self._repo.status(ctx1, ctx2, **opts)
546 548 except error.RepoLookupError as inst:
547 549 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
548 550 % (inst, subrelpath(self)))
549 551 return scmutil.status([], [], [], [], [], [], [])
550 552
551 553 @annotatesubrepoerror
552 554 def diff(self, ui, diffopts, node2, match, prefix, **opts):
553 555 try:
554 556 node1 = node.bin(self._state[1])
555 557 # We currently expect node2 to come from substate and be
556 558 # in hex format
557 559 if node2 is not None:
558 560 node2 = node.bin(node2)
559 561 logcmdutil.diffordiffstat(ui, self._repo, diffopts, node1, node2,
560 562 match, prefix=prefix, listsubrepos=True,
561 563 **opts)
562 564 except error.RepoLookupError as inst:
563 565 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
564 566 % (inst, subrelpath(self)))
565 567
566 568 @annotatesubrepoerror
567 569 def archive(self, archiver, prefix, match=None, decode=True):
568 570 self._get(self._state + ('hg',))
569 571 files = self.files()
570 572 if match:
571 573 files = [f for f in files if match(f)]
572 574 rev = self._state[1]
573 575 ctx = self._repo[rev]
574 576 scmutil.prefetchfiles(self._repo, [ctx.rev()],
575 577 scmutil.matchfiles(self._repo, files))
576 578 total = abstractsubrepo.archive(self, archiver, prefix, match)
577 579 for subpath in ctx.substate:
578 580 s = subrepo(ctx, subpath, True)
579 581 submatch = matchmod.subdirmatcher(subpath, match)
580 582 subprefix = prefix + subpath + '/'
581 583 total += s.archive(archiver, subprefix, submatch,
582 584 decode)
583 585 return total
584 586
585 587 @annotatesubrepoerror
586 588 def dirty(self, ignoreupdate=False, missing=False):
587 589 r = self._state[1]
588 590 if r == '' and not ignoreupdate: # no state recorded
589 591 return True
590 592 w = self._repo[None]
591 593 if r != w.p1().hex() and not ignoreupdate:
592 594 # different version checked out
593 595 return True
594 596 return w.dirty(missing=missing) # working directory changed
595 597
596 598 def basestate(self):
597 599 return self._repo['.'].hex()
598 600
599 601 def checknested(self, path):
600 602 return self._repo._checknested(self._repo.wjoin(path))
601 603
602 604 @annotatesubrepoerror
603 605 def commit(self, text, user, date):
604 606 # don't bother committing in the subrepo if it's only been
605 607 # updated
606 608 if not self.dirty(True):
607 609 return self._repo['.'].hex()
608 610 self.ui.debug("committing subrepo %s\n" % subrelpath(self))
609 611 n = self._repo.commit(text, user, date)
610 612 if not n:
611 613 return self._repo['.'].hex() # different version checked out
612 614 return node.hex(n)
613 615
614 616 @annotatesubrepoerror
615 617 def phase(self, state):
616 618 return self._repo[state or '.'].phase()
617 619
618 620 @annotatesubrepoerror
619 621 def remove(self):
620 622 # we can't fully delete the repository as it may contain
621 623 # local-only history
622 624 self.ui.note(_('removing subrepo %s\n') % subrelpath(self))
623 625 hg.clean(self._repo, node.nullid, False)
624 626
625 627 def _get(self, state):
626 628 source, revision, kind = state
627 629 parentrepo = self._repo._subparent
628 630
629 631 if revision in self._repo.unfiltered():
630 632 # Allow shared subrepos tracked at null to setup the sharedpath
631 633 if len(self._repo) != 0 or not parentrepo.shared():
632 634 return True
633 635 self._repo._subsource = source
634 636 srcurl = _abssource(self._repo)
635 637
636 638 # Defer creating the peer until after the status message is logged, in
637 639 # case there are network problems.
638 640 getpeer = lambda: hg.peer(self._repo, {}, srcurl)
639 641
640 642 if len(self._repo) == 0:
641 643 # use self._repo.vfs instead of self.wvfs to remove .hg only
642 644 self._repo.vfs.rmtree()
643 645
644 646 # A remote subrepo could be shared if there is a local copy
645 647 # relative to the parent's share source. But clone pooling doesn't
646 648 # assemble the repos in a tree, so that can't be consistently done.
647 649 # A simpler option is for the user to configure clone pooling, and
648 650 # work with that.
649 651 if parentrepo.shared() and hg.islocal(srcurl):
650 652 self.ui.status(_('sharing subrepo %s from %s\n')
651 653 % (subrelpath(self), srcurl))
652 654 shared = hg.share(self._repo._subparent.baseui,
653 655 getpeer(), self._repo.root,
654 656 update=False, bookmarks=False)
655 657 self._repo = shared.local()
656 658 else:
657 659 # TODO: find a common place for this and this code in the
658 660 # share.py wrap of the clone command.
659 661 if parentrepo.shared():
660 662 pool = self.ui.config('share', 'pool')
661 663 if pool:
662 664 pool = util.expandpath(pool)
663 665
664 666 shareopts = {
665 667 'pool': pool,
666 668 'mode': self.ui.config('share', 'poolnaming'),
667 669 }
668 670 else:
669 671 shareopts = {}
670 672
671 673 self.ui.status(_('cloning subrepo %s from %s\n')
672 674 % (subrelpath(self), util.hidepassword(srcurl)))
673 675 other, cloned = hg.clone(self._repo._subparent.baseui, {},
674 676 getpeer(), self._repo.root,
675 677 update=False, shareopts=shareopts)
676 678 self._repo = cloned.local()
677 679 self._initrepo(parentrepo, source, create=True)
678 680 self._cachestorehash(srcurl)
679 681 else:
680 682 self.ui.status(_('pulling subrepo %s from %s\n')
681 683 % (subrelpath(self), util.hidepassword(srcurl)))
682 684 cleansub = self.storeclean(srcurl)
683 685 exchange.pull(self._repo, getpeer())
684 686 if cleansub:
685 687 # keep the repo clean after pull
686 688 self._cachestorehash(srcurl)
687 689 return False
688 690
689 691 @annotatesubrepoerror
690 692 def get(self, state, overwrite=False):
691 693 inrepo = self._get(state)
692 694 source, revision, kind = state
693 695 repo = self._repo
694 696 repo.ui.debug("getting subrepo %s\n" % self._path)
695 697 if inrepo:
696 698 urepo = repo.unfiltered()
697 699 ctx = urepo[revision]
698 700 if ctx.hidden():
699 701 urepo.ui.warn(
700 702 _('revision %s in subrepository "%s" is hidden\n')
701 703 % (revision[0:12], self._path))
702 704 repo = urepo
703 705 hg.updaterepo(repo, revision, overwrite)
704 706
705 707 @annotatesubrepoerror
706 708 def merge(self, state):
707 709 self._get(state)
708 710 cur = self._repo['.']
709 711 dst = self._repo[state[1]]
710 712 anc = dst.ancestor(cur)
711 713
712 714 def mergefunc():
713 715 if anc == cur and dst.branch() == cur.branch():
714 716 self.ui.debug('updating subrepository "%s"\n'
715 717 % subrelpath(self))
716 718 hg.update(self._repo, state[1])
717 719 elif anc == dst:
718 720 self.ui.debug('skipping subrepository "%s"\n'
719 721 % subrelpath(self))
720 722 else:
721 723 self.ui.debug('merging subrepository "%s"\n' % subrelpath(self))
722 724 hg.merge(self._repo, state[1], remind=False)
723 725
724 726 wctx = self._repo[None]
725 727 if self.dirty():
726 728 if anc != dst:
727 729 if _updateprompt(self.ui, self, wctx.dirty(), cur, dst):
728 730 mergefunc()
729 731 else:
730 732 mergefunc()
731 733 else:
732 734 mergefunc()
733 735
734 736 @annotatesubrepoerror
735 737 def push(self, opts):
736 738 force = opts.get('force')
737 739 newbranch = opts.get('new_branch')
738 740 ssh = opts.get('ssh')
739 741
740 742 # push subrepos depth-first for coherent ordering
741 743 c = self._repo['.']
742 744 subs = c.substate # only repos that are committed
743 745 for s in sorted(subs):
744 746 if c.sub(s).push(opts) == 0:
745 747 return False
746 748
747 749 dsturl = _abssource(self._repo, True)
748 750 if not force:
749 751 if self.storeclean(dsturl):
750 752 self.ui.status(
751 753 _('no changes made to subrepo %s since last push to %s\n')
752 754 % (subrelpath(self), util.hidepassword(dsturl)))
753 755 return None
754 756 self.ui.status(_('pushing subrepo %s to %s\n') %
755 757 (subrelpath(self), util.hidepassword(dsturl)))
756 758 other = hg.peer(self._repo, {'ssh': ssh}, dsturl)
757 759 res = exchange.push(self._repo, other, force, newbranch=newbranch)
758 760
759 761 # the repo is now clean
760 762 self._cachestorehash(dsturl)
761 763 return res.cgresult
762 764
763 765 @annotatesubrepoerror
764 766 def outgoing(self, ui, dest, opts):
765 767 if 'rev' in opts or 'branch' in opts:
766 768 opts = copy.copy(opts)
767 769 opts.pop('rev', None)
768 770 opts.pop('branch', None)
769 771 return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts)
770 772
771 773 @annotatesubrepoerror
772 774 def incoming(self, ui, source, opts):
773 775 if 'rev' in opts or 'branch' in opts:
774 776 opts = copy.copy(opts)
775 777 opts.pop('rev', None)
776 778 opts.pop('branch', None)
777 779 return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts)
778 780
779 781 @annotatesubrepoerror
780 782 def files(self):
781 783 rev = self._state[1]
782 784 ctx = self._repo[rev]
783 785 return ctx.manifest().keys()
784 786
785 787 def filedata(self, name, decode):
786 788 rev = self._state[1]
787 789 data = self._repo[rev][name].data()
788 790 if decode:
789 791 data = self._repo.wwritedata(name, data)
790 792 return data
791 793
792 794 def fileflags(self, name):
793 795 rev = self._state[1]
794 796 ctx = self._repo[rev]
795 797 return ctx.flags(name)
796 798
797 799 @annotatesubrepoerror
798 800 def printfiles(self, ui, m, uipathfn, fm, fmt, subrepos):
799 801 # If the parent context is a workingctx, use the workingctx here for
800 802 # consistency.
801 803 if self._ctx.rev() is None:
802 804 ctx = self._repo[None]
803 805 else:
804 806 rev = self._state[1]
805 807 ctx = self._repo[rev]
806 808 return cmdutil.files(ui, ctx, m, uipathfn, fm, fmt, subrepos)
807 809
808 810 @annotatesubrepoerror
809 811 def matchfileset(self, expr, badfn=None):
810 812 if self._ctx.rev() is None:
811 813 ctx = self._repo[None]
812 814 else:
813 815 rev = self._state[1]
814 816 ctx = self._repo[rev]
815 817
816 818 matchers = [ctx.matchfileset(expr, badfn=badfn)]
817 819
818 820 for subpath in ctx.substate:
819 821 sub = ctx.sub(subpath)
820 822
821 823 try:
822 824 sm = sub.matchfileset(expr, badfn=badfn)
823 825 pm = matchmod.prefixdirmatcher(subpath, sm, badfn=badfn)
824 826 matchers.append(pm)
825 827 except error.LookupError:
826 828 self.ui.status(_("skipping missing subrepository: %s\n")
827 829 % self.wvfs.reljoin(reporelpath(self), subpath))
828 830 if len(matchers) == 1:
829 831 return matchers[0]
830 832 return matchmod.unionmatcher(matchers)
831 833
832 834 def walk(self, match):
833 835 ctx = self._repo[None]
834 836 return ctx.walk(match)
835 837
836 838 @annotatesubrepoerror
837 839 def forget(self, match, prefix, uipathfn, dryrun, interactive):
838 840 return cmdutil.forget(self.ui, self._repo, match, prefix, uipathfn,
839 841 True, dryrun=dryrun, interactive=interactive)
840 842
841 843 @annotatesubrepoerror
842 844 def removefiles(self, matcher, prefix, uipathfn, after, force, subrepos,
843 845 dryrun, warnings):
844 846 return cmdutil.remove(self.ui, self._repo, matcher, prefix, uipathfn,
845 847 after, force, subrepos, dryrun)
846 848
847 849 @annotatesubrepoerror
848 850 def revert(self, substate, *pats, **opts):
849 851 # reverting a subrepo is a 2 step process:
850 852 # 1. if the no_backup is not set, revert all modified
851 853 # files inside the subrepo
852 854 # 2. update the subrepo to the revision specified in
853 855 # the corresponding substate dictionary
854 856 self.ui.status(_('reverting subrepo %s\n') % substate[0])
855 857 if not opts.get(r'no_backup'):
856 858 # Revert all files on the subrepo, creating backups
857 859 # Note that this will not recursively revert subrepos
858 860 # We could do it if there was a set:subrepos() predicate
859 861 opts = opts.copy()
860 862 opts[r'date'] = None
861 863 opts[r'rev'] = substate[1]
862 864
863 865 self.filerevert(*pats, **opts)
864 866
865 867 # Update the repo to the revision specified in the given substate
866 868 if not opts.get(r'dry_run'):
867 869 self.get(substate, overwrite=True)
868 870
869 871 def filerevert(self, *pats, **opts):
870 872 ctx = self._repo[opts[r'rev']]
871 873 parents = self._repo.dirstate.parents()
872 874 if opts.get(r'all'):
873 875 pats = ['set:modified()']
874 876 else:
875 877 pats = []
876 878 cmdutil.revert(self.ui, self._repo, ctx, parents, *pats, **opts)
877 879
878 880 def shortid(self, revid):
879 881 return revid[:12]
880 882
881 883 @annotatesubrepoerror
882 884 def unshare(self):
883 885 # subrepo inherently violates our import layering rules
884 886 # because it wants to make repo objects from deep inside the stack
885 887 # so we manually delay the circular imports to not break
886 888 # scripts that don't use our demand-loading
887 889 global hg
888 890 from . import hg as h
889 891 hg = h
890 892
891 893 # Nothing prevents a user from sharing in a repo, and then making that a
892 894 # subrepo. Alternately, the previous unshare attempt may have failed
893 895 # part way through. So recurse whether or not this layer is shared.
894 896 if self._repo.shared():
895 897 self.ui.status(_("unsharing subrepo '%s'\n") % self._relpath)
896 898
897 899 hg.unshare(self.ui, self._repo)
898 900
899 901 def verify(self):
900 902 try:
901 903 rev = self._state[1]
902 904 ctx = self._repo.unfiltered()[rev]
903 905 if ctx.hidden():
904 906 # Since hidden revisions aren't pushed/pulled, it seems worth an
905 907 # explicit warning.
906 908 ui = self._repo.ui
907 909 ui.warn(_("subrepo '%s' is hidden in revision %s\n") %
908 910 (self._relpath, node.short(self._ctx.node())))
909 911 return 0
910 912 except error.RepoLookupError:
911 913 # A missing subrepo revision may be a case of needing to pull it, so
912 914 # don't treat this as an error.
913 915 self._repo.ui.warn(_("subrepo '%s' not found in revision %s\n") %
914 916 (self._relpath, node.short(self._ctx.node())))
915 917 return 0
916 918
917 919 @propertycache
918 920 def wvfs(self):
919 921 """return own wvfs for efficiency and consistency
920 922 """
921 923 return self._repo.wvfs
922 924
923 925 @propertycache
924 926 def _relpath(self):
925 927 """return path to this subrepository as seen from outermost repository
926 928 """
927 929 # Keep consistent dir separators by avoiding vfs.join(self._path)
928 930 return reporelpath(self._repo)
929 931
930 932 class svnsubrepo(abstractsubrepo):
931 933 def __init__(self, ctx, path, state, allowcreate):
932 934 super(svnsubrepo, self).__init__(ctx, path)
933 935 self._state = state
934 936 self._exe = procutil.findexe('svn')
935 937 if not self._exe:
936 938 raise error.Abort(_("'svn' executable not found for subrepo '%s'")
937 939 % self._path)
938 940
939 941 def _svncommand(self, commands, filename='', failok=False):
940 942 cmd = [self._exe]
941 943 extrakw = {}
942 944 if not self.ui.interactive():
943 945 # Making stdin be a pipe should prevent svn from behaving
944 946 # interactively even if we can't pass --non-interactive.
945 947 extrakw[r'stdin'] = subprocess.PIPE
946 948 # Starting in svn 1.5 --non-interactive is a global flag
947 949 # instead of being per-command, but we need to support 1.4 so
948 950 # we have to be intelligent about what commands take
949 951 # --non-interactive.
950 952 if commands[0] in ('update', 'checkout', 'commit'):
951 953 cmd.append('--non-interactive')
952 954 cmd.extend(commands)
953 955 if filename is not None:
954 956 path = self.wvfs.reljoin(self._ctx.repo().origroot,
955 957 self._path, filename)
956 958 cmd.append(path)
957 959 env = dict(encoding.environ)
958 960 # Avoid localized output, preserve current locale for everything else.
959 961 lc_all = env.get('LC_ALL')
960 962 if lc_all:
961 963 env['LANG'] = lc_all
962 964 del env['LC_ALL']
963 965 env['LC_MESSAGES'] = 'C'
964 966 p = subprocess.Popen(pycompat.rapply(procutil.tonativestr, cmd),
965 967 bufsize=-1, close_fds=procutil.closefds,
966 968 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
967 969 env=procutil.tonativeenv(env), **extrakw)
968 970 stdout, stderr = map(util.fromnativeeol, p.communicate())
969 971 stderr = stderr.strip()
970 972 if not failok:
971 973 if p.returncode:
972 974 raise error.Abort(stderr or 'exited with code %d'
973 975 % p.returncode)
974 976 if stderr:
975 977 self.ui.warn(stderr + '\n')
976 978 return stdout, stderr
977 979
978 980 @propertycache
979 981 def _svnversion(self):
980 982 output, err = self._svncommand(['--version', '--quiet'], filename=None)
981 983 m = re.search(br'^(\d+)\.(\d+)', output)
982 984 if not m:
983 985 raise error.Abort(_('cannot retrieve svn tool version'))
984 986 return (int(m.group(1)), int(m.group(2)))
985 987
986 988 def _svnmissing(self):
987 989 return not self.wvfs.exists('.svn')
988 990
989 991 def _wcrevs(self):
990 992 # Get the working directory revision as well as the last
991 993 # commit revision so we can compare the subrepo state with
992 994 # both. We used to store the working directory one.
993 995 output, err = self._svncommand(['info', '--xml'])
994 996 doc = xml.dom.minidom.parseString(output)
995 997 entries = doc.getElementsByTagName(r'entry')
996 998 lastrev, rev = '0', '0'
997 999 if entries:
998 1000 rev = pycompat.bytestr(entries[0].getAttribute(r'revision')) or '0'
999 1001 commits = entries[0].getElementsByTagName(r'commit')
1000 1002 if commits:
1001 1003 lastrev = pycompat.bytestr(
1002 1004 commits[0].getAttribute(r'revision')) or '0'
1003 1005 return (lastrev, rev)
1004 1006
1005 1007 def _wcrev(self):
1006 1008 return self._wcrevs()[0]
1007 1009
1008 1010 def _wcchanged(self):
1009 1011 """Return (changes, extchanges, missing) where changes is True
1010 1012 if the working directory was changed, extchanges is
1011 1013 True if any of these changes concern an external entry and missing
1012 1014 is True if any change is a missing entry.
1013 1015 """
1014 1016 output, err = self._svncommand(['status', '--xml'])
1015 1017 externals, changes, missing = [], [], []
1016 1018 doc = xml.dom.minidom.parseString(output)
1017 1019 for e in doc.getElementsByTagName(r'entry'):
1018 1020 s = e.getElementsByTagName(r'wc-status')
1019 1021 if not s:
1020 1022 continue
1021 1023 item = s[0].getAttribute(r'item')
1022 1024 props = s[0].getAttribute(r'props')
1023 1025 path = e.getAttribute(r'path').encode('utf8')
1024 1026 if item == r'external':
1025 1027 externals.append(path)
1026 1028 elif item == r'missing':
1027 1029 missing.append(path)
1028 1030 if (item not in (r'', r'normal', r'unversioned', r'external')
1029 1031 or props not in (r'', r'none', r'normal')):
1030 1032 changes.append(path)
1031 1033 for path in changes:
1032 1034 for ext in externals:
1033 1035 if path == ext or path.startswith(ext + pycompat.ossep):
1034 1036 return True, True, bool(missing)
1035 1037 return bool(changes), False, bool(missing)
1036 1038
1037 1039 @annotatesubrepoerror
1038 1040 def dirty(self, ignoreupdate=False, missing=False):
1039 1041 if self._svnmissing():
1040 1042 return self._state[1] != ''
1041 1043 wcchanged = self._wcchanged()
1042 1044 changed = wcchanged[0] or (missing and wcchanged[2])
1043 1045 if not changed:
1044 1046 if self._state[1] in self._wcrevs() or ignoreupdate:
1045 1047 return False
1046 1048 return True
1047 1049
1048 1050 def basestate(self):
1049 1051 lastrev, rev = self._wcrevs()
1050 1052 if lastrev != rev:
1051 1053 # Last committed rev is not the same than rev. We would
1052 1054 # like to take lastrev but we do not know if the subrepo
1053 1055 # URL exists at lastrev. Test it and fallback to rev it
1054 1056 # is not there.
1055 1057 try:
1056 1058 self._svncommand(['list', '%s@%s' % (self._state[0], lastrev)])
1057 1059 return lastrev
1058 1060 except error.Abort:
1059 1061 pass
1060 1062 return rev
1061 1063
1062 1064 @annotatesubrepoerror
1063 1065 def commit(self, text, user, date):
1064 1066 # user and date are out of our hands since svn is centralized
1065 1067 changed, extchanged, missing = self._wcchanged()
1066 1068 if not changed:
1067 1069 return self.basestate()
1068 1070 if extchanged:
1069 1071 # Do not try to commit externals
1070 1072 raise error.Abort(_('cannot commit svn externals'))
1071 1073 if missing:
1072 1074 # svn can commit with missing entries but aborting like hg
1073 1075 # seems a better approach.
1074 1076 raise error.Abort(_('cannot commit missing svn entries'))
1075 1077 commitinfo, err = self._svncommand(['commit', '-m', text])
1076 1078 self.ui.status(commitinfo)
1077 1079 newrev = re.search('Committed revision ([0-9]+).', commitinfo)
1078 1080 if not newrev:
1079 1081 if not commitinfo.strip():
1080 1082 # Sometimes, our definition of "changed" differs from
1081 1083 # svn one. For instance, svn ignores missing files
1082 1084 # when committing. If there are only missing files, no
1083 1085 # commit is made, no output and no error code.
1084 1086 raise error.Abort(_('failed to commit svn changes'))
1085 1087 raise error.Abort(commitinfo.splitlines()[-1])
1086 1088 newrev = newrev.groups()[0]
1087 1089 self.ui.status(self._svncommand(['update', '-r', newrev])[0])
1088 1090 return newrev
1089 1091
1090 1092 @annotatesubrepoerror
1091 1093 def remove(self):
1092 1094 if self.dirty():
1093 1095 self.ui.warn(_('not removing repo %s because '
1094 1096 'it has changes.\n') % self._path)
1095 1097 return
1096 1098 self.ui.note(_('removing subrepo %s\n') % self._path)
1097 1099
1098 1100 self.wvfs.rmtree(forcibly=True)
1099 1101 try:
1100 1102 pwvfs = self._ctx.repo().wvfs
1101 1103 pwvfs.removedirs(pwvfs.dirname(self._path))
1102 1104 except OSError:
1103 1105 pass
1104 1106
1105 1107 @annotatesubrepoerror
1106 1108 def get(self, state, overwrite=False):
1107 1109 if overwrite:
1108 1110 self._svncommand(['revert', '--recursive'])
1109 1111 args = ['checkout']
1110 1112 if self._svnversion >= (1, 5):
1111 1113 args.append('--force')
1112 1114 # The revision must be specified at the end of the URL to properly
1113 1115 # update to a directory which has since been deleted and recreated.
1114 1116 args.append('%s@%s' % (state[0], state[1]))
1115 1117
1116 1118 # SEC: check that the ssh url is safe
1117 1119 util.checksafessh(state[0])
1118 1120
1119 1121 status, err = self._svncommand(args, failok=True)
1120 1122 _sanitize(self.ui, self.wvfs, '.svn')
1121 1123 if not re.search('Checked out revision [0-9]+.', status):
1122 1124 if ('is already a working copy for a different URL' in err
1123 1125 and (self._wcchanged()[:2] == (False, False))):
1124 1126 # obstructed but clean working copy, so just blow it away.
1125 1127 self.remove()
1126 1128 self.get(state, overwrite=False)
1127 1129 return
1128 1130 raise error.Abort((status or err).splitlines()[-1])
1129 1131 self.ui.status(status)
1130 1132
1131 1133 @annotatesubrepoerror
1132 1134 def merge(self, state):
1133 1135 old = self._state[1]
1134 1136 new = state[1]
1135 1137 wcrev = self._wcrev()
1136 1138 if new != wcrev:
1137 1139 dirty = old == wcrev or self._wcchanged()[0]
1138 1140 if _updateprompt(self.ui, self, dirty, wcrev, new):
1139 1141 self.get(state, False)
1140 1142
1141 1143 def push(self, opts):
1142 1144 # push is a no-op for SVN
1143 1145 return True
1144 1146
1145 1147 @annotatesubrepoerror
1146 1148 def files(self):
1147 1149 output = self._svncommand(['list', '--recursive', '--xml'])[0]
1148 1150 doc = xml.dom.minidom.parseString(output)
1149 1151 paths = []
1150 1152 for e in doc.getElementsByTagName(r'entry'):
1151 1153 kind = pycompat.bytestr(e.getAttribute(r'kind'))
1152 1154 if kind != 'file':
1153 1155 continue
1154 1156 name = r''.join(c.data for c
1155 1157 in e.getElementsByTagName(r'name')[0].childNodes
1156 1158 if c.nodeType == c.TEXT_NODE)
1157 1159 paths.append(name.encode('utf8'))
1158 1160 return paths
1159 1161
1160 1162 def filedata(self, name, decode):
1161 1163 return self._svncommand(['cat'], name)[0]
1162 1164
1163 1165
1164 1166 class gitsubrepo(abstractsubrepo):
1165 1167 def __init__(self, ctx, path, state, allowcreate):
1166 1168 super(gitsubrepo, self).__init__(ctx, path)
1167 1169 self._state = state
1168 1170 self._abspath = ctx.repo().wjoin(path)
1169 1171 self._subparent = ctx.repo()
1170 1172 self._ensuregit()
1171 1173
1172 1174 def _ensuregit(self):
1173 1175 try:
1174 1176 self._gitexecutable = 'git'
1175 1177 out, err = self._gitnodir(['--version'])
1176 1178 except OSError as e:
1177 1179 genericerror = _("error executing git for subrepo '%s': %s")
1178 1180 notfoundhint = _("check git is installed and in your PATH")
1179 1181 if e.errno != errno.ENOENT:
1180 1182 raise error.Abort(genericerror % (
1181 1183 self._path, encoding.strtolocal(e.strerror)))
1182 1184 elif pycompat.iswindows:
1183 1185 try:
1184 1186 self._gitexecutable = 'git.cmd'
1185 1187 out, err = self._gitnodir(['--version'])
1186 1188 except OSError as e2:
1187 1189 if e2.errno == errno.ENOENT:
1188 1190 raise error.Abort(_("couldn't find 'git' or 'git.cmd'"
1189 1191 " for subrepo '%s'") % self._path,
1190 1192 hint=notfoundhint)
1191 1193 else:
1192 1194 raise error.Abort(genericerror % (self._path,
1193 1195 encoding.strtolocal(e2.strerror)))
1194 1196 else:
1195 1197 raise error.Abort(_("couldn't find git for subrepo '%s'")
1196 1198 % self._path, hint=notfoundhint)
1197 1199 versionstatus = self._checkversion(out)
1198 1200 if versionstatus == 'unknown':
1199 1201 self.ui.warn(_('cannot retrieve git version\n'))
1200 1202 elif versionstatus == 'abort':
1201 1203 raise error.Abort(_('git subrepo requires at least 1.6.0 or later'))
1202 1204 elif versionstatus == 'warning':
1203 1205 self.ui.warn(_('git subrepo requires at least 1.6.0 or later\n'))
1204 1206
1205 1207 @staticmethod
1206 1208 def _gitversion(out):
1207 1209 m = re.search(br'^git version (\d+)\.(\d+)\.(\d+)', out)
1208 1210 if m:
1209 1211 return (int(m.group(1)), int(m.group(2)), int(m.group(3)))
1210 1212
1211 1213 m = re.search(br'^git version (\d+)\.(\d+)', out)
1212 1214 if m:
1213 1215 return (int(m.group(1)), int(m.group(2)), 0)
1214 1216
1215 1217 return -1
1216 1218
1217 1219 @staticmethod
1218 1220 def _checkversion(out):
1219 1221 '''ensure git version is new enough
1220 1222
1221 1223 >>> _checkversion = gitsubrepo._checkversion
1222 1224 >>> _checkversion(b'git version 1.6.0')
1223 1225 'ok'
1224 1226 >>> _checkversion(b'git version 1.8.5')
1225 1227 'ok'
1226 1228 >>> _checkversion(b'git version 1.4.0')
1227 1229 'abort'
1228 1230 >>> _checkversion(b'git version 1.5.0')
1229 1231 'warning'
1230 1232 >>> _checkversion(b'git version 1.9-rc0')
1231 1233 'ok'
1232 1234 >>> _checkversion(b'git version 1.9.0.265.g81cdec2')
1233 1235 'ok'
1234 1236 >>> _checkversion(b'git version 1.9.0.GIT')
1235 1237 'ok'
1236 1238 >>> _checkversion(b'git version 12345')
1237 1239 'unknown'
1238 1240 >>> _checkversion(b'no')
1239 1241 'unknown'
1240 1242 '''
1241 1243 version = gitsubrepo._gitversion(out)
1242 1244 # git 1.4.0 can't work at all, but 1.5.X can in at least some cases,
1243 1245 # despite the docstring comment. For now, error on 1.4.0, warn on
1244 1246 # 1.5.0 but attempt to continue.
1245 1247 if version == -1:
1246 1248 return 'unknown'
1247 1249 if version < (1, 5, 0):
1248 1250 return 'abort'
1249 1251 elif version < (1, 6, 0):
1250 1252 return 'warning'
1251 1253 return 'ok'
1252 1254
1253 1255 def _gitcommand(self, commands, env=None, stream=False):
1254 1256 return self._gitdir(commands, env=env, stream=stream)[0]
1255 1257
1256 1258 def _gitdir(self, commands, env=None, stream=False):
1257 1259 return self._gitnodir(commands, env=env, stream=stream,
1258 1260 cwd=self._abspath)
1259 1261
1260 1262 def _gitnodir(self, commands, env=None, stream=False, cwd=None):
1261 1263 """Calls the git command
1262 1264
1263 1265 The methods tries to call the git command. versions prior to 1.6.0
1264 1266 are not supported and very probably fail.
1265 1267 """
1266 1268 self.ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands)))
1267 1269 if env is None:
1268 1270 env = encoding.environ.copy()
1269 1271 # disable localization for Git output (issue5176)
1270 1272 env['LC_ALL'] = 'C'
1271 1273 # fix for Git CVE-2015-7545
1272 1274 if 'GIT_ALLOW_PROTOCOL' not in env:
1273 1275 env['GIT_ALLOW_PROTOCOL'] = 'file:git:http:https:ssh'
1274 1276 # unless ui.quiet is set, print git's stderr,
1275 1277 # which is mostly progress and useful info
1276 1278 errpipe = None
1277 1279 if self.ui.quiet:
1278 1280 errpipe = open(os.devnull, 'w')
1279 1281 if self.ui._colormode and len(commands) and commands[0] == "diff":
1280 1282 # insert the argument in the front,
1281 1283 # the end of git diff arguments is used for paths
1282 1284 commands.insert(1, '--color')
1283 1285 p = subprocess.Popen(pycompat.rapply(procutil.tonativestr,
1284 1286 [self._gitexecutable] + commands),
1285 1287 bufsize=-1,
1286 1288 cwd=pycompat.rapply(procutil.tonativestr, cwd),
1287 1289 env=procutil.tonativeenv(env),
1288 1290 close_fds=procutil.closefds,
1289 1291 stdout=subprocess.PIPE, stderr=errpipe)
1290 1292 if stream:
1291 1293 return p.stdout, None
1292 1294
1293 1295 retdata = p.stdout.read().strip()
1294 1296 # wait for the child to exit to avoid race condition.
1295 1297 p.wait()
1296 1298
1297 1299 if p.returncode != 0 and p.returncode != 1:
1298 1300 # there are certain error codes that are ok
1299 1301 command = commands[0]
1300 1302 if command in ('cat-file', 'symbolic-ref'):
1301 1303 return retdata, p.returncode
1302 1304 # for all others, abort
1303 1305 raise error.Abort(_('git %s error %d in %s') %
1304 1306 (command, p.returncode, self._relpath))
1305 1307
1306 1308 return retdata, p.returncode
1307 1309
1308 1310 def _gitmissing(self):
1309 1311 return not self.wvfs.exists('.git')
1310 1312
1311 1313 def _gitstate(self):
1312 1314 return self._gitcommand(['rev-parse', 'HEAD'])
1313 1315
1314 1316 def _gitcurrentbranch(self):
1315 1317 current, err = self._gitdir(['symbolic-ref', 'HEAD', '--quiet'])
1316 1318 if err:
1317 1319 current = None
1318 1320 return current
1319 1321
1320 1322 def _gitremote(self, remote):
1321 1323 out = self._gitcommand(['remote', 'show', '-n', remote])
1322 1324 line = out.split('\n')[1]
1323 1325 i = line.index('URL: ') + len('URL: ')
1324 1326 return line[i:]
1325 1327
1326 1328 def _githavelocally(self, revision):
1327 1329 out, code = self._gitdir(['cat-file', '-e', revision])
1328 1330 return code == 0
1329 1331
1330 1332 def _gitisancestor(self, r1, r2):
1331 1333 base = self._gitcommand(['merge-base', r1, r2])
1332 1334 return base == r1
1333 1335
1334 1336 def _gitisbare(self):
1335 1337 return self._gitcommand(['config', '--bool', 'core.bare']) == 'true'
1336 1338
1337 1339 def _gitupdatestat(self):
1338 1340 """This must be run before git diff-index.
1339 1341 diff-index only looks at changes to file stat;
1340 1342 this command looks at file contents and updates the stat."""
1341 1343 self._gitcommand(['update-index', '-q', '--refresh'])
1342 1344
1343 1345 def _gitbranchmap(self):
1344 1346 '''returns 2 things:
1345 1347 a map from git branch to revision
1346 1348 a map from revision to branches'''
1347 1349 branch2rev = {}
1348 1350 rev2branch = {}
1349 1351
1350 1352 out = self._gitcommand(['for-each-ref', '--format',
1351 1353 '%(objectname) %(refname)'])
1352 1354 for line in out.split('\n'):
1353 1355 revision, ref = line.split(' ')
1354 1356 if (not ref.startswith('refs/heads/') and
1355 1357 not ref.startswith('refs/remotes/')):
1356 1358 continue
1357 1359 if ref.startswith('refs/remotes/') and ref.endswith('/HEAD'):
1358 1360 continue # ignore remote/HEAD redirects
1359 1361 branch2rev[ref] = revision
1360 1362 rev2branch.setdefault(revision, []).append(ref)
1361 1363 return branch2rev, rev2branch
1362 1364
1363 1365 def _gittracking(self, branches):
1364 1366 'return map of remote branch to local tracking branch'
1365 1367 # assumes no more than one local tracking branch for each remote
1366 1368 tracking = {}
1367 1369 for b in branches:
1368 1370 if b.startswith('refs/remotes/'):
1369 1371 continue
1370 1372 bname = b.split('/', 2)[2]
1371 1373 remote = self._gitcommand(['config', 'branch.%s.remote' % bname])
1372 1374 if remote:
1373 1375 ref = self._gitcommand(['config', 'branch.%s.merge' % bname])
1374 1376 tracking['refs/remotes/%s/%s' %
1375 1377 (remote, ref.split('/', 2)[2])] = b
1376 1378 return tracking
1377 1379
1378 1380 def _abssource(self, source):
1379 1381 if '://' not in source:
1380 1382 # recognize the scp syntax as an absolute source
1381 1383 colon = source.find(':')
1382 1384 if colon != -1 and '/' not in source[:colon]:
1383 1385 return source
1384 1386 self._subsource = source
1385 1387 return _abssource(self)
1386 1388
1387 1389 def _fetch(self, source, revision):
1388 1390 if self._gitmissing():
1389 1391 # SEC: check for safe ssh url
1390 1392 util.checksafessh(source)
1391 1393
1392 1394 source = self._abssource(source)
1393 1395 self.ui.status(_('cloning subrepo %s from %s\n') %
1394 1396 (self._relpath, source))
1395 1397 self._gitnodir(['clone', source, self._abspath])
1396 1398 if self._githavelocally(revision):
1397 1399 return
1398 1400 self.ui.status(_('pulling subrepo %s from %s\n') %
1399 1401 (self._relpath, self._gitremote('origin')))
1400 1402 # try only origin: the originally cloned repo
1401 1403 self._gitcommand(['fetch'])
1402 1404 if not self._githavelocally(revision):
1403 1405 raise error.Abort(_('revision %s does not exist in subrepository '
1404 1406 '"%s"\n') % (revision, self._relpath))
1405 1407
1406 1408 @annotatesubrepoerror
1407 1409 def dirty(self, ignoreupdate=False, missing=False):
1408 1410 if self._gitmissing():
1409 1411 return self._state[1] != ''
1410 1412 if self._gitisbare():
1411 1413 return True
1412 1414 if not ignoreupdate and self._state[1] != self._gitstate():
1413 1415 # different version checked out
1414 1416 return True
1415 1417 # check for staged changes or modified files; ignore untracked files
1416 1418 self._gitupdatestat()
1417 1419 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1418 1420 return code == 1
1419 1421
1420 1422 def basestate(self):
1421 1423 return self._gitstate()
1422 1424
1423 1425 @annotatesubrepoerror
1424 1426 def get(self, state, overwrite=False):
1425 1427 source, revision, kind = state
1426 1428 if not revision:
1427 1429 self.remove()
1428 1430 return
1429 1431 self._fetch(source, revision)
1430 1432 # if the repo was set to be bare, unbare it
1431 1433 if self._gitisbare():
1432 1434 self._gitcommand(['config', 'core.bare', 'false'])
1433 1435 if self._gitstate() == revision:
1434 1436 self._gitcommand(['reset', '--hard', 'HEAD'])
1435 1437 return
1436 1438 elif self._gitstate() == revision:
1437 1439 if overwrite:
1438 1440 # first reset the index to unmark new files for commit, because
1439 1441 # reset --hard will otherwise throw away files added for commit,
1440 1442 # not just unmark them.
1441 1443 self._gitcommand(['reset', 'HEAD'])
1442 1444 self._gitcommand(['reset', '--hard', 'HEAD'])
1443 1445 return
1444 1446 branch2rev, rev2branch = self._gitbranchmap()
1445 1447
1446 1448 def checkout(args):
1447 1449 cmd = ['checkout']
1448 1450 if overwrite:
1449 1451 # first reset the index to unmark new files for commit, because
1450 1452 # the -f option will otherwise throw away files added for
1451 1453 # commit, not just unmark them.
1452 1454 self._gitcommand(['reset', 'HEAD'])
1453 1455 cmd.append('-f')
1454 1456 self._gitcommand(cmd + args)
1455 1457 _sanitize(self.ui, self.wvfs, '.git')
1456 1458
1457 1459 def rawcheckout():
1458 1460 # no branch to checkout, check it out with no branch
1459 1461 self.ui.warn(_('checking out detached HEAD in '
1460 1462 'subrepository "%s"\n') % self._relpath)
1461 1463 self.ui.warn(_('check out a git branch if you intend '
1462 1464 'to make changes\n'))
1463 1465 checkout(['-q', revision])
1464 1466
1465 1467 if revision not in rev2branch:
1466 1468 rawcheckout()
1467 1469 return
1468 1470 branches = rev2branch[revision]
1469 1471 firstlocalbranch = None
1470 1472 for b in branches:
1471 1473 if b == 'refs/heads/master':
1472 1474 # master trumps all other branches
1473 1475 checkout(['refs/heads/master'])
1474 1476 return
1475 1477 if not firstlocalbranch and not b.startswith('refs/remotes/'):
1476 1478 firstlocalbranch = b
1477 1479 if firstlocalbranch:
1478 1480 checkout([firstlocalbranch])
1479 1481 return
1480 1482
1481 1483 tracking = self._gittracking(branch2rev.keys())
1482 1484 # choose a remote branch already tracked if possible
1483 1485 remote = branches[0]
1484 1486 if remote not in tracking:
1485 1487 for b in branches:
1486 1488 if b in tracking:
1487 1489 remote = b
1488 1490 break
1489 1491
1490 1492 if remote not in tracking:
1491 1493 # create a new local tracking branch
1492 1494 local = remote.split('/', 3)[3]
1493 1495 checkout(['-b', local, remote])
1494 1496 elif self._gitisancestor(branch2rev[tracking[remote]], remote):
1495 1497 # When updating to a tracked remote branch,
1496 1498 # if the local tracking branch is downstream of it,
1497 1499 # a normal `git pull` would have performed a "fast-forward merge"
1498 1500 # which is equivalent to updating the local branch to the remote.
1499 1501 # Since we are only looking at branching at update, we need to
1500 1502 # detect this situation and perform this action lazily.
1501 1503 if tracking[remote] != self._gitcurrentbranch():
1502 1504 checkout([tracking[remote]])
1503 1505 self._gitcommand(['merge', '--ff', remote])
1504 1506 _sanitize(self.ui, self.wvfs, '.git')
1505 1507 else:
1506 1508 # a real merge would be required, just checkout the revision
1507 1509 rawcheckout()
1508 1510
1509 1511 @annotatesubrepoerror
1510 1512 def commit(self, text, user, date):
1511 1513 if self._gitmissing():
1512 1514 raise error.Abort(_("subrepo %s is missing") % self._relpath)
1513 1515 cmd = ['commit', '-a', '-m', text]
1514 1516 env = encoding.environ.copy()
1515 1517 if user:
1516 1518 cmd += ['--author', user]
1517 1519 if date:
1518 1520 # git's date parser silently ignores when seconds < 1e9
1519 1521 # convert to ISO8601
1520 1522 env['GIT_AUTHOR_DATE'] = dateutil.datestr(date,
1521 1523 '%Y-%m-%dT%H:%M:%S %1%2')
1522 1524 self._gitcommand(cmd, env=env)
1523 1525 # make sure commit works otherwise HEAD might not exist under certain
1524 1526 # circumstances
1525 1527 return self._gitstate()
1526 1528
1527 1529 @annotatesubrepoerror
1528 1530 def merge(self, state):
1529 1531 source, revision, kind = state
1530 1532 self._fetch(source, revision)
1531 1533 base = self._gitcommand(['merge-base', revision, self._state[1]])
1532 1534 self._gitupdatestat()
1533 1535 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1534 1536
1535 1537 def mergefunc():
1536 1538 if base == revision:
1537 1539 self.get(state) # fast forward merge
1538 1540 elif base != self._state[1]:
1539 1541 self._gitcommand(['merge', '--no-commit', revision])
1540 1542 _sanitize(self.ui, self.wvfs, '.git')
1541 1543
1542 1544 if self.dirty():
1543 1545 if self._gitstate() != revision:
1544 1546 dirty = self._gitstate() == self._state[1] or code != 0
1545 1547 if _updateprompt(self.ui, self, dirty,
1546 1548 self._state[1][:7], revision[:7]):
1547 1549 mergefunc()
1548 1550 else:
1549 1551 mergefunc()
1550 1552
1551 1553 @annotatesubrepoerror
1552 1554 def push(self, opts):
1553 1555 force = opts.get('force')
1554 1556
1555 1557 if not self._state[1]:
1556 1558 return True
1557 1559 if self._gitmissing():
1558 1560 raise error.Abort(_("subrepo %s is missing") % self._relpath)
1559 1561 # if a branch in origin contains the revision, nothing to do
1560 1562 branch2rev, rev2branch = self._gitbranchmap()
1561 1563 if self._state[1] in rev2branch:
1562 1564 for b in rev2branch[self._state[1]]:
1563 1565 if b.startswith('refs/remotes/origin/'):
1564 1566 return True
1565 1567 for b, revision in branch2rev.iteritems():
1566 1568 if b.startswith('refs/remotes/origin/'):
1567 1569 if self._gitisancestor(self._state[1], revision):
1568 1570 return True
1569 1571 # otherwise, try to push the currently checked out branch
1570 1572 cmd = ['push']
1571 1573 if force:
1572 1574 cmd.append('--force')
1573 1575
1574 1576 current = self._gitcurrentbranch()
1575 1577 if current:
1576 1578 # determine if the current branch is even useful
1577 1579 if not self._gitisancestor(self._state[1], current):
1578 1580 self.ui.warn(_('unrelated git branch checked out '
1579 1581 'in subrepository "%s"\n') % self._relpath)
1580 1582 return False
1581 1583 self.ui.status(_('pushing branch %s of subrepository "%s"\n') %
1582 1584 (current.split('/', 2)[2], self._relpath))
1583 1585 ret = self._gitdir(cmd + ['origin', current])
1584 1586 return ret[1] == 0
1585 1587 else:
1586 1588 self.ui.warn(_('no branch checked out in subrepository "%s"\n'
1587 1589 'cannot push revision %s\n') %
1588 1590 (self._relpath, self._state[1]))
1589 1591 return False
1590 1592
1591 1593 @annotatesubrepoerror
1592 1594 def add(self, ui, match, prefix, uipathfn, explicitonly, **opts):
1593 1595 if self._gitmissing():
1594 1596 return []
1595 1597
1596 1598 s = self.status(None, unknown=True, clean=True)
1597 1599
1598 1600 tracked = set()
1599 1601 # dirstates 'amn' warn, 'r' is added again
1600 1602 for l in (s.modified, s.added, s.deleted, s.clean):
1601 1603 tracked.update(l)
1602 1604
1603 1605 # Unknown files not of interest will be rejected by the matcher
1604 1606 files = s.unknown
1605 1607 files.extend(match.files())
1606 1608
1607 1609 rejected = []
1608 1610
1609 1611 files = [f for f in sorted(set(files)) if match(f)]
1610 1612 for f in files:
1611 1613 exact = match.exact(f)
1612 1614 command = ["add"]
1613 1615 if exact:
1614 1616 command.append("-f") #should be added, even if ignored
1615 1617 if ui.verbose or not exact:
1616 1618 ui.status(_('adding %s\n') % uipathfn(f))
1617 1619
1618 1620 if f in tracked: # hg prints 'adding' even if already tracked
1619 1621 if exact:
1620 1622 rejected.append(f)
1621 1623 continue
1622 1624 if not opts.get(r'dry_run'):
1623 1625 self._gitcommand(command + [f])
1624 1626
1625 1627 for f in rejected:
1626 1628 ui.warn(_("%s already tracked!\n") % uipathfn(f))
1627 1629
1628 1630 return rejected
1629 1631
1630 1632 @annotatesubrepoerror
1631 1633 def remove(self):
1632 1634 if self._gitmissing():
1633 1635 return
1634 1636 if self.dirty():
1635 1637 self.ui.warn(_('not removing repo %s because '
1636 1638 'it has changes.\n') % self._relpath)
1637 1639 return
1638 1640 # we can't fully delete the repository as it may contain
1639 1641 # local-only history
1640 1642 self.ui.note(_('removing subrepo %s\n') % self._relpath)
1641 1643 self._gitcommand(['config', 'core.bare', 'true'])
1642 1644 for f, kind in self.wvfs.readdir():
1643 1645 if f == '.git':
1644 1646 continue
1645 1647 if kind == stat.S_IFDIR:
1646 1648 self.wvfs.rmtree(f)
1647 1649 else:
1648 1650 self.wvfs.unlink(f)
1649 1651
1650 1652 def archive(self, archiver, prefix, match=None, decode=True):
1651 1653 total = 0
1652 1654 source, revision = self._state
1653 1655 if not revision:
1654 1656 return total
1655 1657 self._fetch(source, revision)
1656 1658
1657 1659 # Parse git's native archive command.
1658 1660 # This should be much faster than manually traversing the trees
1659 1661 # and objects with many subprocess calls.
1660 1662 tarstream = self._gitcommand(['archive', revision], stream=True)
1661 1663 tar = tarfile.open(fileobj=tarstream, mode=r'r|')
1662 1664 relpath = subrelpath(self)
1663 1665 progress = self.ui.makeprogress(_('archiving (%s)') % relpath,
1664 1666 unit=_('files'))
1665 1667 progress.update(0)
1666 1668 for info in tar:
1667 1669 if info.isdir():
1668 1670 continue
1669 1671 bname = pycompat.fsencode(info.name)
1670 1672 if match and not match(bname):
1671 1673 continue
1672 1674 if info.issym():
1673 1675 data = info.linkname
1674 1676 else:
1675 1677 data = tar.extractfile(info).read()
1676 1678 archiver.addfile(prefix + bname, info.mode, info.issym(), data)
1677 1679 total += 1
1678 1680 progress.increment()
1679 1681 progress.complete()
1680 1682 return total
1681 1683
1682 1684
1683 1685 @annotatesubrepoerror
1684 1686 def cat(self, match, fm, fntemplate, prefix, **opts):
1685 1687 rev = self._state[1]
1686 1688 if match.anypats():
1687 1689 return 1 #No support for include/exclude yet
1688 1690
1689 1691 if not match.files():
1690 1692 return 1
1691 1693
1692 1694 # TODO: add support for non-plain formatter (see cmdutil.cat())
1693 1695 for f in match.files():
1694 1696 output = self._gitcommand(["show", "%s:%s" % (rev, f)])
1695 1697 fp = cmdutil.makefileobj(self._ctx, fntemplate,
1696 1698 pathname=self.wvfs.reljoin(prefix, f))
1697 1699 fp.write(output)
1698 1700 fp.close()
1699 1701 return 0
1700 1702
1701 1703
1702 1704 @annotatesubrepoerror
1703 1705 def status(self, rev2, **opts):
1704 1706 rev1 = self._state[1]
1705 1707 if self._gitmissing() or not rev1:
1706 1708 # if the repo is missing, return no results
1707 1709 return scmutil.status([], [], [], [], [], [], [])
1708 1710 modified, added, removed = [], [], []
1709 1711 self._gitupdatestat()
1710 1712 if rev2:
1711 1713 command = ['diff-tree', '--no-renames', '-r', rev1, rev2]
1712 1714 else:
1713 1715 command = ['diff-index', '--no-renames', rev1]
1714 1716 out = self._gitcommand(command)
1715 1717 for line in out.split('\n'):
1716 1718 tab = line.find('\t')
1717 1719 if tab == -1:
1718 1720 continue
1719 1721 status, f = line[tab - 1:tab], line[tab + 1:]
1720 1722 if status == 'M':
1721 1723 modified.append(f)
1722 1724 elif status == 'A':
1723 1725 added.append(f)
1724 1726 elif status == 'D':
1725 1727 removed.append(f)
1726 1728
1727 1729 deleted, unknown, ignored, clean = [], [], [], []
1728 1730
1729 1731 command = ['status', '--porcelain', '-z']
1730 1732 if opts.get(r'unknown'):
1731 1733 command += ['--untracked-files=all']
1732 1734 if opts.get(r'ignored'):
1733 1735 command += ['--ignored']
1734 1736 out = self._gitcommand(command)
1735 1737
1736 1738 changedfiles = set()
1737 1739 changedfiles.update(modified)
1738 1740 changedfiles.update(added)
1739 1741 changedfiles.update(removed)
1740 1742 for line in out.split('\0'):
1741 1743 if not line:
1742 1744 continue
1743 1745 st = line[0:2]
1744 1746 #moves and copies show 2 files on one line
1745 1747 if line.find('\0') >= 0:
1746 1748 filename1, filename2 = line[3:].split('\0')
1747 1749 else:
1748 1750 filename1 = line[3:]
1749 1751 filename2 = None
1750 1752
1751 1753 changedfiles.add(filename1)
1752 1754 if filename2:
1753 1755 changedfiles.add(filename2)
1754 1756
1755 1757 if st == '??':
1756 1758 unknown.append(filename1)
1757 1759 elif st == '!!':
1758 1760 ignored.append(filename1)
1759 1761
1760 1762 if opts.get(r'clean'):
1761 1763 out = self._gitcommand(['ls-files'])
1762 1764 for f in out.split('\n'):
1763 1765 if not f in changedfiles:
1764 1766 clean.append(f)
1765 1767
1766 1768 return scmutil.status(modified, added, removed, deleted,
1767 1769 unknown, ignored, clean)
1768 1770
1769 1771 @annotatesubrepoerror
1770 1772 def diff(self, ui, diffopts, node2, match, prefix, **opts):
1771 1773 node1 = self._state[1]
1772 1774 cmd = ['diff', '--no-renames']
1773 1775 if opts[r'stat']:
1774 1776 cmd.append('--stat')
1775 1777 else:
1776 1778 # for Git, this also implies '-p'
1777 1779 cmd.append('-U%d' % diffopts.context)
1778 1780
1779 1781 if diffopts.noprefix:
1780 1782 cmd.extend(['--src-prefix=%s/' % prefix,
1781 1783 '--dst-prefix=%s/' % prefix])
1782 1784 else:
1783 1785 cmd.extend(['--src-prefix=a/%s/' % prefix,
1784 1786 '--dst-prefix=b/%s/' % prefix])
1785 1787
1786 1788 if diffopts.ignorews:
1787 1789 cmd.append('--ignore-all-space')
1788 1790 if diffopts.ignorewsamount:
1789 1791 cmd.append('--ignore-space-change')
1790 1792 if (self._gitversion(self._gitcommand(['--version'])) >= (1, 8, 4)
1791 1793 and diffopts.ignoreblanklines):
1792 1794 cmd.append('--ignore-blank-lines')
1793 1795
1794 1796 cmd.append(node1)
1795 1797 if node2:
1796 1798 cmd.append(node2)
1797 1799
1798 1800 output = ""
1799 1801 if match.always():
1800 1802 output += self._gitcommand(cmd) + '\n'
1801 1803 else:
1802 1804 st = self.status(node2)[:3]
1803 1805 files = [f for sublist in st for f in sublist]
1804 1806 for f in files:
1805 1807 if match(f):
1806 1808 output += self._gitcommand(cmd + ['--', f]) + '\n'
1807 1809
1808 1810 if output.strip():
1809 1811 ui.write(output)
1810 1812
1811 1813 @annotatesubrepoerror
1812 1814 def revert(self, substate, *pats, **opts):
1813 1815 self.ui.status(_('reverting subrepo %s\n') % substate[0])
1814 1816 if not opts.get(r'no_backup'):
1815 1817 status = self.status(None)
1816 1818 names = status.modified
1817 1819 for name in names:
1818 1820 # backuppath() expects a path relative to the parent repo (the
1819 1821 # repo that ui.origbackuppath is relative to)
1820 1822 parentname = os.path.join(self._path, name)
1821 1823 bakname = scmutil.backuppath(self.ui, self._subparent,
1822 1824 parentname)
1823 1825 self.ui.note(_('saving current version of %s as %s\n') %
1824 1826 (name, os.path.relpath(bakname)))
1825 1827 util.rename(self.wvfs.join(name), bakname)
1826 1828
1827 1829 if not opts.get(r'dry_run'):
1828 1830 self.get(substate, overwrite=True)
1829 1831 return []
1830 1832
1831 1833 def shortid(self, revid):
1832 1834 return revid[:7]
1833 1835
1834 1836 types = {
1835 1837 'hg': hgsubrepo,
1836 1838 'svn': svnsubrepo,
1837 1839 'git': gitsubrepo,
1838 1840 }
@@ -1,394 +1,397 b''
1 1 # subrepoutil.py - sub-repository operations and substate handling
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 from __future__ import absolute_import
9 9
10 10 import errno
11 11 import os
12 12 import posixpath
13 13 import re
14 14
15 15 from .i18n import _
16 16 from . import (
17 17 config,
18 18 error,
19 19 filemerge,
20 20 pathutil,
21 21 phases,
22 22 util,
23 23 )
24 24 from .utils import (
25 25 stringutil,
26 26 )
27 27
28 28 nullstate = ('', '', 'empty')
29 29
30 30 def state(ctx, ui):
31 31 """return a state dict, mapping subrepo paths configured in .hgsub
32 32 to tuple: (source from .hgsub, revision from .hgsubstate, kind
33 33 (key in types dict))
34 34 """
35 35 p = config.config()
36 36 repo = ctx.repo()
37 37 def read(f, sections=None, remap=None):
38 38 if f in ctx:
39 39 try:
40 40 data = ctx[f].data()
41 41 except IOError as err:
42 42 if err.errno != errno.ENOENT:
43 43 raise
44 44 # handle missing subrepo spec files as removed
45 45 ui.warn(_("warning: subrepo spec file \'%s\' not found\n") %
46 46 repo.pathto(f))
47 47 return
48 48 p.parse(f, data, sections, remap, read)
49 49 else:
50 50 raise error.Abort(_("subrepo spec file \'%s\' not found") %
51 51 repo.pathto(f))
52 52 if '.hgsub' in ctx:
53 53 read('.hgsub')
54 54
55 55 for path, src in ui.configitems('subpaths'):
56 56 p.set('subpaths', path, src, ui.configsource('subpaths', path))
57 57
58 58 rev = {}
59 59 if '.hgsubstate' in ctx:
60 60 try:
61 61 for i, l in enumerate(ctx['.hgsubstate'].data().splitlines()):
62 62 l = l.lstrip()
63 63 if not l:
64 64 continue
65 65 try:
66 66 revision, path = l.split(" ", 1)
67 67 except ValueError:
68 68 raise error.Abort(_("invalid subrepository revision "
69 69 "specifier in \'%s\' line %d")
70 70 % (repo.pathto('.hgsubstate'), (i + 1)))
71 71 rev[path] = revision
72 72 except IOError as err:
73 73 if err.errno != errno.ENOENT:
74 74 raise
75 75
76 76 def remap(src):
77 77 for pattern, repl in p.items('subpaths'):
78 78 # Turn r'C:\foo\bar' into r'C:\\foo\\bar' since re.sub
79 79 # does a string decode.
80 80 repl = stringutil.escapestr(repl)
81 81 # However, we still want to allow back references to go
82 82 # through unharmed, so we turn r'\\1' into r'\1'. Again,
83 83 # extra escapes are needed because re.sub string decodes.
84 84 repl = re.sub(br'\\\\([0-9]+)', br'\\\1', repl)
85 85 try:
86 86 src = re.sub(pattern, repl, src, 1)
87 87 except re.error as e:
88 88 raise error.Abort(_("bad subrepository pattern in %s: %s")
89 89 % (p.source('subpaths', pattern),
90 90 stringutil.forcebytestr(e)))
91 91 return src
92 92
93 93 state = {}
94 94 for path, src in p[''].items():
95 95 kind = 'hg'
96 96 if src.startswith('['):
97 97 if ']' not in src:
98 98 raise error.Abort(_('missing ] in subrepository source'))
99 99 kind, src = src.split(']', 1)
100 100 kind = kind[1:]
101 101 src = src.lstrip() # strip any extra whitespace after ']'
102 102
103 103 if not util.url(src).isabs():
104 104 parent = _abssource(repo, abort=False)
105 105 if parent:
106 106 parent = util.url(parent)
107 107 parent.path = posixpath.join(parent.path or '', src)
108 108 parent.path = posixpath.normpath(parent.path)
109 109 joined = bytes(parent)
110 110 # Remap the full joined path and use it if it changes,
111 111 # else remap the original source.
112 112 remapped = remap(joined)
113 113 if remapped == joined:
114 114 src = remap(src)
115 115 else:
116 116 src = remapped
117 117
118 118 src = remap(src)
119 119 state[util.pconvert(path)] = (src.strip(), rev.get(path, ''), kind)
120 120
121 121 return state
122 122
123 123 def writestate(repo, state):
124 124 """rewrite .hgsubstate in (outer) repo with these subrepo states"""
125 125 lines = ['%s %s\n' % (state[s][1], s) for s in sorted(state)
126 126 if state[s][1] != nullstate[1]]
127 127 repo.wwrite('.hgsubstate', ''.join(lines), '')
128 128
129 129 def submerge(repo, wctx, mctx, actx, overwrite, labels=None):
130 130 """delegated from merge.applyupdates: merging of .hgsubstate file
131 131 in working context, merging context and ancestor context"""
132 132 if mctx == actx: # backwards?
133 133 actx = wctx.p1()
134 134 s1 = wctx.substate
135 135 s2 = mctx.substate
136 136 sa = actx.substate
137 137 sm = {}
138 138
139 139 repo.ui.debug("subrepo merge %s %s %s\n" % (wctx, mctx, actx))
140 140
141 141 def debug(s, msg, r=""):
142 142 if r:
143 143 r = "%s:%s:%s" % r
144 144 repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r))
145 145
146 146 promptssrc = filemerge.partextras(labels)
147 147 for s, l in sorted(s1.iteritems()):
148 148 a = sa.get(s, nullstate)
149 149 ld = l # local state with possible dirty flag for compares
150 150 if wctx.sub(s).dirty():
151 151 ld = (l[0], l[1] + "+")
152 152 if wctx == actx: # overwrite
153 153 a = ld
154 154
155 155 prompts = promptssrc.copy()
156 156 prompts['s'] = s
157 157 if s in s2:
158 158 r = s2[s]
159 159 if ld == r or r == a: # no change or local is newer
160 160 sm[s] = l
161 161 continue
162 162 elif ld == a: # other side changed
163 163 debug(s, "other changed, get", r)
164 164 wctx.sub(s).get(r, overwrite)
165 165 sm[s] = r
166 166 elif ld[0] != r[0]: # sources differ
167 167 prompts['lo'] = l[0]
168 168 prompts['ro'] = r[0]
169 169 if repo.ui.promptchoice(
170 170 _(' subrepository sources for %(s)s differ\n'
171 'use (l)ocal%(l)s source (%(lo)s)'
172 ' or (r)emote%(o)s source (%(ro)s)?'
171 'you can use (l)ocal%(l)s source (%(lo)s)'
172 ' or (r)emote%(o)s source (%(ro)s).\n'
173 'what do you want to do?'
173 174 '$$ &Local $$ &Remote') % prompts, 0):
174 175 debug(s, "prompt changed, get", r)
175 176 wctx.sub(s).get(r, overwrite)
176 177 sm[s] = r
177 178 elif ld[1] == a[1]: # local side is unchanged
178 179 debug(s, "other side changed, get", r)
179 180 wctx.sub(s).get(r, overwrite)
180 181 sm[s] = r
181 182 else:
182 183 debug(s, "both sides changed")
183 184 srepo = wctx.sub(s)
184 185 prompts['sl'] = srepo.shortid(l[1])
185 186 prompts['sr'] = srepo.shortid(r[1])
186 187 option = repo.ui.promptchoice(
187 188 _(' subrepository %(s)s diverged (local revision: %(sl)s, '
188 189 'remote revision: %(sr)s)\n'
189 '(M)erge, keep (l)ocal%(l)s or keep (r)emote%(o)s?'
190 'you can (m)erge, keep (l)ocal%(l)s or keep '
191 '(r)emote%(o)s.\n'
192 'what do you want to do?'
190 193 '$$ &Merge $$ &Local $$ &Remote')
191 194 % prompts, 0)
192 195 if option == 0:
193 196 wctx.sub(s).merge(r)
194 197 sm[s] = l
195 198 debug(s, "merge with", r)
196 199 elif option == 1:
197 200 sm[s] = l
198 201 debug(s, "keep local subrepo revision", l)
199 202 else:
200 203 wctx.sub(s).get(r, overwrite)
201 204 sm[s] = r
202 205 debug(s, "get remote subrepo revision", r)
203 206 elif ld == a: # remote removed, local unchanged
204 207 debug(s, "remote removed, remove")
205 208 wctx.sub(s).remove()
206 209 elif a == nullstate: # not present in remote or ancestor
207 210 debug(s, "local added, keep")
208 211 sm[s] = l
209 212 continue
210 213 else:
211 214 if repo.ui.promptchoice(
212 215 _(' local%(l)s changed subrepository %(s)s'
213 216 ' which remote%(o)s removed\n'
214 217 'use (c)hanged version or (d)elete?'
215 218 '$$ &Changed $$ &Delete') % prompts, 0):
216 219 debug(s, "prompt remove")
217 220 wctx.sub(s).remove()
218 221
219 222 for s, r in sorted(s2.items()):
220 223 if s in s1:
221 224 continue
222 225 elif s not in sa:
223 226 debug(s, "remote added, get", r)
224 227 mctx.sub(s).get(r)
225 228 sm[s] = r
226 229 elif r != sa[s]:
227 230 prompts = promptssrc.copy()
228 231 prompts['s'] = s
229 232 if repo.ui.promptchoice(
230 233 _(' remote%(o)s changed subrepository %(s)s'
231 234 ' which local%(l)s removed\n'
232 235 'use (c)hanged version or (d)elete?'
233 236 '$$ &Changed $$ &Delete') % prompts, 0) == 0:
234 237 debug(s, "prompt recreate", r)
235 238 mctx.sub(s).get(r)
236 239 sm[s] = r
237 240
238 241 # record merged .hgsubstate
239 242 writestate(repo, sm)
240 243 return sm
241 244
242 245 def precommit(ui, wctx, status, match, force=False):
243 246 """Calculate .hgsubstate changes that should be applied before committing
244 247
245 248 Returns (subs, commitsubs, newstate) where
246 249 - subs: changed subrepos (including dirty ones)
247 250 - commitsubs: dirty subrepos which the caller needs to commit recursively
248 251 - newstate: new state dict which the caller must write to .hgsubstate
249 252
250 253 This also updates the given status argument.
251 254 """
252 255 subs = []
253 256 commitsubs = set()
254 257 newstate = wctx.substate.copy()
255 258
256 259 # only manage subrepos and .hgsubstate if .hgsub is present
257 260 if '.hgsub' in wctx:
258 261 # we'll decide whether to track this ourselves, thanks
259 262 for c in status.modified, status.added, status.removed:
260 263 if '.hgsubstate' in c:
261 264 c.remove('.hgsubstate')
262 265
263 266 # compare current state to last committed state
264 267 # build new substate based on last committed state
265 268 oldstate = wctx.p1().substate
266 269 for s in sorted(newstate.keys()):
267 270 if not match(s):
268 271 # ignore working copy, use old state if present
269 272 if s in oldstate:
270 273 newstate[s] = oldstate[s]
271 274 continue
272 275 if not force:
273 276 raise error.Abort(
274 277 _("commit with new subrepo %s excluded") % s)
275 278 dirtyreason = wctx.sub(s).dirtyreason(True)
276 279 if dirtyreason:
277 280 if not ui.configbool('ui', 'commitsubrepos'):
278 281 raise error.Abort(dirtyreason,
279 282 hint=_("use --subrepos for recursive commit"))
280 283 subs.append(s)
281 284 commitsubs.add(s)
282 285 else:
283 286 bs = wctx.sub(s).basestate()
284 287 newstate[s] = (newstate[s][0], bs, newstate[s][2])
285 288 if oldstate.get(s, (None, None, None))[1] != bs:
286 289 subs.append(s)
287 290
288 291 # check for removed subrepos
289 292 for p in wctx.parents():
290 293 r = [s for s in p.substate if s not in newstate]
291 294 subs += [s for s in r if match(s)]
292 295 if subs:
293 296 if (not match('.hgsub') and
294 297 '.hgsub' in (wctx.modified() + wctx.added())):
295 298 raise error.Abort(_("can't commit subrepos without .hgsub"))
296 299 status.modified.insert(0, '.hgsubstate')
297 300
298 301 elif '.hgsub' in status.removed:
299 302 # clean up .hgsubstate when .hgsub is removed
300 303 if ('.hgsubstate' in wctx and
301 304 '.hgsubstate' not in (status.modified + status.added +
302 305 status.removed)):
303 306 status.removed.insert(0, '.hgsubstate')
304 307
305 308 return subs, commitsubs, newstate
306 309
307 310 def reporelpath(repo):
308 311 """return path to this (sub)repo as seen from outermost repo"""
309 312 parent = repo
310 313 while util.safehasattr(parent, '_subparent'):
311 314 parent = parent._subparent
312 315 return repo.root[len(pathutil.normasprefix(parent.root)):]
313 316
314 317 def subrelpath(sub):
315 318 """return path to this subrepo as seen from outermost repo"""
316 319 return sub._relpath
317 320
318 321 def _abssource(repo, push=False, abort=True):
319 322 """return pull/push path of repo - either based on parent repo .hgsub info
320 323 or on the top repo config. Abort or return None if no source found."""
321 324 if util.safehasattr(repo, '_subparent'):
322 325 source = util.url(repo._subsource)
323 326 if source.isabs():
324 327 return bytes(source)
325 328 source.path = posixpath.normpath(source.path)
326 329 parent = _abssource(repo._subparent, push, abort=False)
327 330 if parent:
328 331 parent = util.url(util.pconvert(parent))
329 332 parent.path = posixpath.join(parent.path or '', source.path)
330 333 parent.path = posixpath.normpath(parent.path)
331 334 return bytes(parent)
332 335 else: # recursion reached top repo
333 336 path = None
334 337 if util.safehasattr(repo, '_subtoppath'):
335 338 path = repo._subtoppath
336 339 elif push and repo.ui.config('paths', 'default-push'):
337 340 path = repo.ui.config('paths', 'default-push')
338 341 elif repo.ui.config('paths', 'default'):
339 342 path = repo.ui.config('paths', 'default')
340 343 elif repo.shared():
341 344 # chop off the .hg component to get the default path form. This has
342 345 # already run through vfsmod.vfs(..., realpath=True), so it doesn't
343 346 # have problems with 'C:'
344 347 return os.path.dirname(repo.sharedpath)
345 348 if path:
346 349 # issue5770: 'C:\' and 'C:' are not equivalent paths. The former is
347 350 # as expected: an absolute path to the root of the C: drive. The
348 351 # latter is a relative path, and works like so:
349 352 #
350 353 # C:\>cd C:\some\path
351 354 # C:\>D:
352 355 # D:\>python -c "import os; print os.path.abspath('C:')"
353 356 # C:\some\path
354 357 #
355 358 # D:\>python -c "import os; print os.path.abspath('C:relative')"
356 359 # C:\some\path\relative
357 360 if util.hasdriveletter(path):
358 361 if len(path) == 2 or path[2:3] not in br'\/':
359 362 path = os.path.abspath(path)
360 363 return path
361 364
362 365 if abort:
363 366 raise error.Abort(_("default path for subrepository not found"))
364 367
365 368 def newcommitphase(ui, ctx):
366 369 commitphase = phases.newcommitphase(ui)
367 370 substate = getattr(ctx, "substate", None)
368 371 if not substate:
369 372 return commitphase
370 373 check = ui.config('phases', 'checksubrepos')
371 374 if check not in ('ignore', 'follow', 'abort'):
372 375 raise error.Abort(_('invalid phases.checksubrepos configuration: %s')
373 376 % (check))
374 377 if check == 'ignore':
375 378 return commitphase
376 379 maxphase = phases.public
377 380 maxsub = None
378 381 for s in sorted(substate):
379 382 sub = ctx.sub(s)
380 383 subphase = sub.phase(substate[s][1])
381 384 if maxphase < subphase:
382 385 maxphase = subphase
383 386 maxsub = s
384 387 if commitphase < maxphase:
385 388 if check == 'abort':
386 389 raise error.Abort(_("can't commit in %s phase"
387 390 " conflicting %s from subrepository %s") %
388 391 (phases.phasenames[commitphase],
389 392 phases.phasenames[maxphase], maxsub))
390 393 ui.warn(_("warning: changes are committed in"
391 394 " %s phase from subrepository %s\n") %
392 395 (phases.phasenames[maxphase], maxsub))
393 396 return maxphase
394 397 return commitphase
@@ -1,795 +1,797 b''
1 1 #require no-reposimplestore
2 2
3 3 This file focuses mainly on updating largefiles in the working
4 4 directory (and ".hg/largefiles/dirstate")
5 5
6 6 $ cat >> $HGRCPATH <<EOF
7 7 > [ui]
8 8 > merge = internal:merge
9 9 > [extensions]
10 10 > largefiles =
11 11 > [extdiff]
12 12 > # for portability:
13 13 > pdiff = sh "$RUNTESTDIR/pdiff"
14 14 > EOF
15 15
16 16 $ hg init repo
17 17 $ cd repo
18 18
19 19 $ echo large1 > large1
20 20 $ echo large2 > large2
21 21 $ hg add --large large1 large2
22 22 $ echo normal1 > normal1
23 23 $ hg add normal1
24 24 $ hg commit -m '#0'
25 25 $ echo 'large1 in #1' > large1
26 26 $ echo 'normal1 in #1' > normal1
27 27 $ hg commit -m '#1'
28 28 $ hg pdiff -r '.^' --config extensions.extdiff=
29 29 diff -Nru repo.0d9d9b8dc9a3/.hglf/large1 repo/.hglf/large1
30 30 --- repo.0d9d9b8dc9a3/.hglf/large1 * (glob)
31 31 +++ repo/.hglf/large1 * (glob)
32 32 @@ -1* +1* @@ (glob)
33 33 -4669e532d5b2c093a78eca010077e708a071bb64
34 34 +58e24f733a964da346e2407a2bee99d9001184f5
35 35 diff -Nru repo.0d9d9b8dc9a3/normal1 repo/normal1
36 36 --- repo.0d9d9b8dc9a3/normal1 * (glob)
37 37 +++ repo/normal1 * (glob)
38 38 @@ -1* +1* @@ (glob)
39 39 -normal1
40 40 +normal1 in #1
41 41 [1]
42 42 $ hg update -q -C 0
43 43 $ echo 'large2 in #2' > large2
44 44 $ hg commit -m '#2'
45 45 created new head
46 46
47 47 Test that update also updates the lfdirstate of 'unsure' largefiles after
48 48 hashing them:
49 49
50 50 The previous operations will usually have left us with largefiles with a mtime
51 51 within the same second as the dirstate was written.
52 52 The lfdirstate entries will thus have been written with an invalidated/unset
53 53 mtime to make sure further changes within the same second is detected.
54 54 We will however occasionally be "lucky" and get a tick between writing
55 55 largefiles and writing dirstate so we get valid lfdirstate timestamps. The
56 56 following verification is thus disabled but can be verified manually.
57 57
58 58 #if false
59 59 $ hg debugdirstate --large --nodate
60 60 n 644 7 unset large1
61 61 n 644 13 unset large2
62 62 #endif
63 63
64 64 Wait to make sure we get a tick so the mtime of the largefiles become valid.
65 65
66 66 $ sleep 1
67 67
68 68 A linear merge will update standins before performing the actual merge. It will
69 69 do a lfdirstate status walk and find 'unset'/'unsure' files, hash them, and
70 70 update the corresponding standins.
71 71 Verify that it actually marks the clean files as clean in lfdirstate so
72 72 we don't have to hash them again next time we update.
73 73
74 74 $ hg up
75 75 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
76 76 updated to "f74e50bd9e55: #2"
77 77 1 other heads for branch "default"
78 78 $ hg debugdirstate --large --nodate
79 79 n 644 7 set large1
80 80 n 644 13 set large2
81 81
82 82 Test that lfdirstate keeps track of last modification of largefiles and
83 83 prevents unnecessary hashing of content - also after linear/noop update
84 84
85 85 $ sleep 1
86 86 $ hg st
87 87 $ hg debugdirstate --large --nodate
88 88 n 644 7 set large1
89 89 n 644 13 set large2
90 90 $ hg up
91 91 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
92 92 updated to "f74e50bd9e55: #2"
93 93 1 other heads for branch "default"
94 94 $ hg debugdirstate --large --nodate
95 95 n 644 7 set large1
96 96 n 644 13 set large2
97 97
98 98 Test that "hg merge" updates largefiles from "other" correctly
99 99
100 100 (getting largefiles from "other" normally)
101 101
102 102 $ hg status -A large1
103 103 C large1
104 104 $ cat large1
105 105 large1
106 106 $ cat .hglf/large1
107 107 4669e532d5b2c093a78eca010077e708a071bb64
108 108 $ hg merge --config debug.dirstate.delaywrite=2
109 109 getting changed largefiles
110 110 1 largefiles updated, 0 removed
111 111 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
112 112 (branch merge, don't forget to commit)
113 113 $ hg status -A large1
114 114 M large1
115 115 $ cat large1
116 116 large1 in #1
117 117 $ cat .hglf/large1
118 118 58e24f733a964da346e2407a2bee99d9001184f5
119 119 $ hg diff -c 1 --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
120 120 -4669e532d5b2c093a78eca010077e708a071bb64
121 121 +58e24f733a964da346e2407a2bee99d9001184f5
122 122
123 123 (getting largefiles from "other" via conflict prompt)
124 124
125 125 $ hg update -q -C 2
126 126 $ echo 'large1 in #3' > large1
127 127 $ echo 'normal1 in #3' > normal1
128 128 $ hg commit -m '#3'
129 129 $ cat .hglf/large1
130 130 e5bb990443d6a92aaf7223813720f7566c9dd05b
131 131 $ hg merge --config debug.dirstate.delaywrite=2 --config ui.interactive=True <<EOF
132 132 > o
133 133 > EOF
134 134 largefile large1 has a merge conflict
135 135 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
136 136 you can keep (l)ocal e5bb990443d6a92aaf7223813720f7566c9dd05b or take (o)ther 58e24f733a964da346e2407a2bee99d9001184f5.
137 137 what do you want to do? o
138 138 merging normal1
139 139 warning: conflicts while merging normal1! (edit, then use 'hg resolve --mark')
140 140 getting changed largefiles
141 141 1 largefiles updated, 0 removed
142 142 0 files updated, 1 files merged, 0 files removed, 1 files unresolved
143 143 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
144 144 [1]
145 145 $ hg status -A large1
146 146 M large1
147 147 $ cat large1
148 148 large1 in #1
149 149 $ cat .hglf/large1
150 150 58e24f733a964da346e2407a2bee99d9001184f5
151 151 $ rm normal1.orig
152 152
153 153 (merge non-existing largefiles from "other" via conflict prompt -
154 154 make sure the following commit doesn't abort in a confusing way when trying to
155 155 mark the non-existing file as normal in lfdirstate)
156 156
157 157 $ mv .hg/largefiles/58e24f733a964da346e2407a2bee99d9001184f5 .
158 158 $ hg update -q -C 3
159 159 $ hg merge --config largefiles.usercache=not --config debug.dirstate.delaywrite=2 --tool :local --config ui.interactive=True <<EOF
160 160 > o
161 161 > EOF
162 162 largefile large1 has a merge conflict
163 163 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
164 164 you can keep (l)ocal e5bb990443d6a92aaf7223813720f7566c9dd05b or take (o)ther 58e24f733a964da346e2407a2bee99d9001184f5.
165 165 what do you want to do? o
166 166 getting changed largefiles
167 167 large1: largefile 58e24f733a964da346e2407a2bee99d9001184f5 not available from file:/*/$TESTTMP/repo (glob)
168 168 0 largefiles updated, 0 removed
169 169 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
170 170 (branch merge, don't forget to commit)
171 171 $ hg commit -m '1-2-3 testing' --config largefiles.usercache=not
172 172 large1: largefile 58e24f733a964da346e2407a2bee99d9001184f5 not available from local store
173 173 $ hg up -C . --config largefiles.usercache=not
174 174 getting changed largefiles
175 175 large1: largefile 58e24f733a964da346e2407a2bee99d9001184f5 not available from file:/*/$TESTTMP/repo (glob)
176 176 0 largefiles updated, 0 removed
177 177 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
178 178 $ hg st large1
179 179 ! large1
180 180 $ hg rollback -q
181 181 $ mv 58e24f733a964da346e2407a2bee99d9001184f5 .hg/largefiles/
182 182
183 183 Test that "hg revert -r REV" updates largefiles from "REV" correctly
184 184
185 185 $ hg update -q -C 3
186 186 $ hg status -A large1
187 187 C large1
188 188 $ cat large1
189 189 large1 in #3
190 190 $ cat .hglf/large1
191 191 e5bb990443d6a92aaf7223813720f7566c9dd05b
192 192 $ hg diff -c 1 --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
193 193 -4669e532d5b2c093a78eca010077e708a071bb64
194 194 +58e24f733a964da346e2407a2bee99d9001184f5
195 195 $ hg revert --no-backup -r 1 --config debug.dirstate.delaywrite=2 large1
196 196 $ hg status -A large1
197 197 M large1
198 198 $ cat large1
199 199 large1 in #1
200 200 $ cat .hglf/large1
201 201 58e24f733a964da346e2407a2bee99d9001184f5
202 202
203 203 Test that "hg rollback" restores status of largefiles correctly
204 204
205 205 $ hg update -C -q
206 206 $ hg remove large1
207 207 $ test -f .hglf/large1
208 208 [1]
209 209 $ hg forget large2
210 210 $ test -f .hglf/large2
211 211 [1]
212 212 $ echo largeX > largeX
213 213 $ hg add --large largeX
214 214 $ cat .hglf/largeX
215 215
216 216 $ hg commit -m 'will be rollback-ed soon'
217 217 $ echo largeY > largeY
218 218 $ hg add --large largeY
219 219
220 220 $ hg status -A large1
221 221 large1: $ENOENT$
222 222
223 223 $ hg status -A large2
224 224 ? large2
225 225 $ hg status -A largeX
226 226 C largeX
227 227 $ hg status -A largeY
228 228 A largeY
229 229 $ hg rollback
230 230 repository tip rolled back to revision 3 (undo commit)
231 231 working directory now based on revision 3
232 232 $ hg status -A large1
233 233 R large1
234 234 $ test -f .hglf/large1
235 235 [1]
236 236 $ hg status -A large2
237 237 R large2
238 238 $ test -f .hglf/large2
239 239 [1]
240 240 $ hg status -A largeX
241 241 A largeX
242 242 $ cat .hglf/largeX
243 243
244 244 $ hg status -A largeY
245 245 ? largeY
246 246 $ test -f .hglf/largeY
247 247 [1]
248 248 $ rm largeY
249 249
250 250 Test that "hg rollback" restores standins correctly
251 251
252 252 $ hg commit -m 'will be rollback-ed soon'
253 253 $ hg update -q -C 2
254 254 $ cat large1
255 255 large1
256 256 $ cat .hglf/large1
257 257 4669e532d5b2c093a78eca010077e708a071bb64
258 258 $ cat large2
259 259 large2 in #2
260 260 $ cat .hglf/large2
261 261 3cfce6277e7668985707b6887ce56f9f62f6ccd9
262 262
263 263 $ hg rollback -q -f
264 264 $ cat large1
265 265 large1
266 266 $ cat .hglf/large1
267 267 4669e532d5b2c093a78eca010077e708a071bb64
268 268 $ cat large2
269 269 large2 in #2
270 270 $ cat .hglf/large2
271 271 3cfce6277e7668985707b6887ce56f9f62f6ccd9
272 272
273 273 (rollback the parent of the working directory, when the parent of it
274 274 is not branch-tip)
275 275
276 276 $ hg update -q -C 1
277 277 $ cat .hglf/large1
278 278 58e24f733a964da346e2407a2bee99d9001184f5
279 279 $ cat .hglf/large2
280 280 1deebade43c8c498a3c8daddac0244dc55d1331d
281 281
282 282 $ echo normalX > normalX
283 283 $ hg add normalX
284 284 $ hg commit -m 'will be rollback-ed soon'
285 285 $ hg rollback -q
286 286
287 287 $ cat .hglf/large1
288 288 58e24f733a964da346e2407a2bee99d9001184f5
289 289 $ cat .hglf/large2
290 290 1deebade43c8c498a3c8daddac0244dc55d1331d
291 291 $ rm normalX
292 292
293 293 Test that "hg status" shows status of largefiles correctly just after
294 294 automated commit like rebase/transplant
295 295
296 296 $ cat >> .hg/hgrc <<EOF
297 297 > [extensions]
298 298 > rebase =
299 299 > strip =
300 300 > transplant =
301 301 > EOF
302 302 $ hg update -q -C 1
303 303 $ hg remove large1
304 304 $ echo largeX > largeX
305 305 $ hg add --large largeX
306 306 $ hg commit -m '#4'
307 307
308 308 $ hg rebase -s 1 -d 2 --keep
309 309 rebasing 1:72518492caa6 "#1"
310 310 rebasing 4:07d6153b5c04 "#4" (tip)
311 311
312 312 $ hg status -A large1
313 313 large1: $ENOENT$
314 314
315 315 $ hg status -A largeX
316 316 C largeX
317 317 $ hg strip -q 5
318 318
319 319 $ hg update -q -C 2
320 320 $ hg transplant -q 1 4
321 321
322 322 $ hg status -A large1
323 323 large1: $ENOENT$
324 324
325 325 $ hg status -A largeX
326 326 C largeX
327 327 $ hg strip -q 5
328 328
329 329 $ hg update -q -C 2
330 330 $ hg transplant -q --merge 1 --merge 4
331 331
332 332 $ hg status -A large1
333 333 large1: $ENOENT$
334 334
335 335 $ hg status -A largeX
336 336 C largeX
337 337 $ hg strip -q 5
338 338
339 339 Test that linear merge can detect modification (and conflict) correctly
340 340
341 341 (linear merge without conflict)
342 342
343 343 $ echo 'large2 for linear merge (no conflict)' > large2
344 344 $ hg update 3 --config debug.dirstate.delaywrite=2
345 345 getting changed largefiles
346 346 1 largefiles updated, 0 removed
347 347 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
348 348 $ hg status -A large2
349 349 M large2
350 350 $ cat large2
351 351 large2 for linear merge (no conflict)
352 352 $ cat .hglf/large2
353 353 9c4bf8f1b33536d6e5f89447e10620cfe52ea710
354 354
355 355 (linear merge with conflict, choosing "other")
356 356
357 357 $ hg update -q -C 2
358 358 $ echo 'large1 for linear merge (conflict)' > large1
359 359 $ hg update 3 --config ui.interactive=True <<EOF
360 360 > o
361 361 > EOF
362 362 largefile large1 has a merge conflict
363 363 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
364 364 you can keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b.
365 365 what do you want to do? o
366 366 getting changed largefiles
367 367 1 largefiles updated, 0 removed
368 368 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
369 369 $ hg status -A large1
370 370 C large1
371 371 $ cat large1
372 372 large1 in #3
373 373 $ cat .hglf/large1
374 374 e5bb990443d6a92aaf7223813720f7566c9dd05b
375 375
376 376 (linear merge with conflict, choosing "local")
377 377
378 378 $ hg update -q -C 2
379 379 $ echo 'large1 for linear merge (conflict)' > large1
380 380 $ hg update 3 --config debug.dirstate.delaywrite=2
381 381 largefile large1 has a merge conflict
382 382 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
383 383 you can keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b.
384 384 what do you want to do? l
385 385 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
386 386 $ hg status -A large1
387 387 M large1
388 388 $ cat large1
389 389 large1 for linear merge (conflict)
390 390 $ cat .hglf/large1
391 391 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
392 392
393 393 Test a linear merge to a revision containing same-name normal file
394 394
395 395 $ hg update -q -C 3
396 396 $ hg remove large2
397 397 $ echo 'large2 as normal file' > large2
398 398 $ hg add large2
399 399 $ echo 'large3 as normal file' > large3
400 400 $ hg add large3
401 401 $ hg commit -m '#5'
402 402 $ hg manifest
403 403 .hglf/large1
404 404 large2
405 405 large3
406 406 normal1
407 407
408 408 (modified largefile is already switched to normal)
409 409
410 410 $ hg update -q -C 2
411 411 $ echo 'modified large2 for linear merge' > large2
412 412 $ hg update -q 5
413 413 remote turned local largefile large2 into a normal file
414 414 keep (l)argefile or use (n)ormal file? l
415 415 $ hg debugdirstate --no-dates | grep large2
416 416 a 0 -1 unset .hglf/large2
417 417 r 0 0 set large2
418 418 $ hg status -A large2
419 419 A large2
420 420 $ cat large2
421 421 modified large2 for linear merge
422 422
423 423 (added largefile is already committed as normal)
424 424
425 425 $ hg update -q -C 2
426 426 $ echo 'large3 as large file for linear merge' > large3
427 427 $ hg add --large large3
428 428 $ hg update -q 5
429 429 remote turned local largefile large3 into a normal file
430 430 keep (l)argefile or use (n)ormal file? l
431 431 $ hg debugdirstate --no-dates | grep large3
432 432 a 0 -1 unset .hglf/large3
433 433 r 0 0 set large3
434 434 $ hg status -A large3
435 435 A large3
436 436 $ cat large3
437 437 large3 as large file for linear merge
438 438 $ rm -f large3 .hglf/large3
439 439
440 440 Test that the internal linear merging works correctly
441 441 (both heads are stripped to keep pairing of revision number and commit log)
442 442
443 443 $ hg update -q -C 2
444 444 $ hg strip 3 4
445 445 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/9530e27857f7-2e7b195d-backup.hg
446 446 $ mv .hg/strip-backup/9530e27857f7-2e7b195d-backup.hg $TESTTMP
447 447
448 448 (internal linear merging at "hg pull --update")
449 449
450 450 $ echo 'large1 for linear merge (conflict)' > large1
451 451 $ echo 'large2 for linear merge (conflict with normal file)' > large2
452 452 $ hg pull --update --config debug.dirstate.delaywrite=2 $TESTTMP/9530e27857f7-2e7b195d-backup.hg
453 453 pulling from $TESTTMP/9530e27857f7-2e7b195d-backup.hg
454 454 searching for changes
455 455 adding changesets
456 456 adding manifests
457 457 adding file changes
458 458 added 3 changesets with 5 changes to 5 files
459 459 new changesets 9530e27857f7:d65e59e952a9 (3 drafts)
460 460 remote turned local largefile large2 into a normal file
461 461 keep (l)argefile or use (n)ormal file? l
462 462 largefile large1 has a merge conflict
463 463 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
464 464 you can keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b.
465 465 what do you want to do? l
466 466 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
467 467 updated to "d65e59e952a9: #5"
468 468 1 other heads for branch "default"
469 469
470 470 $ hg status -A large1
471 471 M large1
472 472 $ cat large1
473 473 large1 for linear merge (conflict)
474 474 $ cat .hglf/large1
475 475 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
476 476 $ hg status -A large2
477 477 A large2
478 478 $ cat large2
479 479 large2 for linear merge (conflict with normal file)
480 480 $ cat .hglf/large2
481 481 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
482 482
483 483 (internal linear merging at "hg unbundle --update")
484 484
485 485 $ hg update -q -C 2
486 486 $ hg rollback -q
487 487
488 488 $ echo 'large1 for linear merge (conflict)' > large1
489 489 $ echo 'large2 for linear merge (conflict with normal file)' > large2
490 490 $ hg unbundle --update --config debug.dirstate.delaywrite=2 $TESTTMP/9530e27857f7-2e7b195d-backup.hg
491 491 adding changesets
492 492 adding manifests
493 493 adding file changes
494 494 added 3 changesets with 5 changes to 5 files
495 495 new changesets 9530e27857f7:d65e59e952a9 (3 drafts)
496 496 remote turned local largefile large2 into a normal file
497 497 keep (l)argefile or use (n)ormal file? l
498 498 largefile large1 has a merge conflict
499 499 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
500 500 you can keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b.
501 501 what do you want to do? l
502 502 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
503 503 updated to "d65e59e952a9: #5"
504 504 1 other heads for branch "default"
505 505
506 506 $ hg status -A large1
507 507 M large1
508 508 $ cat large1
509 509 large1 for linear merge (conflict)
510 510 $ cat .hglf/large1
511 511 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
512 512 $ hg status -A large2
513 513 A large2
514 514 $ cat large2
515 515 large2 for linear merge (conflict with normal file)
516 516 $ cat .hglf/large2
517 517 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
518 518
519 519 (internal linear merging in subrepo at "hg update")
520 520
521 521 $ cd ..
522 522 $ hg init subparent
523 523 $ cd subparent
524 524
525 525 $ hg clone -q -u 2 ../repo sub
526 526 $ cat > .hgsub <<EOF
527 527 > sub = sub
528 528 > EOF
529 529 $ hg add .hgsub
530 530 $ hg commit -m '#0@parent'
531 531 $ cat .hgsubstate
532 532 f74e50bd9e5594b7cf1e6c5cbab86ddd25f3ca2f sub
533 533 $ hg -R sub update -q
534 534 $ hg commit -m '#1@parent'
535 535 $ cat .hgsubstate
536 536 d65e59e952a9638e2ce863b41a420ca723dd3e8d sub
537 537 $ hg update -q 0
538 538
539 539 $ echo 'large1 for linear merge (conflict)' > sub/large1
540 540 $ echo 'large2 for linear merge (conflict with normal file)' > sub/large2
541 541 $ hg update --config ui.interactive=True --config debug.dirstate.delaywrite=2 <<EOF
542 542 > m
543 543 > r
544 544 > l
545 545 > l
546 546 > EOF
547 547 subrepository sub diverged (local revision: f74e50bd9e55, remote revision: d65e59e952a9)
548 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
548 you can (m)erge, keep (l)ocal [working copy] or keep (r)emote [destination].
549 what do you want to do? m
549 550 subrepository sources for sub differ (in checked out version)
550 use (l)ocal source (f74e50bd9e55) or (r)emote source (d65e59e952a9)? r
551 you can use (l)ocal source (f74e50bd9e55) or (r)emote source (d65e59e952a9).
552 what do you want to do? r
551 553 remote turned local largefile large2 into a normal file
552 554 keep (l)argefile or use (n)ormal file? l
553 555 largefile large1 has a merge conflict
554 556 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
555 557 you can keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b.
556 558 what do you want to do? l
557 559 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
558 560 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
559 561
560 562 $ hg -R sub status -A sub/large1
561 563 M sub/large1
562 564 $ cat sub/large1
563 565 large1 for linear merge (conflict)
564 566 $ cat sub/.hglf/large1
565 567 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
566 568 $ hg -R sub status -A sub/large2
567 569 A sub/large2
568 570 $ cat sub/large2
569 571 large2 for linear merge (conflict with normal file)
570 572 $ cat sub/.hglf/large2
571 573 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
572 574
573 575 $ cd ..
574 576 $ cd repo
575 577
576 578 Test that rebase updates largefiles in the working directory even if
577 579 it is aborted by conflict.
578 580
579 581 $ hg update -q -C 3
580 582 $ cat .hglf/large1
581 583 e5bb990443d6a92aaf7223813720f7566c9dd05b
582 584 $ cat large1
583 585 large1 in #3
584 586 $ hg rebase -s 1 -d 3 --keep --config ui.interactive=True <<EOF
585 587 > o
586 588 > EOF
587 589 rebasing 1:72518492caa6 "#1"
588 590 largefile large1 has a merge conflict
589 591 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
590 592 you can keep (l)ocal e5bb990443d6a92aaf7223813720f7566c9dd05b or take (o)ther 58e24f733a964da346e2407a2bee99d9001184f5.
591 593 what do you want to do? o
592 594 merging normal1
593 595 warning: conflicts while merging normal1! (edit, then use 'hg resolve --mark')
594 596 unresolved conflicts (see hg resolve, then hg rebase --continue)
595 597 [1]
596 598 $ cat .hglf/large1
597 599 58e24f733a964da346e2407a2bee99d9001184f5
598 600 $ cat large1
599 601 large1 in #1
600 602 $ rm normal1.orig
601 603
602 604 Test that rebase updates standins for manually modified largefiles at
603 605 the 1st commit of resuming.
604 606
605 607 $ echo "manually modified before 'hg rebase --continue'" > large1
606 608 $ hg resolve -m normal1
607 609 (no more unresolved files)
608 610 continue: hg rebase --continue
609 611 $ hg rebase --continue --config ui.interactive=True <<EOF
610 612 > c
611 613 > EOF
612 614 rebasing 1:72518492caa6 "#1"
613 615 rebasing 4:07d6153b5c04 "#4"
614 616 file '.hglf/large1' was deleted in other [source] but was modified in local [dest].
615 617 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
616 618 What do you want to do? c
617 619
618 620 $ hg diff -c "tip~1" --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
619 621 -e5bb990443d6a92aaf7223813720f7566c9dd05b
620 622 +8a4f783556e7dea21139ca0466eafce954c75c13
621 623 $ rm -f large1
622 624 $ hg update -q -C tip
623 625 $ cat large1
624 626 manually modified before 'hg rebase --continue'
625 627
626 628 Test that transplant updates largefiles, of which standins are safely
627 629 changed, even if it is aborted by conflict of other.
628 630
629 631 $ hg update -q -C 5
630 632 $ cat .hglf/large1
631 633 e5bb990443d6a92aaf7223813720f7566c9dd05b
632 634 $ cat large1
633 635 large1 in #3
634 636 $ hg diff -c 4 .hglf/largeX | grep '^[+-][0-9a-z]'
635 637 +fa44618ea25181aff4f48b70428294790cec9f61
636 638 $ hg transplant 4
637 639 applying 07d6153b5c04
638 640 patching file .hglf/large1
639 641 Hunk #1 FAILED at 0
640 642 1 out of 1 hunks FAILED -- saving rejects to file .hglf/large1.rej
641 643 patch failed to apply
642 644 abort: fix up the working directory and run hg transplant --continue
643 645 [255]
644 646 $ hg status -A large1
645 647 C large1
646 648 $ cat .hglf/large1
647 649 e5bb990443d6a92aaf7223813720f7566c9dd05b
648 650 $ cat large1
649 651 large1 in #3
650 652 $ hg status -A largeX
651 653 A largeX
652 654 $ cat .hglf/largeX
653 655 fa44618ea25181aff4f48b70428294790cec9f61
654 656 $ cat largeX
655 657 largeX
656 658
657 659 Test that transplant updates standins for manually modified largefiles
658 660 at the 1st commit of resuming.
659 661
660 662 $ echo "manually modified before 'hg transplant --continue'" > large1
661 663 $ hg transplant --continue
662 664 07d6153b5c04 transplanted as f1bf30eb88cc
663 665 $ hg diff -c tip .hglf/large1 | grep '^[+-][0-9a-z]'
664 666 -e5bb990443d6a92aaf7223813720f7566c9dd05b
665 667 +6a4f36d4075fbe0f30ec1d26ca44e63c05903671
666 668 $ rm -f large1
667 669 $ hg update -q -C tip
668 670 $ cat large1
669 671 manually modified before 'hg transplant --continue'
670 672
671 673 Test that "hg status" doesn't show removal of largefiles not managed
672 674 in the target context.
673 675
674 676 $ hg update -q -C 4
675 677 $ hg remove largeX
676 678 $ hg status -A largeX
677 679 R largeX
678 680 $ hg status -A --rev '.^1' largeX
679 681
680 682 #if execbit
681 683
682 684 Test that "hg status" against revisions other than parent notices exec
683 685 bit changes of largefiles.
684 686
685 687 $ hg update -q -C 4
686 688
687 689 (the case that large2 doesn't have exec bit in the target context but
688 690 in the working context)
689 691
690 692 $ chmod +x large2
691 693 $ hg status -A --rev 0 large2
692 694 M large2
693 695 $ hg commit -m 'chmod +x large2'
694 696
695 697 (the case that large2 has exec bit in the target context but not in
696 698 the working context)
697 699
698 700 $ echo dummy > dummy
699 701 $ hg add dummy
700 702 $ hg commit -m 'revision for separation'
701 703 $ chmod -x large2
702 704 $ hg status -A --rev '.^1' large2
703 705 M large2
704 706
705 707 #else
706 708
707 709 Test that "hg status" against revisions other than parent ignores exec
708 710 bit correctly on the platform being unaware of it.
709 711
710 712 $ hg update -q -C 4
711 713
712 714 $ cat > ../exec-bit.patch <<EOF
713 715 > # HG changeset patch
714 716 > # User test
715 717 > # Date 0 0
716 718 > # Thu Jan 01 00:00:00 1970 +0000
717 719 > # Node ID be1b433a65b12b27b5519d92213e14f7e1769b90
718 720 > # Parent 07d6153b5c04313efb75deec9ba577de7faeb727
719 721 > chmod +x large2
720 722 >
721 723 > diff --git a/.hglf/large2 b/.hglf/large2
722 724 > old mode 100644
723 725 > new mode 100755
724 726 > EOF
725 727 $ hg import --exact --bypass ../exec-bit.patch
726 728 applying ../exec-bit.patch
727 729 $ hg status -A --rev tip large2
728 730 C large2
729 731
730 732 #endif
731 733
732 734 The fileset revset is evaluated for each revision, instead of once on wdir(),
733 735 and then patterns matched on each revision. Here, no exec bits are set in
734 736 wdir(), but a matching revision is detected.
735 737
736 738 (Teach large2 is not an executable. Maybe this is a bug of largefiles.)
737 739 #if execbit
738 740 $ chmod -x .hglf/large2
739 741 #endif
740 742
741 743 $ hg files 'set:exec()'
742 744 [1]
743 745 $ hg log -qr 'file("set:exec()")'
744 746 9:be1b433a65b1
745 747
746 748 Test a fatal error interrupting an update. Verify that status report dirty
747 749 files correctly after an interrupted update. Also verify that checking all
748 750 hashes reveals it isn't clean.
749 751
750 752 Start with clean dirstates:
751 753 $ hg up --quiet --clean --rev "8^"
752 754 $ sleep 1
753 755 $ hg st
754 756 Update standins without updating largefiles - large1 is modified and largeX is
755 757 added:
756 758 $ cat << EOF > ../crashupdatelfiles.py
757 759 > import hgext.largefiles.lfutil
758 760 > def getlfilestoupdate(oldstandins, newstandins):
759 761 > raise SystemExit(7)
760 762 > hgext.largefiles.lfutil.getlfilestoupdate = getlfilestoupdate
761 763 > EOF
762 764 $ hg up -Cr "8" --config extensions.crashupdatelfiles=../crashupdatelfiles.py
763 765 [7]
764 766 Check large1 content and status ... and that update will undo modifications:
765 767 $ cat large1
766 768 large1 in #3
767 769 $ hg st
768 770 M large1
769 771 ! largeX
770 772 $ hg up -Cr .
771 773 getting changed largefiles
772 774 2 largefiles updated, 0 removed
773 775 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
774 776 $ cat large1
775 777 manually modified before 'hg transplant --continue'
776 778 $ hg st
777 779 Force largefiles rehashing and check that all changes have been caught by
778 780 status and update:
779 781 $ rm .hg/largefiles/dirstate
780 782 $ hg st
781 783
782 784 $ cd ..
783 785
784 786 Test that "hg convert" avoids copying largefiles from the working
785 787 directory into store, because "hg convert" doesn't update largefiles
786 788 in the working directory (removing files under ".cache/largefiles"
787 789 forces "hg convert" to copy corresponding largefiles)
788 790
789 791 $ cat >> $HGRCPATH <<EOF
790 792 > [extensions]
791 793 > convert =
792 794 > EOF
793 795
794 796 $ rm $TESTTMP/.cache/largefiles/6a4f36d4075fbe0f30ec1d26ca44e63c05903671
795 797 $ hg convert -q repo repo.converted
@@ -1,615 +1,616 b''
1 1 $ cat <<EOF >> $HGRCPATH
2 2 > [ui]
3 3 > commitsubrepos = Yes
4 4 > [extensions]
5 5 > mq =
6 6 > record =
7 7 > [diff]
8 8 > nodates = 1
9 9 > EOF
10 10
11 11 $ stdin=`pwd`/stdin.tmp
12 12
13 13 fn to create new repository w/dirty subrepo, and cd into it
14 14 $ mkrepo() {
15 15 > hg init $1
16 16 > cd $1
17 17 > hg qinit
18 18 > }
19 19
20 20 fn to create dirty subrepo
21 21 $ mksubrepo() {
22 22 > hg init $1
23 23 > cd $1
24 24 > echo a > a
25 25 > hg add
26 26 > cd ..
27 27 > }
28 28
29 29 $ testadd() {
30 30 > cat - > "$stdin"
31 31 > mksubrepo sub
32 32 > echo sub = sub >> .hgsub
33 33 > hg add .hgsub
34 34 > echo % abort when adding .hgsub w/dirty subrepo
35 35 > hg status -S
36 36 > echo '%' $*
37 37 > cat "$stdin" | hg $*
38 38 > echo [$?]
39 39 > hg -R sub ci -m0sub
40 40 > echo % update substate when adding .hgsub w/clean updated subrepo
41 41 > hg status -S
42 42 > echo '%' $*
43 43 > cat "$stdin" | hg $*
44 44 > hg debugsub
45 45 > }
46 46
47 47 $ testmod() {
48 48 > cat - > "$stdin"
49 49 > mksubrepo sub2
50 50 > echo sub2 = sub2 >> .hgsub
51 51 > echo % abort when modifying .hgsub w/dirty subrepo
52 52 > hg status -S
53 53 > echo '%' $*
54 54 > cat "$stdin" | hg $*
55 55 > echo [$?]
56 56 > hg -R sub2 ci -m0sub2
57 57 > echo % update substate when modifying .hgsub w/clean updated subrepo
58 58 > hg status -S
59 59 > echo '%' $*
60 60 > cat "$stdin" | hg $*
61 61 > hg debugsub
62 62 > }
63 63
64 64 $ testrm1() {
65 65 > cat - > "$stdin"
66 66 > mksubrepo sub3
67 67 > echo sub3 = sub3 >> .hgsub
68 68 > hg ci -Aqmsub3
69 69 > $EXTRA
70 70 > echo b >> sub3/a
71 71 > hg rm .hgsub
72 72 > echo % update substate when removing .hgsub w/dirty subrepo
73 73 > hg status -S
74 74 > echo '%' $*
75 75 > cat "$stdin" | hg $*
76 76 > echo % debugsub should be empty
77 77 > hg debugsub
78 78 > }
79 79
80 80 $ testrm2() {
81 81 > cat - > "$stdin"
82 82 > mksubrepo sub4
83 83 > echo sub4 = sub4 >> .hgsub
84 84 > hg ci -Aqmsub4
85 85 > $EXTRA
86 86 > hg rm .hgsub
87 87 > echo % update substate when removing .hgsub w/clean updated subrepo
88 88 > hg status -S
89 89 > echo '%' $*
90 90 > cat "$stdin" | hg $*
91 91 > echo % debugsub should be empty
92 92 > hg debugsub
93 93 > }
94 94
95 95
96 96 handle subrepos safely on qnew
97 97
98 98 $ mkrepo repo-2499-qnew
99 99 $ testadd qnew -X path:no-effect -m0 0.diff
100 100 adding a
101 101 % abort when adding .hgsub w/dirty subrepo
102 102 A .hgsub
103 103 A sub/a
104 104 % qnew -X path:no-effect -m0 0.diff
105 105 abort: uncommitted changes in subrepository "sub"
106 106 [255]
107 107 % update substate when adding .hgsub w/clean updated subrepo
108 108 A .hgsub
109 109 A sub/a
110 110 % qnew -X path:no-effect -m0 0.diff
111 111 path sub
112 112 source sub
113 113 revision b2fdb12cd82b021c3b7053d67802e77b6eeaee31
114 114
115 115 $ testmod qnew --cwd .. -R repo-2499-qnew -X path:no-effect -m1 1.diff
116 116 adding a
117 117 % abort when modifying .hgsub w/dirty subrepo
118 118 M .hgsub
119 119 A sub2/a
120 120 % qnew --cwd .. -R repo-2499-qnew -X path:no-effect -m1 1.diff
121 121 abort: uncommitted changes in subrepository "sub2"
122 122 [255]
123 123 % update substate when modifying .hgsub w/clean updated subrepo
124 124 M .hgsub
125 125 A sub2/a
126 126 % qnew --cwd .. -R repo-2499-qnew -X path:no-effect -m1 1.diff
127 127 path sub
128 128 source sub
129 129 revision b2fdb12cd82b021c3b7053d67802e77b6eeaee31
130 130 path sub2
131 131 source sub2
132 132 revision 1f94c7611cc6b74f5a17b16121a1170d44776845
133 133
134 134 $ hg qpop -qa
135 135 patch queue now empty
136 136 $ testrm1 qnew -m2 2.diff
137 137 adding a
138 138 % update substate when removing .hgsub w/dirty subrepo
139 139 M sub3/a
140 140 R .hgsub
141 141 % qnew -m2 2.diff
142 142 % debugsub should be empty
143 143
144 144 $ hg qpop -qa
145 145 patch queue now empty
146 146 $ testrm2 qnew -m3 3.diff
147 147 adding a
148 148 % update substate when removing .hgsub w/clean updated subrepo
149 149 R .hgsub
150 150 % qnew -m3 3.diff
151 151 % debugsub should be empty
152 152
153 153 $ cd ..
154 154
155 155
156 156 handle subrepos safely on qrefresh
157 157
158 158 $ mkrepo repo-2499-qrefresh
159 159 $ hg qnew -m0 0.diff
160 160 $ testadd qrefresh
161 161 adding a
162 162 % abort when adding .hgsub w/dirty subrepo
163 163 A .hgsub
164 164 A sub/a
165 165 % qrefresh
166 166 abort: uncommitted changes in subrepository "sub"
167 167 [255]
168 168 % update substate when adding .hgsub w/clean updated subrepo
169 169 A .hgsub
170 170 A sub/a
171 171 % qrefresh
172 172 path sub
173 173 source sub
174 174 revision b2fdb12cd82b021c3b7053d67802e77b6eeaee31
175 175
176 176 $ hg qnew -m1 1.diff
177 177 $ testmod qrefresh
178 178 adding a
179 179 % abort when modifying .hgsub w/dirty subrepo
180 180 M .hgsub
181 181 A sub2/a
182 182 % qrefresh
183 183 abort: uncommitted changes in subrepository "sub2"
184 184 [255]
185 185 % update substate when modifying .hgsub w/clean updated subrepo
186 186 M .hgsub
187 187 A sub2/a
188 188 % qrefresh
189 189 path sub
190 190 source sub
191 191 revision b2fdb12cd82b021c3b7053d67802e77b6eeaee31
192 192 path sub2
193 193 source sub2
194 194 revision 1f94c7611cc6b74f5a17b16121a1170d44776845
195 195
196 196 $ hg qpop -qa
197 197 patch queue now empty
198 198 $ EXTRA='hg qnew -m2 2.diff'
199 199 $ testrm1 qrefresh
200 200 adding a
201 201 % update substate when removing .hgsub w/dirty subrepo
202 202 M sub3/a
203 203 R .hgsub
204 204 % qrefresh
205 205 % debugsub should be empty
206 206
207 207 $ hg qpop -qa
208 208 patch queue now empty
209 209 $ EXTRA='hg qnew -m3 3.diff'
210 210 $ testrm2 qrefresh
211 211 adding a
212 212 % update substate when removing .hgsub w/clean updated subrepo
213 213 R .hgsub
214 214 % qrefresh
215 215 % debugsub should be empty
216 216 $ EXTRA=
217 217
218 218 $ cd ..
219 219
220 220
221 221 handle subrepos safely on qpush/qpop
222 222 (and we cannot qpop / qpush with a modified subrepo)
223 223
224 224 $ mkrepo repo-2499-qpush
225 225 $ mksubrepo sub
226 226 adding a
227 227 $ hg -R sub ci -m0sub
228 228 $ echo sub = sub > .hgsub
229 229 $ hg add .hgsub
230 230 $ hg commit -m0
231 231 $ hg debugsub
232 232 path sub
233 233 source sub
234 234 revision b2fdb12cd82b021c3b7053d67802e77b6eeaee31
235 235 $ echo foo > ./sub/a
236 236 $ hg -R sub commit -m foo
237 237 $ hg commit -m1
238 238 $ hg qimport -r "0:tip"
239 239 $ hg -R sub id --id
240 240 aa037b301eba
241 241
242 242 qpop
243 243 $ hg -R sub update 0000
244 244 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
245 245 $ hg qpop
246 246 abort: local changed subrepos found, qrefresh first
247 247 [255]
248 248 $ hg revert sub
249 249 reverting subrepo sub
250 250 adding sub/a
251 251 $ hg qpop
252 252 popping 1
253 253 now at: 0
254 254 $ hg status -AS
255 255 C .hgsub
256 256 C .hgsubstate
257 257 C sub/a
258 258 $ hg -R sub id --id
259 259 b2fdb12cd82b
260 260
261 261 qpush
262 262 $ hg -R sub update 0000
263 263 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
264 264 $ hg qpush
265 265 abort: local changed subrepos found, qrefresh first
266 266 [255]
267 267 $ hg revert sub
268 268 reverting subrepo sub
269 269 adding sub/a
270 270 $ hg qpush
271 271 applying 1
272 272 subrepository sub diverged (local revision: b2fdb12cd82b, remote revision: aa037b301eba)
273 (M)erge, keep (l)ocal or keep (r)emote? m
273 you can (m)erge, keep (l)ocal or keep (r)emote.
274 what do you want to do? m
274 275 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
275 276 now at: 1
276 277 $ hg status -AS
277 278 C .hgsub
278 279 C .hgsubstate
279 280 C sub/a
280 281 $ hg -R sub id --id
281 282 aa037b301eba
282 283
283 284 $ cd ..
284 285
285 286
286 287 handle subrepos safely on qrecord
287 288
288 289 $ mkrepo repo-2499-qrecord
289 290 $ testadd qrecord --config ui.interactive=1 -m0 0.diff <<EOF
290 291 > y
291 292 > y
292 293 > EOF
293 294 adding a
294 295 % abort when adding .hgsub w/dirty subrepo
295 296 A .hgsub
296 297 A sub/a
297 298 % qrecord --config ui.interactive=1 -m0 0.diff
298 299 abort: uncommitted changes in subrepository "sub"
299 300 [255]
300 301 % update substate when adding .hgsub w/clean updated subrepo
301 302 A .hgsub
302 303 A sub/a
303 304 % qrecord --config ui.interactive=1 -m0 0.diff
304 305 diff --git a/.hgsub b/.hgsub
305 306 new file mode 100644
306 307 examine changes to '.hgsub'?
307 308 (enter ? for help) [Ynesfdaq?] y
308 309
309 310 @@ -0,0 +1,1 @@
310 311 +sub = sub
311 312 record this change to '.hgsub'?
312 313 (enter ? for help) [Ynesfdaq?] y
313 314
314 315 warning: subrepo spec file '.hgsub' not found
315 316 warning: subrepo spec file '.hgsub' not found
316 317 path sub
317 318 source sub
318 319 revision b2fdb12cd82b021c3b7053d67802e77b6eeaee31
319 320 $ testmod qrecord --config ui.interactive=1 -m1 1.diff <<EOF
320 321 > y
321 322 > y
322 323 > EOF
323 324 adding a
324 325 % abort when modifying .hgsub w/dirty subrepo
325 326 M .hgsub
326 327 A sub2/a
327 328 % qrecord --config ui.interactive=1 -m1 1.diff
328 329 abort: uncommitted changes in subrepository "sub2"
329 330 [255]
330 331 % update substate when modifying .hgsub w/clean updated subrepo
331 332 M .hgsub
332 333 A sub2/a
333 334 % qrecord --config ui.interactive=1 -m1 1.diff
334 335 diff --git a/.hgsub b/.hgsub
335 336 1 hunks, 1 lines changed
336 337 examine changes to '.hgsub'?
337 338 (enter ? for help) [Ynesfdaq?] y
338 339
339 340 @@ -1,1 +1,2 @@
340 341 sub = sub
341 342 +sub2 = sub2
342 343 record this change to '.hgsub'?
343 344 (enter ? for help) [Ynesfdaq?] y
344 345
345 346 path sub
346 347 source sub
347 348 revision b2fdb12cd82b021c3b7053d67802e77b6eeaee31
348 349 path sub2
349 350 source sub2
350 351 revision 1f94c7611cc6b74f5a17b16121a1170d44776845
351 352
352 353 $ hg qpop -qa
353 354 patch queue now empty
354 355 $ testrm1 qrecord --config ui.interactive=1 -m2 2.diff <<EOF
355 356 > y
356 357 > y
357 358 > EOF
358 359 adding a
359 360 % update substate when removing .hgsub w/dirty subrepo
360 361 M sub3/a
361 362 R .hgsub
362 363 % qrecord --config ui.interactive=1 -m2 2.diff
363 364 diff --git a/.hgsub b/.hgsub
364 365 deleted file mode 100644
365 366 examine changes to '.hgsub'?
366 367 (enter ? for help) [Ynesfdaq?] y
367 368
368 369 % debugsub should be empty
369 370
370 371 $ hg qpop -qa
371 372 patch queue now empty
372 373 $ testrm2 qrecord --config ui.interactive=1 -m3 3.diff <<EOF
373 374 > y
374 375 > y
375 376 > EOF
376 377 adding a
377 378 % update substate when removing .hgsub w/clean updated subrepo
378 379 R .hgsub
379 380 % qrecord --config ui.interactive=1 -m3 3.diff
380 381 diff --git a/.hgsub b/.hgsub
381 382 deleted file mode 100644
382 383 examine changes to '.hgsub'?
383 384 (enter ? for help) [Ynesfdaq?] y
384 385
385 386 % debugsub should be empty
386 387
387 388 $ cd ..
388 389
389 390
390 391 correctly handle subrepos with patch queues
391 392 $ mkrepo repo-subrepo-with-queue
392 393 $ mksubrepo sub
393 394 adding a
394 395 $ hg -R sub qnew sub0.diff
395 396 $ echo sub = sub >> .hgsub
396 397 $ hg add .hgsub
397 398 $ hg qnew 0.diff
398 399
399 400 $ cd ..
400 401
401 402 check whether MQ operations can import updated .hgsubstate correctly
402 403 both into 'revision' and 'patch file under .hg/patches':
403 404
404 405 $ hg init importing-hgsubstate
405 406 $ cd importing-hgsubstate
406 407
407 408 $ echo a > a
408 409 $ hg commit -u test -d '0 0' -Am '#0 in parent'
409 410 adding a
410 411 $ hg init sub
411 412 $ echo sa > sub/sa
412 413 $ hg -R sub commit -u test -d '0 0' -Am '#0 in sub'
413 414 adding sa
414 415 $ echo 'sub = sub' > .hgsub
415 416 $ touch .hgsubstate
416 417 $ hg add .hgsub .hgsubstate
417 418
418 419 $ hg qnew -u test -d '0 0' import-at-qnew
419 420 $ hg -R sub parents --template '{node} sub\n'
420 421 b6f6e9c41f3dfd374a6d2ed4535c87951cf979cf sub
421 422 $ cat .hgsubstate
422 423 b6f6e9c41f3dfd374a6d2ed4535c87951cf979cf sub
423 424 $ hg diff -c tip
424 425 diff -r f499373e340c -r f69e96d86e75 .hgsub
425 426 --- /dev/null
426 427 +++ b/.hgsub
427 428 @@ -0,0 +1,1 @@
428 429 +sub = sub
429 430 diff -r f499373e340c -r f69e96d86e75 .hgsubstate
430 431 --- /dev/null
431 432 +++ b/.hgsubstate
432 433 @@ -0,0 +1,1 @@
433 434 +b6f6e9c41f3dfd374a6d2ed4535c87951cf979cf sub
434 435 $ cat .hg/patches/import-at-qnew
435 436 # HG changeset patch
436 437 # User test
437 438 # Date 0 0
438 439 # Parent f499373e340cdca5d01dee904aeb42dd2a325e71
439 440
440 441 diff -r f499373e340c -r f69e96d86e75 .hgsub
441 442 --- /dev/null
442 443 +++ b/.hgsub
443 444 @@ -0,0 +1,1 @@
444 445 +sub = sub
445 446 diff -r f499373e340c -r f69e96d86e75 .hgsubstate
446 447 --- /dev/null
447 448 +++ b/.hgsubstate
448 449 @@ -0,0 +1,1 @@
449 450 +b6f6e9c41f3dfd374a6d2ed4535c87951cf979cf sub
450 451 $ hg parents --template '{node}\n'
451 452 f69e96d86e75a6d4fd88285dc9697acb23951041
452 453 $ hg parents --template '{files}\n'
453 454 .hgsub .hgsubstate
454 455
455 456 check also whether qnew not including ".hgsubstate" explicitly causes
456 457 as same result (in node hash) as one including it.
457 458
458 459 $ hg qpop -a -q
459 460 patch queue now empty
460 461 $ hg qdelete import-at-qnew
461 462 $ echo 'sub = sub' > .hgsub
462 463 $ hg add .hgsub
463 464 $ rm -f .hgsubstate
464 465 $ hg qnew -u test -d '0 0' import-at-qnew
465 466 $ hg parents --template '{node}\n'
466 467 f69e96d86e75a6d4fd88285dc9697acb23951041
467 468 $ hg parents --template '{files}\n'
468 469 .hgsub .hgsubstate
469 470
470 471 check whether qrefresh imports updated .hgsubstate correctly
471 472
472 473 $ hg qpop
473 474 popping import-at-qnew
474 475 patch queue now empty
475 476 $ hg qpush
476 477 applying import-at-qnew
477 478 now at: import-at-qnew
478 479 $ hg parents --template '{files}\n'
479 480 .hgsub .hgsubstate
480 481
481 482 $ hg qnew import-at-qrefresh
482 483 $ echo sb > sub/sb
483 484 $ hg -R sub commit -u test -d '0 0' -Am '#1 in sub'
484 485 adding sb
485 486 $ hg qrefresh -u test -d '0 0'
486 487 $ hg -R sub parents --template '{node} sub\n'
487 488 88ac1bef5ed43b689d1d200b59886b675dec474b sub
488 489 $ cat .hgsubstate
489 490 88ac1bef5ed43b689d1d200b59886b675dec474b sub
490 491 $ hg diff -c tip
491 492 diff -r 05b056bb9c8c -r d987bec230f4 .hgsubstate
492 493 --- a/.hgsubstate
493 494 +++ b/.hgsubstate
494 495 @@ -1,1 +1,1 @@
495 496 -b6f6e9c41f3dfd374a6d2ed4535c87951cf979cf sub
496 497 +88ac1bef5ed43b689d1d200b59886b675dec474b sub
497 498 $ cat .hg/patches/import-at-qrefresh
498 499 # HG changeset patch
499 500 # User test
500 501 # Date 0 0
501 502 # Parent 05b056bb9c8c05ff15258b84fd42ab3527271033
502 503
503 504 diff -r 05b056bb9c8c .hgsubstate
504 505 --- a/.hgsubstate
505 506 +++ b/.hgsubstate
506 507 @@ -1,1 +1,1 @@
507 508 -b6f6e9c41f3dfd374a6d2ed4535c87951cf979cf sub
508 509 +88ac1bef5ed43b689d1d200b59886b675dec474b sub
509 510 $ hg parents --template '{files}\n'
510 511 .hgsubstate
511 512
512 513 $ hg qrefresh -u test -d '0 0'
513 514 $ cat .hgsubstate
514 515 88ac1bef5ed43b689d1d200b59886b675dec474b sub
515 516 $ hg diff -c tip
516 517 diff -r 05b056bb9c8c -r d987bec230f4 .hgsubstate
517 518 --- a/.hgsubstate
518 519 +++ b/.hgsubstate
519 520 @@ -1,1 +1,1 @@
520 521 -b6f6e9c41f3dfd374a6d2ed4535c87951cf979cf sub
521 522 +88ac1bef5ed43b689d1d200b59886b675dec474b sub
522 523 $ cat .hg/patches/import-at-qrefresh
523 524 # HG changeset patch
524 525 # User test
525 526 # Date 0 0
526 527 # Parent 05b056bb9c8c05ff15258b84fd42ab3527271033
527 528
528 529 diff -r 05b056bb9c8c .hgsubstate
529 530 --- a/.hgsubstate
530 531 +++ b/.hgsubstate
531 532 @@ -1,1 +1,1 @@
532 533 -b6f6e9c41f3dfd374a6d2ed4535c87951cf979cf sub
533 534 +88ac1bef5ed43b689d1d200b59886b675dec474b sub
534 535 $ hg parents --template '{files}\n'
535 536 .hgsubstate
536 537
537 538 $ hg update -C tip
538 539 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
539 540 $ hg qpop -a
540 541 popping import-at-qrefresh
541 542 popping import-at-qnew
542 543 patch queue now empty
543 544
544 545 $ hg -R sub update -C 0
545 546 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
546 547 $ echo 'sub = sub' > .hgsub
547 548 $ hg commit -Am '#1 in parent'
548 549 adding .hgsub
549 550 $ hg -R sub update -C 1
550 551 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
551 552 $ hg commit -Am '#2 in parent (but will be rolled back soon)'
552 553 $ hg rollback
553 554 repository tip rolled back to revision 1 (undo commit)
554 555 working directory now based on revision 1
555 556 $ hg status
556 557 M .hgsubstate
557 558 $ hg qnew -u test -d '0 0' checkstate-at-qnew
558 559 $ hg -R sub parents --template '{node} sub\n'
559 560 88ac1bef5ed43b689d1d200b59886b675dec474b sub
560 561 $ cat .hgsubstate
561 562 88ac1bef5ed43b689d1d200b59886b675dec474b sub
562 563 $ hg diff -c tip
563 564 diff -r 4d91eb2fa1d1 -r 1259c112d884 .hgsubstate
564 565 --- a/.hgsubstate
565 566 +++ b/.hgsubstate
566 567 @@ -1,1 +1,1 @@
567 568 -b6f6e9c41f3dfd374a6d2ed4535c87951cf979cf sub
568 569 +88ac1bef5ed43b689d1d200b59886b675dec474b sub
569 570 $ cat .hg/patches/checkstate-at-qnew
570 571 # HG changeset patch
571 572 # User test
572 573 # Date 0 0
573 574 # Parent 4d91eb2fa1d1b22ec513347b9cd06f6b49d470fa
574 575
575 576 diff -r 4d91eb2fa1d1 -r 1259c112d884 .hgsubstate
576 577 --- a/.hgsubstate
577 578 +++ b/.hgsubstate
578 579 @@ -1,1 +1,1 @@
579 580 -b6f6e9c41f3dfd374a6d2ed4535c87951cf979cf sub
580 581 +88ac1bef5ed43b689d1d200b59886b675dec474b sub
581 582 $ hg parents --template '{files}\n'
582 583 .hgsubstate
583 584
584 585 check whether qrefresh not including ".hgsubstate" explicitly causes
585 586 as same result (in node hash) as one including it.
586 587
587 588 $ hg update -C -q 0
588 589 $ hg qpop -a -q
589 590 patch queue now empty
590 591 $ hg qnew -u test -d '0 0' add-hgsub-at-qrefresh
591 592 $ echo 'sub = sub' > .hgsub
592 593 $ echo > .hgsubstate
593 594 $ hg add .hgsub .hgsubstate
594 595 $ hg qrefresh -u test -d '0 0'
595 596 $ hg parents --template '{node}\n'
596 597 7c48c35501aae6770ed9c2517014628615821a8e
597 598 $ hg parents --template '{files}\n'
598 599 .hgsub .hgsubstate
599 600
600 601 $ hg qpop -a -q
601 602 patch queue now empty
602 603 $ hg qdelete add-hgsub-at-qrefresh
603 604 $ hg qnew -u test -d '0 0' add-hgsub-at-qrefresh
604 605 $ echo 'sub = sub' > .hgsub
605 606 $ hg add .hgsub
606 607 $ rm -f .hgsubstate
607 608 $ hg qrefresh -u test -d '0 0'
608 609 $ hg parents --template '{node}\n'
609 610 7c48c35501aae6770ed9c2517014628615821a8e
610 611 $ hg parents --template '{files}\n'
611 612 .hgsub .hgsubstate
612 613
613 614 $ cd ..
614 615
615 616 $ cd ..
@@ -1,1255 +1,1262 b''
1 1 #require git
2 2
3 3 make git commits repeatable
4 4
5 5 $ cat >> $HGRCPATH <<EOF
6 6 > [defaults]
7 7 > commit = -d "0 0"
8 8 > EOF
9 9
10 10 $ echo "[core]" >> $HOME/.gitconfig
11 11 $ echo "autocrlf = false" >> $HOME/.gitconfig
12 12 $ GIT_AUTHOR_NAME='test'; export GIT_AUTHOR_NAME
13 13 $ GIT_AUTHOR_EMAIL='test@example.org'; export GIT_AUTHOR_EMAIL
14 14 $ GIT_AUTHOR_DATE='1234567891 +0000'; export GIT_AUTHOR_DATE
15 15 $ GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; export GIT_COMMITTER_NAME
16 16 $ GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"; export GIT_COMMITTER_EMAIL
17 17 $ GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"; export GIT_COMMITTER_DATE
18 18 $ GIT_CONFIG_NOSYSTEM=1; export GIT_CONFIG_NOSYSTEM
19 19
20 20 root hg repo
21 21
22 22 $ hg init t
23 23 $ cd t
24 24 $ echo a > a
25 25 $ hg add a
26 26 $ hg commit -m a
27 27 $ cd ..
28 28
29 29 new external git repo
30 30
31 31 $ mkdir gitroot
32 32 $ cd gitroot
33 33 $ git init -q
34 34 $ echo g > g
35 35 $ git add g
36 36 $ git commit -q -m g
37 37
38 38 add subrepo clone
39 39
40 40 $ cd ../t
41 41 $ echo 's = [git]../gitroot' > .hgsub
42 42 $ git clone -q ../gitroot s
43 43 $ hg add .hgsub
44 44
45 45 git subrepo is disabled by default
46 46
47 47 $ hg commit -m 'new git subrepo'
48 48 abort: git subrepos not allowed
49 49 (see 'hg help config.subrepos' for details)
50 50 [255]
51 51
52 52 so enable it
53 53
54 54 $ cat >> $HGRCPATH <<EOF
55 55 > [subrepos]
56 56 > git:allowed = true
57 57 > EOF
58 58
59 59 $ hg commit -m 'new git subrepo'
60 60
61 61 $ hg debugsub
62 62 path s
63 63 source ../gitroot
64 64 revision da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
65 65
66 66 record a new commit from upstream from a different branch
67 67
68 68 $ cd ../gitroot
69 69 $ git checkout -q -b testing
70 70 $ echo gg >> g
71 71 $ git commit -q -a -m gg
72 72
73 73 $ cd ../t/s
74 74 $ git pull -q >/dev/null 2>/dev/null
75 75 $ git checkout -q -b testing origin/testing >/dev/null
76 76
77 77 $ cd ..
78 78 $ hg status --subrepos
79 79 M s/g
80 80 $ hg commit -m 'update git subrepo'
81 81 $ hg debugsub
82 82 path s
83 83 source ../gitroot
84 84 revision 126f2a14290cd5ce061fdedc430170e8d39e1c5a
85 85
86 86 make $GITROOT pushable, by replacing it with a clone with nothing checked out
87 87
88 88 $ cd ..
89 89 $ git clone gitroot gitrootbare --bare -q
90 90 $ rm -rf gitroot
91 91 $ mv gitrootbare gitroot
92 92
93 93 clone root
94 94
95 95 $ cd t
96 96 $ hg clone . ../tc 2> /dev/null
97 97 updating to branch default
98 98 cloning subrepo s from $TESTTMP/gitroot
99 99 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
100 100 $ cd ../tc
101 101 $ hg debugsub
102 102 path s
103 103 source ../gitroot
104 104 revision 126f2a14290cd5ce061fdedc430170e8d39e1c5a
105 105 $ cd ..
106 106
107 107 clone with subrepo disabled (update should fail)
108 108
109 109 $ hg clone t -U tc2 --config subrepos.allowed=false
110 110 $ hg update -R tc2 --config subrepos.allowed=false
111 111 abort: subrepos not enabled
112 112 (see 'hg help config.subrepos' for details)
113 113 [255]
114 114 $ ls tc2
115 115 a
116 116
117 117 $ hg clone t tc3 --config subrepos.allowed=false
118 118 updating to branch default
119 119 abort: subrepos not enabled
120 120 (see 'hg help config.subrepos' for details)
121 121 [255]
122 122 $ ls tc3
123 123 a
124 124
125 125 update to previous substate
126 126
127 127 $ cd tc
128 128 $ hg update 1 -q
129 129 $ cat s/g
130 130 g
131 131 $ hg debugsub
132 132 path s
133 133 source ../gitroot
134 134 revision da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
135 135
136 136 clone root, make local change
137 137
138 138 $ cd ../t
139 139 $ hg clone . ../ta 2> /dev/null
140 140 updating to branch default
141 141 cloning subrepo s from $TESTTMP/gitroot
142 142 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
143 143
144 144 $ cd ../ta
145 145 $ echo ggg >> s/g
146 146 $ hg status --subrepos
147 147 M s/g
148 148 $ hg diff --subrepos
149 149 diff --git a/s/g b/s/g
150 150 index 089258f..85341ee 100644
151 151 --- a/s/g
152 152 +++ b/s/g
153 153 @@ -1,2 +1,3 @@
154 154 g
155 155 gg
156 156 +ggg
157 157 $ hg commit --subrepos -m ggg
158 158 committing subrepository s
159 159 $ hg debugsub
160 160 path s
161 161 source ../gitroot
162 162 revision 79695940086840c99328513acbe35f90fcd55e57
163 163
164 164 clone root separately, make different local change
165 165
166 166 $ cd ../t
167 167 $ hg clone . ../tb 2> /dev/null
168 168 updating to branch default
169 169 cloning subrepo s from $TESTTMP/gitroot
170 170 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
171 171
172 172 $ cd ../tb/s
173 173 $ hg status --subrepos
174 174 $ echo f > f
175 175 $ hg status --subrepos
176 176 ? s/f
177 177 $ hg add .
178 178 adding f
179 179 $ git add f
180 180 $ cd ..
181 181
182 182 $ hg status --subrepos
183 183 A s/f
184 184 $ hg commit --subrepos -m f
185 185 committing subrepository s
186 186 $ hg debugsub
187 187 path s
188 188 source ../gitroot
189 189 revision aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
190 190
191 191 user b push changes
192 192
193 193 $ hg push 2>/dev/null
194 194 pushing to $TESTTMP/t
195 195 pushing branch testing of subrepository "s"
196 196 searching for changes
197 197 adding changesets
198 198 adding manifests
199 199 adding file changes
200 200 added 1 changesets with 1 changes to 1 files
201 201
202 202 user a pulls, merges, commits
203 203
204 204 $ cd ../ta
205 205 $ hg pull
206 206 pulling from $TESTTMP/t
207 207 searching for changes
208 208 adding changesets
209 209 adding manifests
210 210 adding file changes
211 211 added 1 changesets with 1 changes to 1 files (+1 heads)
212 212 new changesets 089416c11d73
213 213 (run 'hg heads' to see heads, 'hg merge' to merge)
214 214 $ hg merge 2>/dev/null
215 215 subrepository s diverged (local revision: 7969594, remote revision: aa84837)
216 (M)erge, keep (l)ocal [working copy] or keep (r)emote [merge rev]? m
216 you can (m)erge, keep (l)ocal [working copy] or keep (r)emote [merge rev].
217 what do you want to do? m
217 218 pulling subrepo s from $TESTTMP/gitroot
218 219 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
219 220 (branch merge, don't forget to commit)
220 221 $ hg st --subrepos s
221 222 A s/f
222 223 $ cat s/f
223 224 f
224 225 $ cat s/g
225 226 g
226 227 gg
227 228 ggg
228 229 $ hg commit --subrepos -m 'merge'
229 230 committing subrepository s
230 231 $ hg status --subrepos --rev 1:5
231 232 M .hgsubstate
232 233 M s/g
233 234 A s/f
234 235 $ hg debugsub
235 236 path s
236 237 source ../gitroot
237 238 revision f47b465e1bce645dbf37232a00574aa1546ca8d3
238 239 $ hg push 2>/dev/null
239 240 pushing to $TESTTMP/t
240 241 pushing branch testing of subrepository "s"
241 242 searching for changes
242 243 adding changesets
243 244 adding manifests
244 245 adding file changes
245 246 added 2 changesets with 2 changes to 1 files
246 247
247 248 make upstream git changes
248 249
249 250 $ cd ..
250 251 $ git clone -q gitroot gitclone
251 252 $ cd gitclone
252 253 $ echo ff >> f
253 254 $ git commit -q -a -m ff
254 255 $ echo fff >> f
255 256 $ git commit -q -a -m fff
256 257 $ git push origin testing 2>/dev/null
257 258
258 259 make and push changes to hg without updating the subrepo
259 260
260 261 $ cd ../t
261 262 $ hg clone . ../td 2>&1 | egrep -v '^Cloning into|^done\.'
262 263 updating to branch default
263 264 cloning subrepo s from $TESTTMP/gitroot
264 265 checking out detached HEAD in subrepository "s"
265 266 check out a git branch if you intend to make changes
266 267 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
267 268 $ cd ../td
268 269 $ echo aa >> a
269 270 $ hg commit -m aa
270 271 $ hg push
271 272 pushing to $TESTTMP/t
272 273 searching for changes
273 274 adding changesets
274 275 adding manifests
275 276 adding file changes
276 277 added 1 changesets with 1 changes to 1 files
277 278
278 279 sync to upstream git, distribute changes
279 280
280 281 $ cd ../ta
281 282 $ hg pull -u -q
282 283 $ cd s
283 284 $ git pull -q >/dev/null 2>/dev/null
284 285 $ cd ..
285 286 $ hg commit -m 'git upstream sync'
286 287 $ hg debugsub
287 288 path s
288 289 source ../gitroot
289 290 revision 32a343883b74769118bb1d3b4b1fbf9156f4dddc
290 291 $ hg push -q
291 292
292 293 $ cd ../tb
293 294 $ hg pull -q
294 295 $ hg update 2>/dev/null
295 296 pulling subrepo s from $TESTTMP/gitroot
296 297 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
297 298 $ hg debugsub
298 299 path s
299 300 source ../gitroot
300 301 revision 32a343883b74769118bb1d3b4b1fbf9156f4dddc
301 302
302 303 create a new git branch
303 304
304 305 $ cd s
305 306 $ git checkout -b b2
306 307 Switched to a new branch 'b2'
307 308 $ echo a>a
308 309 $ git add a
309 310 $ git commit -qm 'add a'
310 311 $ cd ..
311 312 $ hg commit -m 'add branch in s'
312 313
313 314 pulling new git branch should not create tracking branch named 'origin/b2'
314 315 (issue3870)
315 316 $ cd ../td/s
316 317 $ git remote set-url origin $TESTTMP/tb/s
317 318 $ git branch --no-track oldtesting
318 319 $ cd ..
319 320 $ hg pull -q ../tb
320 321 $ hg up
321 322 From $TESTTMP/tb/s
322 323 * [new branch] b2 -> origin/b2
323 324 Previous HEAD position was f47b465* merge (glob)
324 325 Switched to a new branch 'b2'
325 326 pulling subrepo s from $TESTTMP/tb/s
326 327 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
327 328
328 329 update to a revision without the subrepo, keeping the local git repository
329 330
330 331 $ cd ../t
331 332 $ hg up 0
332 333 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
333 334 $ ls -a s
334 335 .
335 336 ..
336 337 .git
337 338
338 339 $ hg up 2
339 340 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
340 341 $ ls -a s
341 342 .
342 343 ..
343 344 .git
344 345 g
345 346
346 347 archive subrepos
347 348
348 349 $ cd ../tc
349 350 $ hg pull -q
350 351 $ hg archive --subrepos -r 5 ../archive 2>/dev/null
351 352 pulling subrepo s from $TESTTMP/gitroot
352 353 $ cd ../archive
353 354 $ cat s/f
354 355 f
355 356 $ cat s/g
356 357 g
357 358 gg
358 359 ggg
359 360
360 361 $ hg -R ../tc archive --subrepo -r 5 -X ../tc/**f ../archive_x 2>/dev/null
361 362 $ find ../archive_x | sort | grep -v pax_global_header
362 363 ../archive_x
363 364 ../archive_x/.hg_archival.txt
364 365 ../archive_x/.hgsub
365 366 ../archive_x/.hgsubstate
366 367 ../archive_x/a
367 368 ../archive_x/s
368 369 ../archive_x/s/g
369 370
370 371 $ hg -R ../tc archive -S ../archive.tgz --prefix '.' 2>/dev/null
371 372 $ tar -tzf ../archive.tgz | sort | grep -v pax_global_header
372 373 .hg_archival.txt
373 374 .hgsub
374 375 .hgsubstate
375 376 a
376 377 s/g
377 378
378 379 create nested repo
379 380
380 381 $ cd ..
381 382 $ hg init outer
382 383 $ cd outer
383 384 $ echo b>b
384 385 $ hg add b
385 386 $ hg commit -m b
386 387
387 388 $ hg clone ../t inner 2> /dev/null
388 389 updating to branch default
389 390 cloning subrepo s from $TESTTMP/gitroot
390 391 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
391 392 $ echo inner = inner > .hgsub
392 393 $ hg add .hgsub
393 394 $ hg commit -m 'nested sub'
394 395
395 396 nested commit
396 397
397 398 $ echo ffff >> inner/s/f
398 399 $ hg status --subrepos
399 400 M inner/s/f
400 401 $ hg commit --subrepos -m nested
401 402 committing subrepository inner
402 403 committing subrepository inner/s
403 404
404 405 nested archive
405 406
406 407 $ hg archive --subrepos ../narchive
407 408 $ ls ../narchive/inner/s | grep -v pax_global_header
408 409 f
409 410 g
410 411
411 412 relative source expansion
412 413
413 414 $ cd ..
414 415 $ mkdir d
415 416 $ hg clone t d/t 2> /dev/null
416 417 updating to branch default
417 418 cloning subrepo s from $TESTTMP/gitroot
418 419 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
419 420
420 421 Don't crash if the subrepo is missing
421 422
422 423 $ hg clone t missing -q
423 424 $ cd missing
424 425 $ rm -rf s
425 426 $ hg status -S
426 427 $ hg sum | grep commit
427 428 commit: 1 subrepos
428 429 $ hg push -q
429 430 abort: subrepo s is missing (in subrepository "s")
430 431 [255]
431 432 $ hg commit --subrepos -qm missing
432 433 abort: subrepo s is missing (in subrepository "s")
433 434 [255]
434 435
435 436 #if symlink
436 437 Don't crash if subrepo is a broken symlink
437 438 $ ln -s broken s
438 439 $ hg status -S
439 440 abort: subrepo 's' traverses symbolic link
440 441 [255]
441 442 $ hg push -q
442 443 abort: subrepo 's' traverses symbolic link
443 444 [255]
444 445 $ hg commit --subrepos -qm missing
445 446 abort: subrepo 's' traverses symbolic link
446 447 [255]
447 448 $ rm s
448 449 #endif
449 450
450 451 $ hg update -C 2> /dev/null
451 452 cloning subrepo s from $TESTTMP/gitroot
452 453 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
453 454 $ hg sum | grep commit
454 455 commit: (clean)
455 456
456 457 Don't crash if the .hgsubstate entry is missing
457 458
458 459 $ hg update 1 -q
459 460 $ hg rm .hgsubstate
460 461 $ hg commit .hgsubstate -m 'no substate'
461 462 nothing changed
462 463 [1]
463 464 $ hg tag -l nosubstate
464 465 $ hg manifest
465 466 .hgsub
466 467 .hgsubstate
467 468 a
468 469
469 470 $ hg status -S
470 471 R .hgsubstate
471 472 $ hg sum | grep commit
472 473 commit: 1 removed, 1 subrepos (new branch head)
473 474
474 475 $ hg commit -m 'restore substate'
475 476 nothing changed
476 477 [1]
477 478 $ hg manifest
478 479 .hgsub
479 480 .hgsubstate
480 481 a
481 482 $ hg sum | grep commit
482 483 commit: 1 removed, 1 subrepos (new branch head)
483 484
484 485 $ hg update -qC nosubstate
485 486 $ ls s
486 487 g
487 488
488 489 issue3109: false positives in git diff-index
489 490
490 491 $ hg update -q
491 492 $ touch -t 200001010000 s/g
492 493 $ hg status --subrepos
493 494 $ touch -t 200001010000 s/g
494 495 $ hg sum | grep commit
495 496 commit: (clean)
496 497
497 498 Check hg update --clean
498 499 $ cd $TESTTMP/ta
499 500 $ echo > s/g
500 501 $ cd s
501 502 $ echo c1 > f1
502 503 $ echo c1 > f2
503 504 $ git add f1
504 505 $ cd ..
505 506 $ hg status -S
506 507 M s/g
507 508 A s/f1
508 509 ? s/f2
509 510 $ ls s
510 511 f
511 512 f1
512 513 f2
513 514 g
514 515 $ hg update --clean
515 516 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
516 517 $ hg status -S
517 518 ? s/f1
518 519 ? s/f2
519 520 $ ls s
520 521 f
521 522 f1
522 523 f2
523 524 g
524 525
525 526 Sticky subrepositories, no changes
526 527 $ cd $TESTTMP/ta
527 528 $ hg id -n
528 529 7
529 530 $ cd s
530 531 $ git rev-parse HEAD
531 532 32a343883b74769118bb1d3b4b1fbf9156f4dddc
532 533 $ cd ..
533 534 $ hg update 1 > /dev/null 2>&1
534 535 $ hg id -n
535 536 1
536 537 $ cd s
537 538 $ git rev-parse HEAD
538 539 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
539 540 $ cd ..
540 541
541 542 Sticky subrepositories, file changes
542 543 $ touch s/f1
543 544 $ cd s
544 545 $ git add f1
545 546 $ cd ..
546 547 $ hg id -n
547 548 1+
548 549 $ cd s
549 550 $ git rev-parse HEAD
550 551 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
551 552 $ cd ..
552 553 $ hg update 4
553 554 subrepository s diverged (local revision: da5f5b1, remote revision: aa84837)
554 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
555 you can (m)erge, keep (l)ocal [working copy] or keep (r)emote [destination].
556 what do you want to do? m
555 557 subrepository sources for s differ
556 use (l)ocal source (da5f5b1) or (r)emote source (aa84837)? l
558 you can use (l)ocal source (da5f5b1) or (r)emote source (aa84837).
559 what do you want to do? l
557 560 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
558 561 $ hg id -n
559 562 4+
560 563 $ cd s
561 564 $ git rev-parse HEAD
562 565 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
563 566 $ cd ..
564 567 $ hg update --clean tip > /dev/null 2>&1
565 568
566 569 Sticky subrepository, revision updates
567 570 $ hg id -n
568 571 7
569 572 $ cd s
570 573 $ git rev-parse HEAD
571 574 32a343883b74769118bb1d3b4b1fbf9156f4dddc
572 575 $ cd ..
573 576 $ cd s
574 577 $ git checkout aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
575 578 Previous HEAD position was 32a3438* fff (glob)
576 579 HEAD is now at aa84837* f (glob)
577 580 $ cd ..
578 581 $ hg update 1
579 582 subrepository s diverged (local revision: 32a3438, remote revision: da5f5b1)
580 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
583 you can (m)erge, keep (l)ocal [working copy] or keep (r)emote [destination].
584 what do you want to do? m
581 585 subrepository sources for s differ (in checked out version)
582 use (l)ocal source (32a3438) or (r)emote source (da5f5b1)? l
586 you can use (l)ocal source (32a3438) or (r)emote source (da5f5b1).
587 what do you want to do? l
583 588 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
584 589 $ hg id -n
585 590 1+
586 591 $ cd s
587 592 $ git rev-parse HEAD
588 593 aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
589 594 $ cd ..
590 595
591 596 Sticky subrepository, file changes and revision updates
592 597 $ touch s/f1
593 598 $ cd s
594 599 $ git add f1
595 600 $ git rev-parse HEAD
596 601 aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
597 602 $ cd ..
598 603 $ hg id -n
599 604 1+
600 605 $ hg update 7
601 606 subrepository s diverged (local revision: 32a3438, remote revision: 32a3438)
602 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
607 you can (m)erge, keep (l)ocal [working copy] or keep (r)emote [destination].
608 what do you want to do? m
603 609 subrepository sources for s differ
604 use (l)ocal source (32a3438) or (r)emote source (32a3438)? l
610 you can use (l)ocal source (32a3438) or (r)emote source (32a3438).
611 what do you want to do? l
605 612 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
606 613 $ hg id -n
607 614 7+
608 615 $ cd s
609 616 $ git rev-parse HEAD
610 617 aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
611 618 $ cd ..
612 619
613 620 Sticky repository, update --clean
614 621 $ hg update --clean tip 2>/dev/null
615 622 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
616 623 $ hg id -n
617 624 7
618 625 $ cd s
619 626 $ git rev-parse HEAD
620 627 32a343883b74769118bb1d3b4b1fbf9156f4dddc
621 628 $ cd ..
622 629
623 630 Test subrepo already at intended revision:
624 631 $ cd s
625 632 $ git checkout 32a343883b74769118bb1d3b4b1fbf9156f4dddc
626 633 HEAD is now at 32a3438* fff (glob)
627 634 $ cd ..
628 635 $ hg update 1
629 636 Previous HEAD position was 32a3438* fff (glob)
630 637 HEAD is now at da5f5b1* g (glob)
631 638 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
632 639 $ hg id -n
633 640 1
634 641 $ cd s
635 642 $ git rev-parse HEAD
636 643 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
637 644 $ cd ..
638 645
639 646 Test forgetting files, not implemented in git subrepo, used to
640 647 traceback
641 648 #if no-windows
642 649 $ hg forget 'notafile*'
643 650 notafile*: $ENOENT$
644 651 [1]
645 652 #else
646 653 error: The filename, directory name, or volume label syntax is incorrect
647 654 $ hg forget 'notafile'
648 655 notafile: * (glob)
649 656 [1]
650 657 #endif
651 658
652 659 $ cd ..
653 660
654 661 Test sanitizing ".hg/hgrc" in subrepo
655 662
656 663 $ cd t
657 664 $ hg tip -q
658 665 7:af6d2edbb0d3
659 666 $ hg update -q -C af6d2edbb0d3
660 667 $ cd s
661 668 $ git checkout -q -b sanitize-test
662 669 $ mkdir .hg
663 670 $ echo '.hg/hgrc in git repo' > .hg/hgrc
664 671 $ mkdir -p sub/.hg
665 672 $ echo 'sub/.hg/hgrc in git repo' > sub/.hg/hgrc
666 673 $ git add .hg sub
667 674 $ git commit -qm 'add .hg/hgrc to be sanitized at hg update'
668 675 $ git push -q origin sanitize-test
669 676 $ cd ..
670 677 $ grep ' s$' .hgsubstate
671 678 32a343883b74769118bb1d3b4b1fbf9156f4dddc s
672 679 $ hg commit -qm 'commit with git revision including .hg/hgrc'
673 680 $ hg parents -q
674 681 8:3473d20bddcf
675 682 $ grep ' s$' .hgsubstate
676 683 c4069473b459cf27fd4d7c2f50c4346b4e936599 s
677 684 $ cd ..
678 685
679 686 $ hg -R tc pull -q
680 687 $ hg -R tc update -q -C 3473d20bddcf 2>&1 | sort
681 688 warning: removing potentially hostile 'hgrc' in '$TESTTMP/tc/s/.hg'
682 689 warning: removing potentially hostile 'hgrc' in '$TESTTMP/tc/s/sub/.hg'
683 690 $ cd tc
684 691 $ hg parents -q
685 692 8:3473d20bddcf
686 693 $ grep ' s$' .hgsubstate
687 694 c4069473b459cf27fd4d7c2f50c4346b4e936599 s
688 695 $ test -f s/.hg/hgrc
689 696 [1]
690 697 $ test -f s/sub/.hg/hgrc
691 698 [1]
692 699 $ cd ..
693 700
694 701 additional test for "git merge --ff" route:
695 702
696 703 $ cd t
697 704 $ hg tip -q
698 705 8:3473d20bddcf
699 706 $ hg update -q -C af6d2edbb0d3
700 707 $ cd s
701 708 $ git checkout -q testing
702 709 $ mkdir .hg
703 710 $ echo '.hg/hgrc in git repo' > .hg/hgrc
704 711 $ mkdir -p sub/.hg
705 712 $ echo 'sub/.hg/hgrc in git repo' > sub/.hg/hgrc
706 713 $ git add .hg sub
707 714 $ git commit -qm 'add .hg/hgrc to be sanitized at hg update (git merge --ff)'
708 715 $ git push -q origin testing
709 716 $ cd ..
710 717 $ grep ' s$' .hgsubstate
711 718 32a343883b74769118bb1d3b4b1fbf9156f4dddc s
712 719 $ hg commit -qm 'commit with git revision including .hg/hgrc'
713 720 $ hg parents -q
714 721 9:ed23f7fe024e
715 722 $ grep ' s$' .hgsubstate
716 723 f262643c1077219fbd3858d54e78ef050ef84fbf s
717 724 $ cd ..
718 725
719 726 $ cd tc
720 727 $ hg update -q -C af6d2edbb0d3
721 728 $ test -f s/.hg/hgrc
722 729 [1]
723 730 $ test -f s/sub/.hg/hgrc
724 731 [1]
725 732 $ cd ..
726 733 $ hg -R tc pull -q
727 734 $ hg -R tc update -q -C ed23f7fe024e 2>&1 | sort
728 735 warning: removing potentially hostile 'hgrc' in '$TESTTMP/tc/s/.hg'
729 736 warning: removing potentially hostile 'hgrc' in '$TESTTMP/tc/s/sub/.hg'
730 737 $ cd tc
731 738 $ hg parents -q
732 739 9:ed23f7fe024e
733 740 $ grep ' s$' .hgsubstate
734 741 f262643c1077219fbd3858d54e78ef050ef84fbf s
735 742 $ test -f s/.hg/hgrc
736 743 [1]
737 744 $ test -f s/sub/.hg/hgrc
738 745 [1]
739 746
740 747 Test that sanitizing is omitted in meta data area:
741 748
742 749 $ mkdir s/.git/.hg
743 750 $ echo '.hg/hgrc in git metadata area' > s/.git/.hg/hgrc
744 751 $ hg update -q -C af6d2edbb0d3
745 752 checking out detached HEAD in subrepository "s"
746 753 check out a git branch if you intend to make changes
747 754
748 755 check differences made by most recent change
749 756 $ cd s
750 757 $ cat > foobar << EOF
751 758 > woopwoop
752 759 >
753 760 > foo
754 761 > bar
755 762 > EOF
756 763 $ git add foobar
757 764 $ cd ..
758 765
759 766 $ hg diff --subrepos
760 767 diff --git a/s/foobar b/s/foobar
761 768 new file mode 100644
762 769 index 0000000..8a5a5e2
763 770 --- /dev/null
764 771 +++ b/s/foobar
765 772 @@ -0,0 +1,4 @@
766 773 +woopwoop
767 774 +
768 775 +foo
769 776 +bar
770 777
771 778 $ hg commit --subrepos -m "Added foobar"
772 779 committing subrepository s
773 780 created new head
774 781
775 782 $ hg diff -c . --subrepos --nodates
776 783 diff -r af6d2edbb0d3 -r 255ee8cf690e .hgsubstate
777 784 --- a/.hgsubstate
778 785 +++ b/.hgsubstate
779 786 @@ -1,1 +1,1 @@
780 787 -32a343883b74769118bb1d3b4b1fbf9156f4dddc s
781 788 +fd4dbf828a5b2fcd36b2bcf21ea773820970d129 s
782 789 diff --git a/s/foobar b/s/foobar
783 790 new file mode 100644
784 791 index 0000000..8a5a5e2
785 792 --- /dev/null
786 793 +++ b/s/foobar
787 794 @@ -0,0 +1,4 @@
788 795 +woopwoop
789 796 +
790 797 +foo
791 798 +bar
792 799
793 800 check output when only diffing the subrepository
794 801 $ hg diff -c . --subrepos s
795 802 diff --git a/s/foobar b/s/foobar
796 803 new file mode 100644
797 804 index 0000000..8a5a5e2
798 805 --- /dev/null
799 806 +++ b/s/foobar
800 807 @@ -0,0 +1,4 @@
801 808 +woopwoop
802 809 +
803 810 +foo
804 811 +bar
805 812
806 813 check output when diffing something else
807 814 $ hg diff -c . --subrepos .hgsubstate --nodates
808 815 diff -r af6d2edbb0d3 -r 255ee8cf690e .hgsubstate
809 816 --- a/.hgsubstate
810 817 +++ b/.hgsubstate
811 818 @@ -1,1 +1,1 @@
812 819 -32a343883b74769118bb1d3b4b1fbf9156f4dddc s
813 820 +fd4dbf828a5b2fcd36b2bcf21ea773820970d129 s
814 821
815 822 add new changes, including whitespace
816 823 $ cd s
817 824 $ cat > foobar << EOF
818 825 > woop woop
819 826 >
820 827 > foo
821 828 > bar
822 829 > EOF
823 830 $ echo foo > barfoo
824 831 $ git add barfoo
825 832 $ cd ..
826 833
827 834 $ hg diff --subrepos --ignore-all-space
828 835 diff --git a/s/barfoo b/s/barfoo
829 836 new file mode 100644
830 837 index 0000000..257cc56
831 838 --- /dev/null
832 839 +++ b/s/barfoo
833 840 @@ -0,0 +1* @@ (glob)
834 841 +foo
835 842 $ hg diff --subrepos s/foobar
836 843 diff --git a/s/foobar b/s/foobar
837 844 index 8a5a5e2..bd5812a 100644
838 845 --- a/s/foobar
839 846 +++ b/s/foobar
840 847 @@ -1,4 +1,4 @@
841 848 -woopwoop
842 849 +woop woop
843 850
844 851 foo
845 852 bar
846 853
847 854 execute a diffstat
848 855 the output contains a regex, because git 1.7.10 and 1.7.11
849 856 change the amount of whitespace
850 857 $ hg diff --subrepos --stat
851 858 \s*barfoo \|\s+1 \+ (re)
852 859 \s*foobar \|\s+2 \+- (re)
853 860 2 files changed, 2 insertions\(\+\), 1 deletions?\(-\) (re)
854 861
855 862 adding an include should ignore the other elements
856 863 $ hg diff --subrepos -I s/foobar
857 864 diff --git a/s/foobar b/s/foobar
858 865 index 8a5a5e2..bd5812a 100644
859 866 --- a/s/foobar
860 867 +++ b/s/foobar
861 868 @@ -1,4 +1,4 @@
862 869 -woopwoop
863 870 +woop woop
864 871
865 872 foo
866 873 bar
867 874
868 875 adding an exclude should ignore this element
869 876 $ hg diff --subrepos -X s/foobar
870 877 diff --git a/s/barfoo b/s/barfoo
871 878 new file mode 100644
872 879 index 0000000..257cc56
873 880 --- /dev/null
874 881 +++ b/s/barfoo
875 882 @@ -0,0 +1* @@ (glob)
876 883 +foo
877 884
878 885 moving a file should show a removal and an add
879 886 $ hg revert --all
880 887 reverting subrepo ../gitroot
881 888 $ cd s
882 889 $ git mv foobar woop
883 890 $ cd ..
884 891 $ hg diff --subrepos
885 892 diff --git a/s/foobar b/s/foobar
886 893 deleted file mode 100644
887 894 index 8a5a5e2..0000000
888 895 --- a/s/foobar
889 896 +++ /dev/null
890 897 @@ -1,4 +0,0 @@
891 898 -woopwoop
892 899 -
893 900 -foo
894 901 -bar
895 902 diff --git a/s/woop b/s/woop
896 903 new file mode 100644
897 904 index 0000000..8a5a5e2
898 905 --- /dev/null
899 906 +++ b/s/woop
900 907 @@ -0,0 +1,4 @@
901 908 +woopwoop
902 909 +
903 910 +foo
904 911 +bar
905 912 $ rm s/woop
906 913
907 914 revert the subrepository
908 915 $ hg revert --all
909 916 reverting subrepo ../gitroot
910 917
911 918 $ hg status --subrepos
912 919 ? s/barfoo
913 920 ? s/foobar.orig
914 921
915 922 $ mv s/foobar.orig s/foobar
916 923
917 924 $ hg revert --no-backup s
918 925 reverting subrepo ../gitroot
919 926
920 927 $ hg status --subrepos
921 928 ? s/barfoo
922 929
923 930 revert moves orig files to the right place
924 931 $ echo 'bloop' > s/foobar
925 932 $ hg revert --all --verbose --config 'ui.origbackuppath=.hg/origbackups'
926 933 reverting subrepo ../gitroot
927 934 creating directory: $TESTTMP/tc/.hg/origbackups/s
928 935 saving current version of foobar as .hg/origbackups/s/foobar
929 936 $ ls .hg/origbackups/s
930 937 foobar
931 938 $ rm -rf .hg/origbackups
932 939
933 940 show file at specific revision
934 941 $ cat > s/foobar << EOF
935 942 > woop woop
936 943 > fooo bar
937 944 > EOF
938 945 $ hg commit --subrepos -m "updated foobar"
939 946 committing subrepository s
940 947 $ cat > s/foobar << EOF
941 948 > current foobar
942 949 > (should not be visible using hg cat)
943 950 > EOF
944 951
945 952 $ hg cat -r . s/foobar
946 953 woop woop
947 954 fooo bar (no-eol)
948 955 $ hg cat -r "parents(.)" s/foobar > catparents
949 956
950 957 $ mkdir -p tmp/s
951 958
952 959 $ hg cat -r "parents(.)" --output tmp/%% s/foobar
953 960 $ diff tmp/% catparents
954 961
955 962 $ hg cat -r "parents(.)" --output tmp/%s s/foobar
956 963 $ diff tmp/foobar catparents
957 964
958 965 $ hg cat -r "parents(.)" --output tmp/%d/otherfoobar s/foobar
959 966 $ diff tmp/s/otherfoobar catparents
960 967
961 968 $ hg cat -r "parents(.)" --output tmp/%p s/foobar
962 969 $ diff tmp/s/foobar catparents
963 970
964 971 $ hg cat -r "parents(.)" --output tmp/%H s/foobar
965 972 $ diff tmp/255ee8cf690ec86e99b1e80147ea93ece117cd9d catparents
966 973
967 974 $ hg cat -r "parents(.)" --output tmp/%R s/foobar
968 975 $ diff tmp/10 catparents
969 976
970 977 $ hg cat -r "parents(.)" --output tmp/%h s/foobar
971 978 $ diff tmp/255ee8cf690e catparents
972 979
973 980 $ rm tmp/10
974 981 $ hg cat -r "parents(.)" --output tmp/%r s/foobar
975 982 $ diff tmp/10 catparents
976 983
977 984 $ mkdir tmp/tc
978 985 $ hg cat -r "parents(.)" --output tmp/%b/foobar s/foobar
979 986 $ diff tmp/tc/foobar catparents
980 987
981 988 cleanup
982 989 $ rm -r tmp
983 990 $ rm catparents
984 991
985 992 add git files, using either files or patterns
986 993 $ echo "hsss! hsssssssh!" > s/snake.python
987 994 $ echo "ccc" > s/c.c
988 995 $ echo "cpp" > s/cpp.cpp
989 996
990 997 $ hg add s/snake.python s/c.c s/cpp.cpp
991 998 $ hg st --subrepos s
992 999 M s/foobar
993 1000 A s/c.c
994 1001 A s/cpp.cpp
995 1002 A s/snake.python
996 1003 ? s/barfoo
997 1004 $ hg revert s
998 1005 reverting subrepo ../gitroot
999 1006
1000 1007 $ hg add --subrepos "glob:**.python"
1001 1008 adding s/snake.python
1002 1009 $ hg st --subrepos s
1003 1010 A s/snake.python
1004 1011 ? s/barfoo
1005 1012 ? s/c.c
1006 1013 ? s/cpp.cpp
1007 1014 ? s/foobar.orig
1008 1015 $ hg revert s
1009 1016 reverting subrepo ../gitroot
1010 1017
1011 1018 $ hg add --subrepos s
1012 1019 adding s/barfoo
1013 1020 adding s/c.c
1014 1021 adding s/cpp.cpp
1015 1022 adding s/foobar.orig
1016 1023 adding s/snake.python
1017 1024 $ hg st --subrepos s
1018 1025 A s/barfoo
1019 1026 A s/c.c
1020 1027 A s/cpp.cpp
1021 1028 A s/foobar.orig
1022 1029 A s/snake.python
1023 1030 $ hg revert s
1024 1031 reverting subrepo ../gitroot
1025 1032 make sure everything is reverted correctly
1026 1033 $ hg st --subrepos s
1027 1034 ? s/barfoo
1028 1035 ? s/c.c
1029 1036 ? s/cpp.cpp
1030 1037 ? s/foobar.orig
1031 1038 ? s/snake.python
1032 1039
1033 1040 $ hg add --subrepos --exclude "path:s/c.c"
1034 1041 adding s/barfoo
1035 1042 adding s/cpp.cpp
1036 1043 adding s/foobar.orig
1037 1044 adding s/snake.python
1038 1045 $ hg st --subrepos s
1039 1046 A s/barfoo
1040 1047 A s/cpp.cpp
1041 1048 A s/foobar.orig
1042 1049 A s/snake.python
1043 1050 ? s/c.c
1044 1051 $ hg revert --all -q
1045 1052
1046 1053 .hgignore should not have influence in subrepos
1047 1054 $ cat > .hgignore << EOF
1048 1055 > syntax: glob
1049 1056 > *.python
1050 1057 > EOF
1051 1058 $ hg add .hgignore
1052 1059 $ hg add --subrepos "glob:**.python" s/barfoo
1053 1060 adding s/snake.python
1054 1061 $ hg st --subrepos s
1055 1062 A s/barfoo
1056 1063 A s/snake.python
1057 1064 ? s/c.c
1058 1065 ? s/cpp.cpp
1059 1066 ? s/foobar.orig
1060 1067 $ hg revert --all -q
1061 1068
1062 1069 .gitignore should have influence,
1063 1070 except for explicitly added files (no patterns)
1064 1071 $ cat > s/.gitignore << EOF
1065 1072 > *.python
1066 1073 > EOF
1067 1074 $ hg add s/.gitignore
1068 1075 $ hg st --subrepos s
1069 1076 A s/.gitignore
1070 1077 ? s/barfoo
1071 1078 ? s/c.c
1072 1079 ? s/cpp.cpp
1073 1080 ? s/foobar.orig
1074 1081 $ hg st --subrepos s --all
1075 1082 A s/.gitignore
1076 1083 ? s/barfoo
1077 1084 ? s/c.c
1078 1085 ? s/cpp.cpp
1079 1086 ? s/foobar.orig
1080 1087 I s/snake.python
1081 1088 C s/f
1082 1089 C s/foobar
1083 1090 C s/g
1084 1091 $ hg add --subrepos "glob:**.python"
1085 1092 $ hg st --subrepos s
1086 1093 A s/.gitignore
1087 1094 ? s/barfoo
1088 1095 ? s/c.c
1089 1096 ? s/cpp.cpp
1090 1097 ? s/foobar.orig
1091 1098 $ hg add --subrepos s/snake.python
1092 1099 $ hg st --subrepos s
1093 1100 A s/.gitignore
1094 1101 A s/snake.python
1095 1102 ? s/barfoo
1096 1103 ? s/c.c
1097 1104 ? s/cpp.cpp
1098 1105 ? s/foobar.orig
1099 1106
1100 1107 correctly do a dry run
1101 1108 $ hg add --subrepos s --dry-run
1102 1109 adding s/barfoo
1103 1110 adding s/c.c
1104 1111 adding s/cpp.cpp
1105 1112 adding s/foobar.orig
1106 1113 $ hg st --subrepos s
1107 1114 A s/.gitignore
1108 1115 A s/snake.python
1109 1116 ? s/barfoo
1110 1117 ? s/c.c
1111 1118 ? s/cpp.cpp
1112 1119 ? s/foobar.orig
1113 1120
1114 1121 error given when adding an already tracked file
1115 1122 $ hg add s/.gitignore
1116 1123 s/.gitignore already tracked!
1117 1124 [1]
1118 1125 $ hg add s/g
1119 1126 s/g already tracked!
1120 1127 [1]
1121 1128
1122 1129 removed files can be re-added
1123 1130 removing files using 'rm' or 'git rm' has the same effect,
1124 1131 since we ignore the staging area
1125 1132 $ hg ci --subrepos -m 'snake'
1126 1133 committing subrepository s
1127 1134 $ cd s
1128 1135 $ rm snake.python
1129 1136 (remove leftover .hg so Mercurial doesn't look for a root here)
1130 1137 $ rm -rf .hg
1131 1138 $ hg status --subrepos --all .
1132 1139 R snake.python
1133 1140 ? barfoo
1134 1141 ? c.c
1135 1142 ? cpp.cpp
1136 1143 ? foobar.orig
1137 1144 C .gitignore
1138 1145 C f
1139 1146 C foobar
1140 1147 C g
1141 1148 $ git rm snake.python
1142 1149 rm 'snake.python'
1143 1150 $ hg status --subrepos --all .
1144 1151 R snake.python
1145 1152 ? barfoo
1146 1153 ? c.c
1147 1154 ? cpp.cpp
1148 1155 ? foobar.orig
1149 1156 C .gitignore
1150 1157 C f
1151 1158 C foobar
1152 1159 C g
1153 1160 $ touch snake.python
1154 1161 $ cd ..
1155 1162 $ hg add s/snake.python
1156 1163 $ hg status -S
1157 1164 M s/snake.python
1158 1165 ? .hgignore
1159 1166 ? s/barfoo
1160 1167 ? s/c.c
1161 1168 ? s/cpp.cpp
1162 1169 ? s/foobar.orig
1163 1170 $ hg revert --all -q
1164 1171
1165 1172 make sure we show changed files, rather than changed subtrees
1166 1173 $ mkdir s/foo
1167 1174 $ touch s/foo/bwuh
1168 1175 $ hg add s/foo/bwuh
1169 1176 $ hg commit -S -m "add bwuh"
1170 1177 committing subrepository s
1171 1178 $ hg status -S --change .
1172 1179 M .hgsubstate
1173 1180 A s/foo/bwuh
1174 1181 ? s/barfoo
1175 1182 ? s/c.c
1176 1183 ? s/cpp.cpp
1177 1184 ? s/foobar.orig
1178 1185 ? s/snake.python.orig
1179 1186
1180 1187 #if git19
1181 1188
1182 1189 test for Git CVE-2016-3068
1183 1190 $ hg init malicious-subrepository
1184 1191 $ cd malicious-subrepository
1185 1192 $ echo "s = [git]ext::sh -c echo% pwned:% \$PWNED_MSG% >pwned.txt" > .hgsub
1186 1193 $ git init s
1187 1194 Initialized empty Git repository in $TESTTMP/tc/malicious-subrepository/s/.git/
1188 1195 $ cd s
1189 1196 $ git commit --allow-empty -m 'empty'
1190 1197 [master (root-commit) 153f934] empty
1191 1198 $ cd ..
1192 1199 $ hg add .hgsub
1193 1200 $ hg commit -m "add subrepo"
1194 1201 $ cd ..
1195 1202 $ rm -f pwned.txt
1196 1203 $ unset GIT_ALLOW_PROTOCOL
1197 1204 $ PWNED_MSG="your git is too old or mercurial has regressed" hg clone \
1198 1205 > malicious-subrepository malicious-subrepository-protected
1199 1206 Cloning into '$TESTTMP/tc/malicious-subrepository-protected/s'...
1200 1207 fatal: transport 'ext' not allowed
1201 1208 updating to branch default
1202 1209 cloning subrepo s from ext::sh -c echo% pwned:% $PWNED_MSG% >pwned.txt
1203 1210 abort: git clone error 128 in s (in subrepository "s")
1204 1211 [255]
1205 1212 $ f -Dq pwned.txt
1206 1213 pwned.txt: file not found
1207 1214
1208 1215 whitelisting of ext should be respected (that's the git submodule behaviour)
1209 1216 $ rm -f pwned.txt
1210 1217 $ env GIT_ALLOW_PROTOCOL=ext PWNED_MSG="you asked for it" hg clone \
1211 1218 > malicious-subrepository malicious-subrepository-clone-allowed
1212 1219 Cloning into '$TESTTMP/tc/malicious-subrepository-clone-allowed/s'...
1213 1220 fatal: Could not read from remote repository.
1214 1221
1215 1222 Please make sure you have the correct access rights
1216 1223 and the repository exists.
1217 1224 updating to branch default
1218 1225 cloning subrepo s from ext::sh -c echo% pwned:% $PWNED_MSG% >pwned.txt
1219 1226 abort: git clone error 128 in s (in subrepository "s")
1220 1227 [255]
1221 1228 $ f -Dq pwned.txt
1222 1229 pwned: you asked for it
1223 1230
1224 1231 #endif
1225 1232
1226 1233 test for ssh exploit with git subrepos 2017-07-25
1227 1234
1228 1235 $ hg init malicious-proxycommand
1229 1236 $ cd malicious-proxycommand
1230 1237 $ echo 's = [git]ssh://-oProxyCommand=rm${IFS}non-existent/path' > .hgsub
1231 1238 $ git init s
1232 1239 Initialized empty Git repository in $TESTTMP/tc/malicious-proxycommand/s/.git/
1233 1240 $ cd s
1234 1241 $ git commit --allow-empty -m 'empty'
1235 1242 [master (root-commit) 153f934] empty
1236 1243 $ cd ..
1237 1244 $ hg add .hgsub
1238 1245 $ hg ci -m 'add subrepo'
1239 1246 $ cd ..
1240 1247 $ hg clone malicious-proxycommand malicious-proxycommand-clone
1241 1248 updating to branch default
1242 1249 abort: potentially unsafe url: 'ssh://-oProxyCommand=rm${IFS}non-existent/path' (in subrepository "s")
1243 1250 [255]
1244 1251
1245 1252 also check that a percent encoded '-' (%2D) doesn't work
1246 1253
1247 1254 $ cd malicious-proxycommand
1248 1255 $ echo 's = [git]ssh://%2DoProxyCommand=rm${IFS}non-existent/path' > .hgsub
1249 1256 $ hg ci -m 'change url to percent encoded'
1250 1257 $ cd ..
1251 1258 $ rm -r malicious-proxycommand-clone
1252 1259 $ hg clone malicious-proxycommand malicious-proxycommand-clone
1253 1260 updating to branch default
1254 1261 abort: potentially unsafe url: 'ssh://-oProxyCommand=rm${IFS}non-existent/path' (in subrepository "s")
1255 1262 [255]
@@ -1,686 +1,693 b''
1 1 #require svn15
2 2
3 3 $ SVNREPOPATH=`pwd`/svn-repo
4 4 $ SVNREPOURL="`"$PYTHON" $TESTDIR/svnurlof.py \"$SVNREPOPATH\"`"
5 5
6 6 $ filter_svn_output () {
7 7 > egrep -v 'Committing|Transmitting|Updating|(^$)' || true
8 8 > }
9 9
10 10 create subversion repo
11 11
12 12 $ WCROOT="`pwd`/svn-wc"
13 13 $ svnadmin create svn-repo
14 14 $ svn co "$SVNREPOURL" svn-wc
15 15 Checked out revision 0.
16 16 $ cd svn-wc
17 17 $ mkdir src
18 18 $ echo alpha > src/alpha
19 19 $ svn add src
20 20 A src
21 21 A src/alpha
22 22 $ mkdir externals
23 23 $ echo other > externals/other
24 24 $ svn add externals
25 25 A externals
26 26 A externals/other
27 27 $ svn ci -qm 'Add alpha'
28 28 $ svn up -q
29 29 $ echo "externals -r1 $SVNREPOURL/externals" > extdef
30 30 $ svn propset -F extdef svn:externals src
31 31 property 'svn:externals' set on 'src'
32 32 $ svn ci -qm 'Setting externals'
33 33 $ cd ..
34 34
35 35 create hg repo
36 36
37 37 $ mkdir sub
38 38 $ cd sub
39 39 $ hg init t
40 40 $ cd t
41 41
42 42 first revision, no sub
43 43
44 44 $ echo a > a
45 45 $ hg ci -Am0
46 46 adding a
47 47
48 48 add first svn sub with leading whitespaces
49 49
50 50 $ echo "s = [svn] $SVNREPOURL/src" >> .hgsub
51 51 $ echo "subdir/s = [svn] $SVNREPOURL/src" >> .hgsub
52 52 $ svn co --quiet "$SVNREPOURL"/src s
53 53 $ mkdir subdir
54 54 $ svn co --quiet "$SVNREPOURL"/src subdir/s
55 55 $ hg add .hgsub
56 56
57 57 svn subrepo is disabled by default
58 58
59 59 $ hg ci -m1
60 60 abort: svn subrepos not allowed
61 61 (see 'hg help config.subrepos' for details)
62 62 [255]
63 63
64 64 so enable it
65 65
66 66 $ cat >> $HGRCPATH <<EOF
67 67 > [subrepos]
68 68 > svn:allowed = true
69 69 > EOF
70 70
71 71 $ hg ci -m1
72 72
73 73 make sure we avoid empty commits (issue2445)
74 74
75 75 $ hg sum
76 76 parent: 1:* tip (glob)
77 77 1
78 78 branch: default
79 79 commit: (clean)
80 80 update: (current)
81 81 phases: 2 draft
82 82 $ hg ci -moops
83 83 nothing changed
84 84 [1]
85 85
86 86 debugsub
87 87
88 88 $ hg debugsub
89 89 path s
90 90 source file:/*/$TESTTMP/svn-repo/src (glob)
91 91 revision 2
92 92 path subdir/s
93 93 source file:/*/$TESTTMP/svn-repo/src (glob)
94 94 revision 2
95 95
96 96 change file in svn and hg, commit
97 97
98 98 $ echo a >> a
99 99 $ echo alpha >> s/alpha
100 100 $ hg sum
101 101 parent: 1:* tip (glob)
102 102 1
103 103 branch: default
104 104 commit: 1 modified, 1 subrepos
105 105 update: (current)
106 106 phases: 2 draft
107 107 $ hg commit --subrepos -m 'Message!' | filter_svn_output
108 108 committing subrepository s
109 109 Sending*s/alpha (glob)
110 110 Committed revision 3.
111 111 Fetching external item into '*s/externals'* (glob)
112 112 External at revision 1.
113 113 At revision 3.
114 114 $ hg debugsub
115 115 path s
116 116 source file:/*/$TESTTMP/svn-repo/src (glob)
117 117 revision 3
118 118 path subdir/s
119 119 source file:/*/$TESTTMP/svn-repo/src (glob)
120 120 revision 2
121 121
122 122 missing svn file, commit should fail
123 123
124 124 $ rm s/alpha
125 125 $ hg commit --subrepos -m 'abort on missing file'
126 126 committing subrepository s
127 127 abort: cannot commit missing svn entries (in subrepository "s")
128 128 [255]
129 129 $ svn revert s/alpha > /dev/null
130 130
131 131 add an unrelated revision in svn and update the subrepo to without
132 132 bringing any changes.
133 133
134 134 $ svn mkdir "$SVNREPOURL/unrelated" -qm 'create unrelated'
135 135 $ svn up -q s
136 136 $ hg sum
137 137 parent: 2:* tip (glob)
138 138 Message!
139 139 branch: default
140 140 commit: (clean)
141 141 update: (current)
142 142 phases: 3 draft
143 143
144 144 $ echo a > s/a
145 145
146 146 should be empty despite change to s/a
147 147
148 148 $ hg st
149 149
150 150 add a commit from svn
151 151
152 152 $ cd "$WCROOT/src"
153 153 $ svn up -q
154 154 $ echo xyz >> alpha
155 155 $ svn propset svn:mime-type 'text/xml' alpha
156 156 property 'svn:mime-type' set on 'alpha'
157 157 $ svn ci -qm 'amend a from svn'
158 158 $ cd ../../sub/t
159 159
160 160 this commit from hg will fail
161 161
162 162 $ echo zzz >> s/alpha
163 163 $ (hg ci --subrepos -m 'amend alpha from hg' 2>&1; echo "[$?]") | grep -vi 'out of date'
164 164 committing subrepository s
165 165 abort: svn:*Commit failed (details follow): (glob)
166 166 [255]
167 167 $ svn revert -q s/alpha
168 168
169 169 this commit fails because of meta changes
170 170
171 171 $ svn propset svn:mime-type 'text/html' s/alpha
172 172 property 'svn:mime-type' set on 's/alpha'
173 173 $ (hg ci --subrepos -m 'amend alpha from hg' 2>&1; echo "[$?]") | grep -vi 'out of date'
174 174 committing subrepository s
175 175 abort: svn:*Commit failed (details follow): (glob)
176 176 [255]
177 177 $ svn revert -q s/alpha
178 178
179 179 this commit fails because of externals changes
180 180
181 181 $ echo zzz > s/externals/other
182 182 $ hg ci --subrepos -m 'amend externals from hg'
183 183 committing subrepository s
184 184 abort: cannot commit svn externals (in subrepository "s")
185 185 [255]
186 186 $ hg diff --subrepos -r 1:2 | grep -v diff
187 187 --- a/.hgsubstate Thu Jan 01 00:00:00 1970 +0000
188 188 +++ b/.hgsubstate Thu Jan 01 00:00:00 1970 +0000
189 189 @@ -1,2 +1,2 @@
190 190 -2 s
191 191 +3 s
192 192 2 subdir/s
193 193 --- a/a Thu Jan 01 00:00:00 1970 +0000
194 194 +++ b/a Thu Jan 01 00:00:00 1970 +0000
195 195 @@ -1,1 +1,2 @@
196 196 a
197 197 +a
198 198 $ svn revert -q s/externals/other
199 199
200 200 this commit fails because of externals meta changes
201 201
202 202 $ svn propset svn:mime-type 'text/html' s/externals/other
203 203 property 'svn:mime-type' set on 's/externals/other'
204 204 $ hg ci --subrepos -m 'amend externals from hg'
205 205 committing subrepository s
206 206 abort: cannot commit svn externals (in subrepository "s")
207 207 [255]
208 208 $ svn revert -q s/externals/other
209 209
210 210 clone
211 211
212 212 $ cd ..
213 213 $ hg clone t tc
214 214 updating to branch default
215 215 A tc/s/alpha
216 216 U tc/s
217 217
218 218 Fetching external item into 'tc/s/externals'* (glob)
219 219 A tc/s/externals/other
220 220 Checked out external at revision 1.
221 221
222 222 Checked out revision 3.
223 223 A tc/subdir/s/alpha
224 224 U tc/subdir/s
225 225
226 226 Fetching external item into 'tc/subdir/s/externals'* (glob)
227 227 A tc/subdir/s/externals/other
228 228 Checked out external at revision 1.
229 229
230 230 Checked out revision 2.
231 231 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
232 232 $ cd tc
233 233
234 234 debugsub in clone
235 235
236 236 $ hg debugsub
237 237 path s
238 238 source file:/*/$TESTTMP/svn-repo/src (glob)
239 239 revision 3
240 240 path subdir/s
241 241 source file:/*/$TESTTMP/svn-repo/src (glob)
242 242 revision 2
243 243
244 244 verify subrepo is contained within the repo directory
245 245
246 246 $ "$PYTHON" -c "from __future__ import print_function; import os.path; print(os.path.exists('s'))"
247 247 True
248 248
249 249 update to nullrev (must delete the subrepo)
250 250
251 251 $ hg up null
252 252 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
253 253 $ ls
254 254
255 255 Check hg update --clean
256 256 $ cd "$TESTTMP/sub/t"
257 257 $ cd s
258 258 $ echo c0 > alpha
259 259 $ echo c1 > f1
260 260 $ echo c1 > f2
261 261 $ svn add f1 -q
262 262 $ svn status | sort
263 263
264 264 ? * a (glob)
265 265 ? * f2 (glob)
266 266 A * f1 (glob)
267 267 M * alpha (glob)
268 268 Performing status on external item at 'externals'* (glob)
269 269 X * externals (glob)
270 270 $ cd ../..
271 271 $ hg -R t update -C
272 272
273 273 Fetching external item into 't/s/externals'* (glob)
274 274 Checked out external at revision 1.
275 275
276 276 Checked out revision 3.
277 277 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
278 278 $ cd t/s
279 279 $ svn status | sort
280 280
281 281 ? * a (glob)
282 282 ? * f1 (glob)
283 283 ? * f2 (glob)
284 284 Performing status on external item at 'externals'* (glob)
285 285 X * externals (glob)
286 286
287 287 Sticky subrepositories, no changes
288 288 $ cd "$TESTTMP/sub/t"
289 289 $ hg id -n
290 290 2
291 291 $ cd s
292 292 $ svnversion
293 293 3
294 294 $ cd ..
295 295 $ hg update 1
296 296 U *s/alpha (glob)
297 297
298 298 Fetching external item into '*s/externals'* (glob)
299 299 Checked out external at revision 1.
300 300
301 301 Checked out revision 2.
302 302 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
303 303 $ hg id -n
304 304 1
305 305 $ cd s
306 306 $ svnversion
307 307 2
308 308 $ cd ..
309 309
310 310 Sticky subrepositories, file changes
311 311 $ touch s/f1
312 312 $ cd s
313 313 $ svn add f1
314 314 A f1
315 315 $ cd ..
316 316 $ hg id -n
317 317 1+
318 318 $ cd s
319 319 $ svnversion
320 320 2M
321 321 $ cd ..
322 322 $ hg update tip
323 323 subrepository s diverged (local revision: 2, remote revision: 3)
324 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
324 you can (m)erge, keep (l)ocal [working copy] or keep (r)emote [destination].
325 what do you want to do? m
325 326 subrepository sources for s differ
326 use (l)ocal source (2) or (r)emote source (3)? l
327 you can use (l)ocal source (2) or (r)emote source (3).
328 what do you want to do? l
327 329 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
328 330 $ hg id -n
329 331 2+
330 332 $ cd s
331 333 $ svnversion
332 334 2M
333 335 $ cd ..
334 336 $ hg update --clean tip
335 337 U *s/alpha (glob)
336 338
337 339 Fetching external item into '*s/externals'* (glob)
338 340 Checked out external at revision 1.
339 341
340 342 Checked out revision 3.
341 343 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
342 344
343 345 Sticky subrepository, revision updates
344 346 $ hg id -n
345 347 2
346 348 $ cd s
347 349 $ svnversion
348 350 3
349 351 $ cd ..
350 352 $ cd s
351 353 $ svn update -qr 1
352 354 $ cd ..
353 355 $ hg update 1
354 356 subrepository s diverged (local revision: 3, remote revision: 2)
355 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
357 you can (m)erge, keep (l)ocal [working copy] or keep (r)emote [destination].
358 what do you want to do? m
356 359 subrepository sources for s differ (in checked out version)
357 use (l)ocal source (1) or (r)emote source (2)? l
360 you can use (l)ocal source (1) or (r)emote source (2).
361 what do you want to do? l
358 362 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
359 363 $ hg id -n
360 364 1+
361 365 $ cd s
362 366 $ svnversion
363 367 1
364 368 $ cd ..
365 369
366 370 Sticky subrepository, file changes and revision updates
367 371 $ touch s/f1
368 372 $ cd s
369 373 $ svn add f1
370 374 A f1
371 375 $ svnversion
372 376 1M
373 377 $ cd ..
374 378 $ hg id -n
375 379 1+
376 380 $ hg update tip
377 381 subrepository s diverged (local revision: 3, remote revision: 3)
378 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
382 you can (m)erge, keep (l)ocal [working copy] or keep (r)emote [destination].
383 what do you want to do? m
379 384 subrepository sources for s differ
380 use (l)ocal source (1) or (r)emote source (3)? l
385 you can use (l)ocal source (1) or (r)emote source (3).
386 what do you want to do? l
381 387 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
382 388 $ hg id -n
383 389 2+
384 390 $ cd s
385 391 $ svnversion
386 392 1M
387 393 $ cd ..
388 394
389 395 Sticky repository, update --clean
390 396 $ hg update --clean tip | grep -v 's[/\]externals[/\]other'
391 397 U *s/alpha (glob)
392 398 U *s (glob)
393 399
394 400 Fetching external item into '*s/externals'* (glob)
395 401 Checked out external at revision 1.
396 402
397 403 Checked out revision 3.
398 404 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
399 405 $ hg id -n
400 406 2
401 407 $ cd s
402 408 $ svnversion
403 409 3
404 410 $ cd ..
405 411
406 412 Test subrepo already at intended revision:
407 413 $ cd s
408 414 $ svn update -qr 2
409 415 $ cd ..
410 416 $ hg update 1
411 417 subrepository s diverged (local revision: 3, remote revision: 2)
412 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
418 you can (m)erge, keep (l)ocal [working copy] or keep (r)emote [destination].
419 what do you want to do? m
413 420 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
414 421 $ hg id -n
415 422 1+
416 423 $ cd s
417 424 $ svnversion
418 425 2
419 426 $ cd ..
420 427
421 428 Test case where subversion would fail to update the subrepo because there
422 429 are unknown directories being replaced by tracked ones (happens with rebase).
423 430
424 431 $ cd "$WCROOT/src"
425 432 $ mkdir dir
426 433 $ echo epsilon.py > dir/epsilon.py
427 434 $ svn add dir
428 435 A dir
429 436 A dir/epsilon.py
430 437 $ svn ci -qm 'Add dir/epsilon.py'
431 438 $ cd ../..
432 439 $ hg init rebaserepo
433 440 $ cd rebaserepo
434 441 $ svn co -r5 --quiet "$SVNREPOURL"/src s
435 442 $ echo "s = [svn] $SVNREPOURL/src" >> .hgsub
436 443 $ hg add .hgsub
437 444 $ hg ci -m addsub
438 445 $ echo a > a
439 446 $ hg add .
440 447 adding a
441 448 $ hg ci -m adda
442 449 $ hg up 0
443 450 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
444 451 $ svn up -qr6 s
445 452 $ hg ci -m updatesub
446 453 created new head
447 454 $ echo pyc > s/dir/epsilon.pyc
448 455 $ hg up 1
449 456 D *s/dir (glob)
450 457
451 458 Fetching external item into '*s/externals'* (glob)
452 459 Checked out external at revision 1.
453 460
454 461 Checked out revision 5.
455 462 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
456 463 $ hg up -q 2
457 464
458 465 Modify one of the externals to point to a different path so we can
459 466 test having obstructions when switching branches on checkout:
460 467 $ hg checkout tip
461 468 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
462 469 $ echo "obstruct = [svn] $SVNREPOURL/externals" >> .hgsub
463 470 $ svn co -r5 --quiet "$SVNREPOURL"/externals obstruct
464 471 $ hg commit -m 'Start making obstructed working copy'
465 472 $ hg book other
466 473 $ hg co -r 'p1(tip)'
467 474 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
468 475 (leaving bookmark other)
469 476 $ echo "obstruct = [svn] $SVNREPOURL/src" >> .hgsub
470 477 $ svn co -r5 --quiet "$SVNREPOURL"/src obstruct
471 478 $ hg commit -m 'Other branch which will be obstructed'
472 479 created new head
473 480
474 481 Switching back to the head where we have another path mapped to the
475 482 same subrepo should work if the subrepo is clean.
476 483 $ hg co other
477 484 A *obstruct/other (glob)
478 485 Checked out revision 1.
479 486 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
480 487 (activating bookmark other)
481 488
482 489 This is surprising, but is also correct based on the current code:
483 490 $ echo "updating should (maybe) fail" > obstruct/other
484 491 $ hg co tip
485 492 abort: uncommitted changes
486 493 (commit or update --clean to discard changes)
487 494 [255]
488 495
489 496 Point to a Subversion branch which has since been deleted and recreated
490 497 First, create that condition in the repository.
491 498
492 499 $ hg ci --subrepos -m cleanup | filter_svn_output
493 500 committing subrepository obstruct
494 501 Sending obstruct/other
495 502 Committed revision 7.
496 503 At revision 7.
497 504 $ svn mkdir -qm "baseline" $SVNREPOURL/trunk
498 505 $ svn copy -qm "initial branch" $SVNREPOURL/trunk $SVNREPOURL/branch
499 506 $ svn co --quiet "$SVNREPOURL"/branch tempwc
500 507 $ cd tempwc
501 508 $ echo "something old" > somethingold
502 509 $ svn add somethingold
503 510 A somethingold
504 511 $ svn ci -qm 'Something old'
505 512 $ svn rm -qm "remove branch" $SVNREPOURL/branch
506 513 $ svn copy -qm "recreate branch" $SVNREPOURL/trunk $SVNREPOURL/branch
507 514 $ svn up -q
508 515 $ echo "something new" > somethingnew
509 516 $ svn add somethingnew
510 517 A somethingnew
511 518 $ svn ci -qm 'Something new'
512 519 $ cd ..
513 520 $ rm -rf tempwc
514 521 $ svn co "$SVNREPOURL/branch"@10 recreated
515 522 A recreated/somethingold
516 523 Checked out revision 10.
517 524 $ echo "recreated = [svn] $SVNREPOURL/branch" >> .hgsub
518 525 $ hg ci -m addsub
519 526 $ cd recreated
520 527 $ svn up -q
521 528 $ cd ..
522 529 $ hg ci -m updatesub
523 530 $ hg up -r-2
524 531 D *recreated/somethingnew (glob)
525 532 A *recreated/somethingold (glob)
526 533 Checked out revision 10.
527 534 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
528 535 (leaving bookmark other)
529 536 $ test -f recreated/somethingold
530 537
531 538 Test archive
532 539
533 540 $ hg archive -S ../archive-all --debug --config progress.debug=true
534 541 archiving: 0/2 files (0.00%)
535 542 archiving: .hgsub 1/2 files (50.00%)
536 543 archiving: .hgsubstate 2/2 files (100.00%)
537 544 archiving (obstruct): 0/1 files (0.00%)
538 545 archiving (obstruct): 1/1 files (100.00%)
539 546 archiving (recreated): 0/1 files (0.00%)
540 547 archiving (recreated): 1/1 files (100.00%)
541 548 archiving (s): 0/2 files (0.00%)
542 549 archiving (s): 1/2 files (50.00%)
543 550 archiving (s): 2/2 files (100.00%)
544 551
545 552 $ hg archive -S ../archive-exclude --debug --config progress.debug=true -X **old
546 553 archiving: 0/2 files (0.00%)
547 554 archiving: .hgsub 1/2 files (50.00%)
548 555 archiving: .hgsubstate 2/2 files (100.00%)
549 556 archiving (obstruct): 0/1 files (0.00%)
550 557 archiving (obstruct): 1/1 files (100.00%)
551 558 archiving (recreated): 0 files
552 559 archiving (s): 0/2 files (0.00%)
553 560 archiving (s): 1/2 files (50.00%)
554 561 archiving (s): 2/2 files (100.00%)
555 562 $ find ../archive-exclude | sort
556 563 ../archive-exclude
557 564 ../archive-exclude/.hg_archival.txt
558 565 ../archive-exclude/.hgsub
559 566 ../archive-exclude/.hgsubstate
560 567 ../archive-exclude/obstruct
561 568 ../archive-exclude/obstruct/other
562 569 ../archive-exclude/s
563 570 ../archive-exclude/s/alpha
564 571 ../archive-exclude/s/dir
565 572 ../archive-exclude/s/dir/epsilon.py
566 573
567 574 Test forgetting files, not implemented in svn subrepo, used to
568 575 traceback
569 576
570 577 $ hg forget 'notafile*'
571 578 notafile*: $ENOENT$
572 579 [1]
573 580
574 581 Test a subrepo referencing a just moved svn path. Last commit rev will
575 582 be different from the revision, and the path will be different as
576 583 well.
577 584
578 585 $ cd "$WCROOT"
579 586 $ svn up > /dev/null
580 587 $ mkdir trunk/subdir branches
581 588 $ echo a > trunk/subdir/a
582 589 $ svn add trunk/subdir branches
583 590 A trunk/subdir
584 591 A trunk/subdir/a
585 592 A branches
586 593 $ svn ci -qm addsubdir
587 594 $ svn cp -qm branchtrunk $SVNREPOURL/trunk $SVNREPOURL/branches/somebranch
588 595 $ cd ..
589 596
590 597 $ hg init repo2
591 598 $ cd repo2
592 599 $ svn co $SVNREPOURL/branches/somebranch/subdir
593 600 A subdir/a
594 601 Checked out revision 15.
595 602 $ echo "subdir = [svn] $SVNREPOURL/branches/somebranch/subdir" > .hgsub
596 603 $ hg add .hgsub
597 604 $ hg ci -m addsub
598 605 $ hg up null
599 606 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
600 607 $ hg up
601 608 A *subdir/a (glob)
602 609 Checked out revision 15.
603 610 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
604 611 $ cd ..
605 612
606 613 Test sanitizing ".hg/hgrc" in subrepo
607 614
608 615 $ cd sub/t
609 616 $ hg update -q -C tip
610 617 $ cd s
611 618 $ mkdir .hg
612 619 $ echo '.hg/hgrc in svn repo' > .hg/hgrc
613 620 $ mkdir -p sub/.hg
614 621 $ echo 'sub/.hg/hgrc in svn repo' > sub/.hg/hgrc
615 622 $ svn add .hg sub
616 623 A .hg
617 624 A .hg/hgrc
618 625 A sub
619 626 A sub/.hg
620 627 A sub/.hg/hgrc
621 628 $ svn ci -qm 'add .hg/hgrc to be sanitized at hg update'
622 629 $ svn up -q
623 630 $ cd ..
624 631 $ hg commit -S -m 'commit with svn revision including .hg/hgrc'
625 632 $ grep ' s$' .hgsubstate
626 633 16 s
627 634 $ cd ..
628 635
629 636 $ hg -R tc pull -u -q 2>&1 | sort
630 637 warning: removing potentially hostile 'hgrc' in '$TESTTMP/sub/tc/s/.hg'
631 638 warning: removing potentially hostile 'hgrc' in '$TESTTMP/sub/tc/s/sub/.hg'
632 639 $ cd tc
633 640 $ grep ' s$' .hgsubstate
634 641 16 s
635 642 $ test -f s/.hg/hgrc
636 643 [1]
637 644 $ test -f s/sub/.hg/hgrc
638 645 [1]
639 646
640 647 Test that sanitizing is omitted in meta data area:
641 648
642 649 $ mkdir s/.svn/.hg
643 650 $ echo '.hg/hgrc in svn metadata area' > s/.svn/.hg/hgrc
644 651 $ hg update -q -C '.^1'
645 652
646 653 $ cd ../..
647 654
648 655 SEC: test for ssh exploit
649 656
650 657 $ hg init ssh-vuln
651 658 $ cd ssh-vuln
652 659 $ echo "s = [svn]$SVNREPOURL/src" >> .hgsub
653 660 $ svn co --quiet "$SVNREPOURL"/src s
654 661 $ hg add .hgsub
655 662 $ hg ci -m1
656 663 $ echo "s = [svn]svn+ssh://-oProxyCommand=touch%20owned%20nested" > .hgsub
657 664 $ hg ci -m2
658 665 $ cd ..
659 666 $ hg clone ssh-vuln ssh-vuln-clone
660 667 updating to branch default
661 668 abort: potentially unsafe url: 'svn+ssh://-oProxyCommand=touch owned nested' (in subrepository "s")
662 669 [255]
663 670
664 671 also check that a percent encoded '-' (%2D) doesn't work
665 672
666 673 $ cd ssh-vuln
667 674 $ echo "s = [svn]svn+ssh://%2DoProxyCommand=touch%20owned%20nested" > .hgsub
668 675 $ hg ci -m3
669 676 $ cd ..
670 677 $ rm -r ssh-vuln-clone
671 678 $ hg clone ssh-vuln ssh-vuln-clone
672 679 updating to branch default
673 680 abort: potentially unsafe url: 'svn+ssh://-oProxyCommand=touch owned nested' (in subrepository "s")
674 681 [255]
675 682
676 683 also check that hiding the attack in the username doesn't work:
677 684
678 685 $ cd ssh-vuln
679 686 $ echo "s = [svn]svn+ssh://%2DoProxyCommand=touch%20owned%20foo@example.com/nested" > .hgsub
680 687 $ hg ci -m3
681 688 $ cd ..
682 689 $ rm -r ssh-vuln-clone
683 690 $ hg clone ssh-vuln ssh-vuln-clone
684 691 updating to branch default
685 692 abort: potentially unsafe url: 'svn+ssh://-oProxyCommand=touch owned foo@example.com/nested' (in subrepository "s")
686 693 [255]
@@ -1,2007 +1,2023 b''
1 1 Let commit recurse into subrepos by default to match pre-2.0 behavior:
2 2
3 3 $ echo "[ui]" >> $HGRCPATH
4 4 $ echo "commitsubrepos = Yes" >> $HGRCPATH
5 5
6 6 $ hg init t
7 7 $ cd t
8 8
9 9 first revision, no sub
10 10
11 11 $ echo a > a
12 12 $ hg ci -Am0
13 13 adding a
14 14
15 15 add first sub
16 16
17 17 $ echo s = s > .hgsub
18 18 $ hg add .hgsub
19 19 $ hg init s
20 20 $ echo a > s/a
21 21
22 22 Issue2232: committing a subrepo without .hgsub
23 23
24 24 $ hg ci -mbad s
25 25 abort: can't commit subrepos without .hgsub
26 26 [255]
27 27
28 28 $ hg -R s add s/a
29 29 $ hg files -S
30 30 .hgsub
31 31 a
32 32 s/a
33 33
34 34 `hg files` respects ui.relative-paths
35 35 BROKEN: shows subrepo paths relative to the subrepo
36 36 $ hg files -S --config ui.relative-paths=no
37 37 .hgsub
38 38 a
39 39 s/a
40 40
41 41 $ hg -R s ci -Ams0
42 42 $ hg sum
43 43 parent: 0:f7b1eb17ad24 tip
44 44 0
45 45 branch: default
46 46 commit: 1 added, 1 subrepos
47 47 update: (current)
48 48 phases: 1 draft
49 49 $ hg ci -m1
50 50
51 51 test handling .hgsubstate "added" explicitly.
52 52
53 53 $ hg parents --template '{node}\n{files}\n'
54 54 7cf8cfea66e410e8e3336508dfeec07b3192de51
55 55 .hgsub .hgsubstate
56 56 $ hg rollback -q
57 57 $ hg add .hgsubstate
58 58 $ hg ci -m1
59 59 $ hg parents --template '{node}\n{files}\n'
60 60 7cf8cfea66e410e8e3336508dfeec07b3192de51
61 61 .hgsub .hgsubstate
62 62
63 63 Subrepopath which overlaps with filepath, does not change warnings in remove()
64 64
65 65 $ mkdir snot
66 66 $ touch snot/file
67 67 $ hg remove -S snot/file
68 68 not removing snot/file: file is untracked
69 69 [1]
70 70 $ hg cat snot/filenot
71 71 snot/filenot: no such file in rev 7cf8cfea66e4
72 72 [1]
73 73 $ rm -r snot
74 74
75 75 Revert subrepo and test subrepo fileset keyword:
76 76
77 77 $ echo b > s/a
78 78 $ hg revert --dry-run "set:subrepo('glob:s*')"
79 79 reverting subrepo s
80 80 reverting s/a
81 81 $ cat s/a
82 82 b
83 83 $ hg revert "set:subrepo('glob:s*')"
84 84 reverting subrepo s
85 85 reverting s/a
86 86 $ cat s/a
87 87 a
88 88 $ rm s/a.orig
89 89
90 90 Revert subrepo with no backup. The "reverting s/a" line is gone since
91 91 we're really running 'hg update' in the subrepo:
92 92
93 93 $ echo b > s/a
94 94 $ hg revert --no-backup s
95 95 reverting subrepo s
96 96
97 97 Issue2022: update -C
98 98
99 99 $ echo b > s/a
100 100 $ hg sum
101 101 parent: 1:7cf8cfea66e4 tip
102 102 1
103 103 branch: default
104 104 commit: 1 subrepos
105 105 update: (current)
106 106 phases: 2 draft
107 107 $ hg co -C 1
108 108 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
109 109 $ hg sum
110 110 parent: 1:7cf8cfea66e4 tip
111 111 1
112 112 branch: default
113 113 commit: (clean)
114 114 update: (current)
115 115 phases: 2 draft
116 116
117 117 commands that require a clean repo should respect subrepos
118 118
119 119 $ echo b >> s/a
120 120 $ hg backout tip
121 121 abort: uncommitted changes in subrepository "s"
122 122 [255]
123 123 $ hg revert -C -R s s/a
124 124
125 125 add sub sub
126 126
127 127 $ echo ss = ss > s/.hgsub
128 128 $ hg init s/ss
129 129 $ echo a > s/ss/a
130 130 $ hg -R s add s/.hgsub
131 131 $ hg -R s/ss add s/ss/a
132 132 $ hg sum
133 133 parent: 1:7cf8cfea66e4 tip
134 134 1
135 135 branch: default
136 136 commit: 1 subrepos
137 137 update: (current)
138 138 phases: 2 draft
139 139 $ hg ci -m2
140 140 committing subrepository s
141 141 committing subrepository s/ss
142 142 $ hg sum
143 143 parent: 2:df30734270ae tip
144 144 2
145 145 branch: default
146 146 commit: (clean)
147 147 update: (current)
148 148 phases: 3 draft
149 149
150 150 test handling .hgsubstate "modified" explicitly.
151 151
152 152 $ hg parents --template '{node}\n{files}\n'
153 153 df30734270ae757feb35e643b7018e818e78a9aa
154 154 .hgsubstate
155 155 $ hg rollback -q
156 156 $ hg status -A .hgsubstate
157 157 M .hgsubstate
158 158 $ hg ci -m2
159 159 $ hg parents --template '{node}\n{files}\n'
160 160 df30734270ae757feb35e643b7018e818e78a9aa
161 161 .hgsubstate
162 162
163 163 bump sub rev (and check it is ignored by ui.commitsubrepos)
164 164
165 165 $ echo b > s/a
166 166 $ hg -R s ci -ms1
167 167 $ hg --config ui.commitsubrepos=no ci -m3
168 168
169 169 leave sub dirty (and check ui.commitsubrepos=no aborts the commit)
170 170
171 171 $ echo c > s/a
172 172 $ hg --config ui.commitsubrepos=no ci -m4
173 173 abort: uncommitted changes in subrepository "s"
174 174 (use --subrepos for recursive commit)
175 175 [255]
176 176 $ hg id
177 177 f6affe3fbfaa+ tip
178 178 $ hg -R s ci -mc
179 179 $ hg id
180 180 f6affe3fbfaa+ tip
181 181 $ echo d > s/a
182 182 $ hg ci -m4
183 183 committing subrepository s
184 184 $ hg tip -R s
185 185 changeset: 4:02dcf1d70411
186 186 tag: tip
187 187 user: test
188 188 date: Thu Jan 01 00:00:00 1970 +0000
189 189 summary: 4
190 190
191 191
192 192 check caching
193 193
194 194 $ hg co 0
195 195 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
196 196 $ hg debugsub
197 197
198 198 restore
199 199
200 200 $ hg co
201 201 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
202 202 $ hg debugsub
203 203 path s
204 204 source s
205 205 revision 02dcf1d704118aee3ee306ccfa1910850d5b05ef
206 206
207 207 new branch for merge tests
208 208
209 209 $ hg co 1
210 210 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
211 211 $ echo t = t >> .hgsub
212 212 $ hg init t
213 213 $ echo t > t/t
214 214 $ hg -R t add t
215 215 adding t/t
216 216
217 217 5
218 218
219 219 $ hg ci -m5 # add sub
220 220 committing subrepository t
221 221 created new head
222 222 $ echo t2 > t/t
223 223
224 224 6
225 225
226 226 $ hg st -R s
227 227 $ hg ci -m6 # change sub
228 228 committing subrepository t
229 229 $ hg debugsub
230 230 path s
231 231 source s
232 232 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
233 233 path t
234 234 source t
235 235 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
236 236 $ echo t3 > t/t
237 237
238 238 7
239 239
240 240 $ hg ci -m7 # change sub again for conflict test
241 241 committing subrepository t
242 242 $ hg rm .hgsub
243 243
244 244 8
245 245
246 246 $ hg ci -m8 # remove sub
247 247
248 248 test handling .hgsubstate "removed" explicitly.
249 249
250 250 $ hg parents --template '{node}\n{files}\n'
251 251 96615c1dad2dc8e3796d7332c77ce69156f7b78e
252 252 .hgsub .hgsubstate
253 253 $ hg rollback -q
254 254 $ hg remove .hgsubstate
255 255 $ hg ci -m8
256 256 $ hg parents --template '{node}\n{files}\n'
257 257 96615c1dad2dc8e3796d7332c77ce69156f7b78e
258 258 .hgsub .hgsubstate
259 259
260 260 merge tests
261 261
262 262 $ hg co -C 3
263 263 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
264 264 $ hg merge 5 # test adding
265 265 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
266 266 (branch merge, don't forget to commit)
267 267 $ hg debugsub
268 268 path s
269 269 source s
270 270 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
271 271 path t
272 272 source t
273 273 revision 60ca1237c19474e7a3978b0dc1ca4e6f36d51382
274 274 $ hg ci -m9
275 275 created new head
276 276 $ hg merge 6 --debug # test change
277 277 resolving manifests
278 278 branchmerge: True, force: False, partial: False
279 279 ancestor: 1f14a2e2d3ec, local: f0d2028bf86d+, remote: 1831e14459c4
280 280 starting 4 threads for background file closing (?)
281 281 .hgsubstate: versions differ -> m (premerge)
282 282 subrepo merge f0d2028bf86d+ 1831e14459c4 1f14a2e2d3ec
283 283 subrepo t: other changed, get t:6747d179aa9a688023c4b0cad32e4c92bb7f34ad:hg
284 284 getting subrepo t
285 285 resolving manifests
286 286 branchmerge: False, force: False, partial: False
287 287 ancestor: 60ca1237c194, local: 60ca1237c194+, remote: 6747d179aa9a
288 288 t: remote is newer -> g
289 289 getting t
290 290 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
291 291 (branch merge, don't forget to commit)
292 292 $ hg debugsub
293 293 path s
294 294 source s
295 295 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
296 296 path t
297 297 source t
298 298 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
299 299 $ echo conflict > t/t
300 300 $ hg ci -m10
301 301 committing subrepository t
302 302 $ HGMERGE=internal:merge hg merge --debug 7 # test conflict
303 303 resolving manifests
304 304 branchmerge: True, force: False, partial: False
305 305 ancestor: 1831e14459c4, local: e45c8b14af55+, remote: f94576341bcf
306 306 starting 4 threads for background file closing (?)
307 307 .hgsubstate: versions differ -> m (premerge)
308 308 subrepo merge e45c8b14af55+ f94576341bcf 1831e14459c4
309 309 subrepo t: both sides changed
310 310 subrepository t diverged (local revision: 20a0db6fbf6c, remote revision: 7af322bc1198)
311 311 starting 4 threads for background file closing (?)
312 (M)erge, keep (l)ocal [working copy] or keep (r)emote [merge rev]? m
312 you can (m)erge, keep (l)ocal [working copy] or keep (r)emote [merge rev].
313 what do you want to do? m
313 314 merging subrepository "t"
314 315 resolving manifests
315 316 branchmerge: True, force: False, partial: False
316 317 ancestor: 6747d179aa9a, local: 20a0db6fbf6c+, remote: 7af322bc1198
318 starting 4 threads for background file closing (?)
317 319 preserving t for resolve of t
318 starting 4 threads for background file closing (?)
319 320 t: versions differ -> m (premerge)
320 321 picked tool ':merge' for t (binary False symlink False changedelete False)
321 322 merging t
322 323 my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
323 324 t: versions differ -> m (merge)
324 325 picked tool ':merge' for t (binary False symlink False changedelete False)
325 326 my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
326 327 warning: conflicts while merging t! (edit, then use 'hg resolve --mark')
327 328 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
328 329 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
329 330 subrepo t: merge with t:7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4:hg
330 331 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
331 332 (branch merge, don't forget to commit)
332 333
333 334 should conflict
334 335
335 336 $ cat t/t
336 337 <<<<<<< local: 20a0db6fbf6c - test: 10
337 338 conflict
338 339 =======
339 340 t3
340 341 >>>>>>> other: 7af322bc1198 - test: 7
341 342
342 343 11: remove subrepo t
343 344
344 345 $ hg co -C 5
345 346 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
346 347 $ hg revert -r 4 .hgsub # remove t
347 348 $ hg ci -m11
348 349 created new head
349 350 $ hg debugsub
350 351 path s
351 352 source s
352 353 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
353 354
354 355 local removed, remote changed, keep changed
355 356
356 357 $ hg merge 6
357 358 remote [merge rev] changed subrepository t which local [working copy] removed
358 359 use (c)hanged version or (d)elete? c
359 360 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
360 361 (branch merge, don't forget to commit)
361 362 BROKEN: should include subrepo t
362 363 $ hg debugsub
363 364 path s
364 365 source s
365 366 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
366 367 $ cat .hgsubstate
367 368 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
368 369 6747d179aa9a688023c4b0cad32e4c92bb7f34ad t
369 370 $ hg ci -m 'local removed, remote changed, keep changed'
370 371 BROKEN: should include subrepo t
371 372 $ hg debugsub
372 373 path s
373 374 source s
374 375 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
375 376 BROKEN: should include subrepo t
376 377 $ cat .hgsubstate
377 378 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
378 379 $ cat t/t
379 380 t2
380 381
381 382 local removed, remote changed, keep removed
382 383
383 384 $ hg co -C 11
384 385 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
385 386 $ hg merge --config ui.interactive=true 6 <<EOF
386 387 > d
387 388 > EOF
388 389 remote [merge rev] changed subrepository t which local [working copy] removed
389 390 use (c)hanged version or (d)elete? d
390 391 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
391 392 (branch merge, don't forget to commit)
392 393 $ hg debugsub
393 394 path s
394 395 source s
395 396 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
396 397 $ cat .hgsubstate
397 398 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
398 399 $ hg ci -m 'local removed, remote changed, keep removed'
399 400 created new head
400 401 $ hg debugsub
401 402 path s
402 403 source s
403 404 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
404 405 $ cat .hgsubstate
405 406 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
406 407
407 408 local changed, remote removed, keep changed
408 409
409 410 $ hg co -C 6
410 411 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
411 412 $ hg merge 11
412 413 local [working copy] changed subrepository t which remote [merge rev] removed
413 414 use (c)hanged version or (d)elete? c
414 415 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
415 416 (branch merge, don't forget to commit)
416 417 BROKEN: should include subrepo t
417 418 $ hg debugsub
418 419 path s
419 420 source s
420 421 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
421 422 BROKEN: should include subrepo t
422 423 $ cat .hgsubstate
423 424 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
424 425 $ hg ci -m 'local changed, remote removed, keep changed'
425 426 created new head
426 427 BROKEN: should include subrepo t
427 428 $ hg debugsub
428 429 path s
429 430 source s
430 431 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
431 432 BROKEN: should include subrepo t
432 433 $ cat .hgsubstate
433 434 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
434 435 $ cat t/t
435 436 t2
436 437
437 438 local changed, remote removed, keep removed
438 439
439 440 $ hg co -C 6
440 441 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
441 442 $ hg merge --config ui.interactive=true 11 <<EOF
442 443 > d
443 444 > EOF
444 445 local [working copy] changed subrepository t which remote [merge rev] removed
445 446 use (c)hanged version or (d)elete? d
446 447 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
447 448 (branch merge, don't forget to commit)
448 449 $ hg debugsub
449 450 path s
450 451 source s
451 452 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
452 453 $ cat .hgsubstate
453 454 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
454 455 $ hg ci -m 'local changed, remote removed, keep removed'
455 456 created new head
456 457 $ hg debugsub
457 458 path s
458 459 source s
459 460 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
460 461 $ cat .hgsubstate
461 462 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
462 463
463 464 clean up to avoid having to fix up the tests below
464 465
465 466 $ hg co -C 10
466 467 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
467 468 $ cat >> $HGRCPATH <<EOF
468 469 > [extensions]
469 470 > strip=
470 471 > EOF
471 472 $ hg strip -r 11:15
472 473 saved backup bundle to $TESTTMP/t/.hg/strip-backup/*-backup.hg (glob)
473 474
474 475 clone
475 476
476 477 $ cd ..
477 478 $ hg clone t tc
478 479 updating to branch default
479 480 cloning subrepo s from $TESTTMP/t/s
480 481 cloning subrepo s/ss from $TESTTMP/t/s/ss
481 482 cloning subrepo t from $TESTTMP/t/t
482 483 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
483 484 $ cd tc
484 485 $ hg debugsub
485 486 path s
486 487 source s
487 488 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
488 489 path t
489 490 source t
490 491 revision 20a0db6fbf6c3d2836e6519a642ae929bfc67c0e
491 492 $ cd ..
492 493
493 494 clone with subrepo disabled (update should fail)
494 495
495 496 $ hg clone t -U tc2 --config subrepos.allowed=false
496 497 $ hg update -R tc2 --config subrepos.allowed=false
497 498 abort: subrepos not enabled
498 499 (see 'hg help config.subrepos' for details)
499 500 [255]
500 501 $ ls tc2
501 502 a
502 503
503 504 $ hg clone t tc3 --config subrepos.allowed=false
504 505 updating to branch default
505 506 abort: subrepos not enabled
506 507 (see 'hg help config.subrepos' for details)
507 508 [255]
508 509 $ ls tc3
509 510 a
510 511
511 512 And again with just the hg type disabled
512 513
513 514 $ hg clone t -U tc4 --config subrepos.hg:allowed=false
514 515 $ hg update -R tc4 --config subrepos.hg:allowed=false
515 516 abort: hg subrepos not allowed
516 517 (see 'hg help config.subrepos' for details)
517 518 [255]
518 519 $ ls tc4
519 520 a
520 521
521 522 $ hg clone t tc5 --config subrepos.hg:allowed=false
522 523 updating to branch default
523 524 abort: hg subrepos not allowed
524 525 (see 'hg help config.subrepos' for details)
525 526 [255]
526 527 $ ls tc5
527 528 a
528 529
529 530 push
530 531
531 532 $ cd tc
532 533 $ echo bah > t/t
533 534 $ hg ci -m11
534 535 committing subrepository t
535 536 $ hg push
536 537 pushing to $TESTTMP/t
537 538 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss
538 539 no changes made to subrepo s since last push to $TESTTMP/t/s
539 540 pushing subrepo t to $TESTTMP/t/t
540 541 searching for changes
541 542 adding changesets
542 543 adding manifests
543 544 adding file changes
544 545 added 1 changesets with 1 changes to 1 files
545 546 searching for changes
546 547 adding changesets
547 548 adding manifests
548 549 adding file changes
549 550 added 1 changesets with 1 changes to 1 files
550 551
551 552 push -f
552 553
553 554 $ echo bah > s/a
554 555 $ hg ci -m12
555 556 committing subrepository s
556 557 $ hg push
557 558 pushing to $TESTTMP/t
558 559 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss
559 560 pushing subrepo s to $TESTTMP/t/s
560 561 searching for changes
561 562 abort: push creates new remote head 12a213df6fa9! (in subrepository "s")
562 563 (merge or see 'hg help push' for details about pushing new heads)
563 564 [255]
564 565 $ hg push -f
565 566 pushing to $TESTTMP/t
566 567 pushing subrepo s/ss to $TESTTMP/t/s/ss
567 568 searching for changes
568 569 no changes found
569 570 pushing subrepo s to $TESTTMP/t/s
570 571 searching for changes
571 572 adding changesets
572 573 adding manifests
573 574 adding file changes
574 575 added 1 changesets with 1 changes to 1 files (+1 heads)
575 576 pushing subrepo t to $TESTTMP/t/t
576 577 searching for changes
577 578 no changes found
578 579 searching for changes
579 580 adding changesets
580 581 adding manifests
581 582 adding file changes
582 583 added 1 changesets with 1 changes to 1 files
583 584
584 585 check that unmodified subrepos are not pushed
585 586
586 587 $ hg clone . ../tcc
587 588 updating to branch default
588 589 cloning subrepo s from $TESTTMP/tc/s
589 590 cloning subrepo s/ss from $TESTTMP/tc/s/ss
590 591 cloning subrepo t from $TESTTMP/tc/t
591 592 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
592 593
593 594 the subrepos on the new clone have nothing to push to its source
594 595
595 596 $ hg push -R ../tcc .
596 597 pushing to .
597 598 no changes made to subrepo s/ss since last push to s/ss
598 599 no changes made to subrepo s since last push to s
599 600 no changes made to subrepo t since last push to t
600 601 searching for changes
601 602 no changes found
602 603 [1]
603 604
604 605 the subrepos on the source do not have a clean store versus the clone target
605 606 because they were never explicitly pushed to the source
606 607
607 608 $ hg push ../tcc
608 609 pushing to ../tcc
609 610 pushing subrepo s/ss to ../tcc/s/ss
610 611 searching for changes
611 612 no changes found
612 613 pushing subrepo s to ../tcc/s
613 614 searching for changes
614 615 no changes found
615 616 pushing subrepo t to ../tcc/t
616 617 searching for changes
617 618 no changes found
618 619 searching for changes
619 620 no changes found
620 621 [1]
621 622
622 623 after push their stores become clean
623 624
624 625 $ hg push ../tcc
625 626 pushing to ../tcc
626 627 no changes made to subrepo s/ss since last push to ../tcc/s/ss
627 628 no changes made to subrepo s since last push to ../tcc/s
628 629 no changes made to subrepo t since last push to ../tcc/t
629 630 searching for changes
630 631 no changes found
631 632 [1]
632 633
633 634 updating a subrepo to a different revision or changing
634 635 its working directory does not make its store dirty
635 636
636 637 $ hg -R s update '.^'
637 638 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
638 639 $ hg push
639 640 pushing to $TESTTMP/t
640 641 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss
641 642 no changes made to subrepo s since last push to $TESTTMP/t/s
642 643 no changes made to subrepo t since last push to $TESTTMP/t/t
643 644 searching for changes
644 645 no changes found
645 646 [1]
646 647 $ echo foo >> s/a
647 648 $ hg push
648 649 pushing to $TESTTMP/t
649 650 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss
650 651 no changes made to subrepo s since last push to $TESTTMP/t/s
651 652 no changes made to subrepo t since last push to $TESTTMP/t/t
652 653 searching for changes
653 654 no changes found
654 655 [1]
655 656 $ hg -R s update -C tip
656 657 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
657 658
658 659 committing into a subrepo makes its store (but not its parent's store) dirty
659 660
660 661 $ echo foo >> s/ss/a
661 662 $ hg -R s/ss commit -m 'test dirty store detection'
662 663
663 664 $ hg out -S -r `hg log -r tip -T "{node|short}"`
664 665 comparing with $TESTTMP/t
665 666 searching for changes
666 667 no changes found
667 668 comparing with $TESTTMP/t/s
668 669 searching for changes
669 670 no changes found
670 671 comparing with $TESTTMP/t/s/ss
671 672 searching for changes
672 673 changeset: 1:79ea5566a333
673 674 tag: tip
674 675 user: test
675 676 date: Thu Jan 01 00:00:00 1970 +0000
676 677 summary: test dirty store detection
677 678
678 679 comparing with $TESTTMP/t/t
679 680 searching for changes
680 681 no changes found
681 682
682 683 $ hg push
683 684 pushing to $TESTTMP/t
684 685 pushing subrepo s/ss to $TESTTMP/t/s/ss
685 686 searching for changes
686 687 adding changesets
687 688 adding manifests
688 689 adding file changes
689 690 added 1 changesets with 1 changes to 1 files
690 691 no changes made to subrepo s since last push to $TESTTMP/t/s
691 692 no changes made to subrepo t since last push to $TESTTMP/t/t
692 693 searching for changes
693 694 no changes found
694 695 [1]
695 696
696 697 a subrepo store may be clean versus one repo but not versus another
697 698
698 699 $ hg push
699 700 pushing to $TESTTMP/t
700 701 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss
701 702 no changes made to subrepo s since last push to $TESTTMP/t/s
702 703 no changes made to subrepo t since last push to $TESTTMP/t/t
703 704 searching for changes
704 705 no changes found
705 706 [1]
706 707 $ hg push ../tcc
707 708 pushing to ../tcc
708 709 pushing subrepo s/ss to ../tcc/s/ss
709 710 searching for changes
710 711 adding changesets
711 712 adding manifests
712 713 adding file changes
713 714 added 1 changesets with 1 changes to 1 files
714 715 no changes made to subrepo s since last push to ../tcc/s
715 716 no changes made to subrepo t since last push to ../tcc/t
716 717 searching for changes
717 718 no changes found
718 719 [1]
719 720
720 721 update
721 722
722 723 $ cd ../t
723 724 $ hg up -C # discard our earlier merge
724 725 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
725 726 updated to "c373c8102e68: 12"
726 727 2 other heads for branch "default"
727 728 $ echo blah > t/t
728 729 $ hg ci -m13
729 730 committing subrepository t
730 731
731 732 backout calls revert internally with minimal opts, which should not raise
732 733 KeyError
733 734
734 735 $ hg backout ".^" --no-commit
735 736 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
736 737 changeset c373c8102e68 backed out, don't forget to commit.
737 738
738 739 $ hg up -C # discard changes
739 740 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
740 741 updated to "925c17564ef8: 13"
741 742 2 other heads for branch "default"
742 743
743 744 pull
744 745
745 746 $ cd ../tc
746 747 $ hg pull
747 748 pulling from $TESTTMP/t
748 749 searching for changes
749 750 adding changesets
750 751 adding manifests
751 752 adding file changes
752 753 added 1 changesets with 1 changes to 1 files
753 754 new changesets 925c17564ef8
754 755 (run 'hg update' to get a working copy)
755 756
756 757 should pull t
757 758
758 759 $ hg incoming -S -r `hg log -r tip -T "{node|short}"`
759 760 comparing with $TESTTMP/t
760 761 no changes found
761 762 comparing with $TESTTMP/t/s
762 763 searching for changes
763 764 no changes found
764 765 comparing with $TESTTMP/t/s/ss
765 766 searching for changes
766 767 no changes found
767 768 comparing with $TESTTMP/t/t
768 769 searching for changes
769 770 changeset: 5:52c0adc0515a
770 771 tag: tip
771 772 user: test
772 773 date: Thu Jan 01 00:00:00 1970 +0000
773 774 summary: 13
774 775
775 776
776 777 $ hg up
777 778 pulling subrepo t from $TESTTMP/t/t
778 779 searching for changes
779 780 adding changesets
780 781 adding manifests
781 782 adding file changes
782 783 added 1 changesets with 1 changes to 1 files
783 784 new changesets 52c0adc0515a
784 785 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
785 786 updated to "925c17564ef8: 13"
786 787 2 other heads for branch "default"
787 788 $ cat t/t
788 789 blah
789 790
790 791 bogus subrepo path aborts
791 792
792 793 $ echo 'bogus=[boguspath' >> .hgsub
793 794 $ hg ci -m 'bogus subrepo path'
794 795 abort: missing ] in subrepository source
795 796 [255]
796 797
797 798 Issue1986: merge aborts when trying to merge a subrepo that
798 799 shouldn't need merging
799 800
800 801 # subrepo layout
801 802 #
802 803 # o 5 br
803 804 # /|
804 805 # o | 4 default
805 806 # | |
806 807 # | o 3 br
807 808 # |/|
808 809 # o | 2 default
809 810 # | |
810 811 # | o 1 br
811 812 # |/
812 813 # o 0 default
813 814
814 815 $ cd ..
815 816 $ rm -rf sub
816 817 $ hg init main
817 818 $ cd main
818 819 $ hg init s
819 820 $ cd s
820 821 $ echo a > a
821 822 $ hg ci -Am1
822 823 adding a
823 824 $ hg branch br
824 825 marked working directory as branch br
825 826 (branches are permanent and global, did you want a bookmark?)
826 827 $ echo a >> a
827 828 $ hg ci -m1
828 829 $ hg up default
829 830 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
830 831 $ echo b > b
831 832 $ hg ci -Am1
832 833 adding b
833 834 $ hg up br
834 835 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
835 836 $ hg merge tip
836 837 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
837 838 (branch merge, don't forget to commit)
838 839 $ hg ci -m1
839 840 $ hg up 2
840 841 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
841 842 $ echo c > c
842 843 $ hg ci -Am1
843 844 adding c
844 845 $ hg up 3
845 846 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
846 847 $ hg merge 4
847 848 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
848 849 (branch merge, don't forget to commit)
849 850 $ hg ci -m1
850 851
851 852 # main repo layout:
852 853 #
853 854 # * <-- try to merge default into br again
854 855 # .`|
855 856 # . o 5 br --> substate = 5
856 857 # . |
857 858 # o | 4 default --> substate = 4
858 859 # | |
859 860 # | o 3 br --> substate = 2
860 861 # |/|
861 862 # o | 2 default --> substate = 2
862 863 # | |
863 864 # | o 1 br --> substate = 3
864 865 # |/
865 866 # o 0 default --> substate = 2
866 867
867 868 $ cd ..
868 869 $ echo 's = s' > .hgsub
869 870 $ hg -R s up 2
870 871 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
871 872 $ hg ci -Am1
872 873 adding .hgsub
873 874 $ hg branch br
874 875 marked working directory as branch br
875 876 (branches are permanent and global, did you want a bookmark?)
876 877 $ echo b > b
877 878 $ hg -R s up 3
878 879 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
879 880 $ hg ci -Am1
880 881 adding b
881 882 $ hg up default
882 883 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
883 884 $ echo c > c
884 885 $ hg ci -Am1
885 886 adding c
886 887 $ hg up 1
887 888 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
888 889 $ hg merge 2
889 890 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
890 891 (branch merge, don't forget to commit)
891 892 $ hg ci -m1
892 893 $ hg up 2
893 894 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
894 895 $ hg -R s up 4
895 896 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
896 897 $ echo d > d
897 898 $ hg ci -Am1
898 899 adding d
899 900 $ hg up 3
900 901 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
901 902 $ hg -R s up 5
902 903 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
903 904 $ echo e > e
904 905 $ hg ci -Am1
905 906 adding e
906 907
907 908 $ hg up 5
908 909 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
909 910 $ hg merge 4 # try to merge default into br again
910 911 subrepository s diverged (local revision: f8f13b33206e, remote revision: a3f9062a4f88)
911 (M)erge, keep (l)ocal [working copy] or keep (r)emote [merge rev]? m
912 you can (m)erge, keep (l)ocal [working copy] or keep (r)emote [merge rev].
913 what do you want to do? m
912 914 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
913 915 (branch merge, don't forget to commit)
914 916 $ cd ..
915 917
916 918 test subrepo delete from .hgsubstate
917 919
918 920 $ hg init testdelete
919 921 $ mkdir testdelete/nested testdelete/nested2
920 922 $ hg init testdelete/nested
921 923 $ hg init testdelete/nested2
922 924 $ echo test > testdelete/nested/foo
923 925 $ echo test > testdelete/nested2/foo
924 926 $ hg -R testdelete/nested add
925 927 adding testdelete/nested/foo
926 928 $ hg -R testdelete/nested2 add
927 929 adding testdelete/nested2/foo
928 930 $ hg -R testdelete/nested ci -m test
929 931 $ hg -R testdelete/nested2 ci -m test
930 932 $ echo nested = nested > testdelete/.hgsub
931 933 $ echo nested2 = nested2 >> testdelete/.hgsub
932 934 $ hg -R testdelete add
933 935 adding testdelete/.hgsub
934 936 $ hg -R testdelete ci -m "nested 1 & 2 added"
935 937 $ echo nested = nested > testdelete/.hgsub
936 938 $ hg -R testdelete ci -m "nested 2 deleted"
937 939 $ cat testdelete/.hgsubstate
938 940 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
939 941 $ hg -R testdelete remove testdelete/.hgsub
940 942 $ hg -R testdelete ci -m ".hgsub deleted"
941 943 $ cat testdelete/.hgsubstate
942 944 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
943 945
944 946 test repository cloning
945 947
946 948 $ mkdir mercurial mercurial2
947 949 $ hg init nested_absolute
948 950 $ echo test > nested_absolute/foo
949 951 $ hg -R nested_absolute add
950 952 adding nested_absolute/foo
951 953 $ hg -R nested_absolute ci -mtest
952 954 $ cd mercurial
953 955 $ hg init nested_relative
954 956 $ echo test2 > nested_relative/foo2
955 957 $ hg -R nested_relative add
956 958 adding nested_relative/foo2
957 959 $ hg -R nested_relative ci -mtest2
958 960 $ hg init main
959 961 $ echo "nested_relative = ../nested_relative" > main/.hgsub
960 962 $ echo "nested_absolute = `pwd`/nested_absolute" >> main/.hgsub
961 963 $ hg -R main add
962 964 adding main/.hgsub
963 965 $ hg -R main ci -m "add subrepos"
964 966 $ cd ..
965 967 $ hg clone mercurial/main mercurial2/main
966 968 updating to branch default
967 969 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
968 970 $ cat mercurial2/main/nested_absolute/.hg/hgrc \
969 971 > mercurial2/main/nested_relative/.hg/hgrc
970 972 [paths]
971 973 default = $TESTTMP/mercurial/nested_absolute
972 974 [paths]
973 975 default = $TESTTMP/mercurial/nested_relative
974 976 $ rm -rf mercurial mercurial2
975 977
976 978 Issue1977: multirepo push should fail if subrepo push fails
977 979
978 980 $ hg init repo
979 981 $ hg init repo/s
980 982 $ echo a > repo/s/a
981 983 $ hg -R repo/s ci -Am0
982 984 adding a
983 985 $ echo s = s > repo/.hgsub
984 986 $ hg -R repo ci -Am1
985 987 adding .hgsub
986 988 $ hg clone repo repo2
987 989 updating to branch default
988 990 cloning subrepo s from $TESTTMP/repo/s
989 991 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
990 992 $ hg -q -R repo2 pull -u
991 993 $ echo 1 > repo2/s/a
992 994 $ hg -R repo2/s ci -m2
993 995 $ hg -q -R repo2/s push
994 996 $ hg -R repo2/s up -C 0
995 997 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
996 998 $ echo 2 > repo2/s/b
997 999 $ hg -R repo2/s ci -m3 -A
998 1000 adding b
999 1001 created new head
1000 1002 $ hg -R repo2 ci -m3
1001 1003 $ hg -q -R repo2 push
1002 1004 abort: push creates new remote head cc505f09a8b2! (in subrepository "s")
1003 1005 (merge or see 'hg help push' for details about pushing new heads)
1004 1006 [255]
1005 1007 $ hg -R repo update
1006 1008 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1007 1009
1008 1010 test if untracked file is not overwritten
1009 1011
1010 1012 (this also tests that updated .hgsubstate is treated as "modified",
1011 1013 when 'merge.update()' is aborted before 'merge.recordupdates()', even
1012 1014 if none of mode, size and timestamp of it isn't changed on the
1013 1015 filesystem (see also issue4583))
1014 1016
1015 1017 $ echo issue3276_ok > repo/s/b
1016 1018 $ hg -R repo2 push -f -q
1017 1019 $ touch -t 200001010000 repo/.hgsubstate
1018 1020
1019 1021 $ cat >> repo/.hg/hgrc <<EOF
1020 1022 > [fakedirstatewritetime]
1021 1023 > # emulate invoking dirstate.write() via repo.status()
1022 1024 > # at 2000-01-01 00:00
1023 1025 > fakenow = 200001010000
1024 1026 >
1025 1027 > [extensions]
1026 1028 > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py
1027 1029 > EOF
1028 1030 $ hg -R repo update
1029 1031 b: untracked file differs
1030 1032 abort: untracked files in working directory differ from files in requested revision (in subrepository "s")
1031 1033 [255]
1032 1034 $ cat >> repo/.hg/hgrc <<EOF
1033 1035 > [extensions]
1034 1036 > fakedirstatewritetime = !
1035 1037 > EOF
1036 1038
1037 1039 $ cat repo/s/b
1038 1040 issue3276_ok
1039 1041 $ rm repo/s/b
1040 1042 $ touch -t 200001010000 repo/.hgsubstate
1041 1043 $ hg -R repo revert --all
1042 1044 reverting repo/.hgsubstate
1043 1045 reverting subrepo s
1044 1046 $ hg -R repo update
1045 1047 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1046 1048 $ cat repo/s/b
1047 1049 2
1048 1050 $ rm -rf repo2 repo
1049 1051
1050 1052
1051 1053 Issue1852 subrepos with relative paths always push/pull relative to default
1052 1054
1053 1055 Prepare a repo with subrepo
1054 1056
1055 1057 $ hg init issue1852a
1056 1058 $ cd issue1852a
1057 1059 $ hg init sub/repo
1058 1060 $ echo test > sub/repo/foo
1059 1061 $ hg -R sub/repo add sub/repo/foo
1060 1062 $ echo sub/repo = sub/repo > .hgsub
1061 1063 $ hg add .hgsub
1062 1064 $ hg ci -mtest
1063 1065 committing subrepository sub/repo
1064 1066 $ echo test >> sub/repo/foo
1065 1067 $ hg ci -mtest
1066 1068 committing subrepository sub/repo
1067 1069 $ hg cat sub/repo/foo
1068 1070 test
1069 1071 test
1070 1072 $ hg cat sub/repo/foo -Tjson | sed 's|\\\\|/|g'
1071 1073 [
1072 1074 {
1073 1075 "data": "test\ntest\n",
1074 1076 "path": "foo"
1075 1077 }
1076 1078 ]
1077 1079
1078 1080 non-exact match:
1079 1081
1080 1082 $ hg cat -T '{path|relpath}\n' 'glob:**'
1081 1083 .hgsub
1082 1084 .hgsubstate
1083 1085 sub/repo/foo
1084 1086 $ hg cat -T '{path|relpath}\n' 're:^sub'
1085 1087 sub/repo/foo
1086 1088
1087 1089 missing subrepos in working directory:
1088 1090
1089 1091 $ mkdir -p tmp/sub/repo
1090 1092 $ hg cat -r 0 --output tmp/%p_p sub/repo/foo
1091 1093 $ cat tmp/sub/repo/foo_p
1092 1094 test
1093 1095 $ mv sub/repo sub_
1094 1096 $ hg cat sub/repo/baz
1095 1097 skipping missing subrepository: sub/repo
1096 1098 [1]
1097 1099 $ rm -rf sub/repo
1098 1100 $ mv sub_ sub/repo
1099 1101 $ cd ..
1100 1102
1101 1103 Create repo without default path, pull top repo, and see what happens on update
1102 1104
1103 1105 $ hg init issue1852b
1104 1106 $ hg -R issue1852b pull issue1852a
1105 1107 pulling from issue1852a
1106 1108 requesting all changes
1107 1109 adding changesets
1108 1110 adding manifests
1109 1111 adding file changes
1110 1112 added 2 changesets with 3 changes to 2 files
1111 1113 new changesets 19487b456929:be5eb94e7215
1112 1114 (run 'hg update' to get a working copy)
1113 1115 $ hg -R issue1852b update
1114 1116 abort: default path for subrepository not found (in subrepository "sub/repo")
1115 1117 [255]
1116 1118
1117 1119 Ensure a full traceback, not just the SubrepoAbort part
1118 1120
1119 1121 $ hg -R issue1852b update --traceback 2>&1 | grep 'raise error\.Abort'
1120 1122 raise error.Abort(_("default path for subrepository not found"))
1121 1123
1122 1124 Pull -u now doesn't help
1123 1125
1124 1126 $ hg -R issue1852b pull -u issue1852a
1125 1127 pulling from issue1852a
1126 1128 searching for changes
1127 1129 no changes found
1128 1130
1129 1131 Try the same, but with pull -u
1130 1132
1131 1133 $ hg init issue1852c
1132 1134 $ hg -R issue1852c pull -r0 -u issue1852a
1133 1135 pulling from issue1852a
1134 1136 adding changesets
1135 1137 adding manifests
1136 1138 adding file changes
1137 1139 added 1 changesets with 2 changes to 2 files
1138 1140 new changesets 19487b456929
1139 1141 cloning subrepo sub/repo from issue1852a/sub/repo
1140 1142 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1141 1143
1142 1144 Try to push from the other side
1143 1145
1144 1146 $ hg -R issue1852a push `pwd`/issue1852c
1145 1147 pushing to $TESTTMP/issue1852c
1146 1148 pushing subrepo sub/repo to $TESTTMP/issue1852c/sub/repo
1147 1149 searching for changes
1148 1150 no changes found
1149 1151 searching for changes
1150 1152 adding changesets
1151 1153 adding manifests
1152 1154 adding file changes
1153 1155 added 1 changesets with 1 changes to 1 files
1154 1156
1155 1157 Incoming and outgoing should not use the default path:
1156 1158
1157 1159 $ hg clone -q issue1852a issue1852d
1158 1160 $ hg -R issue1852d outgoing --subrepos issue1852c
1159 1161 comparing with issue1852c
1160 1162 searching for changes
1161 1163 no changes found
1162 1164 comparing with issue1852c/sub/repo
1163 1165 searching for changes
1164 1166 no changes found
1165 1167 [1]
1166 1168 $ hg -R issue1852d incoming --subrepos issue1852c
1167 1169 comparing with issue1852c
1168 1170 searching for changes
1169 1171 no changes found
1170 1172 comparing with issue1852c/sub/repo
1171 1173 searching for changes
1172 1174 no changes found
1173 1175 [1]
1174 1176
1175 1177 Check that merge of a new subrepo doesn't write the uncommitted state to
1176 1178 .hgsubstate (issue4622)
1177 1179
1178 1180 $ hg init issue1852a/addedsub
1179 1181 $ echo zzz > issue1852a/addedsub/zz.txt
1180 1182 $ hg -R issue1852a/addedsub ci -Aqm "initial ZZ"
1181 1183
1182 1184 $ hg clone issue1852a/addedsub issue1852d/addedsub
1183 1185 updating to branch default
1184 1186 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1185 1187
1186 1188 $ echo def > issue1852a/sub/repo/foo
1187 1189 $ hg -R issue1852a ci -SAm 'tweaked subrepo'
1188 1190 adding tmp/sub/repo/foo_p
1189 1191 committing subrepository sub/repo
1190 1192
1191 1193 $ echo 'addedsub = addedsub' >> issue1852d/.hgsub
1192 1194 $ echo xyz > issue1852d/sub/repo/foo
1193 1195 $ hg -R issue1852d pull -u
1194 1196 pulling from $TESTTMP/issue1852a
1195 1197 searching for changes
1196 1198 adding changesets
1197 1199 adding manifests
1198 1200 adding file changes
1199 1201 added 1 changesets with 2 changes to 2 files
1200 1202 new changesets c82b79fdcc5b
1201 1203 subrepository sub/repo diverged (local revision: f42d5c7504a8, remote revision: 46cd4aac504c)
1202 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1204 you can (m)erge, keep (l)ocal [working copy] or keep (r)emote [destination].
1205 what do you want to do? m
1203 1206 pulling subrepo sub/repo from $TESTTMP/issue1852a/sub/repo
1204 1207 searching for changes
1205 1208 adding changesets
1206 1209 adding manifests
1207 1210 adding file changes
1208 1211 added 1 changesets with 1 changes to 1 files
1209 1212 new changesets 46cd4aac504c
1210 1213 subrepository sources for sub/repo differ
1211 use (l)ocal source (f42d5c7504a8) or (r)emote source (46cd4aac504c)? l
1214 you can use (l)ocal source (f42d5c7504a8) or (r)emote source (46cd4aac504c).
1215 what do you want to do? l
1212 1216 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1213 1217 $ cat issue1852d/.hgsubstate
1214 1218 f42d5c7504a811dda50f5cf3e5e16c3330b87172 sub/repo
1215 1219
1216 1220 Check status of files when none of them belong to the first
1217 1221 subrepository:
1218 1222
1219 1223 $ hg init subrepo-status
1220 1224 $ cd subrepo-status
1221 1225 $ hg init subrepo-1
1222 1226 $ hg init subrepo-2
1223 1227 $ cd subrepo-2
1224 1228 $ touch file
1225 1229 $ hg add file
1226 1230 $ cd ..
1227 1231 $ echo subrepo-1 = subrepo-1 > .hgsub
1228 1232 $ echo subrepo-2 = subrepo-2 >> .hgsub
1229 1233 $ hg add .hgsub
1230 1234 $ hg ci -m 'Added subrepos'
1231 1235 committing subrepository subrepo-2
1232 1236 $ hg st subrepo-2/file
1233 1237
1234 1238 Check that share works with subrepo
1235 1239 $ hg --config extensions.share= share . ../shared
1236 1240 updating working directory
1237 1241 sharing subrepo subrepo-1 from $TESTTMP/subrepo-status/subrepo-1
1238 1242 sharing subrepo subrepo-2 from $TESTTMP/subrepo-status/subrepo-2
1239 1243 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1240 1244 $ find ../shared/* | sort
1241 1245 ../shared/subrepo-1
1242 1246 ../shared/subrepo-1/.hg
1243 1247 ../shared/subrepo-1/.hg/cache
1244 1248 ../shared/subrepo-1/.hg/cache/storehash
1245 1249 ../shared/subrepo-1/.hg/cache/storehash/* (glob)
1246 1250 ../shared/subrepo-1/.hg/hgrc
1247 1251 ../shared/subrepo-1/.hg/requires
1248 1252 ../shared/subrepo-1/.hg/sharedpath
1249 1253 ../shared/subrepo-1/.hg/wcache
1250 1254 ../shared/subrepo-2
1251 1255 ../shared/subrepo-2/.hg
1252 1256 ../shared/subrepo-2/.hg/branch
1253 1257 ../shared/subrepo-2/.hg/cache
1254 1258 ../shared/subrepo-2/.hg/cache/storehash
1255 1259 ../shared/subrepo-2/.hg/cache/storehash/* (glob)
1256 1260 ../shared/subrepo-2/.hg/dirstate
1257 1261 ../shared/subrepo-2/.hg/hgrc
1258 1262 ../shared/subrepo-2/.hg/requires
1259 1263 ../shared/subrepo-2/.hg/sharedpath
1260 1264 ../shared/subrepo-2/.hg/wcache
1261 1265 ../shared/subrepo-2/.hg/wcache/checkisexec (execbit !)
1262 1266 ../shared/subrepo-2/.hg/wcache/checklink (symlink !)
1263 1267 ../shared/subrepo-2/.hg/wcache/checklink-target (symlink !)
1264 1268 ../shared/subrepo-2/.hg/wcache/manifestfulltextcache (reporevlogstore !)
1265 1269 ../shared/subrepo-2/file
1266 1270 $ hg -R ../shared in
1267 1271 abort: repository default not found!
1268 1272 [255]
1269 1273 $ hg -R ../shared/subrepo-2 showconfig paths
1270 1274 paths.default=$TESTTMP/subrepo-status/subrepo-2
1271 1275 $ hg -R ../shared/subrepo-1 sum --remote
1272 1276 parent: -1:000000000000 tip (empty repository)
1273 1277 branch: default
1274 1278 commit: (clean)
1275 1279 update: (current)
1276 1280 remote: (synced)
1277 1281
1278 1282 Check hg update --clean
1279 1283 $ cd $TESTTMP/t
1280 1284 $ rm -r t/t.orig
1281 1285 $ hg status -S --all
1282 1286 C .hgsub
1283 1287 C .hgsubstate
1284 1288 C a
1285 1289 C s/.hgsub
1286 1290 C s/.hgsubstate
1287 1291 C s/a
1288 1292 C s/ss/a
1289 1293 C t/t
1290 1294 $ echo c1 > s/a
1291 1295 $ cd s
1292 1296 $ echo c1 > b
1293 1297 $ echo c1 > c
1294 1298 $ hg add b
1295 1299 $ cd ..
1296 1300 $ hg status -S
1297 1301 M s/a
1298 1302 A s/b
1299 1303 ? s/c
1300 1304 $ hg update -C
1301 1305 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1302 1306 updated to "925c17564ef8: 13"
1303 1307 2 other heads for branch "default"
1304 1308 $ hg status -S
1305 1309 ? s/b
1306 1310 ? s/c
1307 1311
1308 1312 Sticky subrepositories, no changes
1309 1313 $ cd $TESTTMP/t
1310 1314 $ hg id
1311 1315 925c17564ef8 tip
1312 1316 $ hg -R s id
1313 1317 12a213df6fa9 tip
1314 1318 $ hg -R t id
1315 1319 52c0adc0515a tip
1316 1320 $ hg update 11
1317 1321 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1318 1322 $ hg id
1319 1323 365661e5936a
1320 1324 $ hg -R s id
1321 1325 fc627a69481f
1322 1326 $ hg -R t id
1323 1327 e95bcfa18a35
1324 1328
1325 1329 Sticky subrepositories, file changes
1326 1330 $ touch s/f1
1327 1331 $ touch t/f1
1328 1332 $ hg add -S s/f1
1329 1333 $ hg add -S t/f1
1330 1334 $ hg id
1331 1335 365661e5936a+
1332 1336 $ hg -R s id
1333 1337 fc627a69481f+
1334 1338 $ hg -R t id
1335 1339 e95bcfa18a35+
1336 1340 $ hg update tip
1337 1341 subrepository s diverged (local revision: fc627a69481f, remote revision: 12a213df6fa9)
1338 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1342 you can (m)erge, keep (l)ocal [working copy] or keep (r)emote [destination].
1343 what do you want to do? m
1339 1344 subrepository sources for s differ
1340 use (l)ocal source (fc627a69481f) or (r)emote source (12a213df6fa9)? l
1345 you can use (l)ocal source (fc627a69481f) or (r)emote source (12a213df6fa9).
1346 what do you want to do? l
1341 1347 subrepository t diverged (local revision: e95bcfa18a35, remote revision: 52c0adc0515a)
1342 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1348 you can (m)erge, keep (l)ocal [working copy] or keep (r)emote [destination].
1349 what do you want to do? m
1343 1350 subrepository sources for t differ
1344 use (l)ocal source (e95bcfa18a35) or (r)emote source (52c0adc0515a)? l
1351 you can use (l)ocal source (e95bcfa18a35) or (r)emote source (52c0adc0515a).
1352 what do you want to do? l
1345 1353 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1346 1354 $ hg id
1347 1355 925c17564ef8+ tip
1348 1356 $ hg -R s id
1349 1357 fc627a69481f+
1350 1358 $ hg -R t id
1351 1359 e95bcfa18a35+
1352 1360 $ hg update --clean tip
1353 1361 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1354 1362
1355 1363 Sticky subrepository, revision updates
1356 1364 $ hg id
1357 1365 925c17564ef8 tip
1358 1366 $ hg -R s id
1359 1367 12a213df6fa9 tip
1360 1368 $ hg -R t id
1361 1369 52c0adc0515a tip
1362 1370 $ cd s
1363 1371 $ hg update -r -2
1364 1372 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1365 1373 $ cd ../t
1366 1374 $ hg update -r 2
1367 1375 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1368 1376 $ cd ..
1369 1377 $ hg update 10
1370 1378 subrepository s diverged (local revision: 12a213df6fa9, remote revision: fc627a69481f)
1371 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1379 you can (m)erge, keep (l)ocal [working copy] or keep (r)emote [destination].
1380 what do you want to do? m
1372 1381 subrepository t diverged (local revision: 52c0adc0515a, remote revision: 20a0db6fbf6c)
1373 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1382 you can (m)erge, keep (l)ocal [working copy] or keep (r)emote [destination].
1383 what do you want to do? m
1374 1384 subrepository sources for t differ (in checked out version)
1375 use (l)ocal source (7af322bc1198) or (r)emote source (20a0db6fbf6c)? l
1385 you can use (l)ocal source (7af322bc1198) or (r)emote source (20a0db6fbf6c).
1386 what do you want to do? l
1376 1387 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1377 1388 $ hg id
1378 1389 e45c8b14af55+
1379 1390 $ hg -R s id
1380 1391 02dcf1d70411
1381 1392 $ hg -R t id
1382 1393 7af322bc1198
1383 1394
1384 1395 Sticky subrepository, file changes and revision updates
1385 1396 $ touch s/f1
1386 1397 $ touch t/f1
1387 1398 $ hg add -S s/f1
1388 1399 $ hg add -S t/f1
1389 1400 $ hg id
1390 1401 e45c8b14af55+
1391 1402 $ hg -R s id
1392 1403 02dcf1d70411+
1393 1404 $ hg -R t id
1394 1405 7af322bc1198+
1395 1406 $ hg update tip
1396 1407 subrepository s diverged (local revision: 12a213df6fa9, remote revision: 12a213df6fa9)
1397 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1408 you can (m)erge, keep (l)ocal [working copy] or keep (r)emote [destination].
1409 what do you want to do? m
1398 1410 subrepository sources for s differ
1399 use (l)ocal source (02dcf1d70411) or (r)emote source (12a213df6fa9)? l
1411 you can use (l)ocal source (02dcf1d70411) or (r)emote source (12a213df6fa9).
1412 what do you want to do? l
1400 1413 subrepository t diverged (local revision: 52c0adc0515a, remote revision: 52c0adc0515a)
1401 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1414 you can (m)erge, keep (l)ocal [working copy] or keep (r)emote [destination].
1415 what do you want to do? m
1402 1416 subrepository sources for t differ
1403 use (l)ocal source (7af322bc1198) or (r)emote source (52c0adc0515a)? l
1417 you can use (l)ocal source (7af322bc1198) or (r)emote source (52c0adc0515a).
1418 what do you want to do? l
1404 1419 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1405 1420 $ hg id
1406 1421 925c17564ef8+ tip
1407 1422 $ hg -R s id
1408 1423 02dcf1d70411+
1409 1424 $ hg -R t id
1410 1425 7af322bc1198+
1411 1426
1412 1427 Sticky repository, update --clean
1413 1428 $ hg update --clean tip
1414 1429 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1415 1430 $ hg id
1416 1431 925c17564ef8 tip
1417 1432 $ hg -R s id
1418 1433 12a213df6fa9 tip
1419 1434 $ hg -R t id
1420 1435 52c0adc0515a tip
1421 1436
1422 1437 Test subrepo already at intended revision:
1423 1438 $ cd s
1424 1439 $ hg update fc627a69481f
1425 1440 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1426 1441 $ cd ..
1427 1442 $ hg update 11
1428 1443 subrepository s diverged (local revision: 12a213df6fa9, remote revision: fc627a69481f)
1429 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1444 you can (m)erge, keep (l)ocal [working copy] or keep (r)emote [destination].
1445 what do you want to do? m
1430 1446 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1431 1447 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1432 1448 $ hg id -n
1433 1449 11+
1434 1450 $ hg -R s id
1435 1451 fc627a69481f
1436 1452 $ hg -R t id
1437 1453 e95bcfa18a35
1438 1454
1439 1455 Test that removing .hgsubstate doesn't break anything:
1440 1456
1441 1457 $ hg rm -f .hgsubstate
1442 1458 $ hg ci -mrm
1443 1459 nothing changed
1444 1460 [1]
1445 1461 $ hg log -vr tip
1446 1462 changeset: 13:925c17564ef8
1447 1463 tag: tip
1448 1464 user: test
1449 1465 date: Thu Jan 01 00:00:00 1970 +0000
1450 1466 files: .hgsubstate
1451 1467 description:
1452 1468 13
1453 1469
1454 1470
1455 1471
1456 1472 Test that removing .hgsub removes .hgsubstate:
1457 1473
1458 1474 $ hg rm .hgsub
1459 1475 $ hg ci -mrm2
1460 1476 created new head
1461 1477 $ hg log -vr tip
1462 1478 changeset: 14:2400bccd50af
1463 1479 tag: tip
1464 1480 parent: 11:365661e5936a
1465 1481 user: test
1466 1482 date: Thu Jan 01 00:00:00 1970 +0000
1467 1483 files: .hgsub .hgsubstate
1468 1484 description:
1469 1485 rm2
1470 1486
1471 1487
1472 1488 Test issue3153: diff -S with deleted subrepos
1473 1489
1474 1490 $ hg diff --nodates -S -c .
1475 1491 diff -r 365661e5936a -r 2400bccd50af .hgsub
1476 1492 --- a/.hgsub
1477 1493 +++ /dev/null
1478 1494 @@ -1,2 +0,0 @@
1479 1495 -s = s
1480 1496 -t = t
1481 1497 diff -r 365661e5936a -r 2400bccd50af .hgsubstate
1482 1498 --- a/.hgsubstate
1483 1499 +++ /dev/null
1484 1500 @@ -1,2 +0,0 @@
1485 1501 -fc627a69481fcbe5f1135069e8a3881c023e4cf5 s
1486 1502 -e95bcfa18a358dc4936da981ebf4147b4cad1362 t
1487 1503
1488 1504 Test behavior of add for explicit path in subrepo:
1489 1505 $ cd ..
1490 1506 $ hg init explicit
1491 1507 $ cd explicit
1492 1508 $ echo s = s > .hgsub
1493 1509 $ hg add .hgsub
1494 1510 $ hg init s
1495 1511 $ hg ci -m0
1496 1512 Adding with an explicit path in a subrepo adds the file
1497 1513 $ echo c1 > f1
1498 1514 $ echo c2 > s/f2
1499 1515 $ hg st -S
1500 1516 ? f1
1501 1517 ? s/f2
1502 1518 $ hg add s/f2
1503 1519 $ hg st -S
1504 1520 A s/f2
1505 1521 ? f1
1506 1522 $ hg ci -R s -m0
1507 1523 $ hg ci -Am1
1508 1524 adding f1
1509 1525 Adding with an explicit path in a subrepo with -S has the same behavior
1510 1526 $ echo c3 > f3
1511 1527 $ echo c4 > s/f4
1512 1528 $ hg st -S
1513 1529 ? f3
1514 1530 ? s/f4
1515 1531 $ hg add -S s/f4
1516 1532 $ hg st -S
1517 1533 A s/f4
1518 1534 ? f3
1519 1535 $ hg ci -R s -m1
1520 1536 $ hg ci -Ama2
1521 1537 adding f3
1522 1538 Adding without a path or pattern silently ignores subrepos
1523 1539 $ echo c5 > f5
1524 1540 $ echo c6 > s/f6
1525 1541 $ echo c7 > s/f7
1526 1542 $ hg st -S
1527 1543 ? f5
1528 1544 ? s/f6
1529 1545 ? s/f7
1530 1546 $ hg add
1531 1547 adding f5
1532 1548 $ hg st -S
1533 1549 A f5
1534 1550 ? s/f6
1535 1551 ? s/f7
1536 1552 $ hg ci -R s -Am2
1537 1553 adding f6
1538 1554 adding f7
1539 1555 $ hg ci -m3
1540 1556 Adding without a path or pattern with -S also adds files in subrepos
1541 1557 $ echo c8 > f8
1542 1558 $ echo c9 > s/f9
1543 1559 $ echo c10 > s/f10
1544 1560 $ hg st -S
1545 1561 ? f8
1546 1562 ? s/f10
1547 1563 ? s/f9
1548 1564 $ hg add -S
1549 1565 adding f8
1550 1566 adding s/f10
1551 1567 adding s/f9
1552 1568 $ hg st -S
1553 1569 A f8
1554 1570 A s/f10
1555 1571 A s/f9
1556 1572 $ hg ci -R s -m3
1557 1573 $ hg ci -m4
1558 1574 Adding with a pattern silently ignores subrepos
1559 1575 $ echo c11 > fm11
1560 1576 $ echo c12 > fn12
1561 1577 $ echo c13 > s/fm13
1562 1578 $ echo c14 > s/fn14
1563 1579 $ hg st -S
1564 1580 ? fm11
1565 1581 ? fn12
1566 1582 ? s/fm13
1567 1583 ? s/fn14
1568 1584 $ hg add 'glob:**fm*'
1569 1585 adding fm11
1570 1586 $ hg st -S
1571 1587 A fm11
1572 1588 ? fn12
1573 1589 ? s/fm13
1574 1590 ? s/fn14
1575 1591 $ hg ci -R s -Am4
1576 1592 adding fm13
1577 1593 adding fn14
1578 1594 $ hg ci -Am5
1579 1595 adding fn12
1580 1596 Adding with a pattern with -S also adds matches in subrepos
1581 1597 $ echo c15 > fm15
1582 1598 $ echo c16 > fn16
1583 1599 $ echo c17 > s/fm17
1584 1600 $ echo c18 > s/fn18
1585 1601 $ hg st -S
1586 1602 ? fm15
1587 1603 ? fn16
1588 1604 ? s/fm17
1589 1605 ? s/fn18
1590 1606 $ hg add -S 'glob:**fm*'
1591 1607 adding fm15
1592 1608 adding s/fm17
1593 1609 $ hg st -S
1594 1610 A fm15
1595 1611 A s/fm17
1596 1612 ? fn16
1597 1613 ? s/fn18
1598 1614 $ hg ci -R s -Am5
1599 1615 adding fn18
1600 1616 $ hg ci -Am6
1601 1617 adding fn16
1602 1618
1603 1619 Test behavior of forget for explicit path in subrepo:
1604 1620 Forgetting an explicit path in a subrepo untracks the file
1605 1621 $ echo c19 > s/f19
1606 1622 $ hg add s/f19
1607 1623 $ hg st -S
1608 1624 A s/f19
1609 1625 $ hg forget s/f19
1610 1626 $ hg st -S
1611 1627 ? s/f19
1612 1628 $ rm s/f19
1613 1629 $ cd ..
1614 1630
1615 1631 Courtesy phases synchronisation to publishing server does not block the push
1616 1632 (issue3781)
1617 1633
1618 1634 $ cp -R main issue3781
1619 1635 $ cp -R main issue3781-dest
1620 1636 $ cd issue3781-dest/s
1621 1637 $ hg phase tip # show we have draft changeset
1622 1638 5: draft
1623 1639 $ chmod a-w .hg/store/phaseroots # prevent phase push
1624 1640 $ cd ../../issue3781
1625 1641 $ cat >> .hg/hgrc << EOF
1626 1642 > [paths]
1627 1643 > default=../issue3781-dest/
1628 1644 > EOF
1629 1645 $ hg push --config devel.legacy.exchange=bundle1
1630 1646 pushing to $TESTTMP/issue3781-dest
1631 1647 pushing subrepo s to $TESTTMP/issue3781-dest/s
1632 1648 searching for changes
1633 1649 no changes found
1634 1650 searching for changes
1635 1651 no changes found
1636 1652 [1]
1637 1653 # clean the push cache
1638 1654 $ rm s/.hg/cache/storehash/*
1639 1655 $ hg push # bundle2+
1640 1656 pushing to $TESTTMP/issue3781-dest
1641 1657 pushing subrepo s to $TESTTMP/issue3781-dest/s
1642 1658 searching for changes
1643 1659 no changes found
1644 1660 searching for changes
1645 1661 no changes found
1646 1662 [1]
1647 1663 $ cd ..
1648 1664
1649 1665 Test phase choice for newly created commit with "phases.subrepochecks"
1650 1666 configuration
1651 1667
1652 1668 $ cd t
1653 1669 $ hg update -q -r 12
1654 1670
1655 1671 $ cat >> s/ss/.hg/hgrc <<EOF
1656 1672 > [phases]
1657 1673 > new-commit = secret
1658 1674 > EOF
1659 1675 $ cat >> s/.hg/hgrc <<EOF
1660 1676 > [phases]
1661 1677 > new-commit = draft
1662 1678 > EOF
1663 1679 $ echo phasecheck1 >> s/ss/a
1664 1680 $ hg -R s commit -S --config phases.checksubrepos=abort -m phasecheck1
1665 1681 committing subrepository ss
1666 1682 transaction abort!
1667 1683 rollback completed
1668 1684 abort: can't commit in draft phase conflicting secret from subrepository ss
1669 1685 [255]
1670 1686 $ echo phasecheck2 >> s/ss/a
1671 1687 $ hg -R s commit -S --config phases.checksubrepos=ignore -m phasecheck2
1672 1688 committing subrepository ss
1673 1689 $ hg -R s/ss phase tip
1674 1690 3: secret
1675 1691 $ hg -R s phase tip
1676 1692 6: draft
1677 1693 $ echo phasecheck3 >> s/ss/a
1678 1694 $ hg -R s commit -S -m phasecheck3
1679 1695 committing subrepository ss
1680 1696 warning: changes are committed in secret phase from subrepository ss
1681 1697 $ hg -R s/ss phase tip
1682 1698 4: secret
1683 1699 $ hg -R s phase tip
1684 1700 7: secret
1685 1701
1686 1702 $ cat >> t/.hg/hgrc <<EOF
1687 1703 > [phases]
1688 1704 > new-commit = draft
1689 1705 > EOF
1690 1706 $ cat >> .hg/hgrc <<EOF
1691 1707 > [phases]
1692 1708 > new-commit = public
1693 1709 > EOF
1694 1710 $ echo phasecheck4 >> s/ss/a
1695 1711 $ echo phasecheck4 >> t/t
1696 1712 $ hg commit -S -m phasecheck4
1697 1713 committing subrepository s
1698 1714 committing subrepository s/ss
1699 1715 warning: changes are committed in secret phase from subrepository ss
1700 1716 committing subrepository t
1701 1717 warning: changes are committed in secret phase from subrepository s
1702 1718 created new head
1703 1719 $ hg -R s/ss phase tip
1704 1720 5: secret
1705 1721 $ hg -R s phase tip
1706 1722 8: secret
1707 1723 $ hg -R t phase tip
1708 1724 6: draft
1709 1725 $ hg phase tip
1710 1726 15: secret
1711 1727
1712 1728 $ cd ..
1713 1729
1714 1730
1715 1731 Test that commit --secret works on both repo and subrepo (issue4182)
1716 1732
1717 1733 $ cd main
1718 1734 $ echo secret >> b
1719 1735 $ echo secret >> s/b
1720 1736 $ hg commit --secret --subrepo -m "secret"
1721 1737 committing subrepository s
1722 1738 $ hg phase -r .
1723 1739 6: secret
1724 1740 $ cd s
1725 1741 $ hg phase -r .
1726 1742 6: secret
1727 1743 $ cd ../../
1728 1744
1729 1745 Test "subrepos" template keyword
1730 1746
1731 1747 $ cd t
1732 1748 $ hg update -q 15
1733 1749 $ cat > .hgsub <<EOF
1734 1750 > s = s
1735 1751 > EOF
1736 1752 $ hg commit -m "16"
1737 1753 warning: changes are committed in secret phase from subrepository s
1738 1754
1739 1755 (addition of ".hgsub" itself)
1740 1756
1741 1757 $ hg diff --nodates -c 1 .hgsubstate
1742 1758 diff -r f7b1eb17ad24 -r 7cf8cfea66e4 .hgsubstate
1743 1759 --- /dev/null
1744 1760 +++ b/.hgsubstate
1745 1761 @@ -0,0 +1,1 @@
1746 1762 +e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1747 1763 $ hg log -r 1 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1748 1764 f7b1eb17ad24 000000000000
1749 1765 s
1750 1766
1751 1767 (modification of existing entry)
1752 1768
1753 1769 $ hg diff --nodates -c 2 .hgsubstate
1754 1770 diff -r 7cf8cfea66e4 -r df30734270ae .hgsubstate
1755 1771 --- a/.hgsubstate
1756 1772 +++ b/.hgsubstate
1757 1773 @@ -1,1 +1,1 @@
1758 1774 -e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1759 1775 +dc73e2e6d2675eb2e41e33c205f4bdab4ea5111d s
1760 1776 $ hg log -r 2 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1761 1777 7cf8cfea66e4 000000000000
1762 1778 s
1763 1779
1764 1780 (addition of entry)
1765 1781
1766 1782 $ hg diff --nodates -c 5 .hgsubstate
1767 1783 diff -r 7cf8cfea66e4 -r 1f14a2e2d3ec .hgsubstate
1768 1784 --- a/.hgsubstate
1769 1785 +++ b/.hgsubstate
1770 1786 @@ -1,1 +1,2 @@
1771 1787 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1772 1788 +60ca1237c19474e7a3978b0dc1ca4e6f36d51382 t
1773 1789 $ hg log -r 5 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1774 1790 7cf8cfea66e4 000000000000
1775 1791 t
1776 1792
1777 1793 (removal of existing entry)
1778 1794
1779 1795 $ hg diff --nodates -c 16 .hgsubstate
1780 1796 diff -r 8bec38d2bd0b -r f2f70bc3d3c9 .hgsubstate
1781 1797 --- a/.hgsubstate
1782 1798 +++ b/.hgsubstate
1783 1799 @@ -1,2 +1,1 @@
1784 1800 0731af8ca9423976d3743119d0865097c07bdc1b s
1785 1801 -e202dc79b04c88a636ea8913d9182a1346d9b3dc t
1786 1802 $ hg log -r 16 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1787 1803 8bec38d2bd0b 000000000000
1788 1804 t
1789 1805
1790 1806 (merging)
1791 1807
1792 1808 $ hg diff --nodates -c 9 .hgsubstate
1793 1809 diff -r f6affe3fbfaa -r f0d2028bf86d .hgsubstate
1794 1810 --- a/.hgsubstate
1795 1811 +++ b/.hgsubstate
1796 1812 @@ -1,1 +1,2 @@
1797 1813 fc627a69481fcbe5f1135069e8a3881c023e4cf5 s
1798 1814 +60ca1237c19474e7a3978b0dc1ca4e6f36d51382 t
1799 1815 $ hg log -r 9 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1800 1816 f6affe3fbfaa 1f14a2e2d3ec
1801 1817 t
1802 1818
1803 1819 (removal of ".hgsub" itself)
1804 1820
1805 1821 $ hg diff --nodates -c 8 .hgsubstate
1806 1822 diff -r f94576341bcf -r 96615c1dad2d .hgsubstate
1807 1823 --- a/.hgsubstate
1808 1824 +++ /dev/null
1809 1825 @@ -1,2 +0,0 @@
1810 1826 -e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1811 1827 -7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4 t
1812 1828 $ hg log -r 8 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1813 1829 f94576341bcf 000000000000
1814 1830
1815 1831 Test that '[paths]' is configured correctly at subrepo creation
1816 1832
1817 1833 $ cd $TESTTMP/tc
1818 1834 $ cat > .hgsub <<EOF
1819 1835 > # to clear bogus subrepo path 'bogus=[boguspath'
1820 1836 > s = s
1821 1837 > t = t
1822 1838 > EOF
1823 1839 $ hg update -q --clean null
1824 1840 $ rm -rf s t
1825 1841 $ cat >> .hg/hgrc <<EOF
1826 1842 > [paths]
1827 1843 > default-push = /foo/bar
1828 1844 > EOF
1829 1845 $ hg update -q
1830 1846 $ cat s/.hg/hgrc
1831 1847 [paths]
1832 1848 default = $TESTTMP/t/s
1833 1849 default-push = /foo/bar/s
1834 1850 $ cat s/ss/.hg/hgrc
1835 1851 [paths]
1836 1852 default = $TESTTMP/t/s/ss
1837 1853 default-push = /foo/bar/s/ss
1838 1854 $ cat t/.hg/hgrc
1839 1855 [paths]
1840 1856 default = $TESTTMP/t/t
1841 1857 default-push = /foo/bar/t
1842 1858
1843 1859 $ cd $TESTTMP/t
1844 1860 $ hg up -qC 0
1845 1861 $ echo 'bar' > bar.txt
1846 1862 $ hg ci -Am 'branch before subrepo add'
1847 1863 adding bar.txt
1848 1864 created new head
1849 1865 $ hg merge -r "first(subrepo('s'))"
1850 1866 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1851 1867 (branch merge, don't forget to commit)
1852 1868 $ hg status -S -X '.hgsub*'
1853 1869 A s/a
1854 1870 ? s/b
1855 1871 ? s/c
1856 1872 ? s/f1
1857 1873 $ hg status -S --rev 'p2()'
1858 1874 A bar.txt
1859 1875 ? s/b
1860 1876 ? s/c
1861 1877 ? s/f1
1862 1878 $ hg diff -S -X '.hgsub*' --nodates
1863 1879 diff -r 000000000000 s/a
1864 1880 --- /dev/null
1865 1881 +++ b/s/a
1866 1882 @@ -0,0 +1,1 @@
1867 1883 +a
1868 1884 $ hg diff -S --rev 'p2()' --nodates
1869 1885 diff -r 7cf8cfea66e4 bar.txt
1870 1886 --- /dev/null
1871 1887 +++ b/bar.txt
1872 1888 @@ -0,0 +1,1 @@
1873 1889 +bar
1874 1890
1875 1891 $ hg diff -X '.hgsub*' --nodates s
1876 1892 diff -r 000000000000 s/a
1877 1893 --- /dev/null
1878 1894 +++ b/s/a
1879 1895 @@ -0,0 +1,1 @@
1880 1896 +a
1881 1897 $ hg diff -X '.hgsub*' --nodates s/a
1882 1898 diff -r 000000000000 s/a
1883 1899 --- /dev/null
1884 1900 +++ b/s/a
1885 1901 @@ -0,0 +1,1 @@
1886 1902 +a
1887 1903
1888 1904 $ cd ..
1889 1905
1890 1906 test for ssh exploit 2017-07-25
1891 1907
1892 1908 $ cat >> $HGRCPATH << EOF
1893 1909 > [ui]
1894 1910 > ssh = sh -c "read l; read l; read l"
1895 1911 > EOF
1896 1912
1897 1913 $ hg init malicious-proxycommand
1898 1914 $ cd malicious-proxycommand
1899 1915 $ echo 's = [hg]ssh://-oProxyCommand=touch${IFS}owned/path' > .hgsub
1900 1916 $ hg init s
1901 1917 $ cd s
1902 1918 $ echo init > init
1903 1919 $ hg add
1904 1920 adding init
1905 1921 $ hg commit -m init
1906 1922 $ cd ..
1907 1923 $ hg add .hgsub
1908 1924 $ hg ci -m 'add subrepo'
1909 1925 $ cd ..
1910 1926 $ hg clone malicious-proxycommand malicious-proxycommand-clone
1911 1927 updating to branch default
1912 1928 cloning subrepo s from ssh://-oProxyCommand%3Dtouch%24%7BIFS%7Downed/path
1913 1929 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path' (in subrepository "s")
1914 1930 [255]
1915 1931
1916 1932 also check that a percent encoded '-' (%2D) doesn't work
1917 1933
1918 1934 $ cd malicious-proxycommand
1919 1935 $ echo 's = [hg]ssh://%2DoProxyCommand=touch${IFS}owned/path' > .hgsub
1920 1936 $ hg ci -m 'change url to percent encoded'
1921 1937 $ cd ..
1922 1938 $ rm -r malicious-proxycommand-clone
1923 1939 $ hg clone malicious-proxycommand malicious-proxycommand-clone
1924 1940 updating to branch default
1925 1941 cloning subrepo s from ssh://-oProxyCommand%3Dtouch%24%7BIFS%7Downed/path
1926 1942 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path' (in subrepository "s")
1927 1943 [255]
1928 1944
1929 1945 also check for a pipe
1930 1946
1931 1947 $ cd malicious-proxycommand
1932 1948 $ echo 's = [hg]ssh://fakehost|touch${IFS}owned/path' > .hgsub
1933 1949 $ hg ci -m 'change url to pipe'
1934 1950 $ cd ..
1935 1951 $ rm -r malicious-proxycommand-clone
1936 1952 $ hg clone malicious-proxycommand malicious-proxycommand-clone
1937 1953 updating to branch default
1938 1954 cloning subrepo s from ssh://fakehost%7Ctouch%24%7BIFS%7Downed/path
1939 1955 abort: no suitable response from remote hg!
1940 1956 [255]
1941 1957 $ [ ! -f owned ] || echo 'you got owned'
1942 1958
1943 1959 also check that a percent encoded '|' (%7C) doesn't work
1944 1960
1945 1961 $ cd malicious-proxycommand
1946 1962 $ echo 's = [hg]ssh://fakehost%7Ctouch%20owned/path' > .hgsub
1947 1963 $ hg ci -m 'change url to percent encoded pipe'
1948 1964 $ cd ..
1949 1965 $ rm -r malicious-proxycommand-clone
1950 1966 $ hg clone malicious-proxycommand malicious-proxycommand-clone
1951 1967 updating to branch default
1952 1968 cloning subrepo s from ssh://fakehost%7Ctouch%20owned/path
1953 1969 abort: no suitable response from remote hg!
1954 1970 [255]
1955 1971 $ [ ! -f owned ] || echo 'you got owned'
1956 1972
1957 1973 and bad usernames:
1958 1974 $ cd malicious-proxycommand
1959 1975 $ echo 's = [hg]ssh://-oProxyCommand=touch owned@example.com/path' > .hgsub
1960 1976 $ hg ci -m 'owned username'
1961 1977 $ cd ..
1962 1978 $ rm -r malicious-proxycommand-clone
1963 1979 $ hg clone malicious-proxycommand malicious-proxycommand-clone
1964 1980 updating to branch default
1965 1981 cloning subrepo s from ssh://-oProxyCommand%3Dtouch%20owned@example.com/path
1966 1982 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch owned@example.com/path' (in subrepository "s")
1967 1983 [255]
1968 1984
1969 1985 Test convert subrepositories including merge (issue5526):
1970 1986
1971 1987 $ hg init tconv
1972 1988 $ hg convert --config extensions.convert= -q t/s tconv/s
1973 1989 $ hg convert --config extensions.convert= -q t/s/ss tconv/s/ss
1974 1990 $ hg convert --config extensions.convert= -q t/t tconv/t
1975 1991
1976 1992 convert shouldn't fail because of pseudo filenode:
1977 1993
1978 1994 $ hg convert --config extensions.convert= t tconv
1979 1995 scanning source...
1980 1996 sorting...
1981 1997 converting...
1982 1998 17 0
1983 1999 16 1
1984 2000 15 2
1985 2001 14 3
1986 2002 13 4
1987 2003 12 5
1988 2004 11 6
1989 2005 10 7
1990 2006 9 8
1991 2007 8 9
1992 2008 7 10
1993 2009 6 11
1994 2010 5 12
1995 2011 4 13
1996 2012 3 rm2
1997 2013 2 phasecheck4
1998 2014 1 16
1999 2015 0 branch before subrepo add
2000 2016
2001 2017 converted .hgsubstate should point to valid nodes:
2002 2018
2003 2019 $ hg up -R tconv 9
2004 2020 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
2005 2021 $ cat tconv/.hgsubstate
2006 2022 fc627a69481fcbe5f1135069e8a3881c023e4cf5 s
2007 2023 60ca1237c19474e7a3978b0dc1ca4e6f36d51382 t
General Comments 0
You need to be logged in to leave comments. Login now