/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/reconcile.py

  • Committer: Vincent Ladeuil
  • Date: 2012-01-18 14:09:19 UTC
  • mto: This revision was merged to the branch mainline in revision 6468.
  • Revision ID: v.ladeuil+lp@free.fr-20120118140919-rlvdrhpc0nq1lbwi
Change set/remove to require a lock for the branch config files.

This means that tests (or any plugin for that matter) do not requires an
explicit lock on the branch anymore to change a single option. This also
means the optimisation becomes "opt-in" and as such won't be as
spectacular as it may be and/or harder to get right (nothing fails
anymore).

This reduces the diff by ~300 lines.

Code/tests that were updating more than one config option is still taking
a lock to at least avoid some IOs and demonstrate the benefits through
the decreased number of hpss calls.

The duplication between BranchStack and BranchOnlyStack will be removed
once the same sharing is in place for local config files, at which point
the Stack class itself may be able to host the changes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
"""Reconcilers are able to fix some potential data errors in a branch."""
18
18
 
 
19
from __future__ import absolute_import
19
20
 
20
21
__all__ = [
21
22
    'KnitReconciler',
29
30
from bzrlib import (
30
31
    cleanup,
31
32
    errors,
 
33
    revision as _mod_revision,
32
34
    ui,
33
35
    )
34
36
from bzrlib.trace import mutter
35
37
from bzrlib.tsort import topo_sort
36
38
from bzrlib.versionedfile import AdapterFactory, FulltextContentFactory
37
 
 
38
 
 
39
 
def reconcile(dir, other=None):
 
39
from bzrlib.i18n import gettext
 
40
 
 
41
 
 
42
def reconcile(dir, canonicalize_chks=False):
40
43
    """Reconcile the data in dir.
41
44
 
42
45
    Currently this is limited to a inventory 'reweave'.
46
49
    Directly using Reconciler is recommended for library users that
47
50
    desire fine grained control or analysis of the found issues.
48
51
 
49
 
    :param other: another bzrdir to reconcile against.
 
52
    :param canonicalize_chks: Make sure CHKs are in canonical form.
50
53
    """
51
 
    reconciler = Reconciler(dir, other=other)
 
54
    reconciler = Reconciler(dir, canonicalize_chks=canonicalize_chks)
52
55
    reconciler.reconcile()
53
56
 
54
57
 
55
58
class Reconciler(object):
56
59
    """Reconcilers are used to reconcile existing data."""
57
60
 
58
 
    def __init__(self, dir, other=None):
 
61
    def __init__(self, dir, other=None, canonicalize_chks=False):
59
62
        """Create a Reconciler."""
60
63
        self.bzrdir = dir
 
64
        self.canonicalize_chks = canonicalize_chks
61
65
 
62
66
    def reconcile(self):
63
67
        """Perform reconciliation.
64
68
 
65
69
        After reconciliation the following attributes document found issues:
66
 
        inconsistent_parents: The number of revisions in the repository whose
67
 
                              ancestry was being reported incorrectly.
68
 
        garbage_inventories: The number of inventory objects without revisions
69
 
                             that were garbage collected.
70
 
        fixed_branch_history: None if there was no branch, False if the branch
71
 
                              history was correct, True if the branch history
72
 
                              needed to be re-normalized.
 
70
 
 
71
        * `inconsistent_parents`: The number of revisions in the repository
 
72
          whose ancestry was being reported incorrectly.
 
73
        * `garbage_inventories`: The number of inventory objects without
 
74
          revisions that were garbage collected.
 
75
        * `fixed_branch_history`: None if there was no branch, False if the
 
76
          branch history was correct, True if the branch history needed to be
 
77
          re-normalized.
73
78
        """
74
79
        self.pb = ui.ui_factory.nested_progress_bar()
75
80
        try:
89
94
            # Nothing to check here
90
95
            self.fixed_branch_history = None
91
96
            return
92
 
        ui.ui_factory.note('Reconciling branch %s' % self.branch.base)
 
97
        ui.ui_factory.note(gettext('Reconciling branch %s') % self.branch.base)
93
98
        branch_reconciler = self.branch.reconcile(thorough=True)
94
99
        self.fixed_branch_history = branch_reconciler.fixed_history
95
100
 
96
101
    def _reconcile_repository(self):
97
102
        self.repo = self.bzrdir.find_repository()
98
 
        ui.ui_factory.note('Reconciling repository %s' %
 
103
        ui.ui_factory.note(gettext('Reconciling repository %s') %
99
104
            self.repo.user_url)
100
 
        self.pb.update("Reconciling repository", 0, 1)
101
 
        repo_reconciler = self.repo.reconcile(thorough=True)
 
105
        self.pb.update(gettext("Reconciling repository"), 0, 1)
 
106
        if self.canonicalize_chks:
 
107
            try:
 
108
                self.repo.reconcile_canonicalize_chks
 
109
            except AttributeError:
 
110
                raise errors.BzrError(
 
111
                    gettext("%s cannot canonicalize CHKs.") % (self.repo,))
 
112
            repo_reconciler = self.repo.reconcile_canonicalize_chks()
 
113
        else:
 
114
            repo_reconciler = self.repo.reconcile(thorough=True)
102
115
        self.inconsistent_parents = repo_reconciler.inconsistent_parents
103
116
        self.garbage_inventories = repo_reconciler.garbage_inventories
104
117
        if repo_reconciler.aborted:
105
 
            ui.ui_factory.note(
106
 
                'Reconcile aborted: revision index has inconsistent parents.')
107
 
            ui.ui_factory.note(
108
 
                'Run "bzr check" for more details.')
 
118
            ui.ui_factory.note(gettext(
 
119
                'Reconcile aborted: revision index has inconsistent parents.'))
 
120
            ui.ui_factory.note(gettext(
 
121
                'Run "bzr check" for more details.'))
109
122
        else:
110
 
            ui.ui_factory.note('Reconciliation complete.')
 
123
            ui.ui_factory.note(gettext('Reconciliation complete.'))
111
124
 
112
125
 
113
126
class BranchReconciler(object):
134
147
        self._reconcile_revision_history()
135
148
 
136
149
    def _reconcile_revision_history(self):
137
 
        repo = self.branch.repository
138
150
        last_revno, last_revision_id = self.branch.last_revision_info()
139
151
        real_history = []
 
152
        graph = self.branch.repository.get_graph()
140
153
        try:
141
 
            for revid in repo.iter_reverse_revision_history(
142
 
                    last_revision_id):
 
154
            for revid in graph.iter_lefthand_ancestry(
 
155
                    last_revision_id, (_mod_revision.NULL_REVISION,)):
143
156
                real_history.append(revid)
144
157
        except errors.RevisionNotPresent:
145
158
            pass # Hit a ghost left hand parent
150
163
            # set_revision_history, as this will regenerate it again.
151
164
            # Not really worth a whole BranchReconciler class just for this,
152
165
            # though.
153
 
            ui.ui_factory.note('Fixing last revision info %s => %s' % (
154
 
                 last_revno, len(real_history)))
 
166
            ui.ui_factory.note(gettext('Fixing last revision info {0} '\
 
167
                                       ' => {1}').format(
 
168
                                       last_revno, len(real_history)))
155
169
            self.branch.set_last_revision_info(len(real_history),
156
170
                                               last_revision_id)
157
171
        else:
158
172
            self.fixed_history = False
159
 
            ui.ui_factory.note('revision_history ok.')
 
173
            ui.ui_factory.note(gettext('revision_history ok.'))
160
174
 
161
175
 
162
176
class RepoReconciler(object):
187
201
        """Perform reconciliation.
188
202
 
189
203
        After reconciliation the following attributes document found issues:
190
 
        inconsistent_parents: The number of revisions in the repository whose
191
 
                              ancestry was being reported incorrectly.
192
 
        garbage_inventories: The number of inventory objects without revisions
193
 
                             that were garbage collected.
 
204
 
 
205
        * `inconsistent_parents`: The number of revisions in the repository
 
206
          whose ancestry was being reported incorrectly.
 
207
        * `garbage_inventories`: The number of inventory objects without
 
208
          revisions that were garbage collected.
194
209
        """
195
210
        operation = cleanup.OperationWithCleanups(self._reconcile)
196
211
        self.add_cleanup = operation.add_cleanup
215
230
        only data-loss causing issues (!self.thorough) or all issues
216
231
        (self.thorough) are treated as requiring the reweave.
217
232
        """
218
 
        # local because needing to know about WeaveFile is a wart we want to hide
219
 
        from bzrlib.weave import WeaveFile, Weave
220
233
        transaction = self.repo.get_transaction()
221
 
        self.pb.update('Reading inventory data')
 
234
        self.pb.update(gettext('Reading inventory data'))
222
235
        self.inventory = self.repo.inventories
223
236
        self.revisions = self.repo.revisions
224
237
        # the total set of revisions to process
238
251
        # (no garbage inventories or we are not doing a thorough check)
239
252
        if (not self.inconsistent_parents and
240
253
            (not self.garbage_inventories or not self.thorough)):
241
 
            ui.ui_factory.note('Inventory ok.')
 
254
            ui.ui_factory.note(gettext('Inventory ok.'))
242
255
            return
243
 
        self.pb.update('Backing up inventory', 0, 0)
 
256
        self.pb.update(gettext('Backing up inventory'), 0, 0)
244
257
        self.repo._backup_inventory()
245
 
        ui.ui_factory.note('Backup inventory created.')
 
258
        ui.ui_factory.note(gettext('Backup inventory created.'))
246
259
        new_inventories = self.repo._temp_inventories()
247
260
 
248
261
        # we have topological order of revisions and non ghost parents ready.
258
271
        if not (set(new_inventories.keys()) ==
259
272
            set([(revid,) for revid in self.pending])):
260
273
            raise AssertionError()
261
 
        self.pb.update('Writing weave')
 
274
        self.pb.update(gettext('Writing weave'))
262
275
        self.repo._activate_new_inventory()
263
276
        self.inventory = None
264
 
        ui.ui_factory.note('Inventory regenerated.')
 
277
        ui.ui_factory.note(gettext('Inventory regenerated.'))
265
278
 
266
279
    def _new_inv_parents(self, revision_key):
267
280
        """Lookup ghost-filtered parents for revision_key."""
355
368
    def _load_indexes(self):
356
369
        """Load indexes for the reconciliation."""
357
370
        self.transaction = self.repo.get_transaction()
358
 
        self.pb.update('Reading indexes', 0, 2)
 
371
        self.pb.update(gettext('Reading indexes'), 0, 2)
359
372
        self.inventory = self.repo.inventories
360
 
        self.pb.update('Reading indexes', 1, 2)
 
373
        self.pb.update(gettext('Reading indexes'), 1, 2)
361
374
        self.repo._check_for_inconsistent_revision_parents()
362
375
        self.revisions = self.repo.revisions
363
 
        self.pb.update('Reading indexes', 2, 2)
 
376
        self.pb.update(gettext('Reading indexes'), 2, 2)
364
377
 
365
378
    def _gc_inventory(self):
366
379
        """Remove inventories that are not referenced from the revision store."""
367
 
        self.pb.update('Checking unused inventories', 0, 1)
 
380
        self.pb.update(gettext('Checking unused inventories'), 0, 1)
368
381
        self._check_garbage_inventories()
369
 
        self.pb.update('Checking unused inventories', 1, 3)
 
382
        self.pb.update(gettext('Checking unused inventories'), 1, 3)
370
383
        if not self.garbage_inventories:
371
 
            ui.ui_factory.note('Inventory ok.')
 
384
            ui.ui_factory.note(gettext('Inventory ok.'))
372
385
            return
373
 
        self.pb.update('Backing up inventory', 0, 0)
 
386
        self.pb.update(gettext('Backing up inventory'), 0, 0)
374
387
        self.repo._backup_inventory()
375
 
        ui.ui_factory.note('Backup Inventory created')
 
388
        ui.ui_factory.note(gettext('Backup Inventory created'))
376
389
        # asking for '' should never return a non-empty weave
377
390
        new_inventories = self.repo._temp_inventories()
378
391
        # we have topological order of revisions and non ghost parents ready.
389
402
        # the revisionds list
390
403
        if not(set(new_inventories.keys()) == set(revision_keys)):
391
404
            raise AssertionError()
392
 
        self.pb.update('Writing weave')
 
405
        self.pb.update(gettext('Writing weave'))
393
406
        self.repo._activate_new_inventory()
394
407
        self.inventory = None
395
 
        ui.ui_factory.note('Inventory regenerated.')
 
408
        ui.ui_factory.note(gettext('Inventory regenerated.'))
396
409
 
397
410
    def _fix_text_parents(self):
398
411
        """Fix bad versionedfile parent entries.
430
443
            versions_list.append(text_key[1])
431
444
        # Do the reconcile of individual weaves.
432
445
        for num, file_id in enumerate(per_id_bad_parents):
433
 
            self.pb.update('Fixing text parents', num,
 
446
            self.pb.update(gettext('Fixing text parents'), num,
434
447
                           len(per_id_bad_parents))
435
448
            versions_with_bad_parents = per_id_bad_parents[file_id]
436
449
            id_unused_versions = set(key[-1] for key in unused_versions
494
507
    #  - lock the names list
495
508
    #  - perform a customised pack() that regenerates data as needed
496
509
    #  - unlock the names list
497
 
    # https://bugs.edge.launchpad.net/bzr/+bug/154173
 
510
    # https://bugs.launchpad.net/bzr/+bug/154173
 
511
 
 
512
    def __init__(self, repo, other=None, thorough=False,
 
513
            canonicalize_chks=False):
 
514
        super(PackReconciler, self).__init__(repo, other=other,
 
515
            thorough=thorough)
 
516
        self.canonicalize_chks = canonicalize_chks
498
517
 
499
518
    def _reconcile_steps(self):
500
519
        """Perform the steps to reconcile this repository."""
509
528
        total_inventories = len(list(
510
529
            collection.inventory_index.combined_index.iter_all_entries()))
511
530
        if len(all_revisions):
512
 
            new_pack =  self.repo._reconcile_pack(collection, packs,
513
 
                ".reconcile", all_revisions, self.pb)
 
531
            if self.canonicalize_chks:
 
532
                reconcile_meth = self.repo._canonicalize_chks_pack
 
533
            else:
 
534
                reconcile_meth = self.repo._reconcile_pack
 
535
            new_pack = reconcile_meth(collection, packs, ".reconcile",
 
536
                all_revisions, self.pb)
514
537
            if new_pack is not None:
515
538
                self._discard_and_save(packs)
516
539
        else: