/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 breezy/bzr/bundle/serializer/v08.py

  • Committer: Breezy landing bot
  • Author(s): Colin Watson
  • Date: 2020-11-16 21:47:08 UTC
  • mfrom: (7521.1.1 remove-lp-workaround)
  • Revision ID: breezy.the.bot@gmail.com-20201116214708-jos209mgxi41oy15
Remove breezy.git workaround for bazaar.launchpad.net.

Merged from https://code.launchpad.net/~cjwatson/brz/remove-lp-workaround/+merge/393710

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