/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
17
"""Plugin to fix some potential data errors in a branch.
18
19
This makes sure that the inventory weave's DAG of ancestry is correct so that
20
attempts to fetch the branch over http, or certain merge operations cope
21
correctly.
22
23
This is most likely needed if you have used fetch-ghosts from bzrlib to
24
resolve ghosts after a baz (or otherwise) import and you get bizarre behaviour
25
when either exporting over http or when merging from other translated branches.
26
"""
27
28
29
import os
30
31
32
import bzrlib.branch
33
import bzrlib.progress
34
from bzrlib.trace import mutter
35
import bzrlib.ui as ui
36
from bzrlib.weavefile import write_weave_v5 as w5
37
38
39
40
def reconcile(dir):
41
    """Reconcile the data in dir.
42
43
    Currently this is limited to a inventory 'reweave'.
44
45
    This is a convenience method, and the public api, for using a 
46
    Reconciler object.
47
    """
48
    reconciler = Reconciler(dir)
49
    reconciler.reconcile()
50
51
52
class Reconciler(object):
53
    """Reconcilers are used to reconcile existing data.
54
55
    Currently this is limited to a single repository, and consists
56
    of an inventory reweave with revision cross-checks.
57
    """
58
59
    def __init__(self, dir):
60
        self.bzrdir = dir
61
62
    def reconcile(self):
63
        """Actually perform the reconciliation."""
64
        self.pb = ui.ui_factory.progress_bar()
65
        self.repo = self.bzrdir.open_repository()
66
        self.repo.lock_write()
67
        try:
68
            self.pb.note('Reconciling repository %s',
69
                         self.repo.bzrdir.root_transport.base)
70
            self._reweave_inventory()
71
        finally:
72
            self.repo.unlock()
73
        self.pb.note('Reconciliation complete.')
74
75
    def _reweave_inventory(self):
76
        """Regenerate the inventory weave for the repository from scratch."""
77
        self.pb.update('Reading inventory data.')
78
        inventory = self.repo.get_inventory_weave()
79
        self.repo.control_weaves.put_weave('inventory.backup',
80
                                           inventory,
81
                                           self.repo.get_transaction())
82
        self.pb.note('Backup Inventory created.')
83
        # asking for '' should never return a non-empty weave
84
        new_inventory = self.repo.control_weaves.get_weave_or_empty('',
85
            self.repo.get_transaction())
86
87
        pending = [file_id for file_id in self.repo.revision_store]
88
        self.total = len(pending)
89
        self.count = 0
90
91
        # FIXME this loop is potentially N + N-1 + N-2 etc loops,
92
        # which is rather terrible. Better to make a stack of the
93
        # current desired-for-availability revisions and do those 
94
        # preferentially. RBC 20060223
95
        while pending:
96
            rev_id = pending.pop(0)
97
98
            self.pb.update('regenerating', self.count, self.total)
99
100
            rev = self.repo.get_revision(rev_id)
101
            parents = []
102
            for parent in rev.parent_ids:
103
                if parent in inventory:
104
                    parents.append(parent)
105
                else:
106
                    mutter('found ghost %s', parent)
107
            unavailable = [p for p in parents if p not in new_inventory]
108
            if len(unavailable) == 0:
109
                new_inventory.add(rev_id, parents, inventory.get(rev_id))
110
                self.count += 1
111
            else:
112
                pending.append(rev_id)
113
114
                
115
        self.pb.update('Writing weave')
116
        self.repo.control_weaves.put_weave('inventory',
117
                                           new_inventory,
118
                                           self.repo.get_transaction())
119
        self.pb.note('Inventory regenerated.')