/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: Robert Collins
  • Date: 2010-05-06 11:08:10 UTC
  • mto: This revision was merged to the branch mainline in revision 5223.
  • Revision ID: robertc@robertcollins.net-20100506110810-h3j07fh5gmw54s25
Cleaner matcher matching revised unlocking protocol.

Show diffs side-by-side

added added

removed removed

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