/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
4634.155.1 by John Arbash Meinel
(bug #586926) We accidentally were raising a string exception.
1
# Copyright (C) 2005-2010 Canonical Ltd
1711.3.2 by John Arbash Meinel
Add the read_bundle_from_url command, which handles lots of exceptions
2
#
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.
7
#
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.
12
#
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
1711.3.2 by John Arbash Meinel
Add the read_bundle_from_url command, which handles lots of exceptions
16
6379.6.7 by Jelmer Vernooij
Move importing from future until after doc string, otherwise the doc string will disappear.
17
"""Read in a bundle stream, and process it into a BundleReader object."""
18
6379.6.3 by Jelmer Vernooij
Use absolute_import.
19
from __future__ import absolute_import
20
1185.82.96 by Aaron Bentley
Got first binary test passing
21
import base64
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
22
import os
23
import pprint
24
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
25
from .. import (
6630.1.1 by Jelmer Vernooij
Remove deprecated functionality.
26
    cache_utf8,
2294.1.10 by John Arbash Meinel
Switch all apis over to utf8 file ids. All tests pass
27
    osutils,
2520.4.33 by Aaron Bentley
remove test dependencies on serialization minutia
28
    timestamp,
2294.1.10 by John Arbash Meinel
Switch all apis over to utf8 file ids. All tests pass
29
    )
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
30
from . import apply_bundle
31
from ..errors import (
5798.1.4 by Jelmer Vernooij
Fix bundle tests.
32
    TestamentMismatch,
33
    BzrError,
34
    )
6670.4.1 by Jelmer Vernooij
Update imports.
35
from ..bzr.inventory import (
5798.1.4 by Jelmer Vernooij
Fix bundle tests.
36
    Inventory,
37
    InventoryDirectory,
38
    InventoryFile,
39
    InventoryLink,
40
    )
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
41
from ..osutils import sha_string, pathjoin
42
from ..revision import Revision, NULL_REVISION
43
from ..sixish import (
6621.22.2 by Martin
Use BytesIO or StringIO from bzrlib.sixish
44
    BytesIO,
6656.1.1 by Martin
Apply 2to3 dict fixer and clean up resulting mess using view helpers
45
    viewitems,
6621.22.2 by Martin
Use BytesIO or StringIO from bzrlib.sixish
46
    )
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
47
from ..testament import StrictTestament
48
from ..trace import mutter, warning
49
from ..tree import Tree
6670.4.1 by Jelmer Vernooij
Update imports.
50
from ..bzr.xml5 import serializer_v5
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
51
0.5.57 by John Arbash Meinel
Simplified the header, only output base if it is not the expected one.
52
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
53
class RevisionInfo(object):
54
    """Gets filled out for each revision object that is read.
55
    """
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
56
    def __init__(self, revision_id):
57
        self.revision_id = revision_id
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
58
        self.sha1 = None
59
        self.committer = None
0.5.39 by John Arbash Meinel
(broken) Working on changing the processing to use a ChangesetTree.
60
        self.date = None
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
61
        self.timestamp = None
62
        self.timezone = None
63
        self.inventory_sha1 = None
64
1185.82.27 by Aaron Bentley
Fixed most revision attribute handling
65
        self.parent_ids = None
1185.82.74 by Aaron Bentley
Allow custom base for any revision
66
        self.base_id = None
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
67
        self.message = None
1185.82.27 by Aaron Bentley
Fixed most revision attribute handling
68
        self.properties = None
1185.82.77 by Aaron Bentley
Move tree actions to RevisionInfo
69
        self.tree_actions = None
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
70
71
    def __str__(self):
72
        return pprint.pformat(self.__dict__)
73
0.5.37 by John Arbash Meinel
Made read_changeset able to spit out 'Revision' entities.
74
    def as_revision(self):
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
75
        rev = Revision(revision_id=self.revision_id,
0.5.37 by John Arbash Meinel
Made read_changeset able to spit out 'Revision' entities.
76
            committer=self.committer,
77
            timestamp=float(self.timestamp),
78
            timezone=int(self.timezone),
79
            inventory_sha1=self.inventory_sha1,
80
            message='\n'.join(self.message))
81
1185.82.28 by Aaron Bentley
Got parent_id handling working
82
        if self.parent_ids:
83
            rev.parent_ids.extend(self.parent_ids)
1185.82.35 by Aaron Bentley
Read revision properties
84
1185.82.59 by Aaron Bentley
Behave properly when there are no properties in a revision
85
        if self.properties:
86
            for property in self.properties:
87
                key_end = property.find(': ')
2447.1.2 by John Arbash Meinel
Add tests that we handle empty values whether they end in ': \n' or ':\n'.
88
                if key_end == -1:
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
89
                    if not property.endswith(':'):
90
                        raise ValueError(property)
2447.1.2 by John Arbash Meinel
Add tests that we handle empty values whether they end in ': \n' or ':\n'.
91
                    key = str(property[:-1])
92
                    value = ''
93
                else:
2447.1.4 by John Arbash Meinel
Add a test that we properly round-trip unicode properties.
94
                    key = str(property[:key_end])
95
                    value = property[key_end+2:]
1185.82.59 by Aaron Bentley
Behave properly when there are no properties in a revision
96
                rev.properties[key] = value
1185.82.35 by Aaron Bentley
Read revision properties
97
0.5.37 by John Arbash Meinel
Made read_changeset able to spit out 'Revision' entities.
98
        return rev
99
2520.4.33 by Aaron Bentley
remove test dependencies on serialization minutia
100
    @staticmethod
101
    def from_revision(revision):
102
        revision_info = RevisionInfo(revision.revision_id)
103
        date = timestamp.format_highres_date(revision.timestamp,
104
                                             revision.timezone)
105
        revision_info.date = date
106
        revision_info.timezone = revision.timezone
107
        revision_info.timestamp = revision.timestamp
108
        revision_info.message = revision.message.split('\n')
109
        revision_info.properties = [': '.join(p) for p in
6656.1.1 by Martin
Apply 2to3 dict fixer and clean up resulting mess using view helpers
110
                                    viewitems(revision.properties)]
2520.4.33 by Aaron Bentley
remove test dependencies on serialization minutia
111
        return revision_info
112
1185.82.123 by Aaron Bentley
Cleanups to prepare for review
113
1185.82.130 by Aaron Bentley
Rename changesets to revision bundles
114
class BundleInfo(object):
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
115
    """This contains the meta information. Stuff that allows you to
116
    recreate the revision or inventory XML.
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
117
    """
2520.4.35 by Aaron Bentley
zap obsolete changeset commands, add bundle-info command
118
    def __init__(self, bundle_format=None):
119
        self.bundle_format = None
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
120
        self.committer = None
121
        self.date = None
0.5.17 by John Arbash Meinel
adding apply-changset, plus more meta information.
122
        self.message = None
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
123
0.5.39 by John Arbash Meinel
(broken) Working on changing the processing to use a ChangesetTree.
124
        # A list of RevisionInfo objects
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
125
        self.revisions = []
0.5.56 by John Arbash Meinel
A couple more fixups, it seems actually capable now of writing out a changeset, and reading it back.
126
127
        # The next entries are created during complete_info() and
128
        # other post-read functions.
129
130
        # A list of real Revision objects
131
        self.real_revisions = []
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
132
133
        self.timestamp = None
134
        self.timezone = None
0.5.15 by John Arbash Meinel
Created an apply-changeset function, and modified output for better parsing.
135
2476.2.1 by John Arbash Meinel
Vastly improve bundle install performance by only validating the bundle one time
136
        # Have we checked the repository yet?
137
        self._validated_revisions_against_repo = False
138
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
139
    def __str__(self):
140
        return pprint.pformat(self.__dict__)
141
0.5.39 by John Arbash Meinel
(broken) Working on changing the processing to use a ChangesetTree.
142
    def complete_info(self):
143
        """This makes sure that all information is properly
144
        split up, based on the assumptions that can be made
145
        when information is missing.
146
        """
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
147
        from breezy.timestamp import unpack_highres_date
0.5.56 by John Arbash Meinel
A couple more fixups, it seems actually capable now of writing out a changeset, and reading it back.
148
        # Put in all of the guessable information.
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
149
        if not self.timestamp and self.date:
0.5.81 by John Arbash Meinel
Cleaning up from pychecker.
150
            self.timestamp, self.timezone = unpack_highres_date(self.date)
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
151
0.5.56 by John Arbash Meinel
A couple more fixups, it seems actually capable now of writing out a changeset, and reading it back.
152
        self.real_revisions = []
0.5.39 by John Arbash Meinel
(broken) Working on changing the processing to use a ChangesetTree.
153
        for rev in self.revisions:
0.5.60 by John Arbash Meinel
read_changeset now parses the date: subheader of revisions correctly.
154
            if rev.timestamp is None:
155
                if rev.date is not None:
156
                    rev.timestamp, rev.timezone = \
0.5.81 by John Arbash Meinel
Cleaning up from pychecker.
157
                            unpack_highres_date(rev.date)
0.5.60 by John Arbash Meinel
read_changeset now parses the date: subheader of revisions correctly.
158
                else:
159
                    rev.timestamp = self.timestamp
160
                    rev.timezone = self.timezone
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
161
            if rev.message is None and self.message:
162
                rev.message = self.message
163
            if rev.committer is None and self.committer:
164
                rev.committer = self.committer
0.5.56 by John Arbash Meinel
A couple more fixups, it seems actually capable now of writing out a changeset, and reading it back.
165
            self.real_revisions.append(rev.as_revision())
166
1185.82.48 by Aaron Bentley
Inching closer to supporting multiple revisions per changeset
167
    def get_base(self, revision):
1185.82.74 by Aaron Bentley
Allow custom base for any revision
168
        revision_info = self.get_revision_info(revision.revision_id)
169
        if revision_info.base_id is not None:
3668.5.4 by Jelmer Vernooij
Eliminate more uses of Repository.revision_tree(None).
170
            return revision_info.base_id
1185.82.48 by Aaron Bentley
Inching closer to supporting multiple revisions per changeset
171
        if len(revision.parent_ids) == 0:
172
            # There is no base listed, and
173
            # the lowest revision doesn't have a parent
174
            # so this is probably against the empty tree
3668.5.4 by Jelmer Vernooij
Eliminate more uses of Repository.revision_tree(None).
175
            # and thus base truly is NULL_REVISION
176
            return NULL_REVISION
1185.82.48 by Aaron Bentley
Inching closer to supporting multiple revisions per changeset
177
        else:
1185.82.73 by Aaron Bentley
Use rightmost parent always
178
            return revision.parent_ids[-1]
0.5.56 by John Arbash Meinel
A couple more fixups, it seems actually capable now of writing out a changeset, and reading it back.
179
0.5.67 by John Arbash Meinel
Working on apply_changeset
180
    def _get_target(self):
0.5.81 by John Arbash Meinel
Cleaning up from pychecker.
181
        """Return the target revision."""
0.5.67 by John Arbash Meinel
Working on apply_changeset
182
        if len(self.real_revisions) > 0:
1185.82.48 by Aaron Bentley
Inching closer to supporting multiple revisions per changeset
183
            return self.real_revisions[0].revision_id
0.5.67 by John Arbash Meinel
Working on apply_changeset
184
        elif len(self.revisions) > 0:
1185.82.48 by Aaron Bentley
Inching closer to supporting multiple revisions per changeset
185
            return self.revisions[0].revision_id
0.5.67 by John Arbash Meinel
Working on apply_changeset
186
        return None
187
188
    target = property(_get_target, doc='The target revision id')
189
1185.82.49 by Aaron Bentley
SPOT fixes, fix inventory validation
190
    def get_revision(self, revision_id):
191
        for r in self.real_revisions:
192
            if r.revision_id == revision_id:
193
                return r
194
        raise KeyError(revision_id)
195
196
    def get_revision_info(self, revision_id):
197
        for r in self.revisions:
198
            if r.revision_id == revision_id:
199
                return r
200
        raise KeyError(revision_id)
201
1793.2.1 by Aaron Bentley
Move revision_tree into BundleInfo
202
    def revision_tree(self, repository, revision_id, base=None):
203
        revision = self.get_revision(revision_id)
204
        base = self.get_base(revision)
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
205
        if base == revision_id:
206
            raise AssertionError()
2476.2.1 by John Arbash Meinel
Vastly improve bundle install performance by only validating the bundle one time
207
        if not self._validated_revisions_against_repo:
208
            self._validate_references_from_repository(repository)
1793.2.1 by Aaron Bentley
Move revision_tree into BundleInfo
209
        revision_info = self.get_revision_info(revision_id)
210
        inventory_revision_id = revision_id
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
211
        bundle_tree = BundleTree(repository.revision_tree(base),
1793.2.1 by Aaron Bentley
Move revision_tree into BundleInfo
212
                                  inventory_revision_id)
213
        self._update_tree(bundle_tree, revision_id)
214
215
        inv = bundle_tree.inventory
216
        self._validate_inventory(inv, revision_id)
5798.1.2 by Jelmer Vernooij
Fix some tests.
217
        self._validate_revision(bundle_tree, revision_id)
1793.2.1 by Aaron Bentley
Move revision_tree into BundleInfo
218
219
        return bundle_tree
0.5.62 by John Arbash Meinel
Doing some internal validation before allowing processing to continue, additional checks at the command level.
220
1185.82.15 by Aaron Bentley
Disabled validate_revisions (needs info it doesn't have), updated API to repos
221
    def _validate_references_from_repository(self, repository):
222
        """Now that we have a repository which should have some of the
0.5.63 by John Arbash Meinel
Moving the validation into part of the reading.
223
        revisions we care about, go through and validate all of them
224
        that we can.
225
        """
226
        rev_to_sha = {}
0.5.64 by John Arbash Meinel
SUCCESS, we now are able to validate the inventory XML.
227
        inv_to_sha = {}
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
228
        def add_sha(d, revision_id, sha1):
229
            if revision_id is None:
0.5.63 by John Arbash Meinel
Moving the validation into part of the reading.
230
                if sha1 is not None:
231
                    raise BzrError('A Null revision should always'
232
                        'have a null sha1 hash')
233
                return
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
234
            if revision_id in d:
0.5.63 by John Arbash Meinel
Moving the validation into part of the reading.
235
                # This really should have been validated as part
236
                # of _validate_revisions but lets do it again
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
237
                if sha1 != d[revision_id]:
0.5.63 by John Arbash Meinel
Moving the validation into part of the reading.
238
                    raise BzrError('** Revision %r referenced with 2 different'
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
239
                            ' sha hashes %s != %s' % (revision_id,
240
                                sha1, d[revision_id]))
0.5.63 by John Arbash Meinel
Moving the validation into part of the reading.
241
            else:
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
242
                d[revision_id] = sha1
0.5.63 by John Arbash Meinel
Moving the validation into part of the reading.
243
244
        # All of the contained revisions were checked
245
        # in _validate_revisions
246
        checked = {}
1793.2.1 by Aaron Bentley
Move revision_tree into BundleInfo
247
        for rev_info in self.revisions:
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
248
            checked[rev_info.revision_id] = True
249
            add_sha(rev_to_sha, rev_info.revision_id, rev_info.sha1)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
250
1793.2.1 by Aaron Bentley
Move revision_tree into BundleInfo
251
        for (rev, rev_info) in zip(self.real_revisions, self.revisions):
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
252
            add_sha(inv_to_sha, rev_info.revision_id, rev_info.inventory_sha1)
0.5.63 by John Arbash Meinel
Moving the validation into part of the reading.
253
0.5.64 by John Arbash Meinel
SUCCESS, we now are able to validate the inventory XML.
254
        count = 0
0.5.63 by John Arbash Meinel
Moving the validation into part of the reading.
255
        missing = {}
6656.1.1 by Martin
Apply 2to3 dict fixer and clean up resulting mess using view helpers
256
        for revision_id, sha1 in viewitems(rev_to_sha):
1185.82.15 by Aaron Bentley
Disabled validate_revisions (needs info it doesn't have), updated API to repos
257
            if repository.has_revision(revision_id):
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
258
                testament = StrictTestament.from_revision(repository,
1185.82.121 by Aaron Bentley
Move calculation of Testament sha1s to Testament
259
                                                          revision_id)
1910.2.55 by Aaron Bentley
Bundle 0.9 uses Testament 3 strict
260
                local_sha1 = self._testament_sha1_from_revision(repository,
261
                                                                revision_id)
0.5.63 by John Arbash Meinel
Moving the validation into part of the reading.
262
                if sha1 != local_sha1:
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
263
                    raise BzrError('sha1 mismatch. For revision id {%s}'
1185.82.130 by Aaron Bentley
Rename changesets to revision bundles
264
                            'local: %s, bundle: %s' % (revision_id, local_sha1, sha1))
0.5.64 by John Arbash Meinel
SUCCESS, we now are able to validate the inventory XML.
265
                else:
266
                    count += 1
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
267
            elif revision_id not in checked:
268
                missing[revision_id] = sha1
0.5.63 by John Arbash Meinel
Moving the validation into part of the reading.
269
270
        if len(missing) > 0:
271
            # I don't know if this is an error yet
272
            warning('Not all revision hashes could be validated.'
273
                    ' Unable validate %d hashes' % len(missing))
1185.82.130 by Aaron Bentley
Rename changesets to revision bundles
274
        mutter('Verified %d sha hashes for the bundle.' % count)
2476.2.1 by John Arbash Meinel
Vastly improve bundle install performance by only validating the bundle one time
275
        self._validated_revisions_against_repo = True
0.5.64 by John Arbash Meinel
SUCCESS, we now are able to validate the inventory XML.
276
1185.82.49 by Aaron Bentley
SPOT fixes, fix inventory validation
277
    def _validate_inventory(self, inv, revision_id):
1185.82.130 by Aaron Bentley
Rename changesets to revision bundles
278
        """At this point we should have generated the BundleTree,
0.5.63 by John Arbash Meinel
Moving the validation into part of the reading.
279
        so build up an inventory, and make sure the hashes match.
280
        """
0.5.64 by John Arbash Meinel
SUCCESS, we now are able to validate the inventory XML.
281
        # Now we should have a complete inventory entry.
0.5.117 by John Arbash Meinel
Almost there. Just need to track down a few remaining bugs.
282
        s = serializer_v5.write_inventory_to_string(inv)
283
        sha1 = sha_string(s)
0.5.64 by John Arbash Meinel
SUCCESS, we now are able to validate the inventory XML.
284
        # Target revision is the last entry in the real_revisions list
1793.2.1 by Aaron Bentley
Move revision_tree into BundleInfo
285
        rev = self.get_revision(revision_id)
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
286
        if rev.revision_id != revision_id:
287
            raise AssertionError()
0.5.64 by John Arbash Meinel
SUCCESS, we now are able to validate the inventory XML.
288
        if sha1 != rev.inventory_sha1:
6855.4.5 by Jelmer Vernooij
Fix more bees, use with rather than try/finally for some files.
289
            with open(',,bogus-inv', 'wb') as f:
4708.2.1 by Martin
Ensure all files opened by bazaar proper are explicitly closed
290
                f.write(s)
1185.82.61 by Aaron Bentley
Downgrade inventory mismatch to warning (source can be inaccurate)
291
            warning('Inventory sha hash mismatch for revision %s. %s'
292
                    ' != %s' % (revision_id, sha1, rev.inventory_sha1))
0.5.64 by John Arbash Meinel
SUCCESS, we now are able to validate the inventory XML.
293
5798.1.2 by Jelmer Vernooij
Fix some tests.
294
    def _validate_revision(self, tree, revision_id):
1793.2.1 by Aaron Bentley
Move revision_tree into BundleInfo
295
        """Make sure all revision entries match their checksum."""
296
5448.2.1 by Martin
Fix some "its" vs. "it's" spelling confusion in bzrlib code... also, ahem, a name in the NEWS file
297
        # This is a mapping from each revision id to its sha hash
1793.2.1 by Aaron Bentley
Move revision_tree into BundleInfo
298
        rev_to_sha1 = {}
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
299
1793.2.1 by Aaron Bentley
Move revision_tree into BundleInfo
300
        rev = self.get_revision(revision_id)
301
        rev_info = self.get_revision_info(revision_id)
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
302
        if not (rev.revision_id == rev_info.revision_id):
303
            raise AssertionError()
304
        if not (rev.revision_id == revision_id):
305
            raise AssertionError()
5798.1.2 by Jelmer Vernooij
Fix some tests.
306
        sha1 = self._testament_sha1(rev, tree)
1793.2.1 by Aaron Bentley
Move revision_tree into BundleInfo
307
        if sha1 != rev_info.sha1:
308
            raise TestamentMismatch(rev.revision_id, rev_info.sha1, sha1)
1963.2.1 by Robey Pointer
remove usage of has_key()
309
        if rev.revision_id in rev_to_sha1:
1793.2.1 by Aaron Bentley
Move revision_tree into BundleInfo
310
            raise BzrError('Revision {%s} given twice in the list'
311
                    % (rev.revision_id))
312
        rev_to_sha1[rev.revision_id] = sha1
313
314
    def _update_tree(self, bundle_tree, revision_id):
315
        """This fills out a BundleTree based on the information
316
        that was read in.
317
318
        :param bundle_tree: A BundleTree to update with the new information.
319
        """
320
321
        def get_rev_id(last_changed, path, kind):
322
            if last_changed is not None:
2309.4.5 by John Arbash Meinel
Change bundle_data to use the sanitizing form of safe_*_id
323
                # last_changed will be a Unicode string because of how it was
324
                # read. Convert it back to utf8.
6630.1.1 by Jelmer Vernooij
Remove deprecated functionality.
325
                changed_revision_id = cache_utf8.encode(last_changed)
1793.2.1 by Aaron Bentley
Move revision_tree into BundleInfo
326
            else:
327
                changed_revision_id = revision_id
328
            bundle_tree.note_last_changed(path, changed_revision_id)
329
            return changed_revision_id
330
331
        def extra_info(info, new_path):
332
            last_changed = None
333
            encoding = None
334
            for info_item in info:
335
                try:
336
                    name, value = info_item.split(':', 1)
337
                except ValueError:
5284.2.1 by Jelmer Vernooij
Avoid string exceptions.
338
                    raise ValueError('Value %r has no colon' % info_item)
1793.2.1 by Aaron Bentley
Move revision_tree into BundleInfo
339
                if name == 'last-changed':
340
                    last_changed = value
341
                elif name == 'executable':
342
                    val = (value == 'yes')
343
                    bundle_tree.note_executable(new_path, val)
344
                elif name == 'target':
345
                    bundle_tree.note_target(new_path, value)
346
                elif name == 'encoding':
347
                    encoding = value
348
            return last_changed, encoding
349
350
        def do_patch(path, lines, encoding):
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
351
            if encoding == 'base64':
1793.2.1 by Aaron Bentley
Move revision_tree into BundleInfo
352
                patch = base64.decodestring(''.join(lines))
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
353
            elif encoding is None:
1793.2.1 by Aaron Bentley
Move revision_tree into BundleInfo
354
                patch =  ''.join(lines)
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
355
            else:
356
                raise ValueError(encoding)
1793.2.1 by Aaron Bentley
Move revision_tree into BundleInfo
357
            bundle_tree.note_patch(path, patch)
358
359
        def renamed(kind, extra, lines):
360
            info = extra.split(' // ')
361
            if len(info) < 2:
362
                raise BzrError('renamed action lines need both a from and to'
363
                        ': %r' % extra)
364
            old_path = info[0]
365
            if info[1].startswith('=> '):
366
                new_path = info[1][3:]
367
            else:
368
                new_path = info[1]
369
370
            bundle_tree.note_rename(old_path, new_path)
371
            last_modified, encoding = extra_info(info[2:], new_path)
372
            revision = get_rev_id(last_modified, new_path, kind)
373
            if lines:
374
                do_patch(new_path, lines, encoding)
375
376
        def removed(kind, extra, lines):
377
            info = extra.split(' // ')
378
            if len(info) > 1:
379
                # TODO: in the future we might allow file ids to be
380
                # given for removed entries
381
                raise BzrError('removed action lines should only have the path'
382
                        ': %r' % extra)
383
            path = info[0]
384
            bundle_tree.note_deletion(path)
385
386
        def added(kind, extra, lines):
387
            info = extra.split(' // ')
388
            if len(info) <= 1:
389
                raise BzrError('add action lines require the path and file id'
390
                        ': %r' % extra)
391
            elif len(info) > 5:
392
                raise BzrError('add action lines have fewer than 5 entries.'
393
                        ': %r' % extra)
394
            path = info[0]
395
            if not info[1].startswith('file-id:'):
396
                raise BzrError('The file-id should follow the path for an add'
397
                        ': %r' % extra)
2294.1.10 by John Arbash Meinel
Switch all apis over to utf8 file ids. All tests pass
398
            # This will be Unicode because of how the stream is read. Turn it
399
            # back into a utf8 file_id
6630.1.1 by Jelmer Vernooij
Remove deprecated functionality.
400
            file_id = cache_utf8.encode(info[1][8:])
1793.2.1 by Aaron Bentley
Move revision_tree into BundleInfo
401
402
            bundle_tree.note_id(file_id, path, kind)
403
            # this will be overridden in extra_info if executable is specified.
404
            bundle_tree.note_executable(path, False)
405
            last_changed, encoding = extra_info(info[2:], path)
406
            revision = get_rev_id(last_changed, path, kind)
407
            if kind == 'directory':
408
                return
409
            do_patch(path, lines, encoding)
410
411
        def modified(kind, extra, lines):
412
            info = extra.split(' // ')
413
            if len(info) < 1:
414
                raise BzrError('modified action lines have at least'
415
                        'the path in them: %r' % extra)
416
            path = info[0]
417
418
            last_modified, encoding = extra_info(info[1:], path)
419
            revision = get_rev_id(last_modified, path, kind)
420
            if lines:
421
                do_patch(path, lines, encoding)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
422
1793.2.1 by Aaron Bentley
Move revision_tree into BundleInfo
423
        valid_actions = {
424
            'renamed':renamed,
425
            'removed':removed,
426
            'added':added,
427
            'modified':modified
428
        }
429
        for action_line, lines in \
430
            self.get_revision_info(revision_id).tree_actions:
431
            first = action_line.find(' ')
432
            if first == -1:
433
                raise BzrError('Bogus action line'
434
                        ' (no opening space): %r' % action_line)
435
            second = action_line.find(' ', first+1)
436
            if second == -1:
437
                raise BzrError('Bogus action line'
438
                        ' (missing second space): %r' % action_line)
439
            action = action_line[:first]
440
            kind = action_line[first+1:second]
441
            if kind not in ('file', 'directory', 'symlink'):
442
                raise BzrError('Bogus action line'
443
                        ' (invalid object kind %r): %r' % (kind, action_line))
444
            extra = action_line[second+1:]
445
446
            if action not in valid_actions:
447
                raise BzrError('Bogus action line'
448
                        ' (unrecognized action): %r' % action_line)
449
            valid_actions[action](kind, extra, lines)
450
2520.4.148 by Aaron Bentley
Updates from review
451
    def install_revisions(self, target_repo, stream_input=True):
452
        """Install revisions and return the target revision
453
454
        :param target_repo: The repository to install into
455
        :param stream_input: Ignored by this implementation.
456
        """
1551.14.4 by Aaron Bentley
Change bundle reader and merge directive to both be 'mergeables'
457
        apply_bundle.install_bundle(target_repo, self)
458
        return self.target
459
2520.4.109 by Aaron Bentley
start work on directive cherry-picking
460
    def get_merge_request(self, target_repo):
461
        """Provide data for performing a merge
462
463
        Returns suggested base, suggested target, and patch verification status
464
        """
465
        return None, self.target, 'inapplicable'
466
1551.14.4 by Aaron Bentley
Change bundle reader and merge directive to both be 'mergeables'
467
1185.82.130 by Aaron Bentley
Rename changesets to revision bundles
468
class BundleTree(Tree):
5798.1.4 by Jelmer Vernooij
Fix bundle tests.
469
1185.82.16 by Aaron Bentley
Ensured revision ID is stored in ChangesetTree inventories
470
    def __init__(self, base_tree, revision_id):
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
471
        self.base_tree = base_tree
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
472
        self._renamed = {} # Mapping from old_path => new_path
473
        self._renamed_r = {} # new_path => old_path
474
        self._new_id = {} # new_path => new_id
475
        self._new_id_r = {} # new_id => new_path
6809.4.16 by Jelmer Vernooij
Bundle fixes.
476
        self._kinds = {} # new_path => kind
0.5.118 by John Arbash Meinel
Got most of test_changeset to work. Still needs work for Aaron's test code.
477
        self._last_changed = {} # new_id => revision_id
1185.82.66 by Aaron Bentley
Handle new executable files
478
        self._executable = {} # new_id => executable value
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
479
        self.patches = {}
1185.82.87 by Aaron Bentley
Got symlink adding working
480
        self._targets = {} # new path => new symlink target
0.5.48 by aaron.bentley at utoronto
Implemented deletion for ChangesetTrees
481
        self.deleted = []
0.5.52 by aaron.bentley at utoronto
Make contents-addressing configurable
482
        self.contents_by_id = True
1185.82.16 by Aaron Bentley
Ensured revision ID is stored in ChangesetTree inventories
483
        self.revision_id = revision_id
0.5.82 by John Arbash Meinel
Lots of changes, changing separators, updating tests, updated ChangesetTree to include text_ids
484
        self._inventory = None
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
485
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
486
    def __str__(self):
487
        return pprint.pformat(self.__dict__)
488
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
489
    def note_rename(self, old_path, new_path):
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
490
        """A file/directory has been renamed from old_path => new_path"""
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
491
        if new_path in self._renamed:
492
            raise AssertionError(new_path)
493
        if old_path in self._renamed_r:
494
            raise AssertionError(old_path)
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
495
        self._renamed[new_path] = old_path
496
        self._renamed_r[old_path] = new_path
497
0.5.64 by John Arbash Meinel
SUCCESS, we now are able to validate the inventory XML.
498
    def note_id(self, new_id, new_path, kind='file'):
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
499
        """Files that don't exist in base need a new id."""
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
500
        self._new_id[new_path] = new_id
501
        self._new_id_r[new_id] = new_path
6809.4.16 by Jelmer Vernooij
Bundle fixes.
502
        self._kinds[new_path] = kind
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
503
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
504
    def note_last_changed(self, file_id, revision_id):
1963.2.1 by Robey Pointer
remove usage of has_key()
505
        if (file_id in self._last_changed
0.5.118 by John Arbash Meinel
Got most of test_changeset to work. Still needs work for Aaron's test code.
506
                and self._last_changed[file_id] != revision_id):
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
507
            raise BzrError('Mismatched last-changed revision for file_id {%s}'
0.5.82 by John Arbash Meinel
Lots of changes, changing separators, updating tests, updated ChangesetTree to include text_ids
508
                    ': %s != %s' % (file_id,
0.5.118 by John Arbash Meinel
Got most of test_changeset to work. Still needs work for Aaron's test code.
509
                                    self._last_changed[file_id],
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
510
                                    revision_id))
0.5.118 by John Arbash Meinel
Got most of test_changeset to work. Still needs work for Aaron's test code.
511
        self._last_changed[file_id] = revision_id
0.5.82 by John Arbash Meinel
Lots of changes, changing separators, updating tests, updated ChangesetTree to include text_ids
512
0.5.44 by aaron.bentley at utoronto
Got get_file working for new files
513
    def note_patch(self, new_path, patch):
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
514
        """There is a patch for a given filename."""
1731.1.55 by Aaron Bentley
Fix bundle handling
515
        self.patches[new_path] = patch
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
516
1185.82.87 by Aaron Bentley
Got symlink adding working
517
    def note_target(self, new_path, target):
518
        """The symlink at the new path has the given target"""
1731.1.55 by Aaron Bentley
Fix bundle handling
519
        self._targets[new_path] = target
1185.82.87 by Aaron Bentley
Got symlink adding working
520
0.5.48 by aaron.bentley at utoronto
Implemented deletion for ChangesetTrees
521
    def note_deletion(self, old_path):
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
522
        """The file at old_path has been deleted."""
1731.1.55 by Aaron Bentley
Fix bundle handling
523
        self.deleted.append(old_path)
0.5.48 by aaron.bentley at utoronto
Implemented deletion for ChangesetTrees
524
1185.82.66 by Aaron Bentley
Handle new executable files
525
    def note_executable(self, new_path, executable):
1731.1.55 by Aaron Bentley
Fix bundle handling
526
        self._executable[new_path] = executable
1185.82.66 by Aaron Bentley
Handle new executable files
527
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
528
    def old_path(self, new_path):
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
529
        """Get the old_path (path in the base_tree) for the file at new_path"""
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
530
        if new_path[:1] in ('\\', '/'):
531
            raise ValueError(new_path)
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
532
        old_path = self._renamed.get(new_path)
533
        if old_path is not None:
534
            return old_path
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
535
        dirname, basename = os.path.split(new_path)
0.5.56 by John Arbash Meinel
A couple more fixups, it seems actually capable now of writing out a changeset, and reading it back.
536
        # dirname is not '' doesn't work, because
537
        # dirname may be a unicode entry, and is
538
        # requires the objects to be identical
539
        if dirname != '':
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
540
            old_dir = self.old_path(dirname)
541
            if old_dir is None:
0.5.42 by aaron.bentley at utoronto
Improved rename handling
542
                old_path = None
543
            else:
1711.4.19 by John Arbash Meinel
Bundles were still using os.path.join to compute paths rather than osutils.pathjoin
544
                old_path = pathjoin(old_dir, basename)
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
545
        else:
0.5.42 by aaron.bentley at utoronto
Improved rename handling
546
            old_path = new_path
547
        #If the new path wasn't in renamed, the old one shouldn't be in
548
        #renamed_r
1963.2.1 by Robey Pointer
remove usage of has_key()
549
        if old_path in self._renamed_r:
0.5.42 by aaron.bentley at utoronto
Improved rename handling
550
            return None
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
551
        return old_path
0.5.42 by aaron.bentley at utoronto
Improved rename handling
552
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
553
    def new_path(self, old_path):
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
554
        """Get the new_path (path in the target_tree) for the file at old_path
555
        in the base tree.
556
        """
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
557
        if old_path[:1] in ('\\', '/'):
558
            raise ValueError(old_path)
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
559
        new_path = self._renamed_r.get(old_path)
560
        if new_path is not None:
561
            return new_path
1963.2.1 by Robey Pointer
remove usage of has_key()
562
        if new_path in self._renamed:
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
563
            return None
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
564
        dirname, basename = os.path.split(old_path)
0.5.81 by John Arbash Meinel
Cleaning up from pychecker.
565
        if dirname != '':
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
566
            new_dir = self.new_path(dirname)
567
            if new_dir is None:
0.5.42 by aaron.bentley at utoronto
Improved rename handling
568
                new_path = None
569
            else:
1711.4.19 by John Arbash Meinel
Bundles were still using os.path.join to compute paths rather than osutils.pathjoin
570
                new_path = pathjoin(new_dir, basename)
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
571
        else:
0.5.42 by aaron.bentley at utoronto
Improved rename handling
572
            new_path = old_path
573
        #If the old path wasn't in renamed, the new one shouldn't be in
574
        #renamed_r
1963.2.1 by Robey Pointer
remove usage of has_key()
575
        if new_path in self._renamed:
0.5.42 by aaron.bentley at utoronto
Improved rename handling
576
            return None
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
577
        return new_path
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
578
6405.2.7 by Jelmer Vernooij
Fix more tests.
579
    def get_root_id(self):
580
        return self.path2id('')
581
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
582
    def path2id(self, path):
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
583
        """Return the id of the file present at path in the target tree."""
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
584
        file_id = self._new_id.get(path)
585
        if file_id is not None:
586
            return file_id
0.5.43 by aaron.bentley at utoronto
Handled moves and adds properly
587
        old_path = self.old_path(path)
588
        if old_path is None:
589
            return None
0.5.48 by aaron.bentley at utoronto
Implemented deletion for ChangesetTrees
590
        if old_path in self.deleted:
591
            return None
6405.2.7 by Jelmer Vernooij
Fix more tests.
592
        return self.base_tree.path2id(old_path)
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
593
594
    def id2path(self, file_id):
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
595
        """Return the new path in the target tree of the file with id file_id"""
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
596
        path = self._new_id_r.get(file_id)
597
        if path is not None:
598
            return path
0.5.43 by aaron.bentley at utoronto
Handled moves and adds properly
599
        old_path = self.base_tree.id2path(file_id)
600
        if old_path is None:
601
            return None
0.5.48 by aaron.bentley at utoronto
Implemented deletion for ChangesetTrees
602
        if old_path in self.deleted:
603
            return None
0.5.43 by aaron.bentley at utoronto
Handled moves and adds properly
604
        return self.new_path(old_path)
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
605
0.5.52 by aaron.bentley at utoronto
Make contents-addressing configurable
606
    def old_contents_id(self, file_id):
1185.82.94 by Aaron Bentley
Remove old chatter
607
        """Return the id in the base_tree for the given file_id.
608
        Return None if the file did not exist in base.
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
609
        """
0.5.52 by aaron.bentley at utoronto
Make contents-addressing configurable
610
        if self.contents_by_id:
611
            if self.base_tree.has_id(file_id):
612
                return file_id
613
            else:
614
                return None
615
        new_path = self.id2path(file_id)
616
        return self.base_tree.path2id(new_path)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
617
6809.4.5 by Jelmer Vernooij
Swap arguments for get_file_*.
618
    def get_file(self, path, file_id=None):
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
619
        """Return a file-like object containing the new contents of the
620
        file given by file_id.
621
622
        TODO:   It might be nice if this actually generated an entry
623
                in the text-store, so that the file contents would
624
                then be cached.
625
        """
6809.4.16 by Jelmer Vernooij
Bundle fixes.
626
        if file_id is None:
627
            file_id = self.path2id(path)
628
        base_id = self.old_contents_id(file_id)
629
        if (base_id is not None and
630
            base_id != self.base_tree.get_root_id()):
6928.1.2 by Jelmer Vernooij
Fix path specification.
631
            old_path = self.old_path(path)
6809.4.16 by Jelmer Vernooij
Bundle fixes.
632
            patch_original = self.base_tree.get_file(
6928.1.2 by Jelmer Vernooij
Fix path specification.
633
                    old_path, base_id)
6809.4.16 by Jelmer Vernooij
Bundle fixes.
634
        else:
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
635
            patch_original = None
6809.4.5 by Jelmer Vernooij
Swap arguments for get_file_*.
636
        file_patch = self.patches.get(path)
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
637
        if file_patch is None:
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
638
            if (patch_original is None and
6809.4.20 by Jelmer Vernooij
Fix tests.
639
                self.kind(path, file_id) == 'directory'):
6621.22.2 by Martin
Use BytesIO or StringIO from bzrlib.sixish
640
                return BytesIO()
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
641
            if patch_original is None:
6809.4.16 by Jelmer Vernooij
Bundle fixes.
642
                raise AssertionError("None: %s" % file_id)
0.5.44 by aaron.bentley at utoronto
Got get_file working for new files
643
            return patch_original
0.5.94 by Aaron Bentley
Switched to native patch application, added tests for terminating newlines
644
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
645
        if file_patch.startswith('\\'):
646
            raise ValueError(
6809.4.16 by Jelmer Vernooij
Bundle fixes.
647
                'Malformed patch for %s, %r' % (file_id, file_patch))
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
648
        return patched_file(file_patch, patch_original)
649
6809.4.7 by Jelmer Vernooij
Swap arguments for get_symlink_target and kind/stored_kind.
650
    def get_symlink_target(self, path, file_id=None):
1185.82.87 by Aaron Bentley
Got symlink adding working
651
        try:
5858.1.1 by Jelmer Vernooij
Support optional path argument to Tree.get_symlink_target.
652
            return self._targets[path]
1185.82.87 by Aaron Bentley
Got symlink adding working
653
        except KeyError:
6928.1.2 by Jelmer Vernooij
Fix path specification.
654
            old_path = self.old_path(path)
655
            return self.base_tree.get_symlink_target(old_path, file_id)
1185.82.87 by Aaron Bentley
Got symlink adding working
656
6809.4.7 by Jelmer Vernooij
Swap arguments for get_symlink_target and kind/stored_kind.
657
    def kind(self, path, file_id=None):
6809.4.20 by Jelmer Vernooij
Fix tests.
658
        try:
6809.4.16 by Jelmer Vernooij
Bundle fixes.
659
            return self._kinds[path]
6809.4.20 by Jelmer Vernooij
Fix tests.
660
        except KeyError:
6928.1.2 by Jelmer Vernooij
Fix path specification.
661
            old_path = self.old_path(path)
662
            return self.base_tree.kind(old_path, file_id)
6445.2.4 by Jelmer Vernooij
Extend BundleTree, avoid inventory.
663
6809.4.5 by Jelmer Vernooij
Swap arguments for get_file_*.
664
    def get_file_revision(self, path, file_id=None):
6405.2.7 by Jelmer Vernooij
Fix more tests.
665
        if path in self._last_changed:
666
            return self._last_changed[path]
667
        else:
6928.1.2 by Jelmer Vernooij
Fix path specification.
668
            old_path = self.old_path(path)
669
            return self.base_tree.get_file_revision(old_path, file_id)
6405.2.7 by Jelmer Vernooij
Fix more tests.
670
6809.4.4 by Jelmer Vernooij
Swap arguments for Tree.is_executable.
671
    def is_executable(self, path, file_id=None):
1185.82.93 by Aaron Bentley
Code cleanup
672
        if path in self._executable:
673
            return self._executable[path]
1185.82.66 by Aaron Bentley
Handle new executable files
674
        else:
6928.1.2 by Jelmer Vernooij
Fix path specification.
675
            old_path = self.old_path(path)
676
            return self.base_tree.is_executable(old_path, file_id)
1185.82.66 by Aaron Bentley
Handle new executable files
677
6809.4.16 by Jelmer Vernooij
Bundle fixes.
678
    def get_last_changed(self, path, file_id=None):
1185.82.95 by Aaron Bentley
Restore path-orientation of ChangesetTree
679
        if path in self._last_changed:
680
            return self._last_changed[path]
6928.1.2 by Jelmer Vernooij
Fix path specification.
681
        old_path = self.old_path(path)
682
        return self.base_tree.get_file_revision(old_path, file_id)
0.5.82 by John Arbash Meinel
Lots of changes, changing separators, updating tests, updated ChangesetTree to include text_ids
683
6809.4.16 by Jelmer Vernooij
Bundle fixes.
684
    def get_size_and_sha1(self, new_path, file_id=None):
0.5.64 by John Arbash Meinel
SUCCESS, we now are able to validate the inventory XML.
685
        """Return the size and sha1 hash of the given file id.
686
        If the file was not locally modified, this is extracted
687
        from the base_tree. Rather than re-reading the file.
688
        """
689
        if new_path is None:
690
            return None, None
691
        if new_path not in self.patches:
692
            # If the entry does not have a patch, then the
693
            # contents must be the same as in the base_tree
6928.1.2 by Jelmer Vernooij
Fix path specification.
694
            base_path = self.old_path(new_path)
6809.4.5 by Jelmer Vernooij
Swap arguments for get_file_*.
695
            text_size = self.base_tree.get_file_size(base_path, file_id)
696
            text_sha1 = self.base_tree.get_file_sha1(base_path, file_id)
6468.2.6 by Jelmer Vernooij
Use tree API.
697
            return text_size, text_sha1
6809.4.7 by Jelmer Vernooij
Swap arguments for get_symlink_target and kind/stored_kind.
698
        fileobj = self.get_file(new_path, file_id)
0.5.94 by Aaron Bentley
Switched to native patch application, added tests for terminating newlines
699
        content = fileobj.read()
0.5.64 by John Arbash Meinel
SUCCESS, we now are able to validate the inventory XML.
700
        return len(content), sha_string(content)
701
0.5.82 by John Arbash Meinel
Lots of changes, changing separators, updating tests, updated ChangesetTree to include text_ids
702
    def _get_inventory(self):
1185.82.130 by Aaron Bentley
Rename changesets to revision bundles
703
        """Build up the inventory entry for the BundleTree.
0.5.82 by John Arbash Meinel
Lots of changes, changing separators, updating tests, updated ChangesetTree to include text_ids
704
705
        This need to be called before ever accessing self.inventory
706
        """
707
        from os.path import dirname, basename
1731.1.62 by Aaron Bentley
Changes from review comments
708
        inv = Inventory(None, self.revision_id)
0.5.82 by John Arbash Meinel
Lots of changes, changing separators, updating tests, updated ChangesetTree to include text_ids
709
6809.4.20 by Jelmer Vernooij
Fix tests.
710
        def add_entry(path, file_id):
1731.1.55 by Aaron Bentley
Fix bundle handling
711
            if path == '':
712
                parent_id = None
0.5.82 by John Arbash Meinel
Lots of changes, changing separators, updating tests, updated ChangesetTree to include text_ids
713
            else:
1731.1.55 by Aaron Bentley
Fix bundle handling
714
                parent_path = dirname(path)
0.5.82 by John Arbash Meinel
Lots of changes, changing separators, updating tests, updated ChangesetTree to include text_ids
715
                parent_id = self.path2id(parent_path)
716
6809.4.20 by Jelmer Vernooij
Fix tests.
717
            kind = self.kind(path, file_id)
718
            revision_id = self.get_last_changed(path, file_id)
0.5.83 by John Arbash Meinel
Tests pass. Now ChangesetTree has it's own inventory.
719
720
            name = basename(path)
0.5.116 by John Arbash Meinel
Fixed a bug based on the new InventoryEntry separation.
721
            if kind == 'directory':
722
                ie = InventoryDirectory(file_id, name, parent_id)
723
            elif kind == 'file':
724
                ie = InventoryFile(file_id, name, parent_id)
6809.4.20 by Jelmer Vernooij
Fix tests.
725
                ie.executable = self.is_executable(path, file_id)
0.5.116 by John Arbash Meinel
Fixed a bug based on the new InventoryEntry separation.
726
            elif kind == 'symlink':
727
                ie = InventoryLink(file_id, name, parent_id)
6809.4.20 by Jelmer Vernooij
Fix tests.
728
                ie.symlink_target = self.get_symlink_target(path, file_id)
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
729
            ie.revision = revision_id
730
5365.2.2 by Andrew Bennetts
Fix test failures for bundles and upgrades.
731
            if kind == 'file':
6809.4.21 by Jelmer Vernooij
Fix long lines.
732
                ie.text_size, ie.text_sha1 = self.get_size_and_sha1(
733
                        path, file_id)
5365.2.2 by Andrew Bennetts
Fix test failures for bundles and upgrades.
734
                if ie.text_size is None:
735
                    raise BzrError(
736
                        'Got a text_size of None for file_id %r' % file_id)
0.5.82 by John Arbash Meinel
Lots of changes, changing separators, updating tests, updated ChangesetTree to include text_ids
737
            inv.add(ie)
738
0.6.1 by Aaron Bentley
Fleshed out MockTree, fixed all test failures
739
        sorted_entries = self.sorted_path_id()
740
        for path, file_id in sorted_entries:
6809.4.20 by Jelmer Vernooij
Fix tests.
741
            add_entry(path, file_id)
0.5.82 by John Arbash Meinel
Lots of changes, changing separators, updating tests, updated ChangesetTree to include text_ids
742
743
        return inv
744
745
    # Have to overload the inherited inventory property
746
    # because _get_inventory is only called in the parent.
747
    # Reading the docs, property() objects do not use
748
    # overloading, they use the function as it was defined
749
    # at that instant
750
    inventory = property(_get_inventory)
0.5.64 by John Arbash Meinel
SUCCESS, we now are able to validate the inventory XML.
751
6405.2.10 by Jelmer Vernooij
Fix more tests.
752
    root_inventory = property(_get_inventory)
753
6468.2.8 by Jelmer Vernooij
Fix remaining test.
754
    def all_file_ids(self):
6619.3.12 by Jelmer Vernooij
Use 2to3 set_literal fixer.
755
        return {entry.file_id for path, entry in self.inventory.iter_entries()}
6468.2.8 by Jelmer Vernooij
Fix remaining test.
756
6825.5.1 by Jelmer Vernooij
Implement Tree.all_versioned_paths.
757
    def all_versioned_paths(self):
758
        return {path for path, entry in self.inventory.iter_entries()}
759
5798.1.4 by Jelmer Vernooij
Fix bundle tests.
760
    def list_files(self, include_root=False, from_dir=None, recursive=True):
761
        # The only files returned by this are those from the version
762
        inv = self.inventory
763
        if from_dir is None:
764
            from_dir_id = None
765
        else:
766
            from_dir_id = inv.path2id(from_dir)
767
            if from_dir_id is None:
768
                # Directory not versioned
769
                return
770
        entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
771
        if inv.root is not None and not include_root and from_dir is None:
772
            # skip the root for compatability with the current apis.
6634.2.1 by Martin
Apply 2to3 next fixer and make compatible
773
            next(entries)
5798.1.4 by Jelmer Vernooij
Fix bundle tests.
774
        for path, entry in entries:
775
            yield path, 'V', entry.kind, entry.file_id, entry
776
0.6.1 by Aaron Bentley
Fleshed out MockTree, fixed all test failures
777
    def sorted_path_id(self):
778
        paths = []
6656.1.1 by Martin
Apply 2to3 dict fixer and clean up resulting mess using view helpers
779
        for result in viewitems(self._new_id):
0.6.1 by Aaron Bentley
Fleshed out MockTree, fixed all test failures
780
            paths.append(result)
5837.2.2 by Jelmer Vernooij
Fix more uses of Tree.__iter__
781
        for id in self.base_tree.all_file_ids():
0.6.1 by Aaron Bentley
Fleshed out MockTree, fixed all test failures
782
            path = self.id2path(id)
783
            if path is None:
784
                continue
785
            paths.append((path, id))
786
        paths.sort()
787
        return paths
788
1185.82.123 by Aaron Bentley
Cleanups to prepare for review
789
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
790
def patched_file(file_patch, original):
0.5.94 by Aaron Bentley
Switched to native patch application, added tests for terminating newlines
791
    """Produce a file-like object with the patched version of a text"""
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
792
    from breezy.patches import iter_patched
793
    from breezy.iterablefile import IterableFile
0.5.94 by Aaron Bentley
Switched to native patch application, added tests for terminating newlines
794
    if file_patch == "":
795
        return IterableFile(())
1848.1.1 by John Arbash Meinel
fix bug in bundle handling of binary files with just '\r' in them.
796
    # string.splitlines(True) also splits on '\r', but the iter_patched code
797
    # only expects to iterate over '\n' style lines
798
    return IterableFile(iter_patched(original,
6621.22.2 by Martin
Use BytesIO or StringIO from bzrlib.sixish
799
                BytesIO(file_patch).readlines()))