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( |
|
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( |
|
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 |
|
|
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