~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/bundle/bundle_data.py

(jameinel) Allow 'bzr serve' to interpret SIGHUP as a graceful shutdown.
 (bug #795025) (John A Meinel)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 Canonical Ltd
 
1
# Copyright (C) 2005-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
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
 
25
25
    osutils,
26
26
    timestamp,
27
27
    )
28
 
import bzrlib.errors
29
28
from bzrlib.bundle import apply_bundle
30
 
from bzrlib.errors import (TestamentMismatch, BzrError, 
31
 
                           MalformedHeader, MalformedPatches, NotABundle)
32
 
from bzrlib.inventory import (Inventory, InventoryEntry,
33
 
                              InventoryDirectory, InventoryFile,
34
 
                              InventoryLink)
35
 
from bzrlib.osutils import sha_file, sha_string, pathjoin
 
29
from bzrlib.errors import (
 
30
    TestamentMismatch,
 
31
    BzrError,
 
32
    )
 
33
from bzrlib.inventory import (
 
34
    Inventory,
 
35
    InventoryDirectory,
 
36
    InventoryFile,
 
37
    InventoryLink,
 
38
    )
 
39
from bzrlib.osutils import sha_string, pathjoin
36
40
from bzrlib.revision import Revision, NULL_REVISION
37
41
from bzrlib.testament import StrictTestament
38
42
from bzrlib.trace import mutter, warning
39
 
import bzrlib.transport
40
43
from bzrlib.tree import Tree
41
 
import bzrlib.urlutils
42
44
from bzrlib.xml5 import serializer_v5
43
45
 
44
46
 
200
202
            self._validate_references_from_repository(repository)
201
203
        revision_info = self.get_revision_info(revision_id)
202
204
        inventory_revision_id = revision_id
203
 
        bundle_tree = BundleTree(repository.revision_tree(base), 
 
205
        bundle_tree = BundleTree(repository.revision_tree(base),
204
206
                                  inventory_revision_id)
205
207
        self._update_tree(bundle_tree, revision_id)
206
208
 
207
209
        inv = bundle_tree.inventory
208
210
        self._validate_inventory(inv, revision_id)
209
 
        self._validate_revision(inv, revision_id)
 
211
        self._validate_revision(bundle_tree, revision_id)
210
212
 
211
213
        return bundle_tree
212
214
 
239
241
        for rev_info in self.revisions:
240
242
            checked[rev_info.revision_id] = True
241
243
            add_sha(rev_to_sha, rev_info.revision_id, rev_info.sha1)
242
 
                
 
244
 
243
245
        for (rev, rev_info) in zip(self.real_revisions, self.revisions):
244
246
            add_sha(inv_to_sha, rev_info.revision_id, rev_info.inventory_sha1)
245
247
 
247
249
        missing = {}
248
250
        for revision_id, sha1 in rev_to_sha.iteritems():
249
251
            if repository.has_revision(revision_id):
250
 
                testament = StrictTestament.from_revision(repository, 
 
252
                testament = StrictTestament.from_revision(repository,
251
253
                                                          revision_id)
252
254
                local_sha1 = self._testament_sha1_from_revision(repository,
253
255
                                                                revision_id)
254
256
                if sha1 != local_sha1:
255
 
                    raise BzrError('sha1 mismatch. For revision id {%s}' 
 
257
                    raise BzrError('sha1 mismatch. For revision id {%s}'
256
258
                            'local: %s, bundle: %s' % (revision_id, local_sha1, sha1))
257
259
                else:
258
260
                    count += 1
278
280
        if rev.revision_id != revision_id:
279
281
            raise AssertionError()
280
282
        if sha1 != rev.inventory_sha1:
281
 
            open(',,bogus-inv', 'wb').write(s)
 
283
            f = open(',,bogus-inv', 'wb')
 
284
            try:
 
285
                f.write(s)
 
286
            finally:
 
287
                f.close()
282
288
            warning('Inventory sha hash mismatch for revision %s. %s'
283
289
                    ' != %s' % (revision_id, sha1, rev.inventory_sha1))
284
290
 
285
 
    def _validate_revision(self, inventory, revision_id):
 
291
    def _validate_revision(self, tree, revision_id):
286
292
        """Make sure all revision entries match their checksum."""
287
293
 
288
 
        # This is a mapping from each revision id to it's sha hash
 
294
        # This is a mapping from each revision id to its sha hash
289
295
        rev_to_sha1 = {}
290
 
        
 
296
 
291
297
        rev = self.get_revision(revision_id)
292
298
        rev_info = self.get_revision_info(revision_id)
293
299
        if not (rev.revision_id == rev_info.revision_id):
294
300
            raise AssertionError()
295
301
        if not (rev.revision_id == revision_id):
296
302
            raise AssertionError()
297
 
        sha1 = self._testament_sha1(rev, inventory)
 
303
        sha1 = self._testament_sha1(rev, tree)
298
304
        if sha1 != rev_info.sha1:
299
305
            raise TestamentMismatch(rev.revision_id, rev_info.sha1, sha1)
300
306
        if rev.revision_id in rev_to_sha1:
327
333
                try:
328
334
                    name, value = info_item.split(':', 1)
329
335
                except ValueError:
330
 
                    raise 'Value %r has no colon' % info_item
 
336
                    raise ValueError('Value %r has no colon' % info_item)
331
337
                if name == 'last-changed':
332
338
                    last_changed = value
333
339
                elif name == 'executable':
411
417
            revision = get_rev_id(last_modified, path, kind)
412
418
            if lines:
413
419
                do_patch(path, lines, encoding)
414
 
            
 
420
 
415
421
        valid_actions = {
416
422
            'renamed':renamed,
417
423
            'removed':removed,
458
464
 
459
465
 
460
466
class BundleTree(Tree):
 
467
 
461
468
    def __init__(self, base_tree, revision_id):
462
469
        self.base_tree = base_tree
463
470
        self._renamed = {} # Mapping from old_path => new_path
539
546
        #renamed_r
540
547
        if old_path in self._renamed_r:
541
548
            return None
542
 
        return old_path 
 
549
        return old_path
543
550
 
544
551
    def new_path(self, old_path):
545
552
        """Get the new_path (path in the target_tree) for the file at old_path
565
572
        #renamed_r
566
573
        if new_path in self._renamed:
567
574
            return None
568
 
        return new_path 
 
575
        return new_path
569
576
 
570
577
    def path2id(self, path):
571
578
        """Return the id of the file present at path in the target tree."""
605
612
                return None
606
613
        new_path = self.id2path(file_id)
607
614
        return self.base_tree.path2id(new_path)
608
 
        
 
615
 
609
616
    def get_file(self, file_id):
610
617
        """Return a file-like object containing the new contents of the
611
618
        file given by file_id.
622
629
            patch_original = None
623
630
        file_patch = self.patches.get(self.id2path(file_id))
624
631
        if file_patch is None:
625
 
            if (patch_original is None and 
 
632
            if (patch_original is None and
626
633
                self.get_kind(file_id) == 'directory'):
627
634
                return StringIO()
628
635
            if patch_original is None:
634
641
                'Malformed patch for %s, %r' % (file_id, file_patch))
635
642
        return patched_file(file_patch, patch_original)
636
643
 
637
 
    def get_symlink_target(self, file_id):
638
 
        new_path = self.id2path(file_id)
 
644
    def get_symlink_target(self, file_id, path=None):
 
645
        if path is None:
 
646
            path = self.id2path(file_id)
639
647
        try:
640
 
            return self._targets[new_path]
 
648
            return self._targets[path]
641
649
        except KeyError:
642
650
            return self.base_tree.get_symlink_target(file_id)
643
651
 
657
665
        path = self.id2path(file_id)
658
666
        if path in self._last_changed:
659
667
            return self._last_changed[path]
660
 
        return self.base_tree.inventory[file_id].revision
 
668
        return self.base_tree.get_file_revision(file_id)
661
669
 
662
670
    def get_size_and_sha1(self, file_id):
663
671
        """Return the size and sha1 hash of the given file id.
708
716
                ie.executable = self.is_executable(file_id)
709
717
            elif kind == 'symlink':
710
718
                ie = InventoryLink(file_id, name, parent_id)
711
 
                ie.symlink_target = self.get_symlink_target(file_id)
 
719
                ie.symlink_target = self.get_symlink_target(file_id, path)
712
720
            ie.revision = revision_id
713
721
 
714
 
            if kind in ('directory', 'symlink'):
715
 
                ie.text_size, ie.text_sha1 = None, None
716
 
            else:
 
722
            if kind == 'file':
717
723
                ie.text_size, ie.text_sha1 = self.get_size_and_sha1(file_id)
718
 
            if (ie.text_size is None) and (kind == 'file'):
719
 
                raise BzrError('Got a text_size of None for file_id %r' % file_id)
 
724
                if ie.text_size is None:
 
725
                    raise BzrError(
 
726
                        'Got a text_size of None for file_id %r' % file_id)
720
727
            inv.add(ie)
721
728
 
722
729
        sorted_entries = self.sorted_path_id()
736
743
        for path, entry in self.inventory.iter_entries():
737
744
            yield entry.file_id
738
745
 
 
746
    def list_files(self, include_root=False, from_dir=None, recursive=True):
 
747
        # The only files returned by this are those from the version
 
748
        inv = self.inventory
 
749
        if from_dir is None:
 
750
            from_dir_id = None
 
751
        else:
 
752
            from_dir_id = inv.path2id(from_dir)
 
753
            if from_dir_id is None:
 
754
                # Directory not versioned
 
755
                return
 
756
        entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
 
757
        if inv.root is not None and not include_root and from_dir is None:
 
758
            # skip the root for compatability with the current apis.
 
759
            entries.next()
 
760
        for path, entry in entries:
 
761
            yield path, 'V', entry.kind, entry.file_id, entry
 
762
 
739
763
    def sorted_path_id(self):
740
764
        paths = []
741
765
        for result in self._new_id.iteritems():
742
766
            paths.append(result)
743
 
        for id in self.base_tree:
 
767
        for id in self.base_tree.all_file_ids():
744
768
            path = self.id2path(id)
745
769
            if path is None:
746
770
                continue