##// END OF EJS Templates
repair: improve performance of detection of revisions affected by issue6528...
Raphaël Gomès -
r48624:32e21ac3 stable
parent child Browse files
Show More
@@ -10,6 +10,7 b''
10 import binascii
10 import binascii
11 import contextlib
11 import contextlib
12 import os
12 import os
13 import struct
13
14
14 from ..node import (
15 from ..node import (
15 nullrev,
16 nullrev,
@@ -561,7 +562,7 b' def _reorder_filelog_parents(repo, fl, t'
561 util.tryunlink(new_file_path)
562 util.tryunlink(new_file_path)
562
563
563
564
564 def _is_revision_affected(ui, fl, filerev, path):
565 def _is_revision_affected(fl, filerev, metadata_cache=None):
565 """Mercurial currently (5.9rc0) uses `p1 == nullrev and p2 != nullrev` as a
566 """Mercurial currently (5.9rc0) uses `p1 == nullrev and p2 != nullrev` as a
566 special meaning compared to the reverse in the context of filelog-based
567 special meaning compared to the reverse in the context of filelog-based
567 copytracing. issue6528 exists because new code assumed that parent ordering
568 copytracing. issue6528 exists because new code assumed that parent ordering
@@ -574,6 +575,8 b' def _is_revision_affected(ui, fl, filere'
574 # We don't care about censored nodes as they never carry metadata
575 # We don't care about censored nodes as they never carry metadata
575 return False
576 return False
576 has_meta = raw_text.startswith(b'\x01\n')
577 has_meta = raw_text.startswith(b'\x01\n')
578 if metadata_cache is not None:
579 metadata_cache[filerev] = has_meta
577 if has_meta:
580 if has_meta:
578 (p1, p2) = fl.parentrevs(filerev)
581 (p1, p2) = fl.parentrevs(filerev)
579 if p1 != nullrev and p2 == nullrev:
582 if p1 != nullrev and p2 == nullrev:
@@ -581,6 +584,54 b' def _is_revision_affected(ui, fl, filere'
581 return False
584 return False
582
585
583
586
587 def _is_revision_affected_fast(repo, fl, filerev, metadata_cache):
588 """Optimization fast-path for `_is_revision_affected`.
589
590 `metadata_cache` is a dict of `{rev: has_metadata}` which allows any
591 revision to check if its base has metadata, saving computation of the full
592 text, instead looking at the current delta.
593
594 This optimization only works if the revisions are looked at in order."""
595 rl = fl._revlog
596
597 if rl.iscensored(filerev):
598 # Censored revisions don't contain metadata, so they cannot be affected
599 metadata_cache[filerev] = False
600 return False
601
602 p1, p2 = rl.parentrevs(filerev)
603 if p1 == nullrev or p2 != nullrev:
604 return False
605
606 delta_parent = rl.deltaparent(filerev)
607 parent_has_metadata = metadata_cache.get(delta_parent)
608 if parent_has_metadata is None:
609 is_affected = _is_revision_affected(fl, filerev, metadata_cache)
610 return is_affected
611
612 chunk = rl._chunk(filerev)
613 if not len(chunk):
614 # No diff for this revision
615 return parent_has_metadata
616
617 header_length = 12
618 if len(chunk) < header_length:
619 raise error.Abort(_(b"patch cannot be decoded"))
620
621 start, _end, _length = struct.unpack(b">lll", chunk[:header_length])
622
623 if start < 2: # len(b'\x01\n') == 2
624 # This delta does *something* to the metadata marker (if any).
625 # Check it the slow way
626 is_affected = _is_revision_affected(fl, filerev, metadata_cache)
627 return is_affected
628
629 # The diff did not remove or add the metadata header, it's then in the same
630 # situation as its parent
631 metadata_cache[filerev] = parent_has_metadata
632 return parent_has_metadata
633
634
584 def _from_report(ui, repo, context, from_report, dry_run):
635 def _from_report(ui, repo, context, from_report, dry_run):
585 """
636 """
586 Fix the revisions given in the `from_report` file, but still checks if the
637 Fix the revisions given in the `from_report` file, but still checks if the
@@ -603,7 +654,7 b' def _from_report(ui, repo, context, from'
603 excluded = set()
654 excluded = set()
604
655
605 for filerev in to_fix:
656 for filerev in to_fix:
606 if _is_revision_affected(ui, fl, filerev, filename):
657 if _is_revision_affected(fl, filerev):
607 msg = b"found affected revision %d for filelog '%s'\n"
658 msg = b"found affected revision %d for filelog '%s'\n"
608 ui.warn(msg % (filerev, filename))
659 ui.warn(msg % (filerev, filename))
609 else:
660 else:
@@ -663,11 +714,11 b' def repair_issue6528(ui, repo, dry_run=F'
663
714
664 # Set of filerevs (or hex filenodes if `to_report`) that need fixing
715 # Set of filerevs (or hex filenodes if `to_report`) that need fixing
665 to_fix = set()
716 to_fix = set()
717 metadata_cache = {}
666 for filerev in fl.revs():
718 for filerev in fl.revs():
667 # TODO speed up by looking at the start of the delta
719 affected = _is_revision_affected_fast(
668 # If it hasn't changed, it's not worth looking at the other revs
720 repo, fl, filerev, metadata_cache
669 # in the same chain
721 )
670 affected = _is_revision_affected(ui, fl, filerev, path)
671 if affected:
722 if affected:
672 msg = b"found affected revision %d for filelog '%s'\n"
723 msg = b"found affected revision %d for filelog '%s'\n"
673 ui.warn(msg % (filerev, path))
724 ui.warn(msg % (filerev, path))
General Comments 0
You need to be logged in to leave comments. Login now