~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/reconcile.py

  • Committer: Robert Collins
  • Date: 2006-02-23 04:08:56 UTC
  • mto: (1587.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 1588.
  • Revision ID: robertc@robertcollins.net-20060223040856-8b476741783b6244
Import bzrtools' 'fix' command as 'bzr reconcile.'

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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.')