##// END OF EJS Templates
dirstate: ignore stat data for files that were updated too recently...
Alexis S. L. Carvalho -
r6326:af3f26b6 default
parent child Browse files
Show More
@@ -1,624 +1,636 b''
1 1 """
2 2 dirstate.py - working directory tracking for mercurial
3 3
4 4 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 5
6 6 This software may be used and distributed according to the terms
7 7 of the GNU General Public License, incorporated herein by reference.
8 8 """
9 9
10 10 from node import nullid
11 11 from i18n import _
12 12 import struct, os, bisect, stat, strutil, util, errno, ignore
13 import cStringIO, osutil
13 import cStringIO, osutil, sys
14 14
15 15 _unknown = ('?', 0, 0, 0)
16 16 _format = ">cllll"
17 17
18 18 class dirstate(object):
19 19
20 20 def __init__(self, opener, ui, root):
21 21 self._opener = opener
22 22 self._root = root
23 23 self._dirty = False
24 24 self._dirtypl = False
25 25 self._ui = ui
26 26
27 27 def __getattr__(self, name):
28 28 if name == '_map':
29 29 self._read()
30 30 return self._map
31 31 elif name == '_copymap':
32 32 self._read()
33 33 return self._copymap
34 34 elif name == '_branch':
35 35 try:
36 36 self._branch = (self._opener("branch").read().strip()
37 37 or "default")
38 38 except IOError:
39 39 self._branch = "default"
40 40 return self._branch
41 41 elif name == '_pl':
42 42 self._pl = [nullid, nullid]
43 43 try:
44 44 st = self._opener("dirstate").read(40)
45 45 if len(st) == 40:
46 46 self._pl = st[:20], st[20:40]
47 47 except IOError, err:
48 48 if err.errno != errno.ENOENT: raise
49 49 return self._pl
50 50 elif name == '_dirs':
51 51 self._dirs = {}
52 52 for f in self._map:
53 53 if self[f] != 'r':
54 54 self._incpath(f)
55 55 return self._dirs
56 56 elif name == '_ignore':
57 57 files = [self._join('.hgignore')]
58 58 for name, path in self._ui.configitems("ui"):
59 59 if name == 'ignore' or name.startswith('ignore.'):
60 60 files.append(os.path.expanduser(path))
61 61 self._ignore = ignore.ignore(self._root, files, self._ui.warn)
62 62 return self._ignore
63 63 elif name == '_slash':
64 64 self._slash = self._ui.configbool('ui', 'slash') and os.sep != '/'
65 65 return self._slash
66 66 elif name == '_checkexec':
67 67 self._checkexec = util.checkexec(self._root)
68 68 return self._checkexec
69 elif name == '_limit':
70 try:
71 self._limit = int(self._ui.config('ui', 'limit', 1))
72 except ValueError:
73 self._limit = 1
74 return self._limit
69 75 else:
70 76 raise AttributeError, name
71 77
72 78 def _join(self, f):
73 79 return os.path.join(self._root, f)
74 80
75 81 def getcwd(self):
76 82 cwd = os.getcwd()
77 83 if cwd == self._root: return ''
78 84 # self._root ends with a path separator if self._root is '/' or 'C:\'
79 85 rootsep = self._root
80 86 if not util.endswithsep(rootsep):
81 87 rootsep += os.sep
82 88 if cwd.startswith(rootsep):
83 89 return cwd[len(rootsep):]
84 90 else:
85 91 # we're outside the repo. return an absolute path.
86 92 return cwd
87 93
88 94 def pathto(self, f, cwd=None):
89 95 if cwd is None:
90 96 cwd = self.getcwd()
91 97 path = util.pathto(self._root, cwd, f)
92 98 if self._slash:
93 99 return util.normpath(path)
94 100 return path
95 101
96 102 def __getitem__(self, key):
97 103 ''' current states:
98 104 n normal
99 105 m needs merging
100 106 r marked for removal
101 107 a marked for addition
102 108 ? not tracked'''
103 109 return self._map.get(key, ("?",))[0]
104 110
105 111 def __contains__(self, key):
106 112 return key in self._map
107 113
108 114 def __iter__(self):
109 115 a = self._map.keys()
110 116 a.sort()
111 117 for x in a:
112 118 yield x
113 119
114 120 def parents(self):
115 121 return self._pl
116 122
117 123 def branch(self):
118 124 return self._branch
119 125
120 126 def setparents(self, p1, p2=nullid):
121 127 self._dirty = self._dirtypl = True
122 128 self._pl = p1, p2
123 129
124 130 def setbranch(self, branch):
125 131 self._branch = branch
126 132 self._opener("branch", "w").write(branch + '\n')
127 133
128 134 def _read(self):
129 135 self._map = {}
130 136 self._copymap = {}
131 137 if not self._dirtypl:
132 138 self._pl = [nullid, nullid]
133 139 try:
134 140 st = self._opener("dirstate").read()
135 141 except IOError, err:
136 142 if err.errno != errno.ENOENT: raise
137 143 return
138 144 if not st:
139 145 return
140 146
141 147 if not self._dirtypl:
142 148 self._pl = [st[:20], st[20: 40]]
143 149
144 150 # deref fields so they will be local in loop
145 151 dmap = self._map
146 152 copymap = self._copymap
147 153 unpack = struct.unpack
148 154 e_size = struct.calcsize(_format)
149 155 pos1 = 40
150 156 l = len(st)
151 157
152 158 # the inner loop
153 159 while pos1 < l:
154 160 pos2 = pos1 + e_size
155 161 e = unpack(">cllll", st[pos1:pos2]) # a literal here is faster
156 162 pos1 = pos2 + e[4]
157 163 f = st[pos2:pos1]
158 164 if '\0' in f:
159 165 f, c = f.split('\0')
160 166 copymap[f] = c
161 167 dmap[f] = e # we hold onto e[4] because making a subtuple is slow
162 168
163 169 def invalidate(self):
164 170 for a in "_map _copymap _branch _pl _dirs _ignore".split():
165 171 if a in self.__dict__:
166 172 delattr(self, a)
167 173 self._dirty = False
168 174
169 175 def copy(self, source, dest):
170 176 self._dirty = True
171 177 self._copymap[dest] = source
172 178
173 179 def copied(self, file):
174 180 return self._copymap.get(file, None)
175 181
176 182 def copies(self):
177 183 return self._copymap
178 184
179 185 def _incpath(self, path):
180 186 c = path.rfind('/')
181 187 if c >= 0:
182 188 dirs = self._dirs
183 189 base = path[:c]
184 190 if base not in dirs:
185 191 self._incpath(base)
186 192 dirs[base] = 1
187 193 else:
188 194 dirs[base] += 1
189 195
190 196 def _decpath(self, path):
191 197 c = path.rfind('/')
192 198 if c >= 0:
193 199 base = path[:c]
194 200 dirs = self._dirs
195 201 if dirs[base] == 1:
196 202 del dirs[base]
197 203 self._decpath(base)
198 204 else:
199 205 dirs[base] -= 1
200 206
201 207 def _incpathcheck(self, f):
202 208 if '\r' in f or '\n' in f:
203 209 raise util.Abort(_("'\\n' and '\\r' disallowed in filenames: %r")
204 210 % f)
205 211 # shadows
206 212 if f in self._dirs:
207 213 raise util.Abort(_('directory %r already in dirstate') % f)
208 214 for c in strutil.rfindall(f, '/'):
209 215 d = f[:c]
210 216 if d in self._dirs:
211 217 break
212 218 if d in self._map and self[d] != 'r':
213 219 raise util.Abort(_('file %r in dirstate clashes with %r') %
214 220 (d, f))
215 221 self._incpath(f)
216 222
217 223 def _changepath(self, f, newstate, relaxed=False):
218 224 # handle upcoming path changes
219 225 oldstate = self[f]
220 226 if oldstate not in "?r" and newstate in "?r":
221 227 if "_dirs" in self.__dict__:
222 228 self._decpath(f)
223 229 return
224 230 if oldstate in "?r" and newstate not in "?r":
225 231 if relaxed and oldstate == '?':
226 232 # XXX
227 233 # in relaxed mode we assume the caller knows
228 234 # what it is doing, workaround for updating
229 235 # dir-to-file revisions
230 236 if "_dirs" in self.__dict__:
231 237 self._incpath(f)
232 238 return
233 239 self._incpathcheck(f)
234 240 return
235 241
236 242 def normal(self, f):
237 243 'mark a file normal and clean'
238 244 self._dirty = True
239 245 self._changepath(f, 'n', True)
240 246 s = os.lstat(self._join(f))
241 247 self._map[f] = ('n', s.st_mode, s.st_size, s.st_mtime, 0)
242 248 if f in self._copymap:
243 249 del self._copymap[f]
244 250
245 251 def normallookup(self, f):
246 252 'mark a file normal, but possibly dirty'
247 253 if self._pl[1] != nullid and f in self._map:
248 254 # if there is a merge going on and the file was either
249 255 # in state 'm' or dirty before being removed, restore that state.
250 256 entry = self._map[f]
251 257 if entry[0] == 'r' and entry[2] in (-1, -2):
252 258 source = self._copymap.get(f)
253 259 if entry[2] == -1:
254 260 self.merge(f)
255 261 elif entry[2] == -2:
256 262 self.normaldirty(f)
257 263 if source:
258 264 self.copy(source, f)
259 265 return
260 266 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
261 267 return
262 268 self._dirty = True
263 269 self._changepath(f, 'n', True)
264 270 self._map[f] = ('n', 0, -1, -1, 0)
265 271 if f in self._copymap:
266 272 del self._copymap[f]
267 273
268 274 def normaldirty(self, f):
269 275 'mark a file normal, but dirty'
270 276 self._dirty = True
271 277 self._changepath(f, 'n', True)
272 278 self._map[f] = ('n', 0, -2, -1, 0)
273 279 if f in self._copymap:
274 280 del self._copymap[f]
275 281
276 282 def add(self, f):
277 283 'mark a file added'
278 284 self._dirty = True
279 285 self._changepath(f, 'a')
280 286 self._map[f] = ('a', 0, -1, -1, 0)
281 287 if f in self._copymap:
282 288 del self._copymap[f]
283 289
284 290 def remove(self, f):
285 291 'mark a file removed'
286 292 self._dirty = True
287 293 self._changepath(f, 'r')
288 294 size = 0
289 295 if self._pl[1] != nullid and f in self._map:
290 296 entry = self._map[f]
291 297 if entry[0] == 'm':
292 298 size = -1
293 299 elif entry[0] == 'n' and entry[2] == -2:
294 300 size = -2
295 301 self._map[f] = ('r', 0, size, 0, 0)
296 302 if size == 0 and f in self._copymap:
297 303 del self._copymap[f]
298 304
299 305 def merge(self, f):
300 306 'mark a file merged'
301 307 self._dirty = True
302 308 s = os.lstat(self._join(f))
303 309 self._changepath(f, 'm', True)
304 310 self._map[f] = ('m', s.st_mode, s.st_size, s.st_mtime, 0)
305 311 if f in self._copymap:
306 312 del self._copymap[f]
307 313
308 314 def forget(self, f):
309 315 'forget a file'
310 316 self._dirty = True
311 317 try:
312 318 self._changepath(f, '?')
313 319 del self._map[f]
314 320 except KeyError:
315 321 self._ui.warn(_("not in dirstate: %s\n") % f)
316 322
317 323 def clear(self):
318 324 self._map = {}
319 325 if "_dirs" in self.__dict__:
320 326 delattr(self, "_dirs");
321 327 self._copymap = {}
322 328 self._pl = [nullid, nullid]
323 329 self._dirty = True
324 330
325 331 def rebuild(self, parent, files):
326 332 self.clear()
327 333 for f in files:
328 334 if files.execf(f):
329 335 self._map[f] = ('n', 0777, -1, 0, 0)
330 336 else:
331 337 self._map[f] = ('n', 0666, -1, 0, 0)
332 338 self._pl = (parent, nullid)
333 339 self._dirty = True
334 340
335 341 def write(self):
336 342 if not self._dirty:
337 343 return
344 st = self._opener("dirstate", "w", atomictemp=True)
345 if self._limit > 0:
346 limit = util.fstat(st).st_mtime - self._limit
347 else:
348 limit = sys.maxint
338 349 cs = cStringIO.StringIO()
339 350 copymap = self._copymap
340 351 pack = struct.pack
341 352 write = cs.write
342 353 write("".join(self._pl))
343 354 for f, e in self._map.iteritems():
344 355 if f in copymap:
345 356 f = "%s\0%s" % (f, copymap[f])
357 if e[3] > limit and e[0] == 'n':
358 e = (e[0], 0, -1, -1, 0)
346 359 e = pack(_format, e[0], e[1], e[2], e[3], len(f))
347 360 write(e)
348 361 write(f)
349 st = self._opener("dirstate", "w", atomictemp=True)
350 362 st.write(cs.getvalue())
351 363 st.rename()
352 364 self._dirty = self._dirtypl = False
353 365
354 366 def _filter(self, files):
355 367 ret = {}
356 368 unknown = []
357 369
358 370 for x in files:
359 371 if x == '.':
360 372 return self._map.copy()
361 373 if x not in self._map:
362 374 unknown.append(x)
363 375 else:
364 376 ret[x] = self._map[x]
365 377
366 378 if not unknown:
367 379 return ret
368 380
369 381 b = self._map.keys()
370 382 b.sort()
371 383 blen = len(b)
372 384
373 385 for x in unknown:
374 386 bs = bisect.bisect(b, "%s%s" % (x, '/'))
375 387 while bs < blen:
376 388 s = b[bs]
377 389 if len(s) > len(x) and s.startswith(x):
378 390 ret[s] = self._map[s]
379 391 else:
380 392 break
381 393 bs += 1
382 394 return ret
383 395
384 396 def _supported(self, f, mode, verbose=False):
385 397 if stat.S_ISREG(mode) or stat.S_ISLNK(mode):
386 398 return True
387 399 if verbose:
388 400 kind = 'unknown'
389 401 if stat.S_ISCHR(mode): kind = _('character device')
390 402 elif stat.S_ISBLK(mode): kind = _('block device')
391 403 elif stat.S_ISFIFO(mode): kind = _('fifo')
392 404 elif stat.S_ISSOCK(mode): kind = _('socket')
393 405 elif stat.S_ISDIR(mode): kind = _('directory')
394 406 self._ui.warn(_('%s: unsupported file type (type is %s)\n')
395 407 % (self.pathto(f), kind))
396 408 return False
397 409
398 410 def _dirignore(self, f):
399 411 if self._ignore(f):
400 412 return True
401 413 for c in strutil.findall(f, '/'):
402 414 if self._ignore(f[:c]):
403 415 return True
404 416 return False
405 417
406 418 def walk(self, files=None, match=util.always, badmatch=None):
407 419 # filter out the stat
408 420 for src, f, st in self.statwalk(files, match, badmatch=badmatch):
409 421 yield src, f
410 422
411 423 def statwalk(self, files=None, match=util.always, unknown=True,
412 424 ignored=False, badmatch=None, directories=False):
413 425 '''
414 426 walk recursively through the directory tree, finding all files
415 427 matched by the match function
416 428
417 429 results are yielded in a tuple (src, filename, st), where src
418 430 is one of:
419 431 'f' the file was found in the directory tree
420 432 'd' the file is a directory of the tree
421 433 'm' the file was only in the dirstate and not in the tree
422 434 'b' file was not found and matched badmatch
423 435
424 436 and st is the stat result if the file was found in the directory.
425 437 '''
426 438
427 439 # walk all files by default
428 440 if not files:
429 441 files = ['.']
430 442 dc = self._map.copy()
431 443 else:
432 444 files = util.unique(files)
433 445 dc = self._filter(files)
434 446
435 447 def imatch(file_):
436 448 if file_ not in dc and self._ignore(file_):
437 449 return False
438 450 return match(file_)
439 451
440 452 # TODO: don't walk unknown directories if unknown and ignored are False
441 453 ignore = self._ignore
442 454 dirignore = self._dirignore
443 455 if ignored:
444 456 imatch = match
445 457 ignore = util.never
446 458 dirignore = util.never
447 459
448 460 # self._root may end with a path separator when self._root == '/'
449 461 common_prefix_len = len(self._root)
450 462 if not util.endswithsep(self._root):
451 463 common_prefix_len += 1
452 464
453 465 normpath = util.normpath
454 466 listdir = osutil.listdir
455 467 lstat = os.lstat
456 468 bisect_left = bisect.bisect_left
457 469 isdir = os.path.isdir
458 470 pconvert = util.pconvert
459 471 join = os.path.join
460 472 s_isdir = stat.S_ISDIR
461 473 supported = self._supported
462 474 _join = self._join
463 475 known = {'.hg': 1}
464 476
465 477 # recursion free walker, faster than os.walk.
466 478 def findfiles(s):
467 479 work = [s]
468 480 wadd = work.append
469 481 found = []
470 482 add = found.append
471 483 if directories:
472 484 add((normpath(s[common_prefix_len:]), 'd', lstat(s)))
473 485 while work:
474 486 top = work.pop()
475 487 entries = listdir(top, stat=True)
476 488 # nd is the top of the repository dir tree
477 489 nd = normpath(top[common_prefix_len:])
478 490 if nd == '.':
479 491 nd = ''
480 492 else:
481 493 # do not recurse into a repo contained in this
482 494 # one. use bisect to find .hg directory so speed
483 495 # is good on big directory.
484 496 names = [e[0] for e in entries]
485 497 hg = bisect_left(names, '.hg')
486 498 if hg < len(names) and names[hg] == '.hg':
487 499 if isdir(join(top, '.hg')):
488 500 continue
489 501 for f, kind, st in entries:
490 502 np = pconvert(join(nd, f))
491 503 if np in known:
492 504 continue
493 505 known[np] = 1
494 506 p = join(top, f)
495 507 # don't trip over symlinks
496 508 if kind == stat.S_IFDIR:
497 509 if not ignore(np):
498 510 wadd(p)
499 511 if directories:
500 512 add((np, 'd', st))
501 513 if np in dc and match(np):
502 514 add((np, 'm', st))
503 515 elif imatch(np):
504 516 if supported(np, st.st_mode):
505 517 add((np, 'f', st))
506 518 elif np in dc:
507 519 add((np, 'm', st))
508 520 found.sort()
509 521 return found
510 522
511 523 # step one, find all files that match our criteria
512 524 files.sort()
513 525 for ff in files:
514 526 nf = normpath(ff)
515 527 f = _join(ff)
516 528 try:
517 529 st = lstat(f)
518 530 except OSError, inst:
519 531 found = False
520 532 for fn in dc:
521 533 if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
522 534 found = True
523 535 break
524 536 if not found:
525 537 if inst.errno != errno.ENOENT or not badmatch:
526 538 self._ui.warn('%s: %s\n' %
527 539 (self.pathto(ff), inst.strerror))
528 540 elif badmatch and badmatch(ff) and imatch(nf):
529 541 yield 'b', ff, None
530 542 continue
531 543 if s_isdir(st.st_mode):
532 544 if not dirignore(nf):
533 545 for f, src, st in findfiles(f):
534 546 yield src, f, st
535 547 else:
536 548 if nf in known:
537 549 continue
538 550 known[nf] = 1
539 551 if match(nf):
540 552 if supported(ff, st.st_mode, verbose=True):
541 553 yield 'f', nf, st
542 554 elif ff in dc:
543 555 yield 'm', nf, st
544 556
545 557 # step two run through anything left in the dc hash and yield
546 558 # if we haven't already seen it
547 559 ks = dc.keys()
548 560 ks.sort()
549 561 for k in ks:
550 562 if k in known:
551 563 continue
552 564 known[k] = 1
553 565 if imatch(k):
554 566 yield 'm', k, None
555 567
556 568 def status(self, files, match, list_ignored, list_clean, list_unknown=True):
557 569 lookup, modified, added, unknown, ignored = [], [], [], [], []
558 570 removed, deleted, clean = [], [], []
559 571
560 572 files = files or []
561 573 _join = self._join
562 574 lstat = os.lstat
563 575 cmap = self._copymap
564 576 dmap = self._map
565 577 ladd = lookup.append
566 578 madd = modified.append
567 579 aadd = added.append
568 580 uadd = unknown.append
569 581 iadd = ignored.append
570 582 radd = removed.append
571 583 dadd = deleted.append
572 584 cadd = clean.append
573 585
574 586 for src, fn, st in self.statwalk(files, match, unknown=list_unknown,
575 587 ignored=list_ignored):
576 588 if fn in dmap:
577 589 type_, mode, size, time, foo = dmap[fn]
578 590 else:
579 591 if (list_ignored or fn in files) and self._dirignore(fn):
580 592 if list_ignored:
581 593 iadd(fn)
582 594 elif list_unknown:
583 595 uadd(fn)
584 596 continue
585 597 if src == 'm':
586 598 nonexistent = True
587 599 if not st:
588 600 try:
589 601 st = lstat(_join(fn))
590 602 except OSError, inst:
591 603 if inst.errno not in (errno.ENOENT, errno.ENOTDIR):
592 604 raise
593 605 st = None
594 606 # We need to re-check that it is a valid file
595 607 if st and self._supported(fn, st.st_mode):
596 608 nonexistent = False
597 609 # XXX: what to do with file no longer present in the fs
598 610 # who are not removed in the dirstate ?
599 611 if nonexistent and type_ in "nma":
600 612 dadd(fn)
601 613 continue
602 614 # check the common case first
603 615 if type_ == 'n':
604 616 if not st:
605 617 st = lstat(_join(fn))
606 618 if (size >= 0 and
607 619 (size != st.st_size
608 620 or ((mode ^ st.st_mode) & 0100 and self._checkexec))
609 621 or size == -2
610 622 or fn in self._copymap):
611 623 madd(fn)
612 624 elif time != int(st.st_mtime):
613 625 ladd(fn)
614 626 elif list_clean:
615 627 cadd(fn)
616 628 elif type_ == 'm':
617 629 madd(fn)
618 630 elif type_ == 'a':
619 631 aadd(fn)
620 632 elif type_ == 'r':
621 633 radd(fn)
622 634
623 635 return (lookup, modified, added, removed, deleted, unknown, ignored,
624 636 clean)
@@ -1,72 +1,72 b''
1 1 creating base
2 2 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
3 3 creating branch a
4 4 creating branch b
5 5 we shouldn't have anything but n state here
6 n 644 2 bar
7 n 644 3 baz
8 n 644 3 foo
9 n 644 2 quux
6 n 0 -1 bar
7 n 0 -1 baz
8 n 0 -1 foo
9 n 0 -1 quux
10 10 merging
11 11 pulling from ../a
12 12 searching for changes
13 13 adding changesets
14 14 adding manifests
15 15 adding file changes
16 16 added 1 changesets with 2 changes to 2 files (+1 heads)
17 17 (run 'hg heads' to see heads, 'hg merge' to merge)
18 18 merging for foo
19 19 resolving manifests
20 20 getting bar
21 21 merging foo
22 22 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
23 23 (branch merge, don't forget to commit)
24 24 we shouldn't have anything but foo in merge state here
25 25 m 644 3 foo
26 26 main: we should have a merge here
27 27 rev offset length base linkrev nodeid p1 p2
28 28 0 0 77 0 0 c36078bec30d 000000000000 000000000000
29 29 1 77 73 1 1 182b283965f1 c36078bec30d 000000000000
30 30 2 150 71 2 2 a6aef98656b7 c36078bec30d 000000000000
31 31 3 221 72 3 3 0c2cc6fc80e2 182b283965f1 a6aef98656b7
32 32 log should show foo and quux changed
33 33 changeset: 3:0c2cc6fc80e2
34 34 tag: tip
35 35 parent: 1:182b283965f1
36 36 parent: 2:a6aef98656b7
37 37 user: test
38 38 date: Mon Jan 12 13:46:40 1970 +0000
39 39 files: foo quux
40 40 description:
41 41 merge
42 42
43 43
44 44 foo: we should have a merge here
45 45 rev offset length base linkrev nodeid p1 p2
46 46 0 0 3 0 0 b8e02f643373 000000000000 000000000000
47 47 1 3 4 1 1 2ffeddde1b65 b8e02f643373 000000000000
48 48 2 7 4 2 2 33d1fb69067a b8e02f643373 000000000000
49 49 3 11 4 3 3 aa27919ee430 2ffeddde1b65 33d1fb69067a
50 50 bar: we shouldn't have a merge here
51 51 rev offset length base linkrev nodeid p1 p2
52 52 0 0 3 0 0 b8e02f643373 000000000000 000000000000
53 53 1 3 4 1 2 33d1fb69067a b8e02f643373 000000000000
54 54 baz: we shouldn't have a merge here
55 55 rev offset length base linkrev nodeid p1 p2
56 56 0 0 3 0 0 b8e02f643373 000000000000 000000000000
57 57 1 3 4 1 1 2ffeddde1b65 b8e02f643373 000000000000
58 58 quux: we shouldn't have a merge here
59 59 rev offset length base linkrev nodeid p1 p2
60 60 0 0 3 0 0 b8e02f643373 000000000000 000000000000
61 61 1 3 5 1 3 6128c0f33108 b8e02f643373 000000000000
62 62 manifest entries should match tips of all files
63 63 33d1fb69067a0139622a3fa3b7ba1cdb1367972e 644 bar
64 64 2ffeddde1b65b4827f6746174a145474129fa2ce 644 baz
65 65 aa27919ee4303cfd575e1fb932dd64d75aa08be4 644 foo
66 66 6128c0f33108e8cfbb4e0824d13ae48b466d7280 644 quux
67 67 everything should be clean now
68 68 checking changesets
69 69 checking manifests
70 70 crosschecking files in changesets and manifests
71 71 checking files
72 72 4 files, 4 changesets, 10 total revisions
@@ -1,17 +1,17 b''
1 1 adding bar
2 2 adding foo
3 3 % state dump
4 4 a 0 -1 baz
5 n 644 0 foo
5 n 0 -1 foo
6 6 r 0 0 bar
7 7 % status
8 8 A baz
9 9 R bar
10 10 C foo
11 11 % state dump
12 12 n 666 -1 bar
13 13 n 666 -1 foo
14 14 % status
15 15 ! bar
16 16 ? baz
17 17 C foo
General Comments 0
You need to be logged in to leave comments. Login now