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