~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/bundle/bundle_data.py

  • Committer: Robert Collins
  • Date: 2009-07-07 04:32:13 UTC
  • mto: This revision was merged to the branch mainline in revision 4524.
  • Revision ID: robertc@robertcollins.net-20090707043213-4hjjhgr40iq7gk2d
More informative assertions in xml serialisation.

Show diffs side-by-side

added added

removed removed

Lines of Context:
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
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
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
"""Read in a bundle stream, and process it into a BundleReader object."""
18
18
 
23
23
 
24
24
from bzrlib import (
25
25
    osutils,
 
26
    timestamp,
26
27
    )
27
28
import bzrlib.errors
28
29
from bzrlib.bundle import apply_bundle
29
 
from bzrlib.errors import (TestamentMismatch, BzrError, 
 
30
from bzrlib.errors import (TestamentMismatch, BzrError,
30
31
                           MalformedHeader, MalformedPatches, NotABundle)
31
32
from bzrlib.inventory import (Inventory, InventoryEntry,
32
33
                              InventoryDirectory, InventoryFile,
77
78
            for property in self.properties:
78
79
                key_end = property.find(': ')
79
80
                if key_end == -1:
80
 
                    assert property.endswith(':')
 
81
                    if not property.endswith(':'):
 
82
                        raise ValueError(property)
81
83
                    key = str(property[:-1])
82
84
                    value = ''
83
85
                else:
87
89
 
88
90
        return rev
89
91
 
 
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
 
90
105
 
91
106
class BundleInfo(object):
92
107
    """This contains the meta information. Stuff that allows you to
93
108
    recreate the revision or inventory XML.
94
109
    """
95
 
    def __init__(self):
 
110
    def __init__(self, bundle_format=None):
 
111
        self.bundle_format = None
96
112
        self.committer = None
97
113
        self.date = None
98
114
        self.message = None
143
159
    def get_base(self, revision):
144
160
        revision_info = self.get_revision_info(revision.revision_id)
145
161
        if revision_info.base_id is not None:
146
 
            if revision_info.base_id == NULL_REVISION:
147
 
                return None
148
 
            else:
149
 
                return revision_info.base_id
 
162
            return revision_info.base_id
150
163
        if len(revision.parent_ids) == 0:
151
164
            # There is no base listed, and
152
165
            # the lowest revision doesn't have a parent
153
166
            # so this is probably against the empty tree
154
 
            # and thus base truly is None
155
 
            return None
 
167
            # and thus base truly is NULL_REVISION
 
168
            return NULL_REVISION
156
169
        else:
157
170
            return revision.parent_ids[-1]
158
171
 
179
192
        raise KeyError(revision_id)
180
193
 
181
194
    def revision_tree(self, repository, revision_id, base=None):
182
 
        revision_id = osutils.safe_revision_id(revision_id)
183
195
        revision = self.get_revision(revision_id)
184
196
        base = self.get_base(revision)
185
 
        assert base != revision_id
 
197
        if base == revision_id:
 
198
            raise AssertionError()
186
199
        if not self._validated_revisions_against_repo:
187
200
            self._validate_references_from_repository(repository)
188
201
        revision_info = self.get_revision_info(revision_id)
189
202
        inventory_revision_id = revision_id
190
 
        bundle_tree = BundleTree(repository.revision_tree(base), 
 
203
        bundle_tree = BundleTree(repository.revision_tree(base),
191
204
                                  inventory_revision_id)
192
205
        self._update_tree(bundle_tree, revision_id)
193
206
 
226
239
        for rev_info in self.revisions:
227
240
            checked[rev_info.revision_id] = True
228
241
            add_sha(rev_to_sha, rev_info.revision_id, rev_info.sha1)
229
 
                
 
242
 
230
243
        for (rev, rev_info) in zip(self.real_revisions, self.revisions):
231
244
            add_sha(inv_to_sha, rev_info.revision_id, rev_info.inventory_sha1)
232
245
 
234
247
        missing = {}
235
248
        for revision_id, sha1 in rev_to_sha.iteritems():
236
249
            if repository.has_revision(revision_id):
237
 
                testament = StrictTestament.from_revision(repository, 
 
250
                testament = StrictTestament.from_revision(repository,
238
251
                                                          revision_id)
239
252
                local_sha1 = self._testament_sha1_from_revision(repository,
240
253
                                                                revision_id)
241
254
                if sha1 != local_sha1:
242
 
                    raise BzrError('sha1 mismatch. For revision id {%s}' 
 
255
                    raise BzrError('sha1 mismatch. For revision id {%s}'
243
256
                            'local: %s, bundle: %s' % (revision_id, local_sha1, sha1))
244
257
                else:
245
258
                    count += 1
246
259
            elif revision_id not in checked:
247
260
                missing[revision_id] = sha1
248
261
 
249
 
        for inv_id, sha1 in inv_to_sha.iteritems():
250
 
            if repository.has_revision(inv_id):
251
 
                # Note: branch.get_inventory_sha1() just returns the value that
252
 
                # is stored in the revision text, and that value may be out
253
 
                # of date. This is bogus, because that means we aren't
254
 
                # validating the actual text, just that we wrote and read the
255
 
                # string. But for now, what the hell.
256
 
                local_sha1 = repository.get_inventory_sha1(inv_id)
257
 
                if sha1 != local_sha1:
258
 
                    raise BzrError('sha1 mismatch. For inventory id {%s}' 
259
 
                                   'local: %s, bundle: %s' % 
260
 
                                   (inv_id, local_sha1, sha1))
261
 
                else:
262
 
                    count += 1
263
 
 
264
262
        if len(missing) > 0:
265
263
            # I don't know if this is an error yet
266
264
            warning('Not all revision hashes could be validated.'
272
270
        """At this point we should have generated the BundleTree,
273
271
        so build up an inventory, and make sure the hashes match.
274
272
        """
275
 
 
276
 
        assert inv is not None
277
 
 
278
273
        # Now we should have a complete inventory entry.
279
274
        s = serializer_v5.write_inventory_to_string(inv)
280
275
        sha1 = sha_string(s)
281
276
        # Target revision is the last entry in the real_revisions list
282
277
        rev = self.get_revision(revision_id)
283
 
        assert rev.revision_id == revision_id
 
278
        if rev.revision_id != revision_id:
 
279
            raise AssertionError()
284
280
        if sha1 != rev.inventory_sha1:
285
281
            open(',,bogus-inv', 'wb').write(s)
286
282
            warning('Inventory sha hash mismatch for revision %s. %s'
291
287
 
292
288
        # This is a mapping from each revision id to it's sha hash
293
289
        rev_to_sha1 = {}
294
 
        
 
290
 
295
291
        rev = self.get_revision(revision_id)
296
292
        rev_info = self.get_revision_info(revision_id)
297
 
        assert rev.revision_id == rev_info.revision_id
298
 
        assert rev.revision_id == revision_id
 
293
        if not (rev.revision_id == rev_info.revision_id):
 
294
            raise AssertionError()
 
295
        if not (rev.revision_id == revision_id):
 
296
            raise AssertionError()
299
297
        sha1 = self._testament_sha1(rev, inventory)
300
298
        if sha1 != rev_info.sha1:
301
299
            raise TestamentMismatch(rev.revision_id, rev_info.sha1, sha1)
333
331
                if name == 'last-changed':
334
332
                    last_changed = value
335
333
                elif name == 'executable':
336
 
                    assert value in ('yes', 'no'), value
337
334
                    val = (value == 'yes')
338
335
                    bundle_tree.note_executable(new_path, val)
339
336
                elif name == 'target':
343
340
            return last_changed, encoding
344
341
 
345
342
        def do_patch(path, lines, encoding):
346
 
            if encoding is not None:
347
 
                assert encoding == 'base64'
 
343
            if encoding == 'base64':
348
344
                patch = base64.decodestring(''.join(lines))
349
 
            else:
 
345
            elif encoding is None:
350
346
                patch =  ''.join(lines)
 
347
            else:
 
348
                raise ValueError(encoding)
351
349
            bundle_tree.note_patch(path, patch)
352
350
 
353
351
        def renamed(kind, extra, lines):
413
411
            revision = get_rev_id(last_modified, path, kind)
414
412
            if lines:
415
413
                do_patch(path, lines, encoding)
416
 
            
 
414
 
417
415
        valid_actions = {
418
416
            'renamed':renamed,
419
417
            'removed':removed,
442
440
                        ' (unrecognized action): %r' % action_line)
443
441
            valid_actions[action](kind, extra, lines)
444
442
 
445
 
    def install_revisions(self, target_repo):
446
 
        """Install revisions and return the target revision"""
 
443
    def install_revisions(self, target_repo, stream_input=True):
 
444
        """Install revisions and return the target revision
 
445
 
 
446
        :param target_repo: The repository to install into
 
447
        :param stream_input: Ignored by this implementation.
 
448
        """
447
449
        apply_bundle.install_bundle(target_repo, self)
448
450
        return self.target
449
451
 
 
452
    def get_merge_request(self, target_repo):
 
453
        """Provide data for performing a merge
 
454
 
 
455
        Returns suggested base, suggested target, and patch verification status
 
456
        """
 
457
        return None, self.target, 'inapplicable'
 
458
 
450
459
 
451
460
class BundleTree(Tree):
452
461
    def __init__(self, base_tree, revision_id):
470
479
 
471
480
    def note_rename(self, old_path, new_path):
472
481
        """A file/directory has been renamed from old_path => new_path"""
473
 
        assert new_path not in self._renamed
474
 
        assert old_path not in self._renamed_r
 
482
        if new_path in self._renamed:
 
483
            raise AssertionError(new_path)
 
484
        if old_path in self._renamed_r:
 
485
            raise AssertionError(old_path)
475
486
        self._renamed[new_path] = old_path
476
487
        self._renamed_r[old_path] = new_path
477
488
 
507
518
 
508
519
    def old_path(self, new_path):
509
520
        """Get the old_path (path in the base_tree) for the file at new_path"""
510
 
        assert new_path[:1] not in ('\\', '/')
 
521
        if new_path[:1] in ('\\', '/'):
 
522
            raise ValueError(new_path)
511
523
        old_path = self._renamed.get(new_path)
512
524
        if old_path is not None:
513
525
            return old_path
527
539
        #renamed_r
528
540
        if old_path in self._renamed_r:
529
541
            return None
530
 
        return old_path 
 
542
        return old_path
531
543
 
532
544
    def new_path(self, old_path):
533
545
        """Get the new_path (path in the target_tree) for the file at old_path
534
546
        in the base tree.
535
547
        """
536
 
        assert old_path[:1] not in ('\\', '/')
 
548
        if old_path[:1] in ('\\', '/'):
 
549
            raise ValueError(old_path)
537
550
        new_path = self._renamed_r.get(old_path)
538
551
        if new_path is not None:
539
552
            return new_path
552
565
        #renamed_r
553
566
        if new_path in self._renamed:
554
567
            return None
555
 
        return new_path 
 
568
        return new_path
556
569
 
557
570
    def path2id(self, path):
558
571
        """Return the id of the file present at path in the target tree."""
592
605
                return None
593
606
        new_path = self.id2path(file_id)
594
607
        return self.base_tree.path2id(new_path)
595
 
        
 
608
 
596
609
    def get_file(self, file_id):
597
610
        """Return a file-like object containing the new contents of the
598
611
        file given by file_id.
609
622
            patch_original = None
610
623
        file_patch = self.patches.get(self.id2path(file_id))
611
624
        if file_patch is None:
612
 
            if (patch_original is None and 
 
625
            if (patch_original is None and
613
626
                self.get_kind(file_id) == 'directory'):
614
627
                return StringIO()
615
 
            assert patch_original is not None, "None: %s" % file_id
 
628
            if patch_original is None:
 
629
                raise AssertionError("None: %s" % file_id)
616
630
            return patch_original
617
631
 
618
 
        assert not file_patch.startswith('\\'), \
619
 
            'Malformed patch for %s, %r' % (file_id, file_patch)
 
632
        if file_patch.startswith('\\'):
 
633
            raise ValueError(
 
634
                'Malformed patch for %s, %r' % (file_id, file_patch))
620
635
        return patched_file(file_patch, patch_original)
621
636
 
622
637
    def get_symlink_target(self, file_id):
669
684
        This need to be called before ever accessing self.inventory
670
685
        """
671
686
        from os.path import dirname, basename
672
 
 
673
 
        assert self.base_tree is not None
674
687
        base_inv = self.base_tree.inventory
675
688
        inv = Inventory(None, self.revision_id)
676
689