##// END OF EJS Templates
manifest: use absolute_import
Gregory Szorc -
r27502:2df7f5c0 default
parent child Browse files
Show More
@@ -1,1042 +1,1052 b''
1 1 # manifest.py - manifest revision class 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 from i18n import _
9 import mdiff, parsers, error, revlog, util
10 import array, struct
8 from __future__ import absolute_import
9
10 import array
11 import heapq
11 12 import os
12 import heapq
13 import struct
14
15 from .i18n import _
16 from . import (
17 error,
18 mdiff,
19 parsers,
20 revlog,
21 util,
22 )
13 23
14 24 propertycache = util.propertycache
15 25
16 26 def _parsev1(data):
17 27 # This method does a little bit of excessive-looking
18 28 # precondition checking. This is so that the behavior of this
19 29 # class exactly matches its C counterpart to try and help
20 30 # prevent surprise breakage for anyone that develops against
21 31 # the pure version.
22 32 if data and data[-1] != '\n':
23 33 raise ValueError('Manifest did not end in a newline.')
24 34 prev = None
25 35 for l in data.splitlines():
26 36 if prev is not None and prev > l:
27 37 raise ValueError('Manifest lines not in sorted order.')
28 38 prev = l
29 39 f, n = l.split('\0')
30 40 if len(n) > 40:
31 41 yield f, revlog.bin(n[:40]), n[40:]
32 42 else:
33 43 yield f, revlog.bin(n), ''
34 44
35 45 def _parsev2(data):
36 46 metadataend = data.find('\n')
37 47 # Just ignore metadata for now
38 48 pos = metadataend + 1
39 49 prevf = ''
40 50 while pos < len(data):
41 51 end = data.find('\n', pos + 1) # +1 to skip stem length byte
42 52 if end == -1:
43 53 raise ValueError('Manifest ended with incomplete file entry.')
44 54 stemlen = ord(data[pos])
45 55 items = data[pos + 1:end].split('\0')
46 56 f = prevf[:stemlen] + items[0]
47 57 if prevf > f:
48 58 raise ValueError('Manifest entries not in sorted order.')
49 59 fl = items[1]
50 60 # Just ignore metadata (items[2:] for now)
51 61 n = data[end + 1:end + 21]
52 62 yield f, n, fl
53 63 pos = end + 22
54 64 prevf = f
55 65
56 66 def _parse(data):
57 67 """Generates (path, node, flags) tuples from a manifest text"""
58 68 if data.startswith('\0'):
59 69 return iter(_parsev2(data))
60 70 else:
61 71 return iter(_parsev1(data))
62 72
63 73 def _text(it, usemanifestv2):
64 74 """Given an iterator over (path, node, flags) tuples, returns a manifest
65 75 text"""
66 76 if usemanifestv2:
67 77 return _textv2(it)
68 78 else:
69 79 return _textv1(it)
70 80
71 81 def _textv1(it):
72 82 files = []
73 83 lines = []
74 84 _hex = revlog.hex
75 85 for f, n, fl in it:
76 86 files.append(f)
77 87 # if this is changed to support newlines in filenames,
78 88 # be sure to check the templates/ dir again (especially *-raw.tmpl)
79 89 lines.append("%s\0%s%s\n" % (f, _hex(n), fl))
80 90
81 91 _checkforbidden(files)
82 92 return ''.join(lines)
83 93
84 94 def _textv2(it):
85 95 files = []
86 96 lines = ['\0\n']
87 97 prevf = ''
88 98 for f, n, fl in it:
89 99 files.append(f)
90 100 stem = os.path.commonprefix([prevf, f])
91 101 stemlen = min(len(stem), 255)
92 102 lines.append("%c%s\0%s\n%s\n" % (stemlen, f[stemlen:], fl, n))
93 103 prevf = f
94 104 _checkforbidden(files)
95 105 return ''.join(lines)
96 106
97 107 class _lazymanifest(dict):
98 108 """This is the pure implementation of lazymanifest.
99 109
100 110 It has not been optimized *at all* and is not lazy.
101 111 """
102 112
103 113 def __init__(self, data):
104 114 dict.__init__(self)
105 115 for f, n, fl in _parse(data):
106 116 self[f] = n, fl
107 117
108 118 def __setitem__(self, k, v):
109 119 node, flag = v
110 120 assert node is not None
111 121 if len(node) > 21:
112 122 node = node[:21] # match c implementation behavior
113 123 dict.__setitem__(self, k, (node, flag))
114 124
115 125 def __iter__(self):
116 126 return iter(sorted(dict.keys(self)))
117 127
118 128 def iterkeys(self):
119 129 return iter(sorted(dict.keys(self)))
120 130
121 131 def iterentries(self):
122 132 return ((f, e[0], e[1]) for f, e in sorted(self.iteritems()))
123 133
124 134 def copy(self):
125 135 c = _lazymanifest('')
126 136 c.update(self)
127 137 return c
128 138
129 139 def diff(self, m2, clean=False):
130 140 '''Finds changes between the current manifest and m2.'''
131 141 diff = {}
132 142
133 143 for fn, e1 in self.iteritems():
134 144 if fn not in m2:
135 145 diff[fn] = e1, (None, '')
136 146 else:
137 147 e2 = m2[fn]
138 148 if e1 != e2:
139 149 diff[fn] = e1, e2
140 150 elif clean:
141 151 diff[fn] = None
142 152
143 153 for fn, e2 in m2.iteritems():
144 154 if fn not in self:
145 155 diff[fn] = (None, ''), e2
146 156
147 157 return diff
148 158
149 159 def filtercopy(self, filterfn):
150 160 c = _lazymanifest('')
151 161 for f, n, fl in self.iterentries():
152 162 if filterfn(f):
153 163 c[f] = n, fl
154 164 return c
155 165
156 166 def text(self):
157 167 """Get the full data of this manifest as a bytestring."""
158 168 return _textv1(self.iterentries())
159 169
160 170 try:
161 171 _lazymanifest = parsers.lazymanifest
162 172 except AttributeError:
163 173 pass
164 174
165 175 class manifestdict(object):
166 176 def __init__(self, data=''):
167 177 if data.startswith('\0'):
168 178 #_lazymanifest can not parse v2
169 179 self._lm = _lazymanifest('')
170 180 for f, n, fl in _parsev2(data):
171 181 self._lm[f] = n, fl
172 182 else:
173 183 self._lm = _lazymanifest(data)
174 184
175 185 def __getitem__(self, key):
176 186 return self._lm[key][0]
177 187
178 188 def find(self, key):
179 189 return self._lm[key]
180 190
181 191 def __len__(self):
182 192 return len(self._lm)
183 193
184 194 def __setitem__(self, key, node):
185 195 self._lm[key] = node, self.flags(key, '')
186 196
187 197 def __contains__(self, key):
188 198 return key in self._lm
189 199
190 200 def __delitem__(self, key):
191 201 del self._lm[key]
192 202
193 203 def __iter__(self):
194 204 return self._lm.__iter__()
195 205
196 206 def iterkeys(self):
197 207 return self._lm.iterkeys()
198 208
199 209 def keys(self):
200 210 return list(self.iterkeys())
201 211
202 212 def filesnotin(self, m2):
203 213 '''Set of files in this manifest that are not in the other'''
204 214 files = set(self)
205 215 files.difference_update(m2)
206 216 return files
207 217
208 218 @propertycache
209 219 def _dirs(self):
210 220 return util.dirs(self)
211 221
212 222 def dirs(self):
213 223 return self._dirs
214 224
215 225 def hasdir(self, dir):
216 226 return dir in self._dirs
217 227
218 228 def _filesfastpath(self, match):
219 229 '''Checks whether we can correctly and quickly iterate over matcher
220 230 files instead of over manifest files.'''
221 231 files = match.files()
222 232 return (len(files) < 100 and (match.isexact() or
223 233 (match.prefix() and all(fn in self for fn in files))))
224 234
225 235 def walk(self, match):
226 236 '''Generates matching file names.
227 237
228 238 Equivalent to manifest.matches(match).iterkeys(), but without creating
229 239 an entirely new manifest.
230 240
231 241 It also reports nonexistent files by marking them bad with match.bad().
232 242 '''
233 243 if match.always():
234 244 for f in iter(self):
235 245 yield f
236 246 return
237 247
238 248 fset = set(match.files())
239 249
240 250 # avoid the entire walk if we're only looking for specific files
241 251 if self._filesfastpath(match):
242 252 for fn in sorted(fset):
243 253 yield fn
244 254 return
245 255
246 256 for fn in self:
247 257 if fn in fset:
248 258 # specified pattern is the exact name
249 259 fset.remove(fn)
250 260 if match(fn):
251 261 yield fn
252 262
253 263 # for dirstate.walk, files=['.'] means "walk the whole tree".
254 264 # follow that here, too
255 265 fset.discard('.')
256 266
257 267 for fn in sorted(fset):
258 268 if not self.hasdir(fn):
259 269 match.bad(fn, None)
260 270
261 271 def matches(self, match):
262 272 '''generate a new manifest filtered by the match argument'''
263 273 if match.always():
264 274 return self.copy()
265 275
266 276 if self._filesfastpath(match):
267 277 m = manifestdict()
268 278 lm = self._lm
269 279 for fn in match.files():
270 280 if fn in lm:
271 281 m._lm[fn] = lm[fn]
272 282 return m
273 283
274 284 m = manifestdict()
275 285 m._lm = self._lm.filtercopy(match)
276 286 return m
277 287
278 288 def diff(self, m2, clean=False):
279 289 '''Finds changes between the current manifest and m2.
280 290
281 291 Args:
282 292 m2: the manifest to which this manifest should be compared.
283 293 clean: if true, include files unchanged between these manifests
284 294 with a None value in the returned dictionary.
285 295
286 296 The result is returned as a dict with filename as key and
287 297 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
288 298 nodeid in the current/other manifest and fl1/fl2 is the flag
289 299 in the current/other manifest. Where the file does not exist,
290 300 the nodeid will be None and the flags will be the empty
291 301 string.
292 302 '''
293 303 return self._lm.diff(m2._lm, clean)
294 304
295 305 def setflag(self, key, flag):
296 306 self._lm[key] = self[key], flag
297 307
298 308 def get(self, key, default=None):
299 309 try:
300 310 return self._lm[key][0]
301 311 except KeyError:
302 312 return default
303 313
304 314 def flags(self, key, default=''):
305 315 try:
306 316 return self._lm[key][1]
307 317 except KeyError:
308 318 return default
309 319
310 320 def copy(self):
311 321 c = manifestdict()
312 322 c._lm = self._lm.copy()
313 323 return c
314 324
315 325 def iteritems(self):
316 326 return (x[:2] for x in self._lm.iterentries())
317 327
318 328 def text(self, usemanifestv2=False):
319 329 if usemanifestv2:
320 330 return _textv2(self._lm.iterentries())
321 331 else:
322 332 # use (probably) native version for v1
323 333 return self._lm.text()
324 334
325 335 def fastdelta(self, base, changes):
326 336 """Given a base manifest text as an array.array and a list of changes
327 337 relative to that text, compute a delta that can be used by revlog.
328 338 """
329 339 delta = []
330 340 dstart = None
331 341 dend = None
332 342 dline = [""]
333 343 start = 0
334 344 # zero copy representation of base as a buffer
335 345 addbuf = util.buffer(base)
336 346
337 347 changes = list(changes)
338 348 if len(changes) < 1000:
339 349 # start with a readonly loop that finds the offset of
340 350 # each line and creates the deltas
341 351 for f, todelete in changes:
342 352 # bs will either be the index of the item or the insert point
343 353 start, end = _msearch(addbuf, f, start)
344 354 if not todelete:
345 355 h, fl = self._lm[f]
346 356 l = "%s\0%s%s\n" % (f, revlog.hex(h), fl)
347 357 else:
348 358 if start == end:
349 359 # item we want to delete was not found, error out
350 360 raise AssertionError(
351 361 _("failed to remove %s from manifest") % f)
352 362 l = ""
353 363 if dstart is not None and dstart <= start and dend >= start:
354 364 if dend < end:
355 365 dend = end
356 366 if l:
357 367 dline.append(l)
358 368 else:
359 369 if dstart is not None:
360 370 delta.append([dstart, dend, "".join(dline)])
361 371 dstart = start
362 372 dend = end
363 373 dline = [l]
364 374
365 375 if dstart is not None:
366 376 delta.append([dstart, dend, "".join(dline)])
367 377 # apply the delta to the base, and get a delta for addrevision
368 378 deltatext, arraytext = _addlistdelta(base, delta)
369 379 else:
370 380 # For large changes, it's much cheaper to just build the text and
371 381 # diff it.
372 382 arraytext = array.array('c', self.text())
373 383 deltatext = mdiff.textdiff(base, arraytext)
374 384
375 385 return arraytext, deltatext
376 386
377 387 def _msearch(m, s, lo=0, hi=None):
378 388 '''return a tuple (start, end) that says where to find s within m.
379 389
380 390 If the string is found m[start:end] are the line containing
381 391 that string. If start == end the string was not found and
382 392 they indicate the proper sorted insertion point.
383 393
384 394 m should be a buffer or a string
385 395 s is a string'''
386 396 def advance(i, c):
387 397 while i < lenm and m[i] != c:
388 398 i += 1
389 399 return i
390 400 if not s:
391 401 return (lo, lo)
392 402 lenm = len(m)
393 403 if not hi:
394 404 hi = lenm
395 405 while lo < hi:
396 406 mid = (lo + hi) // 2
397 407 start = mid
398 408 while start > 0 and m[start - 1] != '\n':
399 409 start -= 1
400 410 end = advance(start, '\0')
401 411 if m[start:end] < s:
402 412 # we know that after the null there are 40 bytes of sha1
403 413 # this translates to the bisect lo = mid + 1
404 414 lo = advance(end + 40, '\n') + 1
405 415 else:
406 416 # this translates to the bisect hi = mid
407 417 hi = start
408 418 end = advance(lo, '\0')
409 419 found = m[lo:end]
410 420 if s == found:
411 421 # we know that after the null there are 40 bytes of sha1
412 422 end = advance(end + 40, '\n')
413 423 return (lo, end + 1)
414 424 else:
415 425 return (lo, lo)
416 426
417 427 def _checkforbidden(l):
418 428 """Check filenames for illegal characters."""
419 429 for f in l:
420 430 if '\n' in f or '\r' in f:
421 431 raise error.RevlogError(
422 432 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
423 433
424 434
425 435 # apply the changes collected during the bisect loop to our addlist
426 436 # return a delta suitable for addrevision
427 437 def _addlistdelta(addlist, x):
428 438 # for large addlist arrays, building a new array is cheaper
429 439 # than repeatedly modifying the existing one
430 440 currentposition = 0
431 441 newaddlist = array.array('c')
432 442
433 443 for start, end, content in x:
434 444 newaddlist += addlist[currentposition:start]
435 445 if content:
436 446 newaddlist += array.array('c', content)
437 447
438 448 currentposition = end
439 449
440 450 newaddlist += addlist[currentposition:]
441 451
442 452 deltatext = "".join(struct.pack(">lll", start, end, len(content))
443 453 + content for start, end, content in x)
444 454 return deltatext, newaddlist
445 455
446 456 def _splittopdir(f):
447 457 if '/' in f:
448 458 dir, subpath = f.split('/', 1)
449 459 return dir + '/', subpath
450 460 else:
451 461 return '', f
452 462
453 463 _noop = lambda s: None
454 464
455 465 class treemanifest(object):
456 466 def __init__(self, dir='', text=''):
457 467 self._dir = dir
458 468 self._node = revlog.nullid
459 469 self._loadfunc = _noop
460 470 self._copyfunc = _noop
461 471 self._dirty = False
462 472 self._dirs = {}
463 473 # Using _lazymanifest here is a little slower than plain old dicts
464 474 self._files = {}
465 475 self._flags = {}
466 476 if text:
467 477 def readsubtree(subdir, subm):
468 478 raise AssertionError('treemanifest constructor only accepts '
469 479 'flat manifests')
470 480 self.parse(text, readsubtree)
471 481 self._dirty = True # Mark flat manifest dirty after parsing
472 482
473 483 def _subpath(self, path):
474 484 return self._dir + path
475 485
476 486 def __len__(self):
477 487 self._load()
478 488 size = len(self._files)
479 489 for m in self._dirs.values():
480 490 size += m.__len__()
481 491 return size
482 492
483 493 def _isempty(self):
484 494 self._load() # for consistency; already loaded by all callers
485 495 return (not self._files and (not self._dirs or
486 496 all(m._isempty() for m in self._dirs.values())))
487 497
488 498 def __repr__(self):
489 499 return ('<treemanifest dir=%s, node=%s, loaded=%s, dirty=%s at 0x%x>' %
490 500 (self._dir, revlog.hex(self._node),
491 501 bool(self._loadfunc is _noop),
492 502 self._dirty, id(self)))
493 503
494 504 def dir(self):
495 505 '''The directory that this tree manifest represents, including a
496 506 trailing '/'. Empty string for the repo root directory.'''
497 507 return self._dir
498 508
499 509 def node(self):
500 510 '''This node of this instance. nullid for unsaved instances. Should
501 511 be updated when the instance is read or written from a revlog.
502 512 '''
503 513 assert not self._dirty
504 514 return self._node
505 515
506 516 def setnode(self, node):
507 517 self._node = node
508 518 self._dirty = False
509 519
510 520 def iteritems(self):
511 521 self._load()
512 522 for p, n in sorted(self._dirs.items() + self._files.items()):
513 523 if p in self._files:
514 524 yield self._subpath(p), n
515 525 else:
516 526 for f, sn in n.iteritems():
517 527 yield f, sn
518 528
519 529 def iterkeys(self):
520 530 self._load()
521 531 for p in sorted(self._dirs.keys() + self._files.keys()):
522 532 if p in self._files:
523 533 yield self._subpath(p)
524 534 else:
525 535 for f in self._dirs[p].iterkeys():
526 536 yield f
527 537
528 538 def keys(self):
529 539 return list(self.iterkeys())
530 540
531 541 def __iter__(self):
532 542 return self.iterkeys()
533 543
534 544 def __contains__(self, f):
535 545 if f is None:
536 546 return False
537 547 self._load()
538 548 dir, subpath = _splittopdir(f)
539 549 if dir:
540 550 if dir not in self._dirs:
541 551 return False
542 552 return self._dirs[dir].__contains__(subpath)
543 553 else:
544 554 return f in self._files
545 555
546 556 def get(self, f, default=None):
547 557 self._load()
548 558 dir, subpath = _splittopdir(f)
549 559 if dir:
550 560 if dir not in self._dirs:
551 561 return default
552 562 return self._dirs[dir].get(subpath, default)
553 563 else:
554 564 return self._files.get(f, default)
555 565
556 566 def __getitem__(self, f):
557 567 self._load()
558 568 dir, subpath = _splittopdir(f)
559 569 if dir:
560 570 return self._dirs[dir].__getitem__(subpath)
561 571 else:
562 572 return self._files[f]
563 573
564 574 def flags(self, f):
565 575 self._load()
566 576 dir, subpath = _splittopdir(f)
567 577 if dir:
568 578 if dir not in self._dirs:
569 579 return ''
570 580 return self._dirs[dir].flags(subpath)
571 581 else:
572 582 if f in self._dirs:
573 583 return ''
574 584 return self._flags.get(f, '')
575 585
576 586 def find(self, f):
577 587 self._load()
578 588 dir, subpath = _splittopdir(f)
579 589 if dir:
580 590 return self._dirs[dir].find(subpath)
581 591 else:
582 592 return self._files[f], self._flags.get(f, '')
583 593
584 594 def __delitem__(self, f):
585 595 self._load()
586 596 dir, subpath = _splittopdir(f)
587 597 if dir:
588 598 self._dirs[dir].__delitem__(subpath)
589 599 # If the directory is now empty, remove it
590 600 if self._dirs[dir]._isempty():
591 601 del self._dirs[dir]
592 602 else:
593 603 del self._files[f]
594 604 if f in self._flags:
595 605 del self._flags[f]
596 606 self._dirty = True
597 607
598 608 def __setitem__(self, f, n):
599 609 assert n is not None
600 610 self._load()
601 611 dir, subpath = _splittopdir(f)
602 612 if dir:
603 613 if dir not in self._dirs:
604 614 self._dirs[dir] = treemanifest(self._subpath(dir))
605 615 self._dirs[dir].__setitem__(subpath, n)
606 616 else:
607 617 self._files[f] = n[:21] # to match manifestdict's behavior
608 618 self._dirty = True
609 619
610 620 def _load(self):
611 621 if self._loadfunc is not _noop:
612 622 lf, self._loadfunc = self._loadfunc, _noop
613 623 lf(self)
614 624 elif self._copyfunc is not _noop:
615 625 cf, self._copyfunc = self._copyfunc, _noop
616 626 cf(self)
617 627
618 628 def setflag(self, f, flags):
619 629 """Set the flags (symlink, executable) for path f."""
620 630 assert 't' not in flags
621 631 self._load()
622 632 dir, subpath = _splittopdir(f)
623 633 if dir:
624 634 if dir not in self._dirs:
625 635 self._dirs[dir] = treemanifest(self._subpath(dir))
626 636 self._dirs[dir].setflag(subpath, flags)
627 637 else:
628 638 self._flags[f] = flags
629 639 self._dirty = True
630 640
631 641 def copy(self):
632 642 copy = treemanifest(self._dir)
633 643 copy._node = self._node
634 644 copy._dirty = self._dirty
635 645 if self._copyfunc is _noop:
636 646 def _copyfunc(s):
637 647 self._load()
638 648 for d in self._dirs:
639 649 s._dirs[d] = self._dirs[d].copy()
640 650 s._files = dict.copy(self._files)
641 651 s._flags = dict.copy(self._flags)
642 652 if self._loadfunc is _noop:
643 653 _copyfunc(copy)
644 654 else:
645 655 copy._copyfunc = _copyfunc
646 656 else:
647 657 copy._copyfunc = self._copyfunc
648 658 return copy
649 659
650 660 def filesnotin(self, m2):
651 661 '''Set of files in this manifest that are not in the other'''
652 662 files = set()
653 663 def _filesnotin(t1, t2):
654 664 if t1._node == t2._node and not t1._dirty and not t2._dirty:
655 665 return
656 666 t1._load()
657 667 t2._load()
658 668 for d, m1 in t1._dirs.iteritems():
659 669 if d in t2._dirs:
660 670 m2 = t2._dirs[d]
661 671 _filesnotin(m1, m2)
662 672 else:
663 673 files.update(m1.iterkeys())
664 674
665 675 for fn in t1._files.iterkeys():
666 676 if fn not in t2._files:
667 677 files.add(t1._subpath(fn))
668 678
669 679 _filesnotin(self, m2)
670 680 return files
671 681
672 682 @propertycache
673 683 def _alldirs(self):
674 684 return util.dirs(self)
675 685
676 686 def dirs(self):
677 687 return self._alldirs
678 688
679 689 def hasdir(self, dir):
680 690 self._load()
681 691 topdir, subdir = _splittopdir(dir)
682 692 if topdir:
683 693 if topdir in self._dirs:
684 694 return self._dirs[topdir].hasdir(subdir)
685 695 return False
686 696 return (dir + '/') in self._dirs
687 697
688 698 def walk(self, match):
689 699 '''Generates matching file names.
690 700
691 701 Equivalent to manifest.matches(match).iterkeys(), but without creating
692 702 an entirely new manifest.
693 703
694 704 It also reports nonexistent files by marking them bad with match.bad().
695 705 '''
696 706 if match.always():
697 707 for f in iter(self):
698 708 yield f
699 709 return
700 710
701 711 fset = set(match.files())
702 712
703 713 for fn in self._walk(match):
704 714 if fn in fset:
705 715 # specified pattern is the exact name
706 716 fset.remove(fn)
707 717 yield fn
708 718
709 719 # for dirstate.walk, files=['.'] means "walk the whole tree".
710 720 # follow that here, too
711 721 fset.discard('.')
712 722
713 723 for fn in sorted(fset):
714 724 if not self.hasdir(fn):
715 725 match.bad(fn, None)
716 726
717 727 def _walk(self, match):
718 728 '''Recursively generates matching file names for walk().'''
719 729 if not match.visitdir(self._dir[:-1] or '.'):
720 730 return
721 731
722 732 # yield this dir's files and walk its submanifests
723 733 self._load()
724 734 for p in sorted(self._dirs.keys() + self._files.keys()):
725 735 if p in self._files:
726 736 fullp = self._subpath(p)
727 737 if match(fullp):
728 738 yield fullp
729 739 else:
730 740 for f in self._dirs[p]._walk(match):
731 741 yield f
732 742
733 743 def matches(self, match):
734 744 '''generate a new manifest filtered by the match argument'''
735 745 if match.always():
736 746 return self.copy()
737 747
738 748 return self._matches(match)
739 749
740 750 def _matches(self, match):
741 751 '''recursively generate a new manifest filtered by the match argument.
742 752 '''
743 753
744 754 visit = match.visitdir(self._dir[:-1] or '.')
745 755 if visit == 'all':
746 756 return self.copy()
747 757 ret = treemanifest(self._dir)
748 758 if not visit:
749 759 return ret
750 760
751 761 self._load()
752 762 for fn in self._files:
753 763 fullp = self._subpath(fn)
754 764 if not match(fullp):
755 765 continue
756 766 ret._files[fn] = self._files[fn]
757 767 if fn in self._flags:
758 768 ret._flags[fn] = self._flags[fn]
759 769
760 770 for dir, subm in self._dirs.iteritems():
761 771 m = subm._matches(match)
762 772 if not m._isempty():
763 773 ret._dirs[dir] = m
764 774
765 775 if not ret._isempty():
766 776 ret._dirty = True
767 777 return ret
768 778
769 779 def diff(self, m2, clean=False):
770 780 '''Finds changes between the current manifest and m2.
771 781
772 782 Args:
773 783 m2: the manifest to which this manifest should be compared.
774 784 clean: if true, include files unchanged between these manifests
775 785 with a None value in the returned dictionary.
776 786
777 787 The result is returned as a dict with filename as key and
778 788 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
779 789 nodeid in the current/other manifest and fl1/fl2 is the flag
780 790 in the current/other manifest. Where the file does not exist,
781 791 the nodeid will be None and the flags will be the empty
782 792 string.
783 793 '''
784 794 result = {}
785 795 emptytree = treemanifest()
786 796 def _diff(t1, t2):
787 797 if t1._node == t2._node and not t1._dirty and not t2._dirty:
788 798 return
789 799 t1._load()
790 800 t2._load()
791 801 for d, m1 in t1._dirs.iteritems():
792 802 m2 = t2._dirs.get(d, emptytree)
793 803 _diff(m1, m2)
794 804
795 805 for d, m2 in t2._dirs.iteritems():
796 806 if d not in t1._dirs:
797 807 _diff(emptytree, m2)
798 808
799 809 for fn, n1 in t1._files.iteritems():
800 810 fl1 = t1._flags.get(fn, '')
801 811 n2 = t2._files.get(fn, None)
802 812 fl2 = t2._flags.get(fn, '')
803 813 if n1 != n2 or fl1 != fl2:
804 814 result[t1._subpath(fn)] = ((n1, fl1), (n2, fl2))
805 815 elif clean:
806 816 result[t1._subpath(fn)] = None
807 817
808 818 for fn, n2 in t2._files.iteritems():
809 819 if fn not in t1._files:
810 820 fl2 = t2._flags.get(fn, '')
811 821 result[t2._subpath(fn)] = ((None, ''), (n2, fl2))
812 822
813 823 _diff(self, m2)
814 824 return result
815 825
816 826 def unmodifiedsince(self, m2):
817 827 return not self._dirty and not m2._dirty and self._node == m2._node
818 828
819 829 def parse(self, text, readsubtree):
820 830 for f, n, fl in _parse(text):
821 831 if fl == 't':
822 832 f = f + '/'
823 833 self._dirs[f] = readsubtree(self._subpath(f), n)
824 834 elif '/' in f:
825 835 # This is a flat manifest, so use __setitem__ and setflag rather
826 836 # than assigning directly to _files and _flags, so we can
827 837 # assign a path in a subdirectory, and to mark dirty (compared
828 838 # to nullid).
829 839 self[f] = n
830 840 if fl:
831 841 self.setflag(f, fl)
832 842 else:
833 843 # Assigning to _files and _flags avoids marking as dirty,
834 844 # and should be a little faster.
835 845 self._files[f] = n
836 846 if fl:
837 847 self._flags[f] = fl
838 848
839 849 def text(self, usemanifestv2=False):
840 850 """Get the full data of this manifest as a bytestring."""
841 851 self._load()
842 852 flags = self.flags
843 853 return _text(((f, self[f], flags(f)) for f in self.keys()),
844 854 usemanifestv2)
845 855
846 856 def dirtext(self, usemanifestv2=False):
847 857 """Get the full data of this directory as a bytestring. Make sure that
848 858 any submanifests have been written first, so their nodeids are correct.
849 859 """
850 860 self._load()
851 861 flags = self.flags
852 862 dirs = [(d[:-1], self._dirs[d]._node, 't') for d in self._dirs]
853 863 files = [(f, self._files[f], flags(f)) for f in self._files]
854 864 return _text(sorted(dirs + files), usemanifestv2)
855 865
856 866 def read(self, gettext, readsubtree):
857 867 def _load_for_read(s):
858 868 s.parse(gettext(), readsubtree)
859 869 s._dirty = False
860 870 self._loadfunc = _load_for_read
861 871
862 872 def writesubtrees(self, m1, m2, writesubtree):
863 873 self._load() # for consistency; should never have any effect here
864 874 emptytree = treemanifest()
865 875 for d, subm in self._dirs.iteritems():
866 876 subp1 = m1._dirs.get(d, emptytree)._node
867 877 subp2 = m2._dirs.get(d, emptytree)._node
868 878 if subp1 == revlog.nullid:
869 879 subp1, subp2 = subp2, subp1
870 880 writesubtree(subm, subp1, subp2)
871 881
872 882 class manifest(revlog.revlog):
873 883 def __init__(self, opener, dir='', dirlogcache=None):
874 884 '''The 'dir' and 'dirlogcache' arguments are for internal use by
875 885 manifest.manifest only. External users should create a root manifest
876 886 log with manifest.manifest(opener) and call dirlog() on it.
877 887 '''
878 888 # During normal operations, we expect to deal with not more than four
879 889 # revs at a time (such as during commit --amend). When rebasing large
880 890 # stacks of commits, the number can go up, hence the config knob below.
881 891 cachesize = 4
882 892 usetreemanifest = False
883 893 usemanifestv2 = False
884 894 opts = getattr(opener, 'options', None)
885 895 if opts is not None:
886 896 cachesize = opts.get('manifestcachesize', cachesize)
887 897 usetreemanifest = opts.get('treemanifest', usetreemanifest)
888 898 usemanifestv2 = opts.get('manifestv2', usemanifestv2)
889 899 self._mancache = util.lrucachedict(cachesize)
890 900 self._treeinmem = usetreemanifest
891 901 self._treeondisk = usetreemanifest
892 902 self._usemanifestv2 = usemanifestv2
893 903 indexfile = "00manifest.i"
894 904 if dir:
895 905 assert self._treeondisk
896 906 if not dir.endswith('/'):
897 907 dir = dir + '/'
898 908 indexfile = "meta/" + dir + "00manifest.i"
899 909 revlog.revlog.__init__(self, opener, indexfile)
900 910 self._dir = dir
901 911 # The dirlogcache is kept on the root manifest log
902 912 if dir:
903 913 self._dirlogcache = dirlogcache
904 914 else:
905 915 self._dirlogcache = {'': self}
906 916
907 917 def _newmanifest(self, data=''):
908 918 if self._treeinmem:
909 919 return treemanifest(self._dir, data)
910 920 return manifestdict(data)
911 921
912 922 def dirlog(self, dir):
913 923 assert self._treeondisk
914 924 if dir not in self._dirlogcache:
915 925 self._dirlogcache[dir] = manifest(self.opener, dir,
916 926 self._dirlogcache)
917 927 return self._dirlogcache[dir]
918 928
919 929 def _slowreaddelta(self, node):
920 930 r0 = self.deltaparent(self.rev(node))
921 931 m0 = self.read(self.node(r0))
922 932 m1 = self.read(node)
923 933 md = self._newmanifest()
924 934 for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems():
925 935 if n1:
926 936 md[f] = n1
927 937 if fl1:
928 938 md.setflag(f, fl1)
929 939 return md
930 940
931 941 def readdelta(self, node):
932 942 if self._usemanifestv2 or self._treeondisk:
933 943 return self._slowreaddelta(node)
934 944 r = self.rev(node)
935 945 d = mdiff.patchtext(self.revdiff(self.deltaparent(r), r))
936 946 return self._newmanifest(d)
937 947
938 948 def readfast(self, node):
939 949 '''use the faster of readdelta or read
940 950
941 951 This will return a manifest which is either only the files
942 952 added/modified relative to p1, or all files in the
943 953 manifest. Which one is returned depends on the codepath used
944 954 to retrieve the data.
945 955 '''
946 956 r = self.rev(node)
947 957 deltaparent = self.deltaparent(r)
948 958 if deltaparent != revlog.nullrev and deltaparent in self.parentrevs(r):
949 959 return self.readdelta(node)
950 960 return self.read(node)
951 961
952 962 def read(self, node):
953 963 if node == revlog.nullid:
954 964 return self._newmanifest() # don't upset local cache
955 965 if node in self._mancache:
956 966 return self._mancache[node][0]
957 967 if self._treeondisk:
958 968 def gettext():
959 969 return self.revision(node)
960 970 def readsubtree(dir, subm):
961 971 return self.dirlog(dir).read(subm)
962 972 m = self._newmanifest()
963 973 m.read(gettext, readsubtree)
964 974 m.setnode(node)
965 975 arraytext = None
966 976 else:
967 977 text = self.revision(node)
968 978 m = self._newmanifest(text)
969 979 arraytext = array.array('c', text)
970 980 self._mancache[node] = (m, arraytext)
971 981 return m
972 982
973 983 def find(self, node, f):
974 984 '''look up entry for a single file efficiently.
975 985 return (node, flags) pair if found, (None, None) if not.'''
976 986 m = self.read(node)
977 987 try:
978 988 return m.find(f)
979 989 except KeyError:
980 990 return None, None
981 991
982 992 def add(self, m, transaction, link, p1, p2, added, removed):
983 993 if (p1 in self._mancache and not self._treeinmem
984 994 and not self._usemanifestv2):
985 995 # If our first parent is in the manifest cache, we can
986 996 # compute a delta here using properties we know about the
987 997 # manifest up-front, which may save time later for the
988 998 # revlog layer.
989 999
990 1000 _checkforbidden(added)
991 1001 # combine the changed lists into one sorted iterator
992 1002 work = heapq.merge([(x, False) for x in added],
993 1003 [(x, True) for x in removed])
994 1004
995 1005 arraytext, deltatext = m.fastdelta(self._mancache[p1][1], work)
996 1006 cachedelta = self.rev(p1), deltatext
997 1007 text = util.buffer(arraytext)
998 1008 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
999 1009 else:
1000 1010 # The first parent manifest isn't already loaded, so we'll
1001 1011 # just encode a fulltext of the manifest and pass that
1002 1012 # through to the revlog layer, and let it handle the delta
1003 1013 # process.
1004 1014 if self._treeondisk:
1005 1015 m1 = self.read(p1)
1006 1016 m2 = self.read(p2)
1007 1017 n = self._addtree(m, transaction, link, m1, m2)
1008 1018 arraytext = None
1009 1019 else:
1010 1020 text = m.text(self._usemanifestv2)
1011 1021 n = self.addrevision(text, transaction, link, p1, p2)
1012 1022 arraytext = array.array('c', text)
1013 1023
1014 1024 self._mancache[n] = (m, arraytext)
1015 1025
1016 1026 return n
1017 1027
1018 1028 def _addtree(self, m, transaction, link, m1, m2):
1019 1029 # If the manifest is unchanged compared to one parent,
1020 1030 # don't write a new revision
1021 1031 if m.unmodifiedsince(m1) or m.unmodifiedsince(m2):
1022 1032 return m.node()
1023 1033 def writesubtree(subm, subp1, subp2):
1024 1034 sublog = self.dirlog(subm.dir())
1025 1035 sublog.add(subm, transaction, link, subp1, subp2, None, None)
1026 1036 m.writesubtrees(m1, m2, writesubtree)
1027 1037 text = m.dirtext(self._usemanifestv2)
1028 1038 # Double-check whether contents are unchanged to one parent
1029 1039 if text == m1.dirtext(self._usemanifestv2):
1030 1040 n = m1.node()
1031 1041 elif text == m2.dirtext(self._usemanifestv2):
1032 1042 n = m2.node()
1033 1043 else:
1034 1044 n = self.addrevision(text, transaction, link, m1.node(), m2.node())
1035 1045 # Save nodeid so parent manifest can calculate its nodeid
1036 1046 m.setnode(n)
1037 1047 return n
1038 1048
1039 1049 def clearcaches(self):
1040 1050 super(manifest, self).clearcaches()
1041 1051 self._mancache.clear()
1042 1052 self._dirlogcache = {'': self}
@@ -1,198 +1,197 b''
1 1 #require test-repo
2 2
3 3 $ cd "$TESTDIR"/..
4 4
5 5 $ hg files 'set:(**.py)' | sed 's|\\|/|g' | xargs python contrib/check-py3-compat.py
6 6 contrib/casesmash.py not using absolute_import
7 7 contrib/check-code.py not using absolute_import
8 8 contrib/check-code.py requires print_function
9 9 contrib/check-config.py not using absolute_import
10 10 contrib/check-config.py requires print_function
11 11 contrib/debugcmdserver.py not using absolute_import
12 12 contrib/debugcmdserver.py requires print_function
13 13 contrib/debugshell.py not using absolute_import
14 14 contrib/fixpax.py not using absolute_import
15 15 contrib/fixpax.py requires print_function
16 16 contrib/hgclient.py not using absolute_import
17 17 contrib/hgclient.py requires print_function
18 18 contrib/hgfixes/fix_bytes.py not using absolute_import
19 19 contrib/hgfixes/fix_bytesmod.py not using absolute_import
20 20 contrib/hgfixes/fix_leftover_imports.py not using absolute_import
21 21 contrib/import-checker.py not using absolute_import
22 22 contrib/import-checker.py requires print_function
23 23 contrib/memory.py not using absolute_import
24 24 contrib/perf.py not using absolute_import
25 25 contrib/python-hook-examples.py not using absolute_import
26 26 contrib/revsetbenchmarks.py not using absolute_import
27 27 contrib/revsetbenchmarks.py requires print_function
28 28 contrib/showstack.py not using absolute_import
29 29 contrib/synthrepo.py not using absolute_import
30 30 contrib/win32/hgwebdir_wsgi.py not using absolute_import
31 31 doc/check-seclevel.py not using absolute_import
32 32 doc/gendoc.py not using absolute_import
33 33 doc/hgmanpage.py not using absolute_import
34 34 hgext/__init__.py not using absolute_import
35 35 hgext/acl.py not using absolute_import
36 36 hgext/blackbox.py not using absolute_import
37 37 hgext/bugzilla.py not using absolute_import
38 38 hgext/censor.py not using absolute_import
39 39 hgext/children.py not using absolute_import
40 40 hgext/churn.py not using absolute_import
41 41 hgext/clonebundles.py not using absolute_import
42 42 hgext/color.py not using absolute_import
43 43 hgext/convert/__init__.py not using absolute_import
44 44 hgext/convert/bzr.py not using absolute_import
45 45 hgext/convert/common.py not using absolute_import
46 46 hgext/convert/convcmd.py not using absolute_import
47 47 hgext/convert/cvs.py not using absolute_import
48 48 hgext/convert/cvsps.py not using absolute_import
49 49 hgext/convert/darcs.py not using absolute_import
50 50 hgext/convert/filemap.py not using absolute_import
51 51 hgext/convert/git.py not using absolute_import
52 52 hgext/convert/gnuarch.py not using absolute_import
53 53 hgext/convert/hg.py not using absolute_import
54 54 hgext/convert/monotone.py not using absolute_import
55 55 hgext/convert/p4.py not using absolute_import
56 56 hgext/convert/subversion.py not using absolute_import
57 57 hgext/convert/transport.py not using absolute_import
58 58 hgext/eol.py not using absolute_import
59 59 hgext/extdiff.py not using absolute_import
60 60 hgext/factotum.py not using absolute_import
61 61 hgext/fetch.py not using absolute_import
62 62 hgext/gpg.py not using absolute_import
63 63 hgext/graphlog.py not using absolute_import
64 64 hgext/hgcia.py not using absolute_import
65 65 hgext/hgk.py not using absolute_import
66 66 hgext/highlight/__init__.py not using absolute_import
67 67 hgext/highlight/highlight.py not using absolute_import
68 68 hgext/histedit.py not using absolute_import
69 69 hgext/keyword.py not using absolute_import
70 70 hgext/largefiles/__init__.py not using absolute_import
71 71 hgext/largefiles/basestore.py not using absolute_import
72 72 hgext/largefiles/lfcommands.py not using absolute_import
73 73 hgext/largefiles/lfutil.py not using absolute_import
74 74 hgext/largefiles/localstore.py not using absolute_import
75 75 hgext/largefiles/overrides.py not using absolute_import
76 76 hgext/largefiles/proto.py not using absolute_import
77 77 hgext/largefiles/remotestore.py not using absolute_import
78 78 hgext/largefiles/reposetup.py not using absolute_import
79 79 hgext/largefiles/uisetup.py not using absolute_import
80 80 hgext/largefiles/wirestore.py not using absolute_import
81 81 hgext/mq.py not using absolute_import
82 82 hgext/notify.py not using absolute_import
83 83 hgext/pager.py not using absolute_import
84 84 hgext/patchbomb.py not using absolute_import
85 85 hgext/purge.py not using absolute_import
86 86 hgext/rebase.py not using absolute_import
87 87 hgext/record.py not using absolute_import
88 88 hgext/relink.py not using absolute_import
89 89 hgext/schemes.py not using absolute_import
90 90 hgext/share.py not using absolute_import
91 91 hgext/shelve.py not using absolute_import
92 92 hgext/strip.py not using absolute_import
93 93 hgext/transplant.py not using absolute_import
94 94 hgext/win32mbcs.py not using absolute_import
95 95 hgext/win32text.py not using absolute_import
96 96 hgext/zeroconf/Zeroconf.py not using absolute_import
97 97 hgext/zeroconf/Zeroconf.py requires print_function
98 98 hgext/zeroconf/__init__.py not using absolute_import
99 99 i18n/check-translation.py not using absolute_import
100 100 i18n/polib.py not using absolute_import
101 101 mercurial/byterange.py not using absolute_import
102 102 mercurial/cmdutil.py not using absolute_import
103 103 mercurial/commands.py not using absolute_import
104 104 mercurial/context.py not using absolute_import
105 105 mercurial/dirstate.py not using absolute_import
106 106 mercurial/dispatch.py requires print_function
107 107 mercurial/exchange.py not using absolute_import
108 108 mercurial/httpclient/__init__.py not using absolute_import
109 109 mercurial/httpclient/_readers.py not using absolute_import
110 110 mercurial/httpclient/socketutil.py not using absolute_import
111 111 mercurial/httpconnection.py not using absolute_import
112 112 mercurial/keepalive.py not using absolute_import
113 113 mercurial/keepalive.py requires print_function
114 114 mercurial/localrepo.py not using absolute_import
115 115 mercurial/lsprof.py requires print_function
116 116 mercurial/lsprofcalltree.py not using absolute_import
117 117 mercurial/lsprofcalltree.py requires print_function
118 118 mercurial/mail.py requires print_function
119 mercurial/manifest.py not using absolute_import
120 119 setup.py not using absolute_import
121 120 tests/filterpyflakes.py requires print_function
122 121 tests/generate-working-copy-states.py requires print_function
123 122 tests/get-with-headers.py requires print_function
124 123 tests/heredoctest.py requires print_function
125 124 tests/hypothesishelpers.py not using absolute_import
126 125 tests/hypothesishelpers.py requires print_function
127 126 tests/killdaemons.py not using absolute_import
128 127 tests/md5sum.py not using absolute_import
129 128 tests/mockblackbox.py not using absolute_import
130 129 tests/printenv.py not using absolute_import
131 130 tests/readlink.py not using absolute_import
132 131 tests/readlink.py requires print_function
133 132 tests/revlog-formatv0.py not using absolute_import
134 133 tests/run-tests.py not using absolute_import
135 134 tests/seq.py not using absolute_import
136 135 tests/seq.py requires print_function
137 136 tests/silenttestrunner.py not using absolute_import
138 137 tests/silenttestrunner.py requires print_function
139 138 tests/sitecustomize.py not using absolute_import
140 139 tests/svn-safe-append.py not using absolute_import
141 140 tests/svnxml.py not using absolute_import
142 141 tests/test-ancestor.py requires print_function
143 142 tests/test-atomictempfile.py not using absolute_import
144 143 tests/test-batching.py not using absolute_import
145 144 tests/test-batching.py requires print_function
146 145 tests/test-bdiff.py not using absolute_import
147 146 tests/test-bdiff.py requires print_function
148 147 tests/test-context.py not using absolute_import
149 148 tests/test-context.py requires print_function
150 149 tests/test-demandimport.py not using absolute_import
151 150 tests/test-demandimport.py requires print_function
152 151 tests/test-dispatch.py not using absolute_import
153 152 tests/test-dispatch.py requires print_function
154 153 tests/test-doctest.py not using absolute_import
155 154 tests/test-duplicateoptions.py not using absolute_import
156 155 tests/test-duplicateoptions.py requires print_function
157 156 tests/test-filecache.py not using absolute_import
158 157 tests/test-filecache.py requires print_function
159 158 tests/test-filelog.py not using absolute_import
160 159 tests/test-filelog.py requires print_function
161 160 tests/test-hg-parseurl.py not using absolute_import
162 161 tests/test-hg-parseurl.py requires print_function
163 162 tests/test-hgweb-auth.py not using absolute_import
164 163 tests/test-hgweb-auth.py requires print_function
165 164 tests/test-hgwebdir-paths.py not using absolute_import
166 165 tests/test-hybridencode.py not using absolute_import
167 166 tests/test-hybridencode.py requires print_function
168 167 tests/test-lrucachedict.py not using absolute_import
169 168 tests/test-lrucachedict.py requires print_function
170 169 tests/test-manifest.py not using absolute_import
171 170 tests/test-minirst.py not using absolute_import
172 171 tests/test-minirst.py requires print_function
173 172 tests/test-parseindex2.py not using absolute_import
174 173 tests/test-parseindex2.py requires print_function
175 174 tests/test-pathencode.py not using absolute_import
176 175 tests/test-pathencode.py requires print_function
177 176 tests/test-propertycache.py not using absolute_import
178 177 tests/test-propertycache.py requires print_function
179 178 tests/test-revlog-ancestry.py not using absolute_import
180 179 tests/test-revlog-ancestry.py requires print_function
181 180 tests/test-run-tests.py not using absolute_import
182 181 tests/test-simplemerge.py not using absolute_import
183 182 tests/test-status-inprocess.py not using absolute_import
184 183 tests/test-status-inprocess.py requires print_function
185 184 tests/test-symlink-os-yes-fs-no.py not using absolute_import
186 185 tests/test-trusted.py not using absolute_import
187 186 tests/test-trusted.py requires print_function
188 187 tests/test-ui-color.py not using absolute_import
189 188 tests/test-ui-color.py requires print_function
190 189 tests/test-ui-config.py not using absolute_import
191 190 tests/test-ui-config.py requires print_function
192 191 tests/test-ui-verbosity.py not using absolute_import
193 192 tests/test-ui-verbosity.py requires print_function
194 193 tests/test-url.py not using absolute_import
195 194 tests/test-url.py requires print_function
196 195 tests/test-walkrepo.py requires print_function
197 196 tests/test-wireproto.py requires print_function
198 197 tests/tinyproxy.py requires print_function
General Comments 0
You need to be logged in to leave comments. Login now