/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
1080 by Martin Pool
- test tool for converting history to weave files
1
#! /usr/bin/python
1267 by Martin Pool
- notes on conversion of existing history to weaves
2
#
1080 by Martin Pool
- test tool for converting history to weave files
3
# Copyright (C) 2005 Canonical Ltd
1267 by Martin Pool
- notes on conversion of existing history to weaves
4
#
1080 by Martin Pool
- test tool for converting history to weave files
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.
1267 by Martin Pool
- notes on conversion of existing history to weaves
9
#
1080 by Martin Pool
- test tool for converting history to weave files
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.
1267 by Martin Pool
- notes on conversion of existing history to weaves
14
#
1080 by Martin Pool
- test tool for converting history to weave files
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
1267 by Martin Pool
- notes on conversion of existing history to weaves
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
1315 by Martin Pool
- import file inventories in correct order
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
1318 by Martin Pool
- pull texts into weaves in a fairly lazy way
65
# This currently does a kind of lazy conversion of file texts, where a
66
# new text is written in every version.  That's unnecessary but for
67
# the moment saves us having to worry about when files need new
68
# versions.
69
70
1350 by Martin Pool
- set reasonable name_versions
71
if False:
1300 by Martin Pool
- refactor weave upgrade into a MethodObject
72
    try:
73
        import psyco
74
        psyco.full()
75
    except ImportError:
76
        pass
1083 by Martin Pool
- add space to store revision-id in weave files
77
1080 by Martin Pool
- test tool for converting history to weave files
78
1352 by Martin Pool
- store control weaves in .bzr/, not mixed in with file weaves
79
import os
1267 by Martin Pool
- notes on conversion of existing history to weaves
80
import tempfile
81
import hotshot, hotshot.stats
82
import sys
1132 by Martin Pool
- fix up logging for history2weaves tool
83
import logging
1352 by Martin Pool
- store control weaves in .bzr/, not mixed in with file weaves
84
import shutil
1132 by Martin Pool
- fix up logging for history2weaves tool
85
1355 by Martin Pool
- write working inventory into final location
86
from bzrlib.branch import Branch, find_branch, BZR_BRANCH_FORMAT_5
1080 by Martin Pool
- test tool for converting history to weave files
87
from bzrlib.revfile import Revfile
88
from bzrlib.weave import Weave
89
from bzrlib.weavefile import read_weave, write_weave
90
from bzrlib.progress import ProgressBar
91
from bzrlib.atomicfile import AtomicFile
1300 by Martin Pool
- refactor weave upgrade into a MethodObject
92
from bzrlib.xml4 import serializer_v4
93
from bzrlib.xml5 import serializer_v5
1307 by Martin Pool
- start walking through ancestors in conversion to weaves
94
from bzrlib.trace import mutter, note, warning, enable_default_logging
1326 by Martin Pool
- check inventory sha when loading old revisions
95
from bzrlib.osutils import sha_strings, sha_string
1337 by Martin Pool
- produce ancestry when converting versions
96
from bzrlib.commit import merge_ancestry_lines
1267 by Martin Pool
- notes on conversion of existing history to weaves
97
1080 by Martin Pool
- test tool for converting history to weave files
98
1300 by Martin Pool
- refactor weave upgrade into a MethodObject
99
class Convert(object):
1377 by Martin Pool
- run conversion to weaves from the 'bzr upgrade' command
100
    def __init__(self, base_dir):
101
        self.base = base_dir
1307 by Martin Pool
- start walking through ancestors in conversion to weaves
102
        self.converted_revs = set()
103
        self.absent_revisions = set()
1300 by Martin Pool
- refactor weave upgrade into a MethodObject
104
        self.text_count = 0
1309 by Martin Pool
- first cut at tsort to make order to bring in revisions
105
        self.revisions = {}
1318 by Martin Pool
- pull texts into weaves in a fairly lazy way
106
        self.inventories = {}
1300 by Martin Pool
- refactor weave upgrade into a MethodObject
107
        self.convert()
108
109
110
    def convert(self):
1377 by Martin Pool
- run conversion to weaves from the 'bzr upgrade' command
111
        note('starting upgrade of %s', self.base)
1352 by Martin Pool
- store control weaves in .bzr/, not mixed in with file weaves
112
	self._backup_control_dir()
1358 by Martin Pool
- actually upgrade all of history
113
	note('starting upgrade')
1379 by Martin Pool
- upgrade fixes regarding what files we expect to be present or not
114
	note('note: upgrade may be faster if all store files are ungzipped first')
1300 by Martin Pool
- refactor weave upgrade into a MethodObject
115
        self.pb = ProgressBar()
1377 by Martin Pool
- run conversion to weaves from the 'bzr upgrade' command
116
	if not os.path.isdir(self.base + '/.bzr/weaves'):
117
	    os.mkdir(self.base + '/.bzr/weaves')
1300 by Martin Pool
- refactor weave upgrade into a MethodObject
118
        self.inv_weave = Weave('__inventory')
119
        self.anc_weave = Weave('__ancestry')
1337 by Martin Pool
- produce ancestry when converting versions
120
        self.ancestries = {}
1300 by Martin Pool
- refactor weave upgrade into a MethodObject
121
        # holds in-memory weaves for all files
1318 by Martin Pool
- pull texts into weaves in a fairly lazy way
122
        self.text_weaves = {}
1377 by Martin Pool
- run conversion to weaves from the 'bzr upgrade' command
123
        self.branch = Branch(self.base, relax_version_check=True)
124
        if self.branch._branch_format == 5:
125
            note('this branch is already in the most current format')
126
            return
127
        if self.branch._branch_format != 4:
128
            raise BzrError("cannot upgrade from branch format %r" %
129
                           self.branch._branch_format)
1355 by Martin Pool
- write working inventory into final location
130
	os.remove(self.branch.controlfilename('branch-format'))
1352 by Martin Pool
- store control weaves in .bzr/, not mixed in with file weaves
131
	self._convert_working_inv()
1358 by Martin Pool
- actually upgrade all of history
132
        rev_history = self.branch.revision_history()
1309 by Martin Pool
- first cut at tsort to make order to bring in revisions
133
        # to_read is a stack holding the revisions we still need to process;
1300 by Martin Pool
- refactor weave upgrade into a MethodObject
134
        # appending to it adds new highest-priority revisions
1319 by Martin Pool
- calculate and use file parents for importing texts
135
        self.known_revisions = set(rev_history)
1309 by Martin Pool
- first cut at tsort to make order to bring in revisions
136
        self.to_read = [rev_history[-1]]
137
        while self.to_read:
138
            rev_id = self.to_read.pop()
139
            if (rev_id not in self.revisions
140
                and rev_id not in self.absent_revisions):
141
                self._load_one_rev(rev_id)
1300 by Martin Pool
- refactor weave upgrade into a MethodObject
142
        self.pb.clear()
1332 by Martin Pool
- clean up code that writes out weave results
143
        to_import = self._make_order()
1315 by Martin Pool
- import file inventories in correct order
144
        for i, rev_id in enumerate(to_import):
145
            self.pb.update('converting revision', i, len(to_import))
1318 by Martin Pool
- pull texts into weaves in a fairly lazy way
146
            self._convert_one_rev(rev_id)
1331 by Martin Pool
- write out new revisions after conversion
147
        self.pb.clear()
1352 by Martin Pool
- store control weaves in .bzr/, not mixed in with file weaves
148
        note('upgraded to weaves:')
149
        note('  %6d revisions and inventories' % len(self.revisions))
150
        note('  %6d absent revisions removed' % len(self.absent_revisions))
151
        note('  %6d texts' % self.text_count)
1300 by Martin Pool
- refactor weave upgrade into a MethodObject
152
        self._write_all_weaves()
1331 by Martin Pool
- write out new revisions after conversion
153
        self._write_all_revs()
1357 by Martin Pool
- require marker file to do upgrade so as not to clobber something important
154
	self._set_new_format()
155
	self._cleanup_spare_files()
156
157
158
    def _set_new_format(self):
1355 by Martin Pool
- write working inventory into final location
159
	f = self.branch.controlfile('branch-format', 'wb')
1377 by Martin Pool
- run conversion to weaves from the 'bzr upgrade' command
160
        try:
1355 by Martin Pool
- write working inventory into final location
161
	    f.write(BZR_BRANCH_FORMAT_5)
162
	finally:
163
	    f.close()
164
165
166
    def _cleanup_spare_files(self):
167
	for n in 'merged-patches', 'pending-merged-patches':
168
	    p = self.branch.controlfilename(n)
169
	    if not os.path.exists(p):
170
		continue
1379 by Martin Pool
- upgrade fixes regarding what files we expect to be present or not
171
	    ## assert os.path.getsize(p) == 0
1355 by Martin Pool
- write working inventory into final location
172
	    os.remove(p)
1377 by Martin Pool
- run conversion to weaves from the 'bzr upgrade' command
173
	shutil.rmtree(self.base + '/.bzr/inventory-store')
174
	shutil.rmtree(self.base + '/.bzr/text-store')
1300 by Martin Pool
- refactor weave upgrade into a MethodObject
175
176
1352 by Martin Pool
- store control weaves in .bzr/, not mixed in with file weaves
177
    def _backup_control_dir(self):
1377 by Martin Pool
- run conversion to weaves from the 'bzr upgrade' command
178
        orig = self.base + '/.bzr'
179
        backup = orig + '.backup'
180
	shutil.copytree(orig, backup)
181
	note('%s has been backed up to %s', orig, backup)
1352 by Martin Pool
- store control weaves in .bzr/, not mixed in with file weaves
182
	note('if conversion fails, you can move this directory back to .bzr')
183
	note('if it succeeds, you can remove this directory if you wish')
184
185
186
    def _convert_working_inv(self):
187
	branch = self.branch
188
	inv = serializer_v4.read_inventory(branch.controlfile('inventory', 'rb'))
1355 by Martin Pool
- write working inventory into final location
189
	serializer_v5.write_inventory(inv, branch.controlfile('inventory', 'wb'))
1352 by Martin Pool
- store control weaves in .bzr/, not mixed in with file weaves
190
191
192
1300 by Martin Pool
- refactor weave upgrade into a MethodObject
193
    def _write_all_weaves(self):
1377 by Martin Pool
- run conversion to weaves from the 'bzr upgrade' command
194
        write_a_weave(self.inv_weave, self.base + '/.bzr/inventory.weave')
195
        write_a_weave(self.anc_weave, self.base + '/.bzr/ancestry.weave')
1300 by Martin Pool
- refactor weave upgrade into a MethodObject
196
        i = 0
1318 by Martin Pool
- pull texts into weaves in a fairly lazy way
197
        try:
198
            for file_id, file_weave in self.text_weaves.items():
199
                self.pb.update('writing weave', i, len(self.text_weaves))
1377 by Martin Pool
- run conversion to weaves from the 'bzr upgrade' command
200
                write_a_weave(file_weave, self.base + '/.bzr/weaves/%s.weave' % file_id)
1318 by Martin Pool
- pull texts into weaves in a fairly lazy way
201
                i += 1
202
        finally:
203
            self.pb.clear()
1300 by Martin Pool
- refactor weave upgrade into a MethodObject
204
1331 by Martin Pool
- write out new revisions after conversion
205
206
    def _write_all_revs(self):
207
        """Write all revisions out in new form."""
1377 by Martin Pool
- run conversion to weaves from the 'bzr upgrade' command
208
	shutil.rmtree(self.base + '/.bzr/revision-store')
209
	os.mkdir(self.base + '/.bzr/revision-store')
1331 by Martin Pool
- write out new revisions after conversion
210
        try:
211
            for i, rev_id in enumerate(self.converted_revs):
212
                self.pb.update('write revision', i, len(self.converted_revs))
1377 by Martin Pool
- run conversion to weaves from the 'bzr upgrade' command
213
                f = file(self.base + '/.bzr/revision-store/%s' % rev_id, 'wb')
1331 by Martin Pool
- write out new revisions after conversion
214
                try:
215
                    serializer_v5.write_revision(self.revisions[rev_id], f)
216
                finally:
217
                    f.close()
218
        finally:
219
            self.pb.clear()
1332 by Martin Pool
- clean up code that writes out weave results
220
1331 by Martin Pool
- write out new revisions after conversion
221
            
1309 by Martin Pool
- first cut at tsort to make order to bring in revisions
222
    def _load_one_rev(self, rev_id):
223
        """Load a revision object into memory.
224
225
        Any parents not either loaded or abandoned get queued to be
226
        loaded."""
227
        self.pb.update('loading revision',
1315 by Martin Pool
- import file inventories in correct order
228
                       len(self.revisions),
1319 by Martin Pool
- calculate and use file parents for importing texts
229
                       len(self.known_revisions))
1309 by Martin Pool
- first cut at tsort to make order to bring in revisions
230
        if rev_id not in self.branch.revision_store:
231
            self.pb.clear()
232
            note('revision {%s} not present in branch; '
233
                 'will not be converted',
234
                 rev_id)
235
            self.absent_revisions.add(rev_id)
236
        else:
237
            rev_xml = self.branch.revision_store[rev_id].read()
238
            rev = serializer_v4.read_revision_from_string(rev_xml)
1313 by Martin Pool
- rename to Revision.parent_ids to avoid confusion with old usage
239
            for parent_id in rev.parent_ids:
1319 by Martin Pool
- calculate and use file parents for importing texts
240
                self.known_revisions.add(parent_id)
1309 by Martin Pool
- first cut at tsort to make order to bring in revisions
241
                self.to_read.append(parent_id)
242
            self.revisions[rev_id] = rev
1318 by Martin Pool
- pull texts into weaves in a fairly lazy way
243
            old_inv_xml = self.branch.inventory_store[rev_id].read()
244
            inv = serializer_v4.read_inventory_from_string(old_inv_xml)
1326 by Martin Pool
- check inventory sha when loading old revisions
245
            assert rev.inventory_sha1 == sha_string(old_inv_xml)
1318 by Martin Pool
- pull texts into weaves in a fairly lazy way
246
            self.inventories[rev_id] = inv
247
        
248
249
    def _convert_one_rev(self, rev_id):
250
        """Convert revision and all referenced objects to new format."""
251
        rev = self.revisions[rev_id]
252
        inv = self.inventories[rev_id]
1337 by Martin Pool
- produce ancestry when converting versions
253
        for parent_id in rev.parent_ids[:]:
254
            if parent_id in self.absent_revisions:
255
                rev.parent_ids.remove(parent_id)
256
                self.pb.clear()
257
                note('remove {%s} as parent of {%s}', parent_id, rev_id)
1320 by Martin Pool
- write updated inventory into weave
258
        self._convert_revision_contents(rev, inv)
259
        # the XML is now updated with text versions
1316 by Martin Pool
- upgrade format of inventories as they're converted
260
        new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1325 by Martin Pool
- conversion to weave tries to avoid repeated SHA calculation
261
        new_inv_sha1 = sha_string(new_inv_xml)
1337 by Martin Pool
- produce ancestry when converting versions
262
        self.inv_weave.add(rev_id, rev.parent_ids,
1325 by Martin Pool
- conversion to weave tries to avoid repeated SHA calculation
263
                           new_inv_xml.splitlines(True),
264
                           new_inv_sha1)
1318 by Martin Pool
- pull texts into weaves in a fairly lazy way
265
        # TODO: Upgrade revision XML and write that out
1325 by Martin Pool
- conversion to weave tries to avoid repeated SHA calculation
266
        rev.inventory_sha1 = new_inv_sha1
1337 by Martin Pool
- produce ancestry when converting versions
267
        self._make_rev_ancestry(rev)
1318 by Martin Pool
- pull texts into weaves in a fairly lazy way
268
        self.converted_revs.add(rev_id)
269
270
1337 by Martin Pool
- produce ancestry when converting versions
271
    def _make_rev_ancestry(self, rev):
272
        rev_id = rev.revision_id
273
        for parent_id in rev.parent_ids:
274
            assert parent_id in self.converted_revs
1340 by Martin Pool
- conversion to weave computes ancestries using weave mash
275
        if rev.parent_ids:
276
            lines = list(self.anc_weave.mash_iter(rev.parent_ids))
277
        else:
278
            lines = []
279
        lines.append(rev_id + '\n')
280
        if __debug__:
281
            parent_ancestries = [self.ancestries[p] for p in rev.parent_ids]
282
            new_lines = merge_ancestry_lines(rev_id, parent_ancestries)
283
            assert set(lines) == set(new_lines)
284
            self.ancestries[rev_id] = new_lines
285
        self.anc_weave.add(rev_id, rev.parent_ids, lines)
1337 by Martin Pool
- produce ancestry when converting versions
286
287
1318 by Martin Pool
- pull texts into weaves in a fairly lazy way
288
    def _convert_revision_contents(self, rev, inv):
289
        """Convert all the files within a revision.
290
291
        Also upgrade the inventory to refer to the text revision ids."""
292
        rev_id = rev.revision_id
1319 by Martin Pool
- calculate and use file parents for importing texts
293
        mutter('converting texts of revision {%s}',
294
               rev_id)
1332 by Martin Pool
- clean up code that writes out weave results
295
        for file_id in inv:
296
            ie = inv[file_id]
1350 by Martin Pool
- set reasonable name_versions
297
	    self._set_name_version(rev, ie)
1318 by Martin Pool
- pull texts into weaves in a fairly lazy way
298
            if ie.kind != 'file':
299
                continue
1319 by Martin Pool
- calculate and use file parents for importing texts
300
            self._convert_file_version(rev, ie)
1350 by Martin Pool
- set reasonable name_versions
301
302
303
    def _set_name_version(self, rev, ie):
304
	"""Set name version for a file.
305
306
	Done in a slightly lazy way: if the file is renamed or in a merge revision
307
	it gets a new version, otherwise the same as before.
308
	"""
309
	file_id = ie.file_id
310
	if len(rev.parent_ids) != 1:
311
	    ie.name_version = rev.revision_id
312
	else:
313
	    old_inv = self.inventories[rev.parent_ids[0]]
314
	    if not old_inv.has_id(file_id):
315
		ie.name_version = rev.revision_id
316
	    else:
317
		old_ie = old_inv[file_id]
318
		if (old_ie.parent_id != ie.parent_id
319
		    or old_ie.name != ie.name):
320
		    ie.name_version = rev.revision_id
321
		else:
322
		    ie.name_version = old_ie.name_version
323
1319 by Martin Pool
- calculate and use file parents for importing texts
324
325
326
    def _convert_file_version(self, rev, ie):
327
        """Convert one version of one file.
328
329
        The file needs to be added into the weave if it is a merge
330
        of >=2 parents or if it's changed from its parent.
331
        """
332
        file_id = ie.file_id
333
        rev_id = rev.revision_id
334
        w = self.text_weaves.get(file_id)
335
        if w is None:
336
            w = Weave(file_id)
337
            self.text_weaves[file_id] = w
338
        file_parents = []
339
        text_changed = False
340
        for parent_id in rev.parent_ids:
1337 by Martin Pool
- produce ancestry when converting versions
341
            ##if parent_id in self.absent_revisions:
342
            ##    continue
343
            assert parent_id in self.converted_revs, \
344
                   'parent {%s} not converted' % parent_id
1319 by Martin Pool
- calculate and use file parents for importing texts
345
            parent_inv = self.inventories[parent_id]
346
            if parent_inv.has_id(file_id):
347
                parent_ie = parent_inv[file_id]
348
                old_text_version = parent_ie.text_version
349
                assert old_text_version in self.converted_revs 
350
                if old_text_version not in file_parents:
351
                    file_parents.append(old_text_version)
352
                if parent_ie.text_sha1 != ie.text_sha1:
353
                    text_changed = True
354
        if len(file_parents) != 1 or text_changed:
1378 by Martin Pool
- in upgrade, avoiding loading file texts unless necessary
355
            file_lines = self.branch.text_store[ie.text_id].readlines()
356
            assert sha_strings(file_lines) == ie.text_sha1
357
            assert sum(map(len, file_lines)) == ie.text_size
1325 by Martin Pool
- conversion to weave tries to avoid repeated SHA calculation
358
            w.add(rev_id, file_parents, file_lines, ie.text_sha1)
1350 by Martin Pool
- set reasonable name_versions
359
            ie.text_version = rev_id
1332 by Martin Pool
- clean up code that writes out weave results
360
            self.text_count += 1
1330 by Martin Pool
- fiddle with trace
361
            ##mutter('import text {%s} of {%s}',
362
            ##       ie.text_id, file_id)
1319 by Martin Pool
- calculate and use file parents for importing texts
363
        else:
1377 by Martin Pool
- run conversion to weaves from the 'bzr upgrade' command
364
            ##mutter('text of {%s} unchanged from parent', file_id)
1319 by Martin Pool
- calculate and use file parents for importing texts
365
            ie.text_version = file_parents[0]
366
        del ie.text_id
1377 by Martin Pool
- run conversion to weaves from the 'bzr upgrade' command
367
1310 by Martin Pool
- compute order to import revisions
368
369
1309 by Martin Pool
- first cut at tsort to make order to bring in revisions
370
    def _make_order(self):
1310 by Martin Pool
- compute order to import revisions
371
        """Return a suitable order for importing revisions.
372
373
        The order must be such that an revision is imported after all
374
        its (present) parents.
375
        """
1309 by Martin Pool
- first cut at tsort to make order to bring in revisions
376
        todo = set(self.revisions.keys())
377
        done = self.absent_revisions.copy()
1310 by Martin Pool
- compute order to import revisions
378
        o = []
1309 by Martin Pool
- first cut at tsort to make order to bring in revisions
379
        while todo:
380
            # scan through looking for a revision whose parents
381
            # are all done
1310 by Martin Pool
- compute order to import revisions
382
            for rev_id in sorted(list(todo)):
1309 by Martin Pool
- first cut at tsort to make order to bring in revisions
383
                rev = self.revisions[rev_id]
1313 by Martin Pool
- rename to Revision.parent_ids to avoid confusion with old usage
384
                parent_ids = set(rev.parent_ids)
1309 by Martin Pool
- first cut at tsort to make order to bring in revisions
385
                if parent_ids.issubset(done):
386
                    # can take this one now
1310 by Martin Pool
- compute order to import revisions
387
                    o.append(rev_id)
1309 by Martin Pool
- first cut at tsort to make order to bring in revisions
388
                    todo.remove(rev_id)
389
                    done.add(rev_id)
1315 by Martin Pool
- import file inventories in correct order
390
        return o
1377 by Martin Pool
- run conversion to weaves from the 'bzr upgrade' command
391
1309 by Martin Pool
- first cut at tsort to make order to bring in revisions
392
1332 by Martin Pool
- clean up code that writes out weave results
393
def write_a_weave(weave, filename):
394
    inv_wf = file(filename, 'wb')
1080 by Martin Pool
- test tool for converting history to weave files
395
    try:
1087 by Martin Pool
- add a tool script to convert past history into weaves
396
        write_weave(weave, inv_wf)
1080 by Martin Pool
- test tool for converting history to weave files
397
    finally:
398
        inv_wf.close()
399
1377 by Martin Pool
- run conversion to weaves from the 'bzr upgrade' command
400
401
def upgrade(base_dir):
402
    Convert(base_dir)