Show More
@@ -328,6 +328,228 b' def _addlistdelta(addlist, x):' | |||
|
328 | 328 | + content for start, end, content in x) |
|
329 | 329 | return deltatext, newaddlist |
|
330 | 330 | |
|
331 | def _splittopdir(f): | |
|
332 | if '/' in f: | |
|
333 | dir, subpath = f.split('/', 1) | |
|
334 | return dir + '/', subpath | |
|
335 | else: | |
|
336 | return '', f | |
|
337 | ||
|
338 | class treemanifest(object): | |
|
339 | def __init__(self, text=''): | |
|
340 | self._dirs = {} | |
|
341 | # Using _lazymanifest here is a little slower than plain old dicts | |
|
342 | self._files = {} | |
|
343 | self._flags = {} | |
|
344 | lm = _lazymanifest(text) | |
|
345 | for f, n, fl in lm.iterentries(): | |
|
346 | self[f] = n | |
|
347 | if fl: | |
|
348 | self.setflag(f, fl) | |
|
349 | ||
|
350 | def __len__(self): | |
|
351 | size = len(self._files) | |
|
352 | for m in self._dirs.values(): | |
|
353 | size += m.__len__() | |
|
354 | return size | |
|
355 | ||
|
356 | def iteritems(self): | |
|
357 | for p, n in sorted(self._dirs.items() + self._files.items()): | |
|
358 | if p in self._files: | |
|
359 | yield p, n | |
|
360 | else: | |
|
361 | for sf, sn in n.iteritems(): | |
|
362 | yield p + sf, sn | |
|
363 | ||
|
364 | def iterkeys(self): | |
|
365 | for p in sorted(self._dirs.keys() + self._files.keys()): | |
|
366 | if p in self._files: | |
|
367 | yield p | |
|
368 | else: | |
|
369 | for f in self._dirs[p].iterkeys(): | |
|
370 | yield p + f | |
|
371 | ||
|
372 | def keys(self): | |
|
373 | return list(self.iterkeys()) | |
|
374 | ||
|
375 | def __iter__(self): | |
|
376 | return self.iterkeys() | |
|
377 | ||
|
378 | def __contains__(self, f): | |
|
379 | if f is None: | |
|
380 | return False | |
|
381 | dir, subpath = _splittopdir(f) | |
|
382 | if dir: | |
|
383 | if dir not in self._dirs: | |
|
384 | return False | |
|
385 | return self._dirs[dir].__contains__(subpath) | |
|
386 | else: | |
|
387 | return f in self._files | |
|
388 | ||
|
389 | def get(self, f, default=None): | |
|
390 | dir, subpath = _splittopdir(f) | |
|
391 | if dir: | |
|
392 | if dir not in self._dirs: | |
|
393 | return default | |
|
394 | return self._dirs[dir].get(subpath, default) | |
|
395 | else: | |
|
396 | return self._files.get(f, default) | |
|
397 | ||
|
398 | def __getitem__(self, f): | |
|
399 | dir, subpath = _splittopdir(f) | |
|
400 | if dir: | |
|
401 | return self._dirs[dir].__getitem__(subpath) | |
|
402 | else: | |
|
403 | return self._files[f] | |
|
404 | ||
|
405 | def flags(self, f): | |
|
406 | dir, subpath = _splittopdir(f) | |
|
407 | if dir: | |
|
408 | if dir not in self._dirs: | |
|
409 | return '' | |
|
410 | return self._dirs[dir].flags(subpath) | |
|
411 | else: | |
|
412 | if f in self._dirs: | |
|
413 | return '' | |
|
414 | return self._flags.get(f, '') | |
|
415 | ||
|
416 | def find(self, f): | |
|
417 | dir, subpath = _splittopdir(f) | |
|
418 | if dir: | |
|
419 | return self._dirs[dir].find(subpath) | |
|
420 | else: | |
|
421 | return self._files[f], self._flags.get(f, '') | |
|
422 | ||
|
423 | def __delitem__(self, f): | |
|
424 | dir, subpath = _splittopdir(f) | |
|
425 | if dir: | |
|
426 | self._dirs[dir].__delitem__(subpath) | |
|
427 | # If the directory is now empty, remove it | |
|
428 | if not self._dirs[dir]._dirs and not self._dirs[dir]._files: | |
|
429 | del self._dirs[dir] | |
|
430 | else: | |
|
431 | del self._files[f] | |
|
432 | if f in self._flags: | |
|
433 | del self._flags[f] | |
|
434 | ||
|
435 | def __setitem__(self, f, n): | |
|
436 | assert n is not None | |
|
437 | dir, subpath = _splittopdir(f) | |
|
438 | if dir: | |
|
439 | if dir not in self._dirs: | |
|
440 | self._dirs[dir] = treemanifest() | |
|
441 | self._dirs[dir].__setitem__(subpath, n) | |
|
442 | else: | |
|
443 | self._files[f] = n | |
|
444 | ||
|
445 | def setflag(self, f, flags): | |
|
446 | """Set the flags (symlink, executable) for path f.""" | |
|
447 | dir, subpath = _splittopdir(f) | |
|
448 | if dir: | |
|
449 | if dir not in self._dirs: | |
|
450 | self._dirs[dir] = treemanifest() | |
|
451 | self._dirs[dir].setflag(subpath, flags) | |
|
452 | else: | |
|
453 | self._flags[f] = flags | |
|
454 | ||
|
455 | def copy(self): | |
|
456 | copy = treemanifest() | |
|
457 | for d in self._dirs: | |
|
458 | copy._dirs[d] = self._dirs[d].copy() | |
|
459 | copy._files = dict.copy(self._files) | |
|
460 | copy._flags = dict.copy(self._flags) | |
|
461 | return copy | |
|
462 | ||
|
463 | def intersectfiles(self, files): | |
|
464 | '''make a new treemanifest with the intersection of self with files | |
|
465 | ||
|
466 | The algorithm assumes that files is much smaller than self.''' | |
|
467 | ret = treemanifest() | |
|
468 | for fn in files: | |
|
469 | if fn in self: | |
|
470 | ret[fn] = self[fn] | |
|
471 | flags = self.flags(fn) | |
|
472 | if flags: | |
|
473 | ret.setflag(fn, flags) | |
|
474 | return ret | |
|
475 | ||
|
476 | def filesnotin(self, m2): | |
|
477 | '''Set of files in this manifest that are not in the other''' | |
|
478 | files = set(self.iterkeys()) | |
|
479 | files.difference_update(m2.iterkeys()) | |
|
480 | return files | |
|
481 | ||
|
482 | @propertycache | |
|
483 | def _alldirs(self): | |
|
484 | return scmutil.dirs(self) | |
|
485 | ||
|
486 | def dirs(self): | |
|
487 | return self._alldirs | |
|
488 | ||
|
489 | def hasdir(self, dir): | |
|
490 | return dir in self._alldirs | |
|
491 | ||
|
492 | def matches(self, match): | |
|
493 | '''generate a new manifest filtered by the match argument''' | |
|
494 | if match.always(): | |
|
495 | return self.copy() | |
|
496 | ||
|
497 | files = match.files() | |
|
498 | if (match.matchfn == match.exact or | |
|
499 | (not match.anypats() and util.all(fn in self for fn in files))): | |
|
500 | return self.intersectfiles(files) | |
|
501 | ||
|
502 | m = self.copy() | |
|
503 | for fn in m.keys(): | |
|
504 | if not match(fn): | |
|
505 | del m[fn] | |
|
506 | return m | |
|
507 | ||
|
508 | def diff(self, m2, clean=False): | |
|
509 | '''Finds changes between the current manifest and m2. | |
|
510 | ||
|
511 | Args: | |
|
512 | m2: the manifest to which this manifest should be compared. | |
|
513 | clean: if true, include files unchanged between these manifests | |
|
514 | with a None value in the returned dictionary. | |
|
515 | ||
|
516 | The result is returned as a dict with filename as key and | |
|
517 | values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the | |
|
518 | nodeid in the current/other manifest and fl1/fl2 is the flag | |
|
519 | in the current/other manifest. Where the file does not exist, | |
|
520 | the nodeid will be None and the flags will be the empty | |
|
521 | string. | |
|
522 | ''' | |
|
523 | diff = {} | |
|
524 | ||
|
525 | for fn, n1 in self.iteritems(): | |
|
526 | fl1 = self.flags(fn) | |
|
527 | n2 = m2.get(fn, None) | |
|
528 | fl2 = m2.flags(fn) | |
|
529 | if n2 is None: | |
|
530 | fl2 = '' | |
|
531 | if n1 != n2 or fl1 != fl2: | |
|
532 | diff[fn] = ((n1, fl1), (n2, fl2)) | |
|
533 | elif clean: | |
|
534 | diff[fn] = None | |
|
535 | ||
|
536 | for fn, n2 in m2.iteritems(): | |
|
537 | if fn not in self: | |
|
538 | fl2 = m2.flags(fn) | |
|
539 | diff[fn] = ((None, ''), (n2, fl2)) | |
|
540 | ||
|
541 | return diff | |
|
542 | ||
|
543 | def text(self): | |
|
544 | """Get the full data of this manifest as a bytestring.""" | |
|
545 | fl = self.keys() | |
|
546 | _checkforbidden(fl) | |
|
547 | ||
|
548 | hex, flags = revlog.hex, self.flags | |
|
549 | # if this is changed to support newlines in filenames, | |
|
550 | # be sure to check the templates/ dir again (especially *-raw.tmpl) | |
|
551 | return ''.join("%s\0%s%s\n" % (f, hex(self[f]), flags(f)) for f in fl) | |
|
552 | ||
|
331 | 553 | class manifest(revlog.revlog): |
|
332 | 554 | def __init__(self, opener): |
|
333 | 555 | # During normal operations, we expect to deal with not more than four |
General Comments 0
You need to be logged in to leave comments.
Login now