~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2007-04-26 15:39:04 UTC
  • mfrom: (2456.2.6 rename_iter_changes_109993)
  • Revision ID: pqm@pqm.ubuntu.com-20070426153904-l91p9ybsqpxt2vyv
(John Arbash Meinel) Fix bug #109993 by fixing _iter_changes to not sync an on-disk file with an 'absent' dirblock record.

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
 
20
20
from bzrlib.lazy_import import lazy_import
21
21
lazy_import(globals(), """
 
22
from copy import deepcopy
 
23
from unittest import TestSuite
22
24
from warnings import warn
23
25
 
24
26
import bzrlib
172
174
        return self.get_config().get_nickname()
173
175
 
174
176
    def _set_nick(self, nick):
175
 
        self.get_config().set_user_option('nickname', nick, warn_masked=True)
 
177
        self.get_config().set_user_option('nickname', nick)
176
178
 
177
179
    nick = property(_get_nick, _set_nick)
178
180
 
505
507
 
506
508
    def revision_id_to_revno(self, revision_id):
507
509
        """Given a revision id, return its revno"""
508
 
        if _mod_revision.is_null(revision_id):
 
510
        if revision_id is None:
509
511
            return 0
510
512
        revision_id = osutils.safe_revision_id(revision_id)
511
513
        history = self.revision_history()
584
586
            url = ''
585
587
        elif make_relative:
586
588
            url = urlutils.relative_url(self.base, url)
587
 
        config.set_user_option(name, url, warn_masked=True)
 
589
        config.set_user_option(name, url)
588
590
 
589
591
    def _get_config_location(self, name, config=None):
590
592
        if config is None:
610
612
        pattern is that the user can override it by specifying a
611
613
        location.
612
614
        """
613
 
        self.get_config().set_user_option('submit_branch', location,
614
 
            warn_masked=True)
 
615
        self.get_config().set_user_option('submit_branch', location)
615
616
 
616
617
    def get_public_branch(self):
617
618
        """Return the public location of the branch.
701
702
        :param revision_id: The revision-id to truncate history at.  May
702
703
          be None to copy complete history.
703
704
        """
704
 
        if revision_id == _mod_revision.NULL_REVISION:
705
 
            new_history = []
706
705
        new_history = self.revision_history()
707
 
        if revision_id is not None and new_history != []:
 
706
        if revision_id is not None:
708
707
            revision_id = osutils.safe_revision_id(revision_id)
709
708
            try:
710
709
                new_history = new_history[:new_history.index(revision_id) + 1]
784
783
        :return: The tree of the created checkout
785
784
        """
786
785
        t = transport.get_transport(to_location)
787
 
        t.ensure_base()
 
786
        try:
 
787
            t.mkdir('.')
 
788
        except errors.FileExists:
 
789
            pass
788
790
        if lightweight:
789
791
            format = self._get_checkout_format()
790
792
            checkout = format.initialize_on_transport(t)
848
850
    _formats = {}
849
851
    """The known formats."""
850
852
 
851
 
    def __eq__(self, other):
852
 
        return self.__class__ is other.__class__
853
 
 
854
 
    def __ne__(self, other):
855
 
        return not (self == other)
856
 
 
857
853
    @classmethod
858
854
    def find_format(klass, a_bzrdir):
859
855
        """Return the format for the branch object in a_bzrdir."""
1004
1000
        # (push_result)
1005
1001
        # containing the members
1006
1002
        # (source, local, master, old_revno, old_revid, new_revno, new_revid)
1007
 
        # where local is the local target branch or None, master is the target 
 
1003
        # where local is the local branch or None, master is the target 
1008
1004
        # master branch, and the rest should be self explanatory. The source
1009
1005
        # is read locked and the target branches write locked. Source will
1010
1006
        # be the local low-latency branch.
1406
1402
                          other_branch=None):
1407
1403
        # stop_revision must be a descendant of last_revision
1408
1404
        stop_graph = self.repository.get_revision_graph(revision_id)
1409
 
        if (last_rev is not None and last_rev != _mod_revision.NULL_REVISION
1410
 
            and last_rev not in stop_graph):
 
1405
        if last_rev is not None and last_rev not in stop_graph:
1411
1406
            # our previous tip is not merged into stop_revision
1412
1407
            raise errors.DivergedBranches(self, other_branch)
1413
1408
        # make a new revision history from the graph
1452
1447
                stop_revision = osutils.safe_revision_id(stop_revision)
1453
1448
            # whats the current last revision, before we fetch [and change it
1454
1449
            # possibly]
1455
 
            last_rev = _mod_revision.ensure_null(self.last_revision())
 
1450
            last_rev = self.last_revision()
1456
1451
            # we fetch here regardless of whether we need to so that we pickup
1457
1452
            # filled in ghosts.
1458
1453
            self.fetch(other, stop_revision)
1459
 
            my_ancestry = self.repository.get_ancestry(last_rev,
1460
 
                                                       topo_sorted=False)
 
1454
            my_ancestry = self.repository.get_ancestry(last_rev)
1461
1455
            if stop_revision in my_ancestry:
1462
1456
                # last_revision is a descendant of stop_revision
1463
1457
                return
1482
1476
 
1483
1477
    @needs_write_lock
1484
1478
    def pull(self, source, overwrite=False, stop_revision=None,
1485
 
             _hook_master=None, run_hooks=True):
 
1479
        _hook_master=None, _run_hooks=True):
1486
1480
        """See Branch.pull.
1487
1481
 
1488
1482
        :param _hook_master: Private parameter - set the branch to 
1489
1483
            be supplied as the master to push hooks.
1490
 
        :param run_hooks: Private parameter - if false, this branch
1491
 
            is being called because it's the master of the primary branch,
1492
 
            so it should not run its hooks.
 
1484
        :param _run_hooks: Private parameter - allow disabling of
 
1485
            hooks, used when pushing to a master branch.
1493
1486
        """
1494
1487
        result = PullResult()
1495
1488
        result.source_branch = source
1514
1507
            else:
1515
1508
                result.master_branch = self
1516
1509
                result.local_branch = None
1517
 
            if run_hooks:
 
1510
            if _run_hooks:
1518
1511
                for hook in Branch.hooks['post_pull']:
1519
1512
                    hook(result)
1520
1513
        finally:
1532
1525
 
1533
1526
    @needs_read_lock
1534
1527
    def push(self, target, overwrite=False, stop_revision=None,
1535
 
             _override_hook_source_branch=None):
 
1528
        _hook_master=None, _run_hooks=True):
1536
1529
        """See Branch.push.
1537
 
 
1538
 
        This is the basic concrete implementation of push()
1539
 
 
1540
 
        :param _override_hook_source_branch: If specified, run
1541
 
        the hooks passing this Branch as the source, rather than self.  
1542
 
        This is for use of RemoteBranch, where push is delegated to the
1543
 
        underlying vfs-based Branch. 
1544
 
        """
1545
 
        # TODO: Public option to disable running hooks - should be trivial but
1546
 
        # needs tests.
1547
 
        target.lock_write()
1548
 
        try:
1549
 
            result = self._push_with_bound_branches(target, overwrite,
1550
 
                    stop_revision,
1551
 
                    _override_hook_source_branch=_override_hook_source_branch)
1552
 
            return result
1553
 
        finally:
1554
 
            target.unlock()
1555
 
 
1556
 
    def _push_with_bound_branches(self, target, overwrite,
1557
 
            stop_revision,
1558
 
            _override_hook_source_branch=None):
1559
 
        """Push from self into target, and into target's master if any.
1560
1530
        
1561
 
        This is on the base BzrBranch class even though it doesn't support 
1562
 
        bound branches because the *target* might be bound.
1563
 
        """
1564
 
        def _run_hooks():
1565
 
            if _override_hook_source_branch:
1566
 
                result.source_branch = _override_hook_source_branch
1567
 
            for hook in Branch.hooks['post_push']:
1568
 
                hook(result)
1569
 
 
1570
 
        bound_location = target.get_bound_location()
1571
 
        if bound_location and target.base != bound_location:
1572
 
            # there is a master branch.
1573
 
            #
1574
 
            # XXX: Why the second check?  Is it even supported for a branch to
1575
 
            # be bound to itself? -- mbp 20070507
1576
 
            master_branch = target.get_master_branch()
1577
 
            master_branch.lock_write()
1578
 
            try:
1579
 
                # push into the master from this branch.
1580
 
                self._basic_push(master_branch, overwrite, stop_revision)
1581
 
                # and push into the target branch from this. Note that we push from
1582
 
                # this branch again, because its considered the highest bandwidth
1583
 
                # repository.
1584
 
                result = self._basic_push(target, overwrite, stop_revision)
1585
 
                result.master_branch = master_branch
1586
 
                result.local_branch = target
1587
 
                _run_hooks()
1588
 
                return result
1589
 
            finally:
1590
 
                master_branch.unlock()
1591
 
        else:
1592
 
            # no master branch
1593
 
            result = self._basic_push(target, overwrite, stop_revision)
1594
 
            # TODO: Why set master_branch and local_branch if there's no
1595
 
            # binding?  Maybe cleaner to just leave them unset? -- mbp
1596
 
            # 20070504
1597
 
            result.master_branch = target
1598
 
            result.local_branch = None
1599
 
            _run_hooks()
1600
 
            return result
1601
 
 
1602
 
    def _basic_push(self, target, overwrite, stop_revision):
1603
 
        """Basic implementation of push without bound branches or hooks.
1604
 
 
1605
 
        Must be called with self read locked and target write locked.
 
1531
        :param _hook_master: Private parameter - set the branch to 
 
1532
            be supplied as the master to push hooks.
 
1533
        :param _run_hooks: Private parameter - allow disabling of
 
1534
            hooks, used when pushing to a master branch.
1606
1535
        """
1607
1536
        result = PushResult()
1608
1537
        result.source_branch = self
1609
1538
        result.target_branch = target
1610
 
        result.old_revno, result.old_revid = target.last_revision_info()
 
1539
        target.lock_write()
1611
1540
        try:
1612
 
            target.update_revisions(self, stop_revision)
1613
 
        except DivergedBranches:
1614
 
            if not overwrite:
1615
 
                raise
1616
 
        if overwrite:
1617
 
            target.set_revision_history(self.revision_history())
1618
 
        result.tag_conflicts = self.tags.merge_to(target.tags)
1619
 
        result.new_revno, result.new_revid = target.last_revision_info()
 
1541
            result.old_revno, result.old_revid = target.last_revision_info()
 
1542
            try:
 
1543
                target.update_revisions(self, stop_revision)
 
1544
            except DivergedBranches:
 
1545
                if not overwrite:
 
1546
                    raise
 
1547
            if overwrite:
 
1548
                target.set_revision_history(self.revision_history())
 
1549
            result.tag_conflicts = self.tags.merge_to(target.tags)
 
1550
            result.new_revno, result.new_revid = target.last_revision_info()
 
1551
            if _hook_master:
 
1552
                result.master_branch = _hook_master
 
1553
                result.local_branch = target
 
1554
            else:
 
1555
                result.master_branch = target
 
1556
                result.local_branch = None
 
1557
            if _run_hooks:
 
1558
                for hook in Branch.hooks['post_push']:
 
1559
                    hook(result)
 
1560
        finally:
 
1561
            target.unlock()
1620
1562
        return result
1621
1563
 
1622
1564
    def get_parent(self):
1692
1634
        
1693
1635
    @needs_write_lock
1694
1636
    def pull(self, source, overwrite=False, stop_revision=None,
1695
 
             run_hooks=True):
1696
 
        """Pull from source into self, updating my master if any.
 
1637
        _run_hooks=True):
 
1638
        """Extends branch.pull to be bound branch aware.
1697
1639
        
1698
 
        :param run_hooks: Private parameter - if false, this branch
1699
 
            is being called because it's the master of the primary branch,
1700
 
            so it should not run its hooks.
 
1640
        :param _run_hooks: Private parameter used to force hook running
 
1641
            off during bound branch double-pushing.
1701
1642
        """
1702
1643
        bound_location = self.get_bound_location()
1703
1644
        master_branch = None
1709
1650
            if master_branch:
1710
1651
                # pull from source into master.
1711
1652
                master_branch.pull(source, overwrite, stop_revision,
1712
 
                    run_hooks=False)
 
1653
                    _run_hooks=False)
1713
1654
            return super(BzrBranch5, self).pull(source, overwrite,
1714
1655
                stop_revision, _hook_master=master_branch,
1715
 
                run_hooks=run_hooks)
 
1656
                _run_hooks=_run_hooks)
 
1657
        finally:
 
1658
            if master_branch:
 
1659
                master_branch.unlock()
 
1660
 
 
1661
    @needs_read_lock
 
1662
    def push(self, target, overwrite=False, stop_revision=None):
 
1663
        """Updates branch.push to be bound branch aware."""
 
1664
        bound_location = target.get_bound_location()
 
1665
        master_branch = None
 
1666
        if bound_location and target.base != bound_location:
 
1667
            # not pushing to master, so we need to update master.
 
1668
            master_branch = target.get_master_branch()
 
1669
            master_branch.lock_write()
 
1670
        try:
 
1671
            if master_branch:
 
1672
                # push into the master from this branch.
 
1673
                super(BzrBranch5, self).push(master_branch, overwrite,
 
1674
                    stop_revision, _run_hooks=False)
 
1675
            # and push into the target branch from this. Note that we push from
 
1676
            # this branch again, because its considered the highest bandwidth
 
1677
            # repository.
 
1678
            return super(BzrBranch5, self).push(target, overwrite,
 
1679
                stop_revision, _hook_master=master_branch)
1716
1680
        finally:
1717
1681
            if master_branch:
1718
1682
                master_branch.unlock()
1782
1746
        # last_rev is not in the other_last_rev history, AND
1783
1747
        # other_last_rev is not in our history, and do it without pulling
1784
1748
        # history around
1785
 
        last_rev = _mod_revision.ensure_null(self.last_revision())
1786
 
        if last_rev != _mod_revision.NULL_REVISION:
 
1749
        last_rev = self.last_revision()
 
1750
        if last_rev is not None:
1787
1751
            other.lock_read()
1788
1752
            try:
1789
1753
                other_last_rev = other.last_revision()
1790
 
                if not _mod_revision.is_null(other_last_rev):
 
1754
                if other_last_rev is not None:
1791
1755
                    # neither branch is new, we have to do some work to
1792
1756
                    # ascertain diversion.
1793
1757
                    remote_graph = other.repository.get_revision_graph(
1816
1780
        if master is not None:
1817
1781
            old_tip = self.last_revision()
1818
1782
            self.pull(master, overwrite=True)
1819
 
            if old_tip in self.repository.get_ancestry(
1820
 
                _mod_revision.ensure_null(self.last_revision()),
1821
 
                topo_sorted=False):
 
1783
            if old_tip in self.repository.get_ancestry(self.last_revision()):
1822
1784
                return None
1823
1785
            return old_tip
1824
1786
        return None
1965
1927
        self._clear_cached_state()
1966
1928
 
1967
1929
    def _check_history_violation(self, revision_id):
1968
 
        last_revision = _mod_revision.ensure_null(self.last_revision())
1969
 
        if _mod_revision.is_null(last_revision):
 
1930
        last_revision = self.last_revision()
 
1931
        if last_revision is None:
1970
1932
            return
1971
1933
        if last_revision not in self._lefthand_history(revision_id):
1972
1934
            raise errors.AppendRevisionsOnlyViolation(self.base)
2036
1998
            if config.get_user_option('bound') != 'True':
2037
1999
                return False
2038
2000
            else:
2039
 
                config.set_user_option('bound', 'False', warn_masked=True)
 
2001
                config.set_user_option('bound', 'False')
2040
2002
                return True
2041
2003
        else:
2042
2004
            self._set_config_location('bound_location', location,
2043
2005
                                      config=config)
2044
 
            config.set_user_option('bound', 'True', warn_masked=True)
 
2006
            config.set_user_option('bound', 'True')
2045
2007
        return True
2046
2008
 
2047
2009
    def _get_bound_location(self, bound):
2067
2029
            value = 'True'
2068
2030
        else:
2069
2031
            value = 'False'
2070
 
        self.get_config().set_user_option('append_revisions_only', value,
2071
 
            warn_masked=True)
 
2032
        self.get_config().set_user_option('append_revisions_only', value)
2072
2033
 
2073
2034
    def _get_append_revisions_only(self):
2074
2035
        value = self.get_config().get_user_option('append_revisions_only')
2089
2050
        if revision_id is None:
2090
2051
            revno, revision_id = self.last_revision_info()
2091
2052
        else:
2092
 
            # To figure out the revno for a random revision, we need to build
2093
 
            # the revision history, and count its length.
2094
 
            # We don't care about the order, just how long it is.
2095
 
            # Alternatively, we could start at the current location, and count
2096
 
            # backwards. But there is no guarantee that we will find it since
2097
 
            # it may be a merged revision.
2098
 
            revno = len(list(self.repository.iter_reverse_revision_history(
2099
 
                                                                revision_id)))
 
2053
            revno = self.revision_id_to_revno(revision_id)
2100
2054
        destination.set_last_revision_info(revno, revision_id)
2101
2055
 
2102
2056
    def _make_tags(self):
2103
2057
        return BasicTags(self)
2104
2058
 
2105
2059
 
 
2060
class BranchTestProviderAdapter(object):
 
2061
    """A tool to generate a suite testing multiple branch formats at once.
 
2062
 
 
2063
    This is done by copying the test once for each transport and injecting
 
2064
    the transport_server, transport_readonly_server, and branch_format
 
2065
    classes into each copy. Each copy is also given a new id() to make it
 
2066
    easy to identify.
 
2067
    """
 
2068
 
 
2069
    def __init__(self, transport_server, transport_readonly_server, formats,
 
2070
        vfs_transport_factory=None):
 
2071
        self._transport_server = transport_server
 
2072
        self._transport_readonly_server = transport_readonly_server
 
2073
        self._formats = formats
 
2074
    
 
2075
    def adapt(self, test):
 
2076
        result = TestSuite()
 
2077
        for branch_format, bzrdir_format in self._formats:
 
2078
            new_test = deepcopy(test)
 
2079
            new_test.transport_server = self._transport_server
 
2080
            new_test.transport_readonly_server = self._transport_readonly_server
 
2081
            new_test.bzrdir_format = bzrdir_format
 
2082
            new_test.branch_format = branch_format
 
2083
            def make_new_test_id():
 
2084
                # the format can be either a class or an instance
 
2085
                name = getattr(branch_format, '__name__',
 
2086
                        branch_format.__class__.__name__)
 
2087
                new_id = "%s(%s)" % (new_test.id(), name)
 
2088
                return lambda: new_id
 
2089
            new_test.id = make_new_test_id()
 
2090
            result.addTest(new_test)
 
2091
        return result
 
2092
 
 
2093
 
2106
2094
######################################################################
2107
2095
# results of operations
2108
2096