~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repofmt/weaverepo.py

  • Committer: NamNguyen
  • Date: 2007-08-01 06:14:14 UTC
  • mto: (2789.1.1 ianc-integration)
  • mto: This revision was merged to the branch mainline in revision 2790.
  • Revision ID: namnguyen-20070801061414-u0tzrfgcz6z604lz
``Branch.hooks`` now supports ``pre_commit`` hook.

The hook's signature is

::

  hook(local, master, old_revno, old_revid, new_revno, new_revid,
       deleted_paths, added_paths, future_revision_tree)

``deleted_paths`` and ``added_paths`` are lists of paths. Renamed paths are
recorded in both ``deleted_paths`` and ``added_paths`` (i.e. deleted then
added).

``future_revision_tree`` is obtained from ``CommitBuilder.revision_tree``
to save hooks from getting it from the branch again.

For example, hooks can get a file:

::

  for path in added_paths:
      id = future_revision_tree.path2id(path)
      if future_revision_tree.kind(id) == 'file':
          file = future_revision_tree.get_file(id)
          ...

or export a tree and do ``make check`` or similar

::

  import bzrlib.export
  bzrlib.export.export(future_revision_tree, 'tmp_space')


If the commit is to be rejected, hooks should raise an ``Exception``.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
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
 
"""Deprecated weave-based repository formats.
18
 
 
19
 
Weave based formats scaled linearly with history size and could not represent
20
 
ghosts.
21
 
"""
22
 
 
23
 
import os
24
 
from cStringIO import StringIO
25
 
import urllib
26
 
 
27
 
from bzrlib.lazy_import import lazy_import
28
 
lazy_import(globals(), """
29
 
from bzrlib import (
30
 
    xml5,
31
 
    graph as _mod_graph,
32
 
    )
33
 
""")
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
 
 
18
"""Old weave-based repository formats"""
 
19
 
 
20
from StringIO import StringIO
 
21
 
34
22
from bzrlib import (
35
23
    bzrdir,
36
 
    debug,
37
 
    errors,
38
24
    lockable_files,
39
25
    lockdir,
40
 
    osutils,
41
 
    revision as _mod_revision,
42
 
    urlutils,
43
 
    versionedfile,
44
26
    weave,
45
27
    weavefile,
 
28
    xml5,
46
29
    )
47
30
from bzrlib.decorators import needs_read_lock, needs_write_lock
48
31
from bzrlib.repository import (
49
 
    CommitBuilder,
50
 
    MetaDirVersionedFileRepository,
 
32
    MetaDirRepository,
51
33
    MetaDirRepositoryFormat,
52
34
    Repository,
53
35
    RepositoryFormat,
54
36
    )
55
37
from bzrlib.store.text import TextStore
56
38
from bzrlib.trace import mutter
57
 
from bzrlib.tuned_gzip import GzipFile, bytes_to_gzip
58
 
from bzrlib.versionedfile import (
59
 
    AbsentContentFactory,
60
 
    FulltextContentFactory,
61
 
    VersionedFiles,
62
 
    )
63
39
 
64
40
 
65
41
class AllInOneRepository(Repository):
66
42
    """Legacy support - the repository behaviour for all-in-one branches."""
67
43
 
68
 
    @property
69
 
    def _serializer(self):
70
 
        return xml5.serializer_v5
71
 
 
72
 
    def _escape(self, file_or_path):
73
 
        if not isinstance(file_or_path, basestring):
74
 
            file_or_path = '/'.join(file_or_path)
75
 
        if file_or_path == '':
76
 
            return u''
77
 
        return urlutils.escape(osutils.safe_unicode(file_or_path))
78
 
 
79
 
    def __init__(self, _format, a_bzrdir):
 
44
    _serializer = xml5.serializer_v5
 
45
 
 
46
    def __init__(self, _format, a_bzrdir, _revision_store, control_store, text_store):
80
47
        # we reuse one control files instance.
81
 
        dir_mode = a_bzrdir._get_dir_mode()
82
 
        file_mode = a_bzrdir._get_file_mode()
 
48
        dir_mode = a_bzrdir._control_files._dir_mode
 
49
        file_mode = a_bzrdir._control_files._file_mode
83
50
 
84
51
        def get_store(name, compressed=True, prefixed=False):
85
52
            # FIXME: This approach of assuming stores are all entirely compressed
86
 
            # or entirely uncompressed is tidy, but breaks upgrade from
87
 
            # some existing branches where there's a mixture; we probably
 
53
            # or entirely uncompressed is tidy, but breaks upgrade from 
 
54
            # some existing branches where there's a mixture; we probably 
88
55
            # still want the option to look for both.
89
 
            relpath = self._escape(name)
90
 
            store = TextStore(a_bzrdir.transport.clone(relpath),
 
56
            relpath = a_bzrdir._control_files._escape(name)
 
57
            store = TextStore(a_bzrdir._control_files._transport.clone(relpath),
91
58
                              prefixed=prefixed, compressed=compressed,
92
59
                              dir_mode=dir_mode,
93
60
                              file_mode=file_mode)
 
61
            #if self._transport.should_cache():
 
62
            #    cache_path = os.path.join(self.cache_root, name)
 
63
            #    os.mkdir(cache_path)
 
64
            #    store = bzrlib.store.CachedStore(store, cache_path)
94
65
            return store
95
66
 
96
67
        # not broken out yet because the controlweaves|inventory_store
97
 
        # and texts bits are still different.
 
68
        # and text_store | weave_store bits are still different.
98
69
        if isinstance(_format, RepositoryFormat4):
99
 
            # cannot remove these - there is still no consistent api
 
70
            # cannot remove these - there is still no consistent api 
100
71
            # which allows access to this old info.
101
72
            self.inventory_store = get_store('inventory-store')
102
 
            self._text_store = get_store('text-store')
103
 
        super(AllInOneRepository, self).__init__(_format, a_bzrdir, a_bzrdir._control_files)
104
 
 
105
 
    @needs_read_lock
106
 
    def _all_possible_ids(self):
107
 
        """Return all the possible revisions that we could find."""
108
 
        if 'evil' in debug.debug_flags:
109
 
            mutter_callsite(3, "_all_possible_ids scales with size of history.")
110
 
        return [key[-1] for key in self.inventories.keys()]
111
 
 
112
 
    @needs_read_lock
113
 
    def _all_revision_ids(self):
114
 
        """Returns a list of all the revision ids in the repository.
115
 
 
116
 
        These are in as much topological order as the underlying store can
117
 
        present: for weaves ghosts may lead to a lack of correctness until
118
 
        the reweave updates the parents list.
119
 
        """
120
 
        return [key[-1] for key in self.revisions.keys()]
121
 
 
122
 
    def _activate_new_inventory(self):
123
 
        """Put a replacement inventory.new into use as inventories."""
124
 
        # Copy the content across
125
 
        t = self.bzrdir._control_files._transport
126
 
        t.copy('inventory.new.weave', 'inventory.weave')
127
 
        # delete the temp inventory
128
 
        t.delete('inventory.new.weave')
129
 
        # Check we can parse the new weave properly as a sanity check
130
 
        self.inventories.keys()
131
 
 
132
 
    def _backup_inventory(self):
133
 
        t = self.bzrdir._control_files._transport
134
 
        t.copy('inventory.weave', 'inventory.backup.weave')
135
 
 
136
 
    def _temp_inventories(self):
137
 
        t = self.bzrdir._control_files._transport
138
 
        return self._format._get_inventories(t, self, 'inventory.new')
 
73
            text_store = get_store('text-store')
 
74
        super(AllInOneRepository, self).__init__(_format, a_bzrdir, a_bzrdir._control_files, _revision_store, control_store, text_store)
139
75
 
140
76
    def get_commit_builder(self, branch, parents, config, timestamp=None,
141
77
                           timezone=None, committer=None, revprops=None,
142
78
                           revision_id=None):
143
79
        self._check_ascii_revisionid(revision_id, self.get_commit_builder)
144
 
        result = CommitBuilder(self, parents, config, timestamp, timezone,
145
 
                              committer, revprops, revision_id)
146
 
        self.start_write_group()
147
 
        return result
 
80
        return Repository.get_commit_builder(self, branch, parents, config,
 
81
            timestamp, timezone, committer, revprops, revision_id)
148
82
 
149
83
    @needs_read_lock
150
 
    def get_revisions(self, revision_ids):
151
 
        revs = self._get_revisions(revision_ids)
152
 
        return revs
153
 
 
154
 
    def _inventory_add_lines(self, revision_id, parents, lines,
155
 
        check_content=True):
156
 
        """Store lines in inv_vf and return the sha1 of the inventory."""
157
 
        present_parents = self.get_graph().get_parent_map(parents)
158
 
        final_parents = []
159
 
        for parent in parents:
160
 
            if parent in present_parents:
161
 
                final_parents.append((parent,))
162
 
        return self.inventories.add_lines((revision_id,), final_parents, lines,
163
 
            check_content=check_content)[0]
164
 
 
165
84
    def is_shared(self):
166
85
        """AllInOne repositories cannot be shared."""
167
86
        return False
176
95
        :param new_value: True to restore the default, False to disable making
177
96
                          working trees.
178
97
        """
179
 
        raise errors.RepositoryUpgradeRequired(self.bzrdir.root_transport.base)
180
 
 
 
98
        raise NotImplementedError(self.set_make_working_trees)
 
99
    
181
100
    def make_working_trees(self):
182
101
        """Returns the policy for making working trees on new branches."""
183
102
        return True
184
103
 
185
 
    def revision_graph_can_have_wrong_parents(self):
186
 
        # XXX: This is an old format that we don't support full checking on, so
187
 
        # just claim that checking for this inconsistency is not required.
188
 
        return False
189
 
 
190
 
 
191
 
class WeaveMetaDirRepository(MetaDirVersionedFileRepository):
 
104
 
 
105
class WeaveMetaDirRepository(MetaDirRepository):
192
106
    """A subclass of MetaDirRepository to set weave specific policy."""
193
107
 
194
 
    def __init__(self, _format, a_bzrdir, control_files):
195
 
        super(WeaveMetaDirRepository, self).__init__(_format, a_bzrdir, control_files)
196
 
        self._serializer = _format._serializer
197
 
 
198
 
    @needs_read_lock
199
 
    def _all_possible_ids(self):
200
 
        """Return all the possible revisions that we could find."""
201
 
        if 'evil' in debug.debug_flags:
202
 
            mutter_callsite(3, "_all_possible_ids scales with size of history.")
203
 
        return [key[-1] for key in self.inventories.keys()]
204
 
 
205
 
    @needs_read_lock
206
 
    def _all_revision_ids(self):
207
 
        """Returns a list of all the revision ids in the repository.
208
 
 
209
 
        These are in as much topological order as the underlying store can
210
 
        present: for weaves ghosts may lead to a lack of correctness until
211
 
        the reweave updates the parents list.
212
 
        """
213
 
        return [key[-1] for key in self.revisions.keys()]
214
 
 
215
 
    def _activate_new_inventory(self):
216
 
        """Put a replacement inventory.new into use as inventories."""
217
 
        # Copy the content across
218
 
        t = self._transport
219
 
        t.copy('inventory.new.weave', 'inventory.weave')
220
 
        # delete the temp inventory
221
 
        t.delete('inventory.new.weave')
222
 
        # Check we can parse the new weave properly as a sanity check
223
 
        self.inventories.keys()
224
 
 
225
 
    def _backup_inventory(self):
226
 
        t = self._transport
227
 
        t.copy('inventory.weave', 'inventory.backup.weave')
228
 
 
229
 
    def _temp_inventories(self):
230
 
        t = self._transport
231
 
        return self._format._get_inventories(t, self, 'inventory.new')
 
108
    _serializer = xml5.serializer_v5
232
109
 
233
110
    def get_commit_builder(self, branch, parents, config, timestamp=None,
234
111
                           timezone=None, committer=None, revprops=None,
235
112
                           revision_id=None):
236
113
        self._check_ascii_revisionid(revision_id, self.get_commit_builder)
237
 
        result = CommitBuilder(self, parents, config, timestamp, timezone,
238
 
                              committer, revprops, revision_id)
239
 
        self.start_write_group()
240
 
        return result
241
 
 
242
 
    @needs_read_lock
243
 
    def get_revision(self, revision_id):
244
 
        """Return the Revision object for a named revision"""
245
 
        r = self.get_revision_reconcile(revision_id)
246
 
        return r
247
 
 
248
 
    def _inventory_add_lines(self, revision_id, parents, lines,
249
 
        check_content=True):
250
 
        """Store lines in inv_vf and return the sha1 of the inventory."""
251
 
        present_parents = self.get_graph().get_parent_map(parents)
252
 
        final_parents = []
253
 
        for parent in parents:
254
 
            if parent in present_parents:
255
 
                final_parents.append((parent,))
256
 
        return self.inventories.add_lines((revision_id,), final_parents, lines,
257
 
            check_content=check_content)[0]
258
 
 
259
 
    def revision_graph_can_have_wrong_parents(self):
260
 
        return False
 
114
        return MetaDirRepository.get_commit_builder(self, branch, parents,
 
115
            config, timestamp, timezone, committer, revprops, revision_id)
261
116
 
262
117
 
263
118
class PreSplitOutRepositoryFormat(RepositoryFormat):
265
120
 
266
121
    rich_root_data = False
267
122
    supports_tree_reference = False
268
 
    supports_ghosts = False
269
 
    supports_external_lookups = False
270
 
    supports_chks = False
271
 
    _fetch_order = 'topological'
272
 
    _fetch_reconcile = True
273
 
    fast_deltas = False
274
123
 
275
124
    def initialize(self, a_bzrdir, shared=False, _internal=False):
276
 
        """Create a weave repository."""
 
125
        """Create a weave repository.
 
126
        
 
127
        TODO: when creating split out bzr branch formats, move this to a common
 
128
        base for Format5, Format6. or something like that.
 
129
        """
277
130
        if shared:
278
131
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
279
132
 
280
133
        if not _internal:
281
134
            # always initialized when the bzrdir is.
282
135
            return self.open(a_bzrdir, _found=True)
283
 
 
 
136
        
284
137
        # Create an empty weave
285
138
        sio = StringIO()
286
139
        weavefile.write_weave_v5(weave.Weave(), sio)
287
140
        empty_weave = sio.getvalue()
288
141
 
289
142
        mutter('creating repository in %s.', a_bzrdir.transport.base)
290
 
 
 
143
        dirs = ['revision-store', 'weaves']
 
144
        files = [('inventory.weave', StringIO(empty_weave)),
 
145
                 ]
 
146
        
291
147
        # FIXME: RBC 20060125 don't peek under the covers
292
148
        # NB: no need to escape relative paths that are url safe.
293
149
        control_files = lockable_files.LockableFiles(a_bzrdir.transport,
294
 
            'branch-lock', lockable_files.TransportLock)
 
150
                                'branch-lock', lockable_files.TransportLock)
295
151
        control_files.create_lock()
296
152
        control_files.lock_write()
297
 
        transport = a_bzrdir.transport
 
153
        control_files._transport.mkdir_multi(dirs,
 
154
                mode=control_files._dir_mode)
298
155
        try:
299
 
            transport.mkdir_multi(['revision-store', 'weaves'],
300
 
                mode=a_bzrdir._get_dir_mode())
301
 
            transport.put_bytes_non_atomic('inventory.weave', empty_weave,
302
 
                mode=a_bzrdir._get_file_mode())
 
156
            for file, content in files:
 
157
                control_files.put(file, content)
303
158
        finally:
304
159
            control_files.unlock()
305
160
        return self.open(a_bzrdir, _found=True)
306
161
 
 
162
    def _get_control_store(self, repo_transport, control_files):
 
163
        """Return the control store for this repository."""
 
164
        return self._get_versioned_file_store('',
 
165
                                              repo_transport,
 
166
                                              control_files,
 
167
                                              prefixed=False)
 
168
 
 
169
    def _get_text_store(self, transport, control_files):
 
170
        """Get a store for file texts for this format."""
 
171
        raise NotImplementedError(self._get_text_store)
 
172
 
307
173
    def open(self, a_bzrdir, _found=False):
308
174
        """See RepositoryFormat.open()."""
309
175
        if not _found:
312
178
 
313
179
        repo_transport = a_bzrdir.get_repository_transport(None)
314
180
        control_files = a_bzrdir._control_files
315
 
        result = AllInOneRepository(_format=self, a_bzrdir=a_bzrdir)
316
 
        result.revisions = self._get_revisions(repo_transport, result)
317
 
        result.signatures = self._get_signatures(repo_transport, result)
318
 
        result.inventories = self._get_inventories(repo_transport, result)
319
 
        result.texts = self._get_texts(repo_transport, result)
320
 
        result.chk_bytes = None
321
 
        return result
 
181
        text_store = self._get_text_store(repo_transport, control_files)
 
182
        control_store = self._get_control_store(repo_transport, control_files)
 
183
        _revision_store = self._get_revision_store(repo_transport, control_files)
 
184
        return AllInOneRepository(_format=self,
 
185
                                  a_bzrdir=a_bzrdir,
 
186
                                  _revision_store=_revision_store,
 
187
                                  control_store=control_store,
 
188
                                  text_store=text_store)
 
189
 
 
190
    def check_conversion_target(self, target_format):
 
191
        pass
322
192
 
323
193
 
324
194
class RepositoryFormat4(PreSplitOutRepositoryFormat):
335
205
 
336
206
    _matchingbzrdir = bzrdir.BzrDirFormat4()
337
207
 
 
208
    def __init__(self):
 
209
        super(RepositoryFormat4, self).__init__()
 
210
 
338
211
    def get_format_description(self):
339
212
        """See RepositoryFormat.get_format_description()."""
340
213
        return "Repository format 4"
347
220
        """Format 4 is not supported.
348
221
 
349
222
        It is not supported because the model changed from 4 to 5 and the
350
 
        conversion logic is expensive - so doing it on the fly was not
 
223
        conversion logic is expensive - so doing it on the fly was not 
351
224
        feasible.
352
225
        """
353
226
        return False
354
227
 
355
 
    def _get_inventories(self, repo_transport, repo, name='inventory'):
356
 
        # No inventories store written so far.
 
228
    def _get_control_store(self, repo_transport, control_files):
 
229
        """Format 4 repositories have no formal control store at this point.
 
230
        
 
231
        This will cause any control-file-needing apis to fail - this is desired.
 
232
        """
357
233
        return None
358
 
 
359
 
    def _get_revisions(self, repo_transport, repo):
 
234
    
 
235
    def _get_revision_store(self, repo_transport, control_files):
 
236
        """See RepositoryFormat._get_revision_store()."""
360
237
        from bzrlib.xml4 import serializer_v4
361
 
        return RevisionTextStore(repo_transport.clone('revision-store'),
362
 
            serializer_v4, True, versionedfile.PrefixMapper(),
363
 
            repo.is_locked, repo.is_write_locked)
364
 
 
365
 
    def _get_signatures(self, repo_transport, repo):
366
 
        return SignatureTextStore(repo_transport.clone('revision-store'),
367
 
            False, versionedfile.PrefixMapper(),
368
 
            repo.is_locked, repo.is_write_locked)
369
 
 
370
 
    def _get_texts(self, repo_transport, repo):
371
 
        return None
 
238
        return self._get_text_rev_store(repo_transport,
 
239
                                        control_files,
 
240
                                        'revision-store',
 
241
                                        serializer=serializer_v4)
 
242
 
 
243
    def _get_text_store(self, transport, control_files):
 
244
        """See RepositoryFormat._get_text_store()."""
372
245
 
373
246
 
374
247
class RepositoryFormat5(PreSplitOutRepositoryFormat):
382
255
 
383
256
    _versionedfile_class = weave.WeaveFile
384
257
    _matchingbzrdir = bzrdir.BzrDirFormat5()
385
 
    @property
386
 
    def _serializer(self):
387
 
        return xml5.serializer_v5
 
258
 
 
259
    def __init__(self):
 
260
        super(RepositoryFormat5, self).__init__()
388
261
 
389
262
    def get_format_description(self):
390
263
        """See RepositoryFormat.get_format_description()."""
391
264
        return "Weave repository format 5"
392
265
 
393
 
    def network_name(self):
394
 
        """The network name for this format is the control dirs disk label."""
395
 
        return self._matchingbzrdir.get_format_string()
396
 
 
397
 
    def _get_inventories(self, repo_transport, repo, name='inventory'):
398
 
        mapper = versionedfile.ConstantMapper(name)
399
 
        return versionedfile.ThunkedVersionedFiles(repo_transport,
400
 
            weave.WeaveFile, mapper, repo.is_locked)
401
 
 
402
 
    def _get_revisions(self, repo_transport, repo):
403
 
        return RevisionTextStore(repo_transport.clone('revision-store'),
404
 
            xml5.serializer_v5, False, versionedfile.PrefixMapper(),
405
 
            repo.is_locked, repo.is_write_locked)
406
 
 
407
 
    def _get_signatures(self, repo_transport, repo):
408
 
        return SignatureTextStore(repo_transport.clone('revision-store'),
409
 
            False, versionedfile.PrefixMapper(),
410
 
            repo.is_locked, repo.is_write_locked)
411
 
 
412
 
    def _get_texts(self, repo_transport, repo):
413
 
        mapper = versionedfile.PrefixMapper()
414
 
        base_transport = repo_transport.clone('weaves')
415
 
        return versionedfile.ThunkedVersionedFiles(base_transport,
416
 
            weave.WeaveFile, mapper, repo.is_locked)
 
266
    def _get_revision_store(self, repo_transport, control_files):
 
267
        """See RepositoryFormat._get_revision_store()."""
 
268
        """Return the revision store object for this a_bzrdir."""
 
269
        return self._get_text_rev_store(repo_transport,
 
270
                                        control_files,
 
271
                                        'revision-store',
 
272
                                        compressed=False)
 
273
 
 
274
    def _get_text_store(self, transport, control_files):
 
275
        """See RepositoryFormat._get_text_store()."""
 
276
        return self._get_versioned_file_store('weaves', transport, control_files, prefixed=False)
417
277
 
418
278
 
419
279
class RepositoryFormat6(PreSplitOutRepositoryFormat):
427
287
 
428
288
    _versionedfile_class = weave.WeaveFile
429
289
    _matchingbzrdir = bzrdir.BzrDirFormat6()
430
 
    @property
431
 
    def _serializer(self):
432
 
        return xml5.serializer_v5
 
290
 
 
291
    def __init__(self):
 
292
        super(RepositoryFormat6, self).__init__()
433
293
 
434
294
    def get_format_description(self):
435
295
        """See RepositoryFormat.get_format_description()."""
436
296
        return "Weave repository format 6"
437
297
 
438
 
    def network_name(self):
439
 
        """The network name for this format is the control dirs disk label."""
440
 
        return self._matchingbzrdir.get_format_string()
441
 
 
442
 
    def _get_inventories(self, repo_transport, repo, name='inventory'):
443
 
        mapper = versionedfile.ConstantMapper(name)
444
 
        return versionedfile.ThunkedVersionedFiles(repo_transport,
445
 
            weave.WeaveFile, mapper, repo.is_locked)
446
 
 
447
 
    def _get_revisions(self, repo_transport, repo):
448
 
        return RevisionTextStore(repo_transport.clone('revision-store'),
449
 
            xml5.serializer_v5, False, versionedfile.HashPrefixMapper(),
450
 
            repo.is_locked, repo.is_write_locked)
451
 
 
452
 
    def _get_signatures(self, repo_transport, repo):
453
 
        return SignatureTextStore(repo_transport.clone('revision-store'),
454
 
            False, versionedfile.HashPrefixMapper(),
455
 
            repo.is_locked, repo.is_write_locked)
456
 
 
457
 
    def _get_texts(self, repo_transport, repo):
458
 
        mapper = versionedfile.HashPrefixMapper()
459
 
        base_transport = repo_transport.clone('weaves')
460
 
        return versionedfile.ThunkedVersionedFiles(base_transport,
461
 
            weave.WeaveFile, mapper, repo.is_locked)
 
298
    def _get_revision_store(self, repo_transport, control_files):
 
299
        """See RepositoryFormat._get_revision_store()."""
 
300
        return self._get_text_rev_store(repo_transport,
 
301
                                        control_files,
 
302
                                        'revision-store',
 
303
                                        compressed=False,
 
304
                                        prefixed=True)
 
305
 
 
306
    def _get_text_store(self, transport, control_files):
 
307
        """See RepositoryFormat._get_text_store()."""
 
308
        return self._get_versioned_file_store('weaves', transport, control_files)
462
309
 
463
310
 
464
311
class RepositoryFormat7(MetaDirRepositoryFormat):
474
321
    """
475
322
 
476
323
    _versionedfile_class = weave.WeaveFile
477
 
    supports_ghosts = False
478
 
    supports_chks = False
479
324
 
480
 
    _fetch_order = 'topological'
481
 
    _fetch_reconcile = True
482
 
    fast_deltas = False
483
 
    @property
484
 
    def _serializer(self):
485
 
        return xml5.serializer_v5
 
325
    def _get_control_store(self, repo_transport, control_files):
 
326
        """Return the control store for this repository."""
 
327
        return self._get_versioned_file_store('',
 
328
                                              repo_transport,
 
329
                                              control_files,
 
330
                                              prefixed=False)
486
331
 
487
332
    def get_format_string(self):
488
333
        """See RepositoryFormat.get_format_string()."""
492
337
        """See RepositoryFormat.get_format_description()."""
493
338
        return "Weave repository format 7"
494
339
 
495
 
    def _get_inventories(self, repo_transport, repo, name='inventory'):
496
 
        mapper = versionedfile.ConstantMapper(name)
497
 
        return versionedfile.ThunkedVersionedFiles(repo_transport,
498
 
            weave.WeaveFile, mapper, repo.is_locked)
499
 
 
500
 
    def _get_revisions(self, repo_transport, repo):
501
 
        return RevisionTextStore(repo_transport.clone('revision-store'),
502
 
            xml5.serializer_v5, True, versionedfile.HashPrefixMapper(),
503
 
            repo.is_locked, repo.is_write_locked)
504
 
 
505
 
    def _get_signatures(self, repo_transport, repo):
506
 
        return SignatureTextStore(repo_transport.clone('revision-store'),
507
 
            True, versionedfile.HashPrefixMapper(),
508
 
            repo.is_locked, repo.is_write_locked)
509
 
 
510
 
    def _get_texts(self, repo_transport, repo):
511
 
        mapper = versionedfile.HashPrefixMapper()
512
 
        base_transport = repo_transport.clone('weaves')
513
 
        return versionedfile.ThunkedVersionedFiles(base_transport,
514
 
            weave.WeaveFile, mapper, repo.is_locked)
 
340
    def check_conversion_target(self, target_format):
 
341
        pass
 
342
 
 
343
    def _get_revision_store(self, repo_transport, control_files):
 
344
        """See RepositoryFormat._get_revision_store()."""
 
345
        return self._get_text_rev_store(repo_transport,
 
346
                                        control_files,
 
347
                                        'revision-store',
 
348
                                        compressed=False,
 
349
                                        prefixed=True,
 
350
                                        )
 
351
 
 
352
    def _get_text_store(self, transport, control_files):
 
353
        """See RepositoryFormat._get_text_store()."""
 
354
        return self._get_versioned_file_store('weaves',
 
355
                                              transport,
 
356
                                              control_files)
515
357
 
516
358
    def initialize(self, a_bzrdir, shared=False):
517
359
        """Create a weave repository.
526
368
 
527
369
        mutter('creating repository in %s.', a_bzrdir.transport.base)
528
370
        dirs = ['revision-store', 'weaves']
529
 
        files = [('inventory.weave', StringIO(empty_weave)),
 
371
        files = [('inventory.weave', StringIO(empty_weave)), 
530
372
                 ]
531
373
        utf8_files = [('format', self.get_format_string())]
532
 
 
 
374
 
533
375
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
534
376
        return self.open(a_bzrdir=a_bzrdir, _found=True)
535
377
 
536
378
    def open(self, a_bzrdir, _found=False, _override_transport=None):
537
379
        """See RepositoryFormat.open().
538
 
 
 
380
        
539
381
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
540
382
                                    repository at a slightly different url
541
383
                                    than normal. I.e. during 'upgrade'.
542
384
        """
543
385
        if not _found:
544
386
            format = RepositoryFormat.find_format(a_bzrdir)
 
387
            assert format.__class__ ==  self.__class__
545
388
        if _override_transport is not None:
546
389
            repo_transport = _override_transport
547
390
        else:
548
391
            repo_transport = a_bzrdir.get_repository_transport(None)
549
392
        control_files = lockable_files.LockableFiles(repo_transport,
550
393
                                'lock', lockdir.LockDir)
551
 
        result = WeaveMetaDirRepository(_format=self, a_bzrdir=a_bzrdir,
552
 
            control_files=control_files)
553
 
        result.revisions = self._get_revisions(repo_transport, result)
554
 
        result.signatures = self._get_signatures(repo_transport, result)
555
 
        result.inventories = self._get_inventories(repo_transport, result)
556
 
        result.texts = self._get_texts(repo_transport, result)
557
 
        result.chk_bytes = None
558
 
        result._transport = repo_transport
559
 
        return result
560
 
 
561
 
 
562
 
class TextVersionedFiles(VersionedFiles):
563
 
    """Just-a-bunch-of-files based VersionedFile stores."""
564
 
 
565
 
    def __init__(self, transport, compressed, mapper, is_locked, can_write):
566
 
        self._compressed = compressed
567
 
        self._transport = transport
568
 
        self._mapper = mapper
569
 
        if self._compressed:
570
 
            self._ext = '.gz'
571
 
        else:
572
 
            self._ext = ''
573
 
        self._is_locked = is_locked
574
 
        self._can_write = can_write
575
 
 
576
 
    def add_lines(self, key, parents, lines):
577
 
        """Add a revision to the store."""
578
 
        if not self._is_locked():
579
 
            raise errors.ObjectNotLocked(self)
580
 
        if not self._can_write():
581
 
            raise errors.ReadOnlyError(self)
582
 
        if '/' in key[-1]:
583
 
            raise ValueError('bad idea to put / in %r' % (key,))
584
 
        text = ''.join(lines)
585
 
        if self._compressed:
586
 
            text = bytes_to_gzip(text)
587
 
        path = self._map(key)
588
 
        self._transport.put_bytes_non_atomic(path, text, create_parent_dir=True)
589
 
 
590
 
    def insert_record_stream(self, stream):
591
 
        adapters = {}
592
 
        for record in stream:
593
 
            # Raise an error when a record is missing.
594
 
            if record.storage_kind == 'absent':
595
 
                raise errors.RevisionNotPresent([record.key[0]], self)
596
 
            # adapt to non-tuple interface
597
 
            if record.storage_kind == 'fulltext':
598
 
                self.add_lines(record.key, None,
599
 
                    osutils.split_lines(record.get_bytes_as('fulltext')))
600
 
            else:
601
 
                adapter_key = record.storage_kind, 'fulltext'
602
 
                try:
603
 
                    adapter = adapters[adapter_key]
604
 
                except KeyError:
605
 
                    adapter_factory = adapter_registry.get(adapter_key)
606
 
                    adapter = adapter_factory(self)
607
 
                    adapters[adapter_key] = adapter
608
 
                lines = osutils.split_lines(adapter.get_bytes(
609
 
                    record, record.get_bytes_as(record.storage_kind)))
610
 
                try:
611
 
                    self.add_lines(record.key, None, lines)
612
 
                except RevisionAlreadyPresent:
613
 
                    pass
614
 
 
615
 
    def _load_text(self, key):
616
 
        if not self._is_locked():
617
 
            raise errors.ObjectNotLocked(self)
618
 
        path = self._map(key)
619
 
        try:
620
 
            text = self._transport.get_bytes(path)
621
 
            compressed = self._compressed
622
 
        except errors.NoSuchFile:
623
 
            if self._compressed:
624
 
                # try without the .gz
625
 
                path = path[:-3]
626
 
                try:
627
 
                    text = self._transport.get_bytes(path)
628
 
                    compressed = False
629
 
                except errors.NoSuchFile:
630
 
                    return None
631
 
            else:
632
 
                return None
633
 
        if compressed:
634
 
            text = GzipFile(mode='rb', fileobj=StringIO(text)).read()
635
 
        return text
636
 
 
637
 
    def _map(self, key):
638
 
        return self._mapper.map(key) + self._ext
639
 
 
640
 
 
641
 
class RevisionTextStore(TextVersionedFiles):
642
 
    """Legacy thunk for format 4 repositories."""
643
 
 
644
 
    def __init__(self, transport, serializer, compressed, mapper, is_locked,
645
 
        can_write):
646
 
        """Create a RevisionTextStore at transport with serializer."""
647
 
        TextVersionedFiles.__init__(self, transport, compressed, mapper,
648
 
            is_locked, can_write)
649
 
        self._serializer = serializer
650
 
 
651
 
    def _load_text_parents(self, key):
652
 
        text = self._load_text(key)
653
 
        if text is None:
654
 
            return None, None
655
 
        parents = self._serializer.read_revision_from_string(text).parent_ids
656
 
        return text, tuple((parent,) for parent in parents)
657
 
 
658
 
    def get_parent_map(self, keys):
659
 
        result = {}
660
 
        for key in keys:
661
 
            parents = self._load_text_parents(key)[1]
662
 
            if parents is None:
663
 
                continue
664
 
            result[key] = parents
665
 
        return result
666
 
 
667
 
    def get_known_graph_ancestry(self, keys):
668
 
        """Get a KnownGraph instance with the ancestry of keys."""
669
 
        keys = self.keys()
670
 
        parent_map = self.get_parent_map(keys)
671
 
        kg = _mod_graph.KnownGraph(parent_map)
672
 
        return kg
673
 
 
674
 
    def get_record_stream(self, keys, sort_order, include_delta_closure):
675
 
        for key in keys:
676
 
            text, parents = self._load_text_parents(key)
677
 
            if text is None:
678
 
                yield AbsentContentFactory(key)
679
 
            else:
680
 
                yield FulltextContentFactory(key, parents, None, text)
681
 
 
682
 
    def keys(self):
683
 
        if not self._is_locked():
684
 
            raise errors.ObjectNotLocked(self)
685
 
        relpaths = set()
686
 
        for quoted_relpath in self._transport.iter_files_recursive():
687
 
            relpath = urllib.unquote(quoted_relpath)
688
 
            path, ext = os.path.splitext(relpath)
689
 
            if ext == '.gz':
690
 
                relpath = path
691
 
            if not relpath.endswith('.sig'):
692
 
                relpaths.add(relpath)
693
 
        paths = list(relpaths)
694
 
        return set([self._mapper.unmap(path) for path in paths])
695
 
 
696
 
 
697
 
class SignatureTextStore(TextVersionedFiles):
698
 
    """Legacy thunk for format 4-7 repositories."""
699
 
 
700
 
    def __init__(self, transport, compressed, mapper, is_locked, can_write):
701
 
        TextVersionedFiles.__init__(self, transport, compressed, mapper,
702
 
            is_locked, can_write)
703
 
        self._ext = '.sig' + self._ext
704
 
 
705
 
    def get_parent_map(self, keys):
706
 
        result = {}
707
 
        for key in keys:
708
 
            text = self._load_text(key)
709
 
            if text is None:
710
 
                continue
711
 
            result[key] = None
712
 
        return result
713
 
 
714
 
    def get_record_stream(self, keys, sort_order, include_delta_closure):
715
 
        for key in keys:
716
 
            text = self._load_text(key)
717
 
            if text is None:
718
 
                yield AbsentContentFactory(key)
719
 
            else:
720
 
                yield FulltextContentFactory(key, None, None, text)
721
 
 
722
 
    def keys(self):
723
 
        if not self._is_locked():
724
 
            raise errors.ObjectNotLocked(self)
725
 
        relpaths = set()
726
 
        for quoted_relpath in self._transport.iter_files_recursive():
727
 
            relpath = urllib.unquote(quoted_relpath)
728
 
            path, ext = os.path.splitext(relpath)
729
 
            if ext == '.gz':
730
 
                relpath = path
731
 
            if not relpath.endswith('.sig'):
732
 
                continue
733
 
            relpaths.add(relpath[:-4])
734
 
        paths = list(relpaths)
735
 
        return set([self._mapper.unmap(path) for path in paths])
 
394
        text_store = self._get_text_store(repo_transport, control_files)
 
395
        control_store = self._get_control_store(repo_transport, control_files)
 
396
        _revision_store = self._get_revision_store(repo_transport, control_files)
 
397
        return WeaveMetaDirRepository(_format=self,
 
398
            a_bzrdir=a_bzrdir,
 
399
            control_files=control_files,
 
400
            _revision_store=_revision_store,
 
401
            control_store=control_store,
 
402
            text_store=text_store)
 
403
 
736
404
 
737
405
_legacy_formats = [RepositoryFormat4(),
738
406
                   RepositoryFormat5(),