[MERGE] Reconcile can fix bad parent references, Andrew's version

Andrew Bennetts andrew at canonical.com
Fri Oct 5 04:11:30 BST 2007


Robert Collins wrote:
> On Sat, 2007-09-29 at 00:38 +1000, Andrew Bennetts wrote:
> > === added file
> > 'bzrlib/tests/repository_implementations/test_broken.py'
> > ...
> > +"""Tests that use BrokenRepoScenario objects.
> > +
> > +That is, tests for reconcile and check.
> > +"""
> 
> We try to group tests by the functionality they test. This appears to be
> grouping by how the tests were written, which will make them hard to
> find. I suggest 
> test_check_reconcile.py
> (if you need tests of both functions to be conflated)

That's a better name.  Thanks.

> > +from bzrlib.inventory import Inventory, InventoryFile
> > +from bzrlib.revision import Revision
> > +from bzrlib.tests import TestNotApplicable
> > +from bzrlib.tests.repository_implementations import
> > TestCaseWithRepository
> > +
> > +import sha
> 
> PEP-8, standard lib before local imports.

Fixed.

> > +class TestReconcile(TestCaseWithRepository):
> > +    """Tests for how reconcile corrects errors in parents of file
> > versions."""
> 
> PEP-8, one line if possible, or one and a paragraph... and the class

Um, that docstring *is* one line.

> name does not imply the narrow focus the docstring does. We know this is
> a test class so perhaps
> class TestFileParentReconciliation

That's better, thanks.

> > +    def assertCheckScenario(self, scenario):
> > +        repo =
> > self.make_repository_using_factory(scenario.populate_repository)
> > +        self.require_text_parent_corruption(repo)
> > +        check_result = repo.check()
> > +        check_result.report_results(verbose=True)
> > +        for pattern in scenario.check_regexes():
> > +            self.assertContainsRe(
> > +                self._get_log(keep_log_file=True),
> > +                pattern)
> 
> This method needs a docstring and a better name. either assert or check
> is redundant. And scenario is incredibly generic here, if you want to go
> hungarian (naming after what a thing is, not what its for), then perhaps
> reconcile_scenario).

Well, it's trying to assert something about what “check” does.  So it's not
redundant, even though it is confusing.

It's not really needed anymore; it was factored out to avoid duplication, but
now the test case parameterisation takes care of that duplication.  So I've
removed this method and put the body back into test_check (which I've
renamed test_reconcile_check).

> > +    def make_repository_using_factory(self, factory):
> > +        """Create a new repository populated by the given factory."""
> > +        repo = self.make_repository('broken-repo')
> > +        repo.lock_write()
> > +        try:
> > +            repo.start_write_group()
> > +            try:
> > +                factory(repo)
> > +                repo.commit_write_group()
> > +                return repo
> > +            except:
> > +                repo.abort_write_group()
> > +                raise
> > +        finally:
> > +            repo.unlock()
> 
> Perhaps 'make_populated_repository' ?

That's better, thanks.

> > +    def add_revision(self, repo, revision_id, inv, parent_ids):
> > +        inv.revision_id = revision_id
> > +        inv.root.revision = revision_id
> > +        repo.add_inventory(revision_id, inv, parent_ids)
> > +        revision = Revision(revision_id,
> > committer='jrandom at example.com',
> > +            timestamp=0, inventory_sha1='', timezone=0,
> > message='foo',
> > +            parent_ids=parent_ids)
> > +        repo.add_revision(revision_id,revision, inv)
> 
> This could do with a docstring.

How about:

        """Add a revision with a given inventory and parents to a repository.
        
        :param repo: a repository.
        :param revision_id: the revision ID for the new revision.
        :param inv: an inventory (such as created by
            `make_one_file_inventory`).
        :param parent_ids: the parents for the new revision.
        """

> > +    def require_text_parent_corruption(self, repo):
> > +        if not repo._reconcile_fixes_text_parents:
> > +            raise TestNotApplicable(
> > +                    "Format does not support text parent reconciliation")
> 
> perhaps tweak the name, 'require_repo_suffers_text_parent_corruption'.

Done.

> > +    def file_parents(self, repo, revision_id):
> > +        return repo.weave_store.get_weave('a-file-id',
> > +            repo.get_transaction()).get_parents(revision_id)
> 
> This looks like something worth putting onto Repository.

Well, perhaps partly.  But we'd still want a helper on this TestCase because the
test doesn't care about the detail of what the file-id being used is.

> > +    def assertReconcileResults(self, scenario):
> > +        """Construct a repository and reconcile it, verifying the state before
> > +        and after.
> > +
> > +        :param scenario: a Scenario to test reconcile on.
> > +        """
> 
> going from the method name this is more than an assertion, it takes
> active action.
> I suggest 
> checkReconcileBehaviour

Like assertCheckScenario, this doesn't need to be a separate method anymore.  So
I've removed this method, and put the body of it directly in test_reconcile
(which has been renamed to test_reconcile_behaviour).

> > +        # The content of the versionedfile should be the same after
> > the
> > +        # reconcile.
> 
> 'The content of the versions in the versionedfile ...'

Done.

> > +    def test_reconcile(self):
> > +        self.assertReconcileResults(self.scenario_class(self))
> > +
> > +    def test_check(self):
> > +        self.assertCheckScenario(self.scenario_class(self))
> 
> These methods are very opaque.

They now have docstrings, and more intelligible implementations too.

> > === added file 'bzrlib/tests/repository_implementations/test_check.py'
> > --- bzrlib/tests/repository_implementations/test_check.py       1970-01-01 00:00:00 +0000
> > +++ bzrlib/tests/repository_implementations/test_check.py       2007-09-28 12:56:56 +0000
> > @@ -0,0 +1,135 @@
> > +# Copyright (C) 2007 Canonical Ltd
> > +#
> > +# This program is free software; you can redistribute it and/or modify
> > +# it under the terms of the GNU General Public License as published by
> > +# the Free Software Foundation; either version 2 of the License, or
> > +# (at your option) any later version.
> > +#
> > +# This program is distributed in the hope that it will be useful,
> > +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > +# GNU General Public License for more details.
> > +#
> > +# You should have received a copy of the GNU General Public License
> > +# along with this program; if not, write to the Free Software
> > +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> > +
> > +
> > +"""Test operations that check the repository for corruption"""
> > +
> > +
> > +from bzrlib import (
> > +    inventory,
> > +    revision as _mod_revision,
> > +    )
> > +from bzrlib.repository import _RevisionTextVersionCache
> > +from bzrlib.tests.repository_implementations import TestCaseWithRepository
> > +
> > +
> > +class TestFindBadAncestors(TestCaseWithRepository):
> > +
> > +    def make_broken_repository(self):
> > +        repo = self.make_repository('.')
> > +        cleanups = []
> > +        try:
> > +            repo.lock_write()
> > +            cleanups.append(repo.unlock)
> > +            repo.start_write_group()
> > +            cleanups.append(repo.commit_write_group)
> > +            # make rev1a: A well-formed revision, containing 'file1'
> > +            inv = inventory.Inventory(revision_id='rev1a')
> > +            inv.root.revision = 'rev1a'
> > +            self.add_file(repo, inv, 'file1', 'rev1a', [])
> > +            repo.add_inventory('rev1a', inv, [])
> > +            revision = _mod_revision.Revision('rev1a',
> > +                committer='jrandom at example.com', timestamp=0,
> > +                inventory_sha1='', timezone=0, message='foo', parent_ids=[])
> > +            repo.add_revision('rev1a',revision, inv)
> > +
> > +            # make rev1b, which has no Revision, but has an Inventory, and
> > +            # file1
> > +            inv = inventory.Inventory(revision_id='rev1b')
> > +            inv.root.revision = 'rev1b'
> > +            self.add_file(repo, inv, 'file1', 'rev1b', [])
> > +            repo.add_inventory('rev1b', inv, [])
> > +
> > +            # make rev2, with file1 and file2
> > +            # file2 is sane
> > +            # file1 has 'rev1b' as an ancestor, even though this is not
> > +            # mentioned by 'rev1a', making it an unreferenced ancestor
> > +            inv = inventory.Inventory()
> > +            self.add_file(repo, inv, 'file1', 'rev2', ['rev1a', 'rev1b'])
> > +            self.add_file(repo, inv, 'file2', 'rev2', [])
> > +            self.add_revision(repo, 'rev2', inv, ['rev1a'])
> > +
> > +            # make ghost revision rev1c
> > +            inv = inventory.Inventory()
> > +            self.add_file(repo, inv, 'file2', 'rev1c', [])
> > +
> > +            # make rev3 with file2
> > +            # file2 refers to 'rev1c', which is a ghost in this repository, so
> > +            # file2 cannot have rev1c as its ancestor.
> > +            inv = inventory.Inventory()
> > +            self.add_file(repo, inv, 'file2', 'rev3', ['rev1c'])
> > +            self.add_revision(repo, 'rev3', inv, ['rev1c'])
> > +            return repo
> > +        finally:
> > +            for cleanup in reversed(cleanups):
> > +                cleanup()
> > +
> > +    def add_revision(self, repo, revision_id, inv, parent_ids):
> > +        inv.revision_id = revision_id
> > +        inv.root.revision = revision_id
> > +        repo.add_inventory(revision_id, inv, parent_ids)
> > +        revision = _mod_revision.Revision(revision_id,
> > +            committer='jrandom at example.com', timestamp=0, inventory_sha1='',
> > +            timezone=0, message='foo', parent_ids=parent_ids)
> > +        repo.add_revision(revision_id,revision, inv)
> > +
> > +    def add_file(self, repo, inv, filename, revision, parents):
> > +        file_id = filename + '-id'
> > +        entry = inventory.InventoryFile(file_id, filename, 'TREE_ROOT')
> > +        entry.revision = revision
> > +        entry.text_size = 0
> > +        inv.add(entry)
> > +        vf = repo.weave_store.get_weave_or_empty(file_id,
> > +                                                 repo.get_transaction())
> > +        vf.add_lines(revision, parents, ['line\n'])
> 
> 
> The above logic looks very similar to that int he broken_repo tests, is
> it duplicated?

Oops, yes.  Deleted.

> > +    def find_bad_ancestors(self, file_id, revision_ids):
> > +        repo = self.make_broken_repository()
> > +        vf = repo.weave_store.get_weave(file_id, repo.get_transaction())
> > +        return repo.find_bad_ancestors(revision_ids, file_id, vf,
> > +                                       _RevisionTextVersionCache(repo))
> 
> Going from your patch description this method and tests would seem to be
> redundant now, perhaps delete it ?

I've extended the test_check_behaviour scenarios slightly to make sure they
cover the same cases as these tests (i.e. the “unreferenced ancestors” reported
by check), and deleted these tests.

> > === modified file 'NEWS'
> > --- NEWS        2007-09-24 09:40:01 +0000
> > +++ NEWS        2007-09-26 10:49:46 +0000
> > @@ -25,7 +25,10 @@
> >  
> >    FEATURES:
> >  
> > -   * New ``reconfigure`` command (Aaron Bentley)
> > +   * New ``reconfigure`` command. (Aaron Bentley)
> > +
> > +   * New ``revert --forget-merges`` command, which removes the record of a pending 
> > +     merge without affecting the working tree contents.  (Martin Pool)
> 
> Why are these being changed by this patch?

I have no idea.  Apparently this happened when I merged bzr.dev, so I'm not sure
why it's showing in the diff.

If I try merging this branch into bzr.dev myself, I see only a single entry
added to NEWS.

> ...
> 
> 
> > @@ -3048,19 +3063,24 @@
> >  
> >      _see_also = ['cat', 'export']
> >      takes_options = [
> > -            'revision',
> > -            Option('no-backup', "Do not save backups of reverted files."),
> > -            ]
> > +        'revision',
> > +        Option('no-backup', "Do not save backups of reverted files."),
> > +        Option('forget-merges',
> > +               'Remove pending merge marker, without changing any files.'),
> > +        ]
[...]
> 
> Something is definately wrong in this bundle, there are changes here for
> revert and other commands.

I guess “bzr send” had trouble picking a good ancestor to diff against?

> > === modified file 'bzrlib/check.py'
> > --- bzrlib/check.py     2007-08-22 22:32:23 +0000
> > +++ bzrlib/check.py     2007-09-28 08:18:16 +0000
> > @@ -32,6 +32,7 @@
> >  # raising them.  If there's more than one exception it'd be good to see them
> >  # all.
> >  
> > +from bzrlib import repository as _mod_repository
> >  from bzrlib.errors import BzrCheckError
> >  import bzrlib.ui
> >  from bzrlib.trace import note
> > @@ -53,6 +54,10 @@
> >          # maps (file-id, version) -> sha1; used by InventoryFile._check
> >          self.checked_texts = {}
> >          self.checked_weaves = {}
> > +        self.revision_versions = _mod_repository._RevisionTextVersionCache(
> > +            self.repository)
> > +        self.unreferenced_ancestors = set()
> > +        self.inconsistent_parents = []
> >  
> >      def check(self):
> >          self.repository.lock_read()
> > @@ -64,36 +69,39 @@
> >              self.inventory_weave = self.repository.get_inventory_weave()
> >              self.plan_revisions()
> >              revno = 0
> > -            self.check_weaves()
> >              while revno < len(self.planned_revisions):
> >                  rev_id = self.planned_revisions[revno]
> >                  self.progress.update('checking revision', revno,
> >                                       len(self.planned_revisions))
> >                  revno += 1
> >                  self.check_one_rev(rev_id)
> > +            # check_weaves is done after the revision scan so that
> > +            # revision_versions is pre-populated
> > +            self.check_weaves()
> >          finally:
> >              self.progress.finished()
> >              self.repository.unlock()
> >  
> >      def plan_revisions(self):
> >          repository = self.repository
> > -        self.planned_revisions = set(repository.all_revision_ids())
> > +        self.planned_revisions = repository.all_revision_ids()
> >          self.progress.clear()
> >          inventoried = set(self.inventory_weave.versions())
> > -        awol = self.planned_revisions - inventoried
> > +        awol = set(self.planned_revisions) - inventoried
> >          if len(awol) > 0:
> >              raise BzrCheckError('Stored revisions missing from inventory'
> >                  '{%s}' % ','.join([f for f in awol]))
> > -        self.planned_revisions = list(self.planned_revisions)
> >  
> >      def report_results(self, verbose):
> >          note('checked repository %s format %s',
> >               self.repository.bzrdir.root_transport,
> >               self.repository._format)
> >          note('%6d revisions', self.checked_rev_cnt)
> > +        note('%6d versionedfiles', len(self.checked_weaves))
> 
> I suggest this say '%6d file-ids'

Done.

> > @@ -113,6 +121,19 @@
> >                      note('      %s should be in the ancestry for:', link)
> >                      for linker in linkers:
> >                          note('       * %s', linker)
> > +            if verbose:
> > +                for file_id, revision_id in self.unreferenced_ancestors:
> > +                    note('unreferenced ancestor: {%s} in %s', revision_id,
> > +                        file_id)
> 
> This is a critical error, it should error, not output a warning.

Done.

> > +            result = w.find_bad_ancestors(
> > +                self.planned_revisions,
> > +                self.revision_versions.get_text_version,
> > +                weave_id,
> > +                revision_parents,
> > +                self.repository.get_graph())
> > +            for revision_id in result.iterkeys():
> > +                self.unreferenced_ancestors.add((weave_id, revision_id))
> >              self.checked_weaves[weave_id] = True
> 
> Why do you pass the weave id back into a method on the weave ? This
> *really* looks like its barely related to the weave at all.

Because that refactoring didn't end up as nice as I'd hoped...

I've removed these methods from VersionedFile.  Instead I've added a
“get_versioned_file_checker” method to Repository that returns an object with a
“check_file_version_parents” method.

I expect your pack repository class will want to override that.  I'm not sure
that I've factored this the ideal way, but I hope it's close enough for now.

> >      def _check_revision_tree(self, rev_id):
> >          tree = self.repository.revision_tree(rev_id)
> > +        self.revision_versions.add_revision_text_version(tree)
> 
> I'd pluralise this method.

Done.

> >          inv = tree.inventory
> >          seen_ids = {}
> >          for file_id in inv:
> 
> But as it seems to be doing the same loop as this, for locality of
> reference why not just accrue the id:versions in the loop and supply to
> the cache afterwards.

Well, there's another place that calls add_revision_text_versions, so it seems
simplest to leave this as-is.

> > === modified file 'bzrlib/errors.py'
> > --- bzrlib/errors.py    2007-09-13 01:54:49 +0000
> > +++ bzrlib/errors.py    2007-09-25 06:29:46 +0000
> > @@ -95,7 +95,11 @@
> >          try:
> >              fmt = self._get_format_string()
> >              if fmt:
> > -                s = fmt % self.__dict__
> > +                d = (self.__dict__)
> > +                # special case: python2.5 puts the 'message' attribute in a
> > +                # slot, so it isn't seen in __dict__
> > +                d['message'] = getattr(self, 'message', 'dummy message')
> > +                s = fmt % d
> >                  # __str__() should always return a 'str' object
> >                  # never a 'unicode' object.
> >                  if isinstance(s, unicode):
> 
> This looks entirely unrelated.

Again, the changes in this file are an artifact of the bundle diff, not actually
changes from this branch.  I'll merge bzr.dev just before I send the next
bundle, which I'm guessing will avoid this problem.

> > === modified file 'bzrlib/reconcile.py'
[...]
> > +    def _fix_text_parents(self):
> > +        """Fix bad versionedfile parent entries.
> > +
> > +        It is possible for the parents entry in a versionedfile entry to be
> > +        inconsistent with the values in the revision and inventory.
> > +
> > +        This method finds entries with such inconsistencies, corrects their
> > +        parent lists, and replaces the versionedfile with a corrected version.
> > +        """
> > +        transaction = self.repo.get_transaction()
> > +        revision_parents = repository._RevisionParentsProvider(self.repo)
> > +        revision_graph = graph.Graph(revision_parents)
> 
> Why isn't this just
> revision_graph = self.repo.get_graph() ?

No good reason anymore.  I merged in my find-inconsistent-parents branch, and
got rid of repository._RevisionParentsProvider entirely.  So this is now fixed.

> > +            if len(versions_with_bad_parents) == 0:
> > +                continue
> 
> I think from here to the end could be a separate method.

Done.


> > === modified file 'bzrlib/repofmt/knitrepo.py'
> > --- bzrlib/repofmt/knitrepo.py  2007-09-16 23:48:15 +0000
> > +++ bzrlib/repofmt/knitrepo.py  2007-09-26 10:47:10 +0000
> > @@ -69,6 +69,11 @@
> >      """Knit format repository."""
> >  
> >      _serializer = xml5.serializer_v5
> > +    def __init__(self, _format, a_bzrdir, control_files, _revision_store,
> > +                 control_store, text_store):
> > +        MetaDirRepository.__init__(self, _format, a_bzrdir, control_files,
> > +                                   _revision_store, control_store, text_store)
> > +        self._reconcile_fixes_text_parents = True
> 
> PEP-8 - newline between _serializer and the def __init__.

Fixed.

> > +    def find_bad_ancestors(self, revision_ids, file_id, versionedfile,
> > +                           revision_versions):
[...]
> 
> This method seems rather pointless now.

It is.  Fixed.

> > +class _RevisionTextVersionCache(object):
> > +    """A cache of the versionedfile versions for revision and file-id"""
> > +
> > +    def __init__(self, repository):
> > +        self.repository = repository
> > +        self.revision_versions = {}
> > +
> > +    def add_revision_text_version(self, tree):
> > +        """Cache text version data from the supplied revision tree"""
> > +        inv_revisions = {}
> > +        for path, entry in tree.iter_entries_by_dir():
> > +            inv_revisions[entry.file_id] = entry.revision
> > +        self.revision_versions[tree.get_revision_id()] = inv_revisions
> > +        return inv_revisions
> > +
> > +    def get_text_version(self, file_id, revision_id):
> > +        """Determine the text version for a given file-id and revision-id"""
> > +        try:
> > +            inv_revisions = self.revision_versions[revision_id]
> > +        except KeyError:
> > +            tree = self.repository.revision_tree(revision_id)
> > +            inv_revisions = self.add_revision_text_version(tree)
> > +        return inv_revisions.get(file_id)
> 
> 
> This object does not look like a cache; it looks like a dict that will
> autopopulate when you ask for something it does not know. So I suggest
> not calling it a cache ;). 

Hmm, it serves the purpose of a cache: provide faster access to data that was
already accessed recently.  It doesn't take care to avoid returning stale data
so perhaps calling it a cache is a bit inaccurate, but just calling it an
auto-populating dict doesn't really communicate what it's for.

I'm open to suggestions, but "cache" seems like a reasonable shorthand to use
here.  Would you prefer "memo"?

> > +class _RevisionParentsProvider(object):
[...]
> 
> Is this class now used or needed?

As of a few minutes ago it's gone.

> > === modified file 'bzrlib/tests/repository_implementations/__init__.py'
[...]
> 
> > +class BrokenRepositoryTestProviderAdapter(TestScenarioApplier):
> > +
> > +    scenario_classes = [
> > +        FileParentIsNotInRevisionAncestryScenario,
> > +        FileParentHasInaccessibleInventoryScenario,
> > +        FileParentsNotReferencedByAnyInventoryScenario,
> > +        TooManyParentsScenario,
> > +        ClaimedFileParentDidNotModifyFileScenario,
> > +        IncorrectlyOrderedParentsScenario,
> > +        ]
> > +
> > +    def __init__(self):
> > +        TestScenarioApplier.__init__(self)
> > +        self.scenarios = [
> > +            (s.__name__, {'scenario_class': s}) for s in
> > self.scenario_classes]
> 
> I don't think a class is needed here. TestScenarioApplier is designed to
> be usable without subclassing:
>  applier = TestScenarioApplier()
>  applier.scenarios = (s.__name__, {'scenario_class': s}) for s in
>       [
>         FileParentIsNotInRevisionAncestryScenario,
>         FileParentHasInaccessibleInventoryScenario,
>         FileParentsNotReferencedByAnyInventoryScenario,
>         TooManyParentsScenario,
>         ClaimedFileParentDidNotModifyFileScenario,
>         IncorrectlyOrderedParentsScenario,
>         ])

Hmm, I'm not sure that's clearer.  But I'll do it the way you intended it to be
done.

I note though that the other TestScenarioApplier in this file is already a
subclass.

> >  def test_suite():
> >      result = TestSuite()
> >      test_repository_implementations = [
> >          'bzrlib.tests.repository_implementations.test_break_lock',
> > +        # note that test_broken is intentionally excluded from this list; it is
> > +        # handled further down.
> 
> You could make this more clear, and generic by creating all the appliers
> you need, then making a list of tuples:
> [(appliers, test_names), ...]
> 
> The stock tests are
> ((format_adapter,), [names])
> The broken repo tests are
> ((format_adapter, broken_rep_applier), [test_broken_repo])
> 
> Then its fed straight into:
> ....
> because theres a bunch of duplicate code at the moment.

Done.

> > === modified file 'bzrlib/tests/repository_implementations/test_reconcile.py'
[....]
> > +
> > +#commiting rev X, row 0 local change, row 1 no local change against at least one parent
> > +#rev parents  X.revision inv.revision foreach parent     knit parents   label
> > +#revA          X         revA                            revA            parent-changed
> > +#revA          A         revA                            NO ENTRY
[...]
> 
> This matrix should go, or be documented for other readers.

Gone.

> > +    def test_check_error(self):
> > +        # This has a member called 'message', which is problematic in
> > +        # python2.5 because that is a slot on the base Exception class
> > +        e = errors.BzrCheckError('example check failure')
> > +        self.assertEqual(
> > +            "Internal check failed: example check failure",
> > +            str(e))
> > +        self.assertTrue(e.internal_error)
> 
> I don't think this test needs the commentary: all our exceptions should
> be tested that they render successfully.

This is another change that wasn't actually made in this branch.

> > === modified file 'bzrlib/versionedfile.py'
[...]
> 
> 
> These methods are definately in the wrong place.

Agreed.  bzrlib/versionedfile.py is now unchanged from bzr.dev.

> In summary there are three methods:
> calculate_parents
> check_parents
> find_bad_ancestors
> 
> I don't know whats different between the last two - why are there three
> methods and not two?

This was an incomplete refactoring; find_bad_ancestors is now gone.

> In terms of the right home for this, VersionedFile is the wrong place,
> because it conflates delta storage with per-file graph caching. Knits
> are not able to decouple these without a disk change, but packs do
> decouple (at least on disk) this.
> 
> CommitBuilder currently is the right place to calculate what the
> per-file graph parents for a text version are; I'm not sure what the
> right place for versioned file is; but the fact that calculate_parents
> does not use self at all, and check_parents only uses self to get the
> parents from the knit, and to access calculate_parents is a pretty clear
> smell to me.

As mentioned above, I've pushed this back up to Repository, or rather to an
object returned by Repository.  Does that sound alright to you?

Current bundle attached.  Thanks for reviewing that very large diff!

-Andrew.

-------------- next part --------------
# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: andrew.bennetts at canonical.com-20071005030813-\
#   bdrd5k21y2kvyhqe
# target_branch: http://bazaar-vcs.org/bzr/bzr.dev
# testament_sha1: 03460c4b9dd8249498c392cea4add1d879e29dc2
# timestamp: 2007-10-05 13:10:32 +1000
# source_branch: http://people.ubuntu.com/~andrew/bzr/reconcile-check-\
#   heads
# base_revision_id: pqm at pqm.ubuntu.com-20071004215001-549ul8av89cwpnjp
# 
# Begin patch
=== added file 'bzrlib/tests/repository_implementations/helpers.py'
--- bzrlib/tests/repository_implementations/helpers.py	1970-01-01 00:00:00 +0000
+++ bzrlib/tests/repository_implementations/helpers.py	2007-09-24 03:45:21 +0000
@@ -0,0 +1,70 @@
+# Copyright (C) 2007 Canonical Ltd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+"""Helper classes for repository implementation tests."""
+
+
+from bzrlib import (
+    inventory,
+    revision as _mod_revision,
+    )
+from bzrlib.repofmt.knitrepo import RepositoryFormatKnit
+from bzrlib.tests.repository_implementations import TestCaseWithRepository
+from bzrlib.tests import TestNotApplicable
+
+
+class TestCaseWithBrokenRevisionIndex(TestCaseWithRepository):
+
+    def make_repo_with_extra_ghost_index(self):
+        """Make a corrupt repository.
+        
+        It will contain one revision, 'revision-id'.  The knit index will claim
+        that it has one parent, 'incorrect-parent', but the revision text will
+        claim it has no parents.
+
+        Note: only the *cache* of the knit index is corrupted.  Thus the
+        corruption will only last while the repository is locked.  For this
+        reason, the returned repo is locked.
+        """
+        if not isinstance(self.repository_format, RepositoryFormatKnit):
+            # XXX: Broken revision graphs can happen to weaves too, but they're
+            # pretty deprecated.  Ideally these tests should apply to any repo
+            # where repo.revision_graph_can_have_wrong_parents() is True, but
+            # at the moment we only know how to corrupt knit repos.
+            raise TestNotApplicable(
+                "%s isn't a knit format" % self.repository_format)
+
+        repo = self.make_repository('broken')
+        inv = inventory.Inventory(revision_id='revision-id')
+        inv.root.revision = 'revision-id'
+        repo.add_inventory('revision-id', inv, [])
+        revision = _mod_revision.Revision('revision-id',
+            committer='jrandom at example.com', timestamp=0,
+            inventory_sha1='', timezone=0, message='message', parent_ids=[])
+        repo.add_revision('revision-id',revision, inv)
+
+        # Change the knit index's record of the parents for 'revision-id' to
+        # claim it has a parent, 'incorrect-parent', that doesn't exist in this
+        # knit at all.
+        repo.lock_write()
+        self.addCleanup(repo.unlock)
+        rev_knit = repo._get_revision_vf()
+        index_cache = rev_knit._index._cache
+        cached_index_entry = list(index_cache['revision-id'])
+        cached_index_entry[4] = ['incorrect-parent']
+        index_cache['revision-id'] = tuple(cached_index_entry)
+        return repo
+

=== added file 'bzrlib/tests/repository_implementations/test_check.py'
--- bzrlib/tests/repository_implementations/test_check.py	1970-01-01 00:00:00 +0000
+++ bzrlib/tests/repository_implementations/test_check.py	2007-10-05 01:56:16 +0000
@@ -0,0 +1,79 @@
+# Copyright (C) 2007 Canonical Ltd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+"""Test operations that check the repository for corruption"""
+
+
+from bzrlib import (
+    errors,
+    inventory,
+    revision as _mod_revision,
+    )
+from bzrlib.repository import _RevisionTextVersionCache
+from bzrlib.tests import TestNotApplicable
+from bzrlib.tests.repository_implementations import TestCaseWithRepository
+from bzrlib.tests.repository_implementations.helpers import (
+    TestCaseWithBrokenRevisionIndex,
+    )
+
+
+class TestFindInconsistentRevisionParents(TestCaseWithBrokenRevisionIndex):
+
+    def test__find_inconsistent_revision_parents(self):
+        """_find_inconsistent_revision_parents finds revisions with broken
+        parents.
+        """
+        repo = self.make_repo_with_extra_ghost_index()
+        self.assertEqual(
+            [('revision-id', ['incorrect-parent'], [])],
+            list(repo._find_inconsistent_revision_parents()))
+
+    def test__check_for_inconsistent_revision_parents(self):
+        """_check_for_inconsistent_revision_parents raises BzrCheckError if
+        there are any revisions with inconsistent parents.
+        """
+        repo = self.make_repo_with_extra_ghost_index()
+        self.assertRaises(
+            errors.BzrCheckError,
+            repo._check_for_inconsistent_revision_parents)
+
+    def test__check_for_inconsistent_revision_parents_on_clean_repo(self):
+        """_check_for_inconsistent_revision_parents does nothing if there are
+        no broken revisions.
+        """
+        repo = self.make_repository('empty-repo')
+        if not repo.revision_graph_can_have_wrong_parents():
+            raise TestNotApplicable(
+                '%r cannot have corrupt revision index.' % repo)
+        repo._check_for_inconsistent_revision_parents()  # nothing happens
+
+    def test_check_reports_bad_ancestor(self):
+        repo = self.make_repo_with_extra_ghost_index()
+        # XXX: check requires a non-empty revision IDs list, but it ignores the
+        # contents of it!
+        check_object = repo.check(['ignored'])
+        check_object.report_results(verbose=False)
+        log = self._get_log(keep_log_file=True)
+        self.assertContainsRe(
+            log, '1 revisions have incorrect parents in the revision index')
+        check_object.report_results(verbose=True)
+        log = self._get_log(keep_log_file=True)
+        self.assertContainsRe(
+            log,
+            "revision-id has wrong parents in index: "
+            r"\['incorrect-parent'\] should be \[\]")
+

=== added file 'bzrlib/tests/repository_implementations/test_check_reconcile.py'
--- bzrlib/tests/repository_implementations/test_check_reconcile.py	1970-01-01 00:00:00 +0000
+++ bzrlib/tests/repository_implementations/test_check_reconcile.py	2007-10-03 10:54:07 +0000
@@ -0,0 +1,157 @@
+# Copyright (C) 2007 Canonical Ltd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+"""Tests that use BrokenRepoScenario objects.
+
+That is, tests for reconcile and check.
+"""
+
+
+import sha
+
+from bzrlib.inventory import Inventory, InventoryFile
+from bzrlib.revision import Revision
+from bzrlib.tests import TestNotApplicable
+from bzrlib.tests.repository_implementations import TestCaseWithRepository
+
+
+class TestFileParentReconciliation(TestCaseWithRepository):
+    """Tests for how reconcile corrects errors in parents of file versions."""
+
+    def make_populated_repository(self, factory):
+        """Create a new repository populated by the given factory."""
+        repo = self.make_repository('broken-repo')
+        repo.lock_write()
+        try:
+            repo.start_write_group()
+            try:
+                factory(repo)
+                repo.commit_write_group()
+                return repo
+            except:
+                repo.abort_write_group()
+                raise
+        finally:
+            repo.unlock()
+
+    def add_revision(self, repo, revision_id, inv, parent_ids):
+        """Add a revision with a given inventory and parents to a repository.
+        
+        :param repo: a repository.
+        :param revision_id: the revision ID for the new revision.
+        :param inv: an inventory (such as created by
+            `make_one_file_inventory`).
+        :param parent_ids: the parents for the new revision.
+        """
+        inv.revision_id = revision_id
+        inv.root.revision = revision_id
+        repo.add_inventory(revision_id, inv, parent_ids)
+        revision = Revision(revision_id, committer='jrandom at example.com',
+            timestamp=0, inventory_sha1='', timezone=0, message='foo',
+            parent_ids=parent_ids)
+        repo.add_revision(revision_id,revision, inv)
+
+    def make_one_file_inventory(self, repo, revision, parents,
+                                inv_revision=None, root_revision=None):
+        """Make an inventory containing a version of a file with ID 'a-file'.
+
+        The file's ID will be 'a-file', and its filename will be 'a file name',
+        stored at the tree root.
+
+        :param repo: a repository to add the new file version to.
+        :param revision: the revision ID of the new inventory.
+        :param parents: the parents for this revision of 'a-file'.
+        :param inv_revision: if not None, the revision ID to store in the
+            inventory entry.  Otherwise, this defaults to revision.
+        :param root_revision: if not None, the inventory's root.revision will
+            be set to this.
+        """
+        inv = Inventory(revision_id=revision)
+        if root_revision is not None:
+            inv.root.revision = root_revision
+        file_id = 'a-file-id'
+        entry = InventoryFile(file_id, 'a file name', 'TREE_ROOT')
+        if inv_revision is not None:
+            entry.revision = inv_revision
+        else:
+            entry.revision = revision
+        entry.text_size = 0
+        file_contents = '%sline\n' % entry.revision
+        entry.text_sha1 = sha.sha(file_contents).hexdigest()
+        inv.add(entry)
+        vf = repo.weave_store.get_weave_or_empty(file_id,
+                                                 repo.get_transaction())
+        vf.add_lines(revision, parents, [file_contents])
+        return inv
+
+    def require_repo_suffers_text_parent_corruption(self, repo):
+        if not repo._reconcile_fixes_text_parents:
+            raise TestNotApplicable(
+                    "Format does not support text parent reconciliation")
+
+    def file_parents(self, repo, revision_id):
+        return repo.weave_store.get_weave('a-file-id',
+            repo.get_transaction()).get_parents(revision_id)
+
+    def assertParentsMatch(self, expected_parents_for_versions, repo,
+                           when_description):
+        for expected_parents, version in expected_parents_for_versions:
+            found_parents = self.file_parents(repo, version)
+            self.assertEqual(expected_parents, found_parents,
+                "Expected version %s of a-file-id to have parents %s %s "
+                "reconcile, but it has %s instead."
+                % (version, expected_parents, when_description, found_parents))
+
+    def shas_for_versions_of_file(self, repo, versions):
+        """Get the SHA-1 hashes of the versions of 'a-file' in the repository.
+        
+        :param repo: the repository to get the hashes from.
+        :param versions: a list of versions to get hashes for.
+
+        :returns: A dict of `{version: hash}`.
+        """
+        vf = repo.weave_store.get_weave('a-file-id', repo.get_transaction())
+        return dict((v, vf.get_sha1(v)) for v in versions)
+
+    def test_reconcile_behaviour(self):
+        """Populate a repository and reconcile it, verifying the state before
+        and after.
+        """
+        scenario = self.scenario_class(self)
+        repo = self.make_populated_repository(scenario.populate_repository)
+        self.require_repo_suffers_text_parent_corruption(repo)
+        self.assertParentsMatch(scenario.populated_parents(), repo, 'before')
+        vf_shas = self.shas_for_versions_of_file(repo, scenario.all_versions())
+        result = repo.reconcile(thorough=True)
+        self.assertParentsMatch(scenario.corrected_parents(), repo, 'after')
+        # The contents of the versions in the versionedfile should be the same
+        # after the reconcile.
+        self.assertEqual(
+            vf_shas,
+            self.shas_for_versions_of_file(repo, scenario.all_versions()))
+
+    def test_check_behaviour(self):
+        """Populate a repository and check it, and verify the output."""
+        scenario = self.scenario_class(self)
+        repo = self.make_populated_repository(scenario.populate_repository)
+        self.require_repo_suffers_text_parent_corruption(repo)
+        check_result = repo.check()
+        check_result.report_results(verbose=True)
+        for pattern in scenario.check_regexes():
+            self.assertContainsRe(
+                self._get_log(keep_log_file=True),
+                pattern)
+

=== modified file 'NEWS'
--- NEWS	2007-10-04 17:10:53 +0000
+++ NEWS	2007-10-05 02:04:58 +0000
@@ -447,6 +447,12 @@
      include them as Concepts within the User Reference.
      (Paul Moore, Ian Clatworthy)
 
+    * ``check`` can detect versionedfile parent references that are
+      inconsistent with revision and inventory info, and ``reconcile`` can fix
+      them.  These faulty references were generated by 0.8-era releases,
+      so repositories which were manipulated by old bzrs should be
+      checked, and possibly reconciled ASAP.  (Aaron Bentley)
+
   API BREAKS:
 
    * ``Branch.append_revision`` is removed altogether; please use 

=== modified file 'bzrlib/builtins.py'
--- bzrlib/builtins.py	2007-10-04 15:35:37 +0000
+++ bzrlib/builtins.py	2007-10-05 02:04:58 +0000
@@ -2338,6 +2338,22 @@
 
     This command checks various invariants about the branch storage to
     detect data corruption or bzr bugs.
+
+    Output fields:
+
+        revisions: This is just the number of revisions checked.  It doesn't
+            indicate a problem.
+        versionedfiles: This is just the number of versionedfiles checked.  It
+            doesn't indicate a problem.
+        unreferenced ancestors: Texts that are ancestors of other texts, but
+            are not properly referenced by the revision ancestry.  This is a
+            subtle problem that Bazaar can work around.
+        unique file texts: This is the total number of unique file contents
+            seen in the checked revisions.  It does not indicate a problem.
+        repeated file texts: This is the total number of repeated texts seen
+            in the checked revisions.  Texts can be repeated when their file
+            entries are modified, but the file contents are not.  It does not
+            indicate a problem.
     """
 
     _see_also = ['reconcile']
@@ -2347,8 +2363,7 @@
     def run(self, branch=None, verbose=False):
         from bzrlib.check import check
         if branch is None:
-            tree = WorkingTree.open_containing()[0]
-            branch = tree.branch
+            branch = Branch.open_containing('.')[0]
         else:
             branch = Branch.open(branch)
         check(branch, verbose)

=== modified file 'bzrlib/bundle/serializer/v4.py'
--- bzrlib/bundle/serializer/v4.py	2007-09-25 07:08:07 +0000
+++ bzrlib/bundle/serializer/v4.py	2007-10-03 07:45:17 +0000
@@ -330,7 +330,7 @@
 
     def write_files(self):
         """Write bundle records for all revisions of all files"""
-        for vf, file_id, revision_ids in self.iter_file_revisions_aggressive():
+        for vf, file_id, revision_ids in self.iter_file_revisions():
             self.add_mp_records('file', file_id, vf, revision_ids)
 
     def write_revisions(self):

=== modified file 'bzrlib/check.py'
--- bzrlib/check.py	2007-08-22 22:32:23 +0000
+++ bzrlib/check.py	2007-10-05 02:19:09 +0000
@@ -32,9 +32,12 @@
 # raising them.  If there's more than one exception it'd be good to see them
 # all.
 
+from bzrlib import errors
+from bzrlib import repository as _mod_repository
+from bzrlib import revision
 from bzrlib.errors import BzrCheckError
 import bzrlib.ui
-from bzrlib.trace import note
+from bzrlib.trace import log_error, note, mutter
 
 class Check(object):
     """Check a repository"""
@@ -53,47 +56,64 @@
         # maps (file-id, version) -> sha1; used by InventoryFile._check
         self.checked_texts = {}
         self.checked_weaves = {}
+        self.revision_versions = _mod_repository._RevisionTextVersionCache(
+            self.repository)
+        self.unreferenced_ancestors = set()
+        self.inconsistent_parents = []
 
     def check(self):
         self.repository.lock_read()
         self.progress = bzrlib.ui.ui_factory.nested_progress_bar()
         try:
-            self.progress.update('retrieving inventory', 0, 0)
+            self.progress.update('retrieving inventory', 0, 2)
             # do not put in init, as it should be done with progess,
             # and inside the lock.
             self.inventory_weave = self.repository.get_inventory_weave()
+            self.progress.update('checking revision graph', 1)
+            self.check_revision_graph()
             self.plan_revisions()
             revno = 0
-            self.check_weaves()
             while revno < len(self.planned_revisions):
                 rev_id = self.planned_revisions[revno]
                 self.progress.update('checking revision', revno,
                                      len(self.planned_revisions))
                 revno += 1
                 self.check_one_rev(rev_id)
+            # check_weaves is done after the revision scan so that
+            # revision_versions is pre-populated
+            self.check_weaves()
         finally:
             self.progress.finished()
             self.repository.unlock()
 
+    def check_revision_graph(self):
+        if not self.repository.revision_graph_can_have_wrong_parents():
+            # This check is not necessary.
+            self.revs_with_bad_parents_in_index = None
+            return
+        bad_revisions = self.repository._find_inconsistent_revision_parents()
+        self.revs_with_bad_parents_in_index = list(bad_revisions)
+
     def plan_revisions(self):
         repository = self.repository
-        self.planned_revisions = set(repository.all_revision_ids())
+        self.planned_revisions = repository.all_revision_ids()
         self.progress.clear()
         inventoried = set(self.inventory_weave.versions())
-        awol = self.planned_revisions - inventoried
+        awol = set(self.planned_revisions) - inventoried
         if len(awol) > 0:
             raise BzrCheckError('Stored revisions missing from inventory'
                 '{%s}' % ','.join([f for f in awol]))
-        self.planned_revisions = list(self.planned_revisions)
 
     def report_results(self, verbose):
         note('checked repository %s format %s',
              self.repository.bzrdir.root_transport,
              self.repository._format)
         note('%6d revisions', self.checked_rev_cnt)
+        note('%6d file-ids', len(self.checked_weaves))
         note('%6d unique file texts', self.checked_text_cnt)
         note('%6d repeated file texts', self.repeated_text_cnt)
-        note('%6d weaves', len(self.checked_weaves))
+        note('%6d unreferenced text ancestors',
+             len(self.unreferenced_ancestors))
         if self.missing_inventory_sha_cnt:
             note('%6d revisions are missing inventory_sha1',
                  self.missing_inventory_sha_cnt)
@@ -113,6 +133,29 @@
                     note('      %s should be in the ancestry for:', link)
                     for linker in linkers:
                         note('       * %s', linker)
+            if verbose:
+                for file_id, revision_id in self.unreferenced_ancestors:
+                    log_error('unreferenced ancestor: {%s} in %s', revision_id,
+                        file_id)
+        if len(self.inconsistent_parents):
+            note('%6d inconsistent parents', len(self.inconsistent_parents))
+            if verbose:
+                for info in self.inconsistent_parents:
+                    revision_id, file_id, found_parents, correct_parents = info
+                    note('      * %s version %s has parents %r '
+                         'but should have %r'
+                         % (file_id, revision_id, found_parents,
+                             correct_parents))
+        if self.revs_with_bad_parents_in_index:
+            note('%6d revisions have incorrect parents in the revision index',
+                 len(self.revs_with_bad_parents_in_index))
+            if verbose:
+                for item in self.revs_with_bad_parents_in_index:
+                    revision_id, index_parents, actual_parents = item
+                    note(
+                        '       %s has wrong parents in index: '
+                        '%r should be %r',
+                        revision_id, index_parents, actual_parents)
 
     def check_one_rev(self, rev_id):
         """Check one revision.
@@ -157,18 +200,33 @@
         if self.repository.weave_store.listable():
             weave_ids = list(self.repository.weave_store)
             n_weaves = len(weave_ids) + 1
-        self.progress.update('checking weave', 0, n_weaves)
+        self.progress.update('checking versionedfile', 0, n_weaves)
         self.inventory_weave.check(progress_bar=self.progress)
+        files_in_revisions = {}
+        revisions_of_files = {}
         for i, weave_id in enumerate(weave_ids):
-            self.progress.update('checking weave', i, n_weaves)
+            self.progress.update('checking versionedfile', i, n_weaves)
             w = self.repository.weave_store.get_weave(weave_id,
                     self.repository.get_transaction())
             # No progress here, because it looks ugly.
             w.check()
+
+            weave_checker = self.repository.get_versioned_file_checker(
+                self.planned_revisions, self.revision_versions)
+            result = weave_checker.check_file_version_parents(w, weave_id)
+
+            for revision_id, (weave_parents,correct_parents) in result.items():
+                self.inconsistent_parents.append(
+                    (revision_id, weave_id, weave_parents, correct_parents))
+                unreferenced_parents = set(weave_parents)-set(correct_parents)
+                for unreferenced_parent in unreferenced_parents:
+                    self.unreferenced_ancestors.add(
+                        (weave_id, unreferenced_parent))
             self.checked_weaves[weave_id] = True
 
     def _check_revision_tree(self, rev_id):
         tree = self.repository.revision_tree(rev_id)
+        self.revision_versions.add_revision_text_versions(tree)
         inv = tree.inventory
         seen_ids = {}
         for file_id in inv:
@@ -204,3 +262,6 @@
         branch.unlock()
     branch_result.report_results(verbose)
     repo_result.report_results(verbose)
+
+
+

=== modified file 'bzrlib/inventory.py'
--- bzrlib/inventory.py	2007-10-03 05:04:42 +0000
+++ bzrlib/inventory.py	2007-10-03 07:45:17 +0000
@@ -570,8 +570,10 @@
         if t in checker.checked_texts:
             prev_sha = checker.checked_texts[t]
             if prev_sha != self.text_sha1:
-                raise BzrCheckError('mismatched sha1 on {%s} in {%s}' %
-                                    (self.file_id, tree_revision_id))
+                raise BzrCheckError(
+                    'mismatched sha1 on {%s} in {%s} (%s != %s) %r' %
+                    (self.file_id, tree_revision_id, prev_sha, self.text_sha1,
+                     t))
             else:
                 checker.repeated_text_cnt += 1
                 return

=== modified file 'bzrlib/reconcile.py'
--- bzrlib/reconcile.py	2007-09-27 02:18:12 +0000
+++ bzrlib/reconcile.py	2007-10-05 02:19:09 +0000
@@ -17,17 +17,19 @@
 """Reconcilers are able to fix some potential data errors in a branch."""
 
 
-__all__ = [
-    'KnitReconciler',
-    'reconcile',
-    'Reconciler',
-    'RepoReconciler',
-    ]
-
-
+__all__ = ['reconcile', 'Reconciler', 'RepoReconciler', 'KnitReconciler']
+
+
+from bzrlib import (
+    errors,
+    graph,
+    ui,
+    repository,
+    )
+from bzrlib import errors
 from bzrlib import ui
-from bzrlib.trace import mutter
-from bzrlib.tsort import TopoSorter
+from bzrlib.trace import mutter, note
+from bzrlib.tsort import TopoSorter, topo_sort
 
 
 def reconcile(dir, other=None):
@@ -76,7 +78,13 @@
         repo_reconciler = self.repo.reconcile(thorough=True)
         self.inconsistent_parents = repo_reconciler.inconsistent_parents
         self.garbage_inventories = repo_reconciler.garbage_inventories
-        self.pb.note('Reconciliation complete.')
+        if repo_reconciler.aborted:
+            self.pb.note(
+                'Reconcile aborted: revision index has inconsistent parents.')
+            self.pb.note(
+                'Run "bzr check" for more details.')
+        else:
+            self.pb.note('Reconciliation complete.')
 
 
 class RepoReconciler(object):
@@ -99,6 +107,7 @@
         """
         self.garbage_inventories = 0
         self.inconsistent_parents = 0
+        self.aborted = False
         self.repo = repo
         self.thorough = thorough
 
@@ -283,9 +292,14 @@
     def _reconcile_steps(self):
         """Perform the steps to reconcile this repository."""
         if self.thorough:
-            self._load_indexes()
+            try:
+                self._load_indexes()
+            except errors.BzrCheckError:
+                self.aborted = True
+                return
             # knits never suffer this
             self._gc_inventory()
+            self._fix_text_parents()
 
     def _load_indexes(self):
         """Load indexes for the reconciliation."""
@@ -293,6 +307,7 @@
         self.pb.update('Reading indexes.', 0, 2)
         self.inventory = self.repo.get_inventory_weave()
         self.pb.update('Reading indexes.', 1, 2)
+        self.repo._check_for_inconsistent_revision_parents()
         self.revisions = self.repo._revision_store.get_revision_file(self.transaction)
         self.pb.update('Reading indexes.', 2, 2)
 
@@ -345,3 +360,44 @@
         self.garbage_inventories = len(garbage)
         for revision_id in garbage:
             mutter('Garbage inventory {%s} found.', revision_id)
+
+    def _fix_text_parents(self):
+        """Fix bad versionedfile parent entries.
+
+        It is possible for the parents entry in a versionedfile entry to be
+        inconsistent with the values in the revision and inventory.
+
+        This method finds entries with such inconsistencies, corrects their
+        parent lists, and replaces the versionedfile with a corrected version.
+        """
+        transaction = self.repo.get_transaction()
+        revision_versions = repository._RevisionTextVersionCache(self.repo)
+        for num, file_id in enumerate(self.repo.weave_store):
+            self.pb.update('Fixing text parents', num,
+                           len(self.repo.weave_store))
+            vf = self.repo.weave_store.get_weave(file_id, transaction)
+            vf_checker = self.repo.get_versioned_file_checker(
+                self.revisions.versions(), revision_versions)
+            versions_with_bad_parents = vf_checker.check_file_version_parents(
+                vf, file_id)
+            if len(versions_with_bad_parents) == 0:
+                continue
+            self._fix_text_parent(file_id, vf, versions_with_bad_parents)
+
+    def _fix_text_parent(self, file_id, vf, versions_with_bad_parents):
+        """Fix bad versionedfile entries in a single versioned file."""
+        new_vf = self.repo.weave_store.get_empty('temp:%s' % file_id,
+            self.transaction)
+        new_parents = {}
+        for version in vf.versions():
+            if version in versions_with_bad_parents:
+                parents = versions_with_bad_parents[version][1]
+            else:
+                parents = vf.get_parents(version)
+            new_parents[version] = parents
+        for version in topo_sort(new_parents.items()):
+            new_vf.add_lines(version, new_parents[version],
+                             vf.get_lines(version))
+        self.repo.weave_store.copy(new_vf, file_id, self.transaction)
+        self.repo.weave_store.delete('temp:%s' % file_id, self.transaction)
+

=== modified file 'bzrlib/remote.py'
--- bzrlib/remote.py	2007-10-03 05:25:50 +0000
+++ bzrlib/remote.py	2007-10-05 02:04:58 +0000
@@ -259,6 +259,7 @@
         self._leave_lock = False
         # for tests
         self._reconcile_does_inventory_gc = True
+        self._reconcile_fixes_text_parents = True
         self.base = self.bzrdir.transport.base
 
     def __str__(self):
@@ -344,6 +345,7 @@
         
     def get_graph(self, other_repository=None):
         """Return the graph for this repository format"""
+        self._ensure_real()
         return self._real_repository.get_graph(other_repository)
 
     def gather_stats(self, revid=None, committers=None):
@@ -634,6 +636,11 @@
         self._ensure_real()
         return self._real_repository.fileids_altered_by_revision_ids(revision_ids)
 
+    def get_versioned_file_checker(self, revisions, revision_versions_cache):
+        self._ensure_real()
+        return self._real_repository.get_versioned_file_checker(
+            revisions, revision_versions_cache)
+        
     def iter_files_bytes(self, desired_files):
         """See Repository.iter_file_bytes.
         """
@@ -689,9 +696,9 @@
         return self._real_repository.get_revision_reconcile(revision_id)
 
     @needs_read_lock
-    def check(self, revision_ids):
+    def check(self, revision_ids=None):
         self._ensure_real()
-        return self._real_repository.check(revision_ids)
+        return self._real_repository.check(revision_ids=revision_ids)
 
     def copy_content_into(self, destination, revision_id=None):
         self._ensure_real()
@@ -772,6 +779,19 @@
         self._ensure_real()
         return self._real_repository.has_signature_for_revision_id(revision_id)
 
+    def revision_graph_can_have_wrong_parents(self):
+        # The answer depends on the remote repo format.
+        self._ensure_real()
+        return self._real_repository.revision_graph_can_have_wrong_parents()
+
+    def _find_inconsistent_revision_parents(self):
+        self._ensure_real()
+        return self._real_repository._find_inconsistent_revision_parents()
+
+    def _check_for_inconsistent_revision_parents(self):
+        self._ensure_real()
+        return self._real_repository._check_for_inconsistent_revision_parents()
+
 
 class RemoteBranchLockableFiles(LockableFiles):
     """A 'LockableFiles' implementation that talks to a smart server.

=== modified file 'bzrlib/repofmt/knitrepo.py'
--- bzrlib/repofmt/knitrepo.py	2007-09-27 06:54:37 +0000
+++ bzrlib/repofmt/knitrepo.py	2007-10-05 03:05:35 +0000
@@ -78,6 +78,12 @@
 
     _serializer = xml5.serializer_v5
 
+    def __init__(self, _format, a_bzrdir, control_files, _revision_store,
+                 control_store, text_store):
+        MetaDirRepository.__init__(self, _format, a_bzrdir, control_files,
+                                   _revision_store, control_store, text_store)
+        self._reconcile_fixes_text_parents = True
+
     def _warn_if_deprecated(self):
         # This class isn't deprecated
         pass
@@ -232,6 +238,36 @@
     def _make_parents_provider(self):
         return _KnitParentsProvider(self._get_revision_vf())
 
+    def _find_inconsistent_revision_parents(self):
+        """Find revisions with different parent lists in the revision object
+        and in the index graph.
+
+        :returns: an iterator yielding tuples of (revison-id, parents-in-index,
+            parents-in-revision).
+        """
+        vf = self._get_revision_vf()
+        index_versions = vf.versions()
+        for index_version in index_versions:
+            parents_according_to_index = vf._index.get_parents_with_ghosts(
+                index_version)
+            revision = self._revision_store.get_revision(index_version,
+                self.get_transaction())
+            parents_according_to_revision = revision.parent_ids
+            if parents_according_to_index != parents_according_to_revision:
+                yield (index_version, parents_according_to_index,
+                    parents_according_to_revision)
+
+    def _check_for_inconsistent_revision_parents(self):
+        inconsistencies = list(self._find_inconsistent_revision_parents())
+        if inconsistencies:
+            raise errors.BzrCheckError(
+                "Revision knit has inconsistent parents.")
+
+    def revision_graph_can_have_wrong_parents(self):
+        # The revision.kndx could potentially claim a revision has a different
+        # parent to the revision text.
+        return True
+
 
 class KnitRepository3(KnitRepository):
 

=== modified file 'bzrlib/repofmt/weaverepo.py'
--- bzrlib/repofmt/weaverepo.py	2007-09-27 21:11:38 +0000
+++ bzrlib/repofmt/weaverepo.py	2007-10-05 00:52:37 +0000
@@ -199,6 +199,11 @@
         """Returns the policy for making working trees on new branches."""
         return True
 
+    def revision_graph_can_have_wrong_parents(self):
+        # XXX: This is an old format that we don't support full checking on, so
+        # just claim that checking for this inconsistency is not required.
+        return False
+
 
 class WeaveMetaDirRepository(MetaDirRepository):
     """A subclass of MetaDirRepository to set weave specific policy."""
@@ -308,6 +313,11 @@
                         pending.add(revision_id)
             return result
 
+    def revision_graph_can_have_wrong_parents(self):
+        # XXX: This is an old format that we don't support full checking on, so
+        # just claim that checking for this inconsistency is not required.
+        return False
+
 
 class PreSplitOutRepositoryFormat(RepositoryFormat):
     """Base class for the pre split out repository formats."""
@@ -501,7 +511,6 @@
         """See RepositoryFormat._get_text_store()."""
         return self._get_versioned_file_store('weaves', transport, control_files)
 
-
 class RepositoryFormat7(MetaDirRepositoryFormat):
     """Bzr repository 7.
 

=== modified file 'bzrlib/repository.py'
--- bzrlib/repository.py	2007-10-03 06:15:06 +0000
+++ bzrlib/repository.py	2007-10-05 02:19:09 +0000
@@ -621,6 +621,7 @@
         self.weave_store = text_store
         # for tests
         self._reconcile_does_inventory_gc = True
+        self._reconcile_fixes_text_parents = False
         # not right yet - should be more semantically clear ? 
         # 
         self.control_store = control_store
@@ -1451,6 +1452,9 @@
                 [parents_provider, other_repository._make_parents_provider()])
         return graph.Graph(parents_provider)
 
+    def get_versioned_file_checker(self, revisions, revision_versions_cache):
+        return WeaveChecker(revisions, revision_versions_cache, self)
+
     @needs_write_lock
     def set_make_working_trees(self, new_value):
         """Set the policy flag for making working trees when creating branches.
@@ -1488,7 +1492,7 @@
                                                        self.get_transaction())
 
     @needs_read_lock
-    def check(self, revision_ids):
+    def check(self, revision_ids=None):
         """Check consistency of all history of given revision_ids.
 
         Different repository implementations should override _check().
@@ -1496,10 +1500,10 @@
         :param revision_ids: A non-empty list of revision_ids whose ancestry
              will be checked.  Typically the last revision_id of a branch.
         """
-        if not revision_ids:
-            raise ValueError("revision_ids must be non-empty in %s.check" 
-                    % (self,))
-        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
+        if revision_ids is not None:
+            symbol_versioning.warn('revision_ids should not be supplied to'
+                ' Repostiory.check, as of bzr 0.92.',
+                 DeprecationWarning, stacklevel=3)
         return self._check(revision_ids)
 
     def _check(self, revision_ids):
@@ -1533,9 +1537,18 @@
                     revision_id.decode('ascii')
                 except UnicodeDecodeError:
                     raise errors.NonAsciiRevisionId(method, self)
-
-
-
+    
+    def revision_graph_can_have_wrong_parents(self):
+        """Is it possible for this repository to have a revision graph with
+        incorrect parents?
+
+        If True, then this repository must also implement
+        _find_inconsistent_revision_parents so that check and reconcile can
+        check for inconsistencies before proceeding with other checks that may
+        depend on the revision index being consistent.
+        """
+        raise NotImplementedError(self.revision_graph_can_have_wrong_parents)
+        
 # remove these delegates a while after bzr 0.15
 def __make_delegated(name, from_module):
     def _deprecated_repository_forwarder():
@@ -2434,3 +2447,79 @@
     if _unescape_re is None:
         _unescape_re = re.compile('\&([^;]*);')
     return _unescape_re.sub(_unescaper, data)
+
+
+class _RevisionTextVersionCache(object):
+    """A cache of the versionedfile versions for revision and file-id"""
+
+    def __init__(self, repository):
+        self.repository = repository
+        self.revision_versions = {}
+
+    def add_revision_text_versions(self, tree):
+        """Cache text version data from the supplied revision tree"""
+        inv_revisions = {}
+        for path, entry in tree.iter_entries_by_dir():
+            inv_revisions[entry.file_id] = entry.revision
+        self.revision_versions[tree.get_revision_id()] = inv_revisions
+        return inv_revisions
+
+    def get_text_version(self, file_id, revision_id):
+        """Determine the text version for a given file-id and revision-id"""
+        try:
+            inv_revisions = self.revision_versions[revision_id]
+        except KeyError:
+            tree = self.repository.revision_tree(revision_id)
+            inv_revisions = self.add_revision_text_versions(tree)
+        return inv_revisions.get(file_id)
+
+
+class WeaveChecker(object):
+
+    def __init__(self, planned_revisions, revision_versions, repository):
+        self.planned_revisions = planned_revisions
+        self.revision_versions = revision_versions
+        self.repository = repository
+    
+    def calculate_file_version_parents(self, revision_id, file_id):
+        text_revision = self.revision_versions.get_text_version(
+            file_id, revision_id)
+        if text_revision is None:
+            return None
+        parents_of_text_revision = self.repository.get_parents(
+            [text_revision])[0]
+        parents_from_inventories = []
+        for parent in parents_of_text_revision:
+            if parent == _mod_revision.NULL_REVISION:
+                continue
+            try:
+                inventory = self.repository.get_inventory(parent)
+            except errors.RevisionNotPresent:
+                pass
+            else:
+                introduced_in = inventory[file_id].revision
+                parents_from_inventories.append(introduced_in)
+        mutter('%r:%r introduced in: %r',
+               file_id, revision_id, parents_from_inventories)
+        graph = self.repository.get_graph()
+        heads = set(graph.heads(parents_from_inventories))
+        mutter('    heads: %r', heads)
+        new_parents = []
+        for parent in parents_from_inventories:
+            if parent in heads and parent not in new_parents:
+                new_parents.append(parent)
+        return new_parents
+
+    def check_file_version_parents(self, weave, file_id):
+        result = {}
+        for num, revision_id in enumerate(self.planned_revisions):
+            correct_parents = self.calculate_file_version_parents(
+                revision_id, file_id)
+            if correct_parents is None:
+                continue
+            text_revision = self.revision_versions.get_text_version(
+                file_id, revision_id)
+            knit_parents = weave.get_parents(text_revision)
+            if correct_parents != knit_parents:
+                result[revision_id] = (knit_parents, correct_parents)
+        return result

=== modified file 'bzrlib/tests/repository_implementations/__init__.py'
--- bzrlib/tests/repository_implementations/__init__.py	2007-08-17 05:16:14 +0000
+++ bzrlib/tests/repository_implementations/__init__.py	2007-10-05 03:05:35 +0000
@@ -30,9 +30,14 @@
 from bzrlib.repofmt import (
     weaverepo,
     )
+from bzrlib.remote import RemoteBzrDirFormat, RemoteRepositoryFormat
+from bzrlib.smart.server import (
+    SmartTCPServer_for_testing,
+    ReadonlySmartTCPServer_for_testing,
+    )
 from bzrlib.tests import (
-                          adapt_modules,
                           default_transport,
+                          iter_suite_tests,
                           TestScenarioApplier,
                           TestLoader,
                           TestSuite,
@@ -94,51 +99,488 @@
                 relpath, format=format)
 
 
+class BrokenRepoScenario(object):
+    """Base class for defining scenarios for testing check and reconcile.
+
+    A subclass needs to define the following methods:
+        :populate_repository: a method to use to populate a repository with
+            sample revisions, inventories and file versions.
+        :all_versions: all the versions in repository.  run_test verifies
+            that the text of each of these versions of the file is unchanged
+            by the reconcile.
+        :populated_parents: a list of (parents list, revision).  Each version
+            of the file is verified to have the given parents before running
+            the reconcile.  i.e. this is used to assert that the repo from the
+            factory is what we expect.
+        :corrected_parents: a list of (parents list, revision).  Each version
+            of the file is verified to have the given parents after the
+            reconcile.  i.e. this is used to assert that reconcile made the
+            changes we expect it to make.
+    """
+
+    def __init__(self, test_case):
+        self.test_case = test_case
+
+    def make_one_file_inventory(self, repo, revision, parents,
+                                inv_revision=None, root_revision=None):
+        return self.test_case.make_one_file_inventory(
+            repo, revision, parents, inv_revision=inv_revision,
+            root_revision=root_revision)
+
+    def add_revision(self, repo, revision_id, inv, parent_ids):
+        return self.test_case.add_revision(repo, revision_id, inv, parent_ids)
+
+
+class UndamagedRepositoryScenario(BrokenRepoScenario):
+    """A scenario where the repository has no damage.
+
+    It has a single revision, 'rev1a', with a single file.
+    """
+
+    def all_versions(self):
+        return ['rev1a']
+
+    def populated_parents(self):
+        return [([], 'rev1a')]
+
+    def corrected_parents(self):
+        # Same as the populated parents, because there was nothing wrong.
+        return self.populated_parents()
+
+    def check_regexes(self):
+        return ["0 unreferenced text ancestors"]
+
+    def populate_repository(self, repo):
+        # make rev1a: A well-formed revision, containing 'a-file'
+        inv = self.make_one_file_inventory(
+            repo, 'rev1a', [], root_revision='rev1a')
+        self.add_revision(repo, 'rev1a', inv, [])
+
+
+class FileParentIsNotInRevisionAncestryScenario(BrokenRepoScenario):
+    """A scenario where a revision 'rev2' has 'a-file' with a
+    parent 'rev1b' that is not in the revision ancestry.
+    
+    Reconcile should remove 'rev1b' from the parents list of 'a-file' in
+    'rev2', preserving 'rev1a' as a parent.
+    """
+
+    def all_versions(self):
+        return ['rev1a', 'rev1b', 'rev2']
+
+    def populated_parents(self):
+        return [
+            ([], 'rev1a'),
+            ([], 'rev1b'),
+            (['rev1a', 'rev1b'], 'rev2')]
+
+    def corrected_parents(self):
+        return [
+            ([], 'rev1a'),
+            ([], 'rev1b'),
+            (['rev1a'], 'rev2')]
+
+    def check_regexes(self):
+        return [r"\* a-file-id version rev2 has parents \['rev1a', 'rev1b'\] "
+                r"but should have \['rev1a'\]",
+                "1 unreferenced text ancestors",
+                ]
+
+    def populate_repository(self, repo):
+        # make rev1a: A well-formed revision, containing 'a-file'
+        inv = self.make_one_file_inventory(
+            repo, 'rev1a', [], root_revision='rev1a')
+        self.add_revision(repo, 'rev1a', inv, [])
+
+        # make rev1b, which has no Revision, but has an Inventory, and
+        # a-file
+        inv = self.make_one_file_inventory(
+            repo, 'rev1b', [], root_revision='rev1b')
+        repo.add_inventory('rev1b', inv, [])
+
+        # make rev2, with a-file.
+        # a-file has 'rev1b' as an ancestor, even though this is not
+        # mentioned by 'rev1a', making it an unreferenced ancestor
+        inv = self.make_one_file_inventory(
+            repo, 'rev2', ['rev1a', 'rev1b'])
+        self.add_revision(repo, 'rev2', inv, ['rev1a'])
+
+
+class FileParentHasInaccessibleInventoryScenario(BrokenRepoScenario):
+    """A scenario where a revision 'rev3' containing 'a-file' modified in
+    'rev3', and with a parent which is in the revision ancestory, but whose
+    inventory cannot be accessed at all.
+
+    Reconcile should remove the file version parent whose inventory is
+    inaccessbile (i.e. remove 'rev1c' from the parents of a-file's rev3).
+    """
+
+    def all_versions(self):
+        return ['rev2', 'rev3']
+
+    def populated_parents(self):
+        return [
+            ([], 'rev2'),
+            (['rev1c'], 'rev3')]
+
+    def corrected_parents(self):
+        return [
+            ([], 'rev2'),
+            ([], 'rev3')]
+
+    def check_regexes(self):
+        return [r"\* a-file-id version rev3 has parents "
+                r"\['rev1c'\] but should have \[\]",
+                # Also check reporting of unreferenced ancestors
+                r"unreferenced ancestor: {rev1c} in a-file-id",
+                ]
+
+    def populate_repository(self, repo):
+        # make rev2, with a-file
+        # a-file is sane
+        inv = self.make_one_file_inventory(repo, 'rev2', [])
+        self.add_revision(repo, 'rev2', inv, [])
+
+        # make ghost revision rev1c, with a version of a-file present so
+        # that we generate a knit delta against this version.  In real life
+        # the ghost might never have been present or rev3 might have been
+        # generated against a revision that was present at the time.  So
+        # currently we have the full history of a-file present even though
+        # the inventory and revision objects are not.
+        self.make_one_file_inventory(repo, 'rev1c', [])
+
+        # make rev3 with a-file
+        # a-file refers to 'rev1c', which is a ghost in this repository, so
+        # a-file cannot have rev1c as its ancestor.
+        inv = self.make_one_file_inventory(repo, 'rev3', ['rev1c'])
+        self.add_revision(repo, 'rev3', inv, ['rev1c', 'rev1a'])
+
+
+class FileParentsNotReferencedByAnyInventoryScenario(BrokenRepoScenario):
+    """A scenario where a repository with file 'a-file' which has extra
+    per-file versions that are not referenced by any inventory (even though
+    they have the same ID as actual revisions).  The inventory of 'rev2'
+    references 'rev1a' of 'a-file', but there is a 'rev2' of 'some-file' stored
+    and erroneously referenced by later per-file versions (revisions 'rev4' and
+    'rev5').
+
+    Reconcile should remove the file parents that are not referenced by any
+    inventory.
+    """
+
+    def all_versions(self):
+        return ['rev1a', 'rev2', 'rev4', 'rev2b', 'rev4', 'rev2c', 'rev5']
+
+    def populated_parents(self):
+        return [
+            (['rev2'], 'rev3'),
+            (['rev2'], 'rev4'),
+            (['rev2', 'rev2c'], 'rev5')]
+
+    def corrected_parents(self):
+        return [
+            # rev3's accessible parent inventories all have rev1a as the last
+            # modifier.
+            (['rev1a'], 'rev3'),
+            # rev1a features in both rev4's parents but should only appear once
+            # in the result
+            (['rev1a'], 'rev4'),
+            # rev2c is the head of rev1a and rev2c, the inventory provided
+            # per-file last-modified revisions.
+            (['rev2c'], 'rev5')]
+
+    def check_regexes(self):
+        return [
+            "3 inconsistent parents",
+            r"a-file-id version rev3 has parents \['rev2'\] "
+            r"but should have \['rev1a'\]",
+            r"a-file-id version rev5 has parents \['rev2', 'rev2c'\] "
+            r"but should have \['rev2c'\]",
+            r"a-file-id version rev4 has parents \['rev2'\] "
+            r"but should have \['rev1a'\]",
+            ]
+
+    def populate_repository(self, repo):
+        # make rev1a: A well-formed revision, containing 'a-file'
+        inv = self.make_one_file_inventory(
+            repo, 'rev1a', [], root_revision='rev1a')
+        self.add_revision(repo, 'rev1a', inv, [])
+
+        # make rev2, with a-file.
+        # a-file is unmodified from rev1a.
+        self.make_one_file_inventory(
+            repo, 'rev2', ['rev1a'], inv_revision='rev1a')
+        self.add_revision(repo, 'rev2', inv, ['rev1a'])
+
+        # make rev3 with a-file
+        # a-file has 'rev2' as its ancestor, but the revision in 'rev2' was
+        # rev1a so this is inconsistent with rev2's inventory - it should
+        # be rev1a, and at the revision level 1c is not present - it is a
+        # ghost, so only the details from rev1a are available for
+        # determining whether a delta is acceptable, or a full is needed,
+        # and what the correct parents are.
+        inv = self.make_one_file_inventory(repo, 'rev3', ['rev2'])
+        self.add_revision(repo, 'rev3', inv, ['rev1c', 'rev1a'])
+
+        # In rev2b, the true last-modifying-revision of a-file is rev1a,
+        # inherited from rev2, but there is a version rev2b of the file, which
+        # reconcile could remove, leaving no rev2b.  Most importantly,
+        # revisions descending from rev2b should not have per-file parents of
+        # a-file-rev2b.
+        # ??? This is to test deduplication in fixing rev4
+        inv = self.make_one_file_inventory(
+            repo, 'rev2b', ['rev1a'], inv_revision='rev1a')
+        self.add_revision(repo, 'rev2b', inv, ['rev1a'])
+
+        # rev4 is for testing that when the last modified of a file in
+        # multiple parent revisions is the same, that it only appears once
+        # in the generated per file parents list: rev2 and rev2b both
+        # descend from 1a and do not change the file a-file, so there should
+        # be no version of a-file 'rev2' or 'rev2b', but rev4 does change
+        # a-file, and is a merge of rev2 and rev2b, so it should end up with
+        # a parent of just rev1a - the starting file parents list is simply
+        # completely wrong.
+        inv = self.make_one_file_inventory(repo, 'rev4', ['rev2'])
+        self.add_revision(repo, 'rev4', inv, ['rev2', 'rev2b'])
+
+        # rev2c changes a-file from rev1a, so the version it of a-file it
+        # introduces is a head revision when rev5 is checked.
+        inv = self.make_one_file_inventory(repo, 'rev2c', ['rev1a'])
+        self.add_revision(repo, 'rev2c', inv, ['rev1a'])
+
+        # rev5 descends from rev2 and rev2c; as rev2 does not alter a-file,
+        # but rev2c does, this should use rev2c as the parent for the per
+        # file history, even though more than one per-file parent is
+        # available, because we use the heads of the revision parents for
+        # the inventory modification revisions of the file to determine the
+        # parents for the per file graph.
+        inv = self.make_one_file_inventory(repo, 'rev5', ['rev2', 'rev2c'])
+        self.add_revision(repo, 'rev5', inv, ['rev2', 'rev2c'])
+
+
+class TooManyParentsScenario(BrokenRepoScenario):
+    """A scenario where 'broken-revision' of 'a-file' claims to have parents
+    ['good-parent', 'bad-parent'].  However 'bad-parent' is in the ancestry of
+    'good-parent', so the correct parent list for that file version are is just
+    ['good-parent'].
+    """
+
+    def all_versions(self):
+        return ['bad-parent', 'good-parent', 'broken-revision']
+
+    def populated_parents(self):
+        return [
+            ([], 'bad-parent'),
+            (['bad-parent'], 'good-parent'),
+            (['good-parent', 'bad-parent'], 'broken-revision')]
+
+    def corrected_parents(self):
+        return [
+            ([], 'bad-parent'),
+            (['bad-parent'], 'good-parent'),
+            (['good-parent'], 'broken-revision')]
+
+    def check_regexes(self):
+        return [
+            '     1 inconsistent parents',
+            (r"      \* a-file-id version broken-revision has parents "
+             r"\['good-parent', 'bad-parent'\] but "
+             r"should have \['good-parent'\]")]
+
+    def populate_repository(self, repo):
+        inv = self.make_one_file_inventory(
+            repo, 'bad-parent', [], root_revision='bad-parent')
+        self.add_revision(repo, 'bad-parent', inv, [])
+        
+        inv = self.make_one_file_inventory(
+            repo, 'good-parent', ['bad-parent'])
+        self.add_revision(repo, 'good-parent', inv, ['bad-parent'])
+        
+        inv = self.make_one_file_inventory(
+            repo, 'broken-revision', ['good-parent', 'bad-parent'])
+        self.add_revision(repo, 'broken-revision', inv, ['good-parent'])
+
+
+class ClaimedFileParentDidNotModifyFileScenario(BrokenRepoScenario):
+    """A scenario where the file parent is the same as the revision parent, but
+    should not be because that revision did not modify the file.
+
+    Specifically, the parent revision of 'current' is
+    'modified-something-else', which does not modify 'a-file', but the
+    'current' version of 'a-file' erroneously claims that
+    'modified-something-else' is the parent file version.
+    """
+
+    def all_versions(self):
+        return ['basis', 'modified-something-else', 'current']
+
+    def populated_parents(self):
+        return [
+            ([], 'basis'),
+            (['basis'], 'modified-something-else'),
+            (['modified-something-else'], 'current')]
+
+    def corrected_parents(self):
+        return [
+            ([], 'basis'),
+            (['basis'], 'modified-something-else'),
+            (['basis'], 'current')]
+
+    def check_regexes(self):
+        return [
+            '1 inconsistent parents',
+            r"\* a-file-id version current has parents "
+            r"\['modified-something-else'\] but should have \['basis'\]"]
+
+    def populate_repository(self, repo):
+        inv = self.make_one_file_inventory(repo, 'basis', [])
+        self.add_revision(repo, 'basis', inv, [])
+
+        # 'modified-something-else' is a correctly recorded revision, but it
+        # does not modify the file we are looking at, so the inventory for that
+        # file in this revision points to 'basis'.
+        inv = self.make_one_file_inventory(
+            repo, 'modified-something-else', ['basis'], inv_revision='basis')
+        self.add_revision(repo, 'modified-something-else', inv, ['basis'])
+
+        # The 'current' revision has 'modified-something-else' as its parent,
+        # but the 'current' version of 'a-file' should have 'basis' as its
+        # parent.
+        inv = self.make_one_file_inventory(
+            repo, 'current', ['modified-something-else'])
+        self.add_revision(repo, 'current', inv, ['modified-something-else'])
+
+
+class IncorrectlyOrderedParentsScenario(BrokenRepoScenario):
+    """A scenario where the set parents of a version of a file are correct, but
+    the order of those parents is incorrect.
+
+    This defines a 'broken-revision-1-2' and a 'broken-revision-2-1' which both
+    have their file version parents reversed compared to the revision parents,
+    which is invalid.  (We use two revisions with opposite orderings of the
+    same parents to make sure that accidentally relying on dictionary/set
+    ordering cannot make the test pass; the assumption is that while dict/set
+    iteration order is arbitrary, it is also consistent within a single test).
+    """
+
+    def all_versions(self):
+        return ['parent-1', 'parent-2', 'broken-revision-1-2',
+                'broken-revision-2-1']
+
+    def populated_parents(self):
+        return [
+            ([], 'parent-1'),
+            ([], 'parent-2'),
+            (['parent-2', 'parent-1'], 'broken-revision-1-2'),
+            (['parent-1', 'parent-2'], 'broken-revision-2-1')]
+
+    def corrected_parents(self):
+        return [
+            ([], 'parent-1'),
+            ([], 'parent-2'),
+            (['parent-1', 'parent-2'], 'broken-revision-1-2'),
+            (['parent-2', 'parent-1'], 'broken-revision-2-1')]
+
+    def check_regexes(self):
+        return [
+            "2 inconsistent parents",
+            r"\* a-file-id version broken-revision-1-2 has parents "
+            r"\['parent-2', 'parent-1'\] but should have "
+            r"\['parent-1', 'parent-2'\]",
+            r"\* a-file-id version broken-revision-2-1 has parents "
+            r"\['parent-1', 'parent-2'\] but should have "
+            r"\['parent-2', 'parent-1'\]"]
+
+    def populate_repository(self, repo):
+        inv = self.make_one_file_inventory(repo, 'parent-1', [])
+        self.add_revision(repo, 'parent-1', inv, [])
+
+        inv = self.make_one_file_inventory(repo, 'parent-2', [])
+        self.add_revision(repo, 'parent-2', inv, [])
+
+        inv = self.make_one_file_inventory(
+            repo, 'broken-revision-1-2', ['parent-2', 'parent-1'])
+        self.add_revision(
+            repo, 'broken-revision-1-2', inv, ['parent-1', 'parent-2'])
+
+        inv = self.make_one_file_inventory(
+            repo, 'broken-revision-2-1', ['parent-1', 'parent-2'])
+        self.add_revision(
+            repo, 'broken-revision-2-1', inv, ['parent-2', 'parent-1'])
+
+
+all_scenarios = [
+    UndamagedRepositoryScenario,
+    FileParentIsNotInRevisionAncestryScenario,
+    FileParentHasInaccessibleInventoryScenario,
+    FileParentsNotReferencedByAnyInventoryScenario,
+    TooManyParentsScenario,
+    ClaimedFileParentDidNotModifyFileScenario,
+    IncorrectlyOrderedParentsScenario,
+    ]
+    
 
 def test_suite():
-    result = TestSuite()
-    test_repository_implementations = [
-        'bzrlib.tests.repository_implementations.test_break_lock',
-        'bzrlib.tests.repository_implementations.test_commit_builder',
-        'bzrlib.tests.repository_implementations.test_fetch',
-        'bzrlib.tests.repository_implementations.test_fileid_involved',
-        'bzrlib.tests.repository_implementations.test_has_same_location',
-        'bzrlib.tests.repository_implementations.test_iter_reverse_revision_history',
-        'bzrlib.tests.repository_implementations.test_pack',
-        'bzrlib.tests.repository_implementations.test_reconcile',
-        'bzrlib.tests.repository_implementations.test_repository',
-        'bzrlib.tests.repository_implementations.test_revision',
-        'bzrlib.tests.repository_implementations.test_statistics',
-        'bzrlib.tests.repository_implementations.test_write_group',
-        ]
-
-    from bzrlib.smart.server import (
-        SmartTCPServer_for_testing,
-        ReadonlySmartTCPServer_for_testing,
-        )
-    from bzrlib.remote import RemoteBzrDirFormat, RemoteRepositoryFormat
 
     registry = repository.format_registry
     all_formats = [registry.get(k) for k in registry.keys()]
     all_formats.extend(weaverepo._legacy_formats)
-    adapter = RepositoryTestProviderAdapter(
+    disk_format_adapter = RepositoryTestProviderAdapter(
         default_transport,
         # None here will cause a readonly decorator to be created
         # by the TestCaseWithTransport.get_readonly_transport method.
         None,
         [(format, format._matchingbzrdir) for format in all_formats])
-    loader = TestLoader()
-    adapt_modules(test_repository_implementations, adapter, loader, result)
 
-    adapt_to_smart_server = RepositoryTestProviderAdapter(
+    remote_repo_adapter = RepositoryTestProviderAdapter(
         SmartTCPServer_for_testing,
         ReadonlySmartTCPServer_for_testing,
         [(RemoteRepositoryFormat(), RemoteBzrDirFormat())],
         MemoryServer
         )
-    adapt_modules(test_repository_implementations,
-                  adapt_to_smart_server,
-                  loader,
-                  result)
-
+
+    # format_applier adapts tests by repository format.
+    format_applier = TestScenarioApplier()
+    format_applier.scenarios = (disk_format_adapter.scenarios +
+                                remote_repo_adapter.scenarios)
+
+    # broken_scenario_applier adapts tests by BrokenRepoScenario; it's intended
+    # to be used on top of format_applier.
+    broken_scenario_applier = TestScenarioApplier()
+    broken_scenario_applier.scenarios = [(s.__name__, {'scenario_class': s})
+                                         for s in all_scenarios]
+
+    prefix = 'bzrlib.tests.repository_implementations.'
+    # A list of tests: (module_name, [adapters]).
+    test_repository_implementations = [
+        ('test_break_lock', [format_applier]),
+        ('test_check_reconcile', [format_applier, broken_scenario_applier]),
+        ('test_check', [format_applier]),
+        ('test_commit_builder', [format_applier]),
+        ('test_fetch', [format_applier]),
+        ('test_fileid_involved', [format_applier]),
+        ('test_has_same_location', [format_applier]),
+        ('test_iter_reverse_revision_history', [format_applier]),
+        ('test_pack', [format_applier]),
+        ('test_reconcile', [format_applier]),
+        ('test_repository', [format_applier]),
+        ('test_revision', [format_applier]),
+        ('test_statistics', [format_applier]),
+        ('test_write_group', [format_applier]),
+        ]
+
+    result = TestSuite()
+    loader = TestLoader()
+
+    for module_name, appliers in test_repository_implementations:
+        tests = loader.loadTestsFromModuleNames([prefix + module_name])
+        suite = TestSuite()
+        suite.addTests(tests)
+        for applier in appliers:
+            new_suite = TestSuite()
+            for test in iter_suite_tests(suite):
+                new_suite.addTests(applier.adapt(test))
+            suite = new_suite
+        result.addTests(suite)
+    
     return result

=== modified file 'bzrlib/tests/repository_implementations/test_reconcile.py'
--- bzrlib/tests/repository_implementations/test_reconcile.py	2007-08-22 05:28:32 +0000
+++ bzrlib/tests/repository_implementations/test_reconcile.py	2007-10-05 03:06:20 +0000
@@ -14,19 +14,24 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
-"""Tests for reconiliation of repositories."""
+"""Tests for reconciliation of repositories."""
 
 
 import bzrlib
 import bzrlib.errors as errors
 from bzrlib.inventory import Inventory
 from bzrlib.reconcile import reconcile, Reconciler
+from bzrlib.repofmt.knitrepo import RepositoryFormatKnit
 from bzrlib.revision import Revision
-from bzrlib.tests import TestSkipped
-from bzrlib.tests.repository_implementations.test_repository import TestCaseWithRepository
+from bzrlib.tests import TestSkipped, TestNotApplicable
+from bzrlib.tests.repository_implementations.helpers import (
+    TestCaseWithBrokenRevisionIndex,
+    )
+from bzrlib.tests.repository_implementations.test_repository import (
+    TestCaseWithRepository,
+    )
 from bzrlib.transport import get_transport
 from bzrlib.uncommit import uncommit
-from bzrlib.workingtree import WorkingTree
 
 
 class TestReconcile(TestCaseWithRepository):
@@ -374,3 +379,26 @@
         repo = d.open_repository()
         self.checkUnreconciled(d, repo.reconcile())
         self.checkUnreconciled(d, repo.reconcile(thorough=True))
+
+
+class TestBadRevisionParents(TestCaseWithBrokenRevisionIndex):
+
+    def test_aborts_if_bad_parents_in_index(self):
+        """Reconcile refuses to proceed if the revision index is wrong when
+        checked against the revision texts, so that it does not generate broken
+        data.
+
+        Ideally reconcile would fix this, but until we implement that we just
+        make sure we safely detect this problem.
+        """
+        repo = self.make_repo_with_extra_ghost_index()
+        reconciler = repo.reconcile(thorough=True)
+        self.assertTrue(reconciler.aborted,
+            "reconcile should have aborted due to bad parents.")
+
+    def test_does_not_abort_on_clean_repo(self):
+        repo = self.make_repository('.')
+        reconciler = repo.reconcile(thorough=True)
+        self.assertFalse(reconciler.aborted,
+            "reconcile should not have aborted on an unbroken repository.")
+

=== modified file 'bzrlib/tests/repository_implementations/test_repository.py'
--- bzrlib/tests/repository_implementations/test_repository.py	2007-09-14 02:19:41 +0000
+++ bzrlib/tests/repository_implementations/test_repository.py	2007-10-05 00:52:37 +0000
@@ -273,7 +273,7 @@
         tree = self.make_branch_and_tree('.')
         tree.commit('initial empty commit', rev_id='a-rev',
                     allow_pointless=True)
-        result = tree.branch.repository.check(['a-rev'])
+        result = tree.branch.repository.check()
         # writes to log; should accept both verbose or non-verbose
         result.report_results(verbose=True)
         result.report_results(verbose=False)
@@ -393,6 +393,27 @@
                           repository.iter_files_bytes(
                           [('file3-id', 'rev3', 'file1-notpresent')]))
 
+    def test_get_graph(self):
+        """Bare-bones smoketest that all repositories implement get_graph."""
+        repo = self.make_repository('repo')
+        repo.get_graph()
+
+    def test_implements_revision_graph_can_have_wrong_parents(self):
+        """All repositories should implement
+        revision_graph_can_have_wrong_parents, so that check and reconcile can
+        work correctly.
+        """
+        repo = self.make_repository('.')
+        # This should work, not raise NotImplementedError:
+        result = repo.revision_graph_can_have_wrong_parents()
+        if result:
+            # If true, then this repo must also implement
+            # _find_inconsistent_revision_parents and
+            # _check_for_inconsistent_revision_parents.  So calling these
+            # should also not raise NotImplementedError.
+            list(repo._find_inconsistent_revision_parents())
+            repo._check_for_inconsistent_revision_parents()
+
 
 class TestRepositoryLocking(TestCaseWithRepository):
 

# Begin bundle
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
k3kIQNxkVaDBluOcV8TDYVxNg/cOePQxyGi3s3J8thwE/iPSj4iCm/nA8SjAirBDxrC2keHycni+
q1vX8Ewr7JjVftQFouGBapj9LOcpVtXVrXGdaJp5hVqWxhQlIkiVyFXAMYizQd4eGlZlyYMDGWDL
zVU01JqYmYIVlLTTzYymaoVVh8ZYynChYoIWuPshfcss95NEV+/7Xd+Qcv4MvNLE7P77rKlaR6s/
qsW9+SamszehuWnZLNarUZATyWLdXTA2vCyFk957k1QtvXmaJ9sSSiEkuhD4EdwG5A39ZSG86gCk
cEHApUnUie9NOyMfYqif2/hPjV9lk1++R2trcciiqHcKzbZ2aSisMypqNjKTN0zXGqqgqSqCkN5o
cjhDHIzJMmjZN0z7h9V7r2uXussu1y+N7UWPg+C5UyWKQeSpc8Gz4NkmzBk0XKSYOjJspwWKngqU
2XOz4NGzyftMHgudHsexPr+e/t456+PxxvPz8es+fhx9O3znn3vfLqhau+P65nbvz2iI7cW+r664
FP56i5+F/duu0fH2+7Ua5+5dN409dqv2z3V59MqJhfd9N/L2v46G/u+HoeOw+Ptz716ne4XqnHjz
S+7H149hye9140r6r0VJrrcjtVys5UZUevXq7rs1al2StHZjLOEa7VZeME7sLSR6rY+cD82uc2/j
njqIffPGTzvriOKbx243wu6wjfXbUG4jptmuTq2ttXMjrmsvk9a31fR64L9dbrVdPqObdq1cdTzC
ntlXwrSvWDrrU8BubS1XjI4rlrUo3PC4007JXH4434KkdeZ3DHhkLN2vhcvvWpLdLKZY6t52xLWe
ivywWrl6Xt6fafs1+b4v69fP3+3f5+q6Hjv5X3B26K+3p2ZyS3tcL82xpgqjdZrJjDqGPW6tVi8X
NzM/kkJd/7/6Y8/8KJP+dmyiaD9cny5iTUUdT0JBSPeAYURoMwAP73H55qJCjmjIk091A/viEDBI
QfwYCQxgkhIs1MFMVlrjlyxcgyuRYYL9yPFG3FJmkyz0zR0SzaGZctTK7Xc0adN/Sx/TpldiyX7k
VewJJTiSXjE7yBGIpFPApH/i/40hcQgt4J74+SIeV3xxEcG/lkksoCtJ8FCKl4qHDwuIImQh1AI0
AH6Ef0FxmhMO5YEfp74aD1jQYFwuEBzgGYRE9GYGhhiJgQYS9GhgbNCRCRBEkQRMRTscJNZbgYhh
IxBEjEJEskGwYEBBDLUsSftmYvAgOtMOHT110m6iZn/lkhSPypGfKykCPCkf/SLuYLgEDAP9A5/L
IsiQhIEiyMgDcVfR4Tve93qS553lcY88RLjTjXYCUcHjPJdZVSsM/ydzE4UtHLPEUzEyWpR5em4I
HZ+yyf5v7UKGx6Ho2YRdGj4ElIsPBURPUaqyysMghoWbdknomZ93bTONjgVh4IFBWMcUxeq9jKR6
6gwYIM/L5fyVGSyFa4N8EEYDgGJ94wPoWLwKFcDARgwYMDjANWyu/Oqm3cXdkk3UkxVfku5wu0JV
x3VUei+tYqvUmc0db+TLqmOOxy3q6ixRqLCb1eBCal0LJEbpo0lkwbpyFzkbtwppoEjALtDkdWv5
NgQdhdSR0cCNFtNxwDBt8AfATOhppvBgWO2YJJJTnuc8ElJjscN0EIk6+B5eOfHleInr+el5b4U9
DJB9BKg0oH2O4xZXhQROecHR2aVSulJNu0zVWUiXSTe8TROfy4GtmEgtWGQ90vdK0GvAHEjNnX0M
LhrB8gAvzivznnuONJn0JtB9tiavl+CHgCB0pDqqbNentTV7TqZoFPAMAxpcpYQeUI4QhNjgKjuo
zUboayEkD62jlYq3EZMmFoYWYWC02muN1ujA5xDcGVTK5ZmrIKvjFrFSXSMFBi2xj44HiR2FBqRt
UtqFCgVllCp1cK3IJwZwoCg7zA4i1D4I2YxkjAjtwg0klLRJaCIMqwkTr/u/Lv2pg0eaKcC2ksiT
plEWBoaqVuXH6qlhTDXsBodIybB40HTcgg4JZh+rDqEuL41MYIGDtAGCQZCfjcMH1nwif4sTj2UP
MaA6yxiGUQlBQkQLAHSwuj8DBOEEsAZgBgJyR5opigZPnNDZWZCUCISJicIR2E1k8cwUoOSv7MHY
utGISp4N73HyHN0MHxPWeUy5E4nWYC7FT/9PrS/O2WIg/lTWNmwIGMaQTjpQRdFLnIqVtYMsgGTE
EMhR98OtXO8JYE1YZN/zWhfLF4KYM9i9kqmc+IF020bz430bxs5kcECbewZiSEyx/IS4ldbEn+mf
biWmOIw2VLIL3Ib8vOrZxgkmcR6oL+wjCMA3zvQ4RAJ9NDWrhG+DHgnFOPaPNWDRSxFUuWdoZccN
ogfcQF6HzfvLLyRQVCFBmytkbNmuvI+oJII5gsxLqpJJJPaIsJuNXbFVjIkHCu5obLhO4PQYxjGU
MhMgYwbJwloOFlVpdzzkkfXlrANZ7r8DJqQ4IQ21jvNselpQWcQsyWvcXYlCYGhWHyOVw6USCS1q
6hFWsTvqXQQiUqpYWM4SUdyciqiIlIIWIIJCIIg3SQAwjF7Hz9CI5r4Ol3DYQ1lFR9zCRBattv16
k3pcvy7HfeuEnGT0qiP/yopKAv9iyR7CgbHNwnYomvxy3gHMFu/koETw6na/aeGI3iC4OLLand3Z
BKIQURbtJCFQpqQIGHJo+MnRiMjOdEntKgcrUxA04vMe+D2sgY/giGwuhaFEjC15iX0EqFhpNA1V
wyvzVexH8Wf7PP94jfERjzzc/yWfr/3feerUHRKb/FWL7bTBuQogngcSnNTWlLGrXarpakrqgzwR
ZcByVAC+tpFb19KDDS1giIZ69X0H6ZgwHy+yO3JNAyoIPvaH4xZnyXW0JEkFVngh6FxTsyg+FSWU
IvlV1iptxQbGGh2eFcDRFLGGZXpxfDk0R6FXN6V1PAP2IEQHIJDPrOQOsBszFBOwpVJqllWmEw7t
Tyu5CkYxEUYGWsDpzePRRsbWWDRAmfXiB8uziSvL2vF845RuFhD7ZuInigLYMaG0QerCRISRMTK6
XOntFBukcZ5h+pnOAgrnvlOBSEDwqmgCEHHH2hfYTxJUDH6eZB1wyHfERX1OScmjgIrtHF1ipLD0
LhX9nRs9SAsYxjY5baPTCT+vxfN8QaTIZwE7zrZ2vhCEDQZYesVH/09GFQ6b4SRViY+AYikLffsC
ER72c+rhoX2UkL6oyq8loZWHutd2PLBZqDNU1cEsJ9moI3Ilxu1LYSVAT9n3SYrUGstfq7wtDsi6
yJyK0MIKdMKgLwC6Gml9U8GzP4K/XEDJ4lziUW5OGW93hxyjgzOpRwHPkXt1cYctnrJkrW1gTk0r
4ulxehrCYrUwCgJjkcFTJQ57SlCOXSbDWoN4IxN7KSFgMzJGYRYyoxPXKyyCGu0RjcBqiWRdTlws
nlXUDaX4QhRZ3kFJpi/8Pp//AdjsZ0RVehwOh+3+dS85W+1zx0vzfyvZvD6+Htx/l+13937f4uPI
7oG/4sKlRV5P3cUrLtGgv2tw1ML23qc3RmhkTj0TRr3K8NzEZGzr0psSNonJZ3bTaeXX5epyfWjq
GQ8gcHkdRuxHPNKAWPUJJcqQM0uSitQD7UNeUc4PT8UyNL12HJLM/uJK550c1Ym+hCpkBvKTzNjk
cMeGkCSQtIWl1IJDsFe1XR9J4n12JghQ/X5evXVvDQ3jUkQjTMPtj7A9sbY/XKnnkGKELAxIPXB0
0IwMCCCDD/ArhoYCSKKI7+SpVUTJE0JkkySE4WYBovvxeBHJOEjSWYaNBJu4ZOThWZkwkuTgy2WM
EroObuQWZkSEmjDm2MGjbawRw8Y/P9wGHVh0swHAUGnPjhLgGNJrmCZIG2REJAE0gQENEMU0rIQS
E0NTBBIFJsJ1wwX2+Ub6Ejk4Yc/Eg+HHyfKxnT+6OLxXzU+SznB66Im0oSnEMcUoN+v53Y6tzM3W
Doa7qbnEuuBwQ6c6mqWpbJntfHrAYZlHyiADnRkwZLpydEEOzWRoHCxcTMAC6qFpTQk5RyTI5htj
mo7JrwxYES/q4rfJ8NFaS7eb0vIRaUBobSG2La6o9KkSK9CBBYimmwbOUeWL+ArWORPsegdnruFo
JTMm1DkWnLYU6bSHh9fDi6tsIXQig/5nIbIv+k+Wg4fSfWD3F6kPzcTkwMS2iazudRjvmzAzIfmx
2bSnakTI2uRJ62mdgtB5EtNjHRgljGNDr0D4A2IXLmBx7Ja5OLY57RiG8OgXcH1Ox98+rXsDRLw/
tFQCkICoB/oqAe4qAe0A7gp+QBD8n+SP3HVy1Urh44yUSFpCqColVVUUySHLx3QujdUxMT7VMuYI
EBhCJKCg+wA0QfyBz9SB/eATnLHHtVVMDERMQnYH0AB/uih/OIQoOsgj3RAIAj25KUIw78JLJCqb
CpgpgCMJ74Vfy9cdAHBwKRIkDvIKY8sClU7EoJkEsIgVSApgyo5TCjwlVT5qHsfV0ehQ4AFkeQVO
PoRhShiAD0iGYj3DgieicE4PQdiR1HgjoSvQmC3EzEwdEYBioeHIA2lKhYU0EbmeUIjAUygiaFLo
CfMIfFdmyBIDiA4QCQGAQYMHbAJAQMFD1qbhTZ28qBvANSjSobq1bY1GQQqFQ+4kUSYLKKCgigZM
pkwQQyiYGEplwJBUtznhG3IjzBtyR0QTbZB5X1a+XnR5M1NADKAHMimYq6CnBLhhWsHeKHkgIiiJ
FoIee7mCHsKeoOKu/pmUTLxBVYyulIvliyMiAbhTQUMfP5NHcRzCGDkiLwPcyI0oHWsIqiEj2GxR
D1nREKVhHqhOETC6E2A0ITgIIEhVJGUXLLlSyMTSq4NuvFa2mK249rllvuxxcDnH/UDZnP2/Trgn
arNH90+5dgJ4mu5D1XFzYEHsXNXb/ueeRylAGr0jhQ3hNaRuyKqcUxF+PHb567NatM9O12d5aaTr
hWOT/B1w4qph+nrgqaf03yRJzExJtyqFcrWxPQG8saHMbLGPYanIcrtCWW1DHK+HMUvlwYctsN/x
nyBlH3fd9f3fbTqSq/C0xwfghI/Cc4JrP4Rbz1hCQ9TF71F1mrvF1Es5S095I/W36ePqPPpDu7fT
3G/k8e8KFj5sBP4Q0K7v8SJt2q6gtFi0BnKCejc1rCwFEoqkaJmxQpiYUFGL4YX6ybAg05QxdtiU
gB4AJSSQAzE62j01dQ6g4ivsoAL7hFDv2EUOZHtb4ikj1dO926DOowyse6fl9qlaAoSIKSuwl/OB
oCMuKGEAWBiIkEAAQwiBSoLDCDBKKndeE9sPZcC6D+yBZymknjqdMGKmRSwdQWW7ZAVEY54cD2wr
y72in7md9wgfvHkHijLT2RBYAbFZoryaPwCyysK4JOoR6LtwhyZABnEF5tIw0iEcqKslBIa3FNfb
zt7Xoj6rTB39/04d4yLdnIjczctytwwt3dyctsMw2TYzDKLd3YswzMsw2rNzpTP7+uDkZczOty0c
5mUGZZuaOczEkSyJqJmCPoUFCAwCIAChArEaEoVTiDMQ6gyFTI0GXXdRrBKTIaKIEhuihcAimpPK
4i0BAOTI/jlbtjecBMAGmkSJDSPUbMXqAPQtTHgfBjBoD2NgnBhn8wCfm7CcTnYK6ISgoOfLFBP7
ez/hkfVq5vYKq8+7yfOPyopCrn2/2fpd1U3HYqGLq5rH6vPf8b0tixXw7Idx2XduvMd3ENa1rWg1
uZmGZmZmZgGZjmYZmGZhmYZmZmBmYZmGZgZmOZi5mGZi5mOZgZmLmYuZhmY5mDmY5mGZhmYZmGZh
mYmZhmYmZg5mBmY5mJmY5mOZg5mOZiZmJmY5mOZgmZjmYmZimZiZmLmYGZhmYGZjjWjWtiu5Lc9u
2Xbfnecog2TV33nqall2i+/c5c2fBvZJrM+NlOKxPx2Ia0Nz/gSS9Jf5d398IPiSUbSXU6CST1x1
9+ivkzVHrFSxHlZrlliY7AyPG9FI9JZy29D9ENNcSScnnpfpqszOvVH5KlPpvb8M+5VtUV5earqr
Vn5dq7VmkuwOOWLGWe3M9UcqvW92GtWrO1KHLNMFFMtZ6gn11IL8dMlezFYhsv3KStE1jS1M7WJ+
kfHmQPQXh8VPxpD9UX4oUAddzWnwaRfx+HoG/f67DjW+NzDIjFKoinTPOCdvOsPODRFTIBMERPVZ
Y7wTkJgiNFvLy8Q6w8AX47pAhE61mJzVpJLgkp5AmScjzD13wHyy9++IeZe8HiChDqEhJFfE4Xwi
74genbzrdzrncOgT0kQ5CnmB5VXiUO8gdSZK+Kg2OjTxobbCg7UnpAcqXtCy1hiJYK2GHhiI1fFC
DDVMFpmmlqE8R2kDIV3tnpBkGQO+e+8jZTkJyGgO0bPJDJDkoekPO+D3us544oeLsQ+IOoKpO80v
Uh3jxKdgjzNzMhCkpKTIC84+kHjz5tE7Sfw9Pq9ZrR1QaGYt68r5EIEuu3yryQlvuOXYHJXOtYmN
rXxx2ir+P4hx5hQBWShijig5lA42BJglQkUCq/XnHoVDyHKMvZvt25DDZkLhuJmd4q+XF4vmtobT
GmxjGNsbGwpKSihpkHAXDUU06PTo07B8CwSk0YUEzA4QvPjmkhJgHmdFev+TvvA9GoIGtV2ilSkD
dDA1Bzm83I5iCRiCOAChIAjmKGOlgXQgcpBRHZ0mf6JREZkhHchEIg1uqL1YTZcpN0E1kmsQPGbR
qmrFnDzy6j/Usgng+NvwJnDi7dxXmAbDHnQhWJHoVZgO5AJfbBgRSS4PU2/aq9Iz4jne8bqlgNJA
lLFGb2UtZPF4RYKVcrhSGc4hFOnvlusu/1hqmw4arxksgjPWkVbr3XauGGbdQ1QRNyzZuEQ3pde0
E5FRBNKJEm8Uik5kwnpqTC00Fq8PkutIjCQS2wzmwhGSREKIQnOEZzhGzVu6M2kQbMpywqm6bayu
RFelVIRRVVoq0XbXabxBKoIuwovj+KEbOWj0zbtFkpRdXlh1o5wmte+eVIE0+JijCnNeLXovkiFq
oWF2UUDFQxKA2EFALK6zaWBNZdu0SVQZtk8wt9Pph266iIaxCJtlEIya0eKsu59pkRDdZwvjHz48
as2SEcJ40UdGTsMk0du5tdjv1NqMwnA3Ux0UgUYAMGh5QtCQBoslAuC9BSFGkgm5AwGlKsQbKBvN
ZlYUvRrM5SOQpsOwYT0RBcmgmGGyeEQ4ckmF4iGkVXa0ytCM6btKM2np9O3S010l0ptkk0juFk7X
XXcCZwQTYgWUKMVIJL29oQKQBoABEG9ImRAROBwKFC+aYq7TXkEkQv6YRG7JWq0RFvckGd9eQpkR
FmJV9dq7JYtwuw9OWKtGTRo1KlGaphOXdEBZg/EA1gd+XjFRRBQUVCsQqFHfD0iCPUiBKQSkhGEE
52JtSl0l23LTTrBFDSFps0m2admbHWHLhmk/svs00Sly1Tcpu0DhE2EnDl9yIuDN3rOc9iXIkpzI
oO5cFq94rxWB1KEZeslAwYVwgpcBgbiZRFRXXEVXDHVlrEdeKTDteMs7KrY3xEDYEYomiDZWzSxh
KKsK9JtqKlI3aKQzatVWj4+N+HpD/HbvtRvEjZmurBKQCzRKJLL7Q5IgCRB2ADXiD4wpUU0hX3mB
iicAMThCLqhUzWq/fm0aPXLE8pxXLCe1FE3tm9uHfd2jpn6faEcL76ZFIisuIpneid5+9AwFLogL
ItIiNyUDVkmREMyIhJJCTOUpREQC0V+AeNGrX3m3CUbJdpWlulFVGaanDfakbzbuGq++pNNowskr
VYuY2k3G0sVoeAA604Ciu5BMvc2+Ty/7vz7c4rGGn/G1uYuOfTEdHF+aC86XDCXzEPUlA+IRVB4t
kJZK7PHtOeH6mWSj8E0klE36P5QwzYfoctXLhqu2fpq0e/3RDbXhKejd3EOGsmqCT+eIOHd3CzDJ
RdJI8YZrpKGxq3STW6kqkZLMP598KIGjdqt4zUbNF2ybSyiir3ZNVJVryzcO+F3q6jCbNsqyatPV
27VlzJuqyfVC09qvSr9v7aN0vS89V7zemT9wYTYctknbp0VfohHHvPTtSX0tOc9IQo0aoEaLPhIt
KWJ+unDZPVoyVbO26b9WayjJ2mwwqvJNw2Yf6ptWccOHUs2z7KuHz84ry95KwlGzpRo3UbMmycyV
VWaN2qzJ00wkemzQqzf2n+irWIG/PzEH5mCP0d6IjnlSA2VcYgiNwWOGXS4RAsgBx4ASt3tghS2B
GraAJ0g2khcAY74gqDyFfO+7KhVhAt9VEU0nc8VsvD2lu8ik588stummsrhfPa8Uk8MkJISFFB5Z
ySkoqqoiKff69vgp6wDXAdkDYQQIRBSEEDZDpN/XxXEQl5mo4ZEYnl8s8rg5ahlsIxMknbBKlp94
AW8MgMmIJ0Rlm2Q7dzBTIYFZg1vEoDM77EHUZU0lz01ch9JFPO08OHKERmsk3rPGkAGcRvLHFzqU
OtybdaWpKoApFlEA1avrTDLGsKzEElCQISSuFjJ0s6y5F4XaiSgCUypJ0bu2YYWL3C60XedPGMlx
BuTic7LkFw8sy8sQaa1AiBNJt3iESXHDl6d1U57H4ngF5D/y/34M3Cl/k2+fHE1KSd17n26+Jd/m
vj5geERG2vvRn3L65bXt+at1jrjc9TVV5o7IQ0k0I9+hL4VEw/QLAPUQ1QeohErohrCJhQJHn1hE
KOPWmo3m/TZva1lJqIzhVSm15CUVt2WyihmKbQzXaW1RADmiipinKFFjFf0w1VlMDWEJwQXhNO/K
nTleLjnX+3XhEOkIq5y9yX0QdjbcoEMsxTertMTnNWRuEZvDHG5ar1kcU/dYRWl3CiSoOEzC+Uu3
LGRuiCJquuFWrirVuyTSbtXKSqT4iBmRcQe3w95yHYOadr42pGhsKR5ADv7zTkMhBz0DZmINWXiu
UXIPCKaonANOjacs9d18R275jSNCQU2fA8L4lWBJBKeAbBAUp1PKhjJNhI7JFtHjRW8mQIl3pWz/
p4dKsJN279oV9/N3tsg5WXpAyIiUQi3f2QRX44SfL2+U2f0sd3fR8MlXLR8pPo118RH9j+T0Ovl8
Dn3ITc89UTUQeV339aEkY0WexgNo9DrKnIAyrwI2ZPMxoTb1MHJs9ycbSH3nuCAiIa3Yduhd+0CR
n4GuBj9iCG+/A9VUU4UNUBiqEvhk0bMRRs3V5pxpanMsYzweqpZeeO3GuuJ8dIuohuHmtwxD3Pfj
Kd+dcr1OqieMBfY5Xilu2Pbt2vsu33PZY5rrnfL3rb9G+wrq1yaN9LjyIvqp64h+o5jU4yuqiHe+
y120HoV1vVz1Gmoa5Nlen31xzC1zj67VPW+n51zRqD266311wvbsN1HCrWLJ4rUdqXOMrdBn32HK
IECWFHYKOV01V3Lhyk1J69b8TrrPNCXUk26idYnFL55xqMGSD5OXnz82jJCEZ6a4SrCJpeJ+mFvp
lCPatbvc1FMPR8IiFKw1qlCKyXeKPgbNINNP1zOTgj4DwrdE9j1Kvnk6MGCTYUcHc6PLj93wr363
3UZgpwS0cvq2RSxUDF3WFpN3isN1pxBKywSUmHiIREdd3vpghElOG0ew0tGyifDsmFomia8kBaF7
++FiWo1SAtmLGkeMp0PM6SxEeM18xVjldPFEV2yhHdIRJATWVnN4mayN5+qZAPRFMnsep7F8pISa
sQ2ifM8j3LplFegR5Gh2Sx+rg8dxu+uOfBThCMowtSoJBZ3Dsw8KHUEYiIZHu7J4veUSTC5kmaOm
94ayiNF5JlJBZ7N9miCy8kQ1u0S1iBBB0BYVGRFIpI6BLo7ZHSVnECSx2LUpFeJMPTRRndJJw4VU
cJOXiYbbVicJY6oZShJ1PRPi9N4IrEIpe5Rz8VBG/jeIG2Td2pwoaRSw8Zrnis1pyxCMnbp3WIhM
g10cPrKhWXDWI1PPS2ytuWqmfpOTi6Ns7pcJbKVWXtZd0vZw8eKom8ZMnIu0DPBZuewbeGiXMPUl
S+RS0dMjCnKIkiGn/PmUhBdzukuHVeU5nW9puVYZO1dKN9alQu4ybqItdqiCiNGi2yonulpL5SrS
jX0mmrHHjp40yYdueX+7x70YPU1HLd02d/M6hx335KUdpQtNnzhTHdb+S+aQjM4ZQjikq4nxsuh3
BHDVVmxNssK8THjDDayc8l0yPFHwSZlUsR/TON8LXV0gzelIUdSRXKT0sbraNH92VXT03YaulVbp
eN3www9X3fie7uO6RLajiuKTSk2liyta4fXLWQW1nIcQg3HMgwirsANdA7is8eSZY0yxJCzOWjHj
xWOpON52UnF893tavS+5LRbpsopFcJ6UWUzWf4ePbx0elGyTJsgjV2UJLDVQkWf52QAAcAAB6Djp
jgrbnf3X88XFteVFq08Tc3SZDdSxpiSvsEhIyO4XbMpaYODxNk2m5B0zJ/kw0avzMPbOy2uuj9L+
Jdw0dxCcat0mSb9DpxGTx69aNE4RrEkRJAbJyY8ekmyrllyYKvTdRVozdN2hVd00W1VUfq5iIdJQ
Ipy7ZuWzt0eOm7lbDxVdE1HaTDxWay+jdV/jOa7Dlrsmy5bzbvSszVJmo5SwtGjGbHpDzZx0ldf9
cOHpw2bO+3LRjp2zaLpOT05V6KRk6WU3zSZqYnnYnEmadMv4T/hSdOWSjjWblygij9pnovpPul2T
X+YE1Hl2rph0+/3mzZLu3x7TaO3pQ6QjRZutwqphzbPRP4ZFXB11lhq0wyTfCbty5bnDhiSbZ21d
5xlVpH+fVELlPz+oYQ/nGp2cvfXpWr404QDTKkFeQJ0TIVui5BlQFPC4RcyFkRvCLKFjEK3TgAvp
6sAe7qg04seV2vHAwfjxQ6+6dzWZ4PVeZ+heag57Pty45VG02m9pMVsPdiUg0l3z8zs6WnPkzyy8
yzyjm2OCcEa6dbiVtax8pQxm2suGpWi0RhLqBRAUhjTBUTYSiC0bCsUoxJKb1UA2TjGZxcTGnTIA
R5eXaQQQPJ1D6fPVq7rq45oxm1dR1XidAWai5VgSHVZvKDkmJsibtgForKzl4+WyYUXJC1luxY2L
LGRdywokvBY0DamCLgbFjpSJhMRUYZ3EmlQGpSSTjKKKGk7RsFSo9MHeNr3oOKKlfV/Rx+G4O3u+
og73u6vwnJQAEkiCIISF4JihJAYQhEESkTMUVEEQsEDz+L5fRwQ6B+SqcRBuhDZCYeHqQbRC0KLB
k4ol09112X72YMPkmdxojgjIhdDbQqNz1Snj3qIQJIRpTL+pIq2Ij6yhFd9HgBUiPqTQhBQBFGZh
Sqqhc3m24bOcRthpwOI+IQVBUI5KEhrZaoW4yUuHCyciKrTM1CSHJRibCiyEfH7pobQOmULx/ZZF
X0rp85IKuVVM9fE61ruwsM/A0bPXpo+FlHpTp9U0nb22LOI81DGwj0iaolzA3RBTfPaUhYALjww7
+iW7tGi7sn5xcEZM3b1rzJC+7SfWPXO+S9K8OSN+iWynPDnphaJCtHeVdeCy1C5fSkI1tWaI04aK
1gcOHUncYYXkpakcMBnt5HzAhZ33OjDXL8NFtQOLgREmGTOMSnEEwvVuu2ZoRnEM4bJc1Y3cL1ia
C8jrSyyjyZHCF4O+S0IRuxbLJkRSFcIbIUk7uaZ5Oqxnyqyq3xERFdIR03UbsLruF187w1mpCLqM
kb/O2z04ZKvGMdKNHBo2cDPQ6JNeXjXiFwJkNQyJctHnObCWWjVUMwzOZ8GDaFeGEiEaEG14wRGf
Y7oO1p/NidJ8t+3u2yRuhCXKs28kSc7vOzRKjOa7S7tblvlEFWtkbxEGymmr+NDisaSnNbdXdo/o
3bOX98I0ctHXtP4fRkw9pvb34N9Y+H10oVKWiOyqIKBSQaKgAkDwF1OnetFUwIoJmpyZm/IkxBA0
iO0gjaIOJxOTVDpZo0yS+nTh38bfDpJLLlxCMnbemcyB3RDuPTJbZKdkJWSZrrsrXz9Ka9c4yf8f
px7eJHCvD09O3pf3ffuJ+pJxkSSJPsyZRCF6tU5TzqqpVfakrz5CJ2lQMXYllx2ui8s3OeslZRBt
oF+LRdDfL1RtbtsjKlUqSjiWUpERy5Tp2m5xU2e1EQo4eKpII8VProvdwk3z/hr86uV3j29+NHEE
dT9TibPJ+rfrSoq6i0axcmwA2hdgxn4IAMmjJZatAls9TZBRg2kIjg6PPzyXbZ2jRo19+3i6vLCS
Tbj4v5SGq6iT9nDNrwkRymzW+G/erZYzD+iEV52lxJyfEmUicuJp4lEpPHkAYTzTB34WGb37ShKv
a0+OlR6qwjPZR8Oeco3hngOGz0us0aPlnFqvlSrbJ0T2XcYKemWGZddG1bK1R6rHbJP2m5dpNV0W
dN1tlbu3CjiI915jiW2843lKlL1VBA/apiTFxRUiAN8wkQkHKFpC75OYKpSQRHaUENNlyiIYSIjP
TSgNro310elFY34CqkKBmCKuFSTspu/oIz1Lt12UfTXVTD08coQ3cTUdpqKPHpJRS7lRyo8bsnDd
VTFH8O9fEad3kVl7u5pe5JiXcoIomO+rrSQLwYPezJ5GSeDCFkJO2Waa9MQozVry1ws3i95ZuU+3
jnZm0k6KR56AHiIIB0hICGFCoSAcEAkTKLXt65fD0/na/u/x/DD6wem7FJJOOn4MJvb0wn6Z2ZM1
n3yMijTTQyLmR0hY2kDEhlz5E7nf/X1h1wJ10sROSfjN57cOnTh06JPFWqzN49sElWajR6VbBhVh
RhfN7ZoTTUvrJwq5TKp2UZNiT/XLsjuUIMJ4aapJGsNWjN0ydvNHx8aZpLuCj09pPH/F8MKJLpO6
HL0q8M1HwtlKSyTpKj20SX0VWbsJsPZdJZ7aKLsjvX9cI4ataIuodbM26az37/vDt6b/v4Snyyyw
UbrJpN2WVKyjZ34yZtnvr0yZPpu9aPhhkonGzRR7SbslKLHC68WVZaaKs0IcMcMNM1XDK7Vmusmo
kxs2bxq2Mmq6rRRqzapuLNWzDJjhw4c9sllHSS7hssk0fuiD939EQfTlEfvCjsGxCRo54UKESJ0B
u7YZ94DYAPvpBlQvFKwxUYUJ7adQqoV0KdJc1WLlELJJy3ApH0gxAnD3fIeQ71eQ79qPFz5kLTda
vHfPLMx6ZJJEkJORhUTvMSpJr8lstW1TEjjEJArbbz+DA37teNtCNHrT0kligQqtLDB7iFpyJpeL
XAbaGYgXtIgvOSRahwWwbkUhBDMMhp5zeA9ZA7+PGjgaOz64gpnHN3FFuqAfaZmKMFEuanvD0RbI
WXi6ZRls4lQCSgtVyLtoUDJZjTVK3l3lzCs7AxikZUFnChZUwggVJpAzEZ1P1rkGH6YSgYwVXzG3
rTdHWOLmCo6OenwAcIAnt2vTLzmFOOb4LxvU3fA1d8dIACgQigeT3a8H/LANYprNghQlI71OMAsp
MAaBFpEgpcB3Eiw7z40adQ/by5Z5Pn5evQLpqvciplLmAuiZ7e1hEoSvHvgAwheFNGdpqdslv5MI
Q1iDLzL5u+7ZZaqUXUgZEE/wv5n6lsLVYEsx0GaB8npKQHlIUDsojNjtXOyIDTSrDdVw/qzdv7Ih
7e13Lt+3LccN8yqpzVgTdAq23Tlzsj3dA5FYKDDlxm5rJqG1fb4181b72znXPX4dNFIoq7pG/zs2
VbXZxSEaKa9NUG7aSjJe+L+Lu3qjPzts8c+nOj8s9/dlnp8JqNHUa+o9viUkSv8dUqlKSPjHuF1g
wkIpGOfBkA9DIadbDhJT8n/bLKk6NYRmg7a48q56xAo5X+VQTnMU9utWafU8GUkm0q4NiEaO57FD
IGzJVHJBghdy6+Ul0y1nD21L4y3ZStaWzmaskSle9Lru8z8JuvAgpI0FYFaFZs6Of35hZE1LEQ1c
rt3LZkxIcG1u6g06o0ee7wZcq192W4aqRQOJZuXLJV25daJKqtNXpo6c8+nL+6TVhx4+0Qj9Qcde
uaQ26nOXuWjFCkSiE0wUKR+nx6QpEi++udtjnZIM17GKNujWDSKfHABCks6IrkSMTCQrJXuVk+BR
FHzVzk3ZrtLa5ktU2PGLcOupJsRsw1aZHALHAwIIEE4kdSDfXsc4743l4VLBBMIiY1Xj155SMCXd
7N706PKCLTN5OCgzy92z0LXgrY5jCStknkIVWeCjtJo2FGMmEqX22cJ7v+STKCKF1nrM6cPTCrfO
q7xdyiIfvDE5c3vvO3l010u8WvK/1hEO184Qke8NOq2l10oidMltUnFVEoo1+tM2rJu7WZWfEIUl
CFsE4mkkm9t9ddVlmxds5VZpt1FJJ7R5XKjzETrLDmzQiIYug7RBJ5E9tnJOtekHmfv9uy5g6Gdi
TgOkLwRy5s5UjkNGdVUpRZdMnfJQ2s6dpm0l2Zvs4YSDYigOdcE+MQBiASCmpKkrvfq2jl9+dmYY
aMp57qmSa3v3490pvlFJN1E2eafCWiUVb3imFIg5cKsLpu8m5N4r0dMmzkg/T8hJDPw/iAORFvvo
+Hie1kRDHOKbmKJfNiIj7PY1Z5EISN5MmdETS+XmfIxYddiFrKSayyS6KJ5Ph9PrveFpX5Sz2Xsf
VhPVd95h4fKCInHASqlJVLg8Vas45RMDiO6/X7uGG71uZOPwzVLe91UaoIf6Wy4BeYVhhKXjCNL5
YB8aMHaS/fwGI/YzboGbJOLrUfiwo4bN/0vyVav42qRJpLWEZW74bt11bJpVdq8pIRzm4d97KtV2
rlq4fpv25PHi6zPjjlZq7f8vajxftNN45b+LNW++7HRhRdVN6Yxlm6Wc0ctF27VJNq3aOU2TGMNV
KeRt4m/bomzVMm26urVs1WNnXVH992raqjJ6TcNGrhmnVm7b7+Od1a8K/v/glQ15Yxzs1cs/3S1l
Gjl6ySePlydKLtUklXjNN6eZF9F4XjVP4e2tqGs+1mk2ybjGTxiN2aTJpNdkw5cLbN1U+eSIhaTj
SCgofiICUnHWjGk5lleEc7oZ0g8c3UASCUHGJGSzoBd1nc4owSpyoVdIIBIYGBgbLoZ6MfO+AQRH
LUfsA4hUITyQMk6UJ5wAwtDvweV1IO0IZwlQuzPUx5m1ioyS+XXoHX88Q44OwsVVe5vNakmP7ZDD
CYOiEZr/A+bgcQRz24rpqbnJwbiJV5doEuKKEhIBDGSExIClhecxqF0bLnj73eM0jXqeKiCkwGgh
A7KiS/DKQ4t2m408xWh1CyyQgm6KiLmZhkNLXVJLe8SJOF2SZVg6ILiaJacM5lrkUZJahOSXLQIt
3zA07CCouLWnbS6nLq4gt1pdYzkkZefqec10bb+gsa+0168SqRdhr2I0EFN+KNm1fKiRGFljdG/w
3iO3ny/W2302Ea1BW2N1mALQMQRyydKpologdfysZTWWQRSCVSGOih7IC4o2OQsaswTPMui7TfgG
ecUvNeEZoh7udYlYhHDl74yjbPOTdGda1hraIizeEWCSIDpoxFUlE91JogH+3Jdnh4zdXZefwo8e
la/uv3jnqUcWTpSVd5+uHr1xmhuSkhYP0grDGzOyiGNCA3Z5R59fCvbrnD5x4jHGlZso5QoDy7I1
RoaFs/EuULo0SS/KzanRSAtJGDk7nJzozlIGkHMlN0ohPv+iqka6tu9ZIRy1TUa3ZvliNnnTucI/
TlA8q/cMdEl1faQ4h4V/MdXb97boYO4fTZYHgSE5nDTC7eIKsAutaPp9Mt9lvFEPHxX3V41emlXi
uJWhHZspDJfRebxh6elF0MlalEmF7svGSb98CUQfy88yjmM8+KS10iYjypzio4iADHwQLQBxrZSQ
XGSC5zLsDiyz07aMtdqaWaawiWyrqjx6cNtdEtF7LusWQ3WfeqrDdwwtWTvlhum966P0Hriso25r
jPlPFSr1mn571hAdG+iULCSNYNSbEz1OWvmItNxCOJqxWqUI7TQ2WStp3wz1WTTaIhZhw2RPHKIV
6a5tV30qls0rRpLZoqZI8px0FGApxm/aajA1dHDcYa3ZslkWEZF7O8TF+k8ZDhY8zNeLYhclCSNS
QJEpAel2sQjGuS3S5REM4iOnTW3KT8sMQLIWhFHOTEbrM5Uk3qllXMOnvJZaz2x6zo9MMl7+kBk8
Zt3Tpy66UtFkdRNSk54pVRKsb1OQATSgQSJGEkSizwQSfeaNnf7ZxHWTxBGr5v879LKs+N1HVUvc
UXXZRQkybN1SjSLqGTJ/jm1e0jlQsojhWayfq1dc5oa6yk0ola0Q5a2vdIyEX5KWhHtgHLKINMsS
trm9fF0/ow2QF3bRTCXGrJSK+811mNnLXplpovhOPpm0SfazD2btXyswPBkydGjRYB1VesoyqqZR
siLiKog2bCyaXwGTjXbyOTQB8B5ZpQRh6O50SU5OVzhksp1LzNVWrl5llHyy7YdHjVVu9ta017zn
SQIYmALsyW+IIZU9ua49XpgAtIUITvrSEZpQijNotaMs3eVD0t3WHClnqEb2ZIac56N4hGhrnqsw
324yhu4WpEaXtJesehxrxmAKxVIiqNoiGk746DX5UVhISWq94nvDa0ZKNPJ2QmZs29f9UzlEFUbc
MA4sBZLhiLhhIiG+OjF3t+domwhF35k2/airZjXZJR/SCJFlcLN34NGz8/WVO2EqU3SJxsjZropk
nJVwwqyb5v1/so72cNWzZllVtu8a7ZKNEnar0iH8mRv01Yijtm2NknbO7JV2okzk85YVZtGOkzlZ
mzXjphNy4q2ZbucWnRtjejm1/7URDdRzzy0btHOEjJR01s3ZqPXqrN43eNWynfjlmUYzdv3Z9/KC
6f7v55c6MJTavTCb0ze89JSklk8jhm2jZ8GnpV2eNz06fHxNZq9dOnCrNfpysp6Uuphu7sukuqhd
hy55q1ZK1m7u0aScOsstGTCaTu6WzlZQolABpIXgDiuG659e3ndiEeVxic37KL1SBC6coBfAOUM0
wgpAJWIX0bD0obstvxi0AluAGahB5MVH5xU/1Cni05zvd7VNuvKrWt38b49szjp8ThEQCO3rzfBO
bXjQiAvCxMQN7emHCtD4CUpsspEsUQoShlDiZsrcKIxutNYarWVmcNODko5oFCQLl8ezESeYRLAF
vjxqklJIdgxWtK14oq6SsUTSrIYYFq1Muwd2ZmNMxQIXQMRgM0tDFzFApUnFTHdkjHKMaJiybtxi
cvONSW6udfpEUorVyaq9XOnnN3B+QklaAmEVzg9cHCK7O/qryajtqWyvtmvbfcqWUsIOJpkbxwF3
4o1cRqum0OHE55oEztYR1mEBYXvZBdNTYOf0VzmEiznhZ0GBI6kiDCS6/FEQwhpONZRCGjTwyxKv
lw2SIi7RZt2yf5sCENEkFkE/3P5UIkXi0EVOJzMnpbtW1IbdMqlojt8aOa8Mkl+IMxDWaGrPqTqf
PS9JHoY9JyuJlg5P3WMTpPLwdDKPM87LkflqU3DivSG3UzeSbPcAvII99S1pIVDJPYEIfuXQkiZP
19duO91jPx9JdcyoybElmFKrTimqUQU+++a/Dk+G6uc4IjnqVA1d3edRsrWD75vTJhubabLNpv2E
Lm661Rl1BEh2h9SgmXnix1T8ZwI5AKtSheo+ZA0kKb1sq0tmhmzerFj1NiQaYWZ/dnTPXKkbMvfv
ZaHtJfAcR6bWfCf25U8jDJytpKXb4O11HF1F/GTR+9Ee6viXeKTTMaF3q6pU5inBnTiRIyIYh9Au
PPgWsiIvu1XvGA0c1rYvs4+PjxllC/RTxKsN0Q5gS0SVrHNERlNPpjjrql82b6WWwyZMYVQaafCd
izLSW+jCTvvLLiWNXUfKc3LphppRsTOCZYfTjpD56GeV8swJQASSpUWyhfWPJ6lYRbRm7dolERV8
PStsmWnx+MIteW0QdKPbW1Ot50M7xJztVhVWhpWLubvfurxk+GUQTSS+fUDlCN7aaZvTVb8x9vl7
s+jKdHTdttSr1me5Hp8DpcuPc8gLGDialLtmce8AlFo+GpEUYwIJMkexjWvK7r4JI8qzbtZgEqQG
1BcWi6ilE8NkI8T5dL3k2+WdK9S0rXciPDN5U2ZN1uH1+i26z2qokw9t35/wjOtZ/EQ3zUiabheI
hbvdVisQjWTe6ynv6fK1o+flSmI+EI0WXne7raCJwq5+WbF7445bsbr3s6WTauuqPaTR45fKbh5u
2XSbU/Rhz7zpFOytaRifmdOaxEJzbPWFa0o889uXFpPuhCpSNGbPbK6z0xntZhiXpn3m47menfHj
Lnp7avuIfyaziTK466lgYhppayKdsJVEKkulJXa7WuVyQgISyStCRAoGcHxj5HufBjQJ5GU5VuAb
1GAD6XxgiN5K/CnHFc4zbM0+1d+naijiU99KcJz0aqs/q0/2bvKNWSjLP088uo+z0/r87iM+GspR
SFfW6IPUqPS6yqq+rU0mqyZM/heeVWMPFY1tNzps9tLcfDTdy7e2MYVXZcsLsqUcLxwTlIMMGp/P
0aeHqk86nFkuOVKbbLg1xyIfslgeHUCgLB+H1UUe2ajQwxd9k8myj7KKqZEmrD8Gji7Jk1VK4Zvy
/KjhVCZwT6SVbqtW6yTjh+LemTJq6XbJ8r0TdNN1v4bWzSnm2pq2ctXLCSeGbDybNlhJhDbUwb8q
LFHLRJATN0pNmFWG7+UM0kcqK5Lv49XLZdJ0mxm5YbK9aslGiai/jJ1GmrZs21RqS0TTYcXTdtkG
azZqqptum2Ij+eIRLLbRyCqqic9X6bGTmzcqpksScN2rVVJ9Ygdxmw5aNnDR02XVbsntNW7NwsXb
Vcp1rLVRr+kPSrZvhmzc3aNCibyrxhu2aqu2TtRq2MTd5KM0RD2f0PX8EKYFvoUKBVgDaPrVAkAN
p3cEjeugG3YDq2mCRu0nZWUiQemISPCe2IvAFKFjpgOJAuFJerPDR5wAe/y393v8vbP3kTn77xWI
usqCb86RrMbFEFCpQT9e/38vR2KwfNdcc9Ri2qh8YxmGrtw01FRbxGJIZlruxQ8VDe6IQbXDRGVe
QlLL4iFt7ZEwboy9jGpZ/aQkc3rlbzcZuDGovgknEkVkedInEGM3zJosl4IIkvcFAgYjzahYXC00
Xm8LKkCnqaIu1hXm2t1kNhamvGjLLlpa2Z3rML0ykIAE5sqgKjseO3kzyrDLXrqezaAFk4h2iBSN
kYKb96CYkQJEhEpC8avqcXSMfs9KLiGxmyojpCnBzMSGJiWMVQckZBHWchYR2EEhWES3ZsAjJk3Z
r9Mg/maQj/CGb0tVNyrG2icC6EZVbPjqyD4aMod11avvVg6SZOqvr0ZqTel3v4SzeRAs6n1lObE6
WrKs8V54+LI8vEHkRiIGj6MNWVy6IZ6Xm8Zu2STKsQHVNZUnl1JOrvJGcnU0buNLrw2TZpqMohGr
RdS7u8NXxopd0o1buGzV8KOvsTwRbdHhsVEDpiHw+emtcefnrS+Cd4kgoeWKCHA7hDj0h1HPgcp3
Sxy8zlp06YJCScWVS7xfGpoGC8GBnxMmzC18q+laEdmhfgJgjJFF1oyqyZ8quurx7Tb55tEETaPx
dlHZspw44urLnfybOHqA+ZVdmzhluszZ7JsOU31vfy3Pq2NbUn1K1K6YreL+njW0IvVnV8OLm8Wd
TJ0qkODZ9f7W5PtZa5lkLuB7Gc+/1/E+HYhqFYiijLbjlsslYYPFk1g4IrpBYtzNlr0Mty3GOMWr
lV2lda3fTfVLC2rJ0pvCM+5oXvny2sDySTsJtzdypkhEkk3bhs+z07w5JcjoWLEC5Y69NtxqRMRy
krr4Qrww5Dh5eWHAtVlH23WZJaLVZPFW+i6dUpIRJTVy532RD4cr5tpOk7N1k3x8TdOEnuTxJR00
eatzo14M60loXcgHHgxnyhvFOCD2OxBWzARbRRwy1en7ULbfgZbPjtu3aKSZNVHp3w/MGP84f5a+
c5Ka9xSJZb3XNsifq7N2kll83zaNPkq+uNPpXjNok0SSWa5Mn8ApuxZ8IGc05ab1+HL4YeNFWaab
HLplK/PFt7ROV5USzVVrEFbbX4zt39unTlNn0xws9Jt+Pvoxo0WUjqWVVyy2lVMu/F1NSbC/cI/p
33Sl8NVGHtymuqm3I4bZufyNIQ/g8v1tJCUt7yiq9IQkj5asnuTV7sx7fMl1GkknL07VWdXcvT0z
fC9nbJK6rSJNmibwo0XYat2zlvskhJZKvTPq4sPP193LHXz3NXlevFwa8XCsWS0YS1ZLhHcPvpum
k1bNlWztwyVq3TXVf8ezhhO78Wjlds0TcMnThq3Vnw1u5SateDCzhq4ILRhmw2fx0Quu/P+vbZ6W
8adrJTXTaJYTeONnBo338XcZGqrRJkuosr3s7XZqpKrMk26bP/phJddy2dM2STYpoXHPPTRwuwnK
aXKTGLvTxowvfihws4UZdNiIjp4uw3UVw74ctWxoWMmjnnthSl38JMNEtGrtyooobNlV2Tl28448
cGbr+fRm1VUTWeNmUTu6cJM0p0cu2FErOmGbVa5qwU+KJ581Hw9ahtDb+3pANaDuFKw93zCkJv8i
BOg+AcUP04LiFnAH3xGgK6F50O9rad3sAA+z58/QjngfWHX57/PIVVC1LMfCYNWNN+uJ0SoGFKH1
vX60Brpha64fmIqtmSdQGF0imAUPD0+NWUgUipUBxeM5t8B5YCLfBj1DqkZUsazQFYmhQWODPJF3
kHkNNENYniMFYm8IOSESC9qMZtlbqSBRpaLjVG4FxZwypspgiBLswBtwAoYE3OHHDmXYW11lU8AR
cWzy65vB+IP04f44OPr9DvF7M62W6SS/KBOkQCNB2EcVxQ4KaIBaWZCHFWFCEKQ6ENICRxb8TG3q
PKsw24gtymitnHF1EX5XRk1CQGHXBskSHRZ++gFx2PU/L8s2+aDxkk3aGauOaxZhKcSkhEQZNsUE
RKQiEpQID04eJoQk1eklHtm1R2ZRBgzfso79e0XF3VdmYx2diRk8uIVCRpBEREcX0D3fsIS+gGFj
XGP6ZzfbtfG3FRF531g9rk5pNXm984gWSV5hGLz5k0Tyo4TLrH/D2u0iyTtRJs9NmqbpCM203eeW
vCisjyepTa1JRpOV0I+JtHrNnjSzbTFw0s82hHpJZ7bPE2sc/GuzNA5Zr3lLBtW8QUbK4zgXyy2N
Vlmbk4btOnpNddOTCENEjNNzOi7uc60klCd876M/7Mt1dHpnsy+Sm73tKKuIRflrPr2xSSbSz3hs
u6yDjpBLRNm5ly2at+HKjh08UzZuW7WNOFXFS92X5H823pr3+yXUkvVZT4VdZS3339MUVm2ZzXdy
cxBtTtVjbt5SLtYgaIDVVVhj4xAR/RTzNh/b0q5eersO5NVnLh86NHbRZ3XS9cqm0q2lSaSS+6oU
y0XiqmqzTOMlWj28tEFK272q119N4qZxBL7/XtzJ4iD70TfMsPTVk5xHfHSnxo0drtfiukvIn4jF
Urzs7nvP++9Xn4fRo2j6TeJY34TdppK8+O4w1Vk9ZNrLrFVHj29MnD6LcmhhZN9IRx89YdT2pSkk
viUSSTvlnrxxf1x3B1YgNjI6LoevrrRt32PdyLw+4QCD449B7urHldB30y1b5pqRu3USaN2TVvhB
01vTT1vlEJWyCSSImuqM69DrOSBADwtqD1PI6Oxrvnsqwpk+FMpuOV11mGTJJy02aJtF3b8EQ/QU
C+s/FN5vI0mp5WKzUmrE6bTZMOnDsq1jqS0kDKKM1WauSSnLJZ5RaGq3v5+FnPLd1Zmo41dct3Sb
XVnAjKAiEgjw7r0uWaeC/ZxbLure7+SxOWxhKVyVXCho+WyEcHxjyb80fd6XfdNhZqhGxNJROPzq
tGTC36HDRbLNm14X4WYcPwZNmqXLWuSpblK1pruFdmzZVRuo1Z4oqotOybiaThNh/ihy4YasM27J
ZZVzGSjfdmyVSUcL2XcuUl3S7VdhJRmyqyRDF2E9WjdNkm4UZPy/i35Ijpm6fsQ5c3aLO3TbbZa7
LtkqTVculyjp+izejdd/LEIrml+xs8duEEbqM5sqMes8OikcOmbJm2XYUUUXOIwkry4TYSTss+Wb
JJ/XFDVhZPNXh02TSs3aOGzJqhdn798tHaS8mrPGPlRJSnKySy1FIR0gHhBCMIh031xbXQjWGzAJ
Fa0AWQCpdQuLRRrSS4+PcEjNCGnDi8fi30Zg2kK4JGqznDRmzZbTR8qI8R9ylMqWSfUWn4DoPA4/
JdK5JQmo/REccsnAD3UFpA5WTMsum0isOnxtIBMTdVTkA4FRXGEgpAcM4wtRhrkTk1MxBJKAGcfU
K+ajLaBIwgBKQ5isGzIzLaGYEHDZu1R8xVmgtTVZgmGtlIJGmxUCvANRa6hSrWpQUbjTrmYxdFKA
KWZso004ZlAFBNtlY64utMwWbp2jURjKOeeskIABwFHTcRMBaPjieud75i49+OuestmeZjnGazjw
AeIQuREaBLf8dJBEgECEzndY7mOFz1k60eXbXmobiIuFiOrgFhVjSzl5/PRoabziIHv7MEdEHhEF
wYAb96SfrWWDiSmOgk0Vghg0n0s+Enfer4VaucYxhm/r/hx53b2k7nPd3T1acuf3ZPGaIZdvI9Qo
mwREhEToPcq/u6C3kRi6msYWAOhI8jJkCTiyAsmXQVQzSgiigKqJFqRlVrSCNHw3bMP32YaJv5HT
J11J3HDUSR/PWU9B6KFUz6M5Z/J8yWGA2Ij5OgcoR26G8Y6xFbPa+qxEIa7YUfb7TiG6UQcpQIm1
UbqoL+lclXK72sk4tEEfKsbtN82YWduMKuGiqZ0u/ihH8Pp7+3psN3xKnG9qzqr6tlWYx89Bqzdd
qasJ5w2lEMJ+3bGhXKzxuu9+9W6zfd8uqtUunblNdJ3FFnTCqAw867rbuOgzs0GHZPHAqZ1HWggS
Rrb6EhRGYZu9F18Lgj5Yz2bLLLcYUZpMOW7Y70embiXfslam81M50u/g9/lFIV6ctIUREpuXdXv5
3eeo9OkYPb38fOLMNll2lm6zS7CqrQzLoj/Q9f5g3r39OOJyS9T2nXFLc/HHl76Zxqle9+2Nnmmb
JJqm28b+/ebprZnlq1SSVWv8Qizpwo4b78xduzSUcNUkuvrdRTlOXU+J+aFsm2Oh5Hkdtixnv7DQ
jwOFF+CdWQ+chACMC+KwSWbvo3LJP1oh6dtXShd42Zu036vXxiPiUrLzpLRzzlv5v3nErsspz6ML
njvco4frs+nMt3pSikaMqpZN3Thy/DhbNVt02dJ2fX68PTVCEk3Db7Qify7WfMldaeqS6SekvHVm
zx5qs5WfDtRm05rlus+9H3+kZMGrNNd0vIbYUkx0pJV0KhIQqxH61+6R/J1Rw9smzkcSESmeDw2Q
bsHZJJaKC8OGF5rS6XSyQjS1AXB0iGkAwYX2aZkmw/X+v7xvJqiH9hT+yIPybpinJy2dSYaJpbbc
uFk2zpPZm5aJKNW5+hwqzaxN+phpNKrJq3YZrpPGHpnrZuzaK8OHC7JJw3535bMO+9ndnTpJRXDh
2s6VcrtmjdJw5a/vhH8OHDxzeP466qLs2Htsp3Hirhksu6e/fjVu6asOdUvbDDkzUYcv6H6NoKvy
TSEY2fhlKB4RKVxwaTiEMKtWsPm5HZbLoda5+r5bt3LhI2fRR+D7apMntyyXXf6cnw3fCmt9C7Gx
zlgtWqQqXSmcnjYc4kPkpCWB5wgHCmcmmiK8pMWCEpHyOQnGUXmiSHxIruuMGWhIe7dN8uSIUnFw
pBsQpOhCoB3RGHMgWcRaCEAfSBgivf1W5ACS3DhDdrS34ZoWZn25GuPuvDH33MELQejQfchQDlCg
FPLafPyQkZTaaFmBDS8TNsu7gdVnKvRxdEXTQjYxIGDE2IMaoOB8ZwioSKWFjdTG6tTiJzO4BBz1
ZTNPZOFOZKlbsCcdTjokqjLqXUU2ZNTPW1BrDcF2oalE0oqC5C2Lt7tGxaWbkXN08MtCmgrCUzMI
fBQDGYZZBIvW1QIPPl2Lhd/GPvT03veueuvXWOec6Wyyy71DvAI0JQjVCaBT/dWALAJj07Rxy+Yc
7zCF26fUy6ib4q0LzvJSIRThZowv2kVZN1OJLpogJsNH8WbKxFhtrURcC1AuwjqOQOOwBsmoia4v
geDpJZyyUdJP1/rlLpk8SrXVdVPJnVmtlCIHk8+f4CBlYvq4dwVgaUSRyCzeA4Xwg9I9/u36VM8S
zy3cbTNWTMzESk1TRGqXar16maNJwPTdNNNNhleVM5Fz1ER+OahJiOm9FF6JPSqTlPTvHvfE0t+P
7M9aohDyREQ2nsotKIZUcKFrauNJWhuSQD2+OaCIMZzWSs4+HERFYRCIowzmqu5SZPebmLN3TCjR
m1duPi7lw/T6+PNqT14lVJbLzm/HVs73jR5xo227cqNqfzeea1SdOgY623cN0+MqLnS7lZ321SW3
cOHoqlguo4as1bP5fo+acN43478QPEEe2FZ+L7vSt3jZd6bSVSb6M4tDEI17ZzeJbt8ti2bPrbhV
84dtU0u2blJD9oZonvptzaW8pp07upilJyL6MKX2wokFWqeT4TbpRJd0kk193/chG68mjNpzVvRC
Pjhe7d5w6UbJOFGyaSvw/vIjbhKrNy1OjXvno9ZeTpK8qSpJNJnW9aazjHRRqqk6S9IMLRI4Cyyy
GetVBUX6nJ5F+/yzngZ8BklrYfbqy667VRwzfDhkk/af1r0r8vfcfK03z7+c3L5o+E5llGyvr1R4
393STUbp/PxLt8NeGR05V87T660QEqasKXb5rKptKrMsOWRLtxy236bO2STevr5jSnkr3kvEX9rV
lZzNWVJ+qVle3U41vOcnPTJjH/By+fPOU9Hy9Vn64ic+qdKJMOXfT3q8VaMdHy20XlxrT3SUuKNW
ybVT29fWIi2Tf1yk2SeKNXidcbNvzV9qe2bCvpxs9OW7J6PThTVk/25uGrt2onum5UbP5ER8ZAGG
3NI86buvexT9NMGOXOgsTlqqACcGlsYOFQhP0zbsH3fK6rKiS75iblcmqvz+H4/o0dN3TNm/F4Xc
vum36UYdrOvTdLrqa+7t5wokz2eN02HlXVn7Yg0ctNXpk2XaNpNpP3wi8NiI9NNHo3blVHUcO278
zVohFDVxdzNm/bkFXx8XbNO0o2dR+0+fCy706YdSTQmuzTO2qzFHjCzJ+3xR4ylJ09qrOXJRnhV8
snwdNGj8stZdqtW0Ws/d9tpT4Vey6trylKc5SaWTe+lUvaXKCOn93ys6ZsmTnj2q+32lLdy9rtE6
MPz7u0lHthNN9JUZuXpJypJu21YTqom7dqllU1nLVNxwzXf9rm5eTz+cTpO8AYoGavq41c5p7NXf
VcLgHEjWwXnAK7EeioHy5o+dHxIHuwB0E5njAOdHwYAvcU2cpSWEtbw/xRzIp1SohlJD3dxov0Ri
g9QOSKfokRyb+W7avS9UQ/3DvYyLVQSM+kVMDdASpn5QDmGTBlwTA1PMH85kIW8v/fzRas0QRMxy
JnBxCrFKAWGUOGyaZQdl5XiQri6fSzuEpE/kEuv0QLDmU3HDJFIfKimGhDbcKDmMayMCjGblgOES
v43aEiQ20MY9sYvTZ5mLryY3Dcw1YZuwfYHl5FQNsUEkR9AAw8KgQP8LgABSB6YAWwoBK8KiN/Sl
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


More information about the bazaar mailing list