~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/bundle/serializer/v08.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2011-05-04 12:10:51 UTC
  • mfrom: (5819.1.4 777007-developer-doc)
  • Revision ID: pqm@pqm.ubuntu.com-20110504121051-aovlsmqiivjmc4fc
(jelmer) Small fixes to developer documentation. (Jonathan Riddell)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2009 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
"""Serializer factory for reading and writing bundles.
18
18
"""
19
19
 
20
20
import os
21
21
 
22
 
from bzrlib import errors
 
22
from bzrlib import (
 
23
    errors,
 
24
    ui,
 
25
    )
23
26
from bzrlib.bundle.serializer import (BundleSerializer,
24
 
                                      BUNDLE_HEADER,
25
 
                                      format_highres_date,
26
 
                                      unpack_highres_date,
 
27
                                      _get_bundle_header,
27
28
                                     )
28
29
from bzrlib.bundle.serializer import binary_diff
29
30
from bzrlib.bundle.bundle_data import (RevisionInfo, BundleInfo, BundleTree)
30
31
from bzrlib.diff import internal_diff
31
32
from bzrlib.osutils import pathjoin
32
 
from bzrlib.progress import DummyProgress
33
33
from bzrlib.revision import NULL_REVISION
34
 
from bzrlib.rio import RioWriter, read_stanzas
35
 
import bzrlib.ui
36
34
from bzrlib.testament import StrictTestament
 
35
from bzrlib.timestamp import (
 
36
    format_highres_date,
 
37
    unpack_highres_date,
 
38
)
37
39
from bzrlib.textfile import text_file
38
40
from bzrlib.trace import mutter
39
41
 
54
56
        else:
55
57
            self.properties = properties
56
58
 
 
59
    def add_utf8_property(self, name, value):
 
60
        """Add a property whose value is currently utf8 to the action."""
 
61
        self.properties.append((name, value.decode('utf8')))
 
62
 
57
63
    def add_property(self, name, value):
58
64
        """Add a property to the action"""
59
65
        self.properties.append((name, value))
114
120
        source.lock_read()
115
121
        try:
116
122
            self._write_main_header()
117
 
            pb = DummyProgress()
 
123
            pb = ui.ui_factory.nested_progress_bar()
118
124
            try:
119
125
                self._write_revisions(pb)
120
126
            finally:
121
 
                pass
122
 
                #pb.finished()
 
127
                pb.finished()
123
128
        finally:
124
129
            source.unlock()
125
130
 
 
131
    def write_bundle(self, repository, target, base, fileobj):
 
132
        return self._write_bundle(repository, target, base, fileobj)
 
133
 
126
134
    def _write_main_header(self):
127
135
        """Write the header for the changes"""
128
136
        f = self.to_file
129
 
        f.write(BUNDLE_HEADER)
130
 
        f.write('0.8\n')
 
137
        f.write(_get_bundle_header('0.8'))
131
138
        f.write('#\n')
132
139
 
133
 
    def _write(self, key, value, indent=1):
134
 
        """Write out meta information, with proper indenting, etc"""
135
 
        assert indent > 0, 'indentation must be greater than 0'
 
140
    def _write(self, key, value, indent=1, trailing_space_when_empty=False):
 
141
        """Write out meta information, with proper indenting, etc.
 
142
 
 
143
        :param trailing_space_when_empty: To work around a bug in earlier
 
144
            bundle readers, when writing an empty property, we use "prop: \n"
 
145
            rather than writing "prop:\n".
 
146
            If this parameter is True, and value is the empty string, we will
 
147
            write an extra space.
 
148
        """
 
149
        if indent < 1:
 
150
            raise ValueError('indentation must be greater than 0')
136
151
        f = self.to_file
137
152
        f.write('#' + (' ' * indent))
138
153
        f.write(key.encode('utf-8'))
139
154
        if not value:
140
 
            f.write(':\n')
141
 
        elif isinstance(value, basestring):
 
155
            if trailing_space_when_empty and value == '':
 
156
                f.write(': \n')
 
157
            else:
 
158
                f.write(':\n')
 
159
        elif isinstance(value, str):
 
160
            f.write(': ')
 
161
            f.write(value)
 
162
            f.write('\n')
 
163
        elif isinstance(value, unicode):
142
164
            f.write(': ')
143
165
            f.write(value.encode('utf-8'))
144
166
            f.write('\n')
146
168
            f.write(':\n')
147
169
            for entry in value:
148
170
                f.write('#' + (' ' * (indent+2)))
149
 
                f.write(entry.encode('utf-8'))
 
171
                if isinstance(entry, str):
 
172
                    f.write(entry)
 
173
                else:
 
174
                    f.write(entry.encode('utf-8'))
150
175
                f.write('\n')
151
176
 
152
177
    def _write_revisions(self, pb):
156
181
        last_rev_id = None
157
182
        last_rev_tree = None
158
183
 
159
 
        i_max = len(self.revision_ids) 
 
184
        i_max = len(self.revision_ids)
160
185
        for i, rev_id in enumerate(self.revision_ids):
161
 
            pb.update("Generating revsion data", i, i_max)
 
186
            pb.update("Generating revision data", i, i_max)
162
187
            rev = self.source.get_revision(rev_id)
163
188
            if rev_id == last_rev_id:
164
189
                rev_tree = last_rev_tree
181
206
            else:
182
207
                base_tree = self.source.revision_tree(base_id)
183
208
            force_binary = (i != 0)
184
 
            self._write_revision(rev, rev_tree, base_id, base_tree, 
 
209
            self._write_revision(rev, rev_tree, base_id, base_tree,
185
210
                                 explicit_base, force_binary)
186
211
 
187
212
            last_rev_id = base_id
188
213
            last_rev_tree = base_tree
189
214
 
190
215
    def _testament_sha1(self, revision_id):
191
 
        return StrictTestament.from_revision(self.source, 
 
216
        return StrictTestament.from_revision(self.source,
192
217
                                             revision_id).as_sha1()
193
218
 
194
 
    def _write_revision(self, rev, rev_tree, base_rev, base_tree, 
 
219
    def _write_revision(self, rev, rev_tree, base_rev, base_tree,
195
220
                        explicit_base, force_binary):
196
221
        """Write out the information for a revision."""
197
222
        def w(key, value):
213
238
            w('base id', base_rev)
214
239
        if rev.properties:
215
240
            self._write('properties', None, indent=1)
216
 
            for name, value in rev.properties.items():
217
 
                self._write(name, value, indent=3)
218
 
        
 
241
            for name, value in sorted(rev.properties.items()):
 
242
                self._write(name, value, indent=3,
 
243
                            trailing_space_when_empty=True)
 
244
 
219
245
        # Add an extra blank space at the end
220
246
        self.to_file.write('\n')
221
247
 
228
254
        self.to_file.write(' // '.join(p_texts).encode('utf-8'))
229
255
        self.to_file.write('\n')
230
256
 
231
 
    def _write_delta(self, new_tree, old_tree, default_revision_id, 
 
257
    def _write_delta(self, new_tree, old_tree, default_revision_id,
232
258
                     force_binary):
233
259
        """Write out the changes between the trees."""
234
260
        DEVNULL = '/dev/null'
251
277
                old_lines = tree_lines(old_tree, require_text=True)
252
278
                new_lines = tree_lines(new_tree, require_text=True)
253
279
                action.write(self.to_file)
254
 
                internal_diff(old_path, old_lines, new_path, new_lines, 
 
280
                internal_diff(old_path, old_lines, new_path, new_lines,
255
281
                              self.to_file)
256
282
            except errors.BinaryFile:
257
283
                old_lines = tree_lines(old_tree, require_text=False)
258
284
                new_lines = tree_lines(new_tree, require_text=False)
259
285
                action.add_property('encoding', 'base64')
260
286
                action.write(self.to_file)
261
 
                binary_diff(old_path, old_lines, new_path, new_lines, 
 
287
                binary_diff(old_path, old_lines, new_path, new_lines,
262
288
                            self.to_file)
263
289
 
264
290
        def finish_action(action, file_id, kind, meta_modified, text_modified,
265
291
                          old_path, new_path):
266
292
            entry = new_tree.inventory[file_id]
267
293
            if entry.revision != default_revision_id:
268
 
                action.add_property('last-changed', entry.revision)
 
294
                action.add_utf8_property('last-changed', entry.revision)
269
295
            if meta_modified:
270
296
                action.add_bool_property('executable', entry.executable)
271
297
            if text_modified and kind == "symlink":
282
308
 
283
309
        for path, file_id, kind in delta.added:
284
310
            action = Action('added', [kind, path], [('file-id', file_id)])
285
 
            meta_modified = (kind=='file' and 
 
311
            meta_modified = (kind=='file' and
286
312
                             new_tree.is_executable(file_id))
287
313
            finish_action(action, file_id, kind, meta_modified, True,
288
314
                          DEVNULL, path)
306
332
                continue
307
333
            old_rev = getattr(old_tree.inventory[ie.file_id], 'revision', None)
308
334
            if new_rev != old_rev:
309
 
                action = Action('modified', [ie.kind, 
 
335
                action = Action('modified', [ie.kind,
310
336
                                             new_tree.id2path(ie.file_id)])
311
 
                action.add_property('last-changed', ie.revision)
 
337
                action.add_utf8_property('last-changed', ie.revision)
312
338
                action.write(self.to_file)
313
339
 
314
340
 
324
350
        object.__init__(self)
325
351
        self.from_file = iter(from_file)
326
352
        self._next_line = None
327
 
        
 
353
 
328
354
        self.info = self._get_info()
329
355
        # We put the actual inventory ids in the footer, so that the patch
330
356
        # is easier to read for humans.
425
451
        revision_info = self.info.revisions[-1]
426
452
        if key in revision_info.__dict__:
427
453
            if getattr(revision_info, key) is None:
 
454
                if key in ('file_id', 'revision_id', 'base_id'):
 
455
                    value = value.encode('utf8')
 
456
                elif key in ('parent_ids'):
 
457
                    value = [v.encode('utf8') for v in value]
428
458
                setattr(revision_info, key, value)
429
459
            else:
430
460
                raise errors.MalformedHeader('Duplicated Key: %s' % key)
431
461
        else:
432
462
            # What do we do with a key we don't recognize
433
463
            raise errors.MalformedHeader('Unknown Key: "%s"' % key)
434
 
    
 
464
 
435
465
    def _read_many(self, indent):
436
466
        """If a line ends with no entry, that means that it should be
437
467
        followed with multiple lines of values.
474
504
            elif line.startswith('... '):
475
505
                action += line[len('... '):-1].decode('utf-8')
476
506
 
477
 
            if (self._next_line is not None and 
 
507
            if (self._next_line is not None and
478
508
                self._next_line.startswith('===')):
479
509
                return action, lines, True
480
510
            elif self._next_line is None or self._next_line.startswith('#'):
486
516
                lines.append(line)
487
517
 
488
518
        return action, lines, False
489
 
            
 
519
 
490
520
    def _read_patches(self):
491
521
        do_continue = True
492
522
        revision_actions = []
494
524
            action, lines, do_continue = self._read_one_patch()
495
525
            if action is not None:
496
526
                revision_actions.append((action, lines))
497
 
        assert self.info.revisions[-1].tree_actions is None
 
527
        if self.info.revisions[-1].tree_actions is not None:
 
528
            raise AssertionError()
498
529
        self.info.revisions[-1].tree_actions = revision_actions
499
530
 
500
531
    def _read_footer(self):
512
543
                self._next().next()
513
544
                break
514
545
 
515
 
 
516
546
class BundleInfo08(BundleInfo):
517
547
 
518
548
    def _update_tree(self, bundle_tree, revision_id):
523
553
        testament = StrictTestament.from_revision(repository, revision_id)
524
554
        return testament.as_sha1()
525
555
 
526
 
    def _testament_sha1(self, revision, inventory):
527
 
        return StrictTestament(revision, inventory).as_sha1()
 
556
    def _testament_sha1(self, revision, tree):
 
557
        return StrictTestament(revision, tree).as_sha1()