~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/plugins/weave_fmt/bzrdir.py

  • Committer: Vincent Ladeuil
  • Date: 2012-01-18 14:09:19 UTC
  • mto: This revision was merged to the branch mainline in revision 6468.
  • Revision ID: v.ladeuil+lp@free.fr-20120118140919-rlvdrhpc0nq1lbwi
Change set/remove to require a lock for the branch config files.

This means that tests (or any plugin for that matter) do not requires an
explicit lock on the branch anymore to change a single option. This also
means the optimisation becomes "opt-in" and as such won't be as
spectacular as it may be and/or harder to get right (nothing fails
anymore).

This reduces the diff by ~300 lines.

Code/tests that were updating more than one config option is still taking
a lock to at least avoid some IOs and demonstrate the benefits through
the decreased number of hpss calls.

The duplication between BranchStack and BranchOnlyStack will be removed
once the same sharing is in place for local config files, at which point
the Stack class itself may be able to host the changes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2006-2010 Canonical Ltd
 
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
"""Weave-era BzrDir formats."""
 
18
 
 
19
from __future__ import absolute_import
 
20
 
 
21
from bzrlib.bzrdir import (
 
22
    BzrDir,
 
23
    BzrDirFormat,
 
24
    BzrDirMetaFormat1,
 
25
    )
 
26
from bzrlib.controldir import (
 
27
    Converter,
 
28
    format_registry,
 
29
    )
 
30
from bzrlib.lazy_import import lazy_import
 
31
lazy_import(globals(), """
 
32
import os
 
33
import warnings
 
34
 
 
35
from bzrlib import (
 
36
    errors,
 
37
    graph,
 
38
    lockable_files,
 
39
    lockdir,
 
40
    osutils,
 
41
    revision as _mod_revision,
 
42
    trace,
 
43
    ui,
 
44
    urlutils,
 
45
    versionedfile,
 
46
    weave,
 
47
    xml5,
 
48
    )
 
49
from bzrlib.i18n import gettext
 
50
from bzrlib.store.versioned import VersionedFileStore
 
51
from bzrlib.transactions import WriteTransaction
 
52
from bzrlib.transport import (
 
53
    get_transport,
 
54
    local,
 
55
    )
 
56
from bzrlib.plugins.weave_fmt import xml4
 
57
""")
 
58
 
 
59
 
 
60
class BzrDirFormatAllInOne(BzrDirFormat):
 
61
    """Common class for formats before meta-dirs."""
 
62
 
 
63
    fixed_components = True
 
64
 
 
65
    def initialize_on_transport_ex(self, transport, use_existing_dir=False,
 
66
        create_prefix=False, force_new_repo=False, stacked_on=None,
 
67
        stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
 
68
        shared_repo=False):
 
69
        """See BzrDirFormat.initialize_on_transport_ex."""
 
70
        require_stacking = (stacked_on is not None)
 
71
        # Format 5 cannot stack, but we've been asked to - actually init
 
72
        # a Meta1Dir
 
73
        if require_stacking:
 
74
            format = BzrDirMetaFormat1()
 
75
            return format.initialize_on_transport_ex(transport,
 
76
                use_existing_dir=use_existing_dir, create_prefix=create_prefix,
 
77
                force_new_repo=force_new_repo, stacked_on=stacked_on,
 
78
                stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
 
79
                make_working_trees=make_working_trees, shared_repo=shared_repo)
 
80
        return BzrDirFormat.initialize_on_transport_ex(self, transport,
 
81
            use_existing_dir=use_existing_dir, create_prefix=create_prefix,
 
82
            force_new_repo=force_new_repo, stacked_on=stacked_on,
 
83
            stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
 
84
            make_working_trees=make_working_trees, shared_repo=shared_repo)
 
85
 
 
86
    @classmethod
 
87
    def from_string(cls, format_string):
 
88
        if format_string != cls.get_format_string():
 
89
            raise AssertionError("unexpected format string %r" % format_string)
 
90
        return cls()
 
91
 
 
92
 
 
93
class BzrDirFormat5(BzrDirFormatAllInOne):
 
94
    """Bzr control format 5.
 
95
 
 
96
    This format is a combined format for working tree, branch and repository.
 
97
    It has:
 
98
     - Format 2 working trees [always]
 
99
     - Format 4 branches [always]
 
100
     - Format 5 repositories [always]
 
101
       Unhashed stores in the repository.
 
102
    """
 
103
 
 
104
    _lock_class = lockable_files.TransportLock
 
105
 
 
106
    def __eq__(self, other):
 
107
        return type(self) == type(other)
 
108
 
 
109
    @classmethod
 
110
    def get_format_string(cls):
 
111
        """See BzrDirFormat.get_format_string()."""
 
112
        return "Bazaar-NG branch, format 5\n"
 
113
 
 
114
    def get_branch_format(self):
 
115
        from bzrlib.plugins.weave_fmt.branch import BzrBranchFormat4
 
116
        return BzrBranchFormat4()
 
117
 
 
118
    def get_format_description(self):
 
119
        """See BzrDirFormat.get_format_description()."""
 
120
        return "All-in-one format 5"
 
121
 
 
122
    def get_converter(self, format=None):
 
123
        """See BzrDirFormat.get_converter()."""
 
124
        # there is one and only one upgrade path here.
 
125
        return ConvertBzrDir5To6()
 
126
 
 
127
    def _initialize_for_clone(self, url):
 
128
        return self.initialize_on_transport(get_transport(url), _cloning=True)
 
129
 
 
130
    def initialize_on_transport(self, transport, _cloning=False):
 
131
        """Format 5 dirs always have working tree, branch and repository.
 
132
 
 
133
        Except when they are being cloned.
 
134
        """
 
135
        from bzrlib.plugins.weave_fmt.branch import BzrBranchFormat4
 
136
        from bzrlib.plugins.weave_fmt.repository import RepositoryFormat5
 
137
        result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
 
138
        RepositoryFormat5().initialize(result, _internal=True)
 
139
        if not _cloning:
 
140
            branch = BzrBranchFormat4().initialize(result)
 
141
            result._init_workingtree()
 
142
        return result
 
143
 
 
144
    def network_name(self):
 
145
        return self.get_format_string()
 
146
 
 
147
    def _open(self, transport):
 
148
        """See BzrDirFormat._open."""
 
149
        return BzrDir5(transport, self)
 
150
 
 
151
    def __return_repository_format(self):
 
152
        """Circular import protection."""
 
153
        from bzrlib.plugins.weave_fmt.repository import RepositoryFormat5
 
154
        return RepositoryFormat5()
 
155
    repository_format = property(__return_repository_format)
 
156
 
 
157
 
 
158
class BzrDirFormat6(BzrDirFormatAllInOne):
 
159
    """Bzr control format 6.
 
160
 
 
161
    This format is a combined format for working tree, branch and repository.
 
162
    It has:
 
163
     - Format 2 working trees [always]
 
164
     - Format 4 branches [always]
 
165
     - Format 6 repositories [always]
 
166
    """
 
167
 
 
168
    _lock_class = lockable_files.TransportLock
 
169
 
 
170
    def __eq__(self, other):
 
171
        return type(self) == type(other)
 
172
 
 
173
    @classmethod
 
174
    def get_format_string(cls):
 
175
        """See BzrDirFormat.get_format_string()."""
 
176
        return "Bazaar-NG branch, format 6\n"
 
177
 
 
178
    def get_format_description(self):
 
179
        """See BzrDirFormat.get_format_description()."""
 
180
        return "All-in-one format 6"
 
181
 
 
182
    def get_branch_format(self):
 
183
        from bzrlib.plugins.weave_fmt.branch import BzrBranchFormat4
 
184
        return BzrBranchFormat4()
 
185
 
 
186
    def get_converter(self, format=None):
 
187
        """See BzrDirFormat.get_converter()."""
 
188
        # there is one and only one upgrade path here.
 
189
        return ConvertBzrDir6ToMeta()
 
190
 
 
191
    def _initialize_for_clone(self, url):
 
192
        return self.initialize_on_transport(get_transport(url), _cloning=True)
 
193
 
 
194
    def initialize_on_transport(self, transport, _cloning=False):
 
195
        """Format 6 dirs always have working tree, branch and repository.
 
196
 
 
197
        Except when they are being cloned.
 
198
        """
 
199
        from bzrlib.plugins.weave_fmt.branch import BzrBranchFormat4
 
200
        from bzrlib.plugins.weave_fmt.repository import RepositoryFormat6
 
201
        result = super(BzrDirFormat6, self).initialize_on_transport(transport)
 
202
        RepositoryFormat6().initialize(result, _internal=True)
 
203
        if not _cloning:
 
204
            branch = BzrBranchFormat4().initialize(result)
 
205
            result._init_workingtree()
 
206
        return result
 
207
 
 
208
    def network_name(self):
 
209
        return self.get_format_string()
 
210
 
 
211
    def _open(self, transport):
 
212
        """See BzrDirFormat._open."""
 
213
        return BzrDir6(transport, self)
 
214
 
 
215
    def __return_repository_format(self):
 
216
        """Circular import protection."""
 
217
        from bzrlib.plugins.weave_fmt.repository import RepositoryFormat6
 
218
        return RepositoryFormat6()
 
219
    repository_format = property(__return_repository_format)
 
220
 
 
221
 
 
222
class ConvertBzrDir4To5(Converter):
 
223
    """Converts format 4 bzr dirs to format 5."""
 
224
 
 
225
    def __init__(self):
 
226
        super(ConvertBzrDir4To5, self).__init__()
 
227
        self.converted_revs = set()
 
228
        self.absent_revisions = set()
 
229
        self.text_count = 0
 
230
        self.revisions = {}
 
231
 
 
232
    def convert(self, to_convert, pb):
 
233
        """See Converter.convert()."""
 
234
        self.bzrdir = to_convert
 
235
        if pb is not None:
 
236
            warnings.warn(gettext("pb parameter to convert() is deprecated"))
 
237
        self.pb = ui.ui_factory.nested_progress_bar()
 
238
        try:
 
239
            ui.ui_factory.note(gettext('starting upgrade from format 4 to 5'))
 
240
            if isinstance(self.bzrdir.transport, local.LocalTransport):
 
241
                self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
 
242
            self._convert_to_weaves()
 
243
            return BzrDir.open(self.bzrdir.user_url)
 
244
        finally:
 
245
            self.pb.finished()
 
246
 
 
247
    def _convert_to_weaves(self):
 
248
        ui.ui_factory.note(gettext(
 
249
          'note: upgrade may be faster if all store files are ungzipped first'))
 
250
        try:
 
251
            # TODO permissions
 
252
            stat = self.bzrdir.transport.stat('weaves')
 
253
            if not S_ISDIR(stat.st_mode):
 
254
                self.bzrdir.transport.delete('weaves')
 
255
                self.bzrdir.transport.mkdir('weaves')
 
256
        except errors.NoSuchFile:
 
257
            self.bzrdir.transport.mkdir('weaves')
 
258
        # deliberately not a WeaveFile as we want to build it up slowly.
 
259
        self.inv_weave = weave.Weave('inventory')
 
260
        # holds in-memory weaves for all files
 
261
        self.text_weaves = {}
 
262
        self.bzrdir.transport.delete('branch-format')
 
263
        self.branch = self.bzrdir.open_branch()
 
264
        self._convert_working_inv()
 
265
        rev_history = self.branch._revision_history()
 
266
        # to_read is a stack holding the revisions we still need to process;
 
267
        # appending to it adds new highest-priority revisions
 
268
        self.known_revisions = set(rev_history)
 
269
        self.to_read = rev_history[-1:]
 
270
        while self.to_read:
 
271
            rev_id = self.to_read.pop()
 
272
            if (rev_id not in self.revisions
 
273
                and rev_id not in self.absent_revisions):
 
274
                self._load_one_rev(rev_id)
 
275
        self.pb.clear()
 
276
        to_import = self._make_order()
 
277
        for i, rev_id in enumerate(to_import):
 
278
            self.pb.update(gettext('converting revision'), i, len(to_import))
 
279
            self._convert_one_rev(rev_id)
 
280
        self.pb.clear()
 
281
        self._write_all_weaves()
 
282
        self._write_all_revs()
 
283
        ui.ui_factory.note(gettext('upgraded to weaves:'))
 
284
        ui.ui_factory.note('  ' + gettext('%6d revisions and inventories') %
 
285
                                                        len(self.revisions))
 
286
        ui.ui_factory.note('  ' + gettext('%6d revisions not present') %
 
287
                                                    len(self.absent_revisions))
 
288
        ui.ui_factory.note('  ' + gettext('%6d texts') % self.text_count)
 
289
        self._cleanup_spare_files_after_format4()
 
290
        self.branch._transport.put_bytes(
 
291
            'branch-format',
 
292
            BzrDirFormat5().get_format_string(),
 
293
            mode=self.bzrdir._get_file_mode())
 
294
 
 
295
    def _cleanup_spare_files_after_format4(self):
 
296
        # FIXME working tree upgrade foo.
 
297
        for n in 'merged-patches', 'pending-merged-patches':
 
298
            try:
 
299
                ## assert os.path.getsize(p) == 0
 
300
                self.bzrdir.transport.delete(n)
 
301
            except errors.NoSuchFile:
 
302
                pass
 
303
        self.bzrdir.transport.delete_tree('inventory-store')
 
304
        self.bzrdir.transport.delete_tree('text-store')
 
305
 
 
306
    def _convert_working_inv(self):
 
307
        inv = xml4.serializer_v4.read_inventory(
 
308
                self.branch._transport.get('inventory'))
 
309
        new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv, working=True)
 
310
        self.branch._transport.put_bytes('inventory', new_inv_xml,
 
311
            mode=self.bzrdir._get_file_mode())
 
312
 
 
313
    def _write_all_weaves(self):
 
314
        controlweaves = VersionedFileStore(self.bzrdir.transport, prefixed=False,
 
315
            versionedfile_class=weave.WeaveFile)
 
316
        weave_transport = self.bzrdir.transport.clone('weaves')
 
317
        weaves = VersionedFileStore(weave_transport, prefixed=False,
 
318
                versionedfile_class=weave.WeaveFile)
 
319
        transaction = WriteTransaction()
 
320
 
 
321
        try:
 
322
            i = 0
 
323
            for file_id, file_weave in self.text_weaves.items():
 
324
                self.pb.update(gettext('writing weave'), i,
 
325
                                                        len(self.text_weaves))
 
326
                weaves._put_weave(file_id, file_weave, transaction)
 
327
                i += 1
 
328
            self.pb.update(gettext('inventory'), 0, 1)
 
329
            controlweaves._put_weave('inventory', self.inv_weave, transaction)
 
330
            self.pb.update(gettext('inventory'), 1, 1)
 
331
        finally:
 
332
            self.pb.clear()
 
333
 
 
334
    def _write_all_revs(self):
 
335
        """Write all revisions out in new form."""
 
336
        self.bzrdir.transport.delete_tree('revision-store')
 
337
        self.bzrdir.transport.mkdir('revision-store')
 
338
        revision_transport = self.bzrdir.transport.clone('revision-store')
 
339
        # TODO permissions
 
340
        from bzrlib.xml5 import serializer_v5
 
341
        from bzrlib.plugins.weave_fmt.repository import RevisionTextStore
 
342
        revision_store = RevisionTextStore(revision_transport,
 
343
            serializer_v5, False, versionedfile.PrefixMapper(),
 
344
            lambda:True, lambda:True)
 
345
        try:
 
346
            for i, rev_id in enumerate(self.converted_revs):
 
347
                self.pb.update(gettext('write revision'), i,
 
348
                                                len(self.converted_revs))
 
349
                text = serializer_v5.write_revision_to_string(
 
350
                    self.revisions[rev_id])
 
351
                key = (rev_id,)
 
352
                revision_store.add_lines(key, None, osutils.split_lines(text))
 
353
        finally:
 
354
            self.pb.clear()
 
355
 
 
356
    def _load_one_rev(self, rev_id):
 
357
        """Load a revision object into memory.
 
358
 
 
359
        Any parents not either loaded or abandoned get queued to be
 
360
        loaded."""
 
361
        self.pb.update(gettext('loading revision'),
 
362
                       len(self.revisions),
 
363
                       len(self.known_revisions))
 
364
        if not self.branch.repository.has_revision(rev_id):
 
365
            self.pb.clear()
 
366
            ui.ui_factory.note(gettext('revision {%s} not present in branch; '
 
367
                         'will be converted as a ghost') %
 
368
                         rev_id)
 
369
            self.absent_revisions.add(rev_id)
 
370
        else:
 
371
            rev = self.branch.repository.get_revision(rev_id)
 
372
            for parent_id in rev.parent_ids:
 
373
                self.known_revisions.add(parent_id)
 
374
                self.to_read.append(parent_id)
 
375
            self.revisions[rev_id] = rev
 
376
 
 
377
    def _load_old_inventory(self, rev_id):
 
378
        f = self.branch.repository.inventory_store.get(rev_id)
 
379
        try:
 
380
            old_inv_xml = f.read()
 
381
        finally:
 
382
            f.close()
 
383
        inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
 
384
        inv.revision_id = rev_id
 
385
        rev = self.revisions[rev_id]
 
386
        return inv
 
387
 
 
388
    def _load_updated_inventory(self, rev_id):
 
389
        inv_xml = self.inv_weave.get_text(rev_id)
 
390
        inv = xml5.serializer_v5.read_inventory_from_string(inv_xml, rev_id)
 
391
        return inv
 
392
 
 
393
    def _convert_one_rev(self, rev_id):
 
394
        """Convert revision and all referenced objects to new format."""
 
395
        rev = self.revisions[rev_id]
 
396
        inv = self._load_old_inventory(rev_id)
 
397
        present_parents = [p for p in rev.parent_ids
 
398
                           if p not in self.absent_revisions]
 
399
        self._convert_revision_contents(rev, inv, present_parents)
 
400
        self._store_new_inv(rev, inv, present_parents)
 
401
        self.converted_revs.add(rev_id)
 
402
 
 
403
    def _store_new_inv(self, rev, inv, present_parents):
 
404
        new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
 
405
        new_inv_sha1 = osutils.sha_string(new_inv_xml)
 
406
        self.inv_weave.add_lines(rev.revision_id,
 
407
                                 present_parents,
 
408
                                 new_inv_xml.splitlines(True))
 
409
        rev.inventory_sha1 = new_inv_sha1
 
410
 
 
411
    def _convert_revision_contents(self, rev, inv, present_parents):
 
412
        """Convert all the files within a revision.
 
413
 
 
414
        Also upgrade the inventory to refer to the text revision ids."""
 
415
        rev_id = rev.revision_id
 
416
        trace.mutter('converting texts of revision {%s}', rev_id)
 
417
        parent_invs = map(self._load_updated_inventory, present_parents)
 
418
        entries = inv.iter_entries()
 
419
        entries.next()
 
420
        for path, ie in entries:
 
421
            self._convert_file_version(rev, ie, parent_invs)
 
422
 
 
423
    def _convert_file_version(self, rev, ie, parent_invs):
 
424
        """Convert one version of one file.
 
425
 
 
426
        The file needs to be added into the weave if it is a merge
 
427
        of >=2 parents or if it's changed from its parent.
 
428
        """
 
429
        file_id = ie.file_id
 
430
        rev_id = rev.revision_id
 
431
        w = self.text_weaves.get(file_id)
 
432
        if w is None:
 
433
            w = weave.Weave(file_id)
 
434
            self.text_weaves[file_id] = w
 
435
        text_changed = False
 
436
        parent_candiate_entries = ie.parent_candidates(parent_invs)
 
437
        heads = graph.Graph(self).heads(parent_candiate_entries.keys())
 
438
        # XXX: Note that this is unordered - and this is tolerable because
 
439
        # the previous code was also unordered.
 
440
        previous_entries = dict((head, parent_candiate_entries[head]) for head
 
441
            in heads)
 
442
        self.snapshot_ie(previous_entries, ie, w, rev_id)
 
443
 
 
444
    def get_parent_map(self, revision_ids):
 
445
        """See graph.StackedParentsProvider.get_parent_map"""
 
446
        return dict((revision_id, self.revisions[revision_id])
 
447
                    for revision_id in revision_ids
 
448
                     if revision_id in self.revisions)
 
449
 
 
450
    def snapshot_ie(self, previous_revisions, ie, w, rev_id):
 
451
        # TODO: convert this logic, which is ~= snapshot to
 
452
        # a call to:. This needs the path figured out. rather than a work_tree
 
453
        # a v4 revision_tree can be given, or something that looks enough like
 
454
        # one to give the file content to the entry if it needs it.
 
455
        # and we need something that looks like a weave store for snapshot to
 
456
        # save against.
 
457
        #ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
 
458
        if len(previous_revisions) == 1:
 
459
            previous_ie = previous_revisions.values()[0]
 
460
            if ie._unchanged(previous_ie):
 
461
                ie.revision = previous_ie.revision
 
462
                return
 
463
        if ie.has_text():
 
464
            f = self.branch.repository._text_store.get(ie.text_id)
 
465
            try:
 
466
                file_lines = f.readlines()
 
467
            finally:
 
468
                f.close()
 
469
            w.add_lines(rev_id, previous_revisions, file_lines)
 
470
            self.text_count += 1
 
471
        else:
 
472
            w.add_lines(rev_id, previous_revisions, [])
 
473
        ie.revision = rev_id
 
474
 
 
475
    def _make_order(self):
 
476
        """Return a suitable order for importing revisions.
 
477
 
 
478
        The order must be such that an revision is imported after all
 
479
        its (present) parents.
 
480
        """
 
481
        todo = set(self.revisions.keys())
 
482
        done = self.absent_revisions.copy()
 
483
        order = []
 
484
        while todo:
 
485
            # scan through looking for a revision whose parents
 
486
            # are all done
 
487
            for rev_id in sorted(list(todo)):
 
488
                rev = self.revisions[rev_id]
 
489
                parent_ids = set(rev.parent_ids)
 
490
                if parent_ids.issubset(done):
 
491
                    # can take this one now
 
492
                    order.append(rev_id)
 
493
                    todo.remove(rev_id)
 
494
                    done.add(rev_id)
 
495
        return order
 
496
 
 
497
 
 
498
class ConvertBzrDir5To6(Converter):
 
499
    """Converts format 5 bzr dirs to format 6."""
 
500
 
 
501
    def convert(self, to_convert, pb):
 
502
        """See Converter.convert()."""
 
503
        self.bzrdir = to_convert
 
504
        pb = ui.ui_factory.nested_progress_bar()
 
505
        try:
 
506
            ui.ui_factory.note(gettext('starting upgrade from format 5 to 6'))
 
507
            self._convert_to_prefixed()
 
508
            return BzrDir.open(self.bzrdir.user_url)
 
509
        finally:
 
510
            pb.finished()
 
511
 
 
512
    def _convert_to_prefixed(self):
 
513
        from bzrlib.store import TransportStore
 
514
        self.bzrdir.transport.delete('branch-format')
 
515
        for store_name in ["weaves", "revision-store"]:
 
516
            ui.ui_factory.note(gettext("adding prefixes to %s") % store_name)
 
517
            store_transport = self.bzrdir.transport.clone(store_name)
 
518
            store = TransportStore(store_transport, prefixed=True)
 
519
            for urlfilename in store_transport.list_dir('.'):
 
520
                filename = urlutils.unescape(urlfilename)
 
521
                if (filename.endswith(".weave") or
 
522
                    filename.endswith(".gz") or
 
523
                    filename.endswith(".sig")):
 
524
                    file_id, suffix = os.path.splitext(filename)
 
525
                else:
 
526
                    file_id = filename
 
527
                    suffix = ''
 
528
                new_name = store._mapper.map((file_id,)) + suffix
 
529
                # FIXME keep track of the dirs made RBC 20060121
 
530
                try:
 
531
                    store_transport.move(filename, new_name)
 
532
                except errors.NoSuchFile: # catches missing dirs strangely enough
 
533
                    store_transport.mkdir(osutils.dirname(new_name))
 
534
                    store_transport.move(filename, new_name)
 
535
        self.bzrdir.transport.put_bytes(
 
536
            'branch-format',
 
537
            BzrDirFormat6().get_format_string(),
 
538
            mode=self.bzrdir._get_file_mode())
 
539
 
 
540
 
 
541
class ConvertBzrDir6ToMeta(Converter):
 
542
    """Converts format 6 bzr dirs to metadirs."""
 
543
 
 
544
    def convert(self, to_convert, pb):
 
545
        """See Converter.convert()."""
 
546
        from bzrlib.plugins.weave_fmt.repository import RepositoryFormat7
 
547
        from bzrlib.branch import BzrBranchFormat5
 
548
        self.bzrdir = to_convert
 
549
        self.pb = ui.ui_factory.nested_progress_bar()
 
550
        self.count = 0
 
551
        self.total = 20 # the steps we know about
 
552
        self.garbage_inventories = []
 
553
        self.dir_mode = self.bzrdir._get_dir_mode()
 
554
        self.file_mode = self.bzrdir._get_file_mode()
 
555
 
 
556
        ui.ui_factory.note(gettext('starting upgrade from format 6 to metadir'))
 
557
        self.bzrdir.transport.put_bytes(
 
558
                'branch-format',
 
559
                "Converting to format 6",
 
560
                mode=self.file_mode)
 
561
        # its faster to move specific files around than to open and use the apis...
 
562
        # first off, nuke ancestry.weave, it was never used.
 
563
        try:
 
564
            self.step(gettext('Removing ancestry.weave'))
 
565
            self.bzrdir.transport.delete('ancestry.weave')
 
566
        except errors.NoSuchFile:
 
567
            pass
 
568
        # find out whats there
 
569
        self.step(gettext('Finding branch files'))
 
570
        last_revision = self.bzrdir.open_branch().last_revision()
 
571
        bzrcontents = self.bzrdir.transport.list_dir('.')
 
572
        for name in bzrcontents:
 
573
            if name.startswith('basis-inventory.'):
 
574
                self.garbage_inventories.append(name)
 
575
        # create new directories for repository, working tree and branch
 
576
        repository_names = [('inventory.weave', True),
 
577
                            ('revision-store', True),
 
578
                            ('weaves', True)]
 
579
        self.step(gettext('Upgrading repository') + '  ')
 
580
        self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
 
581
        self.make_lock('repository')
 
582
        # we hard code the formats here because we are converting into
 
583
        # the meta format. The meta format upgrader can take this to a
 
584
        # future format within each component.
 
585
        self.put_format('repository', RepositoryFormat7())
 
586
        for entry in repository_names:
 
587
            self.move_entry('repository', entry)
 
588
 
 
589
        self.step(gettext('Upgrading branch') + '      ')
 
590
        self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
 
591
        self.make_lock('branch')
 
592
        self.put_format('branch', BzrBranchFormat5())
 
593
        branch_files = [('revision-history', True),
 
594
                        ('branch-name', True),
 
595
                        ('parent', False)]
 
596
        for entry in branch_files:
 
597
            self.move_entry('branch', entry)
 
598
 
 
599
        checkout_files = [('pending-merges', True),
 
600
                          ('inventory', True),
 
601
                          ('stat-cache', False)]
 
602
        # If a mandatory checkout file is not present, the branch does not have
 
603
        # a functional checkout. Do not create a checkout in the converted
 
604
        # branch.
 
605
        for name, mandatory in checkout_files:
 
606
            if mandatory and name not in bzrcontents:
 
607
                has_checkout = False
 
608
                break
 
609
        else:
 
610
            has_checkout = True
 
611
        if not has_checkout:
 
612
            ui.ui_factory.note(gettext('No working tree.'))
 
613
            # If some checkout files are there, we may as well get rid of them.
 
614
            for name, mandatory in checkout_files:
 
615
                if name in bzrcontents:
 
616
                    self.bzrdir.transport.delete(name)
 
617
        else:
 
618
            from bzrlib.workingtree_3 import WorkingTreeFormat3
 
619
            self.step(gettext('Upgrading working tree'))
 
620
            self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
 
621
            self.make_lock('checkout')
 
622
            self.put_format(
 
623
                'checkout', WorkingTreeFormat3())
 
624
            self.bzrdir.transport.delete_multi(
 
625
                self.garbage_inventories, self.pb)
 
626
            for entry in checkout_files:
 
627
                self.move_entry('checkout', entry)
 
628
            if last_revision is not None:
 
629
                self.bzrdir.transport.put_bytes(
 
630
                    'checkout/last-revision', last_revision)
 
631
        self.bzrdir.transport.put_bytes(
 
632
            'branch-format',
 
633
            BzrDirMetaFormat1().get_format_string(),
 
634
            mode=self.file_mode)
 
635
        self.pb.finished()
 
636
        return BzrDir.open(self.bzrdir.user_url)
 
637
 
 
638
    def make_lock(self, name):
 
639
        """Make a lock for the new control dir name."""
 
640
        self.step(gettext('Make %s lock') % name)
 
641
        ld = lockdir.LockDir(self.bzrdir.transport,
 
642
                             '%s/lock' % name,
 
643
                             file_modebits=self.file_mode,
 
644
                             dir_modebits=self.dir_mode)
 
645
        ld.create()
 
646
 
 
647
    def move_entry(self, new_dir, entry):
 
648
        """Move then entry name into new_dir."""
 
649
        name = entry[0]
 
650
        mandatory = entry[1]
 
651
        self.step(gettext('Moving %s') % name)
 
652
        try:
 
653
            self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
 
654
        except errors.NoSuchFile:
 
655
            if mandatory:
 
656
                raise
 
657
 
 
658
    def put_format(self, dirname, format):
 
659
        self.bzrdir.transport.put_bytes('%s/format' % dirname,
 
660
            format.get_format_string(),
 
661
            self.file_mode)
 
662
 
 
663
 
 
664
class BzrDirFormat4(BzrDirFormat):
 
665
    """Bzr dir format 4.
 
666
 
 
667
    This format is a combined format for working tree, branch and repository.
 
668
    It has:
 
669
     - Format 1 working trees [always]
 
670
     - Format 4 branches [always]
 
671
     - Format 4 repositories [always]
 
672
 
 
673
    This format is deprecated: it indexes texts using a text it which is
 
674
    removed in format 5; write support for this format has been removed.
 
675
    """
 
676
 
 
677
    _lock_class = lockable_files.TransportLock
 
678
 
 
679
    def __eq__(self, other):
 
680
        return type(self) == type(other)
 
681
 
 
682
    @classmethod
 
683
    def get_format_string(cls):
 
684
        """See BzrDirFormat.get_format_string()."""
 
685
        return "Bazaar-NG branch, format 0.0.4\n"
 
686
 
 
687
    def get_format_description(self):
 
688
        """See BzrDirFormat.get_format_description()."""
 
689
        return "All-in-one format 4"
 
690
 
 
691
    def get_converter(self, format=None):
 
692
        """See BzrDirFormat.get_converter()."""
 
693
        # there is one and only one upgrade path here.
 
694
        return ConvertBzrDir4To5()
 
695
 
 
696
    def initialize_on_transport(self, transport):
 
697
        """Format 4 branches cannot be created."""
 
698
        raise errors.UninitializableFormat(self)
 
699
 
 
700
    def is_supported(self):
 
701
        """Format 4 is not supported.
 
702
 
 
703
        It is not supported because the model changed from 4 to 5 and the
 
704
        conversion logic is expensive - so doing it on the fly was not
 
705
        feasible.
 
706
        """
 
707
        return False
 
708
 
 
709
    def network_name(self):
 
710
        return self.get_format_string()
 
711
 
 
712
    def _open(self, transport):
 
713
        """See BzrDirFormat._open."""
 
714
        return BzrDir4(transport, self)
 
715
 
 
716
    def __return_repository_format(self):
 
717
        """Circular import protection."""
 
718
        from bzrlib.plugins.weave_fmt.repository import RepositoryFormat4
 
719
        return RepositoryFormat4()
 
720
    repository_format = property(__return_repository_format)
 
721
 
 
722
    @classmethod
 
723
    def from_string(cls, format_string):
 
724
        if format_string != cls.get_format_string():
 
725
            raise AssertionError("unexpected format string %r" % format_string)
 
726
        return cls()
 
727
 
 
728
 
 
729
class BzrDirPreSplitOut(BzrDir):
 
730
    """A common class for the all-in-one formats."""
 
731
 
 
732
    def __init__(self, _transport, _format):
 
733
        """See BzrDir.__init__."""
 
734
        super(BzrDirPreSplitOut, self).__init__(_transport, _format)
 
735
        self._control_files = lockable_files.LockableFiles(
 
736
                                            self.get_branch_transport(None),
 
737
                                            self._format._lock_file_name,
 
738
                                            self._format._lock_class)
 
739
 
 
740
    def break_lock(self):
 
741
        """Pre-splitout bzrdirs do not suffer from stale locks."""
 
742
        raise NotImplementedError(self.break_lock)
 
743
 
 
744
    def cloning_metadir(self, require_stacking=False):
 
745
        """Produce a metadir suitable for cloning with."""
 
746
        if require_stacking:
 
747
            return format_registry.make_bzrdir('1.6')
 
748
        return self._format.__class__()
 
749
 
 
750
    def clone(self, url, revision_id=None, force_new_repo=False,
 
751
              preserve_stacking=False):
 
752
        """See BzrDir.clone().
 
753
 
 
754
        force_new_repo has no effect, since this family of formats always
 
755
        require a new repository.
 
756
        preserve_stacking has no effect, since no source branch using this
 
757
        family of formats can be stacked, so there is no stacking to preserve.
 
758
        """
 
759
        self._make_tail(url)
 
760
        result = self._format._initialize_for_clone(url)
 
761
        self.open_repository().clone(result, revision_id=revision_id)
 
762
        from_branch = self.open_branch()
 
763
        from_branch.clone(result, revision_id=revision_id)
 
764
        try:
 
765
            tree = self.open_workingtree()
 
766
        except errors.NotLocalUrl:
 
767
            # make a new one, this format always has to have one.
 
768
            result._init_workingtree()
 
769
        else:
 
770
            tree.clone(result)
 
771
        return result
 
772
 
 
773
    def create_branch(self, name=None, repository=None,
 
774
                      append_revisions_only=None):
 
775
        """See BzrDir.create_branch."""
 
776
        if repository is not None:
 
777
            raise NotImplementedError(
 
778
                "create_branch(repository=<not None>) on %r" % (self,))
 
779
        return self._format.get_branch_format().initialize(self, name=name,
 
780
            append_revisions_only=append_revisions_only)
 
781
 
 
782
    def destroy_branch(self, name=None):
 
783
        """See BzrDir.destroy_branch."""
 
784
        raise errors.UnsupportedOperation(self.destroy_branch, self)
 
785
 
 
786
    def create_repository(self, shared=False):
 
787
        """See BzrDir.create_repository."""
 
788
        if shared:
 
789
            raise errors.IncompatibleFormat('shared repository', self._format)
 
790
        return self.open_repository()
 
791
 
 
792
    def destroy_repository(self):
 
793
        """See BzrDir.destroy_repository."""
 
794
        raise errors.UnsupportedOperation(self.destroy_repository, self)
 
795
 
 
796
    def create_workingtree(self, revision_id=None, from_branch=None,
 
797
                           accelerator_tree=None, hardlink=False):
 
798
        """See BzrDir.create_workingtree."""
 
799
        # The workingtree is sometimes created when the bzrdir is created,
 
800
        # but not when cloning.
 
801
 
 
802
        # this looks buggy but is not -really-
 
803
        # because this format creates the workingtree when the bzrdir is
 
804
        # created
 
805
        # clone and sprout will have set the revision_id
 
806
        # and that will have set it for us, its only
 
807
        # specific uses of create_workingtree in isolation
 
808
        # that can do wonky stuff here, and that only
 
809
        # happens for creating checkouts, which cannot be
 
810
        # done on this format anyway. So - acceptable wart.
 
811
        if hardlink:
 
812
            warning("can't support hardlinked working trees in %r"
 
813
                % (self,))
 
814
        try:
 
815
            result = self.open_workingtree(recommend_upgrade=False)
 
816
        except errors.NoSuchFile:
 
817
            result = self._init_workingtree()
 
818
        if revision_id is not None:
 
819
            if revision_id == _mod_revision.NULL_REVISION:
 
820
                result.set_parent_ids([])
 
821
            else:
 
822
                result.set_parent_ids([revision_id])
 
823
        return result
 
824
 
 
825
    def _init_workingtree(self):
 
826
        from bzrlib.plugins.weave_fmt.workingtree import WorkingTreeFormat2
 
827
        try:
 
828
            return WorkingTreeFormat2().initialize(self)
 
829
        except errors.NotLocalUrl:
 
830
            # Even though we can't access the working tree, we need to
 
831
            # create its control files.
 
832
            return WorkingTreeFormat2()._stub_initialize_on_transport(
 
833
                self.transport, self._control_files._file_mode)
 
834
 
 
835
    def destroy_workingtree(self):
 
836
        """See BzrDir.destroy_workingtree."""
 
837
        raise errors.UnsupportedOperation(self.destroy_workingtree, self)
 
838
 
 
839
    def destroy_workingtree_metadata(self):
 
840
        """See BzrDir.destroy_workingtree_metadata."""
 
841
        raise errors.UnsupportedOperation(self.destroy_workingtree_metadata,
 
842
                                          self)
 
843
 
 
844
    def get_branch_transport(self, branch_format, name=None):
 
845
        """See BzrDir.get_branch_transport()."""
 
846
        if name is not None:
 
847
            raise errors.NoColocatedBranchSupport(self)
 
848
        if branch_format is None:
 
849
            return self.transport
 
850
        try:
 
851
            branch_format.get_format_string()
 
852
        except NotImplementedError:
 
853
            return self.transport
 
854
        raise errors.IncompatibleFormat(branch_format, self._format)
 
855
 
 
856
    def get_repository_transport(self, repository_format):
 
857
        """See BzrDir.get_repository_transport()."""
 
858
        if repository_format is None:
 
859
            return self.transport
 
860
        try:
 
861
            repository_format.get_format_string()
 
862
        except NotImplementedError:
 
863
            return self.transport
 
864
        raise errors.IncompatibleFormat(repository_format, self._format)
 
865
 
 
866
    def get_workingtree_transport(self, workingtree_format):
 
867
        """See BzrDir.get_workingtree_transport()."""
 
868
        if workingtree_format is None:
 
869
            return self.transport
 
870
        try:
 
871
            workingtree_format.get_format_string()
 
872
        except NotImplementedError:
 
873
            return self.transport
 
874
        raise errors.IncompatibleFormat(workingtree_format, self._format)
 
875
 
 
876
    def needs_format_conversion(self, format=None):
 
877
        """See BzrDir.needs_format_conversion()."""
 
878
        # if the format is not the same as the system default,
 
879
        # an upgrade is needed.
 
880
        if format is None:
 
881
            symbol_versioning.warn(symbol_versioning.deprecated_in((1, 13, 0))
 
882
                % 'needs_format_conversion(format=None)')
 
883
            format = BzrDirFormat.get_default_format()
 
884
        return not isinstance(self._format, format.__class__)
 
885
 
 
886
    def open_branch(self, name=None, unsupported=False,
 
887
                    ignore_fallbacks=False, possible_transports=None):
 
888
        """See BzrDir.open_branch."""
 
889
        from bzrlib.plugins.weave_fmt.branch import BzrBranchFormat4
 
890
        format = BzrBranchFormat4()
 
891
        format.check_support_status(unsupported)
 
892
        return format.open(self, name, _found=True,
 
893
            possible_transports=possible_transports)
 
894
 
 
895
    def sprout(self, url, revision_id=None, force_new_repo=False,
 
896
               possible_transports=None, accelerator_tree=None,
 
897
               hardlink=False, stacked=False, create_tree_if_local=True,
 
898
               source_branch=None):
 
899
        """See BzrDir.sprout()."""
 
900
        if source_branch is not None:
 
901
            my_branch = self.open_branch()
 
902
            if source_branch.base != my_branch.base:
 
903
                raise AssertionError(
 
904
                    "source branch %r is not within %r with branch %r" %
 
905
                    (source_branch, self, my_branch))
 
906
        if stacked:
 
907
            raise errors.UnstackableBranchFormat(
 
908
                self._format, self.root_transport.base)
 
909
        if not create_tree_if_local:
 
910
            raise errors.MustHaveWorkingTree(
 
911
                self._format, self.root_transport.base)
 
912
        from bzrlib.plugins.weave_fmt.workingtree import WorkingTreeFormat2
 
913
        self._make_tail(url)
 
914
        result = self._format._initialize_for_clone(url)
 
915
        try:
 
916
            self.open_repository().clone(result, revision_id=revision_id)
 
917
        except errors.NoRepositoryPresent:
 
918
            pass
 
919
        try:
 
920
            self.open_branch().sprout(result, revision_id=revision_id)
 
921
        except errors.NotBranchError:
 
922
            pass
 
923
 
 
924
        # we always want a working tree
 
925
        WorkingTreeFormat2().initialize(result,
 
926
                                        accelerator_tree=accelerator_tree,
 
927
                                        hardlink=hardlink)
 
928
        return result
 
929
 
 
930
 
 
931
class BzrDir4(BzrDirPreSplitOut):
 
932
    """A .bzr version 4 control object.
 
933
 
 
934
    This is a deprecated format and may be removed after sept 2006.
 
935
    """
 
936
 
 
937
    def create_repository(self, shared=False):
 
938
        """See BzrDir.create_repository."""
 
939
        return self._format.repository_format.initialize(self, shared)
 
940
 
 
941
    def needs_format_conversion(self, format=None):
 
942
        """Format 4 dirs are always in need of conversion."""
 
943
        if format is None:
 
944
            symbol_versioning.warn(symbol_versioning.deprecated_in((1, 13, 0))
 
945
                % 'needs_format_conversion(format=None)')
 
946
        return True
 
947
 
 
948
    def open_repository(self):
 
949
        """See BzrDir.open_repository."""
 
950
        from bzrlib.plugins.weave_fmt.repository import RepositoryFormat4
 
951
        return RepositoryFormat4().open(self, _found=True)
 
952
 
 
953
 
 
954
class BzrDir5(BzrDirPreSplitOut):
 
955
    """A .bzr version 5 control object.
 
956
 
 
957
    This is a deprecated format and may be removed after sept 2006.
 
958
    """
 
959
 
 
960
    def has_workingtree(self):
 
961
        """See BzrDir.has_workingtree."""
 
962
        return True
 
963
    
 
964
    def open_repository(self):
 
965
        """See BzrDir.open_repository."""
 
966
        from bzrlib.plugins.weave_fmt.repository import RepositoryFormat5
 
967
        return RepositoryFormat5().open(self, _found=True)
 
968
 
 
969
    def open_workingtree(self, unsupported=False,
 
970
            recommend_upgrade=True):
 
971
        """See BzrDir.create_workingtree."""
 
972
        from bzrlib.plugins.weave_fmt.workingtree import WorkingTreeFormat2
 
973
        wt_format = WorkingTreeFormat2()
 
974
        # we don't warn here about upgrades; that ought to be handled for the
 
975
        # bzrdir as a whole
 
976
        return wt_format.open(self, _found=True)
 
977
 
 
978
 
 
979
class BzrDir6(BzrDirPreSplitOut):
 
980
    """A .bzr version 6 control object.
 
981
 
 
982
    This is a deprecated format and may be removed after sept 2006.
 
983
    """
 
984
 
 
985
    def has_workingtree(self):
 
986
        """See BzrDir.has_workingtree."""
 
987
        return True
 
988
 
 
989
    def open_repository(self):
 
990
        """See BzrDir.open_repository."""
 
991
        from bzrlib.plugins.weave_fmt.repository import RepositoryFormat6
 
992
        return RepositoryFormat6().open(self, _found=True)
 
993
 
 
994
    def open_workingtree(self, unsupported=False, recommend_upgrade=True):
 
995
        """See BzrDir.create_workingtree."""
 
996
        # we don't warn here about upgrades; that ought to be handled for the
 
997
        # bzrdir as a whole
 
998
        from bzrlib.plugins.weave_fmt.workingtree import WorkingTreeFormat2
 
999
        return WorkingTreeFormat2().open(self, _found=True)