/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
1
# (C) 2005, 2006 Canonical Limited.
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
1570.1.7 by Robert Collins
Replace the slow topo_sort routine with a much faster one for non trivial datasets.
17
"""Reconcilers are able to fix some potential data errors in a branch."""
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
18
19
20
import bzrlib.branch
1570.1.6 by Robert Collins
Update fast topological_sort to be a function and to have the topo_sort tests run against it.
21
import bzrlib.errors as errors
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
22
import bzrlib.progress
23
from bzrlib.trace import mutter
1570.1.7 by Robert Collins
Replace the slow topo_sort routine with a much faster one for non trivial datasets.
24
from bzrlib.tsort import TopoSorter
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
25
import bzrlib.ui as ui
26
27
28
def reconcile(dir):
29
    """Reconcile the data in dir.
30
31
    Currently this is limited to a inventory 'reweave'.
32
1570.1.8 by Robert Collins
Only reconcile if doing so will perform gc or correct ancestry.
33
    This is a convenience method, for using a Reconciler object.
34
35
    Directly using Reconciler is recommended for library users that
36
    desire fine grained control or analysis of the found issues.
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
37
    """
38
    reconciler = Reconciler(dir)
39
    reconciler.reconcile()
40
41
1570.1.6 by Robert Collins
Update fast topological_sort to be a function and to have the topo_sort tests run against it.
42
class Reconciler(object):
43
    """Reconcilers are used to reconcile existing data.
44
45
    Currently this is limited to a single repository, and consists
46
    of an inventory reweave with revision cross-checks.
47
    """
48
49
    def __init__(self, dir):
50
        self.bzrdir = dir
51
52
    def reconcile(self):
1570.1.8 by Robert Collins
Only reconcile if doing so will perform gc or correct ancestry.
53
        """Perform reconciliation.
54
        
55
        After reconciliation the following attributes document found issues:
56
        inconsistent_parents: The number of revisions in the repository whose
57
                              ancestry was being reported incorrectly.
58
        garbage_inventories: The number of inventory objects without revisions
59
                             that were garbage collected.
60
        """
1570.1.6 by Robert Collins
Update fast topological_sort to be a function and to have the topo_sort tests run against it.
61
        self.pb = ui.ui_factory.progress_bar()
1570.1.11 by Robert Collins
Make reconcile work with shared repositories.
62
        self.repo = self.bzrdir.find_repository()
1570.1.6 by Robert Collins
Update fast topological_sort to be a function and to have the topo_sort tests run against it.
63
        self.repo.lock_write()
64
        try:
65
            self.pb.note('Reconciling repository %s',
66
                         self.repo.bzrdir.root_transport.base)
67
            self._reweave_inventory()
68
        finally:
69
            self.repo.unlock()
70
        self.pb.note('Reconciliation complete.')
71
72
    def _reweave_inventory(self):
73
        """Regenerate the inventory weave for the repository from scratch."""
74
        self.pb.update('Reading inventory data.')
75
        self.inventory = self.repo.get_inventory_weave()
76
        # the total set of revisions to process
77
        self.pending = set([file_id for file_id in self.repo.revision_store])
78
79
        # mapping from revision_id to parents
80
        self._rev_graph = {}
1570.1.8 by Robert Collins
Only reconcile if doing so will perform gc or correct ancestry.
81
        # errors that we detect
82
        self.inconsistent_parents = 0
1570.1.6 by Robert Collins
Update fast topological_sort to be a function and to have the topo_sort tests run against it.
83
        # we need the revision id of each revision and its available parents list
1570.1.10 by Robert Collins
UI tweaks to reconcile - show progress for inventory backup.
84
        self._setup_steps(len(self.pending))
1570.1.6 by Robert Collins
Update fast topological_sort to be a function and to have the topo_sort tests run against it.
85
        for rev_id in self.pending:
86
            # put a revision into the graph.
87
            self._graph_revision(rev_id)
1570.1.8 by Robert Collins
Only reconcile if doing so will perform gc or correct ancestry.
88
        # we gc unreferenced inventories too
89
        self.garbage_inventories = len(self.inventory.names()) \
90
                                   - len(self._rev_graph)
91
92
        if not self.inconsistent_parents and not self.garbage_inventories:
93
            self.pb.note('Inventory ok.')
94
            return
1570.1.10 by Robert Collins
UI tweaks to reconcile - show progress for inventory backup.
95
        self.pb.update('Backing up inventory...', 0, 0)
1570.1.8 by Robert Collins
Only reconcile if doing so will perform gc or correct ancestry.
96
        self.repo.control_weaves.put_weave('inventory.backup',
97
                                           self.inventory,
98
                                           self.repo.get_transaction())
99
        self.pb.note('Backup Inventory created.')
100
        # asking for '' should never return a non-empty weave
101
        new_inventory = self.repo.control_weaves.get_weave_or_empty('',
102
            self.repo.get_transaction())
1570.1.6 by Robert Collins
Update fast topological_sort to be a function and to have the topo_sort tests run against it.
103
1570.1.4 by Robert Collins
Somewhat optimised version of reconciler.
104
        # we have topological order of revisions and non ghost parents ready.
1570.1.10 by Robert Collins
UI tweaks to reconcile - show progress for inventory backup.
105
        self._setup_steps(len(self._rev_graph))
1570.1.7 by Robert Collins
Replace the slow topo_sort routine with a much faster one for non trivial datasets.
106
        for rev_id in TopoSorter(self._rev_graph.items()).iter_topo_order():
107
            parents = self._rev_graph[rev_id]
1570.1.4 by Robert Collins
Somewhat optimised version of reconciler.
108
            # double check this really is in topological order.
109
            unavailable = [p for p in parents if p not in new_inventory]
110
            assert len(unavailable) == 0
111
            # this entry has all the non ghost parents in the inventory
112
            # file already.
113
            self._reweave_step('adding inventories')
114
            new_inventory.add(rev_id, parents, self.inventory.get(rev_id))
115
116
        # if this worked, the set of new_inventory.names should equal
117
        # self.pending
118
        assert set(new_inventory.names()) == self.pending
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
119
        self.pb.update('Writing weave')
120
        self.repo.control_weaves.put_weave('inventory',
121
                                           new_inventory,
122
                                           self.repo.get_transaction())
1570.1.3 by Robert Collins
Optimise reconcilation to only hit each revision once.
123
        self.inventory = None
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
124
        self.pb.note('Inventory regenerated.')
1570.1.3 by Robert Collins
Optimise reconcilation to only hit each revision once.
125
1570.1.10 by Robert Collins
UI tweaks to reconcile - show progress for inventory backup.
126
    def _setup_steps(self, new_total):
127
        """Setup the markers we need to control the progress bar."""
128
        self.total = new_total
129
        self.count = 0
130
1570.1.4 by Robert Collins
Somewhat optimised version of reconciler.
131
    def _graph_revision(self, rev_id):
132
        """Load a revision into the revision graph."""
133
        # pick a random revision
134
        # analyse revision id rev_id and put it in the stack.
135
        self._reweave_step('loading revisions')
1570.1.3 by Robert Collins
Optimise reconcilation to only hit each revision once.
136
        rev = self.repo.get_revision(rev_id)
137
        assert rev.revision_id == rev_id
138
        parents = []
139
        for parent in rev.parent_ids:
140
            if parent in self.inventory:
141
                parents.append(parent)
142
            else:
143
                mutter('found ghost %s', parent)
1570.1.4 by Robert Collins
Somewhat optimised version of reconciler.
144
        self._rev_graph[rev_id] = parents   
1570.1.8 by Robert Collins
Only reconcile if doing so will perform gc or correct ancestry.
145
        if set(self.inventory.parent_names(rev_id)) != set(parents):
146
            self.inconsistent_parents += 1
1570.1.4 by Robert Collins
Somewhat optimised version of reconciler.
147
148
    def _reweave_step(self, message):
149
        """Mark a single step of regeneration complete."""
150
        self.pb.update(message, self.count, self.total)
151
        self.count += 1