/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to tools/history2weaves.py

  • Committer: Martin Pool
  • Date: 2005-09-19 09:24:40 UTC
  • Revision ID: mbp@sourcefrog.net-20050919092439-053a3afe592fe718
- BZR_DEBUG only turns on full exceptions and verbose logs into log file

- BZR_NOISY gives everything to stderr (very noisy)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#! /usr/bin/python
 
2
#
 
3
# Copyright (C) 2005 Canonical Ltd
 
4
#
 
5
# This program is free software; you can redistribute it and/or modify
 
6
# it under the terms of the GNU General Public License as published by
 
7
# the Free Software Foundation; either version 2 of the License, or
 
8
# (at your option) any later version.
 
9
#
 
10
# This program is distributed in the hope that it will be useful,
 
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
# GNU General Public License for more details.
 
14
#
 
15
# You should have received a copy of the GNU General Public License
 
16
# along with this program; if not, write to the Free Software
 
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
18
 
 
19
"""Experiment in converting existing bzr branches to weaves."""
 
20
 
 
21
# To make this properly useful
 
22
#
 
23
# 1. assign text version ids, and put those text versions into
 
24
#    the inventory as they're converted.
 
25
#
 
26
# 2. keep track of the previous version of each file, rather than
 
27
#    just using the last one imported
 
28
#
 
29
# 3. assign entry versions when files are added, renamed or moved.
 
30
#
 
31
# 4. when merged-in versions are observed, walk down through them
 
32
#    to discover everything, then commit bottom-up
 
33
#
 
34
# 5. track ancestry as things are merged in, and commit that in each
 
35
#    revision
 
36
#
 
37
# Perhaps it's best to first walk the whole graph and make a plan for
 
38
# what should be imported in what order?  Need a kind of topological
 
39
# sort of all revisions.  (Or do we, can we just before doing a revision
 
40
# see that all its parents have either been converted or abandoned?)
 
41
 
 
42
 
 
43
# Cannot import a revision until all its parents have been
 
44
# imported.  in other words, we can only import revisions whose
 
45
# parents have all been imported.  the first step must be to
 
46
# import a revision with no parents, of which there must be at
 
47
# least one.  (So perhaps it's useful to store forward pointers
 
48
# from a list of parents to their children?)
 
49
#
 
50
# Another (equivalent?) approach is to build up the ordered
 
51
# ancestry list for the last revision, and walk through that.  We
 
52
# are going to need that.
 
53
#
 
54
# We don't want to have to recurse all the way back down the list.
 
55
#
 
56
# Suppose we keep a queue of the revisions able to be processed at
 
57
# any point.  This starts out with all the revisions having no
 
58
# parents.
 
59
#
 
60
# This seems like a generally useful algorithm...
 
61
#
 
62
# The current algorithm is dumb (O(n**2)?) but will do the job, and
 
63
# takes less than a second on the bzr.dev branch.
 
64
 
 
65
if False:
 
66
    try:
 
67
        import psyco
 
68
        psyco.full()
 
69
    except ImportError:
 
70
        pass
 
71
 
 
72
 
 
73
import tempfile
 
74
import hotshot, hotshot.stats
 
75
import sys
 
76
import logging
 
77
import time
 
78
 
 
79
from bzrlib.branch import Branch, find_branch
 
80
from bzrlib.revfile import Revfile
 
81
from bzrlib.weave import Weave
 
82
from bzrlib.weavefile import read_weave, write_weave
 
83
from bzrlib.progress import ProgressBar
 
84
from bzrlib.atomicfile import AtomicFile
 
85
from bzrlib.xml4 import serializer_v4
 
86
from bzrlib.xml5 import serializer_v5
 
87
from bzrlib.trace import mutter, note, warning, enable_default_logging
 
88
 
 
89
 
 
90
 
 
91
class Convert(object):
 
92
    def __init__(self):
 
93
        self.total_revs = 0
 
94
        self.converted_revs = set()
 
95
        self.absent_revisions = set()
 
96
        self.text_count = 0
 
97
        self.revisions = {}
 
98
        self.convert()
 
99
        
 
100
 
 
101
 
 
102
 
 
103
    def convert(self):
 
104
        enable_default_logging()
 
105
        self.pb = ProgressBar()
 
106
        self.inv_weave = Weave('__inventory')
 
107
        self.anc_weave = Weave('__ancestry')
 
108
 
 
109
        last_text_sha = {}
 
110
 
 
111
        # holds in-memory weaves for all files
 
112
        text_weaves = {}
 
113
 
 
114
        b = self.branch = Branch('.', relax_version_check=True)
 
115
 
 
116
        revno = 1
 
117
        rev_history = b.revision_history()
 
118
        last_idx = None
 
119
        inv_parents = []
 
120
 
 
121
        # to_read is a stack holding the revisions we still need to process;
 
122
        # appending to it adds new highest-priority revisions
 
123
        importorder = []
 
124
        self.to_read = [rev_history[-1]]
 
125
        self.total_revs = len(rev_history)
 
126
        while self.to_read:
 
127
            rev_id = self.to_read.pop()
 
128
            if (rev_id not in self.revisions
 
129
                and rev_id not in self.absent_revisions):
 
130
                self._load_one_rev(rev_id)
 
131
        self.pb.clear()
 
132
        to_import = self._make_order()
 
133
        for i, rev_id in enumerate(to_import):
 
134
            self.pb.update('converting revision', i, len(to_import))
 
135
            self._import_one_rev(rev_id)
 
136
 
 
137
        print '(not really) upgraded to weaves:'
 
138
        print '  %6d revisions and inventories' % len(self.revisions)
 
139
        print '  %6d absent revisions removed' % len(self.absent_revisions)
 
140
        print '  %6d texts' % self.text_count
 
141
 
 
142
        self._write_all_weaves()
 
143
 
 
144
 
 
145
    def _write_all_weaves(self):
 
146
        i = 0
 
147
        write_atomic_weave(self.inv_weave, 'weaves/inventory.weave')
 
148
        return #######################
 
149
        write_atomic_weave(self.anc_weave, 'weaves/ancestry.weave')
 
150
        for file_id, file_weave in text_weaves.items():
 
151
            self.pb.update('writing weave', i, len(text_weaves))
 
152
            write_atomic_weave(file_weave, 'weaves/%s.weave' % file_id)
 
153
            i += 1
 
154
 
 
155
        self.pb.clear()
 
156
 
 
157
        
 
158
    def _load_one_rev(self, rev_id):
 
159
        """Load a revision object into memory.
 
160
 
 
161
        Any parents not either loaded or abandoned get queued to be
 
162
        loaded."""
 
163
        self.pb.update('loading revision',
 
164
                       len(self.revisions),
 
165
                       self.total_revs)
 
166
        if rev_id not in self.branch.revision_store:
 
167
            self.pb.clear()
 
168
            note('revision {%s} not present in branch; '
 
169
                 'will not be converted',
 
170
                 rev_id)
 
171
            self.absent_revisions.add(rev_id)
 
172
        else:
 
173
            rev_xml = self.branch.revision_store[rev_id].read()
 
174
            rev = serializer_v4.read_revision_from_string(rev_xml)
 
175
            for parent_id in rev.parent_ids:
 
176
                self.total_revs += 1
 
177
                self.to_read.append(parent_id)
 
178
            self.revisions[rev_id] = rev
 
179
 
 
180
 
 
181
    def _import_one_rev(self, rev_id):
 
182
        """Convert rev_id and all referenced file texts to new format."""
 
183
        old_inv_xml = self.branch.inventory_store[rev_id].read()
 
184
        inv = serializer_v4.read_inventory_from_string(old_inv_xml)
 
185
        new_inv_xml = serializer_v5.write_inventory_to_string(inv)
 
186
        inv_parents = [x for x in self.revisions[rev_id].parent_ids
 
187
                       if x not in self.absent_revisions]
 
188
        self.inv_weave.add(rev_id, inv_parents,
 
189
                           new_inv_xml.splitlines(True))
 
190
 
 
191
 
 
192
    def _make_order(self):
 
193
        """Return a suitable order for importing revisions.
 
194
 
 
195
        The order must be such that an revision is imported after all
 
196
        its (present) parents.
 
197
        """
 
198
        todo = set(self.revisions.keys())
 
199
        done = self.absent_revisions.copy()
 
200
        o = []
 
201
        while todo:
 
202
            # scan through looking for a revision whose parents
 
203
            # are all done
 
204
            for rev_id in sorted(list(todo)):
 
205
                rev = self.revisions[rev_id]
 
206
                parent_ids = set(rev.parent_ids)
 
207
                if parent_ids.issubset(done):
 
208
                    # can take this one now
 
209
                    o.append(rev_id)
 
210
                    todo.remove(rev_id)
 
211
                    done.add(rev_id)
 
212
        return o
 
213
                
 
214
 
 
215
def write_atomic_weave(weave, filename):
 
216
    inv_wf = AtomicFile(filename)
 
217
    try:
 
218
        write_weave(weave, inv_wf)
 
219
        inv_wf.commit()
 
220
    finally:
 
221
        inv_wf.close()
 
222
 
 
223
    
 
224
 
 
225
 
 
226
def profile_convert(): 
 
227
    prof_f = tempfile.NamedTemporaryFile()
 
228
 
 
229
    prof = hotshot.Profile(prof_f.name)
 
230
 
 
231
    prof.runcall(Convert) 
 
232
    prof.close()
 
233
 
 
234
    stats = hotshot.stats.load(prof_f.name)
 
235
    ##stats.strip_dirs()
 
236
    stats.sort_stats('time')
 
237
    # XXX: Might like to write to stderr or the trace file instead but
 
238
    # print_stats seems hardcoded to stdout
 
239
    stats.print_stats(20)
 
240
            
 
241
 
 
242
if '-p' in sys.argv[1:]:
 
243
    profile_convert()
 
244
else:
 
245
    Convert()
 
246