~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/reconcile.py

  • Committer: Danny van Heumen
  • Date: 2010-03-09 21:42:11 UTC
  • mto: (4634.139.5 2.0)
  • mto: This revision was merged to the branch mainline in revision 5160.
  • Revision ID: danny@dannyvanheumen.nl-20100309214211-iqh42x6qcikgd9p3
Reverted now-useless TODO list.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2005, 2006 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
27
27
 
28
28
 
29
29
from bzrlib import (
30
 
    cleanup,
31
30
    errors,
32
31
    ui,
 
32
    repository,
 
33
    repofmt,
33
34
    )
34
 
from bzrlib.trace import mutter
 
35
from bzrlib.trace import mutter, note
35
36
from bzrlib.tsort import topo_sort
36
37
from bzrlib.versionedfile import AdapterFactory, FulltextContentFactory
37
38
 
38
39
 
39
 
def reconcile(dir, canonicalize_chks=False):
 
40
def reconcile(dir, other=None):
40
41
    """Reconcile the data in dir.
41
42
 
42
43
    Currently this is limited to a inventory 'reweave'.
46
47
    Directly using Reconciler is recommended for library users that
47
48
    desire fine grained control or analysis of the found issues.
48
49
 
49
 
    :param canonicalize_chks: Make sure CHKs are in canonical form.
 
50
    :param other: another bzrdir to reconcile against.
50
51
    """
51
 
    reconciler = Reconciler(dir, canonicalize_chks=canonicalize_chks)
 
52
    reconciler = Reconciler(dir, other=other)
52
53
    reconciler.reconcile()
53
54
 
54
55
 
55
56
class Reconciler(object):
56
57
    """Reconcilers are used to reconcile existing data."""
57
58
 
58
 
    def __init__(self, dir, other=None, canonicalize_chks=False):
 
59
    def __init__(self, dir, other=None):
59
60
        """Create a Reconciler."""
60
61
        self.bzrdir = dir
61
 
        self.canonicalize_chks = canonicalize_chks
62
62
 
63
63
    def reconcile(self):
64
64
        """Perform reconciliation.
90
90
            # Nothing to check here
91
91
            self.fixed_branch_history = None
92
92
            return
93
 
        ui.ui_factory.note('Reconciling branch %s' % self.branch.base)
 
93
        self.pb.note('Reconciling branch %s',
 
94
                     self.branch.base)
94
95
        branch_reconciler = self.branch.reconcile(thorough=True)
95
96
        self.fixed_branch_history = branch_reconciler.fixed_history
96
97
 
97
98
    def _reconcile_repository(self):
98
99
        self.repo = self.bzrdir.find_repository()
99
 
        ui.ui_factory.note('Reconciling repository %s' %
100
 
            self.repo.user_url)
 
100
        self.pb.note('Reconciling repository %s',
 
101
                     self.repo.bzrdir.root_transport.base)
101
102
        self.pb.update("Reconciling repository", 0, 1)
102
 
        if self.canonicalize_chks:
103
 
            try:
104
 
                self.repo.reconcile_canonicalize_chks
105
 
            except AttributeError:
106
 
                raise errors.BzrError(
107
 
                    "%s cannot canonicalize CHKs." % (self.repo,))
108
 
            repo_reconciler = self.repo.reconcile_canonicalize_chks()
109
 
        else:
110
 
            repo_reconciler = self.repo.reconcile(thorough=True)
 
103
        repo_reconciler = self.repo.reconcile(thorough=True)
111
104
        self.inconsistent_parents = repo_reconciler.inconsistent_parents
112
105
        self.garbage_inventories = repo_reconciler.garbage_inventories
113
106
        if repo_reconciler.aborted:
114
 
            ui.ui_factory.note(
 
107
            self.pb.note(
115
108
                'Reconcile aborted: revision index has inconsistent parents.')
116
 
            ui.ui_factory.note(
 
109
            self.pb.note(
117
110
                'Run "bzr check" for more details.')
118
111
        else:
119
 
            ui.ui_factory.note('Reconciliation complete.')
 
112
            self.pb.note('Reconciliation complete.')
120
113
 
121
114
 
122
115
class BranchReconciler(object):
128
121
        self.branch = a_branch
129
122
 
130
123
    def reconcile(self):
131
 
        operation = cleanup.OperationWithCleanups(self._reconcile)
132
 
        self.add_cleanup = operation.add_cleanup
133
 
        operation.run_simple()
134
 
 
135
 
    def _reconcile(self):
136
124
        self.branch.lock_write()
137
 
        self.add_cleanup(self.branch.unlock)
138
 
        self.pb = ui.ui_factory.nested_progress_bar()
139
 
        self.add_cleanup(self.pb.finished)
140
 
        self._reconcile_steps()
 
125
        try:
 
126
            self.pb = ui.ui_factory.nested_progress_bar()
 
127
            try:
 
128
                self._reconcile_steps()
 
129
            finally:
 
130
                self.pb.finished()
 
131
        finally:
 
132
            self.branch.unlock()
141
133
 
142
134
    def _reconcile_steps(self):
143
135
        self._reconcile_revision_history()
159
151
            # set_revision_history, as this will regenerate it again.
160
152
            # Not really worth a whole BranchReconciler class just for this,
161
153
            # though.
162
 
            ui.ui_factory.note('Fixing last revision info %s => %s' % (
163
 
                 last_revno, len(real_history)))
 
154
            self.pb.note('Fixing last revision info %s => %s',
 
155
                         last_revno, len(real_history))
164
156
            self.branch.set_last_revision_info(len(real_history),
165
157
                                               last_revision_id)
166
158
        else:
167
159
            self.fixed_history = False
168
 
            ui.ui_factory.note('revision_history ok.')
 
160
            self.pb.note('revision_history ok.')
169
161
 
170
162
 
171
163
class RepoReconciler(object):
201
193
        garbage_inventories: The number of inventory objects without revisions
202
194
                             that were garbage collected.
203
195
        """
204
 
        operation = cleanup.OperationWithCleanups(self._reconcile)
205
 
        self.add_cleanup = operation.add_cleanup
206
 
        operation.run_simple()
207
 
 
208
 
    def _reconcile(self):
209
196
        self.repo.lock_write()
210
 
        self.add_cleanup(self.repo.unlock)
211
 
        self.pb = ui.ui_factory.nested_progress_bar()
212
 
        self.add_cleanup(self.pb.finished)
213
 
        self._reconcile_steps()
 
197
        try:
 
198
            self.pb = ui.ui_factory.nested_progress_bar()
 
199
            try:
 
200
                self._reconcile_steps()
 
201
            finally:
 
202
                self.pb.finished()
 
203
        finally:
 
204
            self.repo.unlock()
214
205
 
215
206
    def _reconcile_steps(self):
216
207
        """Perform the steps to reconcile this repository."""
247
238
        # (no garbage inventories or we are not doing a thorough check)
248
239
        if (not self.inconsistent_parents and
249
240
            (not self.garbage_inventories or not self.thorough)):
250
 
            ui.ui_factory.note('Inventory ok.')
 
241
            self.pb.note('Inventory ok.')
251
242
            return
252
243
        self.pb.update('Backing up inventory', 0, 0)
253
244
        self.repo._backup_inventory()
254
 
        ui.ui_factory.note('Backup inventory created.')
 
245
        self.pb.note('Backup inventory created.')
255
246
        new_inventories = self.repo._temp_inventories()
256
247
 
257
248
        # we have topological order of revisions and non ghost parents ready.
270
261
        self.pb.update('Writing weave')
271
262
        self.repo._activate_new_inventory()
272
263
        self.inventory = None
273
 
        ui.ui_factory.note('Inventory regenerated.')
 
264
        self.pb.note('Inventory regenerated.')
274
265
 
275
266
    def _new_inv_parents(self, revision_key):
276
267
        """Lookup ghost-filtered parents for revision_key."""
377
368
        self._check_garbage_inventories()
378
369
        self.pb.update('Checking unused inventories', 1, 3)
379
370
        if not self.garbage_inventories:
380
 
            ui.ui_factory.note('Inventory ok.')
 
371
            self.pb.note('Inventory ok.')
381
372
            return
382
373
        self.pb.update('Backing up inventory', 0, 0)
383
374
        self.repo._backup_inventory()
384
 
        ui.ui_factory.note('Backup Inventory created')
 
375
        self.pb.note('Backup Inventory created')
385
376
        # asking for '' should never return a non-empty weave
386
377
        new_inventories = self.repo._temp_inventories()
387
378
        # we have topological order of revisions and non ghost parents ready.
401
392
        self.pb.update('Writing weave')
402
393
        self.repo._activate_new_inventory()
403
394
        self.inventory = None
404
 
        ui.ui_factory.note('Inventory regenerated.')
 
395
        self.pb.note('Inventory regenerated.')
405
396
 
406
397
    def _fix_text_parents(self):
407
398
        """Fix bad versionedfile parent entries.
503
494
    #  - lock the names list
504
495
    #  - perform a customised pack() that regenerates data as needed
505
496
    #  - unlock the names list
506
 
    # https://bugs.launchpad.net/bzr/+bug/154173
507
 
 
508
 
    def __init__(self, repo, other=None, thorough=False,
509
 
            canonicalize_chks=False):
510
 
        super(PackReconciler, self).__init__(repo, other=other,
511
 
            thorough=thorough)
512
 
        self.canonicalize_chks = canonicalize_chks
 
497
    # https://bugs.edge.launchpad.net/bzr/+bug/154173
513
498
 
514
499
    def _reconcile_steps(self):
515
500
        """Perform the steps to reconcile this repository."""
518
503
        collection = self.repo._pack_collection
519
504
        collection.ensure_loaded()
520
505
        collection.lock_names()
521
 
        self.add_cleanup(collection._unlock_names)
522
 
        packs = collection.all_packs()
523
 
        all_revisions = self.repo.all_revision_ids()
524
 
        total_inventories = len(list(
525
 
            collection.inventory_index.combined_index.iter_all_entries()))
526
 
        if len(all_revisions):
527
 
            if self.canonicalize_chks:
528
 
                reconcile_meth = self.repo._canonicalize_chks_pack
 
506
        try:
 
507
            packs = collection.all_packs()
 
508
            all_revisions = self.repo.all_revision_ids()
 
509
            total_inventories = len(list(
 
510
                collection.inventory_index.combined_index.iter_all_entries()))
 
511
            if len(all_revisions):
 
512
                new_pack =  self.repo._reconcile_pack(collection, packs,
 
513
                    ".reconcile", all_revisions, self.pb)
 
514
                if new_pack is not None:
 
515
                    self._discard_and_save(packs)
529
516
            else:
530
 
                reconcile_meth = self.repo._reconcile_pack
531
 
            new_pack = reconcile_meth(collection, packs, ".reconcile",
532
 
                all_revisions, self.pb)
533
 
            if new_pack is not None:
 
517
                # only make a new pack when there is data to copy.
534
518
                self._discard_and_save(packs)
535
 
        else:
536
 
            # only make a new pack when there is data to copy.
537
 
            self._discard_and_save(packs)
538
 
        self.garbage_inventories = total_inventories - len(list(
539
 
            collection.inventory_index.combined_index.iter_all_entries()))
 
519
            self.garbage_inventories = total_inventories - len(list(
 
520
                collection.inventory_index.combined_index.iter_all_entries()))
 
521
        finally:
 
522
            collection._unlock_names()
540
523
 
541
524
    def _discard_and_save(self, packs):
542
525
        """Discard some packs from the repository.