/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
1
#!/usr/bin/env python
2
"""\
3
Read in a changeset output, and process it into a Changeset object.
4
"""
5
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
6
import os
7
import pprint
8
from cStringIO import StringIO
1185.82.34 by Aaron Bentley
Reactivated revision sha1 check
9
from sha import sha
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
10
0.5.82 by John Arbash Meinel
Lots of changes, changing separators, updating tests, updated ChangesetTree to include text_ids
11
from bzrlib.tree import Tree
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
12
from bzrlib.trace import mutter, warning
1185.82.34 by Aaron Bentley
Reactivated revision sha1 check
13
from bzrlib.testament import Testament
0.5.62 by John Arbash Meinel
Doing some internal validation before allowing processing to continue, additional checks at the command level.
14
from bzrlib.errors import BzrError
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
15
from bzrlib.xml5 import serializer_v5
16
from bzrlib.osutils import sha_file, sha_string
17
from bzrlib.revision import Revision
0.5.116 by John Arbash Meinel
Fixed a bug based on the new InventoryEntry separation.
18
from bzrlib.inventory import (Inventory, InventoryEntry,
19
                              InventoryDirectory, InventoryFile,
20
                              InventoryLink)
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
21
1185.82.15 by Aaron Bentley
Disabled validate_revisions (needs info it doesn't have), updated API to repos
22
from bzrlib.changeset.common import (decode, get_header, header_str,
23
                                     testament_sha1)
0.5.57 by John Arbash Meinel
Simplified the header, only output base if it is not the expected one.
24
0.5.9 by John Arbash Meinel
Now adding the patch information to the ChangesetInfo
25
class BadChangeset(Exception): pass
26
class MalformedHeader(BadChangeset): pass
27
class MalformedPatches(BadChangeset): pass
28
class MalformedFooter(BadChangeset): pass
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
29
0.5.11 by John Arbash Meinel
Working on properly representing renames.
30
def _unescape(name):
31
    """Now we want to find the filename effected.
32
    Unfortunately the filename is written out as
33
    repr(filename), which means that it surrounds
34
    the name with quotes which may be single or double
35
    (single is preferred unless there is a single quote in
36
    the filename). And some characters will be escaped.
37
38
    TODO:   There has to be some pythonic way of undo-ing the
39
            representation of a string rather than using eval.
40
    """
41
    delimiter = name[0]
42
    if name[-1] != delimiter:
43
        raise BadChangeset('Could not properly parse the'
44
                ' filename: %r' % name)
45
    # We need to handle escaped hexadecimals too.
46
    return name[1:-1].replace('\"', '"').replace("\'", "'")
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
47
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
48
class RevisionInfo(object):
49
    """Gets filled out for each revision object that is read.
50
    """
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
51
    def __init__(self, revision_id):
52
        self.revision_id = revision_id
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
53
        self.sha1 = None
54
        self.committer = None
0.5.39 by John Arbash Meinel
(broken) Working on changing the processing to use a ChangesetTree.
55
        self.date = None
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
56
        self.timestamp = None
57
        self.timezone = None
58
        self.inventory_sha1 = None
1185.82.60 by Aaron Bentley
Only include revision ids in inventories that originally had them
59
        self.inventory_has_revision = None
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
60
1185.82.27 by Aaron Bentley
Fixed most revision attribute handling
61
        self.parent_ids = None
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
62
        self.message = None
1185.82.27 by Aaron Bentley
Fixed most revision attribute handling
63
        self.properties = None
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
64
65
    def __str__(self):
66
        return pprint.pformat(self.__dict__)
67
0.5.37 by John Arbash Meinel
Made read_changeset able to spit out 'Revision' entities.
68
    def as_revision(self):
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
69
        rev = Revision(revision_id=self.revision_id,
0.5.37 by John Arbash Meinel
Made read_changeset able to spit out 'Revision' entities.
70
            committer=self.committer,
71
            timestamp=float(self.timestamp),
72
            timezone=int(self.timezone),
73
            inventory_sha1=self.inventory_sha1,
74
            message='\n'.join(self.message))
75
1185.82.28 by Aaron Bentley
Got parent_id handling working
76
        if self.parent_ids:
77
            rev.parent_ids.extend(self.parent_ids)
1185.82.35 by Aaron Bentley
Read revision properties
78
1185.82.59 by Aaron Bentley
Behave properly when there are no properties in a revision
79
        if self.properties:
80
            for property in self.properties:
81
                key_end = property.find(': ')
82
                assert key_end is not None
83
                key = property[:key_end].encode('utf-8')
84
                value = property[key_end+2:].encode('utf-8')
85
                rev.properties[key] = value
1185.82.35 by Aaron Bentley
Read revision properties
86
0.5.37 by John Arbash Meinel
Made read_changeset able to spit out 'Revision' entities.
87
        return rev
88
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
89
class ChangesetInfo(object):
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
90
    """This contains the meta information. Stuff that allows you to
91
    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.
92
    """
93
    def __init__(self):
94
        self.committer = None
95
        self.date = None
0.5.17 by John Arbash Meinel
adding apply-changset, plus more meta information.
96
        self.message = None
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
97
        self.base = None
98
        self.base_sha1 = None
99
0.5.39 by John Arbash Meinel
(broken) Working on changing the processing to use a ChangesetTree.
100
        # A list of RevisionInfo objects
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
101
        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.
102
1185.82.48 by Aaron Bentley
Inching closer to supporting multiple revisions per changeset
103
        self.actions = {}
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.
104
105
        # The next entries are created during complete_info() and
106
        # other post-read functions.
107
108
        # A list of real Revision objects
109
        self.real_revisions = []
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
110
111
        self.timestamp = None
112
        self.timezone = None
0.5.15 by John Arbash Meinel
Created an apply-changeset function, and modified output for better parsing.
113
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
114
    def __str__(self):
115
        return pprint.pformat(self.__dict__)
116
0.5.39 by John Arbash Meinel
(broken) Working on changing the processing to use a ChangesetTree.
117
    def complete_info(self):
118
        """This makes sure that all information is properly
119
        split up, based on the assumptions that can be made
120
        when information is missing.
121
        """
1185.82.13 by Aaron Bentley
Got old changeset tests running
122
        from bzrlib.changeset.common 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.
123
        # Put in all of the guessable information.
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
124
        if not self.timestamp and self.date:
0.5.81 by John Arbash Meinel
Cleaning up from pychecker.
125
            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.
126
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
        self.real_revisions = []
0.5.39 by John Arbash Meinel
(broken) Working on changing the processing to use a ChangesetTree.
128
        for rev in self.revisions:
0.5.60 by John Arbash Meinel
read_changeset now parses the date: subheader of revisions correctly.
129
            if rev.timestamp is None:
130
                if rev.date is not None:
131
                    rev.timestamp, rev.timezone = \
0.5.81 by John Arbash Meinel
Cleaning up from pychecker.
132
                            unpack_highres_date(rev.date)
0.5.60 by John Arbash Meinel
read_changeset now parses the date: subheader of revisions correctly.
133
                else:
134
                    rev.timestamp = self.timestamp
135
                    rev.timezone = self.timezone
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
136
            if rev.message is None and self.message:
137
                rev.message = self.message
138
            if rev.committer is None and self.committer:
139
                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.
140
            self.real_revisions.append(rev.as_revision())
141
1185.82.40 by Aaron Bentley
Started work on testing install_revisions/handling empty changesets
142
        if self.base is None and len(self.real_revisions) > 0:
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.
143
            # When we don't have a base, then the real base
144
            # is the first parent of the first revision listed
145
            rev = self.real_revisions[0]
1185.82.48 by Aaron Bentley
Inching closer to supporting multiple revisions per changeset
146
            self.base = self.get_base(rev)
147
148
    def get_base(self, revision):
149
        if len(revision.parent_ids) == 0:
150
            # There is no base listed, and
151
            # the lowest revision doesn't have a parent
152
            # so this is probably against the empty tree
153
            # and thus base truly is None
154
            return None
155
        else:
156
            return revision.parent_ids[0]
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
0.5.67 by John Arbash Meinel
Working on apply_changeset
158
    def _get_target(self):
0.5.81 by John Arbash Meinel
Cleaning up from pychecker.
159
        """Return the target revision."""
0.5.67 by John Arbash Meinel
Working on apply_changeset
160
        if len(self.real_revisions) > 0:
1185.82.48 by Aaron Bentley
Inching closer to supporting multiple revisions per changeset
161
            return self.real_revisions[0].revision_id
0.5.67 by John Arbash Meinel
Working on apply_changeset
162
        elif len(self.revisions) > 0:
1185.82.48 by Aaron Bentley
Inching closer to supporting multiple revisions per changeset
163
            return self.revisions[0].revision_id
0.5.67 by John Arbash Meinel
Working on apply_changeset
164
        return None
165
166
    target = property(_get_target, doc='The target revision id')
167
1185.82.49 by Aaron Bentley
SPOT fixes, fix inventory validation
168
    def get_revision(self, revision_id):
169
        for r in self.real_revisions:
170
            if r.revision_id == revision_id:
171
                return r
172
        raise KeyError(revision_id)
173
174
    def get_revision_info(self, revision_id):
175
        for r in self.revisions:
176
            if r.revision_id == revision_id:
177
                return r
178
        raise KeyError(revision_id)
179
180
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
181
class ChangesetReader(object):
0.5.9 by John Arbash Meinel
Now adding the patch information to the ChangesetInfo
182
    """This class reads in a changeset from a file, and returns
183
    a Changeset object, which can then be applied against a tree.
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
184
    """
185
    def __init__(self, from_file):
186
        """Read in the changeset from the file.
187
188
        :param from_file: A file-like object (must have iterator support).
189
        """
190
        object.__init__(self)
1185.82.50 by Aaron Bentley
Avoid overwriting action lines
191
        self.from_file = iter(from_file)
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
192
        self._next_line = 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.
193
        
194
        self.info = ChangesetInfo()
195
        # We put the actual inventory ids in the footer, so that the patch
196
        # is easier to read for humans.
197
        # Unfortunately, that means we need to read everything before we
198
        # can create a proper changeset.
0.5.62 by John Arbash Meinel
Doing some internal validation before allowing processing to continue, additional checks at the command level.
199
        self._read()
200
        self._validate()
201
202
    def _read(self):
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
203
        self._read_header()
1185.82.47 by Aaron Bentley
Ensure all intended rev_ids end up in the revision
204
        while self._next_line is not None:
205
            self._read_revision_header()
1185.82.48 by Aaron Bentley
Inching closer to supporting multiple revisions per changeset
206
            if self._next_line is None:
207
                break
1185.82.47 by Aaron Bentley
Ensure all intended rev_ids end up in the revision
208
            self._read_patches()
209
            self._read_footer()
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
210
0.5.62 by John Arbash Meinel
Doing some internal validation before allowing processing to continue, additional checks at the command level.
211
    def _validate(self):
212
        """Make sure that the information read in makes sense
213
        and passes appropriate checksums.
214
        """
215
        # Fill in all the missing blanks for the revisions
216
        # and generate the real_revisions list.
217
        self.info.complete_info()
218
1185.82.49 by Aaron Bentley
SPOT fixes, fix inventory validation
219
    def _validate_revision(self, inventory, revision_id):
0.5.62 by John Arbash Meinel
Doing some internal validation before allowing processing to continue, additional checks at the command level.
220
        """Make sure all revision entries match their checksum."""
221
222
        # This is a mapping from each revision id to it's sha hash
223
        rev_to_sha1 = {}
1185.82.49 by Aaron Bentley
SPOT fixes, fix inventory validation
224
        
225
        rev = self.info.get_revision(revision_id)
226
        rev_info = self.info.get_revision_info(revision_id)
227
        assert rev.revision_id == rev_info.revision_id
1185.82.50 by Aaron Bentley
Avoid overwriting action lines
228
        assert rev.revision_id == revision_id
1185.82.49 by Aaron Bentley
SPOT fixes, fix inventory validation
229
        sha1 = sha(Testament(rev, inventory).as_short_text()).hexdigest()
230
        if sha1 != rev_info.sha1:
231
            raise BzrError('Revision checksum mismatch.'
232
                ' For revision_id {%s} supplied sha1 (%s) != measured (%s)'
233
                % (rev.revision_id, rev_info.sha1, sha1))
234
        if rev_to_sha1.has_key(rev.revision_id):
235
            raise BzrError('Revision {%s} given twice in the list'
236
                    % (rev.revision_id))
237
        rev_to_sha1[rev.revision_id] = sha1
0.5.62 by John Arbash Meinel
Doing some internal validation before allowing processing to continue, additional checks at the command level.
238
239
        # Now that we've checked all the sha1 sums, we can make sure that
240
        # at least for the small list we have, all of the references are
241
        # valid.
0.5.118 by John Arbash Meinel
Got most of test_changeset to work. Still needs work for Aaron's test code.
242
        ## TODO: Bring this back
243
        ## for rev in self.info.real_revisions:
244
        ##     for p_id in rev.parent_ids:
245
        ##         if p_id in rev_to_sha1:
246
        ##             if parent.revision_sha1 != rev_to_sha1[p_id]:
247
        ##                 raise BzrError('Parent revision checksum mismatch.'
248
        ##                         ' A parent was referenced with an'
249
        ##                         ' incorrect checksum'
250
        ##                         ': {%r} %s != %s' % (parent.revision_id,
251
        ##                                     parent.revision_sha1,
252
        ##                                     rev_to_sha1[parent.revision_id]))
0.5.63 by John Arbash Meinel
Moving the validation into part of the reading.
253
1185.82.15 by Aaron Bentley
Disabled validate_revisions (needs info it doesn't have), updated API to repos
254
    def _validate_references_from_repository(self, repository):
255
        """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.
256
        revisions we care about, go through and validate all of them
257
        that we can.
258
        """
259
        rev_to_sha = {}
0.5.64 by John Arbash Meinel
SUCCESS, we now are able to validate the inventory XML.
260
        inv_to_sha = {}
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
261
        def add_sha(d, revision_id, sha1):
262
            if revision_id is None:
0.5.63 by John Arbash Meinel
Moving the validation into part of the reading.
263
                if sha1 is not None:
264
                    raise BzrError('A Null revision should always'
265
                        'have a null sha1 hash')
266
                return
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
267
            if revision_id in d:
0.5.63 by John Arbash Meinel
Moving the validation into part of the reading.
268
                # This really should have been validated as part
269
                # 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.
270
                if sha1 != d[revision_id]:
0.5.63 by John Arbash Meinel
Moving the validation into part of the reading.
271
                    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.
272
                            ' sha hashes %s != %s' % (revision_id,
273
                                sha1, d[revision_id]))
0.5.63 by John Arbash Meinel
Moving the validation into part of the reading.
274
            else:
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
275
                d[revision_id] = sha1
0.5.63 by John Arbash Meinel
Moving the validation into part of the reading.
276
277
        # All of the contained revisions were checked
278
        # in _validate_revisions
279
        checked = {}
280
        for rev_info in self.info.revisions:
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
281
            checked[rev_info.revision_id] = True
282
            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.
283
                
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
284
        for (rev, rev_info) in zip(self.info.real_revisions, self.info.revisions):
285
            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.
286
0.5.64 by John Arbash Meinel
SUCCESS, we now are able to validate the inventory XML.
287
        count = 0
0.5.63 by John Arbash Meinel
Moving the validation into part of the reading.
288
        missing = {}
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
289
        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
290
            if repository.has_revision(revision_id):
291
                local_sha1 = testament_sha1(repository, revision_id)
0.5.63 by John Arbash Meinel
Moving the validation into part of the reading.
292
                if sha1 != local_sha1:
0.5.64 by John Arbash Meinel
SUCCESS, we now are able to validate the inventory XML.
293
                    raise BzrError('sha1 mismatch. For revision id {%s}' 
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
294
                            'local: %s, cset: %s' % (revision_id, local_sha1, sha1))
0.5.64 by John Arbash Meinel
SUCCESS, we now are able to validate the inventory XML.
295
                else:
296
                    count += 1
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
297
            elif revision_id not in checked:
298
                missing[revision_id] = sha1
0.5.63 by John Arbash Meinel
Moving the validation into part of the reading.
299
0.5.64 by John Arbash Meinel
SUCCESS, we now are able to validate the inventory XML.
300
        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
301
            if repository.has_revision(inv_id):
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
302
                # TODO: Currently branch.get_inventory_sha1() just returns the value
303
                # that is stored in the revision text. Which is *really* bogus, because
304
                # that means we aren't validating the actual text, just that we wrote 
305
                # and read the 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
306
                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.
307
                if sha1 != local_sha1:
308
                    raise BzrError('sha1 mismatch. For inventory id {%s}' 
309
                            'local: %s, cset: %s' % (inv_id, local_sha1, sha1))
310
                else:
311
                    count += 1
312
0.5.63 by John Arbash Meinel
Moving the validation into part of the reading.
313
        if len(missing) > 0:
314
            # I don't know if this is an error yet
315
            warning('Not all revision hashes could be validated.'
316
                    ' Unable validate %d hashes' % len(missing))
0.5.64 by John Arbash Meinel
SUCCESS, we now are able to validate the inventory XML.
317
        mutter('Verified %d sha hashes for the changeset.' % count)
318
1185.82.49 by Aaron Bentley
SPOT fixes, fix inventory validation
319
    def _validate_inventory(self, inv, revision_id):
0.5.63 by John Arbash Meinel
Moving the validation into part of the reading.
320
        """At this point we should have generated the ChangesetTree,
321
        so build up an inventory, and make sure the hashes match.
322
        """
0.5.64 by John Arbash Meinel
SUCCESS, we now are able to validate the inventory XML.
323
0.5.82 by John Arbash Meinel
Lots of changes, changing separators, updating tests, updated ChangesetTree to include text_ids
324
        assert inv is not None
325
0.5.64 by John Arbash Meinel
SUCCESS, we now are able to validate the inventory XML.
326
        # 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.
327
        s = serializer_v5.write_inventory_to_string(inv)
328
        sha1 = sha_string(s)
0.5.64 by John Arbash Meinel
SUCCESS, we now are able to validate the inventory XML.
329
        # Target revision is the last entry in the real_revisions list
1185.82.49 by Aaron Bentley
SPOT fixes, fix inventory validation
330
        rev = self.info.get_revision(revision_id)
331
        assert rev.revision_id == revision_id
0.5.64 by John Arbash Meinel
SUCCESS, we now are able to validate the inventory XML.
332
        if sha1 != rev.inventory_sha1:
0.5.117 by John Arbash Meinel
Almost there. Just need to track down a few remaining bugs.
333
            open(',,bogus-inv', 'wb').write(s)
1185.82.61 by Aaron Bentley
Downgrade inventory mismatch to warning (source can be inaccurate)
334
            warning('Inventory sha hash mismatch for revision %s. %s'
335
                    ' != %s' % (revision_id, sha1, rev.inventory_sha1))
0.5.64 by John Arbash Meinel
SUCCESS, we now are able to validate the inventory XML.
336
0.5.63 by John Arbash Meinel
Moving the validation into part of the reading.
337
        
1185.82.15 by Aaron Bentley
Disabled validate_revisions (needs info it doesn't have), updated API to repos
338
    def get_changeset(self, repository):
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
339
        """Return the meta information, and a Changeset tree which can
340
        be used to populate the local stores and working tree, respectively.
341
        """
1185.82.48 by Aaron Bentley
Inching closer to supporting multiple revisions per changeset
342
        return self.info, self.revision_tree(repository, self.info.target)
343
344
    def revision_tree(self, repository, revision_id, base=None):
1185.82.49 by Aaron Bentley
SPOT fixes, fix inventory validation
345
        revision = self.info.get_revision(revision_id)
1185.82.48 by Aaron Bentley
Inching closer to supporting multiple revisions per changeset
346
        if revision_id == self.info.target:
347
            base = self.info.base
348
        else:
349
            base = self.info.get_base(revision)
350
        assert base != revision_id
1185.82.15 by Aaron Bentley
Disabled validate_revisions (needs info it doesn't have), updated API to repos
351
        self._validate_references_from_repository(repository)
1185.82.60 by Aaron Bentley
Only include revision ids in inventories that originally had them
352
        revision_info = self.info.get_revision_info(revision_id)
353
        if revision_info.inventory_has_revision == 'yes':
354
            inventory_revision_id = revision_id
355
        else:
356
            inventory_revision_id = None
357
        cset_tree = ChangesetTree(repository.revision_tree(base), 
358
                                  inventory_revision_id)
1185.82.48 by Aaron Bentley
Inching closer to supporting multiple revisions per changeset
359
        self._update_tree(cset_tree, revision_id)
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
360
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
361
        inv = cset_tree.inventory
1185.82.49 by Aaron Bentley
SPOT fixes, fix inventory validation
362
        self._validate_inventory(inv, revision_id)
363
        self._validate_revision(inv, revision_id)
0.5.63 by John Arbash Meinel
Moving the validation into part of the reading.
364
1185.82.48 by Aaron Bentley
Inching closer to supporting multiple revisions per changeset
365
        return cset_tree
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
366
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
367
    def _next(self):
368
        """yield the next line, but secretly
369
        keep 1 extra line for peeking.
370
        """
371
        for line in self.from_file:
372
            last = self._next_line
373
            self._next_line = line
374
            if last is not None:
0.5.57 by John Arbash Meinel
Simplified the header, only output base if it is not the expected one.
375
                #mutter('yielding line: %r' % last)
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
376
                yield last
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.
377
        last = self._next_line
378
        self._next_line = None
0.5.57 by John Arbash Meinel
Simplified the header, only output base if it is not the expected one.
379
        #mutter('yielding line: %r' % last)
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.
380
        yield last
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
381
382
    def _read_header(self):
383
        """Read the bzr header"""
0.5.87 by John Arbash Meinel
Handling international characters, added more test cases.
384
        header = get_header()
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
385
        found = False
386
        for line in self._next():
387
            if found:
0.5.69 by John Arbash Meinel
Applying patch from Robey Pointer to clean up apply_changeset.
388
                # not all mailers will keep trailing whitespace
389
                if line == '#\n':
390
                    line = '# \n'
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
391
                if (not line.startswith('# ') or not line.endswith('\n')
0.5.87 by John Arbash Meinel
Handling international characters, added more test cases.
392
                        or decode(line[2:-1]) != header[0]):
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
393
                    raise MalformedHeader('Found a header, but it'
394
                        ' was improperly formatted')
395
                header.pop(0) # We read this line.
396
                if not header:
397
                    break # We found everything.
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
398
            elif (line.startswith('#') and line.endswith('\n')):
0.5.87 by John Arbash Meinel
Handling international characters, added more test cases.
399
                line = decode(line[1:-1].strip())
400
                if line[:len(header_str)] == header_str:
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
401
                    if line == header[0]:
402
                        found = True
403
                    else:
404
                        raise MalformedHeader('Found what looks like'
405
                                ' a header, but did not match')
406
                    header.pop(0)
407
        else:
408
            raise MalformedHeader('Did not find an opening header')
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
409
1185.82.47 by Aaron Bentley
Ensure all intended rev_ids end up in the revision
410
    def _read_revision_header(self):
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
411
        for line in self._next():
412
            # The bzr header is terminated with a blank line
413
            # which does not start with '#'
1185.82.40 by Aaron Bentley
Started work on testing install_revisions/handling empty changesets
414
            if line is None or line == '\n':
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
415
                break
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
416
            self._handle_next(line)
417
418
    def _read_next_entry(self, line, indent=1):
419
        """Read in a key-value pair
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
420
        """
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
421
        if not line.startswith('#'):
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
422
            raise MalformedHeader('Bzr header did not start with #')
0.5.87 by John Arbash Meinel
Handling international characters, added more test cases.
423
        line = decode(line[1:-1]) # Remove the '#' and '\n'
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
424
        if line[:indent] == ' '*indent:
425
            line = line[indent:]
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
426
        if not line:
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
427
            return None, None# Ignore blank lines
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
428
429
        loc = line.find(': ')
430
        if loc != -1:
431
            key = line[:loc]
432
            value = line[loc+2:]
0.5.9 by John Arbash Meinel
Now adding the patch information to the ChangesetInfo
433
            if not value:
1185.82.28 by Aaron Bentley
Got parent_id handling working
434
                value = self._read_many(indent=indent+2)
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
435
        elif line[-1:] == ':':
436
            key = line[:-1]
1185.82.28 by Aaron Bentley
Got parent_id handling working
437
            value = self._read_many(indent=indent+2)
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
438
        else:
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
439
            raise MalformedHeader('While looking for key: value pairs,'
440
                    ' did not find the colon %r' % (line))
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
441
442
        key = key.replace(' ', '_')
0.5.57 by John Arbash Meinel
Simplified the header, only output base if it is not the expected one.
443
        #mutter('found %s: %s' % (key, value))
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
444
        return key, value
445
446
    def _handle_next(self, line):
1185.82.40 by Aaron Bentley
Started work on testing install_revisions/handling empty changesets
447
        if line is None:
448
            return
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
449
        key, value = self._read_next_entry(line, indent=1)
0.5.118 by John Arbash Meinel
Got most of test_changeset to work. Still needs work for Aaron's test code.
450
        mutter('_handle_next %r => %r' % (key, value))
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
451
        if key is None:
452
            return
453
1185.82.27 by Aaron Bentley
Fixed most revision attribute handling
454
        if key == 'revision_id':
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
455
            self._read_revision(value)
1185.82.27 by Aaron Bentley
Fixed most revision attribute handling
456
        else:
457
            revision_info = self.info.revisions[-1]
458
            if hasattr(revision_info, key):
459
                if getattr(revision_info, key) is None:
460
                    setattr(revision_info, key, value)
461
                else:
462
                    raise MalformedHeader('Duplicated Key: %s' % key)
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
463
            else:
1185.82.27 by Aaron Bentley
Fixed most revision attribute handling
464
                # What do we do with a key we don't recognize
465
                raise MalformedHeader('Unknown Key: "%s"' % key)
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
466
        
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
467
    def _read_many(self, indent):
0.5.9 by John Arbash Meinel
Now adding the patch information to the ChangesetInfo
468
        """If a line ends with no entry, that means that it should be
469
        followed with multiple lines of values.
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
470
0.5.9 by John Arbash Meinel
Now adding the patch information to the ChangesetInfo
471
        This detects the end of the list, because it will be a line that
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
472
        does not start properly indented.
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
473
        """
474
        values = []
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
475
        start = '#' + (' '*indent)
476
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.
477
        if self._next_line is None or self._next_line[:len(start)] != start:
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
478
            return values
479
480
        for line in self._next():
0.5.87 by John Arbash Meinel
Handling international characters, added more test cases.
481
            values.append(decode(line[len(start):-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.
482
            if self._next_line is None or self._next_line[:len(start)] != start:
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
483
                break
484
        return values
485
486
    def _read_one_patch(self):
0.5.9 by John Arbash Meinel
Now adding the patch information to the ChangesetInfo
487
        """Read in one patch, return the complete patch, along with
488
        the next line.
489
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
490
        :return: action, lines, do_continue
0.5.9 by John Arbash Meinel
Now adding the patch information to the ChangesetInfo
491
        """
0.5.57 by John Arbash Meinel
Simplified the header, only output base if it is not the expected one.
492
        #mutter('_read_one_patch: %r' % self._next_line)
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
493
        # Peek and see if there are no patches
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
494
        if self._next_line is None or self._next_line.startswith('#'):
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
495
            return None, [], False
496
0.5.83 by John Arbash Meinel
Tests pass. Now ChangesetTree has it's own inventory.
497
        first = True
0.5.9 by John Arbash Meinel
Now adding the patch information to the ChangesetInfo
498
        lines = []
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
499
        for line in self._next():
0.5.83 by John Arbash Meinel
Tests pass. Now ChangesetTree has it's own inventory.
500
            if first:
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
501
                if not line.startswith('==='):
0.5.83 by John Arbash Meinel
Tests pass. Now ChangesetTree has it's own inventory.
502
                    raise MalformedPatches('The first line of all patches'
0.5.100 by John Arbash Meinel
Switching from *** to ===
503
                        ' should be a bzr meta line "==="'
0.5.83 by John Arbash Meinel
Tests pass. Now ChangesetTree has it's own inventory.
504
                        ': %r' % line)
0.5.87 by John Arbash Meinel
Handling international characters, added more test cases.
505
                action = decode(line[4:-1])
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
506
            if self._next_line is not None and self._next_line.startswith('==='):
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
507
                return action, lines, True
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
508
            elif self._next_line is None or self._next_line.startswith('#'):
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
509
                return action, lines, False
0.5.83 by John Arbash Meinel
Tests pass. Now ChangesetTree has it's own inventory.
510
511
            if first:
512
                first = False
513
            else:
514
                lines.append(line)
515
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
516
        return action, lines, False
0.5.9 by John Arbash Meinel
Now adding the patch information to the ChangesetInfo
517
            
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
518
    def _read_patches(self):
0.5.9 by John Arbash Meinel
Now adding the patch information to the ChangesetInfo
519
        do_continue = True
1185.82.48 by Aaron Bentley
Inching closer to supporting multiple revisions per changeset
520
        revision_actions = []
0.5.9 by John Arbash Meinel
Now adding the patch information to the ChangesetInfo
521
        while do_continue:
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
522
            action, lines, do_continue = self._read_one_patch()
0.5.9 by John Arbash Meinel
Now adding the patch information to the ChangesetInfo
523
            if action is not None:
1185.82.48 by Aaron Bentley
Inching closer to supporting multiple revisions per changeset
524
                revision_actions.append((action, lines))
1185.82.50 by Aaron Bentley
Avoid overwriting action lines
525
        if self.info.revisions[-1].revision_id not in self.info.actions:
526
            self.info.actions[self.info.revisions[-1].revision_id] = \
527
                revision_actions
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
528
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
529
    def _read_revision(self, revision_id):
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
530
        """Revision entries have extra information associated.
531
        """
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
532
        rev_info = RevisionInfo(revision_id)
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
533
        start = '#    '
534
        for line in self._next():
1185.82.27 by Aaron Bentley
Fixed most revision attribute handling
535
            key,value = self._read_next_entry(line, indent=1)
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
536
            #if key is None:
537
            #    continue
538
            if hasattr(rev_info, key):
539
                if getattr(rev_info, key) is None:
540
                    setattr(rev_info, key, value)
541
                else:
542
                    raise MalformedHeader('Duplicated Key: %s' % key)
543
            else:
544
                # What do we do with a key we don't recognize
545
                raise MalformedHeader('Unknown Key: %s' % key)
546
0.5.118 by John Arbash Meinel
Got most of test_changeset to work. Still needs work for Aaron's test code.
547
            if self._next_line is None or not self._next_line.startswith(start):
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
548
                break
549
550
        self.info.revisions.append(rev_info)
551
552
    def _read_footer(self):
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
553
        """Read the rest of the meta information.
554
0.5.9 by John Arbash Meinel
Now adding the patch information to the ChangesetInfo
555
        :param first_line:  The previous step iterates past what it
556
                            can handle. That extra line is given here.
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
557
        """
0.5.36 by John Arbash Meinel
Updated so that read_changeset is able to parse the output
558
        for line in self._next():
559
            self._handle_next(line)
0.5.118 by John Arbash Meinel
Got most of test_changeset to work. Still needs work for Aaron's test code.
560
            if self._next_line is None or not self._next_line.startswith('#'):
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
561
                break
562
1185.82.48 by Aaron Bentley
Inching closer to supporting multiple revisions per changeset
563
    def _update_tree(self, cset_tree, revision_id):
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
564
        """This fills out a ChangesetTree based on the information
565
        that was read in.
566
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
567
        :param cset_tree: A ChangesetTree to update with the new information.
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
568
        """
569
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
570
        def get_rev_id(info, file_id, kind):
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
571
            if info is not None:
0.5.117 by John Arbash Meinel
Almost there. Just need to track down a few remaining bugs.
572
                if not info.startswith('last-changed:'):
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
573
                    raise BzrError("Last changed revision should start with 'last-changed:'"
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
574
                        ': %r' % info)
1185.82.49 by Aaron Bentley
SPOT fixes, fix inventory validation
575
                changed_revision_id = decode(info[13:])
0.5.118 by John Arbash Meinel
Got most of test_changeset to work. Still needs work for Aaron's test code.
576
            elif cset_tree._last_changed.has_key(file_id):
577
                return cset_tree._last_changed[file_id]
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
578
            else:
1185.82.49 by Aaron Bentley
SPOT fixes, fix inventory validation
579
                changed_revision_id = revision_id
580
            cset_tree.note_last_changed(file_id, changed_revision_id)
581
            return changed_revision_id
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
582
583
        def renamed(kind, extra, lines):
0.5.82 by John Arbash Meinel
Lots of changes, changing separators, updating tests, updated ChangesetTree to include text_ids
584
            info = extra.split(' // ')
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
585
            if len(info) < 2:
586
                raise BzrError('renamed action lines need both a from and to'
587
                        ': %r' % extra)
0.5.87 by John Arbash Meinel
Handling international characters, added more test cases.
588
            old_path = info[0]
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
589
            if info[1].startswith('=> '):
0.5.87 by John Arbash Meinel
Handling international characters, added more test cases.
590
                new_path = info[1][3:]
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
591
            else:
0.5.87 by John Arbash Meinel
Handling international characters, added more test cases.
592
                new_path = info[1]
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
593
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
594
            file_id = cset_tree.path2id(old_path)
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
595
            if len(info) > 2:
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
596
                revision = get_rev_id(info[2], file_id, kind)
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
597
            else:
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
598
                revision = get_rev_id(None, file_id, kind)
599
            cset_tree.note_rename(old_path, new_path)
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
600
            if lines:
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
601
                cset_tree.note_patch(new_path, ''.join(lines))
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
602
603
        def removed(kind, extra, lines):
0.5.82 by John Arbash Meinel
Lots of changes, changing separators, updating tests, updated ChangesetTree to include text_ids
604
            info = extra.split(' // ')
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
605
            if len(info) > 1:
606
                # TODO: in the future we might allow file ids to be
607
                # given for removed entries
608
                raise BzrError('removed action lines should only have the path'
609
                        ': %r' % extra)
0.5.87 by John Arbash Meinel
Handling international characters, added more test cases.
610
            path = info[0]
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
611
            cset_tree.note_deletion(path)
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
612
613
        def added(kind, extra, lines):
0.5.82 by John Arbash Meinel
Lots of changes, changing separators, updating tests, updated ChangesetTree to include text_ids
614
            info = extra.split(' // ')
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
615
            if len(info) <= 1:
616
                raise BzrError('add action lines require the path and file id'
617
                        ': %r' % extra)
618
            elif len(info) > 3:
619
                raise BzrError('add action lines have fewer than 3 entries.'
620
                        ': %r' % extra)
0.5.87 by John Arbash Meinel
Handling international characters, added more test cases.
621
            path = info[0]
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
622
            if not info[1].startswith('file-id:'):
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
623
                raise BzrError('The file-id should follow the path for an add'
624
                        ': %r' % extra)
0.5.87 by John Arbash Meinel
Handling international characters, added more test cases.
625
            file_id = info[1][8:]
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
626
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
627
            cset_tree.note_id(file_id, path, kind)
0.5.83 by John Arbash Meinel
Tests pass. Now ChangesetTree has it's own inventory.
628
            if len(info) > 2:
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
629
                revision = get_rev_id(info[2], file_id, kind)
0.5.83 by John Arbash Meinel
Tests pass. Now ChangesetTree has it's own inventory.
630
            else:
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
631
                revision = get_rev_id(None, file_id, kind)
0.5.117 by John Arbash Meinel
Almost there. Just need to track down a few remaining bugs.
632
            if kind == 'directory':
633
                return
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
634
            cset_tree.note_patch(path, ''.join(lines))
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
635
636
        def modified(kind, extra, lines):
0.5.82 by John Arbash Meinel
Lots of changes, changing separators, updating tests, updated ChangesetTree to include text_ids
637
            info = extra.split(' // ')
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
638
            if len(info) < 1:
639
                raise BzrError('modified action lines have at least'
640
                        'the path in them: %r' % extra)
0.5.87 by John Arbash Meinel
Handling international characters, added more test cases.
641
            path = info[0]
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
642
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
643
            file_id = cset_tree.path2id(path)
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
644
            if len(info) > 1:
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
645
                revision = get_rev_id(info[1], file_id, kind)
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
646
            else:
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
647
                revision = get_rev_id(None, file_id, kind)
648
            cset_tree.note_patch(path, ''.join(lines))
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
649
            
650
651
        valid_actions = {
652
            'renamed':renamed,
653
            'removed':removed,
654
            'added':added,
655
            'modified':modified
656
        }
1185.82.48 by Aaron Bentley
Inching closer to supporting multiple revisions per changeset
657
        for action_line, lines in self.info.actions[revision_id]:
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
658
            first = action_line.find(' ')
659
            if first == -1:
660
                raise BzrError('Bogus action line'
661
                        ' (no opening space): %r' % action_line)
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.
662
            second = action_line.find(' ', first+1)
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
663
            if second == -1:
664
                raise BzrError('Bogus action line'
665
                        ' (missing second space): %r' % action_line)
666
            action = action_line[:first]
667
            kind = action_line[first+1:second]
668
            if kind not in ('file', 'directory'):
669
                raise BzrError('Bogus action line'
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.
670
                        ' (invalid object kind %r): %r' % (kind, action_line))
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
671
            extra = action_line[second+1:]
672
673
            if action not in valid_actions:
674
                raise BzrError('Bogus action line'
675
                        ' (unrecognized action): %r' % action_line)
676
            valid_actions[action](kind, extra, lines)
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
677
1185.82.15 by Aaron Bentley
Disabled validate_revisions (needs info it doesn't have), updated API to repos
678
def read_changeset(from_file, repository):
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.
679
    """Read in a changeset from a iterable object (such as a file object)
680
681
    :param from_file: A file-like object to read the changeset information.
1185.82.15 by Aaron Bentley
Disabled validate_revisions (needs info it doesn't have), updated API to repos
682
    :param repository: This will be used to build the changeset tree, it needs
683
                       to contain the base of the changeset. (Which you
684
                       probably won't know about until after the changeset is
685
                       parsed.)
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
686
    """
687
    cr = ChangesetReader(from_file)
1185.82.15 by Aaron Bentley
Disabled validate_revisions (needs info it doesn't have), updated API to repos
688
    return cr.get_changeset(repository)
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
689
0.5.82 by John Arbash Meinel
Lots of changes, changing separators, updating tests, updated ChangesetTree to include text_ids
690
class ChangesetTree(Tree):
1185.82.16 by Aaron Bentley
Ensured revision ID is stored in ChangesetTree inventories
691
    def __init__(self, base_tree, revision_id):
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
692
        self.base_tree = base_tree
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
693
        self._renamed = {} # Mapping from old_path => new_path
694
        self._renamed_r = {} # new_path => old_path
695
        self._new_id = {} # new_path => new_id
696
        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.
697
        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.
698
        self._last_changed = {} # new_id => revision_id
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
699
        self.patches = {}
0.5.48 by aaron.bentley at utoronto
Implemented deletion for ChangesetTrees
700
        self.deleted = []
0.5.52 by aaron.bentley at utoronto
Make contents-addressing configurable
701
        self.contents_by_id = True
1185.82.16 by Aaron Bentley
Ensured revision ID is stored in ChangesetTree inventories
702
        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
703
        self._inventory = None
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
704
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
705
    def __str__(self):
706
        return pprint.pformat(self.__dict__)
707
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
708
    def note_rename(self, old_path, new_path):
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
709
        """A file/directory has been renamed from old_path => new_path"""
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
710
        assert not self._renamed.has_key(old_path)
711
        assert not self._renamed_r.has_key(new_path)
712
        self._renamed[new_path] = old_path
713
        self._renamed_r[old_path] = new_path
714
0.5.64 by John Arbash Meinel
SUCCESS, we now are able to validate the inventory XML.
715
    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.
716
        """Files that don't exist in base need a new id."""
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
717
        self._new_id[new_path] = new_id
718
        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.
719
        self._kinds[new_id] = kind
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
720
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
721
    def note_last_changed(self, file_id, revision_id):
0.5.118 by John Arbash Meinel
Got most of test_changeset to work. Still needs work for Aaron's test code.
722
        if (self._last_changed.has_key(file_id)
723
                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.
724
            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
725
                    ': %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.
726
                                    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.
727
                                    revision_id))
0.5.118 by John Arbash Meinel
Got most of test_changeset to work. Still needs work for Aaron's test code.
728
        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
729
0.5.44 by aaron.bentley at utoronto
Got get_file working for new files
730
    def note_patch(self, new_path, patch):
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
731
        """There is a patch for a given filename."""
0.5.44 by aaron.bentley at utoronto
Got get_file working for new files
732
        self.patches[new_path] = patch
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
733
0.5.48 by aaron.bentley at utoronto
Implemented deletion for ChangesetTrees
734
    def note_deletion(self, old_path):
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
735
        """The file at old_path has been deleted."""
0.5.48 by aaron.bentley at utoronto
Implemented deletion for ChangesetTrees
736
        self.deleted.append(old_path)
737
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
738
    def old_path(self, new_path):
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
739
        """Get the old_path (path in the base_tree) for the file at new_path"""
0.6.1 by Aaron Bentley
Fleshed out MockTree, fixed all test failures
740
        assert new_path[:1] not in ('\\', '/')
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
741
        old_path = self._renamed.get(new_path)
742
        if old_path is not None:
743
            return old_path
744
        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.
745
        # dirname is not '' doesn't work, because
746
        # dirname may be a unicode entry, and is
747
        # requires the objects to be identical
748
        if dirname != '':
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
749
            old_dir = self.old_path(dirname)
750
            if old_dir is None:
0.5.42 by aaron.bentley at utoronto
Improved rename handling
751
                old_path = None
752
            else:
753
                old_path = os.path.join(old_dir, basename)
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
754
        else:
0.5.42 by aaron.bentley at utoronto
Improved rename handling
755
            old_path = new_path
756
        #If the new path wasn't in renamed, the old one shouldn't be in
757
        #renamed_r
758
        if self._renamed_r.has_key(old_path):
759
            return None
760
        return old_path 
761
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
762
    def new_path(self, old_path):
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
763
        """Get the new_path (path in the target_tree) for the file at old_path
764
        in the base tree.
765
        """
0.6.1 by Aaron Bentley
Fleshed out MockTree, fixed all test failures
766
        assert old_path[:1] not in ('\\', '/')
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
767
        new_path = self._renamed_r.get(old_path)
768
        if new_path is not None:
769
            return new_path
770
        if self._renamed.has_key(new_path):
771
            return None
772
        dirname,basename = os.path.split(old_path)
0.5.81 by John Arbash Meinel
Cleaning up from pychecker.
773
        if dirname != '':
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
774
            new_dir = self.new_path(dirname)
775
            if new_dir is None:
0.5.42 by aaron.bentley at utoronto
Improved rename handling
776
                new_path = None
777
            else:
778
                new_path = os.path.join(new_dir, basename)
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
779
        else:
0.5.42 by aaron.bentley at utoronto
Improved rename handling
780
            new_path = old_path
781
        #If the old path wasn't in renamed, the new one shouldn't be in
782
        #renamed_r
783
        if self._renamed.has_key(new_path):
784
            return None
785
        return new_path 
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
786
787
    def path2id(self, path):
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
788
        """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
789
        file_id = self._new_id.get(path)
790
        if file_id is not None:
791
            return file_id
0.5.43 by aaron.bentley at utoronto
Handled moves and adds properly
792
        old_path = self.old_path(path)
793
        if old_path is None:
794
            return None
0.5.48 by aaron.bentley at utoronto
Implemented deletion for ChangesetTrees
795
        if old_path in self.deleted:
796
            return None
0.5.66 by John Arbash Meinel
Refactoring, moving test code into test (switching back to assert is None)
797
        if hasattr(self.base_tree, 'path2id'):
798
            return self.base_tree.path2id(old_path)
799
        else:
800
            return self.base_tree.inventory.path2id(old_path)
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
801
802
    def id2path(self, file_id):
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
803
        """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
804
        path = self._new_id_r.get(file_id)
805
        if path is not None:
806
            return path
0.5.43 by aaron.bentley at utoronto
Handled moves and adds properly
807
        old_path = self.base_tree.id2path(file_id)
808
        if old_path is None:
809
            return None
0.5.48 by aaron.bentley at utoronto
Implemented deletion for ChangesetTrees
810
        if old_path in self.deleted:
811
            return None
0.5.43 by aaron.bentley at utoronto
Handled moves and adds properly
812
        return self.new_path(old_path)
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
813
0.5.52 by aaron.bentley at utoronto
Make contents-addressing configurable
814
    def old_contents_id(self, file_id):
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
815
        """Return the id in the base_tree for the given file_id,
816
        or None if the file did not exist in base.
817
818
        FIXME:  Something doesn't seem right here. It seems like this function
819
                should always either return None or file_id. Even if
820
                you are doing the by-path lookup, you are doing a
821
                id2path lookup, just to do the reverse path2id lookup.
0.6.1 by Aaron Bentley
Fleshed out MockTree, fixed all test failures
822
823
        Notice that you're doing the path2id on a different tree!
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
824
        """
0.5.52 by aaron.bentley at utoronto
Make contents-addressing configurable
825
        if self.contents_by_id:
826
            if self.base_tree.has_id(file_id):
827
                return file_id
828
            else:
829
                return None
830
        new_path = self.id2path(file_id)
831
        return self.base_tree.path2id(new_path)
832
        
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
833
    def get_file(self, file_id):
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
834
        """Return a file-like object containing the new contents of the
835
        file given by file_id.
836
837
        TODO:   It might be nice if this actually generated an entry
838
                in the text-store, so that the file contents would
839
                then be cached.
840
        """
0.5.52 by aaron.bentley at utoronto
Make contents-addressing configurable
841
        base_id = self.old_contents_id(file_id)
0.5.50 by aaron.bentley at utoronto
Evaluate patches against file paths, not file ids
842
        if base_id is not None:
843
            patch_original = self.base_tree.get_file(base_id)
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
844
        else:
845
            patch_original = None
0.5.52 by aaron.bentley at utoronto
Make contents-addressing configurable
846
        file_patch = self.patches.get(self.id2path(file_id))
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
847
        if file_patch is None:
1185.82.42 by Aaron Bentley
Handle new directories properly
848
            if (patch_original is None and 
849
                self.get_kind(file_id) == 'directory'):
850
                return StringIO()
851
            assert patch_original is not None, "None: %s" % file_id
0.5.44 by aaron.bentley at utoronto
Got get_file working for new files
852
            return patch_original
0.5.94 by Aaron Bentley
Switched to native patch application, added tests for terminating newlines
853
854
        assert not file_patch.startswith('\\'), \
855
            'Malformed patch for %s, %r' % (file_id, file_patch)
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
856
        return patched_file(file_patch, patch_original)
857
0.5.64 by John Arbash Meinel
SUCCESS, we now are able to validate the inventory XML.
858
    def get_kind(self, file_id):
859
        if file_id in self._kinds:
860
            return self._kinds[file_id]
861
        return self.base_tree.inventory[file_id].kind
862
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
863
    def get_last_changed(self, file_id):
0.5.118 by John Arbash Meinel
Got most of test_changeset to work. Still needs work for Aaron's test code.
864
        if file_id in self._last_changed:
865
            return 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.
866
        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
867
0.5.64 by John Arbash Meinel
SUCCESS, we now are able to validate the inventory XML.
868
    def get_size_and_sha1(self, file_id):
869
        """Return the size and sha1 hash of the given file id.
870
        If the file was not locally modified, this is extracted
871
        from the base_tree. Rather than re-reading the file.
872
        """
873
        new_path = self.id2path(file_id)
874
        if new_path is None:
875
            return None, None
876
        if new_path not in self.patches:
877
            # If the entry does not have a patch, then the
878
            # contents must be the same as in the base_tree
879
            ie = self.base_tree.inventory[file_id]
0.5.69 by John Arbash Meinel
Applying patch from Robey Pointer to clean up apply_changeset.
880
            if ie.text_size is None:
881
                return ie.text_size, ie.text_sha1
0.5.64 by John Arbash Meinel
SUCCESS, we now are able to validate the inventory XML.
882
            return int(ie.text_size), ie.text_sha1
0.5.94 by Aaron Bentley
Switched to native patch application, added tests for terminating newlines
883
        fileobj = self.get_file(file_id)
884
        content = fileobj.read()
0.5.64 by John Arbash Meinel
SUCCESS, we now are able to validate the inventory XML.
885
        return len(content), sha_string(content)
886
0.5.82 by John Arbash Meinel
Lots of changes, changing separators, updating tests, updated ChangesetTree to include text_ids
887
888
    def _get_inventory(self):
889
        """Build up the inventory entry for the ChangesetTree.
890
891
        This need to be called before ever accessing self.inventory
892
        """
893
        from os.path import dirname, basename
894
895
        assert self.base_tree is not None
896
        base_inv = self.base_tree.inventory
897
        root_id = base_inv.root.file_id
898
        try:
899
            # New inventories have a unique root_id
1185.82.16 by Aaron Bentley
Ensured revision ID is stored in ChangesetTree inventories
900
            inv = Inventory(root_id, self.revision_id)
0.5.82 by John Arbash Meinel
Lots of changes, changing separators, updating tests, updated ChangesetTree to include text_ids
901
        except TypeError:
1185.82.16 by Aaron Bentley
Ensured revision ID is stored in ChangesetTree inventories
902
            inv = Inventory(revision_id=self.revision_id)
0.5.82 by John Arbash Meinel
Lots of changes, changing separators, updating tests, updated ChangesetTree to include text_ids
903
904
        def add_entry(file_id):
905
            path = self.id2path(file_id)
906
            if path is None:
907
                return
908
            parent_path = dirname(path)
0.5.116 by John Arbash Meinel
Fixed a bug based on the new InventoryEntry separation.
909
            if parent_path == u'':
0.5.82 by John Arbash Meinel
Lots of changes, changing separators, updating tests, updated ChangesetTree to include text_ids
910
                parent_id = root_id
911
            else:
912
                parent_id = self.path2id(parent_path)
913
914
            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.
915
            revision_id = self.get_last_changed(file_id)
0.5.83 by John Arbash Meinel
Tests pass. Now ChangesetTree has it's own inventory.
916
917
            name = basename(path)
0.5.116 by John Arbash Meinel
Fixed a bug based on the new InventoryEntry separation.
918
            if kind == 'directory':
919
                ie = InventoryDirectory(file_id, name, parent_id)
920
            elif kind == 'file':
921
                ie = InventoryFile(file_id, name, parent_id)
922
            elif kind == 'symlink':
923
                ie = InventoryLink(file_id, name, parent_id)
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
924
            ie.revision = revision_id
925
0.5.83 by John Arbash Meinel
Tests pass. Now ChangesetTree has it's own inventory.
926
            if kind == 'directory':
927
                ie.text_size, ie.text_sha1 = None, None
928
            else:
929
                ie.text_size, ie.text_sha1 = self.get_size_and_sha1(file_id)
0.5.82 by John Arbash Meinel
Lots of changes, changing separators, updating tests, updated ChangesetTree to include text_ids
930
            if (ie.text_size is None) and (kind != 'directory'):
931
                raise BzrError('Got a text_size of None for file_id %r' % file_id)
932
            inv.add(ie)
933
0.6.1 by Aaron Bentley
Fleshed out MockTree, fixed all test failures
934
        sorted_entries = self.sorted_path_id()
935
        for path, file_id in sorted_entries:
936
            if file_id == inv.root.file_id:
0.5.83 by John Arbash Meinel
Tests pass. Now ChangesetTree has it's own inventory.
937
                continue
0.5.82 by John Arbash Meinel
Lots of changes, changing separators, updating tests, updated ChangesetTree to include text_ids
938
            add_entry(file_id)
939
940
        return inv
941
942
    # Have to overload the inherited inventory property
943
    # because _get_inventory is only called in the parent.
944
    # Reading the docs, property() objects do not use
945
    # overloading, they use the function as it was defined
946
    # at that instant
947
    inventory = property(_get_inventory)
0.5.64 by John Arbash Meinel
SUCCESS, we now are able to validate the inventory XML.
948
0.5.49 by aaron.bentley at utoronto
Implemented iteration over ids
949
    def __iter__(self):
0.5.82 by John Arbash Meinel
Lots of changes, changing separators, updating tests, updated ChangesetTree to include text_ids
950
        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.
951
            yield entry.file_id
0.5.49 by aaron.bentley at utoronto
Implemented iteration over ids
952
0.6.1 by Aaron Bentley
Fleshed out MockTree, fixed all test failures
953
    def sorted_path_id(self):
954
        paths = []
955
        for result in self._new_id.iteritems():
956
            paths.append(result)
957
        for id in self.base_tree:
958
            path = self.id2path(id)
959
            if path is None:
960
                continue
961
            paths.append((path, id))
962
        paths.sort()
963
        return paths
964
0.5.41 by aaron.bentley at utoronto
Added non-working ChangesetTree
965
def patched_file(file_patch, original):
0.5.94 by Aaron Bentley
Switched to native patch application, added tests for terminating newlines
966
    """Produce a file-like object with the patched version of a text"""
1185.82.13 by Aaron Bentley
Got old changeset tests running
967
    from bzrlib.patches import iter_patched
968
    from bzrlib.iterablefile import IterableFile
0.5.94 by Aaron Bentley
Switched to native patch application, added tests for terminating newlines
969
    if file_patch == "":
970
        return IterableFile(())
971
    return IterableFile(iter_patched(original, file_patch.splitlines(True)))
972