##// END OF EJS Templates
subrepo: do not report known files inside repositories as unknown
Oleg Stepanov -
r13233:0b30e614 stable
parent child Browse files
Show More
@@ -1,681 +1,681 b''
1 1 # dirstate.py - working directory tracking for mercurial
2 2 #
3 3 # Copyright 2005-2007 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 node import nullid
9 9 from i18n import _
10 10 import util, ignore, osutil, parsers
11 11 import struct, os, stat, errno
12 12 import cStringIO
13 13
14 14 _format = ">cllll"
15 15 propertycache = util.propertycache
16 16
17 17 def _finddirs(path):
18 18 pos = path.rfind('/')
19 19 while pos != -1:
20 20 yield path[:pos]
21 21 pos = path.rfind('/', 0, pos)
22 22
23 23 def _incdirs(dirs, path):
24 24 for base in _finddirs(path):
25 25 if base in dirs:
26 26 dirs[base] += 1
27 27 return
28 28 dirs[base] = 1
29 29
30 30 def _decdirs(dirs, path):
31 31 for base in _finddirs(path):
32 32 if dirs[base] > 1:
33 33 dirs[base] -= 1
34 34 return
35 35 del dirs[base]
36 36
37 37 class dirstate(object):
38 38
39 39 def __init__(self, opener, ui, root):
40 40 '''Create a new dirstate object.
41 41
42 42 opener is an open()-like callable that can be used to open the
43 43 dirstate file; root is the root of the directory tracked by
44 44 the dirstate.
45 45 '''
46 46 self._opener = opener
47 47 self._root = root
48 48 self._rootdir = os.path.join(root, '')
49 49 self._dirty = False
50 50 self._dirtypl = False
51 51 self._ui = ui
52 52
53 53 @propertycache
54 54 def _map(self):
55 55 '''Return the dirstate contents as a map from filename to
56 56 (state, mode, size, time).'''
57 57 self._read()
58 58 return self._map
59 59
60 60 @propertycache
61 61 def _copymap(self):
62 62 self._read()
63 63 return self._copymap
64 64
65 65 @propertycache
66 66 def _foldmap(self):
67 67 f = {}
68 68 for name in self._map:
69 69 f[os.path.normcase(name)] = name
70 70 return f
71 71
72 72 @propertycache
73 73 def _branch(self):
74 74 try:
75 75 return self._opener("branch").read().strip() or "default"
76 76 except IOError:
77 77 return "default"
78 78
79 79 @propertycache
80 80 def _pl(self):
81 81 try:
82 82 st = self._opener("dirstate").read(40)
83 83 l = len(st)
84 84 if l == 40:
85 85 return st[:20], st[20:40]
86 86 elif l > 0 and l < 40:
87 87 raise util.Abort(_('working directory state appears damaged!'))
88 88 except IOError, err:
89 89 if err.errno != errno.ENOENT:
90 90 raise
91 91 return [nullid, nullid]
92 92
93 93 @propertycache
94 94 def _dirs(self):
95 95 dirs = {}
96 96 for f, s in self._map.iteritems():
97 97 if s[0] != 'r':
98 98 _incdirs(dirs, f)
99 99 return dirs
100 100
101 101 @propertycache
102 102 def _ignore(self):
103 103 files = [self._join('.hgignore')]
104 104 for name, path in self._ui.configitems("ui"):
105 105 if name == 'ignore' or name.startswith('ignore.'):
106 106 files.append(util.expandpath(path))
107 107 return ignore.ignore(self._root, files, self._ui.warn)
108 108
109 109 @propertycache
110 110 def _slash(self):
111 111 return self._ui.configbool('ui', 'slash') and os.sep != '/'
112 112
113 113 @propertycache
114 114 def _checklink(self):
115 115 return util.checklink(self._root)
116 116
117 117 @propertycache
118 118 def _checkexec(self):
119 119 return util.checkexec(self._root)
120 120
121 121 @propertycache
122 122 def _checkcase(self):
123 123 return not util.checkcase(self._join('.hg'))
124 124
125 125 def _join(self, f):
126 126 # much faster than os.path.join()
127 127 # it's safe because f is always a relative path
128 128 return self._rootdir + f
129 129
130 130 def flagfunc(self, fallback):
131 131 if self._checklink:
132 132 if self._checkexec:
133 133 def f(x):
134 134 p = self._join(x)
135 135 if os.path.islink(p):
136 136 return 'l'
137 137 if util.is_exec(p):
138 138 return 'x'
139 139 return ''
140 140 return f
141 141 def f(x):
142 142 if os.path.islink(self._join(x)):
143 143 return 'l'
144 144 if 'x' in fallback(x):
145 145 return 'x'
146 146 return ''
147 147 return f
148 148 if self._checkexec:
149 149 def f(x):
150 150 if 'l' in fallback(x):
151 151 return 'l'
152 152 if util.is_exec(self._join(x)):
153 153 return 'x'
154 154 return ''
155 155 return f
156 156 return fallback
157 157
158 158 def getcwd(self):
159 159 cwd = os.getcwd()
160 160 if cwd == self._root:
161 161 return ''
162 162 # self._root ends with a path separator if self._root is '/' or 'C:\'
163 163 rootsep = self._root
164 164 if not util.endswithsep(rootsep):
165 165 rootsep += os.sep
166 166 if cwd.startswith(rootsep):
167 167 return cwd[len(rootsep):]
168 168 else:
169 169 # we're outside the repo. return an absolute path.
170 170 return cwd
171 171
172 172 def pathto(self, f, cwd=None):
173 173 if cwd is None:
174 174 cwd = self.getcwd()
175 175 path = util.pathto(self._root, cwd, f)
176 176 if self._slash:
177 177 return util.normpath(path)
178 178 return path
179 179
180 180 def __getitem__(self, key):
181 181 '''Return the current state of key (a filename) in the dirstate.
182 182
183 183 States are:
184 184 n normal
185 185 m needs merging
186 186 r marked for removal
187 187 a marked for addition
188 188 ? not tracked
189 189 '''
190 190 return self._map.get(key, ("?",))[0]
191 191
192 192 def __contains__(self, key):
193 193 return key in self._map
194 194
195 195 def __iter__(self):
196 196 for x in sorted(self._map):
197 197 yield x
198 198
199 199 def parents(self):
200 200 return self._pl
201 201
202 202 def branch(self):
203 203 return self._branch
204 204
205 205 def setparents(self, p1, p2=nullid):
206 206 self._dirty = self._dirtypl = True
207 207 self._pl = p1, p2
208 208
209 209 def setbranch(self, branch):
210 210 if branch in ['tip', '.', 'null']:
211 211 raise util.Abort(_('the name \'%s\' is reserved') % branch)
212 212 self._branch = branch
213 213 self._opener("branch", "w").write(branch + '\n')
214 214
215 215 def _read(self):
216 216 self._map = {}
217 217 self._copymap = {}
218 218 try:
219 219 st = self._opener("dirstate").read()
220 220 except IOError, err:
221 221 if err.errno != errno.ENOENT:
222 222 raise
223 223 return
224 224 if not st:
225 225 return
226 226
227 227 p = parsers.parse_dirstate(self._map, self._copymap, st)
228 228 if not self._dirtypl:
229 229 self._pl = p
230 230
231 231 def invalidate(self):
232 232 for a in "_map _copymap _foldmap _branch _pl _dirs _ignore".split():
233 233 if a in self.__dict__:
234 234 delattr(self, a)
235 235 self._dirty = False
236 236
237 237 def copy(self, source, dest):
238 238 """Mark dest as a copy of source. Unmark dest if source is None."""
239 239 if source == dest:
240 240 return
241 241 self._dirty = True
242 242 if source is not None:
243 243 self._copymap[dest] = source
244 244 elif dest in self._copymap:
245 245 del self._copymap[dest]
246 246
247 247 def copied(self, file):
248 248 return self._copymap.get(file, None)
249 249
250 250 def copies(self):
251 251 return self._copymap
252 252
253 253 def _droppath(self, f):
254 254 if self[f] not in "?r" and "_dirs" in self.__dict__:
255 255 _decdirs(self._dirs, f)
256 256
257 257 def _addpath(self, f, check=False):
258 258 oldstate = self[f]
259 259 if check or oldstate == "r":
260 260 if '\r' in f or '\n' in f:
261 261 raise util.Abort(
262 262 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
263 263 if f in self._dirs:
264 264 raise util.Abort(_('directory %r already in dirstate') % f)
265 265 # shadows
266 266 for d in _finddirs(f):
267 267 if d in self._dirs:
268 268 break
269 269 if d in self._map and self[d] != 'r':
270 270 raise util.Abort(
271 271 _('file %r in dirstate clashes with %r') % (d, f))
272 272 if oldstate in "?r" and "_dirs" in self.__dict__:
273 273 _incdirs(self._dirs, f)
274 274
275 275 def normal(self, f):
276 276 '''Mark a file normal and clean.'''
277 277 self._dirty = True
278 278 self._addpath(f)
279 279 s = os.lstat(self._join(f))
280 280 self._map[f] = ('n', s.st_mode, s.st_size, int(s.st_mtime))
281 281 if f in self._copymap:
282 282 del self._copymap[f]
283 283
284 284 def normallookup(self, f):
285 285 '''Mark a file normal, but possibly dirty.'''
286 286 if self._pl[1] != nullid and f in self._map:
287 287 # if there is a merge going on and the file was either
288 288 # in state 'm' (-1) or coming from other parent (-2) before
289 289 # being removed, restore that state.
290 290 entry = self._map[f]
291 291 if entry[0] == 'r' and entry[2] in (-1, -2):
292 292 source = self._copymap.get(f)
293 293 if entry[2] == -1:
294 294 self.merge(f)
295 295 elif entry[2] == -2:
296 296 self.otherparent(f)
297 297 if source:
298 298 self.copy(source, f)
299 299 return
300 300 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
301 301 return
302 302 self._dirty = True
303 303 self._addpath(f)
304 304 self._map[f] = ('n', 0, -1, -1)
305 305 if f in self._copymap:
306 306 del self._copymap[f]
307 307
308 308 def otherparent(self, f):
309 309 '''Mark as coming from the other parent, always dirty.'''
310 310 if self._pl[1] == nullid:
311 311 raise util.Abort(_("setting %r to other parent "
312 312 "only allowed in merges") % f)
313 313 self._dirty = True
314 314 self._addpath(f)
315 315 self._map[f] = ('n', 0, -2, -1)
316 316 if f in self._copymap:
317 317 del self._copymap[f]
318 318
319 319 def add(self, f):
320 320 '''Mark a file added.'''
321 321 self._dirty = True
322 322 self._addpath(f, True)
323 323 self._map[f] = ('a', 0, -1, -1)
324 324 if f in self._copymap:
325 325 del self._copymap[f]
326 326
327 327 def remove(self, f):
328 328 '''Mark a file removed.'''
329 329 self._dirty = True
330 330 self._droppath(f)
331 331 size = 0
332 332 if self._pl[1] != nullid and f in self._map:
333 333 # backup the previous state
334 334 entry = self._map[f]
335 335 if entry[0] == 'm': # merge
336 336 size = -1
337 337 elif entry[0] == 'n' and entry[2] == -2: # other parent
338 338 size = -2
339 339 self._map[f] = ('r', 0, size, 0)
340 340 if size == 0 and f in self._copymap:
341 341 del self._copymap[f]
342 342
343 343 def merge(self, f):
344 344 '''Mark a file merged.'''
345 345 self._dirty = True
346 346 s = os.lstat(self._join(f))
347 347 self._addpath(f)
348 348 self._map[f] = ('m', s.st_mode, s.st_size, int(s.st_mtime))
349 349 if f in self._copymap:
350 350 del self._copymap[f]
351 351
352 352 def forget(self, f):
353 353 '''Forget a file.'''
354 354 self._dirty = True
355 355 try:
356 356 self._droppath(f)
357 357 del self._map[f]
358 358 except KeyError:
359 359 self._ui.warn(_("not in dirstate: %s\n") % f)
360 360
361 361 def _normalize(self, path, knownpath):
362 362 norm_path = os.path.normcase(path)
363 363 fold_path = self._foldmap.get(norm_path, None)
364 364 if fold_path is None:
365 365 if knownpath or not os.path.lexists(os.path.join(self._root, path)):
366 366 fold_path = path
367 367 else:
368 368 fold_path = self._foldmap.setdefault(norm_path,
369 369 util.fspath(path, self._root))
370 370 return fold_path
371 371
372 372 def clear(self):
373 373 self._map = {}
374 374 if "_dirs" in self.__dict__:
375 375 delattr(self, "_dirs")
376 376 self._copymap = {}
377 377 self._pl = [nullid, nullid]
378 378 self._dirty = True
379 379
380 380 def rebuild(self, parent, files):
381 381 self.clear()
382 382 for f in files:
383 383 if 'x' in files.flags(f):
384 384 self._map[f] = ('n', 0777, -1, 0)
385 385 else:
386 386 self._map[f] = ('n', 0666, -1, 0)
387 387 self._pl = (parent, nullid)
388 388 self._dirty = True
389 389
390 390 def write(self):
391 391 if not self._dirty:
392 392 return
393 393 st = self._opener("dirstate", "w", atomictemp=True)
394 394
395 395 # use the modification time of the newly created temporary file as the
396 396 # filesystem's notion of 'now'
397 397 now = int(util.fstat(st).st_mtime)
398 398
399 399 cs = cStringIO.StringIO()
400 400 copymap = self._copymap
401 401 pack = struct.pack
402 402 write = cs.write
403 403 write("".join(self._pl))
404 404 for f, e in self._map.iteritems():
405 405 if e[0] == 'n' and e[3] == now:
406 406 # The file was last modified "simultaneously" with the current
407 407 # write to dirstate (i.e. within the same second for file-
408 408 # systems with a granularity of 1 sec). This commonly happens
409 409 # for at least a couple of files on 'update'.
410 410 # The user could change the file without changing its size
411 411 # within the same second. Invalidate the file's stat data in
412 412 # dirstate, forcing future 'status' calls to compare the
413 413 # contents of the file. This prevents mistakenly treating such
414 414 # files as clean.
415 415 e = (e[0], 0, -1, -1) # mark entry as 'unset'
416 416 self._map[f] = e
417 417
418 418 if f in copymap:
419 419 f = "%s\0%s" % (f, copymap[f])
420 420 e = pack(_format, e[0], e[1], e[2], e[3], len(f))
421 421 write(e)
422 422 write(f)
423 423 st.write(cs.getvalue())
424 424 st.rename()
425 425 self._dirty = self._dirtypl = False
426 426
427 427 def _dirignore(self, f):
428 428 if f == '.':
429 429 return False
430 430 if self._ignore(f):
431 431 return True
432 432 for p in _finddirs(f):
433 433 if self._ignore(p):
434 434 return True
435 435 return False
436 436
437 437 def walk(self, match, subrepos, unknown, ignored):
438 438 '''
439 439 Walk recursively through the directory tree, finding all files
440 440 matched by match.
441 441
442 442 Return a dict mapping filename to stat-like object (either
443 443 mercurial.osutil.stat instance or return value of os.stat()).
444 444 '''
445 445
446 446 def fwarn(f, msg):
447 447 self._ui.warn('%s: %s\n' % (self.pathto(f), msg))
448 448 return False
449 449
450 450 def badtype(mode):
451 451 kind = _('unknown')
452 452 if stat.S_ISCHR(mode):
453 453 kind = _('character device')
454 454 elif stat.S_ISBLK(mode):
455 455 kind = _('block device')
456 456 elif stat.S_ISFIFO(mode):
457 457 kind = _('fifo')
458 458 elif stat.S_ISSOCK(mode):
459 459 kind = _('socket')
460 460 elif stat.S_ISDIR(mode):
461 461 kind = _('directory')
462 462 return _('unsupported file type (type is %s)') % kind
463 463
464 464 ignore = self._ignore
465 465 dirignore = self._dirignore
466 466 if ignored:
467 467 ignore = util.never
468 468 dirignore = util.never
469 469 elif not unknown:
470 470 # if unknown and ignored are False, skip step 2
471 471 ignore = util.always
472 472 dirignore = util.always
473 473
474 474 matchfn = match.matchfn
475 475 badfn = match.bad
476 476 dmap = self._map
477 477 normpath = util.normpath
478 478 listdir = osutil.listdir
479 479 lstat = os.lstat
480 480 getkind = stat.S_IFMT
481 481 dirkind = stat.S_IFDIR
482 482 regkind = stat.S_IFREG
483 483 lnkkind = stat.S_IFLNK
484 484 join = self._join
485 485 work = []
486 486 wadd = work.append
487 487
488 488 exact = skipstep3 = False
489 489 if matchfn == match.exact: # match.exact
490 490 exact = True
491 491 dirignore = util.always # skip step 2
492 492 elif match.files() and not match.anypats(): # match.match, no patterns
493 493 skipstep3 = True
494 494
495 495 if self._checkcase:
496 496 normalize = self._normalize
497 497 skipstep3 = False
498 498 else:
499 499 normalize = lambda x, y: x
500 500
501 501 files = sorted(match.files())
502 502 subrepos.sort()
503 503 i, j = 0, 0
504 504 while i < len(files) and j < len(subrepos):
505 505 subpath = subrepos[j] + "/"
506 if not files[i].startswith(subpath):
506 if files[i] < subpath:
507 507 i += 1
508 508 continue
509 509 while files and files[i].startswith(subpath):
510 510 del files[i]
511 511 j += 1
512 512
513 513 if not files or '.' in files:
514 514 files = ['']
515 515 results = dict.fromkeys(subrepos)
516 516 results['.hg'] = None
517 517
518 518 # step 1: find all explicit files
519 519 for ff in files:
520 520 nf = normalize(normpath(ff), False)
521 521 if nf in results:
522 522 continue
523 523
524 524 try:
525 525 st = lstat(join(nf))
526 526 kind = getkind(st.st_mode)
527 527 if kind == dirkind:
528 528 skipstep3 = False
529 529 if nf in dmap:
530 530 #file deleted on disk but still in dirstate
531 531 results[nf] = None
532 532 match.dir(nf)
533 533 if not dirignore(nf):
534 534 wadd(nf)
535 535 elif kind == regkind or kind == lnkkind:
536 536 results[nf] = st
537 537 else:
538 538 badfn(ff, badtype(kind))
539 539 if nf in dmap:
540 540 results[nf] = None
541 541 except OSError, inst:
542 542 if nf in dmap: # does it exactly match a file?
543 543 results[nf] = None
544 544 else: # does it match a directory?
545 545 prefix = nf + "/"
546 546 for fn in dmap:
547 547 if fn.startswith(prefix):
548 548 match.dir(nf)
549 549 skipstep3 = False
550 550 break
551 551 else:
552 552 badfn(ff, inst.strerror)
553 553
554 554 # step 2: visit subdirectories
555 555 while work:
556 556 nd = work.pop()
557 557 skip = None
558 558 if nd == '.':
559 559 nd = ''
560 560 else:
561 561 skip = '.hg'
562 562 try:
563 563 entries = listdir(join(nd), stat=True, skip=skip)
564 564 except OSError, inst:
565 565 if inst.errno == errno.EACCES:
566 566 fwarn(nd, inst.strerror)
567 567 continue
568 568 raise
569 569 for f, kind, st in entries:
570 570 nf = normalize(nd and (nd + "/" + f) or f, True)
571 571 if nf not in results:
572 572 if kind == dirkind:
573 573 if not ignore(nf):
574 574 match.dir(nf)
575 575 wadd(nf)
576 576 if nf in dmap and matchfn(nf):
577 577 results[nf] = None
578 578 elif kind == regkind or kind == lnkkind:
579 579 if nf in dmap:
580 580 if matchfn(nf):
581 581 results[nf] = st
582 582 elif matchfn(nf) and not ignore(nf):
583 583 results[nf] = st
584 584 elif nf in dmap and matchfn(nf):
585 585 results[nf] = None
586 586
587 587 # step 3: report unseen items in the dmap hash
588 588 if not skipstep3 and not exact:
589 589 visit = sorted([f for f in dmap if f not in results and matchfn(f)])
590 590 for nf, st in zip(visit, util.statfiles([join(i) for i in visit])):
591 591 if not st is None and not getkind(st.st_mode) in (regkind, lnkkind):
592 592 st = None
593 593 results[nf] = st
594 594 for s in subrepos:
595 595 del results[s]
596 596 del results['.hg']
597 597 return results
598 598
599 599 def status(self, match, subrepos, ignored, clean, unknown):
600 600 '''Determine the status of the working copy relative to the
601 601 dirstate and return a tuple of lists (unsure, modified, added,
602 602 removed, deleted, unknown, ignored, clean), where:
603 603
604 604 unsure:
605 605 files that might have been modified since the dirstate was
606 606 written, but need to be read to be sure (size is the same
607 607 but mtime differs)
608 608 modified:
609 609 files that have definitely been modified since the dirstate
610 610 was written (different size or mode)
611 611 added:
612 612 files that have been explicitly added with hg add
613 613 removed:
614 614 files that have been explicitly removed with hg remove
615 615 deleted:
616 616 files that have been deleted through other means ("missing")
617 617 unknown:
618 618 files not in the dirstate that are not ignored
619 619 ignored:
620 620 files not in the dirstate that are ignored
621 621 (by _dirignore())
622 622 clean:
623 623 files that have definitely not been modified since the
624 624 dirstate was written
625 625 '''
626 626 listignored, listclean, listunknown = ignored, clean, unknown
627 627 lookup, modified, added, unknown, ignored = [], [], [], [], []
628 628 removed, deleted, clean = [], [], []
629 629
630 630 dmap = self._map
631 631 ladd = lookup.append # aka "unsure"
632 632 madd = modified.append
633 633 aadd = added.append
634 634 uadd = unknown.append
635 635 iadd = ignored.append
636 636 radd = removed.append
637 637 dadd = deleted.append
638 638 cadd = clean.append
639 639
640 640 lnkkind = stat.S_IFLNK
641 641
642 642 for fn, st in self.walk(match, subrepos, listunknown,
643 643 listignored).iteritems():
644 644 if fn not in dmap:
645 645 if (listignored or match.exact(fn)) and self._dirignore(fn):
646 646 if listignored:
647 647 iadd(fn)
648 648 elif listunknown:
649 649 uadd(fn)
650 650 continue
651 651
652 652 state, mode, size, time = dmap[fn]
653 653
654 654 if not st and state in "nma":
655 655 dadd(fn)
656 656 elif state == 'n':
657 657 # The "mode & lnkkind != lnkkind or self._checklink"
658 658 # lines are an expansion of "islink => checklink"
659 659 # where islink means "is this a link?" and checklink
660 660 # means "can we check links?".
661 661 if (size >= 0 and
662 662 (size != st.st_size
663 663 or ((mode ^ st.st_mode) & 0100 and self._checkexec))
664 664 and (mode & lnkkind != lnkkind or self._checklink)
665 665 or size == -2 # other parent
666 666 or fn in self._copymap):
667 667 madd(fn)
668 668 elif (time != int(st.st_mtime)
669 669 and (mode & lnkkind != lnkkind or self._checklink)):
670 670 ladd(fn)
671 671 elif listclean:
672 672 cadd(fn)
673 673 elif state == 'm':
674 674 madd(fn)
675 675 elif state == 'a':
676 676 aadd(fn)
677 677 elif state == 'r':
678 678 radd(fn)
679 679
680 680 return (lookup, modified, added, removed, deleted, unknown, ignored,
681 681 clean)
@@ -1,658 +1,677 b''
1 1 $ rm -rf sub
2 2 $ mkdir sub
3 3 $ cd sub
4 4 $ hg init t
5 5 $ cd t
6 6
7 7 first revision, no sub
8 8
9 9 $ echo a > a
10 10 $ hg ci -Am0
11 11 adding a
12 12
13 13 add first sub
14 14
15 15 $ echo s = s > .hgsub
16 16 $ hg add .hgsub
17 17 $ hg init s
18 18 $ echo a > s/a
19 19
20 20 Issue2232: committing a subrepo without .hgsub
21 21
22 22 $ hg ci -mbad s
23 23 abort: can't commit subrepos without .hgsub
24 24 [255]
25 25
26 26 $ hg -R s ci -Ams0
27 27 adding a
28 28 $ hg sum
29 29 parent: 0:f7b1eb17ad24 tip
30 30 0
31 31 branch: default
32 32 commit: 1 added, 1 subrepos
33 33 update: (current)
34 34 $ hg ci -m1
35 35 committing subrepository s
36 36
37 37 Issue2022: update -C
38 38
39 39 $ echo b > s/a
40 40 $ hg sum
41 41 parent: 1:7cf8cfea66e4 tip
42 42 1
43 43 branch: default
44 44 commit: 1 subrepos
45 45 update: (current)
46 46 $ hg co -C 1
47 47 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
48 48 $ hg sum
49 49 parent: 1:7cf8cfea66e4 tip
50 50 1
51 51 branch: default
52 52 commit: (clean)
53 53 update: (current)
54 54
55 55 add sub sub
56 56
57 57 $ echo ss = ss > s/.hgsub
58 58 $ hg init s/ss
59 59 $ echo a > s/ss/a
60 60 $ hg -R s add s/.hgsub
61 61 $ hg -R s/ss add s/ss/a
62 62 $ hg sum
63 63 parent: 1:7cf8cfea66e4 tip
64 64 1
65 65 branch: default
66 66 commit: 1 subrepos
67 67 update: (current)
68 68 $ hg ci -m2
69 69 committing subrepository s
70 70 committing subrepository s/ss
71 71 $ hg sum
72 72 parent: 2:df30734270ae tip
73 73 2
74 74 branch: default
75 75 commit: (clean)
76 76 update: (current)
77 77
78 78 bump sub rev
79 79
80 80 $ echo b > s/a
81 81 $ hg -R s ci -ms1
82 82 $ hg ci -m3
83 83 committing subrepository s
84 84
85 85 leave sub dirty
86 86
87 87 $ echo c > s/a
88 88 $ hg ci -m4
89 89 committing subrepository s
90 90 $ hg tip -R s
91 91 changeset: 3:1c833a7a9e3a
92 92 tag: tip
93 93 user: test
94 94 date: Thu Jan 01 00:00:00 1970 +0000
95 95 summary: 4
96 96
97 97
98 98 check caching
99 99
100 100 $ hg co 0
101 101 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
102 102 $ hg debugsub
103 103
104 104 restore
105 105
106 106 $ hg co
107 107 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
108 108 $ hg debugsub
109 109 path s
110 110 source s
111 111 revision 1c833a7a9e3a4445c711aaf0f012379cd0d4034e
112 112
113 113 new branch for merge tests
114 114
115 115 $ hg co 1
116 116 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
117 117 $ echo t = t >> .hgsub
118 118 $ hg init t
119 119 $ echo t > t/t
120 120 $ hg -R t add t
121 121 adding t/t
122 122
123 123 5
124 124
125 125 $ hg ci -m5 # add sub
126 126 committing subrepository t
127 127 created new head
128 128 $ echo t2 > t/t
129 129
130 130 6
131 131
132 132 $ hg st -R s
133 133 $ hg ci -m6 # change sub
134 134 committing subrepository t
135 135 $ hg debugsub
136 136 path s
137 137 source s
138 138 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
139 139 path t
140 140 source t
141 141 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
142 142 $ echo t3 > t/t
143 143
144 144 7
145 145
146 146 $ hg ci -m7 # change sub again for conflict test
147 147 committing subrepository t
148 148 $ hg rm .hgsub
149 149
150 150 8
151 151
152 152 $ hg ci -m8 # remove sub
153 153
154 154 merge tests
155 155
156 156 $ hg co -C 3
157 157 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
158 158 $ hg merge 5 # test adding
159 159 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
160 160 (branch merge, don't forget to commit)
161 161 $ hg debugsub
162 162 path s
163 163 source s
164 164 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
165 165 path t
166 166 source t
167 167 revision 60ca1237c19474e7a3978b0dc1ca4e6f36d51382
168 168 $ hg ci -m9
169 169 created new head
170 170 $ hg merge 6 --debug # test change
171 171 searching for copies back to rev 2
172 172 resolving manifests
173 173 overwrite None partial False
174 174 ancestor 1f14a2e2d3ec local f0d2028bf86d+ remote 1831e14459c4
175 175 .hgsubstate: versions differ -> m
176 176 updating: .hgsubstate 1/1 files (100.00%)
177 177 subrepo merge f0d2028bf86d+ 1831e14459c4 1f14a2e2d3ec
178 178 subrepo t: other changed, get t:6747d179aa9a688023c4b0cad32e4c92bb7f34ad:hg
179 179 getting subrepo t
180 180 resolving manifests
181 181 overwrite True partial False
182 182 ancestor 60ca1237c194+ local 60ca1237c194+ remote 6747d179aa9a
183 183 t: remote is newer -> g
184 184 updating: t 1/1 files (100.00%)
185 185 getting t
186 186 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
187 187 (branch merge, don't forget to commit)
188 188 $ hg debugsub
189 189 path s
190 190 source s
191 191 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
192 192 path t
193 193 source t
194 194 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
195 195 $ echo conflict > t/t
196 196 $ hg ci -m10
197 197 committing subrepository t
198 198 $ HGMERGE=internal:merge hg merge --debug 7 # test conflict
199 199 searching for copies back to rev 2
200 200 resolving manifests
201 201 overwrite None partial False
202 202 ancestor 1831e14459c4 local e45c8b14af55+ remote f94576341bcf
203 203 .hgsubstate: versions differ -> m
204 204 updating: .hgsubstate 1/1 files (100.00%)
205 205 subrepo merge e45c8b14af55+ f94576341bcf 1831e14459c4
206 206 subrepo t: both sides changed, merge with t:7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4:hg
207 207 merging subrepo t
208 208 searching for copies back to rev 2
209 209 resolving manifests
210 210 overwrite None partial False
211 211 ancestor 6747d179aa9a local 20a0db6fbf6c+ remote 7af322bc1198
212 212 t: versions differ -> m
213 213 preserving t for resolve of t
214 214 updating: t 1/1 files (100.00%)
215 215 picked tool 'internal:merge' for t (binary False symlink False)
216 216 merging t
217 217 my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
218 218 warning: conflicts during merge.
219 219 merging t failed!
220 220 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
221 221 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
222 222 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
223 223 (branch merge, don't forget to commit)
224 224
225 225 should conflict
226 226
227 227 $ cat t/t
228 228 <<<<<<< local
229 229 conflict
230 230 =======
231 231 t3
232 232 >>>>>>> other
233 233
234 234 clone
235 235
236 236 $ cd ..
237 237 $ hg clone t tc
238 238 updating to branch default
239 239 pulling subrepo s from $TESTTMP/sub/t/s
240 240 requesting all changes
241 241 adding changesets
242 242 adding manifests
243 243 adding file changes
244 244 added 4 changesets with 5 changes to 3 files
245 245 pulling subrepo s/ss from $TESTTMP/sub/t/s/ss
246 246 requesting all changes
247 247 adding changesets
248 248 adding manifests
249 249 adding file changes
250 250 added 1 changesets with 1 changes to 1 files
251 251 pulling subrepo t from $TESTTMP/sub/t/t
252 252 requesting all changes
253 253 adding changesets
254 254 adding manifests
255 255 adding file changes
256 256 added 4 changesets with 4 changes to 1 files (+1 heads)
257 257 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
258 258 $ cd tc
259 259 $ hg debugsub
260 260 path s
261 261 source s
262 262 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
263 263 path t
264 264 source t
265 265 revision 20a0db6fbf6c3d2836e6519a642ae929bfc67c0e
266 266
267 267 push
268 268
269 269 $ echo bah > t/t
270 270 $ hg ci -m11
271 271 committing subrepository t
272 272 $ hg push
273 273 pushing to $TESTTMP/sub/t
274 274 pushing subrepo s/ss to $TESTTMP/sub/t/s/ss
275 275 searching for changes
276 276 no changes found
277 277 pushing subrepo s to $TESTTMP/sub/t/s
278 278 searching for changes
279 279 no changes found
280 280 pushing subrepo t to $TESTTMP/sub/t/t
281 281 searching for changes
282 282 adding changesets
283 283 adding manifests
284 284 adding file changes
285 285 added 1 changesets with 1 changes to 1 files
286 286 searching for changes
287 287 adding changesets
288 288 adding manifests
289 289 adding file changes
290 290 added 1 changesets with 1 changes to 1 files
291 291
292 292 push -f
293 293
294 294 $ echo bah > s/a
295 295 $ hg ci -m12
296 296 committing subrepository s
297 297 $ hg push
298 298 pushing to $TESTTMP/sub/t
299 299 pushing subrepo s/ss to $TESTTMP/sub/t/s/ss
300 300 searching for changes
301 301 no changes found
302 302 pushing subrepo s to $TESTTMP/sub/t/s
303 303 searching for changes
304 304 abort: push creates new remote heads on branch 'default'!
305 305 (did you forget to merge? use push -f to force)
306 306 [255]
307 307 $ hg push -f
308 308 pushing to $TESTTMP/sub/t
309 309 pushing subrepo s/ss to $TESTTMP/sub/t/s/ss
310 310 searching for changes
311 311 no changes found
312 312 pushing subrepo s to $TESTTMP/sub/t/s
313 313 searching for changes
314 314 adding changesets
315 315 adding manifests
316 316 adding file changes
317 317 added 1 changesets with 1 changes to 1 files (+1 heads)
318 318 pushing subrepo t to $TESTTMP/sub/t/t
319 319 searching for changes
320 320 no changes found
321 321 searching for changes
322 322 adding changesets
323 323 adding manifests
324 324 adding file changes
325 325 added 1 changesets with 1 changes to 1 files
326 326
327 327 update
328 328
329 329 $ cd ../t
330 330 $ hg up -C # discard our earlier merge
331 331 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
332 332 $ echo blah > t/t
333 333 $ hg ci -m13
334 334 committing subrepository t
335 335
336 336 pull
337 337
338 338 $ cd ../tc
339 339 $ hg pull
340 340 pulling from $TESTTMP/sub/t
341 341 searching for changes
342 342 adding changesets
343 343 adding manifests
344 344 adding file changes
345 345 added 1 changesets with 1 changes to 1 files
346 346 (run 'hg update' to get a working copy)
347 347
348 348 should pull t
349 349
350 350 $ hg up
351 351 pulling subrepo t from $TESTTMP/sub/t/t
352 352 searching for changes
353 353 adding changesets
354 354 adding manifests
355 355 adding file changes
356 356 added 1 changesets with 1 changes to 1 files
357 357 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
358 358 $ cat t/t
359 359 blah
360 360
361 361 bogus subrepo path aborts
362 362
363 363 $ echo 'bogus=[boguspath' >> .hgsub
364 364 $ hg ci -m 'bogus subrepo path'
365 365 abort: missing ] in subrepo source
366 366 [255]
367 367
368 368 Issue1986: merge aborts when trying to merge a subrepo that
369 369 shouldn't need merging
370 370
371 371 # subrepo layout
372 372 #
373 373 # o 5 br
374 374 # /|
375 375 # o | 4 default
376 376 # | |
377 377 # | o 3 br
378 378 # |/|
379 379 # o | 2 default
380 380 # | |
381 381 # | o 1 br
382 382 # |/
383 383 # o 0 default
384 384
385 385 $ cd ..
386 386 $ rm -rf sub
387 387 $ hg init main
388 388 $ cd main
389 389 $ hg init s
390 390 $ cd s
391 391 $ echo a > a
392 392 $ hg ci -Am1
393 393 adding a
394 394 $ hg branch br
395 395 marked working directory as branch br
396 396 $ echo a >> a
397 397 $ hg ci -m1
398 398 $ hg up default
399 399 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
400 400 $ echo b > b
401 401 $ hg ci -Am1
402 402 adding b
403 403 $ hg up br
404 404 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
405 405 $ hg merge tip
406 406 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
407 407 (branch merge, don't forget to commit)
408 408 $ hg ci -m1
409 409 $ hg up 2
410 410 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
411 411 $ echo c > c
412 412 $ hg ci -Am1
413 413 adding c
414 414 $ hg up 3
415 415 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
416 416 $ hg merge 4
417 417 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
418 418 (branch merge, don't forget to commit)
419 419 $ hg ci -m1
420 420
421 421 # main repo layout:
422 422 #
423 423 # * <-- try to merge default into br again
424 424 # .`|
425 425 # . o 5 br --> substate = 5
426 426 # . |
427 427 # o | 4 default --> substate = 4
428 428 # | |
429 429 # | o 3 br --> substate = 2
430 430 # |/|
431 431 # o | 2 default --> substate = 2
432 432 # | |
433 433 # | o 1 br --> substate = 3
434 434 # |/
435 435 # o 0 default --> substate = 2
436 436
437 437 $ cd ..
438 438 $ echo 's = s' > .hgsub
439 439 $ hg -R s up 2
440 440 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
441 441 $ hg ci -Am1
442 442 adding .hgsub
443 443 committing subrepository s
444 444 $ hg branch br
445 445 marked working directory as branch br
446 446 $ echo b > b
447 447 $ hg -R s up 3
448 448 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
449 449 $ hg ci -Am1
450 450 adding b
451 451 committing subrepository s
452 452 $ hg up default
453 453 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
454 454 $ echo c > c
455 455 $ hg ci -Am1
456 456 adding c
457 457 $ hg up 1
458 458 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
459 459 $ hg merge 2
460 460 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
461 461 (branch merge, don't forget to commit)
462 462 $ hg ci -m1
463 463 $ hg up 2
464 464 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
465 465 $ hg -R s up 4
466 466 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
467 467 $ echo d > d
468 468 $ hg ci -Am1
469 469 adding d
470 470 committing subrepository s
471 471 $ hg up 3
472 472 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
473 473 $ hg -R s up 5
474 474 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
475 475 $ echo e > e
476 476 $ hg ci -Am1
477 477 adding e
478 478 committing subrepository s
479 479
480 480 $ hg up 5
481 481 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
482 482 $ hg merge 4 # try to merge default into br again
483 483 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
484 484 (branch merge, don't forget to commit)
485 485 $ cd ..
486 486
487 487 test subrepo delete from .hgsubstate
488 488
489 489 $ hg init testdelete
490 490 $ mkdir testdelete/nested testdelete/nested2
491 491 $ hg init testdelete/nested
492 492 $ hg init testdelete/nested2
493 493 $ echo test > testdelete/nested/foo
494 494 $ echo test > testdelete/nested2/foo
495 495 $ hg -R testdelete/nested add
496 496 adding testdelete/nested/foo
497 497 $ hg -R testdelete/nested2 add
498 498 adding testdelete/nested2/foo
499 499 $ hg -R testdelete/nested ci -m test
500 500 $ hg -R testdelete/nested2 ci -m test
501 501 $ echo nested = nested > testdelete/.hgsub
502 502 $ echo nested2 = nested2 >> testdelete/.hgsub
503 503 $ hg -R testdelete add
504 504 adding testdelete/.hgsub
505 505 $ hg -R testdelete ci -m "nested 1 & 2 added"
506 506 committing subrepository nested
507 507 committing subrepository nested2
508 508 $ echo nested = nested > testdelete/.hgsub
509 509 $ hg -R testdelete ci -m "nested 2 deleted"
510 510 $ cat testdelete/.hgsubstate
511 511 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
512 512 $ hg -R testdelete remove testdelete/.hgsub
513 513 $ hg -R testdelete ci -m ".hgsub deleted"
514 514 $ cat testdelete/.hgsubstate
515 515
516 516 test repository cloning
517 517
518 518 $ mkdir mercurial mercurial2
519 519 $ hg init nested_absolute
520 520 $ echo test > nested_absolute/foo
521 521 $ hg -R nested_absolute add
522 522 adding nested_absolute/foo
523 523 $ hg -R nested_absolute ci -mtest
524 524 $ cd mercurial
525 525 $ hg init nested_relative
526 526 $ echo test2 > nested_relative/foo2
527 527 $ hg -R nested_relative add
528 528 adding nested_relative/foo2
529 529 $ hg -R nested_relative ci -mtest2
530 530 $ hg init main
531 531 $ echo "nested_relative = ../nested_relative" > main/.hgsub
532 532 $ echo "nested_absolute = `pwd`/nested_absolute" >> main/.hgsub
533 533 $ hg -R main add
534 534 adding main/.hgsub
535 535 $ hg -R main ci -m "add subrepos"
536 536 committing subrepository nested_absolute
537 537 committing subrepository nested_relative
538 538 $ cd ..
539 539 $ hg clone mercurial/main mercurial2/main
540 540 updating to branch default
541 541 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
542 542 $ cat mercurial2/main/nested_absolute/.hg/hgrc \
543 543 > mercurial2/main/nested_relative/.hg/hgrc
544 544 [paths]
545 545 default = $TESTTMP/sub/mercurial/nested_absolute
546 546 [paths]
547 547 default = $TESTTMP/sub/mercurial/nested_relative
548 548 $ rm -rf mercurial mercurial2
549 549
550 550 Issue1977: multirepo push should fail if subrepo push fails
551 551
552 552 $ hg init repo
553 553 $ hg init repo/s
554 554 $ echo a > repo/s/a
555 555 $ hg -R repo/s ci -Am0
556 556 adding a
557 557 $ echo s = s > repo/.hgsub
558 558 $ hg -R repo ci -Am1
559 559 adding .hgsub
560 560 committing subrepository s
561 561 $ hg clone repo repo2
562 562 updating to branch default
563 563 pulling subrepo s from $TESTTMP/sub/repo/s
564 564 requesting all changes
565 565 adding changesets
566 566 adding manifests
567 567 adding file changes
568 568 added 1 changesets with 1 changes to 1 files
569 569 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
570 570 $ hg -q -R repo2 pull -u
571 571 $ echo 1 > repo2/s/a
572 572 $ hg -R repo2/s ci -m2
573 573 $ hg -q -R repo2/s push
574 574 $ hg -R repo2/s up -C 0
575 575 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
576 576 $ echo 2 > repo2/s/a
577 577 $ hg -R repo2/s ci -m3
578 578 created new head
579 579 $ hg -R repo2 ci -m3
580 580 committing subrepository s
581 581 $ hg -q -R repo2 push
582 582 abort: push creates new remote heads on branch 'default'!
583 583 (did you forget to merge? use push -f to force)
584 584 [255]
585 585 $ hg -R repo update
586 586 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
587 587 $ rm -rf repo2 repo
588 588
589 589
590 590 Issue1852 subrepos with relative paths always push/pull relative to default
591 591
592 592 Prepare a repo with subrepo
593 593
594 594 $ hg init issue1852a
595 595 $ cd issue1852a
596 596 $ hg init sub/repo
597 597 $ echo test > sub/repo/foo
598 598 $ hg -R sub/repo add sub/repo/foo
599 599 $ echo sub/repo = sub/repo > .hgsub
600 600 $ hg add .hgsub
601 601 $ hg ci -mtest
602 602 committing subrepository sub/repo
603 603 $ echo test >> sub/repo/foo
604 604 $ hg ci -mtest
605 605 committing subrepository sub/repo
606 606 $ cd ..
607 607
608 608 Create repo without default path, pull top repo, and see what happens on update
609 609
610 610 $ hg init issue1852b
611 611 $ hg -R issue1852b pull issue1852a
612 612 pulling from issue1852a
613 613 requesting all changes
614 614 adding changesets
615 615 adding manifests
616 616 adding file changes
617 617 added 2 changesets with 3 changes to 2 files
618 618 (run 'hg update' to get a working copy)
619 619 $ hg -R issue1852b update
620 620 abort: default path for subrepository sub/repo not found
621 621 [255]
622 622
623 623 Pull -u now doesn't help
624 624
625 625 $ hg -R issue1852b pull -u issue1852a
626 626 pulling from issue1852a
627 627 searching for changes
628 628 no changes found
629 629
630 630 Try the same, but with pull -u
631 631
632 632 $ hg init issue1852c
633 633 $ hg -R issue1852c pull -r0 -u issue1852a
634 634 pulling from issue1852a
635 635 adding changesets
636 636 adding manifests
637 637 adding file changes
638 638 added 1 changesets with 2 changes to 2 files
639 639 pulling subrepo sub/repo from issue1852a/sub/repo
640 640 requesting all changes
641 641 adding changesets
642 642 adding manifests
643 643 adding file changes
644 644 added 2 changesets with 2 changes to 1 files
645 645 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
646 646
647 647 Try to push from the other side
648 648
649 649 $ hg -R issue1852a push `pwd`/issue1852c
650 650 pushing to $TESTTMP/sub/issue1852c
651 651 pushing subrepo sub/repo to $TESTTMP/sub/issue1852c/sub/repo
652 652 searching for changes
653 653 no changes found
654 654 searching for changes
655 655 adding changesets
656 656 adding manifests
657 657 adding file changes
658 658 added 1 changesets with 1 changes to 1 files
659
660 Check status of files when none of them belong to the first
661 subrepository:
662
663 $ hg init subrepo-status
664 $ cd subrepo-status
665 $ hg init subrepo-1
666 $ hg init subrepo-2
667 $ cd subrepo-2
668 $ touch file
669 $ hg add file
670 $ cd ..
671 $ echo subrepo-1 = subrepo-1 > .hgsub
672 $ echo subrepo-2 = subrepo-2 >> .hgsub
673 $ hg add .hgsub
674 $ hg ci -m 'Added subrepos'
675 committing subrepository subrepo-1
676 committing subrepository subrepo-2
677 $ hg st subrepo-2/file
General Comments 0
You need to be logged in to leave comments. Login now