/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

Merge from bzr.ab.integration

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# Copyright (C) 2005, 2006 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
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
 
 
7
#
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
23
import sys
24
24
from unittest import TestSuite
25
25
from warnings import warn
26
 
try:
27
 
    import xml.sax.saxutils
28
 
except ImportError:
29
 
    raise ImportError("We were unable to import 'xml.sax.saxutils',"
30
 
                      " most likely you have an xml.pyc or xml.pyo file"
31
 
                      " lying around in your bzrlib directory."
32
 
                      " Please remove it.")
33
 
 
34
26
 
35
27
import bzrlib
36
28
import bzrlib.bzrdir as bzrdir
47
39
                           NoWorkingTree)
48
40
import bzrlib.inventory as inventory
49
41
from bzrlib.inventory import Inventory
50
 
from bzrlib.lockable_files import LockableFiles
 
42
from bzrlib.lockable_files import LockableFiles, TransportLock
 
43
from bzrlib.lockdir import LockDir
51
44
from bzrlib.osutils import (isdir, quotefn,
52
45
                            rename, splitpath, sha_file,
53
46
                            file_kind, abspath, normpath, pathjoin,
183
176
        """
184
177
        raise NotImplementedError('abspath is abstract')
185
178
 
 
179
    def bind(self, other):
 
180
        """Bind the local branch the other branch.
 
181
 
 
182
        :param other: The branch to bind to
 
183
        :type other: Branch
 
184
        """
 
185
        raise errors.UpgradeRequired(self.base)
 
186
 
186
187
    @needs_write_lock
187
188
    def fetch(self, from_branch, last_revision=None, pb=None):
188
189
        """Copy revisions from from_branch into this branch.
199
200
            raise Exception("can't fetch from a branch to itself %s, %s" % 
200
201
                            (self.base, to_branch.base))
201
202
        if pb is None:
202
 
            pb = bzrlib.ui.ui_factory.progress_bar()
 
203
            nested_pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
204
            pb = nested_pb
 
205
        else:
 
206
            nested_pb = None
203
207
 
204
208
        from_branch.lock_read()
205
209
        try:
213
217
                    last_revision = NULL_REVISION
214
218
            return self.repository.fetch(from_branch.repository,
215
219
                                         revision_id=last_revision,
216
 
                                         pb=pb)
 
220
                                         pb=nested_pb)
217
221
        finally:
 
222
            if nested_pb is not None:
 
223
                nested_pb.finished()
218
224
            from_branch.unlock()
219
225
 
 
226
    def get_bound_location(self):
 
227
        """Return the URL of the rbanch we are bound to.
 
228
 
 
229
        Older format branches cannot bind, please be sure to use a metadir
 
230
        branch.
 
231
        """
 
232
        return None
 
233
 
 
234
    def get_master_branch(self):
 
235
        """Return the branch we are bound to.
 
236
        
 
237
        :return: Either a Branch, or None
 
238
        """
 
239
        return None
 
240
 
220
241
    def get_root_id(self):
221
242
        """Return the id of this branches root"""
222
243
        raise NotImplementedError('get_root_id is abstract')
243
264
        """
244
265
        return len(self.revision_history())
245
266
 
 
267
    def unbind(self):
 
268
        """Older format branches cannot bind or unbind."""
 
269
        raise errors.UpgradeRequired(self.base)
 
270
 
246
271
    def last_revision(self):
247
272
        """Return last patch hash, or None if no history."""
248
273
        ph = self.revision_history()
251
276
        else:
252
277
            return None
253
278
 
254
 
    def missing_revisions(self, other, stop_revision=None, diverged_ok=False):
 
279
    def missing_revisions(self, other, stop_revision=None):
255
280
        """Return a list of new revisions that would perfectly fit.
256
281
        
257
282
        If self and other have not diverged, return a list of the revisions
301
326
        return other_history[self_len:stop_revision]
302
327
 
303
328
    def update_revisions(self, other, stop_revision=None):
304
 
        """Pull in new perfect-fit revisions."""
 
329
        """Pull in new perfect-fit revisions.
 
330
 
 
331
        :param other: Another Branch to pull from
 
332
        :param stop_revision: Updated until the given revision
 
333
        :return: None
 
334
        """
305
335
        raise NotImplementedError('update_revisions is abstract')
306
336
 
307
337
    def pullable_revisions(self, other, stop_revision):
380
410
    def set_parent(self, url):
381
411
        raise NotImplementedError('set_parent is abstract')
382
412
 
 
413
    @needs_write_lock
 
414
    def update(self):
 
415
        """Synchronise this branch with the master branch if any. 
 
416
 
 
417
        :return: None or the last_revision pivoted out during the update.
 
418
        """
 
419
        return None
 
420
 
383
421
    def check_revno(self, revno):
384
422
        """\
385
423
        Check whether a revno corresponds to any revision.
579
617
        utf8_files = [('revision-history', ''),
580
618
                      ('branch-name', ''),
581
619
                      ]
582
 
        control_files = LockableFiles(branch_transport, 'branch-lock')
 
620
        control_files = LockableFiles(branch_transport, 'branch-lock',
 
621
                                      TransportLock)
 
622
        control_files.create_lock()
583
623
        control_files.lock_write()
584
624
        try:
585
625
            for file, content in utf8_files:
613
653
    This format has:
614
654
     - a revision-history file.
615
655
     - a format string
616
 
     - a lock file.
 
656
     - a lock dir guarding the branch itself
 
657
     - all of this stored in a branch/ subdirectory
617
658
     - works with shared repositories.
 
659
 
 
660
    This format is new in bzr 0.8.
618
661
    """
619
662
 
620
663
    def get_format_string(self):
623
666
        
624
667
    def initialize(self, a_bzrdir):
625
668
        """Create a branch of this format in a_bzrdir."""
626
 
        mutter('creating branch in %s', a_bzrdir.transport.base)
 
669
        mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
627
670
        branch_transport = a_bzrdir.get_branch_transport(self)
628
 
 
629
671
        utf8_files = [('revision-history', ''),
630
672
                      ('branch-name', ''),
631
673
                      ]
632
 
        lock_file = 'lock'
633
 
        branch_transport.put(lock_file, StringIO()) # TODO get the file mode from the bzrdir lock files., mode=file_mode)
634
 
        control_files = LockableFiles(branch_transport, 'lock')
 
674
        control_files = LockableFiles(branch_transport, 'lock', LockDir)
 
675
        control_files.create_lock()
635
676
        control_files.lock_write()
636
677
        control_files.put_utf8('format', self.get_format_string())
637
678
        try:
655
696
            format = BranchFormat.find_format(a_bzrdir)
656
697
            assert format.__class__ == self.__class__
657
698
        transport = a_bzrdir.get_branch_transport(None)
658
 
        control_files = LockableFiles(transport, 'lock')
659
 
        return BzrBranch(_format=self,
660
 
                         _control_files=control_files,
661
 
                         a_bzrdir=a_bzrdir,
662
 
                         _repository=a_bzrdir.find_repository())
 
699
        control_files = LockableFiles(transport, 'lock', LockDir)
 
700
        return BzrBranch5(_format=self,
 
701
                          _control_files=control_files,
 
702
                          a_bzrdir=a_bzrdir,
 
703
                          _repository=a_bzrdir.find_repository())
 
704
 
 
705
    def __str__(self):
 
706
        return "Bazaar-NG Metadir branch format 5"
663
707
 
664
708
 
665
709
class BranchReferenceFormat(BranchFormat):
744
788
    really matter if it's on an nfs/smb/afs/coda/... share, as long as
745
789
    it's writable, and can be accessed via the normal filesystem API.
746
790
    """
747
 
    # We actually expect this class to be somewhat short-lived; part of its
748
 
    # purpose is to try to isolate what bits of the branch logic are tied to
749
 
    # filesystem access, so that in a later step, we can extricate them to
750
 
    # a separarte ("storage") class.
751
 
    _inventory_weave = None
752
791
    
753
 
    # Map some sort of prefix into a namespace
754
 
    # stuff like "revno:10", "revid:", etc.
755
 
    # This should match a prefix with a function which accepts
756
 
    REVISION_NAMESPACES = {}
757
 
 
758
792
    def __init__(self, transport=DEPRECATED_PARAMETER, init=DEPRECATED_PARAMETER,
759
793
                 relax_version_check=DEPRECATED_PARAMETER, _format=None,
760
794
                 _control_files=None, a_bzrdir=None, _repository=None):
901
935
        # TODO: test for failed two phase locks. This is known broken.
902
936
        self.repository.unlock()
903
937
        self.control_files.unlock()
904
 
 
 
938
        
905
939
    def peek_lock_mode(self):
906
940
        if self.control_files._lock_count == 0:
907
941
            return None
927
961
        """See Branch.set_revision_history."""
928
962
        self.control_files.put_utf8(
929
963
            'revision-history', '\n'.join(rev_history))
 
964
        transaction = self.get_transaction()
 
965
        history = transaction.map.find_revision_history()
 
966
        if history is not None:
 
967
            # update the revision history in the identity map.
 
968
            history[:] = list(rev_history)
 
969
            # this call is disabled because revision_history is 
 
970
            # not really an object yet, and the transaction is for objects.
 
971
            # transaction.register_dirty(history)
 
972
        else:
 
973
            transaction.map.add_revision_history(rev_history)
 
974
            # this call is disabled because revision_history is 
 
975
            # not really an object yet, and the transaction is for objects.
 
976
            # transaction.register_clean(history)
930
977
 
931
978
    def get_revision_delta(self, revno):
932
979
        """Return the delta for one revision.
951
998
    @needs_read_lock
952
999
    def revision_history(self):
953
1000
        """See Branch.revision_history."""
954
 
        # FIXME are transactions bound to control files ? RBC 20051121
955
1001
        transaction = self.get_transaction()
956
1002
        history = transaction.map.find_revision_history()
957
1003
        if history is not None:
979
1025
            self.append_revision(*pullable_revs)
980
1026
 
981
1027
    def pullable_revisions(self, other, stop_revision):
982
 
        """See Branch.pullable_revisions."""
983
1028
        other_revno = other.revision_id_to_revno(stop_revision)
984
1029
        try:
985
1030
            return self.missing_revisions(other, other_revno)
1063
1108
    def tree_config(self):
1064
1109
        return TreeConfig(self)
1065
1110
 
1066
 
    def _get_truncated_history(self, revision_id):
1067
 
        history = self.revision_history()
1068
 
        if revision_id is None:
1069
 
            return history
 
1111
 
 
1112
class BzrBranch5(BzrBranch):
 
1113
    """A format 5 branch. This supports new features over plan branches.
 
1114
 
 
1115
    It has support for a master_branch which is the data for bound branches.
 
1116
    """
 
1117
 
 
1118
    def __init__(self,
 
1119
                 _format,
 
1120
                 _control_files,
 
1121
                 a_bzrdir,
 
1122
                 _repository):
 
1123
        super(BzrBranch5, self).__init__(_format=_format,
 
1124
                                         _control_files=_control_files,
 
1125
                                         a_bzrdir=a_bzrdir,
 
1126
                                         _repository=_repository)
 
1127
        
 
1128
    @needs_write_lock
 
1129
    def pull(self, source, overwrite=False, stop_revision=None):
 
1130
        """Updates branch.pull to be bound branch aware."""
 
1131
        bound_location = self.get_bound_location()
 
1132
        if source.base != bound_location:
 
1133
            # not pulling from master, so we need to update master.
 
1134
            master_branch = self.get_master_branch()
 
1135
            if master_branch:
 
1136
                master_branch.pull(source)
 
1137
                source = master_branch
 
1138
        return super(BzrBranch5, self).pull(source, overwrite, stop_revision)
 
1139
 
 
1140
    def get_bound_location(self):
1070
1141
        try:
1071
 
            idx = history.index(revision_id)
1072
 
        except ValueError:
1073
 
            raise InvalidRevisionId(revision_id=revision, branch=self)
1074
 
        return history[:idx+1]
 
1142
            return self.control_files.get_utf8('bound').read()[:-1]
 
1143
        except errors.NoSuchFile:
 
1144
            return None
1075
1145
 
1076
1146
    @needs_read_lock
1077
 
    def _clone_weave(self, to_location, revision=None, basis_branch=None):
1078
 
        # prevent leakage
1079
 
        from bzrlib.workingtree import WorkingTree
1080
 
        assert isinstance(to_location, basestring)
1081
 
        if basis_branch is not None:
1082
 
            note("basis_branch is not supported for fast weave copy yet.")
1083
 
 
1084
 
        history = self._get_truncated_history(revision)
1085
 
        if not bzrlib.osutils.lexists(to_location):
1086
 
            os.mkdir(to_location)
1087
 
        bzrdir_to = self.bzrdir._format.initialize(to_location)
1088
 
        self.repository.clone(bzrdir_to)
1089
 
        branch_to = bzrdir_to.create_branch()
1090
 
        mutter("copy branch from %s to %s", self, branch_to)
1091
 
 
1092
 
        # FIXME duplicate code with base .clone().
1093
 
        # .. would template method be useful here?  RBC 20051207
1094
 
        branch_to.set_parent(self.base)
1095
 
        branch_to.append_revision(*history)
1096
 
        WorkingTree.create(branch_to, branch_to.base)
1097
 
        mutter("copied")
1098
 
        return branch_to
 
1147
    def get_master_branch(self):
 
1148
        """Return the branch we are bound to.
 
1149
        
 
1150
        :return: Either a Branch, or None
 
1151
 
 
1152
        This could memoise the branch, but if thats done
 
1153
        it must be revalidated on each new lock.
 
1154
        So for now we just dont memoise it.
 
1155
        # RBC 20060304 review this decision.
 
1156
        """
 
1157
        bound_loc = self.get_bound_location()
 
1158
        if not bound_loc:
 
1159
            return None
 
1160
        try:
 
1161
            return Branch.open(bound_loc)
 
1162
        except (errors.NotBranchError, errors.ConnectionError), e:
 
1163
            raise errors.BoundBranchConnectionFailure(
 
1164
                    self, bound_loc, e)
 
1165
 
 
1166
    @needs_write_lock
 
1167
    def set_bound_location(self, location):
 
1168
        """Set the target where this branch is bound to.
 
1169
 
 
1170
        :param location: URL to the target branch
 
1171
        """
 
1172
        if location:
 
1173
            self.control_files.put_utf8('bound', location+'\n')
 
1174
        else:
 
1175
            try:
 
1176
                self.control_files._transport.delete('bound')
 
1177
            except NoSuchFile:
 
1178
                return False
 
1179
            return True
 
1180
 
 
1181
    @needs_write_lock
 
1182
    def bind(self, other):
 
1183
        """Bind the local branch the other branch.
 
1184
 
 
1185
        :param other: The branch to bind to
 
1186
        :type other: Branch
 
1187
        """
 
1188
        # TODO: jam 20051230 Consider checking if the target is bound
 
1189
        #       It is debatable whether you should be able to bind to
 
1190
        #       a branch which is itself bound.
 
1191
        #       Committing is obviously forbidden,
 
1192
        #       but binding itself may not be.
 
1193
        #       Since we *have* to check at commit time, we don't
 
1194
        #       *need* to check here
 
1195
        self.pull(other)
 
1196
 
 
1197
        # we are now equal to or a suffix of other.
 
1198
 
 
1199
        # Since we have 'pulled' from the remote location,
 
1200
        # now we should try to pull in the opposite direction
 
1201
        # in case the local tree has more revisions than the
 
1202
        # remote one.
 
1203
        # There may be a different check you could do here
 
1204
        # rather than actually trying to install revisions remotely.
 
1205
        # TODO: capture an exception which indicates the remote branch
 
1206
        #       is not writeable. 
 
1207
        #       If it is up-to-date, this probably should not be a failure
 
1208
        
 
1209
        # lock other for write so the revision-history syncing cannot race
 
1210
        other.lock_write()
 
1211
        try:
 
1212
            other.pull(self)
 
1213
            # if this does not error, other now has the same last rev we do
 
1214
            # it can only error if the pull from other was concurrent with
 
1215
            # a commit to other from someone else.
 
1216
 
 
1217
            # until we ditch revision-history, we need to sync them up:
 
1218
            self.set_revision_history(other.revision_history())
 
1219
            # now other and self are up to date with each other and have the
 
1220
            # same revision-history.
 
1221
        finally:
 
1222
            other.unlock()
 
1223
 
 
1224
        self.set_bound_location(other.base)
 
1225
 
 
1226
    @needs_write_lock
 
1227
    def unbind(self):
 
1228
        """If bound, unbind"""
 
1229
        return self.set_bound_location(None)
 
1230
 
 
1231
    @needs_write_lock
 
1232
    def update(self):
 
1233
        """Synchronise this branch with the master branch if any. 
 
1234
 
 
1235
        :return: None or the last_revision that was pivoted out during the
 
1236
                 update.
 
1237
        """
 
1238
        master = self.get_master_branch()
 
1239
        if master is not None:
 
1240
            old_tip = self.last_revision()
 
1241
            self.pull(master, overwrite=True)
 
1242
            if old_tip in self.repository.get_ancestry(self.last_revision()):
 
1243
                return None
 
1244
            return old_tip
 
1245
        return None
1099
1246
 
1100
1247
 
1101
1248
class BranchTestProviderAdapter(object):