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