326 |
326 |
327 deltatext = "".join(struct.pack(">lll", start, end, len(content)) |
327 deltatext = "".join(struct.pack(">lll", start, end, len(content)) |
328 + content for start, end, content in x) |
328 + content for start, end, content in x) |
329 return deltatext, newaddlist |
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 class manifest(revlog.revlog): |
553 class manifest(revlog.revlog): |
332 def __init__(self, opener): |
554 def __init__(self, opener): |
333 # During normal operations, we expect to deal with not more than four |
555 # During normal operations, we expect to deal with not more than four |
334 # revs at a time (such as during commit --amend). When rebasing large |
556 # revs at a time (such as during commit --amend). When rebasing large |
335 # stacks of commits, the number can go up, hence the config knob below. |
557 # stacks of commits, the number can go up, hence the config knob below. |