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