1
# (C) 2005, 2006 Canonical Limited.
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.
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.
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
17
"""Plugin to fix some potential data errors in a branch.
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
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.
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
41
"""Reconcile the data in dir.
43
Currently this is limited to a inventory 'reweave'.
45
This is a convenience method, and the public api, for using a
48
reconciler = Reconciler(dir)
49
reconciler.reconcile()
52
class Reconciler(object):
53
"""Reconcilers are used to reconcile existing data.
55
Currently this is limited to a single repository, and consists
56
of an inventory reweave with revision cross-checks.
59
def __init__(self, dir):
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()
68
self.pb.note('Reconciling repository %s',
69
self.repo.bzrdir.root_transport.base)
70
self._reweave_inventory()
73
self.pb.note('Reconciliation complete.')
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',
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())
87
pending = [file_id for file_id in self.repo.revision_store]
88
self.total = len(pending)
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
96
rev_id = pending.pop(0)
98
self.pb.update('regenerating', self.count, self.total)
100
rev = self.repo.get_revision(rev_id)
102
for parent in rev.parent_ids:
103
if parent in inventory:
104
parents.append(parent)
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))
112
pending.append(rev_id)
115
self.pb.update('Writing weave')
116
self.repo.control_weaves.put_weave('inventory',
118
self.repo.get_transaction())
119
self.pb.note('Inventory regenerated.')