/brz/remove-bazaar

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

« back to all changes in this revision

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

  • Committer: Robert Collins
  • Date: 2010-05-06 23:41:35 UTC
  • mto: This revision was merged to the branch mainline in revision 5223.
  • Revision ID: robertc@robertcollins.net-20100506234135-yivbzczw1sejxnxc
Lock methods on ``Tree``, ``Branch`` and ``Repository`` are now
expected to return an object which can be used to unlock them. This reduces
duplicate code when using cleanups. The previous 'tokens's returned by
``Branch.lock_write`` and ``Repository.lock_write`` are now attributes
on the result of the lock_write. ``repository.RepositoryWriteLockResult``
and ``branch.BranchWriteLockResult`` document this. (Robert Collins)

``log._get_info_for_log_files`` now takes an add_cleanup callable.
(Robert Collins)

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
"""Serializer factory for reading and writing bundles.
18
18
"""
19
19
 
20
 
from __future__ import absolute_import
 
20
import os
21
21
 
22
 
from .... import (
 
22
from bzrlib import (
23
23
    errors,
24
24
    ui,
25
25
    )
26
 
from . import (
27
 
    BundleSerializer,
28
 
    _get_bundle_header,
29
 
    binary_diff,
30
 
    )
31
 
from ..bundle_data import (
32
 
    RevisionInfo,
33
 
    BundleInfo,
34
 
    )
35
 
from ....diff import internal_diff
36
 
from ....revision import NULL_REVISION
37
 
from ....sixish import text_type
38
 
from ...testament import StrictTestament
39
 
from ....timestamp import (
 
26
from bzrlib.bundle.serializer import (BundleSerializer,
 
27
                                      _get_bundle_header,
 
28
                                     )
 
29
from bzrlib.bundle.serializer import binary_diff
 
30
from bzrlib.bundle.bundle_data import (RevisionInfo, BundleInfo, BundleTree)
 
31
from bzrlib.diff import internal_diff
 
32
from bzrlib.osutils import pathjoin
 
33
from bzrlib.revision import NULL_REVISION
 
34
from bzrlib.testament import StrictTestament
 
35
from bzrlib.timestamp import (
40
36
    format_highres_date,
41
 
    )
42
 
from ....textfile import text_file
43
 
from ....trace import mutter
 
37
    unpack_highres_date,
 
38
)
 
39
from bzrlib.textfile import text_file
 
40
from bzrlib.trace import mutter
44
41
 
45
42
bool_text = {True: 'yes', False: 'no'}
46
43
 
73
70
 
74
71
    def write(self, to_file):
75
72
        """Write action as to a file"""
76
 
        p_texts = [' '.join([self.name] + self.parameters)]
 
73
        p_texts = [' '.join([self.name]+self.parameters)]
77
74
        for prop in self.properties:
78
75
            if len(prop) == 1:
79
76
                p_texts.append(prop[0])
80
77
            else:
81
 
                p_texts.append('%s:%s' % prop)
 
78
                try:
 
79
                    p_texts.append('%s:%s' % prop)
 
80
                except:
 
81
                    raise repr(prop)
82
82
        text = ['=== ']
83
83
        text.append(' // '.join(p_texts))
84
84
        text_line = ''.join(text).encode('utf-8')
86
86
        while len(text_line) > available:
87
87
            to_file.write(text_line[:available])
88
88
            text_line = text_line[available:]
89
 
            to_file.write(b'\n... ')
90
 
            available = 79 - len(b'... ')
91
 
        to_file.write(text_line + b'\n')
 
89
            to_file.write('\n... ')
 
90
            available = 79 - len('... ')
 
91
        to_file.write(text_line+'\n')
92
92
 
93
93
 
94
94
class BundleSerializerV08(BundleSerializer):
95
 
 
96
95
    def read(self, f):
97
96
        """Read the rest of the bundles from the supplied file.
98
97
 
118
117
        self.forced_bases = forced_bases
119
118
        self.to_file = f
120
119
        self.check_compatible()
121
 
        with source.lock_read():
 
120
        source.lock_read()
 
121
        try:
122
122
            self._write_main_header()
123
 
            with ui.ui_factory.nested_progress_bar() as pb:
 
123
            pb = ui.ui_factory.nested_progress_bar()
 
124
            try:
124
125
                self._write_revisions(pb)
 
126
            finally:
 
127
                pb.finished()
 
128
        finally:
 
129
            source.unlock()
125
130
 
126
 
    def write_bundle(self, repository, revision_id, base_revision_id, out):
127
 
        """Helper function for translating write_bundle to write"""
128
 
        forced_bases = {revision_id: base_revision_id}
129
 
        if base_revision_id is NULL_REVISION:
130
 
            base_revision_id = None
131
 
        graph = repository.get_graph()
132
 
        revision_ids = graph.find_unique_ancestors(revision_id,
133
 
                                                   [base_revision_id])
134
 
        revision_ids = list(repository.get_graph().iter_topo_order(
135
 
            revision_ids))
136
 
        revision_ids.reverse()
137
 
        self.write(repository, revision_ids, forced_bases, out)
138
 
        return revision_ids
 
131
    def write_bundle(self, repository, target, base, fileobj):
 
132
        return self._write_bundle(repository, target, base, fileobj)
139
133
 
140
134
    def _write_main_header(self):
141
135
        """Write the header for the changes"""
142
136
        f = self.to_file
143
137
        f.write(_get_bundle_header('0.8'))
144
 
        f.write(b'#\n')
 
138
        f.write('#\n')
145
139
 
146
140
    def _write(self, key, value, indent=1, trailing_space_when_empty=False):
147
141
        """Write out meta information, with proper indenting, etc.
155
149
        if indent < 1:
156
150
            raise ValueError('indentation must be greater than 0')
157
151
        f = self.to_file
158
 
        f.write(b'#' + (b' ' * indent))
 
152
        f.write('#' + (' ' * indent))
159
153
        f.write(key.encode('utf-8'))
160
154
        if not value:
161
155
            if trailing_space_when_empty and value == '':
162
 
                f.write(b': \n')
 
156
                f.write(': \n')
163
157
            else:
164
 
                f.write(b':\n')
165
 
        elif isinstance(value, bytes):
166
 
            f.write(b': ')
 
158
                f.write(':\n')
 
159
        elif isinstance(value, str):
 
160
            f.write(': ')
167
161
            f.write(value)
168
 
            f.write(b'\n')
169
 
        elif isinstance(value, text_type):
170
 
            f.write(b': ')
 
162
            f.write('\n')
 
163
        elif isinstance(value, unicode):
 
164
            f.write(': ')
171
165
            f.write(value.encode('utf-8'))
172
 
            f.write(b'\n')
 
166
            f.write('\n')
173
167
        else:
174
 
            f.write(b':\n')
 
168
            f.write(':\n')
175
169
            for entry in value:
176
 
                f.write(b'#' + (b' ' * (indent + 2)))
177
 
                if isinstance(entry, bytes):
 
170
                f.write('#' + (' ' * (indent+2)))
 
171
                if isinstance(entry, str):
178
172
                    f.write(entry)
179
173
                else:
180
174
                    f.write(entry.encode('utf-8'))
181
 
                f.write(b'\n')
 
175
                f.write('\n')
182
176
 
183
177
    def _write_revisions(self, pb):
184
178
        """Write the information for all of the revisions."""
231
225
        w('message', rev.message.split('\n'))
232
226
        w('committer', rev.committer)
233
227
        w('date', format_highres_date(rev.timestamp, rev.timezone))
234
 
        self.to_file.write(b'\n')
 
228
        self.to_file.write('\n')
235
229
 
236
230
        self._write_delta(rev_tree, base_tree, rev.revision_id, force_binary)
237
231
 
249
243
                            trailing_space_when_empty=True)
250
244
 
251
245
        # Add an extra blank space at the end
252
 
        self.to_file.write(b'\n')
 
246
        self.to_file.write('\n')
253
247
 
254
248
    def _write_action(self, name, parameters, properties=None):
255
249
        if properties is None:
256
250
            properties = []
257
251
        p_texts = ['%s:%s' % v for v in properties]
258
 
        self.to_file.write(b'=== ')
259
 
        self.to_file.write(' '.join([name] + parameters).encode('utf-8'))
 
252
        self.to_file.write('=== ')
 
253
        self.to_file.write(' '.join([name]+parameters).encode('utf-8'))
260
254
        self.to_file.write(' // '.join(p_texts).encode('utf-8'))
261
 
        self.to_file.write(b'\n')
 
255
        self.to_file.write('\n')
262
256
 
263
257
    def _write_delta(self, new_tree, old_tree, default_revision_id,
264
258
                     force_binary):
268
262
        new_label = ''
269
263
 
270
264
        def do_diff(file_id, old_path, new_path, action, force_binary):
271
 
            def tree_lines(tree, path, require_text=False):
272
 
                try:
273
 
                    tree_file = tree.get_file(path)
274
 
                except errors.NoSuchFile:
275
 
                    return []
276
 
                else:
 
265
            def tree_lines(tree, require_text=False):
 
266
                if file_id in tree:
 
267
                    tree_file = tree.get_file(file_id)
277
268
                    if require_text is True:
278
269
                        tree_file = text_file(tree_file)
279
270
                    return tree_file.readlines()
 
271
                else:
 
272
                    return []
280
273
 
281
274
            try:
282
275
                if force_binary:
283
276
                    raise errors.BinaryFile()
284
 
                old_lines = tree_lines(old_tree, old_path, require_text=True)
285
 
                new_lines = tree_lines(new_tree, new_path, require_text=True)
 
277
                old_lines = tree_lines(old_tree, require_text=True)
 
278
                new_lines = tree_lines(new_tree, require_text=True)
286
279
                action.write(self.to_file)
287
280
                internal_diff(old_path, old_lines, new_path, new_lines,
288
281
                              self.to_file)
289
282
            except errors.BinaryFile:
290
 
                old_lines = tree_lines(old_tree, old_path, require_text=False)
291
 
                new_lines = tree_lines(new_tree, new_path, require_text=False)
 
283
                old_lines = tree_lines(old_tree, require_text=False)
 
284
                new_lines = tree_lines(new_tree, require_text=False)
292
285
                action.add_property('encoding', 'base64')
293
286
                action.write(self.to_file)
294
287
                binary_diff(old_path, old_lines, new_path, new_lines,
296
289
 
297
290
        def finish_action(action, file_id, kind, meta_modified, text_modified,
298
291
                          old_path, new_path):
299
 
            entry = new_tree.root_inventory.get_entry(file_id)
 
292
            entry = new_tree.inventory[file_id]
300
293
            if entry.revision != default_revision_id:
301
294
                action.add_utf8_property('last-changed', entry.revision)
302
295
            if meta_modified:
310
303
 
311
304
        delta = new_tree.changes_from(old_tree, want_unchanged=True,
312
305
                                      include_root=True)
313
 
        for change in delta.removed:
314
 
            action = Action('removed', [change.kind[0], change.path[0]]).write(self.to_file)
315
 
 
316
 
        # TODO(jelmer): Treat copied specially here?
317
 
        for change in delta.added + delta.copied:
318
 
            action = Action(
319
 
                'added', [change.kind[1], change.path[1]],
320
 
                [('file-id', change.file_id.decode('utf-8'))])
321
 
            meta_modified = (change.kind[1] == 'file' and
322
 
                             change.executable[1])
323
 
            finish_action(action, change.file_id, change.kind[1], meta_modified, change.changed_content,
324
 
                          DEVNULL, change.path[1])
325
 
 
326
 
        for change in delta.renamed:
327
 
            action = Action('renamed', [change.kind[1], change.path[0]], [(change.path[1],)])
328
 
            finish_action(action, change.file_id, change.kind[1], change.meta_modified(), change.changed_content,
329
 
                          change.path[0], change.path[1])
330
 
 
331
 
        for change in delta.modified:
332
 
            action = Action('modified', [change.kind[1], change.path[1]])
333
 
            finish_action(action, change.file_id, change.kind[1], change.meta_modified(), change.changed_content,
334
 
                          change.path[0], change.path[1])
335
 
 
336
 
        for change in delta.unchanged:
337
 
            new_rev = new_tree.get_file_revision(change.path[1])
 
306
        for path, file_id, kind in delta.removed:
 
307
            action = Action('removed', [kind, path]).write(self.to_file)
 
308
 
 
309
        for path, file_id, kind in delta.added:
 
310
            action = Action('added', [kind, path], [('file-id', file_id)])
 
311
            meta_modified = (kind=='file' and
 
312
                             new_tree.is_executable(file_id))
 
313
            finish_action(action, file_id, kind, meta_modified, True,
 
314
                          DEVNULL, path)
 
315
 
 
316
        for (old_path, new_path, file_id, kind,
 
317
             text_modified, meta_modified) in delta.renamed:
 
318
            action = Action('renamed', [kind, old_path], [(new_path,)])
 
319
            finish_action(action, file_id, kind, meta_modified, text_modified,
 
320
                          old_path, new_path)
 
321
 
 
322
        for (path, file_id, kind,
 
323
             text_modified, meta_modified) in delta.modified:
 
324
            action = Action('modified', [kind, path])
 
325
            finish_action(action, file_id, kind, meta_modified, text_modified,
 
326
                          path, path)
 
327
 
 
328
        for path, file_id, kind in delta.unchanged:
 
329
            ie = new_tree.inventory[file_id]
 
330
            new_rev = getattr(ie, 'revision', None)
338
331
            if new_rev is None:
339
332
                continue
340
 
            old_rev = old_tree.get_file_revision(change.path[0])
 
333
            old_rev = getattr(old_tree.inventory[ie.file_id], 'revision', None)
341
334
            if new_rev != old_rev:
342
 
                action = Action('modified', [change.kind[1], change.path[1]])
343
 
                action.add_utf8_property('last-changed', new_rev)
 
335
                action = Action('modified', [ie.kind,
 
336
                                             new_tree.id2path(ie.file_id)])
 
337
                action.add_utf8_property('last-changed', ie.revision)
344
338
                action.write(self.to_file)
345
339
 
346
340
 
348
342
    """This class reads in a bundle from a file, and returns
349
343
    a Bundle object, which can then be applied against a tree.
350
344
    """
351
 
 
352
345
    def __init__(self, from_file):
353
346
        """Read in the bundle from the file.
354
347
 
370
363
        return BundleInfo08()
371
364
 
372
365
    def _read(self):
373
 
        next(self._next())
 
366
        self._next().next()
374
367
        while self._next_line is not None:
375
368
            if not self._read_revision_header():
376
369
                break
408
401
        for line in self._next():
409
402
            # The bzr header is terminated with a blank line
410
403
            # which does not start with '#'
411
 
            if line is None or line == b'\n':
 
404
            if line is None or line == '\n':
412
405
                break
413
 
            if not line.startswith(b'#'):
 
406
            if not line.startswith('#'):
414
407
                continue
415
408
            found_something = True
416
409
            self._handle_next(line)
422
415
    def _read_next_entry(self, line, indent=1):
423
416
        """Read in a key-value pair
424
417
        """
425
 
        if not line.startswith(b'#'):
 
418
        if not line.startswith('#'):
426
419
            raise errors.MalformedHeader('Bzr header did not start with #')
427
 
        line = line[1:-1].decode('utf-8')  # Remove the '#' and '\n'
428
 
        if line[:indent] == ' ' * indent:
 
420
        line = line[1:-1].decode('utf-8') # Remove the '#' and '\n'
 
421
        if line[:indent] == ' '*indent:
429
422
            line = line[indent:]
430
423
        if not line:
431
 
            return None, None  # Ignore blank lines
 
424
            return None, None# Ignore blank lines
432
425
 
433
426
        loc = line.find(': ')
434
427
        if loc != -1:
435
428
            key = line[:loc]
436
 
            value = line[loc + 2:]
 
429
            value = line[loc+2:]
437
430
            if not value:
438
 
                value = self._read_many(indent=indent + 2)
 
431
                value = self._read_many(indent=indent+2)
439
432
        elif line[-1:] == ':':
440
433
            key = line[:-1]
441
 
            value = self._read_many(indent=indent + 2)
 
434
            value = self._read_many(indent=indent+2)
442
435
        else:
443
436
            raise errors.MalformedHeader('While looking for key: value pairs,'
444
 
                                         ' did not find the colon %r' % (line))
 
437
                    ' did not find the colon %r' % (line))
445
438
 
446
439
        key = key.replace(' ', '_')
447
440
        #mutter('found %s: %s' % (key, value))
462
455
                    value = value.encode('utf8')
463
456
                elif key in ('parent_ids'):
464
457
                    value = [v.encode('utf8') for v in value]
465
 
                elif key in ('testament_sha1', 'inventory_sha1', 'sha1'):
466
 
                    value = value.encode('ascii')
467
458
                setattr(revision_info, key, value)
468
459
            else:
469
460
                raise errors.MalformedHeader('Duplicated Key: %s' % key)
479
470
        does not start properly indented.
480
471
        """
481
472
        values = []
482
 
        start = b'#' + (b' ' * indent)
 
473
        start = '#' + (' '*indent)
483
474
 
484
 
        if self._next_line is None or not self._next_line.startswith(start):
 
475
        if self._next_line is None or self._next_line[:len(start)] != start:
485
476
            return values
486
477
 
487
478
        for line in self._next():
488
479
            values.append(line[len(start):-1].decode('utf-8'))
489
 
            if self._next_line is None or not self._next_line.startswith(start):
 
480
            if self._next_line is None or self._next_line[:len(start)] != start:
490
481
                break
491
482
        return values
492
483
 
498
489
        """
499
490
        #mutter('_read_one_patch: %r' % self._next_line)
500
491
        # Peek and see if there are no patches
501
 
        if self._next_line is None or self._next_line.startswith(b'#'):
 
492
        if self._next_line is None or self._next_line.startswith('#'):
502
493
            return None, [], False
503
494
 
504
495
        first = True
505
496
        lines = []
506
497
        for line in self._next():
507
498
            if first:
508
 
                if not line.startswith(b'==='):
 
499
                if not line.startswith('==='):
509
500
                    raise errors.MalformedPatches('The first line of all patches'
510
 
                                                  ' should be a bzr meta line "==="'
511
 
                                                  ': %r' % line)
 
501
                        ' should be a bzr meta line "==="'
 
502
                        ': %r' % line)
512
503
                action = line[4:-1].decode('utf-8')
513
 
            elif line.startswith(b'... '):
514
 
                action += line[len(b'... '):-1].decode('utf-8')
 
504
            elif line.startswith('... '):
 
505
                action += line[len('... '):-1].decode('utf-8')
515
506
 
516
507
            if (self._next_line is not None and
517
 
                    self._next_line.startswith(b'===')):
 
508
                self._next_line.startswith('===')):
518
509
                return action, lines, True
519
 
            elif self._next_line is None or self._next_line.startswith(b'#'):
 
510
            elif self._next_line is None or self._next_line.startswith('#'):
520
511
                return action, lines, False
521
512
 
522
513
            if first:
523
514
                first = False
524
 
            elif not line.startswith(b'... '):
 
515
            elif not line.startswith('... '):
525
516
                lines.append(line)
526
517
 
527
518
        return action, lines, False
547
538
            self._handle_next(line)
548
539
            if self._next_line is None:
549
540
                break
550
 
            if not self._next_line.startswith(b'#'):
 
541
            if not self._next_line.startswith('#'):
551
542
                # Consume the trailing \n and stop processing
552
 
                next(self._next())
 
543
                self._next().next()
553
544
                break
554
545
 
555
 
 
556
546
class BundleInfo08(BundleInfo):
557
547
 
558
548
    def _update_tree(self, bundle_tree, revision_id):
563
553
        testament = StrictTestament.from_revision(repository, revision_id)
564
554
        return testament.as_sha1()
565
555
 
566
 
    def _testament(self, revision, tree):
567
 
        return StrictTestament(revision, tree)
 
556
    def _testament_sha1(self, revision, inventory):
 
557
        return StrictTestament(revision, inventory).as_sha1()