/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: Jelmer Vernooij
  • Date: 2010-08-02 23:53:16 UTC
  • mto: This revision was merged to the branch mainline in revision 5389.
  • Revision ID: jelmer@samba.org-20100802235316-v6a6p0r2a1os0dsn
baseĀ ControlDirĀ onĀ ControlComponent.

Show diffs side-by-side

added added

removed removed

Lines of Context:
25
25
        bzrdir,
26
26
        cache_utf8,
27
27
        config as _mod_config,
 
28
        controldir,
28
29
        debug,
29
30
        errors,
30
31
        lockdir,
31
32
        lockable_files,
 
33
        remote,
32
34
        repository,
33
35
        revision as _mod_revision,
34
36
        rio,
49
51
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
50
52
from bzrlib.hooks import HookPoint, Hooks
51
53
from bzrlib.inter import InterObject
52
 
from bzrlib.lock import _RelockDebugMixin
 
54
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
53
55
from bzrlib import registry
54
56
from bzrlib.symbol_versioning import (
55
57
    deprecated_in,
63
65
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
64
66
 
65
67
 
66
 
class Branch(bzrdir.ControlComponent):
 
68
class Branch(controldir.ControlComponent):
67
69
    """Branch holding a history of revisions.
68
70
 
69
71
    :ivar base:
197
199
        return self.supports_tags() and self.tags.get_tag_dict()
198
200
 
199
201
    def get_config(self):
 
202
        """Get a bzrlib.config.BranchConfig for this Branch.
 
203
 
 
204
        This can then be used to get and set configuration options for the
 
205
        branch.
 
206
 
 
207
        :return: A bzrlib.config.BranchConfig.
 
208
        """
200
209
        return BranchConfig(self)
201
210
 
202
211
    def _get_config(self):
283
292
        new_history.reverse()
284
293
        return new_history
285
294
 
286
 
    def lock_write(self):
 
295
    def lock_write(self, token=None):
 
296
        """Lock the branch for write operations.
 
297
 
 
298
        :param token: A token to permit reacquiring a previously held and
 
299
            preserved lock.
 
300
        :return: A BranchWriteLockResult.
 
301
        """
287
302
        raise NotImplementedError(self.lock_write)
288
303
 
289
304
    def lock_read(self):
 
305
        """Lock the branch for read operations.
 
306
 
 
307
        :return: A bzrlib.lock.LogicalLockResult.
 
308
        """
290
309
        raise NotImplementedError(self.lock_read)
291
310
 
292
311
    def unlock(self):
783
802
            if len(old_repository._fallback_repositories) != 1:
784
803
                raise AssertionError("can't cope with fallback repositories "
785
804
                    "of %r" % (self.repository,))
786
 
            # unlock it, including unlocking the fallback
 
805
            # Open the new repository object.
 
806
            # Repositories don't offer an interface to remove fallback
 
807
            # repositories today; take the conceptually simpler option and just
 
808
            # reopen it.  We reopen it starting from the URL so that we
 
809
            # get a separate connection for RemoteRepositories and can
 
810
            # stream from one of them to the other.  This does mean doing
 
811
            # separate SSH connection setup, but unstacking is not a
 
812
            # common operation so it's tolerable.
 
813
            new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
 
814
            new_repository = new_bzrdir.find_repository()
 
815
            if new_repository._fallback_repositories:
 
816
                raise AssertionError("didn't expect %r to have "
 
817
                    "fallback_repositories"
 
818
                    % (self.repository,))
 
819
            # Replace self.repository with the new repository.
 
820
            # Do our best to transfer the lock state (i.e. lock-tokens and
 
821
            # lock count) of self.repository to the new repository.
 
822
            lock_token = old_repository.lock_write().repository_token
 
823
            self.repository = new_repository
 
824
            if isinstance(self, remote.RemoteBranch):
 
825
                # Remote branches can have a second reference to the old
 
826
                # repository that need to be replaced.
 
827
                if self._real_branch is not None:
 
828
                    self._real_branch.repository = new_repository
 
829
            self.repository.lock_write(token=lock_token)
 
830
            if lock_token is not None:
 
831
                old_repository.leave_lock_in_place()
787
832
            old_repository.unlock()
 
833
            if lock_token is not None:
 
834
                # XXX: self.repository.leave_lock_in_place() before this
 
835
                # function will not be preserved.  Fortunately that doesn't
 
836
                # affect the current default format (2a), and would be a
 
837
                # corner-case anyway.
 
838
                #  - Andrew Bennetts, 2010/06/30
 
839
                self.repository.dont_leave_lock_in_place()
 
840
            old_lock_count = 0
 
841
            while True:
 
842
                try:
 
843
                    old_repository.unlock()
 
844
                except errors.LockNotHeld:
 
845
                    break
 
846
                old_lock_count += 1
 
847
            if old_lock_count == 0:
 
848
                raise AssertionError(
 
849
                    'old_repository should have been locked at least once.')
 
850
            for i in range(old_lock_count-1):
 
851
                self.repository.lock_write()
 
852
            # Fetch from the old repository into the new.
788
853
            old_repository.lock_read()
789
854
            try:
790
 
                # Repositories don't offer an interface to remove fallback
791
 
                # repositories today; take the conceptually simpler option and just
792
 
                # reopen it.  We reopen it starting from the URL so that we
793
 
                # get a separate connection for RemoteRepositories and can
794
 
                # stream from one of them to the other.  This does mean doing
795
 
                # separate SSH connection setup, but unstacking is not a
796
 
                # common operation so it's tolerable.
797
 
                new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
798
 
                new_repository = new_bzrdir.find_repository()
799
 
                self.repository = new_repository
800
 
                if self.repository._fallback_repositories:
801
 
                    raise AssertionError("didn't expect %r to have "
802
 
                        "fallback_repositories"
803
 
                        % (self.repository,))
804
 
                # this is not paired with an unlock because it's just restoring
805
 
                # the previous state; the lock's released when set_stacked_on_url
806
 
                # returns
807
 
                self.repository.lock_write()
808
855
                # XXX: If you unstack a branch while it has a working tree
809
856
                # with a pending merge, the pending-merged revisions will no
810
857
                # longer be present.  You can (probably) revert and remerge.
951
998
                raise errors.NoSuchRevision(self, stop_revision)
952
999
        return other_history[self_len:stop_revision]
953
1000
 
954
 
    @needs_write_lock
955
1001
    def update_revisions(self, other, stop_revision=None, overwrite=False,
956
1002
                         graph=None):
957
1003
        """Pull in new perfect-fit revisions.
1006
1052
            self._extend_partial_history(distance_from_last)
1007
1053
        return self._partial_revision_history_cache[distance_from_last]
1008
1054
 
1009
 
    @needs_write_lock
1010
1055
    def pull(self, source, overwrite=False, stop_revision=None,
1011
1056
             possible_transports=None, *args, **kwargs):
1012
1057
        """Mirror source into this branch.
1255
1300
                revno = 1
1256
1301
        destination.set_last_revision_info(revno, revision_id)
1257
1302
 
1258
 
    @needs_read_lock
1259
1303
    def copy_content_into(self, destination, revision_id=None):
1260
1304
        """Copy the content of self into destination.
1261
1305
 
1262
1306
        revision_id: if not None, the revision history in the new branch will
1263
1307
                     be truncated to end with revision_id.
1264
1308
        """
1265
 
        self.update_references(destination)
1266
 
        self._synchronize_history(destination, revision_id)
1267
 
        try:
1268
 
            parent = self.get_parent()
1269
 
        except errors.InaccessibleParent, e:
1270
 
            mutter('parent was not accessible to copy: %s', e)
1271
 
        else:
1272
 
            if parent:
1273
 
                destination.set_parent(parent)
1274
 
        if self._push_should_merge_tags():
1275
 
            self.tags.merge_to(destination.tags)
 
1309
        return InterBranch.get(self, destination).copy_content_into(
 
1310
            revision_id=revision_id)
1276
1311
 
1277
1312
    def update_references(self, target):
1278
1313
        if not getattr(self._format, 'supports_reference_locations', False):
1346
1381
        """
1347
1382
        # XXX: Fix the bzrdir API to allow getting the branch back from the
1348
1383
        # clone call. Or something. 20090224 RBC/spiv.
 
1384
        # XXX: Should this perhaps clone colocated branches as well, 
 
1385
        # rather than just the default branch? 20100319 JRV
1349
1386
        if revision_id is None:
1350
1387
            revision_id = self.last_revision()
1351
1388
        dir_to = self.bzrdir.clone_on_transport(to_transport,
1510
1547
        try:
1511
1548
            transport = a_bzrdir.get_branch_transport(None, name=name)
1512
1549
            format_string = transport.get_bytes("format")
1513
 
            return klass._formats[format_string]
 
1550
            format = klass._formats[format_string]
 
1551
            if isinstance(format, MetaDirBranchFormatFactory):
 
1552
                return format()
 
1553
            return format
1514
1554
        except errors.NoSuchFile:
1515
1555
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1516
1556
        except KeyError:
1521
1561
        """Return the current default format."""
1522
1562
        return klass._default_format
1523
1563
 
1524
 
    def get_reference(self, a_bzrdir):
 
1564
    @classmethod
 
1565
    def get_formats(klass):
 
1566
        """Get all the known formats.
 
1567
 
 
1568
        Warning: This triggers a load of all lazy registered formats: do not
 
1569
        use except when that is desireed.
 
1570
        """
 
1571
        result = []
 
1572
        for fmt in klass._formats.values():
 
1573
            if isinstance(fmt, MetaDirBranchFormatFactory):
 
1574
                fmt = fmt()
 
1575
            result.append(fmt)
 
1576
        return result
 
1577
 
 
1578
    def get_reference(self, a_bzrdir, name=None):
1525
1579
        """Get the target reference of the branch in a_bzrdir.
1526
1580
 
1527
1581
        format probing must have been completed before calling
1529
1583
        in a_bzrdir is correct.
1530
1584
 
1531
1585
        :param a_bzrdir: The bzrdir to get the branch data from.
 
1586
        :param name: Name of the colocated branch to fetch
1532
1587
        :return: None if the branch is not a reference branch.
1533
1588
        """
1534
1589
        return None
1535
1590
 
1536
1591
    @classmethod
1537
 
    def set_reference(self, a_bzrdir, to_branch):
 
1592
    def set_reference(self, a_bzrdir, name, to_branch):
1538
1593
        """Set the target reference of the branch in a_bzrdir.
1539
1594
 
1540
1595
        format probing must have been completed before calling
1542
1597
        in a_bzrdir is correct.
1543
1598
 
1544
1599
        :param a_bzrdir: The bzrdir to set the branch reference for.
 
1600
        :param name: Name of colocated branch to set, None for default
1545
1601
        :param to_branch: branch that the checkout is to reference
1546
1602
        """
1547
1603
        raise NotImplementedError(self.set_reference)
1661
1717
 
1662
1718
    @classmethod
1663
1719
    def register_format(klass, format):
1664
 
        """Register a metadir format."""
 
1720
        """Register a metadir format.
 
1721
        
 
1722
        See MetaDirBranchFormatFactory for the ability to register a format
 
1723
        without loading the code the format needs until it is actually used.
 
1724
        """
1665
1725
        klass._formats[format.get_format_string()] = format
1666
1726
        # Metadir formats have a network name of their format string, and get
1667
 
        # registered as class factories.
1668
 
        network_format_registry.register(format.get_format_string(), format.__class__)
 
1727
        # registered as factories.
 
1728
        if isinstance(format, MetaDirBranchFormatFactory):
 
1729
            network_format_registry.register(format.get_format_string(), format)
 
1730
        else:
 
1731
            network_format_registry.register(format.get_format_string(),
 
1732
                format.__class__)
1669
1733
 
1670
1734
    @classmethod
1671
1735
    def set_default_format(klass, format):
1691
1755
        return False  # by default
1692
1756
 
1693
1757
 
 
1758
class MetaDirBranchFormatFactory(registry._LazyObjectGetter):
 
1759
    """A factory for a BranchFormat object, permitting simple lazy registration.
 
1760
    
 
1761
    While none of the built in BranchFormats are lazy registered yet,
 
1762
    bzrlib.tests.test_branch.TestMetaDirBranchFormatFactory demonstrates how to
 
1763
    use it, and the bzr-loom plugin uses it as well (see
 
1764
    bzrlib.plugins.loom.formats).
 
1765
    """
 
1766
 
 
1767
    def __init__(self, format_string, module_name, member_name):
 
1768
        """Create a MetaDirBranchFormatFactory.
 
1769
 
 
1770
        :param format_string: The format string the format has.
 
1771
        :param module_name: Module to load the format class from.
 
1772
        :param member_name: Attribute name within the module for the format class.
 
1773
        """
 
1774
        registry._LazyObjectGetter.__init__(self, module_name, member_name)
 
1775
        self._format_string = format_string
 
1776
        
 
1777
    def get_format_string(self):
 
1778
        """See BranchFormat.get_format_string."""
 
1779
        return self._format_string
 
1780
 
 
1781
    def __call__(self):
 
1782
        """Used for network_format_registry support."""
 
1783
        return self.get_obj()()
 
1784
 
 
1785
 
1694
1786
class BranchHooks(Hooks):
1695
1787
    """A dictionary mapping hook name to a list of callables for branch hooks.
1696
1788
 
2157
2249
        """See BranchFormat.get_format_description()."""
2158
2250
        return "Checkout reference format 1"
2159
2251
 
2160
 
    def get_reference(self, a_bzrdir):
 
2252
    def get_reference(self, a_bzrdir, name=None):
2161
2253
        """See BranchFormat.get_reference()."""
2162
 
        transport = a_bzrdir.get_branch_transport(None)
 
2254
        transport = a_bzrdir.get_branch_transport(None, name=name)
2163
2255
        return transport.get_bytes('location')
2164
2256
 
2165
 
    def set_reference(self, a_bzrdir, to_branch):
 
2257
    def set_reference(self, a_bzrdir, name, to_branch):
2166
2258
        """See BranchFormat.set_reference()."""
2167
 
        transport = a_bzrdir.get_branch_transport(None)
 
2259
        transport = a_bzrdir.get_branch_transport(None, name=name)
2168
2260
        location = transport.put_bytes('location', to_branch.base)
2169
2261
 
2170
2262
    def initialize(self, a_bzrdir, name=None, target_branch=None):
2221
2313
                raise AssertionError("wrong format %r found for %r" %
2222
2314
                    (format, self))
2223
2315
        if location is None:
2224
 
            location = self.get_reference(a_bzrdir)
 
2316
            location = self.get_reference(a_bzrdir, name)
2225
2317
        real_bzrdir = bzrdir.BzrDir.open(
2226
2318
            location, possible_transports=possible_transports)
2227
2319
        result = real_bzrdir.open_branch(name=name, 
2265
2357
    _legacy_formats[0].network_name(), _legacy_formats[0].__class__)
2266
2358
 
2267
2359
 
 
2360
class BranchWriteLockResult(LogicalLockResult):
 
2361
    """The result of write locking a branch.
 
2362
 
 
2363
    :ivar branch_token: The token obtained from the underlying branch lock, or
 
2364
        None.
 
2365
    :ivar unlock: A callable which will unlock the lock.
 
2366
    """
 
2367
 
 
2368
    def __init__(self, unlock, branch_token):
 
2369
        LogicalLockResult.__init__(self, unlock)
 
2370
        self.branch_token = branch_token
 
2371
 
 
2372
    def __repr__(self):
 
2373
        return "BranchWriteLockResult(%s, %s)" % (self.branch_token,
 
2374
            self.unlock)
 
2375
 
 
2376
 
2268
2377
class BzrBranch(Branch, _RelockDebugMixin):
2269
2378
    """A branch stored in the actual filesystem.
2270
2379
 
2324
2433
        return self.control_files.is_locked()
2325
2434
 
2326
2435
    def lock_write(self, token=None):
 
2436
        """Lock the branch for write operations.
 
2437
 
 
2438
        :param token: A token to permit reacquiring a previously held and
 
2439
            preserved lock.
 
2440
        :return: A BranchWriteLockResult.
 
2441
        """
2327
2442
        if not self.is_locked():
2328
2443
            self._note_lock('w')
2329
2444
        # All-in-one needs to always unlock/lock.
2335
2450
        else:
2336
2451
            took_lock = False
2337
2452
        try:
2338
 
            return self.control_files.lock_write(token=token)
 
2453
            return BranchWriteLockResult(self.unlock,
 
2454
                self.control_files.lock_write(token=token))
2339
2455
        except:
2340
2456
            if took_lock:
2341
2457
                self.repository.unlock()
2342
2458
            raise
2343
2459
 
2344
2460
    def lock_read(self):
 
2461
        """Lock the branch for read operations.
 
2462
 
 
2463
        :return: A bzrlib.lock.LogicalLockResult.
 
2464
        """
2345
2465
        if not self.is_locked():
2346
2466
            self._note_lock('r')
2347
2467
        # All-in-one needs to always unlock/lock.
2354
2474
            took_lock = False
2355
2475
        try:
2356
2476
            self.control_files.lock_read()
 
2477
            return LogicalLockResult(self.unlock)
2357
2478
        except:
2358
2479
            if took_lock:
2359
2480
                self.repository.unlock()
3145
3266
    _optimisers = []
3146
3267
    """The available optimised InterBranch types."""
3147
3268
 
3148
 
    @staticmethod
3149
 
    def _get_branch_formats_to_test():
3150
 
        """Return a tuple with the Branch formats to use when testing."""
3151
 
        raise NotImplementedError(InterBranch._get_branch_formats_to_test)
 
3269
    @classmethod
 
3270
    def _get_branch_formats_to_test(klass):
 
3271
        """Return an iterable of format tuples for testing.
 
3272
        
 
3273
        :return: An iterable of (from_format, to_format) to use when testing
 
3274
            this InterBranch class. Each InterBranch class should define this
 
3275
            method itself.
 
3276
        """
 
3277
        raise NotImplementedError(klass._get_branch_formats_to_test)
3152
3278
 
 
3279
    @needs_write_lock
3153
3280
    def pull(self, overwrite=False, stop_revision=None,
3154
3281
             possible_transports=None, local=False):
3155
3282
        """Mirror source into target branch.
3160
3287
        """
3161
3288
        raise NotImplementedError(self.pull)
3162
3289
 
 
3290
    @needs_write_lock
3163
3291
    def update_revisions(self, stop_revision=None, overwrite=False,
3164
3292
                         graph=None):
3165
3293
        """Pull in new perfect-fit revisions.
3173
3301
        """
3174
3302
        raise NotImplementedError(self.update_revisions)
3175
3303
 
 
3304
    @needs_write_lock
3176
3305
    def push(self, overwrite=False, stop_revision=None,
3177
3306
             _override_hook_source_branch=None):
3178
3307
        """Mirror the source branch into the target branch.
3181
3310
        """
3182
3311
        raise NotImplementedError(self.push)
3183
3312
 
 
3313
    @needs_write_lock
 
3314
    def copy_content_into(self, revision_id=None):
 
3315
        """Copy the content of source into target
 
3316
 
 
3317
        revision_id: if not None, the revision history in the new branch will
 
3318
                     be truncated to end with revision_id.
 
3319
        """
 
3320
        raise NotImplementedError(self.copy_content_into)
 
3321
 
3184
3322
 
3185
3323
class GenericInterBranch(InterBranch):
3186
 
    """InterBranch implementation that uses public Branch functions.
3187
 
    """
3188
 
 
3189
 
    @staticmethod
3190
 
    def _get_branch_formats_to_test():
3191
 
        return BranchFormat._default_format, BranchFormat._default_format
3192
 
 
 
3324
    """InterBranch implementation that uses public Branch functions."""
 
3325
 
 
3326
    @classmethod
 
3327
    def is_compatible(klass, source, target):
 
3328
        # GenericBranch uses the public API, so always compatible
 
3329
        return True
 
3330
 
 
3331
    @classmethod
 
3332
    def _get_branch_formats_to_test(klass):
 
3333
        return [(BranchFormat._default_format, BranchFormat._default_format)]
 
3334
 
 
3335
    @classmethod
 
3336
    def unwrap_format(klass, format):
 
3337
        if isinstance(format, remote.RemoteBranchFormat):
 
3338
            format._ensure_real()
 
3339
            return format._custom_format
 
3340
        return format                                                                                                  
 
3341
 
 
3342
    @needs_write_lock
 
3343
    def copy_content_into(self, revision_id=None):
 
3344
        """Copy the content of source into target
 
3345
 
 
3346
        revision_id: if not None, the revision history in the new branch will
 
3347
                     be truncated to end with revision_id.
 
3348
        """
 
3349
        self.source.update_references(self.target)
 
3350
        self.source._synchronize_history(self.target, revision_id)
 
3351
        try:
 
3352
            parent = self.source.get_parent()
 
3353
        except errors.InaccessibleParent, e:
 
3354
            mutter('parent was not accessible to copy: %s', e)
 
3355
        else:
 
3356
            if parent:
 
3357
                self.target.set_parent(parent)
 
3358
        if self.source._push_should_merge_tags():
 
3359
            self.source.tags.merge_to(self.target.tags)
 
3360
 
 
3361
    @needs_write_lock
3193
3362
    def update_revisions(self, stop_revision=None, overwrite=False,
3194
3363
        graph=None):
3195
3364
        """See InterBranch.update_revisions()."""
3196
 
        self.source.lock_read()
3197
 
        try:
3198
 
            other_revno, other_last_revision = self.source.last_revision_info()
3199
 
            stop_revno = None # unknown
3200
 
            if stop_revision is None:
3201
 
                stop_revision = other_last_revision
3202
 
                if _mod_revision.is_null(stop_revision):
3203
 
                    # if there are no commits, we're done.
3204
 
                    return
3205
 
                stop_revno = other_revno
3206
 
 
3207
 
            # what's the current last revision, before we fetch [and change it
3208
 
            # possibly]
3209
 
            last_rev = _mod_revision.ensure_null(self.target.last_revision())
3210
 
            # we fetch here so that we don't process data twice in the common
3211
 
            # case of having something to pull, and so that the check for
3212
 
            # already merged can operate on the just fetched graph, which will
3213
 
            # be cached in memory.
3214
 
            self.target.fetch(self.source, stop_revision)
3215
 
            # Check to see if one is an ancestor of the other
3216
 
            if not overwrite:
3217
 
                if graph is None:
3218
 
                    graph = self.target.repository.get_graph()
3219
 
                if self.target._check_if_descendant_or_diverged(
3220
 
                        stop_revision, last_rev, graph, self.source):
3221
 
                    # stop_revision is a descendant of last_rev, but we aren't
3222
 
                    # overwriting, so we're done.
3223
 
                    return
3224
 
            if stop_revno is None:
3225
 
                if graph is None:
3226
 
                    graph = self.target.repository.get_graph()
3227
 
                this_revno, this_last_revision = \
3228
 
                        self.target.last_revision_info()
3229
 
                stop_revno = graph.find_distance_to_null(stop_revision,
3230
 
                                [(other_last_revision, other_revno),
3231
 
                                 (this_last_revision, this_revno)])
3232
 
            self.target.set_last_revision_info(stop_revno, stop_revision)
3233
 
        finally:
3234
 
            self.source.unlock()
3235
 
 
 
3365
        other_revno, other_last_revision = self.source.last_revision_info()
 
3366
        stop_revno = None # unknown
 
3367
        if stop_revision is None:
 
3368
            stop_revision = other_last_revision
 
3369
            if _mod_revision.is_null(stop_revision):
 
3370
                # if there are no commits, we're done.
 
3371
                return
 
3372
            stop_revno = other_revno
 
3373
 
 
3374
        # what's the current last revision, before we fetch [and change it
 
3375
        # possibly]
 
3376
        last_rev = _mod_revision.ensure_null(self.target.last_revision())
 
3377
        # we fetch here so that we don't process data twice in the common
 
3378
        # case of having something to pull, and so that the check for
 
3379
        # already merged can operate on the just fetched graph, which will
 
3380
        # be cached in memory.
 
3381
        self.target.fetch(self.source, stop_revision)
 
3382
        # Check to see if one is an ancestor of the other
 
3383
        if not overwrite:
 
3384
            if graph is None:
 
3385
                graph = self.target.repository.get_graph()
 
3386
            if self.target._check_if_descendant_or_diverged(
 
3387
                    stop_revision, last_rev, graph, self.source):
 
3388
                # stop_revision is a descendant of last_rev, but we aren't
 
3389
                # overwriting, so we're done.
 
3390
                return
 
3391
        if stop_revno is None:
 
3392
            if graph is None:
 
3393
                graph = self.target.repository.get_graph()
 
3394
            this_revno, this_last_revision = \
 
3395
                    self.target.last_revision_info()
 
3396
            stop_revno = graph.find_distance_to_null(stop_revision,
 
3397
                            [(other_last_revision, other_revno),
 
3398
                             (this_last_revision, this_revno)])
 
3399
        self.target.set_last_revision_info(stop_revno, stop_revision)
 
3400
 
 
3401
    @needs_write_lock
3236
3402
    def pull(self, overwrite=False, stop_revision=None,
3237
 
             possible_transports=None, _hook_master=None, run_hooks=True,
 
3403
             possible_transports=None, run_hooks=True,
3238
3404
             _override_hook_target=None, local=False):
3239
 
        """See Branch.pull.
 
3405
        """Pull from source into self, updating my master if any.
3240
3406
 
3241
 
        :param _hook_master: Private parameter - set the branch to
3242
 
            be supplied as the master to pull hooks.
3243
3407
        :param run_hooks: Private parameter - if false, this branch
3244
3408
            is being called because it's the master of the primary branch,
3245
3409
            so it should not run its hooks.
3246
 
        :param _override_hook_target: Private parameter - set the branch to be
3247
 
            supplied as the target_branch to pull hooks.
3248
 
        :param local: Only update the local branch, and not the bound branch.
3249
3410
        """
3250
 
        # This type of branch can't be bound.
3251
 
        if local:
 
3411
        bound_location = self.target.get_bound_location()
 
3412
        if local and not bound_location:
3252
3413
            raise errors.LocalRequiresBoundBranch()
3253
 
        result = PullResult()
3254
 
        result.source_branch = self.source
3255
 
        if _override_hook_target is None:
3256
 
            result.target_branch = self.target
3257
 
        else:
3258
 
            result.target_branch = _override_hook_target
3259
 
        self.source.lock_read()
 
3414
        master_branch = None
 
3415
        if not local and bound_location and self.source.user_url != bound_location:
 
3416
            # not pulling from master, so we need to update master.
 
3417
            master_branch = self.target.get_master_branch(possible_transports)
 
3418
            master_branch.lock_write()
3260
3419
        try:
3261
 
            # We assume that during 'pull' the target repository is closer than
3262
 
            # the source one.
3263
 
            self.source.update_references(self.target)
3264
 
            graph = self.target.repository.get_graph(self.source.repository)
3265
 
            # TODO: Branch formats should have a flag that indicates 
3266
 
            # that revno's are expensive, and pull() should honor that flag.
3267
 
            # -- JRV20090506
3268
 
            result.old_revno, result.old_revid = \
3269
 
                self.target.last_revision_info()
3270
 
            self.target.update_revisions(self.source, stop_revision,
3271
 
                overwrite=overwrite, graph=graph)
3272
 
            # TODO: The old revid should be specified when merging tags, 
3273
 
            # so a tags implementation that versions tags can only 
3274
 
            # pull in the most recent changes. -- JRV20090506
3275
 
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3276
 
                overwrite)
3277
 
            result.new_revno, result.new_revid = self.target.last_revision_info()
3278
 
            if _hook_master:
3279
 
                result.master_branch = _hook_master
3280
 
                result.local_branch = result.target_branch
3281
 
            else:
3282
 
                result.master_branch = result.target_branch
3283
 
                result.local_branch = None
3284
 
            if run_hooks:
3285
 
                for hook in Branch.hooks['post_pull']:
3286
 
                    hook(result)
 
3420
            if master_branch:
 
3421
                # pull from source into master.
 
3422
                master_branch.pull(self.source, overwrite, stop_revision,
 
3423
                    run_hooks=False)
 
3424
            return self._pull(overwrite,
 
3425
                stop_revision, _hook_master=master_branch,
 
3426
                run_hooks=run_hooks,
 
3427
                _override_hook_target=_override_hook_target)
3287
3428
        finally:
3288
 
            self.source.unlock()
3289
 
        return result
 
3429
            if master_branch:
 
3430
                master_branch.unlock()
3290
3431
 
3291
3432
    def push(self, overwrite=False, stop_revision=None,
3292
3433
             _override_hook_source_branch=None):
3354
3495
            _run_hooks()
3355
3496
            return result
3356
3497
 
3357
 
    @classmethod
3358
 
    def is_compatible(self, source, target):
3359
 
        # GenericBranch uses the public API, so always compatible
3360
 
        return True
3361
 
 
3362
 
 
3363
 
class InterToBranch5(GenericInterBranch):
3364
 
 
3365
 
    @staticmethod
3366
 
    def _get_branch_formats_to_test():
3367
 
        return BranchFormat._default_format, BzrBranchFormat5()
3368
 
 
3369
 
    def pull(self, overwrite=False, stop_revision=None,
3370
 
             possible_transports=None, run_hooks=True,
 
3498
    def _pull(self, overwrite=False, stop_revision=None,
 
3499
             possible_transports=None, _hook_master=None, run_hooks=True,
3371
3500
             _override_hook_target=None, local=False):
3372
 
        """Pull from source into self, updating my master if any.
3373
 
 
 
3501
        """See Branch.pull.
 
3502
 
 
3503
        This function is the core worker, used by GenericInterBranch.pull to
 
3504
        avoid duplication when pulling source->master and source->local.
 
3505
 
 
3506
        :param _hook_master: Private parameter - set the branch to
 
3507
            be supplied as the master to pull hooks.
3374
3508
        :param run_hooks: Private parameter - if false, this branch
3375
3509
            is being called because it's the master of the primary branch,
3376
3510
            so it should not run its hooks.
 
3511
        :param _override_hook_target: Private parameter - set the branch to be
 
3512
            supplied as the target_branch to pull hooks.
 
3513
        :param local: Only update the local branch, and not the bound branch.
3377
3514
        """
3378
 
        bound_location = self.target.get_bound_location()
3379
 
        if local and not bound_location:
 
3515
        # This type of branch can't be bound.
 
3516
        if local:
3380
3517
            raise errors.LocalRequiresBoundBranch()
3381
 
        master_branch = None
3382
 
        if not local and bound_location and self.source.user_url != bound_location:
3383
 
            # not pulling from master, so we need to update master.
3384
 
            master_branch = self.target.get_master_branch(possible_transports)
3385
 
            master_branch.lock_write()
 
3518
        result = PullResult()
 
3519
        result.source_branch = self.source
 
3520
        if _override_hook_target is None:
 
3521
            result.target_branch = self.target
 
3522
        else:
 
3523
            result.target_branch = _override_hook_target
 
3524
        self.source.lock_read()
3386
3525
        try:
3387
 
            if master_branch:
3388
 
                # pull from source into master.
3389
 
                master_branch.pull(self.source, overwrite, stop_revision,
3390
 
                    run_hooks=False)
3391
 
            return super(InterToBranch5, self).pull(overwrite,
3392
 
                stop_revision, _hook_master=master_branch,
3393
 
                run_hooks=run_hooks,
3394
 
                _override_hook_target=_override_hook_target)
 
3526
            # We assume that during 'pull' the target repository is closer than
 
3527
            # the source one.
 
3528
            self.source.update_references(self.target)
 
3529
            graph = self.target.repository.get_graph(self.source.repository)
 
3530
            # TODO: Branch formats should have a flag that indicates 
 
3531
            # that revno's are expensive, and pull() should honor that flag.
 
3532
            # -- JRV20090506
 
3533
            result.old_revno, result.old_revid = \
 
3534
                self.target.last_revision_info()
 
3535
            self.target.update_revisions(self.source, stop_revision,
 
3536
                overwrite=overwrite, graph=graph)
 
3537
            # TODO: The old revid should be specified when merging tags, 
 
3538
            # so a tags implementation that versions tags can only 
 
3539
            # pull in the most recent changes. -- JRV20090506
 
3540
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
 
3541
                overwrite)
 
3542
            result.new_revno, result.new_revid = self.target.last_revision_info()
 
3543
            if _hook_master:
 
3544
                result.master_branch = _hook_master
 
3545
                result.local_branch = result.target_branch
 
3546
            else:
 
3547
                result.master_branch = result.target_branch
 
3548
                result.local_branch = None
 
3549
            if run_hooks:
 
3550
                for hook in Branch.hooks['post_pull']:
 
3551
                    hook(result)
3395
3552
        finally:
3396
 
            if master_branch:
3397
 
                master_branch.unlock()
 
3553
            self.source.unlock()
 
3554
        return result
3398
3555
 
3399
3556
 
3400
3557
InterBranch.register_optimiser(GenericInterBranch)
3401
 
InterBranch.register_optimiser(InterToBranch5)