~michael-ellerman/bzr/mpe

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

[merge] bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
 
18
18
from copy import deepcopy
19
19
from cStringIO import StringIO
20
 
import errno
21
 
import os
22
 
import shutil
23
 
import sys
24
20
from unittest import TestSuite
25
21
from warnings import warn
26
22
 
27
23
import bzrlib
28
 
import bzrlib.bzrdir as bzrdir
 
24
from bzrlib import bzrdir, errors, lockdir, osutils, revision, \
 
25
        tree, \
 
26
        ui, \
 
27
        urlutils
29
28
from bzrlib.config import TreeConfig
30
29
from bzrlib.decorators import needs_read_lock, needs_write_lock
31
 
from bzrlib.delta import compare_trees
32
30
import bzrlib.errors as errors
33
 
from bzrlib.errors import (BzrError, InvalidRevisionNumber, InvalidRevisionId,
34
 
                           NoSuchRevision, HistoryMissing, NotBranchError,
35
 
                           DivergedBranches, LockError,
36
 
                           UninitializableFormat,
37
 
                           UnlistableStore,
38
 
                           UnlistableBranch, NoSuchFile, NotVersionedError,
39
 
                           NoWorkingTree)
40
 
import bzrlib.inventory as inventory
41
 
from bzrlib.inventory import Inventory
 
31
from bzrlib.errors import (BzrError, BzrCheckError, DivergedBranches, 
 
32
                           HistoryMissing, InvalidRevisionId, 
 
33
                           InvalidRevisionNumber, LockError, NoSuchFile, 
 
34
                           NoSuchRevision, NoWorkingTree, NotVersionedError,
 
35
                           NotBranchError, UninitializableFormat, 
 
36
                           UnlistableStore, UnlistableBranch, 
 
37
                           )
42
38
from bzrlib.lockable_files import LockableFiles, TransportLock
43
 
from bzrlib.lockdir import LockDir
44
 
from bzrlib.osutils import (isdir, quotefn,
45
 
                            rename, splitpath, sha_file,
46
 
                            file_kind, abspath, normpath, pathjoin,
47
 
                            safe_unicode,
48
 
                            rmtree,
49
 
                            )
50
 
from bzrlib.textui import show_status
 
39
from bzrlib.symbol_versioning import (deprecated_function,
 
40
                                      deprecated_method,
 
41
                                      DEPRECATED_PARAMETER,
 
42
                                      deprecated_passed,
 
43
                                      zero_eight,
 
44
                                      )
51
45
from bzrlib.trace import mutter, note
52
 
from bzrlib.tree import EmptyTree, RevisionTree
53
 
from bzrlib.repository import Repository
54
 
from bzrlib.revision import (
55
 
                             is_ancestor,
56
 
                             NULL_REVISION,
57
 
                             Revision,
58
 
                             )
59
 
from bzrlib.store import copy_all
60
 
from bzrlib.symbol_versioning import *
61
 
import bzrlib.transactions as transactions
62
 
from bzrlib.transport import Transport, get_transport
63
 
from bzrlib.tree import EmptyTree, RevisionTree
64
 
import bzrlib.ui
65
 
import bzrlib.xml5
66
46
 
67
47
 
68
48
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
219
199
        if self.base == from_branch.base:
220
200
            return (0, [])
221
201
        if pb is None:
222
 
            nested_pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
202
            nested_pb = ui.ui_factory.nested_progress_bar()
223
203
            pb = nested_pb
224
204
        else:
225
205
            nested_pb = None
233
213
                    last_revision = from_history[-1]
234
214
                else:
235
215
                    # no history in the source branch
236
 
                    last_revision = NULL_REVISION
 
216
                    last_revision = revision.NULL_REVISION
237
217
            return self.repository.fetch(from_branch.repository,
238
218
                                         revision_id=last_revision,
239
219
                                         pb=nested_pb)
249
229
        branch.
250
230
        """
251
231
        return None
 
232
    
 
233
    def get_commit_builder(self, parents, config=None, timestamp=None, 
 
234
                           timezone=None, committer=None, revprops=None, 
 
235
                           revision_id=None):
 
236
        """Obtain a CommitBuilder for this branch.
 
237
        
 
238
        :param parents: Revision ids of the parents of the new revision.
 
239
        :param config: Optional configuration to use.
 
240
        :param timestamp: Optional timestamp recorded for commit.
 
241
        :param timezone: Optional timezone for timestamp.
 
242
        :param committer: Optional committer to set for commit.
 
243
        :param revprops: Optional dictionary of revision properties.
 
244
        :param revision_id: Optional revision id.
 
245
        """
 
246
 
 
247
        if config is None:
 
248
            config = bzrlib.config.BranchConfig(self)
 
249
        
 
250
        return self.repository.get_commit_builder(self, parents, config, 
 
251
            timestamp, timezone, committer, revprops, revision_id)
252
252
 
253
253
    def get_master_branch(self):
254
254
        """Return the branch we are bound to.
257
257
        """
258
258
        return None
259
259
 
 
260
    def get_revision_delta(self, revno):
 
261
        """Return the delta for one revision.
 
262
 
 
263
        The delta is relative to its mainline predecessor, or the
 
264
        empty tree for revision 1.
 
265
        """
 
266
        assert isinstance(revno, int)
 
267
        rh = self.revision_history()
 
268
        if not (1 <= revno <= len(rh)):
 
269
            raise InvalidRevisionNumber(revno)
 
270
        return self.repository.get_revision_delta(rh[revno-1])
 
271
 
260
272
    def get_root_id(self):
261
273
        """Return the id of this branches root"""
262
274
        raise NotImplementedError('get_root_id is abstract')
315
327
        else:
316
328
            assert isinstance(stop_revision, int)
317
329
            if stop_revision > other_len:
318
 
                raise bzrlib.errors.NoSuchRevision(self, stop_revision)
 
330
                raise errors.NoSuchRevision(self, stop_revision)
319
331
        return other_history[self_len:stop_revision]
320
332
 
321
333
    def update_revisions(self, other, stop_revision=None):
431
443
        revision_id: if not None, the revision history in the new branch will
432
444
                     be truncated to end with revision_id.
433
445
        """
434
 
        # for API compatability, until 0.8 releases we provide the old api:
 
446
        # for API compatibility, until 0.8 releases we provide the old api:
435
447
        # def clone(self, to_location, revision=None, basis_branch=None, to_branch_format=None):
436
448
        # after 0.8 releases, the *args and **kwargs should be changed:
437
449
        # def clone(self, to_bzrdir, revision_id=None):
439
451
            kwargs.get('revision', None) or
440
452
            kwargs.get('basis_branch', None) or
441
453
            (len(args) and isinstance(args[0], basestring))):
442
 
            # backwards compatability api:
 
454
            # backwards compatibility api:
443
455
            warn("Branch.clone() has been deprecated for BzrDir.clone() from"
444
456
                 " bzrlib 0.8.", DeprecationWarning, stacklevel=3)
445
457
            # get basis_branch
511
523
        if parent:
512
524
            destination.set_parent(parent)
513
525
 
 
526
    @needs_read_lock
 
527
    def check(self):
 
528
        """Check consistency of the branch.
 
529
 
 
530
        In particular this checks that revisions given in the revision-history
 
531
        do actually match up in the revision graph, and that they're all 
 
532
        present in the repository.
 
533
        
 
534
        Callers will typically also want to check the repository.
 
535
 
 
536
        :return: A BranchCheckResult.
 
537
        """
 
538
        mainline_parent_id = None
 
539
        for revision_id in self.revision_history():
 
540
            try:
 
541
                revision = self.repository.get_revision(revision_id)
 
542
            except errors.NoSuchRevision, e:
 
543
                raise errors.BzrCheckError("mainline revision {%s} not in repository"
 
544
                            % revision_id)
 
545
            # In general the first entry on the revision history has no parents.
 
546
            # But it's not illegal for it to have parents listed; this can happen
 
547
            # in imports from Arch when the parents weren't reachable.
 
548
            if mainline_parent_id is not None:
 
549
                if mainline_parent_id not in revision.parent_ids:
 
550
                    raise errors.BzrCheckError("previous revision {%s} not listed among "
 
551
                                        "parents of {%s}"
 
552
                                        % (mainline_parent_id, revision_id))
 
553
            mainline_parent_id = revision_id
 
554
        return BranchCheckResult(self)
 
555
 
514
556
 
515
557
class BranchFormat(object):
516
558
    """An encapsulation of the initialization and open routines for a format.
546
588
        except NoSuchFile:
547
589
            raise NotBranchError(path=transport.base)
548
590
        except KeyError:
549
 
            raise errors.UnknownFormatError(format_string)
 
591
            raise errors.UnknownFormatError(format=format_string)
550
592
 
551
593
    @classmethod
552
594
    def get_default_format(klass):
563
605
 
564
606
    def initialize(self, a_bzrdir):
565
607
        """Create a branch of this format in a_bzrdir."""
566
 
        raise NotImplementedError(self.initialized)
 
608
        raise NotImplementedError(self.initialize)
567
609
 
568
610
    def is_supported(self):
569
611
        """Is this format supported?
679
721
        utf8_files = [('revision-history', ''),
680
722
                      ('branch-name', ''),
681
723
                      ]
682
 
        control_files = LockableFiles(branch_transport, 'lock', LockDir)
 
724
        control_files = LockableFiles(branch_transport, 'lock', lockdir.LockDir)
683
725
        control_files.create_lock()
684
726
        control_files.lock_write()
685
727
        control_files.put_utf8('format', self.get_format_string())
704
746
            format = BranchFormat.find_format(a_bzrdir)
705
747
            assert format.__class__ == self.__class__
706
748
        transport = a_bzrdir.get_branch_transport(None)
707
 
        control_files = LockableFiles(transport, 'lock', LockDir)
 
749
        control_files = LockableFiles(transport, 'lock', lockdir.LockDir)
708
750
        return BzrBranch5(_format=self,
709
751
                          _control_files=control_files,
710
752
                          a_bzrdir=a_bzrdir,
825
867
        self._base = self._transport.base
826
868
        self._format = _format
827
869
        if _control_files is None:
828
 
            raise BzrBadParameterMissing('_control_files')
 
870
            raise ValueError('BzrBranch _control_files is None')
829
871
        self.control_files = _control_files
830
872
        if deprecated_passed(init):
831
873
            warn("BzrBranch.__init__(..., init=XXX): The init parameter is "
846
888
                 stacklevel=2)
847
889
            if (not relax_version_check
848
890
                and not self._format.is_supported()):
849
 
                raise errors.UnsupportedFormatError(
850
 
                        'sorry, branch format %r not supported' % fmt,
851
 
                        ['use a different bzr version',
852
 
                         'or remove the .bzr directory'
853
 
                         ' and "bzr init" again'])
 
891
                raise errors.UnsupportedFormatError(format=fmt)
854
892
        if deprecated_passed(transport):
855
893
            warn("BzrBranch.__init__(transport=XXX...): The transport "
856
894
                 "parameter is deprecated as of bzr 0.8. "
874
912
        # XXX: cache_root seems to be unused, 2006-01-13 mbp
875
913
        if hasattr(self, 'cache_root') and self.cache_root is not None:
876
914
            try:
877
 
                rmtree(self.cache_root)
 
915
                osutils.rmtree(self.cache_root)
878
916
            except:
879
917
                pass
880
918
            self.cache_root = None
923
961
        FIXME: DELETE THIS METHOD when pre 0.8 support is removed.
924
962
        """
925
963
        if format is None:
926
 
            format = BzrBranchFormat.find_format(self.bzrdir)
 
964
            format = BranchFormat.find_format(self.bzrdir)
927
965
        self._format = format
928
966
        mutter("got branch format %s", self._format)
929
967
 
995
1033
            # not really an object yet, and the transaction is for objects.
996
1034
            # transaction.register_clean(history)
997
1035
 
998
 
    def get_revision_delta(self, revno):
999
 
        """Return the delta for one revision.
1000
 
 
1001
 
        The delta is relative to its mainline predecessor, or the
1002
 
        empty tree for revision 1.
1003
 
        """
1004
 
        assert isinstance(revno, int)
1005
 
        rh = self.revision_history()
1006
 
        if not (1 <= revno <= len(rh)):
1007
 
            raise InvalidRevisionNumber(revno)
1008
 
 
1009
 
        # revno is 1-based; list is 0-based
1010
 
 
1011
 
        new_tree = self.repository.revision_tree(rh[revno-1])
1012
 
        if revno == 1:
1013
 
            old_tree = EmptyTree()
1014
 
        else:
1015
 
            old_tree = self.repository.revision_tree(rh[revno-2])
1016
 
        return compare_trees(old_tree, new_tree)
1017
 
 
1018
1036
    @needs_read_lock
1019
1037
    def revision_history(self):
1020
1038
        """See Branch.revision_history."""
1059
1077
            # make a new revision history from the graph
1060
1078
            current_rev_id = stop_revision
1061
1079
            new_history = []
1062
 
            while current_rev_id not in (None, NULL_REVISION):
 
1080
            while current_rev_id not in (None, revision.NULL_REVISION):
1063
1081
                new_history.append(current_rev_id)
1064
1082
                current_rev_id_parents = stop_graph[current_rev_id]
1065
1083
                try:
1078
1096
    @deprecated_method(zero_eight)
1079
1097
    def working_tree(self):
1080
1098
        """Create a Working tree object for this branch."""
1081
 
        from bzrlib.workingtree import WorkingTree
 
1099
 
1082
1100
        from bzrlib.transport.local import LocalTransport
1083
1101
        if (self.base.find('://') != -1 or 
1084
1102
            not isinstance(self._transport, LocalTransport)):
1105
1123
 
1106
1124
    def get_parent(self):
1107
1125
        """See Branch.get_parent."""
1108
 
        import errno
 
1126
 
1109
1127
        _locs = ['parent', 'pull', 'x-pull']
 
1128
        assert self.base[-1] == '/'
1110
1129
        for l in _locs:
1111
1130
            try:
1112
 
                return self.control_files.get_utf8(l).read().strip('\n')
 
1131
                parent = self.control_files.get(l).read().strip('\n')
1113
1132
            except NoSuchFile:
1114
 
                pass
 
1133
                continue
 
1134
            # This is an old-format absolute path to a local branch
 
1135
            # turn it into a url
 
1136
            if parent.startswith('/'):
 
1137
                parent = urlutils.local_path_to_url(parent.decode('utf8'))
 
1138
            return urlutils.join(self.base[:-1], parent)
1115
1139
        return None
1116
1140
 
1117
1141
    def get_push_location(self):
1136
1160
        if url is None:
1137
1161
            self.control_files._transport.delete('parent')
1138
1162
        else:
1139
 
            self.control_files.put_utf8('parent', url + '\n')
 
1163
            if isinstance(url, unicode):
 
1164
                try: 
 
1165
                    url = url.encode('ascii')
 
1166
                except UnicodeEncodeError:
 
1167
                    raise bzrlib.errors.InvalidURL(url,
 
1168
                        "Urls must be 7-bit ascii, "
 
1169
                        "use bzrlib.urlutils.escape")
 
1170
                    
 
1171
            url = urlutils.relative_url(self.base, url)
 
1172
            self.control_files.put('parent', url + '\n')
1140
1173
 
1141
1174
    def tree_config(self):
1142
1175
        return TreeConfig(self)
1184
1217
 
1185
1218
        This could memoise the branch, but if thats done
1186
1219
        it must be revalidated on each new lock.
1187
 
        So for now we just dont memoise it.
 
1220
        So for now we just don't memoise it.
1188
1221
        # RBC 20060304 review this decision.
1189
1222
        """
1190
1223
        bound_loc = self.get_bound_location()
1236
1269
        # There may be a different check you could do here
1237
1270
        # rather than actually trying to install revisions remotely.
1238
1271
        # TODO: capture an exception which indicates the remote branch
1239
 
        #       is not writeable. 
 
1272
        #       is not writable. 
1240
1273
        #       If it is up-to-date, this probably should not be a failure
1241
1274
        
1242
1275
        # lock other for write so the revision-history syncing cannot race
1306
1339
            new_test.id = make_new_test_id()
1307
1340
            result.addTest(new_test)
1308
1341
        return result
 
1342
 
 
1343
 
 
1344
class BranchCheckResult(object):
 
1345
    """Results of checking branch consistency.
 
1346
 
 
1347
    :see: Branch.check
 
1348
    """
 
1349
 
 
1350
    def __init__(self, branch):
 
1351
        self.branch = branch
 
1352
 
 
1353
    def report_results(self, verbose):
 
1354
        """Report the check results via trace.note.
 
1355
        
 
1356
        :param verbose: Requests more detailed display of what was checked,
 
1357
            if any.
 
1358
        """
 
1359
        note('checked branch %s format %s',
 
1360
             self.branch.base,
 
1361
             self.branch._format)
 
1362
 
 
1363
 
 
1364
######################################################################
 
1365
# predicates
 
1366
 
 
1367
 
 
1368
@deprecated_function(zero_eight)
 
1369
def is_control_file(*args, **kwargs):
 
1370
    """See bzrlib.workingtree.is_control_file."""
 
1371
    return bzrlib.workingtree.is_control_file(*args, **kwargs)