~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

Merge from integration.

Show diffs side-by-side

added added

removed removed

Lines of Context:
27
27
import bzrlib.inventory as inventory
28
28
from bzrlib.trace import mutter, note
29
29
from bzrlib.osutils import (isdir, quotefn,
30
 
                            rename, splitpath, sha_file, appendpath, 
31
 
                            file_kind, abspath)
 
30
                            rename, splitpath, sha_file,
 
31
                            file_kind, abspath, normpath, pathjoin)
32
32
import bzrlib.errors as errors
33
33
from bzrlib.errors import (BzrError, InvalidRevisionNumber, InvalidRevisionId,
34
34
                           NoSuchRevision, HistoryMissing, NotBranchError,
133
133
        while True:
134
134
            try:
135
135
                return BzrBranch(t), t.relpath(url)
136
 
            except NotBranchError:
137
 
                pass
 
136
            except NotBranchError, e:
 
137
                mutter('not a branch in: %r %s', t.base, e)
138
138
            new_t = t.clone('..')
139
139
            if new_t.base == t.base:
140
140
                # reached the root, whatever that may be
233
233
        """Return the id of this branches root"""
234
234
        raise NotImplementedError('get_root_id is abstract')
235
235
 
236
 
    def print_file(self, file, revno):
 
236
    def set_root_id(self, file_id):
 
237
        raise NotImplementedError('set_root_id is abstract')
 
238
 
 
239
    def print_file(self, file, revision_id):
237
240
        """Print `file` to stdout."""
238
241
        raise NotImplementedError('print_file is abstract')
239
242
 
250
253
        or on the mainline."""
251
254
        raise NotImplementedError('has_revision is abstract')
252
255
 
253
 
    def get_revision_xml_file(self, revision_id):
254
 
        """Return XML file object for revision object."""
255
 
        raise NotImplementedError('get_revision_xml_file is abstract')
256
 
 
257
256
    def get_revision_xml(self, revision_id):
258
257
        raise NotImplementedError('get_revision_xml is abstract')
259
258
 
356
355
        >>> commit(br1, "lala!", rev_id="REVISION-ID-2B")
357
356
        >>> br1.missing_revisions(br2)
358
357
        Traceback (most recent call last):
359
 
        DivergedBranches: These branches have diverged.
 
358
        DivergedBranches: These branches have diverged.  Try merge.
360
359
        """
361
360
        self_history = self.revision_history()
362
361
        self_len = len(self_history)
446
445
        """
447
446
        raise NotImplementedError('move is abstract')
448
447
 
449
 
    def revert(self, filenames, old_tree=None, backups=True):
450
 
        """Restore selected files to the versions from a previous tree.
451
 
 
452
 
        backups
453
 
            If true (default) backups are made of files before
454
 
            they're renamed.
455
 
        """
456
 
        raise NotImplementedError('revert is abstract')
457
 
 
458
 
    def pending_merges(self):
459
 
        """Return a list of pending merges.
460
 
 
461
 
        These are revisions that have been merged into the working
462
 
        directory but not yet committed.
463
 
        """
464
 
        raise NotImplementedError('pending_merges is abstract')
465
 
 
466
 
    def add_pending_merge(self, *revision_ids):
467
 
        # TODO: Perhaps should check at this point that the
468
 
        # history of the revision is actually present?
469
 
        raise NotImplementedError('add_pending_merge is abstract')
470
 
 
471
 
    def set_pending_merges(self, rev_list):
472
 
        raise NotImplementedError('set_pending_merges is abstract')
473
 
 
474
448
    def get_parent(self):
475
449
        """Return the parent location of the branch.
476
450
 
538
512
    _lock_count = None
539
513
    _lock = None
540
514
    _inventory_weave = None
 
515
    # If set to False (by a plugin, etc) BzrBranch will not set the
 
516
    # mode on created files or directories
 
517
    _set_file_mode = True
 
518
    _set_dir_mode = True
541
519
    
542
520
    # Map some sort of prefix into a namespace
543
521
    # stuff like "revno:10", "revid:", etc.
588
566
        if init:
589
567
            self._make_control()
590
568
        self._check_format(relax_version_check)
 
569
        self._find_modes()
591
570
 
592
571
        def get_store(name, compressed=True, prefixed=False):
593
 
            # FIXME: This approach of assuming stores are all entirely compressed
594
 
            # or entirely uncompressed is tidy, but breaks upgrade from 
595
 
            # some existing branches where there's a mixture; we probably 
596
 
            # still want the option to look for both.
597
 
            relpath = self._rel_controlfilename(name)
 
572
            relpath = self._rel_controlfilename(unicode(name))
598
573
            store = TextStore(self._transport.clone(relpath),
 
574
                              dir_mode=self._dir_mode,
 
575
                              file_mode=self._file_mode,
599
576
                              prefixed=prefixed,
600
577
                              compressed=compressed)
601
 
            #if self._transport.should_cache():
602
 
            #    cache_path = os.path.join(self.cache_root, name)
603
 
            #    os.mkdir(cache_path)
604
 
            #    store = bzrlib.store.CachedStore(store, cache_path)
605
578
            return store
 
579
 
606
580
        def get_weave(name, prefixed=False):
607
 
            relpath = self._rel_controlfilename(name)
608
 
            ws = WeaveStore(self._transport.clone(relpath), prefixed=prefixed)
 
581
            relpath = self._rel_controlfilename(unicode(name))
 
582
            ws = WeaveStore(self._transport.clone(relpath),
 
583
                            prefixed=prefixed,
 
584
                            dir_mode=self._dir_mode,
 
585
                            file_mode=self._file_mode)
609
586
            if self._transport.should_cache():
610
587
                ws.enable_cache = True
611
588
            return ws
615
592
            self.text_store = get_store('text-store')
616
593
            self.revision_store = get_store('revision-store')
617
594
        elif self._branch_format == 5:
618
 
            self.control_weaves = get_weave('')
619
 
            self.weave_store = get_weave('weaves')
620
 
            self.revision_store = get_store('revision-store', compressed=False)
 
595
            self.control_weaves = get_weave(u'')
 
596
            self.weave_store = get_weave(u'weaves')
 
597
            self.revision_store = get_store(u'revision-store', compressed=False)
621
598
        elif self._branch_format == 6:
622
 
            self.control_weaves = get_weave('')
623
 
            self.weave_store = get_weave('weaves', prefixed=True)
624
 
            self.revision_store = get_store('revision-store', compressed=False,
 
599
            self.control_weaves = get_weave(u'')
 
600
            self.weave_store = get_weave(u'weaves', prefixed=True)
 
601
            self.revision_store = get_store(u'revision-store', compressed=False,
625
602
                                            prefixed=True)
626
603
        self.revision_store.register_suffix('sig')
627
604
        self._transaction = None
682
659
        self._transaction = new_transaction
683
660
 
684
661
    def lock_write(self):
685
 
        mutter("lock write: %s (%s)", self, self._lock_count)
 
662
        #mutter("lock write: %s (%s)", self, self._lock_count)
686
663
        # TODO: Upgrade locking to support using a Transport,
687
664
        # and potentially a remote locking protocol
688
665
        if self._lock_mode:
698
675
            self._set_transaction(transactions.PassThroughTransaction())
699
676
 
700
677
    def lock_read(self):
701
 
        mutter("lock read: %s (%s)", self, self._lock_count)
 
678
        #mutter("lock read: %s (%s)", self, self._lock_count)
702
679
        if self._lock_mode:
703
680
            assert self._lock_mode in ('r', 'w'), \
704
681
                   "invalid lock mode %r" % self._lock_mode
713
690
            self.get_transaction().set_cache_size(5000)
714
691
                        
715
692
    def unlock(self):
716
 
        mutter("unlock: %s (%s)", self, self._lock_count)
 
693
        #mutter("unlock: %s (%s)", self, self._lock_count)
717
694
        if not self._lock_mode:
718
695
            raise LockError('branch %r is not locked' % (self))
719
696
 
731
708
 
732
709
    def _rel_controlfilename(self, file_or_path):
733
710
        if not isinstance(file_or_path, basestring):
734
 
            file_or_path = '/'.join(file_or_path)
 
711
            file_or_path = u'/'.join(file_or_path)
735
712
        if file_or_path == '':
736
713
            return bzrlib.BZRDIR
737
 
        return bzrlib.transport.urlescape(bzrlib.BZRDIR + '/' + file_or_path)
 
714
        return bzrlib.transport.urlescape(bzrlib.BZRDIR + u'/' + file_or_path)
738
715
 
739
716
    def controlfilename(self, file_or_path):
740
717
        """See Branch.controlfilename."""
778
755
                    f = codecs.getwriter('utf-8')(f, errors='replace')
779
756
            path = self._rel_controlfilename(path)
780
757
            ctrl_files.append((path, f))
781
 
        self._transport.put_multi(ctrl_files)
 
758
        self._transport.put_multi(ctrl_files, mode=self._file_mode)
 
759
 
 
760
    def _find_modes(self, path=None):
 
761
        """Determine the appropriate modes for files and directories."""
 
762
        try:
 
763
            if path is None:
 
764
                path = self._rel_controlfilename('')
 
765
            st = self._transport.stat(path)
 
766
        except errors.TransportNotPossible:
 
767
            self._dir_mode = 0755
 
768
            self._file_mode = 0644
 
769
        else:
 
770
            self._dir_mode = st.st_mode & 07777
 
771
            # Remove the sticky and execute bits for files
 
772
            self._file_mode = self._dir_mode & ~07111
 
773
        if not self._set_dir_mode:
 
774
            self._dir_mode = None
 
775
        if not self._set_file_mode:
 
776
            self._file_mode = None
782
777
 
783
778
    def _make_control(self):
784
779
        from bzrlib.inventory import Inventory
796
791
        bzrlib.weavefile.write_weave_v5(Weave(), sio)
797
792
        empty_weave = sio.getvalue()
798
793
 
799
 
        dirs = [[], 'revision-store', 'weaves']
 
794
        cfn = self._rel_controlfilename
 
795
        # Since we don't have a .bzr directory, inherit the
 
796
        # mode from the root directory
 
797
        self._find_modes(u'.')
 
798
 
 
799
        dirs = ['', 'revision-store', 'weaves']
800
800
        files = [('README', 
801
801
            "This is a Bazaar-NG control directory.\n"
802
802
            "Do not change any files in this directory.\n"),
809
809
            ('inventory.weave', empty_weave),
810
810
            ('ancestry.weave', empty_weave)
811
811
        ]
812
 
        cfn = self._rel_controlfilename
813
 
        self._transport.mkdir_multi([cfn(d) for d in dirs])
 
812
        self._transport.mkdir_multi([cfn(d) for d in dirs], mode=self._dir_mode)
814
813
        self.put_controlfiles(files)
815
814
        mutter('created control directory in ' + self._transport.base)
816
815
 
850
849
        return inv.root.file_id
851
850
 
852
851
    @needs_read_lock
853
 
    def print_file(self, file, revno):
 
852
    def print_file(self, file, revision_id):
854
853
        """See Branch.print_file."""
855
 
        tree = self.revision_tree(self.get_rev_id(revno))
 
854
        tree = self.revision_tree(revision_id)
856
855
        # use inventory as it was in that revision
857
856
        file_id = tree.inventory.path2id(file)
858
857
        if not file_id:
859
 
            raise BzrError("%r is not present in revision %s" % (file, revno))
 
858
            try:
 
859
                revno = self.revision_id_to_revno(revision_id)
 
860
            except errors.NoSuchRevision:
 
861
                # TODO: This should not be BzrError,
 
862
                # but NoSuchFile doesn't fit either
 
863
                raise BzrError('%r is not present in revision %s' 
 
864
                                % (file, revision_id))
 
865
            else:
 
866
                raise BzrError('%r is not present in revision %s'
 
867
                                % (file, revno))
860
868
        tree.print_file(file_id)
861
869
 
862
870
    @needs_write_lock
871
879
    @needs_write_lock
872
880
    def set_revision_history(self, rev_history):
873
881
        """See Branch.set_revision_history."""
 
882
        old_revision = self.last_revision()
 
883
        new_revision = rev_history[-1]
874
884
        self.put_controlfile('revision-history', '\n'.join(rev_history))
 
885
        try:
 
886
            self.working_tree().set_last_revision(new_revision, old_revision)
 
887
        except NoWorkingTree:
 
888
            mutter('Unable to set_last_revision without a working tree.')
875
889
 
876
890
    def has_revision(self, revision_id):
877
891
        """See Branch.has_revision."""
879
893
                or self.revision_store.has_id(revision_id))
880
894
 
881
895
    @needs_read_lock
882
 
    def get_revision_xml_file(self, revision_id):
883
 
        """See Branch.get_revision_xml_file."""
 
896
    def _get_revision_xml_file(self, revision_id):
884
897
        if not revision_id or not isinstance(revision_id, basestring):
885
898
            raise InvalidRevisionId(revision_id=revision_id, branch=self)
886
899
        try:
888
901
        except (IndexError, KeyError):
889
902
            raise bzrlib.errors.NoSuchRevision(self, revision_id)
890
903
 
891
 
    #deprecated
892
 
    get_revision_xml = get_revision_xml_file
893
 
 
894
904
    def get_revision_xml(self, revision_id):
895
905
        """See Branch.get_revision_xml."""
896
 
        return self.get_revision_xml_file(revision_id).read()
897
 
 
 
906
        return self._get_revision_xml_file(revision_id).read()
898
907
 
899
908
    def get_revision(self, revision_id):
900
909
        """See Branch.get_revision."""
901
 
        xml_file = self.get_revision_xml_file(revision_id)
 
910
        xml_file = self._get_revision_xml_file(revision_id)
902
911
 
903
912
        try:
904
913
            r = bzrlib.xml5.serializer_v5.read_revision(xml_file)
1013
1022
                else:
1014
1023
                    raise e
1015
1024
        
1016
 
    def revision_id_to_revno(self, revision_id):
1017
 
        """Given a revision id, return its revno"""
1018
 
        if revision_id is None:
1019
 
            return 0
1020
 
        history = self.revision_history()
1021
 
        try:
1022
 
            return history.index(revision_id) + 1
1023
 
        except ValueError:
1024
 
            raise bzrlib.errors.NoSuchRevision(self, revision_id)
1025
 
 
1026
 
    def get_rev_id(self, revno, history=None):
1027
 
        """Find the revision id of the specified revno."""
1028
 
        if revno == 0:
1029
 
            return None
1030
 
        if history is None:
1031
 
            history = self.revision_history()
1032
 
        elif revno <= 0 or revno > len(history):
1033
 
            raise bzrlib.errors.NoSuchRevision(self, revno)
1034
 
        return history[revno - 1]
1035
 
 
1036
1025
    def revision_tree(self, revision_id):
1037
1026
        """See Branch.revision_tree."""
1038
1027
        # TODO: refactor this to use an existing revision object
1041
1030
            return EmptyTree()
1042
1031
        else:
1043
1032
            inv = self.get_revision_inventory(revision_id)
1044
 
            return RevisionTree(self.weave_store, inv, revision_id)
 
1033
            return RevisionTree(self, inv, revision_id)
 
1034
 
 
1035
    def basis_tree(self):
 
1036
        """See Branch.basis_tree."""
 
1037
        try:
 
1038
            revision_id = self.revision_history()[-1]
 
1039
            xml = self.working_tree().read_basis_inventory(revision_id)
 
1040
            inv = bzrlib.xml5.serializer_v5.read_inventory_from_string(xml)
 
1041
            return RevisionTree(self, inv, revision_id)
 
1042
        except (IndexError, NoSuchFile, NoWorkingTree), e:
 
1043
            return self.revision_tree(self.last_revision())
1045
1044
 
1046
1045
    def working_tree(self):
1047
1046
        """See Branch.working_tree."""
1055
1054
        """See Branch.pull."""
1056
1055
        source.lock_read()
1057
1056
        try:
 
1057
            old_count = len(self.revision_history())
1058
1058
            try:
1059
1059
                self.update_revisions(source)
1060
1060
            except DivergedBranches:
1061
1061
                if not overwrite:
1062
1062
                    raise
 
1063
            if overwrite:
1063
1064
                self.set_revision_history(source.revision_history())
 
1065
            new_count = len(self.revision_history())
 
1066
            return new_count - old_count
1064
1067
        finally:
1065
1068
            source.unlock()
1066
1069
 
1071
1074
        for l in _locs:
1072
1075
            try:
1073
1076
                return self.controlfile(l, 'r').read().strip('\n')
1074
 
            except IOError, e:
1075
 
                if e.errno != errno.ENOENT:
1076
 
                    raise
 
1077
            except NoSuchFile:
 
1078
                pass
1077
1079
        return None
1078
1080
 
1079
1081
    def get_push_location(self):
1102
1104
    def tree_config(self):
1103
1105
        return TreeConfig(self)
1104
1106
 
1105
 
    def check_revno(self, revno):
1106
 
        """\
1107
 
        Check whether a revno corresponds to any revision.
1108
 
        Zero (the NULL revision) is considered valid.
1109
 
        """
1110
 
        if revno != 0:
1111
 
            self.check_real_revno(revno)
1112
 
            
1113
 
    def check_real_revno(self, revno):
1114
 
        """\
1115
 
        Check whether a revno corresponds to a real revision.
1116
 
        Zero (the NULL revision) is considered invalid
1117
 
        """
1118
 
        if revno < 1 or revno > self.revno():
1119
 
            raise InvalidRevisionNumber(revno)
1120
 
        
1121
1107
    def sign_revision(self, revision_id, gpg_strategy):
1122
1108
        """See Branch.sign_revision."""
1123
1109
        plaintext = Testament.from_revision(self, revision_id).as_short_text()
1172
1158
        ...   orig.base == clone.base
1173
1159
        ...
1174
1160
        False
1175
 
        >>> os.path.isfile(os.path.join(clone.base, "file1"))
 
1161
        >>> os.path.isfile(pathjoin(clone.base, "file1"))
1176
1162
        True
1177
1163
        """
1178
1164
        from shutil import copytree
1179
 
        from tempfile import mkdtemp
 
1165
        from bzrlib.osutils import mkdtemp
1180
1166
        base = mkdtemp()
1181
1167
        os.rmdir(base)
1182
1168
        copytree(self.base, base, symlinks=True)
1190
1176
 
1191
1177
def is_control_file(filename):
1192
1178
    ## FIXME: better check
1193
 
    filename = os.path.normpath(filename)
 
1179
    filename = normpath(filename)
1194
1180
    while filename != '':
1195
1181
        head, tail = os.path.split(filename)
1196
1182
        ## mutter('check %r for control file' % ((head, tail), ))