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