/brz/remove-bazaar

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