/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

  • Committer: Martin Pool
  • Date: 2008-05-27 03:00:53 UTC
  • mfrom: (3452 +trunk)
  • mto: (3724.1.1 lock-hooks)
  • mto: This revision was merged to the branch mainline in revision 3730.
  • Revision ID: mbp@sourcefrog.net-20080527030053-0mct6dypek0ysjc3
merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007, 2008 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
41
41
 
42
42
from bzrlib.decorators import needs_read_lock, needs_write_lock
43
43
from bzrlib.hooks import Hooks
44
 
from bzrlib.symbol_versioning import (deprecated_method,
45
 
                                      zero_sixteen,
46
 
                                      )
 
44
from bzrlib.symbol_versioning import (
 
45
    deprecated_in,
 
46
    deprecated_method,
 
47
    )
47
48
from bzrlib.trace import mutter, mutter_callsite, note, is_quiet
48
49
 
49
50
 
221
222
        """
222
223
        self.control_files.dont_leave_in_place()
223
224
 
 
225
    @deprecated_method(deprecated_in((0, 16, 0)))
224
226
    def abspath(self, name):
225
227
        """Return absolute filename for something in the branch
226
228
        
317
319
        The delta is relative to its mainline predecessor, or the
318
320
        empty tree for revision 1.
319
321
        """
320
 
        assert isinstance(revno, int)
321
322
        rh = self.revision_history()
322
323
        if not (1 <= revno <= len(rh)):
323
324
            raise errors.InvalidRevisionNumber(revno)
324
325
        return self.repository.get_revision_delta(rh[revno-1])
325
326
 
326
 
    @deprecated_method(zero_sixteen)
327
 
    def get_root_id(self):
328
 
        """Return the id of this branches root
329
 
 
330
 
        Deprecated: branches don't have root ids-- trees do.
331
 
        Use basis_tree().get_root_id() instead.
332
 
        """
333
 
        raise NotImplementedError(self.get_root_id)
334
 
 
335
327
    def print_file(self, file, revision_id):
336
328
        """Print `file` to stdout."""
337
329
        raise NotImplementedError(self.print_file)
451
443
        if stop_revision is None:
452
444
            stop_revision = other_len
453
445
        else:
454
 
            assert isinstance(stop_revision, int)
455
446
            if stop_revision > other_len:
456
447
                raise errors.NoSuchRevision(self, stop_revision)
457
448
        return other_history[self_len:stop_revision]
704
695
        :return: A BranchCheckResult.
705
696
        """
706
697
        mainline_parent_id = None
707
 
        for revision_id in self.revision_history():
 
698
        last_revno, last_revision_id = self.last_revision_info()
 
699
        real_rev_history = list(self.repository.iter_reverse_revision_history(
 
700
                                last_revision_id))
 
701
        real_rev_history.reverse()
 
702
        if len(real_rev_history) != last_revno:
 
703
            raise errors.BzrCheckError('revno does not match len(mainline)'
 
704
                ' %s != %s' % (last_revno, len(real_rev_history)))
 
705
        # TODO: We should probably also check that real_rev_history actually
 
706
        #       matches self.revision_history()
 
707
        for revision_id in real_rev_history:
708
708
            try:
709
709
                revision = self.repository.get_revision(revision_id)
710
710
            except errors.NoSuchRevision, e:
783
783
            basis_tree.unlock()
784
784
        return tree
785
785
 
 
786
    @needs_write_lock
 
787
    def reconcile(self, thorough=True):
 
788
        """Make sure the data stored in this branch is consistent."""
 
789
        from bzrlib.reconcile import BranchReconciler
 
790
        reconciler = BranchReconciler(self, thorough=thorough)
 
791
        reconciler.reconcile()
 
792
        return reconciler
 
793
 
786
794
    def reference_parent(self, file_id, path):
787
795
        """Return the parent branch for a tree-reference file_id
788
796
        :param file_id: The file_id of the tree reference
900
908
        control_files.create_lock()
901
909
        control_files.lock_write()
902
910
        if set_format:
903
 
            control_files.put_utf8('format', self.get_format_string())
 
911
            utf8_files += [('format', self.get_format_string())]
904
912
        try:
905
 
            for file, content in utf8_files:
906
 
                control_files.put_utf8(file, content)
 
913
            for (filename, content) in utf8_files:
 
914
                branch_transport.put_bytes(
 
915
                    filename, content,
 
916
                    mode=control_files._file_mode)
907
917
        finally:
908
918
            control_files.unlock()
909
919
        return self.open(a_bzrdir, _found=True)
939
949
 
940
950
    @classmethod
941
951
    def unregister_format(klass, format):
942
 
        assert klass._formats[format.get_format_string()] is format
943
952
        del klass._formats[format.get_format_string()]
944
953
 
945
954
    def __str__(self):
949
958
        """True if this format supports tags stored in the branch"""
950
959
        return False  # by default
951
960
 
952
 
    # XXX: Probably doesn't really belong here -- mbp 20070212
953
 
    def _initialize_control_files(self, a_bzrdir, utf8_files, lock_filename,
954
 
            lock_class):
955
 
        branch_transport = a_bzrdir.get_branch_transport(self)
956
 
        control_files = lockable_files.LockableFiles(branch_transport,
957
 
            lock_filename, lock_class)
958
 
        control_files.create_lock()
959
 
        control_files.lock_write()
960
 
        try:
961
 
            for filename, content in utf8_files:
962
 
                control_files.put_utf8(filename, content)
963
 
        finally:
964
 
            control_files.unlock()
965
 
 
966
961
 
967
962
class BranchHooks(Hooks):
968
963
    """A dictionary mapping hook name to a list of callables for branch hooks.
1025
1020
        # local is the local branch or None, master is the target branch,
1026
1021
        # and an empty branch recieves new_revno of 0, new_revid of None.
1027
1022
        self['post_uncommit'] = []
 
1023
        # Introduced in 1.4
 
1024
        # Invoked after the tip of a branch changes.
 
1025
        # the api signature is
 
1026
        # (params) where params is a ChangeBranchTipParams with the members
 
1027
        # (branch, old_revno, new_revno, old_revid, new_revid)
 
1028
        self['post_change_branch_tip'] = []
1028
1029
 
1029
1030
 
1030
1031
# install the default hooks into the Branch class.
1031
1032
Branch.hooks = BranchHooks()
1032
1033
 
1033
1034
 
 
1035
class ChangeBranchTipParams(object):
 
1036
    """Object holding parameters passed to *_change_branch_tip hooks.
 
1037
 
 
1038
    There are 5 fields that hooks may wish to access:
 
1039
 
 
1040
    :ivar branch: the branch being changed
 
1041
    :ivar old_revno: revision number before the change
 
1042
    :ivar new_revno: revision number after the change
 
1043
    :ivar old_revid: revision id before the change
 
1044
    :ivar new_revid: revision id after the change
 
1045
 
 
1046
    The revid fields are strings. The revno fields are integers.
 
1047
    """
 
1048
 
 
1049
    def __init__(self, branch, old_revno, new_revno, old_revid, new_revid):
 
1050
        """Create a group of ChangeBranchTip parameters.
 
1051
 
 
1052
        :param branch: The branch being changed.
 
1053
        :param old_revno: Revision number before the change.
 
1054
        :param new_revno: Revision number after the change.
 
1055
        :param old_revid: Tip revision id before the change.
 
1056
        :param new_revid: Tip revision id after the change.
 
1057
        """
 
1058
        self.branch = branch
 
1059
        self.old_revno = old_revno
 
1060
        self.new_revno = new_revno
 
1061
        self.old_revid = old_revid
 
1062
        self.new_revid = new_revid
 
1063
 
 
1064
 
1034
1065
class BzrBranchFormat4(BranchFormat):
1035
1066
    """Bzr branch format 4.
1036
1067
 
1113
1144
        """
1114
1145
        if not _found:
1115
1146
            format = BranchFormat.find_format(a_bzrdir)
1116
 
            assert format.__class__ == self.__class__
 
1147
            if format.__class__ != self.__class__:
 
1148
                raise AssertionError("wrong format %r found for %r" %
 
1149
                    (format, self))
1117
1150
        try:
1118
1151
            transport = a_bzrdir.get_branch_transport(None)
1119
1152
            control_files = lockable_files.LockableFiles(transport, 'lock',
1161
1194
        """
1162
1195
        if not _found:
1163
1196
            format = BranchFormat.find_format(a_bzrdir)
1164
 
            assert format.__class__ == self.__class__
 
1197
            if format.__class__ != self.__class__:
 
1198
                raise AssertionError("wrong format %r found for %r" %
 
1199
                    (format, self))
1165
1200
        transport = a_bzrdir.get_branch_transport(None)
1166
1201
        control_files = lockable_files.LockableFiles(transport, 'lock',
1167
1202
                                                     lockdir.LockDir)
1241
1276
        """
1242
1277
        if not _found:
1243
1278
            format = BranchFormat.find_format(a_bzrdir)
1244
 
            assert format.__class__ == self.__class__
 
1279
            if format.__class__ != self.__class__:
 
1280
                raise AssertionError("wrong format %r found for %r" %
 
1281
                    (format, self))
1245
1282
        if location is None:
1246
1283
            location = self.get_reference(a_bzrdir)
1247
1284
        real_bzrdir = bzrdir.BzrDir.open(
1276
1313
    Note that it's "local" in the context of the filesystem; it doesn't
1277
1314
    really matter if it's on an nfs/smb/afs/coda/... share, as long as
1278
1315
    it's writable, and can be accessed via the normal filesystem API.
 
1316
 
 
1317
    :ivar _transport: Transport for file operations on this branch's 
 
1318
        control files, typically pointing to the .bzr/branch directory.
 
1319
    :ivar repository: Repository for this branch.
 
1320
    :ivar base: The url of the base directory for this branch; the one 
 
1321
        containing the .bzr directory.
1279
1322
    """
1280
1323
    
1281
1324
    def __init__(self, _format=None,
1286
1329
            raise ValueError('a_bzrdir must be supplied')
1287
1330
        else:
1288
1331
            self.bzrdir = a_bzrdir
1289
 
        # self._transport used to point to the directory containing the
1290
 
        # control directory, but was not used - now it's just the transport
1291
 
        # for the branch control files.  mbp 20070212
1292
1332
        self._base = self.bzrdir.transport.clone('..').base
 
1333
        # XXX: We should be able to just do
 
1334
        #   self.base = self.bzrdir.root_transport.base
 
1335
        # but this does not quite work yet -- mbp 20080522
1293
1336
        self._format = _format
1294
1337
        if _control_files is None:
1295
1338
            raise ValueError('BzrBranch _control_files is None')
1308
1351
 
1309
1352
    base = property(_get_base, doc="The URL for the root of this branch.")
1310
1353
 
 
1354
    @deprecated_method(deprecated_in((0, 16, 0)))
1311
1355
    def abspath(self, name):
1312
1356
        """See Branch.abspath."""
1313
 
        return self.control_files._transport.abspath(name)
1314
 
 
1315
 
 
1316
 
    @deprecated_method(zero_sixteen)
1317
 
    @needs_read_lock
1318
 
    def get_root_id(self):
1319
 
        """See Branch.get_root_id."""
1320
 
        tree = self.repository.revision_tree(self.last_revision())
1321
 
        return tree.get_root_id()
 
1357
        return self._transport.abspath(name)
1322
1358
 
1323
1359
    def is_locked(self):
1324
1360
        return self.control_files.is_locked()
1369
1405
 
1370
1406
        This performs the actual writing to disk.
1371
1407
        It is intended to be called by BzrBranch5.set_revision_history."""
1372
 
        self.control_files.put_bytes(
1373
 
            'revision-history', '\n'.join(history))
 
1408
        self._transport.put_bytes(
 
1409
            'revision-history', '\n'.join(history),
 
1410
            mode=self.control_files._file_mode)
1374
1411
 
1375
1412
    @needs_write_lock
1376
1413
    def set_revision_history(self, rev_history):
1383
1420
        for hook in Branch.hooks['set_rh']:
1384
1421
            hook(self, rev_history)
1385
1422
 
 
1423
    def _run_post_change_branch_tip_hooks(self, old_revno, old_revid):
 
1424
        """Run the post_change_branch_tip hooks."""
 
1425
        hooks = Branch.hooks['post_change_branch_tip']
 
1426
        if not hooks:
 
1427
            return
 
1428
        new_revno, new_revid = self.last_revision_info()
 
1429
        params = ChangeBranchTipParams(
 
1430
            self, old_revno, new_revno, old_revid, new_revid)
 
1431
        for hook in hooks:
 
1432
            hook(params)
 
1433
 
1386
1434
    @needs_write_lock
1387
1435
    def set_last_revision_info(self, revno, revision_id):
1388
1436
        """Set the last revision of this branch.
1396
1444
        be permitted.
1397
1445
        """
1398
1446
        revision_id = _mod_revision.ensure_null(revision_id)
 
1447
        old_revno, old_revid = self.last_revision_info()
 
1448
        # this old format stores the full history, but this api doesn't
 
1449
        # provide it, so we must generate, and might as well check it's
 
1450
        # correct
1399
1451
        history = self._lefthand_history(revision_id)
1400
 
        assert len(history) == revno, '%d != %d' % (len(history), revno)
 
1452
        if len(history) != revno:
 
1453
            raise AssertionError('%d != %d' % (len(history), revno))
1401
1454
        self.set_revision_history(history)
 
1455
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
1402
1456
 
1403
1457
    def _gen_revision_history(self):
1404
 
        history = self.control_files.get('revision-history').read().split('\n')
 
1458
        history = self._transport.get_bytes('revision-history').split('\n')
1405
1459
        if history[-1:] == ['']:
1406
1460
            # There shouldn't be a trailing newline, but just in case.
1407
1461
            history.pop()
1476
1530
                elif heads == set([stop_revision, last_rev]):
1477
1531
                    # These branches have diverged
1478
1532
                    raise errors.DivergedBranches(self, other)
1479
 
                assert heads == set([stop_revision])
 
1533
                elif heads != set([stop_revision]):
 
1534
                    raise AssertionError("invalid heads: %r" % heads)
1480
1535
            if other_last_revision == stop_revision:
1481
1536
                self.set_last_revision_info(other_last_revno,
1482
1537
                                            other_last_revision)
1532
1587
        _locs = ['parent', 'pull', 'x-pull']
1533
1588
        for l in _locs:
1534
1589
            try:
1535
 
                return self.control_files.get(l).read().strip('\n')
 
1590
                return self._transport.get_bytes(l).strip('\n')
1536
1591
            except errors.NoSuchFile:
1537
1592
                pass
1538
1593
        return None
1615
1670
        result.source_branch = self
1616
1671
        result.target_branch = target
1617
1672
        result.old_revno, result.old_revid = target.last_revision_info()
1618
 
        try:
1619
 
            target.update_revisions(self, stop_revision)
1620
 
        except errors.DivergedBranches:
1621
 
            if not overwrite:
1622
 
                raise
1623
 
        if overwrite:
1624
 
            target.set_revision_history(self.revision_history())
 
1673
        target.update_revisions(self, stop_revision, overwrite)
1625
1674
        result.tag_conflicts = self.tags.merge_to(target.tags, overwrite)
1626
1675
        result.new_revno, result.new_revid = target.last_revision_info()
1627
1676
        return result
1628
1677
 
1629
1678
    def get_parent(self):
1630
1679
        """See Branch.get_parent."""
1631
 
 
1632
 
        assert self.base[-1] == '/'
1633
1680
        parent = self._get_parent_location()
1634
1681
        if parent is None:
1635
1682
            return parent
1654
1701
        # TODO: Maybe delete old location files?
1655
1702
        # URLs should never be unicode, even on the local fs,
1656
1703
        # FIXUP this and get_parent in a future branch format bump:
1657
 
        # read and rewrite the file, and have the new format code read
1658
 
        # using .get not .get_utf8. RBC 20060125
 
1704
        # read and rewrite the file. RBC 20060125
1659
1705
        if url is not None:
1660
1706
            if isinstance(url, unicode):
1661
 
                try: 
 
1707
                try:
1662
1708
                    url = url.encode('ascii')
1663
1709
                except UnicodeEncodeError:
1664
1710
                    raise errors.InvalidURL(url,
1669
1715
 
1670
1716
    def _set_parent_location(self, url):
1671
1717
        if url is None:
1672
 
            self.control_files._transport.delete('parent')
 
1718
            self._transport.delete('parent')
1673
1719
        else:
1674
 
            assert isinstance(url, str)
1675
 
            self.control_files.put_bytes('parent', url + '\n')
 
1720
            self._transport.put_bytes('parent', url + '\n',
 
1721
                mode=self.control_files._file_mode)
1676
1722
 
1677
1723
 
1678
1724
class BzrBranch5(BzrBranch):
1720
1766
 
1721
1767
    def get_bound_location(self):
1722
1768
        try:
1723
 
            return self.control_files.get_utf8('bound').read()[:-1]
 
1769
            return self._transport.get_bytes('bound')[:-1]
1724
1770
        except errors.NoSuchFile:
1725
1771
            return None
1726
1772
 
1752
1798
        :param location: URL to the target branch
1753
1799
        """
1754
1800
        if location:
1755
 
            self.control_files.put_utf8('bound', location+'\n')
 
1801
            self._transport.put_bytes('bound', location+'\n',
 
1802
                mode=self.bzrdir._get_file_mode())
1756
1803
        else:
1757
1804
            try:
1758
 
                self.control_files._transport.delete('bound')
 
1805
                self._transport.delete('bound')
1759
1806
            except errors.NoSuchFile:
1760
1807
                return False
1761
1808
            return True
1832
1879
        return self._last_revision_info_cache
1833
1880
 
1834
1881
    def _last_revision_info(self):
1835
 
        revision_string = self.control_files.get('last-revision').read()
 
1882
        revision_string = self._transport.get_bytes('last-revision')
1836
1883
        revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
1837
1884
        revision_id = cache_utf8.get_cached_utf8(revision_id)
1838
1885
        revno = int(revno)
1847
1894
        Intended to be called by set_last_revision_info and
1848
1895
        _write_revision_history.
1849
1896
        """
1850
 
        assert revision_id is not None, "Use NULL_REVISION, not None"
 
1897
        revision_id = _mod_revision.ensure_null(revision_id)
1851
1898
        out_string = '%d %s\n' % (revno, revision_id)
1852
 
        self.control_files.put_bytes('last-revision', out_string)
 
1899
        self._transport.put_bytes('last-revision', out_string,
 
1900
            mode=self.control_files._file_mode)
1853
1901
 
1854
1902
    @needs_write_lock
1855
1903
    def set_last_revision_info(self, revno, revision_id):
1856
1904
        revision_id = _mod_revision.ensure_null(revision_id)
 
1905
        old_revno, old_revid = self.last_revision_info()
1857
1906
        if self._get_append_revisions_only():
1858
1907
            self._check_history_violation(revision_id)
1859
1908
        self._write_last_revision_info(revno, revision_id)
1860
1909
        self._clear_cached_state()
1861
1910
        self._last_revision_info_cache = revno, revision_id
 
1911
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
1862
1912
 
1863
1913
    def _check_history_violation(self, revision_id):
1864
1914
        last_revision = _mod_revision.ensure_null(self.last_revision())
1894
1944
            iterator = repo.iter_reverse_revision_history(start_revision)
1895
1945
            #skip the last revision in the list
1896
1946
            next_revision = iterator.next()
1897
 
            assert next_revision == start_revision
1898
1947
        for revision_id in iterator:
1899
1948
            self._partial_revision_history_cache.append(revision_id)
1900
1949
            if (stop_index is not None and
2030
2079
            raise errors.NoSuchRevision(self, revno)
2031
2080
 
2032
2081
        if history is not None:
2033
 
            assert len(history) == last_revno, 'revno/history mismatch'
2034
2082
            return history[revno - 1]
2035
2083
 
2036
2084
        index = last_revno - revno
2149
2197
        new_branch = format.open(branch.bzrdir, _found=True)
2150
2198
 
2151
2199
        # Copy source data into target
2152
 
        new_branch.set_last_revision_info(*branch.last_revision_info())
 
2200
        new_branch._write_last_revision_info(*branch.last_revision_info())
2153
2201
        new_branch.set_parent(branch.get_parent())
2154
2202
        new_branch.set_bound_location(branch.get_bound_location())
2155
2203
        new_branch.set_push_location(branch.get_push_location())
2158
2206
        new_branch.tags._set_tag_dict({})
2159
2207
 
2160
2208
        # Copying done; now update target format
2161
 
        new_branch.control_files.put_utf8('format',
2162
 
            format.get_format_string())
 
2209
        new_branch._transport.put_bytes('format',
 
2210
            format.get_format_string(),
 
2211
            mode=new_branch.control_files._file_mode)
2163
2212
 
2164
2213
        # Clean up old files
2165
 
        new_branch.control_files._transport.delete('revision-history')
 
2214
        new_branch._transport.delete('revision-history')
2166
2215
        try:
2167
2216
            branch.set_parent(None)
2168
2217
        except errors.NoSuchFile: