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