~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/bundle/bundle_data.py

  • Committer: John Arbash Meinel
  • Date: 2007-03-14 20:15:52 UTC
  • mto: (2353.4.2 locking)
  • mto: This revision was merged to the branch mainline in revision 2360.
  • Revision ID: john@arbash-meinel.com-20070314201552-bjtfua57456dviep
Update the lock code and test code so that if more than one
lock implementation is available, they will both be tested.

It is quite a bit of overhead, for a case where we are likely to only have 1
real lock implementation per platform, but hey, for now we have 2.

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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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,
27
26
    )
28
27
import bzrlib.errors
29
 
from bzrlib.bundle import apply_bundle
30
 
from bzrlib.errors import (TestamentMismatch, BzrError,
 
28
from bzrlib.errors import (TestamentMismatch, BzrError, 
31
29
                           MalformedHeader, MalformedPatches, NotABundle)
32
30
from bzrlib.inventory import (Inventory, InventoryEntry,
33
31
                              InventoryDirectory, InventoryFile,
77
75
        if self.properties:
78
76
            for property in self.properties:
79
77
                key_end = property.find(': ')
80
 
                if key_end == -1:
81
 
                    if not property.endswith(':'):
82
 
                        raise ValueError(property)
83
 
                    key = str(property[:-1])
84
 
                    value = ''
85
 
                else:
86
 
                    key = str(property[:key_end])
87
 
                    value = property[key_end+2:]
 
78
                assert key_end is not None
 
79
                key = property[:key_end].encode('utf-8')
 
80
                value = property[key_end+2:].encode('utf-8')
88
81
                rev.properties[key] = value
89
82
 
90
83
        return rev
91
84
 
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
 
 
105
85
 
106
86
class BundleInfo(object):
107
87
    """This contains the meta information. Stuff that allows you to
108
88
    recreate the revision or inventory XML.
109
89
    """
110
 
    def __init__(self, bundle_format=None):
111
 
        self.bundle_format = None
 
90
    def __init__(self):
112
91
        self.committer = None
113
92
        self.date = None
114
93
        self.message = None
125
104
        self.timestamp = None
126
105
        self.timezone = None
127
106
 
128
 
        # Have we checked the repository yet?
129
 
        self._validated_revisions_against_repo = False
130
 
 
131
107
    def __str__(self):
132
108
        return pprint.pformat(self.__dict__)
133
109
 
159
135
    def get_base(self, revision):
160
136
        revision_info = self.get_revision_info(revision.revision_id)
161
137
        if revision_info.base_id is not None:
162
 
            return revision_info.base_id
 
138
            if revision_info.base_id == NULL_REVISION:
 
139
                return None
 
140
            else:
 
141
                return revision_info.base_id
163
142
        if len(revision.parent_ids) == 0:
164
143
            # There is no base listed, and
165
144
            # the lowest revision doesn't have a parent
166
145
            # so this is probably against the empty tree
167
 
            # and thus base truly is NULL_REVISION
168
 
            return NULL_REVISION
 
146
            # and thus base truly is None
 
147
            return None
169
148
        else:
170
149
            return revision.parent_ids[-1]
171
150
 
192
171
        raise KeyError(revision_id)
193
172
 
194
173
    def revision_tree(self, repository, revision_id, base=None):
 
174
        revision_id = osutils.safe_revision_id(revision_id)
195
175
        revision = self.get_revision(revision_id)
196
176
        base = self.get_base(revision)
197
 
        if base == revision_id:
198
 
            raise AssertionError()
199
 
        if not self._validated_revisions_against_repo:
200
 
            self._validate_references_from_repository(repository)
 
177
        assert base != revision_id
 
178
        self._validate_references_from_repository(repository)
201
179
        revision_info = self.get_revision_info(revision_id)
202
180
        inventory_revision_id = revision_id
203
 
        bundle_tree = BundleTree(repository.revision_tree(base),
 
181
        bundle_tree = BundleTree(repository.revision_tree(base), 
204
182
                                  inventory_revision_id)
205
183
        self._update_tree(bundle_tree, revision_id)
206
184
 
239
217
        for rev_info in self.revisions:
240
218
            checked[rev_info.revision_id] = True
241
219
            add_sha(rev_to_sha, rev_info.revision_id, rev_info.sha1)
242
 
 
 
220
                
243
221
        for (rev, rev_info) in zip(self.real_revisions, self.revisions):
244
222
            add_sha(inv_to_sha, rev_info.revision_id, rev_info.inventory_sha1)
245
223
 
247
225
        missing = {}
248
226
        for revision_id, sha1 in rev_to_sha.iteritems():
249
227
            if repository.has_revision(revision_id):
250
 
                testament = StrictTestament.from_revision(repository,
 
228
                testament = StrictTestament.from_revision(repository, 
251
229
                                                          revision_id)
252
230
                local_sha1 = self._testament_sha1_from_revision(repository,
253
231
                                                                revision_id)
254
232
                if sha1 != local_sha1:
255
 
                    raise BzrError('sha1 mismatch. For revision id {%s}'
 
233
                    raise BzrError('sha1 mismatch. For revision id {%s}' 
256
234
                            'local: %s, bundle: %s' % (revision_id, local_sha1, sha1))
257
235
                else:
258
236
                    count += 1
259
237
            elif revision_id not in checked:
260
238
                missing[revision_id] = sha1
261
239
 
 
240
        for inv_id, sha1 in inv_to_sha.iteritems():
 
241
            if repository.has_revision(inv_id):
 
242
                # Note: branch.get_inventory_sha1() just returns the value that
 
243
                # is stored in the revision text, and that value may be out
 
244
                # of date. This is bogus, because that means we aren't
 
245
                # validating the actual text, just that we wrote and read the
 
246
                # string. But for now, what the hell.
 
247
                local_sha1 = repository.get_inventory_sha1(inv_id)
 
248
                if sha1 != local_sha1:
 
249
                    raise BzrError('sha1 mismatch. For inventory id {%s}' 
 
250
                                   'local: %s, bundle: %s' % 
 
251
                                   (inv_id, local_sha1, sha1))
 
252
                else:
 
253
                    count += 1
 
254
 
262
255
        if len(missing) > 0:
263
256
            # I don't know if this is an error yet
264
257
            warning('Not all revision hashes could be validated.'
265
258
                    ' Unable validate %d hashes' % len(missing))
266
259
        mutter('Verified %d sha hashes for the bundle.' % count)
267
 
        self._validated_revisions_against_repo = True
268
260
 
269
261
    def _validate_inventory(self, inv, revision_id):
270
262
        """At this point we should have generated the BundleTree,
271
263
        so build up an inventory, and make sure the hashes match.
272
264
        """
 
265
 
 
266
        assert inv is not None
 
267
 
273
268
        # Now we should have a complete inventory entry.
274
269
        s = serializer_v5.write_inventory_to_string(inv)
275
270
        sha1 = sha_string(s)
276
271
        # Target revision is the last entry in the real_revisions list
277
272
        rev = self.get_revision(revision_id)
278
 
        if rev.revision_id != revision_id:
279
 
            raise AssertionError()
 
273
        assert rev.revision_id == revision_id
280
274
        if sha1 != rev.inventory_sha1:
281
275
            open(',,bogus-inv', 'wb').write(s)
282
276
            warning('Inventory sha hash mismatch for revision %s. %s'
287
281
 
288
282
        # This is a mapping from each revision id to it's sha hash
289
283
        rev_to_sha1 = {}
290
 
 
 
284
        
291
285
        rev = self.get_revision(revision_id)
292
286
        rev_info = self.get_revision_info(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()
 
287
        assert rev.revision_id == rev_info.revision_id
 
288
        assert rev.revision_id == revision_id
297
289
        sha1 = self._testament_sha1(rev, inventory)
298
290
        if sha1 != rev_info.sha1:
299
291
            raise TestamentMismatch(rev.revision_id, rev_info.sha1, sha1)
331
323
                if name == 'last-changed':
332
324
                    last_changed = value
333
325
                elif name == 'executable':
 
326
                    assert value in ('yes', 'no'), value
334
327
                    val = (value == 'yes')
335
328
                    bundle_tree.note_executable(new_path, val)
336
329
                elif name == 'target':
340
333
            return last_changed, encoding
341
334
 
342
335
        def do_patch(path, lines, encoding):
343
 
            if encoding == 'base64':
 
336
            if encoding is not None:
 
337
                assert encoding == 'base64'
344
338
                patch = base64.decodestring(''.join(lines))
345
 
            elif encoding is None:
 
339
            else:
346
340
                patch =  ''.join(lines)
347
 
            else:
348
 
                raise ValueError(encoding)
349
341
            bundle_tree.note_patch(path, patch)
350
342
 
351
343
        def renamed(kind, extra, lines):
411
403
            revision = get_rev_id(last_modified, path, kind)
412
404
            if lines:
413
405
                do_patch(path, lines, encoding)
414
 
 
 
406
            
415
407
        valid_actions = {
416
408
            'renamed':renamed,
417
409
            'removed':removed,
440
432
                        ' (unrecognized action): %r' % action_line)
441
433
            valid_actions[action](kind, extra, lines)
442
434
 
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
 
        """
449
 
        apply_bundle.install_bundle(target_repo, self)
450
 
        return self.target
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
 
 
459
435
 
460
436
class BundleTree(Tree):
461
437
    def __init__(self, base_tree, revision_id):
479
455
 
480
456
    def note_rename(self, old_path, new_path):
481
457
        """A file/directory has been renamed from old_path => new_path"""
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)
 
458
        assert new_path not in self._renamed
 
459
        assert old_path not in self._renamed_r
486
460
        self._renamed[new_path] = old_path
487
461
        self._renamed_r[old_path] = new_path
488
462
 
518
492
 
519
493
    def old_path(self, new_path):
520
494
        """Get the old_path (path in the base_tree) for the file at new_path"""
521
 
        if new_path[:1] in ('\\', '/'):
522
 
            raise ValueError(new_path)
 
495
        assert new_path[:1] not in ('\\', '/')
523
496
        old_path = self._renamed.get(new_path)
524
497
        if old_path is not None:
525
498
            return old_path
539
512
        #renamed_r
540
513
        if old_path in self._renamed_r:
541
514
            return None
542
 
        return old_path
 
515
        return old_path 
543
516
 
544
517
    def new_path(self, old_path):
545
518
        """Get the new_path (path in the target_tree) for the file at old_path
546
519
        in the base tree.
547
520
        """
548
 
        if old_path[:1] in ('\\', '/'):
549
 
            raise ValueError(old_path)
 
521
        assert old_path[:1] not in ('\\', '/')
550
522
        new_path = self._renamed_r.get(old_path)
551
523
        if new_path is not None:
552
524
            return new_path
565
537
        #renamed_r
566
538
        if new_path in self._renamed:
567
539
            return None
568
 
        return new_path
 
540
        return new_path 
569
541
 
570
542
    def path2id(self, path):
571
543
        """Return the id of the file present at path in the target tree."""
605
577
                return None
606
578
        new_path = self.id2path(file_id)
607
579
        return self.base_tree.path2id(new_path)
608
 
 
 
580
        
609
581
    def get_file(self, file_id):
610
582
        """Return a file-like object containing the new contents of the
611
583
        file given by file_id.
622
594
            patch_original = None
623
595
        file_patch = self.patches.get(self.id2path(file_id))
624
596
        if file_patch is None:
625
 
            if (patch_original is None and
 
597
            if (patch_original is None and 
626
598
                self.get_kind(file_id) == 'directory'):
627
599
                return StringIO()
628
 
            if patch_original is None:
629
 
                raise AssertionError("None: %s" % file_id)
 
600
            assert patch_original is not None, "None: %s" % file_id
630
601
            return patch_original
631
602
 
632
 
        if file_patch.startswith('\\'):
633
 
            raise ValueError(
634
 
                'Malformed patch for %s, %r' % (file_id, file_patch))
 
603
        assert not file_patch.startswith('\\'), \
 
604
            'Malformed patch for %s, %r' % (file_id, file_patch)
635
605
        return patched_file(file_patch, patch_original)
636
606
 
637
607
    def get_symlink_target(self, file_id):
684
654
        This need to be called before ever accessing self.inventory
685
655
        """
686
656
        from os.path import dirname, basename
 
657
 
 
658
        assert self.base_tree is not None
687
659
        base_inv = self.base_tree.inventory
688
660
        inv = Inventory(None, self.revision_id)
689
661