/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
7206.4.1 by Jelmer Vernooij
Move breezy.testament to breezy.bzr.testament.
38
from breezy.bzr.testament import StrictTestament
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
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"""
7143.15.2 by Jelmer Vernooij
Run autopep8.
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'... ')
7143.15.2 by Jelmer Vernooij
Run autopep8.
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"""
7143.15.2 by Jelmer Vernooij
Run autopep8.
131
        forced_bases = {revision_id: base_revision_id}
7029.2.1 by Jelmer Vernooij
don't make assumptions about the order in which revision ids are returned by write_bundle.
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,
7143.15.2 by Jelmer Vernooij
Run autopep8.
136
                                                   [base_revision_id])
7029.2.1 by Jelmer Vernooij
don't make assumptions about the order in which revision ids are returned by write_bundle.
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:
7143.15.2 by Jelmer Vernooij
Run autopep8.
179
                f.write(b'#' + (b' ' * (indent + 2)))
6973.6.1 by Jelmer Vernooij
More bees.
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'=== ')
7143.15.2 by Jelmer Vernooij
Run autopep8.
262
        self.to_file.write(' '.join([name] + parameters).encode('utf-8'))
1185.82.101 by Aaron Bentley
Start using a standard action writer
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):
7141.7.3 by Jelmer Vernooij
Fix some bundle tests.
275
                if tree.has_id(file_id):
7141.7.1 by Jelmer Vernooij
Get rid of file_ids in most of Tree.
276
                    tree_file = tree.get_file(path)
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:
7143.15.15 by Jelmer Vernooij
Merge trunk.
319
            action = Action(
320
                'added', [kind, path], [('file-id', file_id.decode('utf-8'))])
7143.16.21 by Jelmer Vernooij
Fix regressions.
321
            meta_modified = (kind == 'file' and
7141.7.1 by Jelmer Vernooij
Get rid of file_ids in most of Tree.
322
                             new_tree.is_executable(path))
1185.82.119 by Aaron Bentley
Default execute bit to no for new files, directories, symlinks
323
            finish_action(action, file_id, kind, meta_modified, True,
1185.82.104 by Aaron Bentley
Refactored action writing
324
                          DEVNULL, path)
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
325
326
        for (old_path, new_path, file_id, kind,
327
             text_modified, meta_modified) in delta.renamed:
1731.1.55 by Aaron Bentley
Fix bundle handling
328
            action = Action('renamed', [kind, old_path], [(new_path,)])
1185.82.104 by Aaron Bentley
Refactored action writing
329
            finish_action(action, file_id, kind, meta_modified, text_modified,
330
                          old_path, new_path)
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
331
332
        for (path, file_id, kind,
333
             text_modified, meta_modified) in delta.modified:
1731.1.55 by Aaron Bentley
Fix bundle handling
334
            action = Action('modified', [kind, path])
1185.82.104 by Aaron Bentley
Refactored action writing
335
            finish_action(action, file_id, kind, meta_modified, text_modified,
336
                          path, path)
1185.82.117 by Aaron Bentley
Handle last-modified changes on their own
337
338
        for path, file_id, kind in delta.unchanged:
7141.7.1 by Jelmer Vernooij
Get rid of file_ids in most of Tree.
339
            new_rev = new_tree.get_file_revision(path)
1185.82.117 by Aaron Bentley
Handle last-modified changes on their own
340
            if new_rev is None:
341
                continue
7141.7.3 by Jelmer Vernooij
Fix some bundle tests.
342
            old_rev = old_tree.get_file_revision(old_tree.id2path(file_id))
1185.82.117 by Aaron Bentley
Handle last-modified changes on their own
343
            if new_rev != old_rev:
7143.15.15 by Jelmer Vernooij
Merge trunk.
344
                action = Action('modified', [new_tree.kind(path), 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
    """
7143.15.2 by Jelmer Vernooij
Run autopep8.
353
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
354
    def __init__(self, from_file):
355
        """Read in the bundle from the file.
356
357
        :param from_file: A file-like object (must have iterator support).
358
        """
359
        object.__init__(self)
360
        self.from_file = iter(from_file)
361
        self._next_line = None
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
362
1910.2.55 by Aaron Bentley
Bundle 0.9 uses Testament 3 strict
363
        self.info = self._get_info()
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
364
        # We put the actual inventory ids in the footer, so that the patch
365
        # is easier to read for humans.
366
        # Unfortunately, that means we need to read everything before we
367
        # can create a proper bundle.
368
        self._read()
369
        self._validate()
370
1910.2.55 by Aaron Bentley
Bundle 0.9 uses Testament 3 strict
371
    def _get_info(self):
372
        return BundleInfo08()
373
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
374
    def _read(self):
6634.2.1 by Martin
Apply 2to3 next fixer and make compatible
375
        next(self._next())
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
376
        while self._next_line is not None:
1793.3.4 by John Arbash Meinel
[merge] bzr.dev 1804 and fix conflicts.
377
            if not self._read_revision_header():
378
                break
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
379
            if self._next_line is None:
380
                break
381
            self._read_patches()
382
            self._read_footer()
383
384
    def _validate(self):
385
        """Make sure that the information read in makes sense
386
        and passes appropriate checksums.
387
        """
388
        # Fill in all the missing blanks for the revisions
389
        # and generate the real_revisions list.
390
        self.info.complete_info()
391
392
    def _next(self):
393
        """yield the next line, but secretly
394
        keep 1 extra line for peeking.
395
        """
396
        for line in self.from_file:
397
            last = self._next_line
398
            self._next_line = line
399
            if last is not None:
400
                #mutter('yielding line: %r' % last)
401
                yield last
402
        last = self._next_line
403
        self._next_line = None
404
        #mutter('yielding line: %r' % last)
405
        yield last
406
407
    def _read_revision_header(self):
1793.3.4 by John Arbash Meinel
[merge] bzr.dev 1804 and fix conflicts.
408
        found_something = False
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
409
        self.info.revisions.append(RevisionInfo(None))
410
        for line in self._next():
411
            # The bzr header is terminated with a blank line
412
            # which does not start with '#'
7029.4.1 by Jelmer Vernooij
Fix a bunch of merge tests.
413
            if line is None or line == b'\n':
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
414
                break
7029.4.1 by Jelmer Vernooij
Fix a bunch of merge tests.
415
            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.
416
                continue
1793.3.4 by John Arbash Meinel
[merge] bzr.dev 1804 and fix conflicts.
417
            found_something = True
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
418
            self._handle_next(line)
1793.3.4 by John Arbash Meinel
[merge] bzr.dev 1804 and fix conflicts.
419
        if not found_something:
420
            # Nothing was there, so remove the added revision
421
            self.info.revisions.pop()
422
        return found_something
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
423
424
    def _read_next_entry(self, line, indent=1):
425
        """Read in a key-value pair
426
        """
7029.4.1 by Jelmer Vernooij
Fix a bunch of merge tests.
427
        if not line.startswith(b'#'):
1793.3.15 by John Arbash Meinel
Raise the right errors
428
            raise errors.MalformedHeader('Bzr header did not start with #')
7143.15.2 by Jelmer Vernooij
Run autopep8.
429
        line = line[1:-1].decode('utf-8')  # Remove the '#' and '\n'
430
        if line[:indent] == ' ' * indent:
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
431
            line = line[indent:]
432
        if not line:
7143.15.2 by Jelmer Vernooij
Run autopep8.
433
            return None, None  # Ignore blank lines
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
434
435
        loc = line.find(': ')
436
        if loc != -1:
437
            key = line[:loc]
7143.15.2 by Jelmer Vernooij
Run autopep8.
438
            value = line[loc + 2:]
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
439
            if not value:
7143.15.2 by Jelmer Vernooij
Run autopep8.
440
                value = self._read_many(indent=indent + 2)
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
441
        elif line[-1:] == ':':
442
            key = line[:-1]
7143.15.2 by Jelmer Vernooij
Run autopep8.
443
            value = self._read_many(indent=indent + 2)
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
444
        else:
1793.3.15 by John Arbash Meinel
Raise the right errors
445
            raise errors.MalformedHeader('While looking for key: value pairs,'
7143.15.2 by Jelmer Vernooij
Run autopep8.
446
                                         ' did not find the colon %r' % (line))
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
447
448
        key = key.replace(' ', '_')
449
        #mutter('found %s: %s' % (key, value))
450
        return key, value
451
452
    def _handle_next(self, line):
453
        if line is None:
454
            return
455
        key, value = self._read_next_entry(line, indent=1)
456
        mutter('_handle_next %r => %r' % (key, value))
457
        if key is None:
458
            return
459
460
        revision_info = self.info.revisions[-1]
1963.2.4 by Robey Pointer
remove usage of hasattr
461
        if key in revision_info.__dict__:
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
462
            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
463
                if key in ('file_id', 'revision_id', 'base_id'):
464
                    value = value.encode('utf8')
465
                elif key in ('parent_ids'):
466
                    value = [v.encode('utf8') for v in value]
7045.2.14 by Jelmer Vernooij
Fix some bundle tests.:
467
                elif key in ('testament_sha1', 'inventory_sha1', 'sha1'):
7031.1.2 by Jelmer Vernooij
Port diff/merge_directive to Python3.
468
                    value = value.encode('ascii')
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
469
                setattr(revision_info, key, value)
470
            else:
1793.3.15 by John Arbash Meinel
Raise the right errors
471
                raise errors.MalformedHeader('Duplicated Key: %s' % key)
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
472
        else:
473
            # What do we do with a key we don't recognize
1793.3.15 by John Arbash Meinel
Raise the right errors
474
            raise errors.MalformedHeader('Unknown Key: "%s"' % key)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
475
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
476
    def _read_many(self, indent):
477
        """If a line ends with no entry, that means that it should be
478
        followed with multiple lines of values.
479
480
        This detects the end of the list, because it will be a line that
481
        does not start properly indented.
482
        """
483
        values = []
7143.15.2 by Jelmer Vernooij
Run autopep8.
484
        start = b'#' + (b' ' * indent)
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
485
7029.4.1 by Jelmer Vernooij
Fix a bunch of merge tests.
486
        if self._next_line is None or not self._next_line.startswith(start):
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
487
            return values
488
489
        for line in self._next():
490
            values.append(line[len(start):-1].decode('utf-8'))
7029.4.1 by Jelmer Vernooij
Fix a bunch of merge tests.
491
            if self._next_line is None or not self._next_line.startswith(start):
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
492
                break
493
        return values
494
495
    def _read_one_patch(self):
496
        """Read in one patch, return the complete patch, along with
497
        the next line.
498
499
        :return: action, lines, do_continue
500
        """
501
        #mutter('_read_one_patch: %r' % self._next_line)
502
        # Peek and see if there are no patches
7029.4.1 by Jelmer Vernooij
Fix a bunch of merge tests.
503
        if self._next_line is None or self._next_line.startswith(b'#'):
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
504
            return None, [], False
505
506
        first = True
507
        lines = []
508
        for line in self._next():
509
            if first:
7031.1.2 by Jelmer Vernooij
Port diff/merge_directive to Python3.
510
                if not line.startswith(b'==='):
1793.3.15 by John Arbash Meinel
Raise the right errors
511
                    raise errors.MalformedPatches('The first line of all patches'
7143.15.2 by Jelmer Vernooij
Run autopep8.
512
                                                  ' should be a bzr meta line "==="'
513
                                                  ': %r' % line)
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
514
                action = line[4:-1].decode('utf-8')
7031.1.2 by Jelmer Vernooij
Port diff/merge_directive to Python3.
515
            elif line.startswith(b'... '):
516
                action += line[len(b'... '):-1].decode('utf-8')
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
517
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
518
            if (self._next_line is not None and
7143.15.2 by Jelmer Vernooij
Run autopep8.
519
                    self._next_line.startswith(b'===')):
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
520
                return action, lines, True
7031.1.2 by Jelmer Vernooij
Port diff/merge_directive to Python3.
521
            elif self._next_line is None or self._next_line.startswith(b'#'):
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
522
                return action, lines, False
523
524
            if first:
525
                first = False
7031.1.2 by Jelmer Vernooij
Port diff/merge_directive to Python3.
526
            elif not line.startswith(b'... '):
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
527
                lines.append(line)
528
529
        return action, lines, False
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
530
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
531
    def _read_patches(self):
532
        do_continue = True
533
        revision_actions = []
534
        while do_continue:
535
            action, lines, do_continue = self._read_one_patch()
536
            if action is not None:
537
                revision_actions.append((action, lines))
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
538
        if self.info.revisions[-1].tree_actions is not None:
539
            raise AssertionError()
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
540
        self.info.revisions[-1].tree_actions = revision_actions
541
542
    def _read_footer(self):
543
        """Read the rest of the meta information.
544
545
        :param first_line:  The previous step iterates past what it
546
                            can handle. That extra line is given here.
547
        """
548
        for line in self._next():
549
            self._handle_next(line)
1793.3.14 by John Arbash Meinel
Actually fix the bug with missing trailing newline bug #49182
550
            if self._next_line is None:
551
                break
7029.4.1 by Jelmer Vernooij
Fix a bunch of merge tests.
552
            if not self._next_line.startswith(b'#'):
1793.3.14 by John Arbash Meinel
Actually fix the bug with missing trailing newline bug #49182
553
                # Consume the trailing \n and stop processing
6634.2.1 by Martin
Apply 2to3 next fixer and make compatible
554
                next(self._next())
1793.2.2 by Aaron Bentley
Move BundleReader into v07 serializer
555
                break
1910.2.1 by Aaron Bentley
Ensure root entry always has a revision
556
7143.15.2 by Jelmer Vernooij
Run autopep8.
557
1910.2.1 by Aaron Bentley
Ensure root entry always has a revision
558
class BundleInfo08(BundleInfo):
1910.2.55 by Aaron Bentley
Bundle 0.9 uses Testament 3 strict
559
1910.2.1 by Aaron Bentley
Ensure root entry always has a revision
560
    def _update_tree(self, bundle_tree, revision_id):
561
        bundle_tree.note_last_changed('', revision_id)
562
        BundleInfo._update_tree(self, bundle_tree, revision_id)
1910.2.55 by Aaron Bentley
Bundle 0.9 uses Testament 3 strict
563
564
    def _testament_sha1_from_revision(self, repository, revision_id):
565
        testament = StrictTestament.from_revision(repository, revision_id)
566
        return testament.as_sha1()
567
5798.1.2 by Jelmer Vernooij
Fix some tests.
568
    def _testament_sha1(self, revision, tree):
569
        return StrictTestament(revision, tree).as_sha1()