/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
4415.2.2 by Martin Pool
Remove one use of DummyProgress
1
# Copyright (C) 2005, 2006, 2009 Canonical Ltd
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
2
#
1185.82.3 by John Arbash Meinel
Working on creating a factor for serializing changesets.
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
7
#
1185.82.3 by John Arbash Meinel
Working on creating a factor for serializing changesets.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
12
#
1185.82.3 by John Arbash Meinel
Working on creating a factor for serializing changesets.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1185.82.3 by John Arbash Meinel
Working on creating a factor for serializing changesets.
16
1185.82.130 by Aaron Bentley
Rename changesets to revision bundles
17
"""Serializer factory for reading and writing bundles.
1185.82.3 by John Arbash Meinel
Working on creating a factor for serializing changesets.
18
"""
19
6379.6.7 by Jelmer Vernooij
Move importing from future until after doc string, otherwise the doc string will disappear.
20
from __future__ import absolute_import
21
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
22
from breezy import (
4415.2.2 by Martin Pool
Remove one use of DummyProgress
23
    errors,
24
    ui,
25
    )
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
26
from breezy.bundle.serializer import (BundleSerializer,
2520.4.14 by Aaron Bentley
Get most tests passing, use format header
27
                                      _get_bundle_header,
1185.82.130 by Aaron Bentley
Rename changesets to revision bundles
28
                                     )
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
29
from breezy.bundle.serializer import binary_diff
30
from breezy.bundle.bundle_data import (RevisionInfo, BundleInfo)
31
from breezy.diff import internal_diff
32
from breezy.revision import NULL_REVISION
33
from breezy.testament import StrictTestament
34
from breezy.timestamp import (
1551.12.28 by Aaron Bentley
Move bundle timestamp code to timestamp
35
    format_highres_date,
6379.6.3 by Jelmer Vernooij
Use absolute_import.
36
    )
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
37
from breezy.textfile import text_file
38
from breezy.trace import mutter
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
39
1185.82.65 by Aaron Bentley
Factored out boolean text stuff
40
bool_text = {True: 'yes', False: 'no'}
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
41
1185.82.102 by Aaron Bentley
Start abstracting action line writing
42
43
class Action(object):
44
    """Represent an action"""
45
46
    def __init__(self, name, parameters=None, properties=None):
47
        self.name = name
48
        if parameters is None:
49
            self.parameters = []
50
        else:
51
            self.parameters = parameters
52
        if properties is None:
53
            self.properties = []
54
        else:
55
            self.properties = properties
56
2294.1.10 by John Arbash Meinel
Switch all apis over to utf8 file ids. All tests pass
57
    def add_utf8_property(self, name, value):
58
        """Add a property whose value is currently utf8 to the action."""
59
        self.properties.append((name, value.decode('utf8')))
60
1185.82.103 by Aaron Bentley
Serialize all actions through the action object
61
    def add_property(self, name, value):
62
        """Add a property to the action"""
63
        self.properties.append((name, value))
64
65
    def add_bool_property(self, name, value):
66
        """Add a boolean property to the action"""
67
        self.add_property(name, bool_text[value])
68
1185.82.102 by Aaron Bentley
Start abstracting action line writing
69
    def write(self, to_file):
70
        """Write action as to a file"""
1185.82.103 by Aaron Bentley
Serialize all actions through the action object
71
        p_texts = [' '.join([self.name]+self.parameters)]
72
        for prop in self.properties:
73
            if len(prop) == 1:
74
                p_texts.append(prop[0])
75
            else:
76
                try:
77
                    p_texts.append('%s:%s' % prop)
78
                except:
79
                    raise repr(prop)
1185.82.102 by Aaron Bentley
Start abstracting action line writing
80
        text = ['=== ']
81
        text.append(' // '.join(p_texts))
1185.82.106 by Aaron Bentley
Use elipsis to continue long meta lines
82
        text_line = ''.join(text).encode('utf-8')
83
        available = 79
84
        while len(text_line) > available:
85
            to_file.write(text_line[:available])
86
            text_line = text_line[available:]
87
            to_file.write('\n... ')
88
            available = 79 - len('... ')
89
        to_file.write(text_line+'\n')
1185.82.102 by Aaron Bentley
Start abstracting action line writing
90
91
1551.7.3 by Aaron Bentley
Fix strict testaments, as_sha1
92
class BundleSerializerV08(BundleSerializer):
1185.82.3 by John Arbash Meinel
Working on creating a factor for serializing changesets.
93
    def read(self, f):
1185.82.130 by Aaron Bentley
Rename changesets to revision bundles
94
        """Read the rest of the bundles from the supplied file.
1185.82.3 by John Arbash Meinel
Working on creating a factor for serializing changesets.
95
96
        :param f: The file to read from
1185.82.130 by Aaron Bentley
Rename changesets to revision bundles
97
        :return: A list of bundles
1185.82.3 by John Arbash Meinel
Working on creating a factor for serializing changesets.
98
        """
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
99
        return BundleReader(f).info
1185.82.3 by John Arbash Meinel
Working on creating a factor for serializing changesets.
100
1910.2.50 by Aaron Bentley
start work on format 0.9 serializer
101
    def check_compatible(self):
1910.2.63 by Aaron Bentley
Add supports_rich_root member to repository
102
        if self.source.supports_rich_root():
2067.3.1 by Martin Pool
Clean up BzrNewError, other exception classes and users.
103
            raise errors.IncompatibleBundleFormat('0.8', repr(self.source))
1910.2.50 by Aaron Bentley
start work on format 0.9 serializer
104
1185.82.74 by Aaron Bentley
Allow custom base for any revision
105
    def write(self, source, revision_ids, forced_bases, f):
1185.82.130 by Aaron Bentley
Rename changesets to revision bundles
106
        """Write the bundless to the supplied files.
1185.82.3 by John Arbash Meinel
Working on creating a factor for serializing changesets.
107
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
108
        :param source: A source for revision information
109
        :param revision_ids: The list of revision ids to serialize
1185.82.74 by Aaron Bentley
Allow custom base for any revision
110
        :param forced_bases: A dict of revision -> base that overrides default
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
111
        :param f: The file to output to
1185.82.3 by John Arbash Meinel
Working on creating a factor for serializing changesets.
112
        """
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
113
        self.source = source
114
        self.revision_ids = revision_ids
1185.82.74 by Aaron Bentley
Allow custom base for any revision
115
        self.forced_bases = forced_bases
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
116
        self.to_file = f
1910.2.50 by Aaron Bentley
start work on format 0.9 serializer
117
        self.check_compatible()
6754.8.4 by Jelmer Vernooij
Use new context stuff.
118
        with source.lock_read():
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
119
            self._write_main_header()
4415.2.2 by Martin Pool
Remove one use of DummyProgress
120
            pb = ui.ui_factory.nested_progress_bar()
1185.82.92 by Aaron Bentley
Add progress bar for changeset generation
121
            try:
122
                self._write_revisions(pb)
123
            finally:
4415.2.2 by Martin Pool
Remove one use of DummyProgress
124
                pb.finished()
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
125
2520.4.53 by Aaron Bentley
refactor bundle serialization to make write_bundle primary
126
    def write_bundle(self, repository, target, base, fileobj):
127
        return self._write_bundle(repository, target, base, fileobj)
128
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
129
    def _write_main_header(self):
130
        """Write the header for the changes"""
131
        f = self.to_file
2520.4.15 by Aaron Bentley
Fix _get_bundle invocations
132
        f.write(_get_bundle_header('0.8'))
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
133
        f.write('#\n')
134
2447.1.3 by John Arbash Meinel
Change the default serializer to include a trailing whitespace for empty properties.
135
    def _write(self, key, value, indent=1, trailing_space_when_empty=False):
136
        """Write out meta information, with proper indenting, etc.
137
138
        :param trailing_space_when_empty: To work around a bug in earlier
139
            bundle readers, when writing an empty property, we use "prop: \n"
140
            rather than writing "prop:\n".
141
            If this parameter is True, and value is the empty string, we will
142
            write an extra space.
143
        """
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
144
        if indent < 1:
145
            raise ValueError('indentation must be greater than 0')
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
146
        f = self.to_file
147
        f.write('#' + (' ' * indent))
148
        f.write(key.encode('utf-8'))
149
        if not value:
2447.1.3 by John Arbash Meinel
Change the default serializer to include a trailing whitespace for empty properties.
150
            if trailing_space_when_empty and value == '':
151
                f.write(': \n')
152
            else:
153
                f.write(':\n')
2294.1.10 by John Arbash Meinel
Switch all apis over to utf8 file ids. All tests pass
154
        elif isinstance(value, str):
155
            f.write(': ')
156
            f.write(value)
157
            f.write('\n')
158
        elif isinstance(value, unicode):
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
159
            f.write(': ')
160
            f.write(value.encode('utf-8'))
161
            f.write('\n')
162
        else:
163
            f.write(':\n')
1185.82.28 by Aaron Bentley
Got parent_id handling working
164
            for entry in value:
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
165
                f.write('#' + (' ' * (indent+2)))
2294.1.10 by John Arbash Meinel
Switch all apis over to utf8 file ids. All tests pass
166
                if isinstance(entry, str):
167
                    f.write(entry)
168
                else:
169
                    f.write(entry.encode('utf-8'))
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
170
                f.write('\n')
171
1185.82.92 by Aaron Bentley
Add progress bar for changeset generation
172
    def _write_revisions(self, pb):
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
173
        """Write the information for all of the revisions."""
174
1927.1.3 by John Arbash Meinel
Get 10% savings by just fixing a bug
175
        # Optimize for the case of revisions in order
176
        last_rev_id = None
177
        last_rev_tree = None
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
178
2294.1.10 by John Arbash Meinel
Switch all apis over to utf8 file ids. All tests pass
179
        i_max = len(self.revision_ids)
1927.1.3 by John Arbash Meinel
Get 10% savings by just fixing a bug
180
        for i, rev_id in enumerate(self.revision_ids):
4415.2.1 by Martin Pool
Fix typo in progress message
181
            pb.update("Generating revision data", i, i_max)
1927.1.3 by John Arbash Meinel
Get 10% savings by just fixing a bug
182
            rev = self.source.get_revision(rev_id)
183
            if rev_id == last_rev_id:
184
                rev_tree = last_rev_tree
185
            else:
186
                rev_tree = self.source.revision_tree(rev_id)
187
            if rev_id in self.forced_bases:
1185.82.74 by Aaron Bentley
Allow custom base for any revision
188
                explicit_base = True
1927.1.3 by John Arbash Meinel
Get 10% savings by just fixing a bug
189
                base_id = self.forced_bases[rev_id]
1185.82.74 by Aaron Bentley
Allow custom base for any revision
190
                if base_id is None:
191
                    base_id = NULL_REVISION
1185.82.72 by Aaron Bentley
Always use leftmost base for changesets
192
            else:
1185.82.74 by Aaron Bentley
Allow custom base for any revision
193
                explicit_base = False
194
                if rev.parent_ids:
195
                    base_id = rev.parent_ids[-1]
196
                else:
197
                    base_id = NULL_REVISION
1927.1.3 by John Arbash Meinel
Get 10% savings by just fixing a bug
198
199
            if base_id == last_rev_id:
200
                base_tree = last_rev_tree
201
            else:
202
                base_tree = self.source.revision_tree(base_id)
1185.84.3 by Aaron Bentley
Hide diffs for old revisions in bundles
203
            force_binary = (i != 0)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
204
            self._write_revision(rev, rev_tree, base_id, base_tree,
1185.84.3 by Aaron Bentley
Hide diffs for old revisions in bundles
205
                                 explicit_base, force_binary)
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
206
1927.1.3 by John Arbash Meinel
Get 10% savings by just fixing a bug
207
            last_rev_id = base_id
208
            last_rev_tree = base_tree
209
1910.2.55 by Aaron Bentley
Bundle 0.9 uses Testament 3 strict
210
    def _testament_sha1(self, revision_id):
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
211
        return StrictTestament.from_revision(self.source,
1910.2.55 by Aaron Bentley
Bundle 0.9 uses Testament 3 strict
212
                                             revision_id).as_sha1()
213
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
214
    def _write_revision(self, rev, rev_tree, base_rev, base_tree,
1185.84.3 by Aaron Bentley
Hide diffs for old revisions in bundles
215
                        explicit_base, force_binary):
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
216
        """Write out the information for a revision."""
217
        def w(key, value):
218
            self._write(key, value, indent=1)
219
1185.82.79 by Aaron Bentley
Move message to top, revision-id to footer
220
        w('message', rev.message.split('\n'))
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
221
        w('committer', rev.committer)
222
        w('date', format_highres_date(rev.timestamp, rev.timezone))
223
        self.to_file.write('\n')
224
1185.84.3 by Aaron Bentley
Hide diffs for old revisions in bundles
225
        self._write_delta(rev_tree, base_tree, rev.revision_id, force_binary)
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
226
1185.82.79 by Aaron Bentley
Move message to top, revision-id to footer
227
        w('revision id', rev.revision_id)
1910.2.55 by Aaron Bentley
Bundle 0.9 uses Testament 3 strict
228
        w('sha1', self._testament_sha1(rev.revision_id))
1185.82.29 by Aaron Bentley
Got merge test working
229
        w('inventory sha1', rev.inventory_sha1)
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
230
        if rev.parent_ids:
1185.82.28 by Aaron Bentley
Got parent_id handling working
231
            w('parent ids', rev.parent_ids)
1185.82.74 by Aaron Bentley
Allow custom base for any revision
232
        if explicit_base:
233
            w('base id', base_rev)
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
234
        if rev.properties:
235
            self._write('properties', None, indent=1)
2447.1.1 by John Arbash Meinel
For stability and ease of testing, write properties in sorted order.
236
            for name, value in sorted(rev.properties.items()):
2447.1.3 by John Arbash Meinel
Change the default serializer to include a trailing whitespace for empty properties.
237
                self._write(name, value, indent=3,
238
                            trailing_space_when_empty=True)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
239
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
240
        # Add an extra blank space at the end
241
        self.to_file.write('\n')
242
1185.82.101 by Aaron Bentley
Start using a standard action writer
243
    def _write_action(self, name, parameters, properties=None):
244
        if properties is None:
245
            properties = []
246
        p_texts = ['%s:%s' % v for v in properties]
247
        self.to_file.write('=== ')
248
        self.to_file.write(' '.join([name]+parameters).encode('utf-8'))
249
        self.to_file.write(' // '.join(p_texts).encode('utf-8'))
250
        self.to_file.write('\n')
251
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
252
    def _write_delta(self, new_tree, old_tree, default_revision_id,
1185.84.3 by Aaron Bentley
Hide diffs for old revisions in bundles
253
                     force_binary):
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
254
        """Write out the changes between the trees."""
255
        DEVNULL = '/dev/null'
256
        old_label = ''
257
        new_label = ''
258
1185.84.3 by Aaron Bentley
Hide diffs for old revisions in bundles
259
        def do_diff(file_id, old_path, new_path, action, force_binary):
1185.82.96 by Aaron Bentley
Got first binary test passing
260
            def tree_lines(tree, require_text=False):
5967.7.1 by Martin Pool
Deprecate __contains__ on Tree and Inventory
261
                if tree.has_id(file_id):
1185.82.96 by Aaron Bentley
Got first binary test passing
262
                    tree_file = tree.get_file(file_id)
263
                    if require_text is True:
264
                        tree_file = text_file(tree_file)
265
                    return tree_file.readlines()
266
                else:
267
                    return []
268
269
            try:
1185.84.3 by Aaron Bentley
Hide diffs for old revisions in bundles
270
                if force_binary:
271
                    raise errors.BinaryFile()
1185.82.96 by Aaron Bentley
Got first binary test passing
272
                old_lines = tree_lines(old_tree, require_text=True)
273
                new_lines = tree_lines(new_tree, require_text=True)
1185.82.103 by Aaron Bentley
Serialize all actions through the action object
274
                action.write(self.to_file)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
275
                internal_diff(old_path, old_lines, new_path, new_lines,
1185.82.96 by Aaron Bentley
Got first binary test passing
276
                              self.to_file)
277
            except errors.BinaryFile:
278
                old_lines = tree_lines(old_tree, require_text=False)
279
                new_lines = tree_lines(new_tree, require_text=False)
1185.82.103 by Aaron Bentley
Serialize all actions through the action object
280
                action.add_property('encoding', 'base64')
281
                action.write(self.to_file)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
282
                binary_diff(old_path, old_lines, new_path, new_lines,
1185.82.96 by Aaron Bentley
Got first binary test passing
283
                            self.to_file)
284
1185.82.104 by Aaron Bentley
Refactored action writing
285
        def finish_action(action, file_id, kind, meta_modified, text_modified,
286
                          old_path, new_path):
6471.1.6 by Jelmer Vernooij
Revert bundle serializer code.
287
            entry = new_tree.root_inventory[file_id]
288
            if entry.revision != default_revision_id:
289
                action.add_utf8_property('last-changed', entry.revision)
1185.82.104 by Aaron Bentley
Refactored action writing
290
            if meta_modified:
6471.1.6 by Jelmer Vernooij
Revert bundle serializer code.
291
                action.add_bool_property('executable', entry.executable)
1185.82.104 by Aaron Bentley
Refactored action writing
292
            if text_modified and kind == "symlink":
6471.1.6 by Jelmer Vernooij
Revert bundle serializer code.
293
                action.add_property('target', entry.symlink_target)
1185.82.104 by Aaron Bentley
Refactored action writing
294
            if text_modified and kind == "file":
1185.84.3 by Aaron Bentley
Hide diffs for old revisions in bundles
295
                do_diff(file_id, old_path, new_path, action, force_binary)
1185.82.104 by Aaron Bentley
Refactored action writing
296
            else:
297
                action.write(self.to_file)
298
1910.2.64 by Aaron Bentley
Changes from review
299
        delta = new_tree.changes_from(old_tree, want_unchanged=True,
1731.1.33 by Aaron Bentley
Revert no-special-root changes
300
                                      include_root=True)
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
301
        for path, file_id, kind in delta.removed:
1185.82.103 by Aaron Bentley
Serialize all actions through the action object
302
            action = Action('removed', [kind, path]).write(self.to_file)
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
303
304
        for path, file_id, kind in delta.added:
1731.1.55 by Aaron Bentley
Fix bundle handling
305
            action = Action('added', [kind, path], [('file-id', file_id)])
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
306
            meta_modified = (kind=='file' and
1185.82.119 by Aaron Bentley
Default execute bit to no for new files, directories, symlinks
307
                             new_tree.is_executable(file_id))
308
            finish_action(action, file_id, kind, meta_modified, True,
1185.82.104 by Aaron Bentley
Refactored action writing
309
                          DEVNULL, path)
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
310
311
        for (old_path, new_path, file_id, kind,
312
             text_modified, meta_modified) in delta.renamed:
1731.1.55 by Aaron Bentley
Fix bundle handling
313
            action = Action('renamed', [kind, old_path], [(new_path,)])
1185.82.104 by Aaron Bentley
Refactored action writing
314
            finish_action(action, file_id, kind, meta_modified, text_modified,
315
                          old_path, new_path)
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
316
317
        for (path, file_id, kind,
318
             text_modified, meta_modified) in delta.modified:
1731.1.55 by Aaron Bentley
Fix bundle handling
319
            action = Action('modified', [kind, path])
1185.82.104 by Aaron Bentley
Refactored action writing
320
            finish_action(action, file_id, kind, meta_modified, text_modified,
321
                          path, path)
1185.82.117 by Aaron Bentley
Handle last-modified changes on their own
322
323
        for path, file_id, kind in delta.unchanged:
6405.2.10 by Jelmer Vernooij
Fix more tests.
324
            new_rev = new_tree.get_file_revision(file_id)
1185.82.117 by Aaron Bentley
Handle last-modified changes on their own
325
            if new_rev is None:
326
                continue
6405.2.10 by Jelmer Vernooij
Fix more tests.
327
            old_rev = old_tree.get_file_revision(file_id)
1185.82.117 by Aaron Bentley
Handle last-modified changes on their own
328
            if new_rev != old_rev:
6405.2.10 by Jelmer Vernooij
Fix more tests.
329
                action = Action('modified', [new_tree.kind(file_id),
330
                                             new_tree.id2path(file_id)])
331
                action.add_utf8_property('last-changed', new_rev)
1185.82.117 by Aaron Bentley
Handle last-modified changes on their own
332
                action.write(self.to_file)
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
333
334
335
class BundleReader(object):
336
    """This class reads in a bundle from a file, and returns
337
    a Bundle object, which can then be applied against a tree.
338
    """
339
    def __init__(self, from_file):
340
        """Read in the bundle from the file.
341
342
        :param from_file: A file-like object (must have iterator support).
343
        """
344
        object.__init__(self)
345
        self.from_file = iter(from_file)
346
        self._next_line = None
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
347
1910.2.55 by Aaron Bentley
Bundle 0.9 uses Testament 3 strict
348
        self.info = self._get_info()
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
349
        # We put the actual inventory ids in the footer, so that the patch
350
        # is easier to read for humans.
351
        # Unfortunately, that means we need to read everything before we
352
        # can create a proper bundle.
353
        self._read()
354
        self._validate()
355
1910.2.55 by Aaron Bentley
Bundle 0.9 uses Testament 3 strict
356
    def _get_info(self):
357
        return BundleInfo08()
358
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
359
    def _read(self):
6634.2.1 by Martin
Apply 2to3 next fixer and make compatible
360
        next(self._next())
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
361
        while self._next_line is not None:
1793.3.4 by John Arbash Meinel
[merge] bzr.dev 1804 and fix conflicts.
362
            if not self._read_revision_header():
363
                break
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
364
            if self._next_line is None:
365
                break
366
            self._read_patches()
367
            self._read_footer()
368
369
    def _validate(self):
370
        """Make sure that the information read in makes sense
371
        and passes appropriate checksums.
372
        """
373
        # Fill in all the missing blanks for the revisions
374
        # and generate the real_revisions list.
375
        self.info.complete_info()
376
377
    def _next(self):
378
        """yield the next line, but secretly
379
        keep 1 extra line for peeking.
380
        """
381
        for line in self.from_file:
382
            last = self._next_line
383
            self._next_line = line
384
            if last is not None:
385
                #mutter('yielding line: %r' % last)
386
                yield last
387
        last = self._next_line
388
        self._next_line = None
389
        #mutter('yielding line: %r' % last)
390
        yield last
391
392
    def _read_revision_header(self):
1793.3.4 by John Arbash Meinel
[merge] bzr.dev 1804 and fix conflicts.
393
        found_something = False
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
394
        self.info.revisions.append(RevisionInfo(None))
395
        for line in self._next():
396
            # The bzr header is terminated with a blank line
397
            # which does not start with '#'
398
            if line is None or line == '\n':
399
                break
1793.3.16 by John Arbash Meinel
Add tests to ensure that we gracefully handle opening and trailing non-bundle text.
400
            if not line.startswith('#'):
401
                continue
1793.3.4 by John Arbash Meinel
[merge] bzr.dev 1804 and fix conflicts.
402
            found_something = True
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
403
            self._handle_next(line)
1793.3.4 by John Arbash Meinel
[merge] bzr.dev 1804 and fix conflicts.
404
        if not found_something:
405
            # Nothing was there, so remove the added revision
406
            self.info.revisions.pop()
407
        return found_something
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
408
409
    def _read_next_entry(self, line, indent=1):
410
        """Read in a key-value pair
411
        """
412
        if not line.startswith('#'):
1793.3.15 by John Arbash Meinel
Raise the right errors
413
            raise errors.MalformedHeader('Bzr header did not start with #')
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
414
        line = line[1:-1].decode('utf-8') # Remove the '#' and '\n'
415
        if line[:indent] == ' '*indent:
416
            line = line[indent:]
417
        if not line:
418
            return None, None# Ignore blank lines
419
420
        loc = line.find(': ')
421
        if loc != -1:
422
            key = line[:loc]
423
            value = line[loc+2:]
424
            if not value:
425
                value = self._read_many(indent=indent+2)
426
        elif line[-1:] == ':':
427
            key = line[:-1]
428
            value = self._read_many(indent=indent+2)
429
        else:
1793.3.15 by John Arbash Meinel
Raise the right errors
430
            raise errors.MalformedHeader('While looking for key: value pairs,'
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
431
                    ' did not find the colon %r' % (line))
432
433
        key = key.replace(' ', '_')
434
        #mutter('found %s: %s' % (key, value))
435
        return key, value
436
437
    def _handle_next(self, line):
438
        if line is None:
439
            return
440
        key, value = self._read_next_entry(line, indent=1)
441
        mutter('_handle_next %r => %r' % (key, value))
442
        if key is None:
443
            return
444
445
        revision_info = self.info.revisions[-1]
1963.2.4 by Robey Pointer
remove usage of hasattr
446
        if key in revision_info.__dict__:
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
447
            if getattr(revision_info, key) is None:
2294.1.10 by John Arbash Meinel
Switch all apis over to utf8 file ids. All tests pass
448
                if key in ('file_id', 'revision_id', 'base_id'):
449
                    value = value.encode('utf8')
450
                elif key in ('parent_ids'):
451
                    value = [v.encode('utf8') for v in value]
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
452
                setattr(revision_info, key, value)
453
            else:
1793.3.15 by John Arbash Meinel
Raise the right errors
454
                raise errors.MalformedHeader('Duplicated Key: %s' % key)
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
455
        else:
456
            # What do we do with a key we don't recognize
1793.3.15 by John Arbash Meinel
Raise the right errors
457
            raise errors.MalformedHeader('Unknown Key: "%s"' % key)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
458
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
459
    def _read_many(self, indent):
460
        """If a line ends with no entry, that means that it should be
461
        followed with multiple lines of values.
462
463
        This detects the end of the list, because it will be a line that
464
        does not start properly indented.
465
        """
466
        values = []
467
        start = '#' + (' '*indent)
468
469
        if self._next_line is None or self._next_line[:len(start)] != start:
470
            return values
471
472
        for line in self._next():
473
            values.append(line[len(start):-1].decode('utf-8'))
474
            if self._next_line is None or self._next_line[:len(start)] != start:
475
                break
476
        return values
477
478
    def _read_one_patch(self):
479
        """Read in one patch, return the complete patch, along with
480
        the next line.
481
482
        :return: action, lines, do_continue
483
        """
484
        #mutter('_read_one_patch: %r' % self._next_line)
485
        # Peek and see if there are no patches
486
        if self._next_line is None or self._next_line.startswith('#'):
487
            return None, [], False
488
489
        first = True
490
        lines = []
491
        for line in self._next():
492
            if first:
493
                if not line.startswith('==='):
1793.3.15 by John Arbash Meinel
Raise the right errors
494
                    raise errors.MalformedPatches('The first line of all patches'
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
495
                        ' should be a bzr meta line "==="'
496
                        ': %r' % line)
497
                action = line[4:-1].decode('utf-8')
498
            elif line.startswith('... '):
499
                action += line[len('... '):-1].decode('utf-8')
500
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
501
            if (self._next_line is not None and
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
502
                self._next_line.startswith('===')):
503
                return action, lines, True
504
            elif self._next_line is None or self._next_line.startswith('#'):
505
                return action, lines, False
506
507
            if first:
508
                first = False
509
            elif not line.startswith('... '):
510
                lines.append(line)
511
512
        return action, lines, False
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
513
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
514
    def _read_patches(self):
515
        do_continue = True
516
        revision_actions = []
517
        while do_continue:
518
            action, lines, do_continue = self._read_one_patch()
519
            if action is not None:
520
                revision_actions.append((action, lines))
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
521
        if self.info.revisions[-1].tree_actions is not None:
522
            raise AssertionError()
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
523
        self.info.revisions[-1].tree_actions = revision_actions
524
525
    def _read_footer(self):
526
        """Read the rest of the meta information.
527
528
        :param first_line:  The previous step iterates past what it
529
                            can handle. That extra line is given here.
530
        """
531
        for line in self._next():
532
            self._handle_next(line)
1793.3.14 by John Arbash Meinel
Actually fix the bug with missing trailing newline bug #49182
533
            if self._next_line is None:
534
                break
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
535
            if not self._next_line.startswith('#'):
1793.3.14 by John Arbash Meinel
Actually fix the bug with missing trailing newline bug #49182
536
                # Consume the trailing \n and stop processing
6634.2.1 by Martin
Apply 2to3 next fixer and make compatible
537
                next(self._next())
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
538
                break
1910.2.1 by Aaron Bentley
Ensure root entry always has a revision
539
540
class BundleInfo08(BundleInfo):
1910.2.55 by Aaron Bentley
Bundle 0.9 uses Testament 3 strict
541
1910.2.1 by Aaron Bentley
Ensure root entry always has a revision
542
    def _update_tree(self, bundle_tree, revision_id):
543
        bundle_tree.note_last_changed('', revision_id)
544
        BundleInfo._update_tree(self, bundle_tree, revision_id)
1910.2.55 by Aaron Bentley
Bundle 0.9 uses Testament 3 strict
545
546
    def _testament_sha1_from_revision(self, repository, revision_id):
547
        testament = StrictTestament.from_revision(repository, revision_id)
548
        return testament.as_sha1()
549
5798.1.2 by Jelmer Vernooij
Fix some tests.
550
    def _testament_sha1(self, revision, tree):
551
        return StrictTestament(revision, tree).as_sha1()