/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()
6861.4.1 by Jelmer Vernooij
Make progress bars context managers.
120
            with ui.ui_factory.nested_progress_bar() as pb:
1185.82.92 by Aaron Bentley
Add progress bar for changeset generation
121
                self._write_revisions(pb)
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
122
2520.4.53 by Aaron Bentley
refactor bundle serialization to make write_bundle primary
123
    def write_bundle(self, repository, target, base, fileobj):
124
        return self._write_bundle(repository, target, base, fileobj)
125
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
126
    def _write_main_header(self):
127
        """Write the header for the changes"""
128
        f = self.to_file
2520.4.15 by Aaron Bentley
Fix _get_bundle invocations
129
        f.write(_get_bundle_header('0.8'))
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
130
        f.write('#\n')
131
2447.1.3 by John Arbash Meinel
Change the default serializer to include a trailing whitespace for empty properties.
132
    def _write(self, key, value, indent=1, trailing_space_when_empty=False):
133
        """Write out meta information, with proper indenting, etc.
134
135
        :param trailing_space_when_empty: To work around a bug in earlier
136
            bundle readers, when writing an empty property, we use "prop: \n"
137
            rather than writing "prop:\n".
138
            If this parameter is True, and value is the empty string, we will
139
            write an extra space.
140
        """
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
141
        if indent < 1:
142
            raise ValueError('indentation must be greater than 0')
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
143
        f = self.to_file
144
        f.write('#' + (' ' * indent))
145
        f.write(key.encode('utf-8'))
146
        if not value:
2447.1.3 by John Arbash Meinel
Change the default serializer to include a trailing whitespace for empty properties.
147
            if trailing_space_when_empty and value == '':
148
                f.write(': \n')
149
            else:
150
                f.write(':\n')
2294.1.10 by John Arbash Meinel
Switch all apis over to utf8 file ids. All tests pass
151
        elif isinstance(value, str):
152
            f.write(': ')
153
            f.write(value)
154
            f.write('\n')
155
        elif isinstance(value, unicode):
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
156
            f.write(': ')
157
            f.write(value.encode('utf-8'))
158
            f.write('\n')
159
        else:
160
            f.write(':\n')
1185.82.28 by Aaron Bentley
Got parent_id handling working
161
            for entry in value:
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
162
                f.write('#' + (' ' * (indent+2)))
2294.1.10 by John Arbash Meinel
Switch all apis over to utf8 file ids. All tests pass
163
                if isinstance(entry, str):
164
                    f.write(entry)
165
                else:
166
                    f.write(entry.encode('utf-8'))
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
167
                f.write('\n')
168
1185.82.92 by Aaron Bentley
Add progress bar for changeset generation
169
    def _write_revisions(self, pb):
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
170
        """Write the information for all of the revisions."""
171
1927.1.3 by John Arbash Meinel
Get 10% savings by just fixing a bug
172
        # Optimize for the case of revisions in order
173
        last_rev_id = None
174
        last_rev_tree = None
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
175
2294.1.10 by John Arbash Meinel
Switch all apis over to utf8 file ids. All tests pass
176
        i_max = len(self.revision_ids)
1927.1.3 by John Arbash Meinel
Get 10% savings by just fixing a bug
177
        for i, rev_id in enumerate(self.revision_ids):
4415.2.1 by Martin Pool
Fix typo in progress message
178
            pb.update("Generating revision data", i, i_max)
1927.1.3 by John Arbash Meinel
Get 10% savings by just fixing a bug
179
            rev = self.source.get_revision(rev_id)
180
            if rev_id == last_rev_id:
181
                rev_tree = last_rev_tree
182
            else:
183
                rev_tree = self.source.revision_tree(rev_id)
184
            if rev_id in self.forced_bases:
1185.82.74 by Aaron Bentley
Allow custom base for any revision
185
                explicit_base = True
1927.1.3 by John Arbash Meinel
Get 10% savings by just fixing a bug
186
                base_id = self.forced_bases[rev_id]
1185.82.74 by Aaron Bentley
Allow custom base for any revision
187
                if base_id is None:
188
                    base_id = NULL_REVISION
1185.82.72 by Aaron Bentley
Always use leftmost base for changesets
189
            else:
1185.82.74 by Aaron Bentley
Allow custom base for any revision
190
                explicit_base = False
191
                if rev.parent_ids:
192
                    base_id = rev.parent_ids[-1]
193
                else:
194
                    base_id = NULL_REVISION
1927.1.3 by John Arbash Meinel
Get 10% savings by just fixing a bug
195
196
            if base_id == last_rev_id:
197
                base_tree = last_rev_tree
198
            else:
199
                base_tree = self.source.revision_tree(base_id)
1185.84.3 by Aaron Bentley
Hide diffs for old revisions in bundles
200
            force_binary = (i != 0)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
201
            self._write_revision(rev, rev_tree, base_id, base_tree,
1185.84.3 by Aaron Bentley
Hide diffs for old revisions in bundles
202
                                 explicit_base, force_binary)
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
203
1927.1.3 by John Arbash Meinel
Get 10% savings by just fixing a bug
204
            last_rev_id = base_id
205
            last_rev_tree = base_tree
206
1910.2.55 by Aaron Bentley
Bundle 0.9 uses Testament 3 strict
207
    def _testament_sha1(self, revision_id):
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
208
        return StrictTestament.from_revision(self.source,
1910.2.55 by Aaron Bentley
Bundle 0.9 uses Testament 3 strict
209
                                             revision_id).as_sha1()
210
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
211
    def _write_revision(self, rev, rev_tree, base_rev, base_tree,
1185.84.3 by Aaron Bentley
Hide diffs for old revisions in bundles
212
                        explicit_base, force_binary):
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
213
        """Write out the information for a revision."""
214
        def w(key, value):
215
            self._write(key, value, indent=1)
216
1185.82.79 by Aaron Bentley
Move message to top, revision-id to footer
217
        w('message', rev.message.split('\n'))
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
218
        w('committer', rev.committer)
219
        w('date', format_highres_date(rev.timestamp, rev.timezone))
220
        self.to_file.write('\n')
221
1185.84.3 by Aaron Bentley
Hide diffs for old revisions in bundles
222
        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
223
1185.82.79 by Aaron Bentley
Move message to top, revision-id to footer
224
        w('revision id', rev.revision_id)
1910.2.55 by Aaron Bentley
Bundle 0.9 uses Testament 3 strict
225
        w('sha1', self._testament_sha1(rev.revision_id))
1185.82.29 by Aaron Bentley
Got merge test working
226
        w('inventory sha1', rev.inventory_sha1)
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
227
        if rev.parent_ids:
1185.82.28 by Aaron Bentley
Got parent_id handling working
228
            w('parent ids', rev.parent_ids)
1185.82.74 by Aaron Bentley
Allow custom base for any revision
229
        if explicit_base:
230
            w('base id', base_rev)
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
231
        if rev.properties:
232
            self._write('properties', None, indent=1)
2447.1.1 by John Arbash Meinel
For stability and ease of testing, write properties in sorted order.
233
            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.
234
                self._write(name, value, indent=3,
235
                            trailing_space_when_empty=True)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
236
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
237
        # Add an extra blank space at the end
238
        self.to_file.write('\n')
239
1185.82.101 by Aaron Bentley
Start using a standard action writer
240
    def _write_action(self, name, parameters, properties=None):
241
        if properties is None:
242
            properties = []
243
        p_texts = ['%s:%s' % v for v in properties]
244
        self.to_file.write('=== ')
245
        self.to_file.write(' '.join([name]+parameters).encode('utf-8'))
246
        self.to_file.write(' // '.join(p_texts).encode('utf-8'))
247
        self.to_file.write('\n')
248
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
249
    def _write_delta(self, new_tree, old_tree, default_revision_id,
1185.84.3 by Aaron Bentley
Hide diffs for old revisions in bundles
250
                     force_binary):
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
251
        """Write out the changes between the trees."""
252
        DEVNULL = '/dev/null'
253
        old_label = ''
254
        new_label = ''
255
1185.84.3 by Aaron Bentley
Hide diffs for old revisions in bundles
256
        def do_diff(file_id, old_path, new_path, action, force_binary):
6809.4.7 by Jelmer Vernooij
Swap arguments for get_symlink_target and kind/stored_kind.
257
            def tree_lines(tree, path, require_text=False):
5967.7.1 by Martin Pool
Deprecate __contains__ on Tree and Inventory
258
                if tree.has_id(file_id):
6809.4.7 by Jelmer Vernooij
Swap arguments for get_symlink_target and kind/stored_kind.
259
                    tree_file = tree.get_file(path, file_id)
1185.82.96 by Aaron Bentley
Got first binary test passing
260
                    if require_text is True:
261
                        tree_file = text_file(tree_file)
262
                    return tree_file.readlines()
263
                else:
264
                    return []
265
266
            try:
1185.84.3 by Aaron Bentley
Hide diffs for old revisions in bundles
267
                if force_binary:
268
                    raise errors.BinaryFile()
6809.4.7 by Jelmer Vernooij
Swap arguments for get_symlink_target and kind/stored_kind.
269
                old_lines = tree_lines(old_tree, old_path, require_text=True)
270
                new_lines = tree_lines(new_tree, new_path, require_text=True)
1185.82.103 by Aaron Bentley
Serialize all actions through the action object
271
                action.write(self.to_file)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
272
                internal_diff(old_path, old_lines, new_path, new_lines,
1185.82.96 by Aaron Bentley
Got first binary test passing
273
                              self.to_file)
274
            except errors.BinaryFile:
6809.4.7 by Jelmer Vernooij
Swap arguments for get_symlink_target and kind/stored_kind.
275
                old_lines = tree_lines(old_tree, old_path, require_text=False)
276
                new_lines = tree_lines(new_tree, new_path, require_text=False)
1185.82.103 by Aaron Bentley
Serialize all actions through the action object
277
                action.add_property('encoding', 'base64')
278
                action.write(self.to_file)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
279
                binary_diff(old_path, old_lines, new_path, new_lines,
1185.82.96 by Aaron Bentley
Got first binary test passing
280
                            self.to_file)
281
1185.82.104 by Aaron Bentley
Refactored action writing
282
        def finish_action(action, file_id, kind, meta_modified, text_modified,
283
                          old_path, new_path):
6915.4.2 by Jelmer Vernooij
Remove __getitem__ and __iter__ from Inventory.
284
            entry = new_tree.root_inventory.get_entry(file_id)
6471.1.6 by Jelmer Vernooij
Revert bundle serializer code.
285
            if entry.revision != default_revision_id:
286
                action.add_utf8_property('last-changed', entry.revision)
1185.82.104 by Aaron Bentley
Refactored action writing
287
            if meta_modified:
6471.1.6 by Jelmer Vernooij
Revert bundle serializer code.
288
                action.add_bool_property('executable', entry.executable)
1185.82.104 by Aaron Bentley
Refactored action writing
289
            if text_modified and kind == "symlink":
6471.1.6 by Jelmer Vernooij
Revert bundle serializer code.
290
                action.add_property('target', entry.symlink_target)
1185.82.104 by Aaron Bentley
Refactored action writing
291
            if text_modified and kind == "file":
1185.84.3 by Aaron Bentley
Hide diffs for old revisions in bundles
292
                do_diff(file_id, old_path, new_path, action, force_binary)
1185.82.104 by Aaron Bentley
Refactored action writing
293
            else:
294
                action.write(self.to_file)
295
1910.2.64 by Aaron Bentley
Changes from review
296
        delta = new_tree.changes_from(old_tree, want_unchanged=True,
1731.1.33 by Aaron Bentley
Revert no-special-root changes
297
                                      include_root=True)
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
298
        for path, file_id, kind in delta.removed:
1185.82.103 by Aaron Bentley
Serialize all actions through the action object
299
            action = Action('removed', [kind, path]).write(self.to_file)
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
300
301
        for path, file_id, kind in delta.added:
1731.1.55 by Aaron Bentley
Fix bundle handling
302
            action = Action('added', [kind, path], [('file-id', file_id)])
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
303
            meta_modified = (kind=='file' and
6809.4.4 by Jelmer Vernooij
Swap arguments for Tree.is_executable.
304
                             new_tree.is_executable(path, file_id))
1185.82.119 by Aaron Bentley
Default execute bit to no for new files, directories, symlinks
305
            finish_action(action, file_id, kind, meta_modified, True,
1185.82.104 by Aaron Bentley
Refactored action writing
306
                          DEVNULL, path)
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
307
308
        for (old_path, new_path, file_id, kind,
309
             text_modified, meta_modified) in delta.renamed:
1731.1.55 by Aaron Bentley
Fix bundle handling
310
            action = Action('renamed', [kind, old_path], [(new_path,)])
1185.82.104 by Aaron Bentley
Refactored action writing
311
            finish_action(action, file_id, kind, meta_modified, text_modified,
312
                          old_path, new_path)
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
313
314
        for (path, file_id, kind,
315
             text_modified, meta_modified) in delta.modified:
1731.1.55 by Aaron Bentley
Fix bundle handling
316
            action = Action('modified', [kind, path])
1185.82.104 by Aaron Bentley
Refactored action writing
317
            finish_action(action, file_id, kind, meta_modified, text_modified,
318
                          path, path)
1185.82.117 by Aaron Bentley
Handle last-modified changes on their own
319
320
        for path, file_id, kind in delta.unchanged:
6809.4.15 by Jelmer Vernooij
Fix some more tests.
321
            new_rev = new_tree.get_file_revision(path, file_id)
1185.82.117 by Aaron Bentley
Handle last-modified changes on their own
322
            if new_rev is None:
323
                continue
6809.4.15 by Jelmer Vernooij
Fix some more tests.
324
            old_rev = old_tree.get_file_revision(path, file_id)
1185.82.117 by Aaron Bentley
Handle last-modified changes on their own
325
            if new_rev != old_rev:
6809.4.20 by Jelmer Vernooij
Fix tests.
326
                action = Action('modified', [new_tree.kind(path, file_id),
6809.4.16 by Jelmer Vernooij
Bundle fixes.
327
                                             path])
6405.2.10 by Jelmer Vernooij
Fix more tests.
328
                action.add_utf8_property('last-changed', new_rev)
1185.82.117 by Aaron Bentley
Handle last-modified changes on their own
329
                action.write(self.to_file)
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
330
331
332
class BundleReader(object):
333
    """This class reads in a bundle from a file, and returns
334
    a Bundle object, which can then be applied against a tree.
335
    """
336
    def __init__(self, from_file):
337
        """Read in the bundle from the file.
338
339
        :param from_file: A file-like object (must have iterator support).
340
        """
341
        object.__init__(self)
342
        self.from_file = iter(from_file)
343
        self._next_line = None
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
344
1910.2.55 by Aaron Bentley
Bundle 0.9 uses Testament 3 strict
345
        self.info = self._get_info()
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
346
        # We put the actual inventory ids in the footer, so that the patch
347
        # is easier to read for humans.
348
        # Unfortunately, that means we need to read everything before we
349
        # can create a proper bundle.
350
        self._read()
351
        self._validate()
352
1910.2.55 by Aaron Bentley
Bundle 0.9 uses Testament 3 strict
353
    def _get_info(self):
354
        return BundleInfo08()
355
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
356
    def _read(self):
6634.2.1 by Martin
Apply 2to3 next fixer and make compatible
357
        next(self._next())
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
358
        while self._next_line is not None:
1793.3.4 by John Arbash Meinel
[merge] bzr.dev 1804 and fix conflicts.
359
            if not self._read_revision_header():
360
                break
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
361
            if self._next_line is None:
362
                break
363
            self._read_patches()
364
            self._read_footer()
365
366
    def _validate(self):
367
        """Make sure that the information read in makes sense
368
        and passes appropriate checksums.
369
        """
370
        # Fill in all the missing blanks for the revisions
371
        # and generate the real_revisions list.
372
        self.info.complete_info()
373
374
    def _next(self):
375
        """yield the next line, but secretly
376
        keep 1 extra line for peeking.
377
        """
378
        for line in self.from_file:
379
            last = self._next_line
380
            self._next_line = line
381
            if last is not None:
382
                #mutter('yielding line: %r' % last)
383
                yield last
384
        last = self._next_line
385
        self._next_line = None
386
        #mutter('yielding line: %r' % last)
387
        yield last
388
389
    def _read_revision_header(self):
1793.3.4 by John Arbash Meinel
[merge] bzr.dev 1804 and fix conflicts.
390
        found_something = False
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
391
        self.info.revisions.append(RevisionInfo(None))
392
        for line in self._next():
393
            # The bzr header is terminated with a blank line
394
            # which does not start with '#'
395
            if line is None or line == '\n':
396
                break
1793.3.16 by John Arbash Meinel
Add tests to ensure that we gracefully handle opening and trailing non-bundle text.
397
            if not line.startswith('#'):
398
                continue
1793.3.4 by John Arbash Meinel
[merge] bzr.dev 1804 and fix conflicts.
399
            found_something = True
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
400
            self._handle_next(line)
1793.3.4 by John Arbash Meinel
[merge] bzr.dev 1804 and fix conflicts.
401
        if not found_something:
402
            # Nothing was there, so remove the added revision
403
            self.info.revisions.pop()
404
        return found_something
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
405
406
    def _read_next_entry(self, line, indent=1):
407
        """Read in a key-value pair
408
        """
409
        if not line.startswith('#'):
1793.3.15 by John Arbash Meinel
Raise the right errors
410
            raise errors.MalformedHeader('Bzr header did not start with #')
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
411
        line = line[1:-1].decode('utf-8') # Remove the '#' and '\n'
412
        if line[:indent] == ' '*indent:
413
            line = line[indent:]
414
        if not line:
415
            return None, None# Ignore blank lines
416
417
        loc = line.find(': ')
418
        if loc != -1:
419
            key = line[:loc]
420
            value = line[loc+2:]
421
            if not value:
422
                value = self._read_many(indent=indent+2)
423
        elif line[-1:] == ':':
424
            key = line[:-1]
425
            value = self._read_many(indent=indent+2)
426
        else:
1793.3.15 by John Arbash Meinel
Raise the right errors
427
            raise errors.MalformedHeader('While looking for key: value pairs,'
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
428
                    ' did not find the colon %r' % (line))
429
430
        key = key.replace(' ', '_')
431
        #mutter('found %s: %s' % (key, value))
432
        return key, value
433
434
    def _handle_next(self, line):
435
        if line is None:
436
            return
437
        key, value = self._read_next_entry(line, indent=1)
438
        mutter('_handle_next %r => %r' % (key, value))
439
        if key is None:
440
            return
441
442
        revision_info = self.info.revisions[-1]
1963.2.4 by Robey Pointer
remove usage of hasattr
443
        if key in revision_info.__dict__:
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
444
            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
445
                if key in ('file_id', 'revision_id', 'base_id'):
446
                    value = value.encode('utf8')
447
                elif key in ('parent_ids'):
448
                    value = [v.encode('utf8') for v in value]
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
449
                setattr(revision_info, key, value)
450
            else:
1793.3.15 by John Arbash Meinel
Raise the right errors
451
                raise errors.MalformedHeader('Duplicated Key: %s' % key)
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
452
        else:
453
            # What do we do with a key we don't recognize
1793.3.15 by John Arbash Meinel
Raise the right errors
454
            raise errors.MalformedHeader('Unknown Key: "%s"' % key)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
455
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
456
    def _read_many(self, indent):
457
        """If a line ends with no entry, that means that it should be
458
        followed with multiple lines of values.
459
460
        This detects the end of the list, because it will be a line that
461
        does not start properly indented.
462
        """
463
        values = []
464
        start = '#' + (' '*indent)
465
466
        if self._next_line is None or self._next_line[:len(start)] != start:
467
            return values
468
469
        for line in self._next():
470
            values.append(line[len(start):-1].decode('utf-8'))
471
            if self._next_line is None or self._next_line[:len(start)] != start:
472
                break
473
        return values
474
475
    def _read_one_patch(self):
476
        """Read in one patch, return the complete patch, along with
477
        the next line.
478
479
        :return: action, lines, do_continue
480
        """
481
        #mutter('_read_one_patch: %r' % self._next_line)
482
        # Peek and see if there are no patches
483
        if self._next_line is None or self._next_line.startswith('#'):
484
            return None, [], False
485
486
        first = True
487
        lines = []
488
        for line in self._next():
489
            if first:
490
                if not line.startswith('==='):
1793.3.15 by John Arbash Meinel
Raise the right errors
491
                    raise errors.MalformedPatches('The first line of all patches'
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
492
                        ' should be a bzr meta line "==="'
493
                        ': %r' % line)
494
                action = line[4:-1].decode('utf-8')
495
            elif line.startswith('... '):
496
                action += line[len('... '):-1].decode('utf-8')
497
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
498
            if (self._next_line is not None and
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
499
                self._next_line.startswith('===')):
500
                return action, lines, True
501
            elif self._next_line is None or self._next_line.startswith('#'):
502
                return action, lines, False
503
504
            if first:
505
                first = False
506
            elif not line.startswith('... '):
507
                lines.append(line)
508
509
        return action, lines, False
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
510
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
511
    def _read_patches(self):
512
        do_continue = True
513
        revision_actions = []
514
        while do_continue:
515
            action, lines, do_continue = self._read_one_patch()
516
            if action is not None:
517
                revision_actions.append((action, lines))
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
518
        if self.info.revisions[-1].tree_actions is not None:
519
            raise AssertionError()
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
520
        self.info.revisions[-1].tree_actions = revision_actions
521
522
    def _read_footer(self):
523
        """Read the rest of the meta information.
524
525
        :param first_line:  The previous step iterates past what it
526
                            can handle. That extra line is given here.
527
        """
528
        for line in self._next():
529
            self._handle_next(line)
1793.3.14 by John Arbash Meinel
Actually fix the bug with missing trailing newline bug #49182
530
            if self._next_line is None:
531
                break
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
532
            if not self._next_line.startswith('#'):
1793.3.14 by John Arbash Meinel
Actually fix the bug with missing trailing newline bug #49182
533
                # Consume the trailing \n and stop processing
6634.2.1 by Martin
Apply 2to3 next fixer and make compatible
534
                next(self._next())
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
535
                break
1910.2.1 by Aaron Bentley
Ensure root entry always has a revision
536
537
class BundleInfo08(BundleInfo):
1910.2.55 by Aaron Bentley
Bundle 0.9 uses Testament 3 strict
538
1910.2.1 by Aaron Bentley
Ensure root entry always has a revision
539
    def _update_tree(self, bundle_tree, revision_id):
540
        bundle_tree.note_last_changed('', revision_id)
541
        BundleInfo._update_tree(self, bundle_tree, revision_id)
1910.2.55 by Aaron Bentley
Bundle 0.9 uses Testament 3 strict
542
543
    def _testament_sha1_from_revision(self, repository, revision_id):
544
        testament = StrictTestament.from_revision(repository, revision_id)
545
        return testament.as_sha1()
546
5798.1.2 by Jelmer Vernooij
Fix some tests.
547
    def _testament_sha1(self, revision, tree):
548
        return StrictTestament(revision, tree).as_sha1()