/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/remote.py

merge bzr.dev r4029

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
 
22
22
from bzrlib import (
23
23
    branch,
 
24
    bzrdir,
24
25
    debug,
25
26
    errors,
26
27
    graph,
27
28
    lockdir,
 
29
    pack,
28
30
    repository,
29
31
    revision,
30
32
    symbol_versioning,
41
43
from bzrlib.smart import client, vfs
42
44
from bzrlib.revision import ensure_null, NULL_REVISION
43
45
from bzrlib.trace import mutter, note, warning
 
46
from bzrlib.versionedfile import record_to_fulltext_bytes
44
47
 
45
48
 
46
49
class _RpcHelper(object):
71
74
class RemoteBzrDir(BzrDir, _RpcHelper):
72
75
    """Control directory on a remote server, accessed via bzr:// or similar."""
73
76
 
74
 
    def __init__(self, transport, _client=None):
 
77
    def __init__(self, transport, format, _client=None):
75
78
        """Construct a RemoteBzrDir.
76
79
 
77
80
        :param _client: Private parameter for testing. Disables probing and the
78
81
            use of a real bzrdir.
79
82
        """
80
 
        BzrDir.__init__(self, transport, RemoteBzrDirFormat())
 
83
        BzrDir.__init__(self, transport, format)
81
84
        # this object holds a delegated bzrdir that uses file-level operations
82
85
        # to talk to the other side
83
86
        self._real_bzrdir = None
113
116
        return self._real_bzrdir.cloning_metadir(stacked)
114
117
 
115
118
    def create_repository(self, shared=False):
116
 
        self._ensure_real()
117
 
        self._real_bzrdir.create_repository(shared=shared)
118
 
        return self.open_repository()
 
119
        # as per meta1 formats - just delegate to the format object which may
 
120
        # be parameterised.
 
121
        result = self._format.repository_format.initialize(self, shared)
 
122
        if not isinstance(result, RemoteRepository):
 
123
            return self.open_repository()
 
124
        else:
 
125
            return result
119
126
 
120
127
    def destroy_repository(self):
121
128
        """See BzrDir.destroy_repository"""
123
130
        self._real_bzrdir.destroy_repository()
124
131
 
125
132
    def create_branch(self):
126
 
        self._ensure_real()
127
 
        real_branch = self._real_bzrdir.create_branch()
128
 
        return RemoteBranch(self, self.find_repository(), real_branch)
 
133
        # as per meta1 formats - just delegate to the format object which may
 
134
        # be parameterised.
 
135
        real_branch = self._format.get_branch_format().initialize(self)
 
136
        if not isinstance(real_branch, RemoteBranch):
 
137
            return RemoteBranch(self, self.find_repository(), real_branch)
 
138
        else:
 
139
            return real_branch
129
140
 
130
141
    def destroy_branch(self):
131
142
        """See BzrDir.destroy_branch"""
197
208
            format.supports_external_lookups = (response[4] == 'yes')
198
209
            # Used to support creating a real format instance when needed.
199
210
            format._creating_bzrdir = self
200
 
            return RemoteRepository(self, format)
 
211
            remote_repo = RemoteRepository(self, format)
 
212
            format._creating_repo = remote_repo
 
213
            return remote_repo
201
214
        else:
202
215
            raise errors.NoRepositoryPresent(self)
203
216
 
257
270
    the attributes rich_root_data and supports_tree_reference are set
258
271
    on a per instance basis, and are not set (and should not be) at
259
272
    the class level.
 
273
 
 
274
    :ivar _custom_format: If set, a specific concrete repository format that 
 
275
        will be used when initializing a repository with this
 
276
        RemoteRepositoryFormat.
 
277
    :ivar _creating_repo: If set, the repository object that this
 
278
        RemoteRepositoryFormat was created for: it can be called into
 
279
        to obtain data like the network name.
260
280
    """
261
281
 
262
282
    _matchingbzrdir = RemoteBzrDirFormat()
263
283
 
264
 
    def initialize(self, a_bzrdir, shared=False):
265
 
        if not isinstance(a_bzrdir, RemoteBzrDir):
 
284
    def __init__(self):
 
285
        repository.RepositoryFormat.__init__(self)
 
286
        self._custom_format = None
 
287
        self._network_name = None
 
288
        self._creating_bzrdir = None
 
289
 
 
290
    def _vfs_initialize(self, a_bzrdir, shared):
 
291
        """Helper for common code in initialize."""
 
292
        if self._custom_format:
 
293
            # Custom format requested
 
294
            result = self._custom_format.initialize(a_bzrdir, shared=shared)
 
295
        elif self._creating_bzrdir is not None:
 
296
            # Use the format that the repository we were created to back
 
297
            # has.
266
298
            prior_repo = self._creating_bzrdir.open_repository()
267
299
            prior_repo._ensure_real()
268
 
            return prior_repo._real_repository._format.initialize(
 
300
            result = prior_repo._real_repository._format.initialize(
269
301
                a_bzrdir, shared=shared)
270
 
        return a_bzrdir.create_repository(shared=shared)
 
302
        else:
 
303
            # assume that a_bzr is a RemoteBzrDir but the smart server didn't
 
304
            # support remote initialization.
 
305
            # We delegate to a real object at this point (as RemoteBzrDir
 
306
            # delegate to the repository format which would lead to infinite
 
307
            # recursion if we just called a_bzrdir.create_repository.
 
308
            a_bzrdir._ensure_real()
 
309
            result = a_bzrdir._real_bzrdir.create_repository(shared=shared)
 
310
        if not isinstance(result, RemoteRepository):
 
311
            return self.open(a_bzrdir)
 
312
        else:
 
313
            return result
 
314
 
 
315
    def initialize(self, a_bzrdir, shared=False):
 
316
        # Being asked to create on a non RemoteBzrDir:
 
317
        if not isinstance(a_bzrdir, RemoteBzrDir):
 
318
            return self._vfs_initialize(a_bzrdir, shared)
 
319
        medium = a_bzrdir._client._medium
 
320
        if medium._is_remote_before((1, 13)):
 
321
            return self._vfs_initialize(a_bzrdir, shared)
 
322
        # Creating on a remote bzr dir.
 
323
        # 1) get the network name to use.
 
324
        if self._custom_format:
 
325
            network_name = self._custom_format.network_name()
 
326
        else:
 
327
            # Select the current bzrlib default and ask for that.
 
328
            reference_bzrdir_format = bzrdir.format_registry.get('default')()
 
329
            reference_format = reference_bzrdir_format.repository_format
 
330
            network_name = reference_format.network_name()
 
331
        # 2) try direct creation via RPC
 
332
        path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
 
333
        verb = 'BzrDir.create_repository'
 
334
        if shared:
 
335
            shared_str = 'True'
 
336
        else:
 
337
            shared_str = 'False'
 
338
        try:
 
339
            response = a_bzrdir._call(verb, path, network_name, shared_str)
 
340
        except errors.UnknownSmartMethod:
 
341
            # Fallback - use vfs methods
 
342
            return self._vfs_initialize(a_bzrdir, shared)
 
343
        else:
 
344
            # Turn the response into a RemoteRepository object.
 
345
            format = RemoteRepositoryFormat()
 
346
            format.rich_root_data = (response[1] == 'yes')
 
347
            format.supports_tree_reference = (response[2] == 'yes')
 
348
            format.supports_external_lookups = (response[3] == 'yes')
 
349
            format._network_name = response[4]
 
350
            # Used to support creating a real format instance when needed.
 
351
            format._creating_bzrdir = a_bzrdir
 
352
            remote_repo = RemoteRepository(a_bzrdir, format)
 
353
            format._creating_repo = remote_repo
 
354
            return remote_repo
271
355
    
272
356
    def open(self, a_bzrdir):
273
357
        if not isinstance(a_bzrdir, RemoteBzrDir):
289
373
            raise errors.BadConversionTarget(
290
374
                'Does not support nested trees', target_format)
291
375
 
 
376
    def network_name(self):
 
377
        if self._network_name:
 
378
            return self._network_name
 
379
        self._creating_repo._ensure_real()
 
380
        return self._creating_repo._real_repository._format.network_name()
 
381
 
 
382
    @property
 
383
    def _serializer(self):
 
384
        # We should only be getting asked for the serializer for
 
385
        # RemoteRepositoryFormat objects when the RemoteRepositoryFormat object
 
386
        # is a concrete instance for a RemoteRepository. In this case we know
 
387
        # the creating_repo and can use it to supply the serializer.
 
388
        self._creating_repo._ensure_real()
 
389
        return self._creating_repo._real_repository._format._serializer
 
390
 
292
391
 
293
392
class RemoteRepository(_RpcHelper):
294
393
    """Repository accessed over rpc.
367
466
        self._ensure_real()
368
467
        return self._real_repository.commit_write_group()
369
468
 
 
469
    def resume_write_group(self, tokens):
 
470
        self._ensure_real()
 
471
        return self._real_repository.resume_write_group(tokens)
 
472
 
 
473
    def suspend_write_group(self):
 
474
        self._ensure_real()
 
475
        return self._real_repository.suspend_write_group()
 
476
 
370
477
    def _ensure_real(self):
371
478
        """Ensure that there is a _real_repository set.
372
479
 
435
542
            
436
543
        return revision_graph
437
544
 
 
545
    def _get_sink(self):
 
546
        """See Repository._get_sink()."""
 
547
        return RemoteStreamSink(self)
 
548
 
438
549
    def has_revision(self, revision_id):
439
550
        """See Repository.has_revision()."""
440
551
        if revision_id == NULL_REVISION:
1106
1217
        return self._real_repository.revisions
1107
1218
 
1108
1219
    def set_make_working_trees(self, new_value):
1109
 
        self._ensure_real()
1110
 
        self._real_repository.set_make_working_trees(new_value)
 
1220
        if new_value:
 
1221
            new_value_str = "True"
 
1222
        else:
 
1223
            new_value_str = "False"
 
1224
        path = self.bzrdir._path_for_remote_call(self._client)
 
1225
        try:
 
1226
            response = self._call(
 
1227
                'Repository.set_make_working_trees', path, new_value_str)
 
1228
        except errors.UnknownSmartMethod:
 
1229
            self._ensure_real()
 
1230
            self._real_repository.set_make_working_trees(new_value)
 
1231
        else:
 
1232
            if response[0] != 'ok':
 
1233
                raise errors.UnexpectedSmartServerResponse(response)
1111
1234
 
1112
1235
    @property
1113
1236
    def signatures(self):
1220
1343
            raise errors.UnexpectedSmartServerResponse(response)
1221
1344
 
1222
1345
 
 
1346
class RemoteStreamSink(repository.StreamSink):
 
1347
 
 
1348
    def _insert_real(self, stream, src_format):
 
1349
        self.target_repo._ensure_real()
 
1350
        sink = self.target_repo._real_repository._get_sink()
 
1351
        return sink.insert_stream(stream, src_format)
 
1352
 
 
1353
    def insert_stream(self, stream, src_format):
 
1354
        repo = self.target_repo
 
1355
        # Until we can handle deltas in stack repositories we can't hand all
 
1356
        # the processing off to a remote server.
 
1357
        if self.target_repo._fallback_repositories:
 
1358
            return self._insert_real(stream, src_format)
 
1359
        client = repo._client
 
1360
        medium = client._medium
 
1361
        if medium._is_remote_before((1,13)):
 
1362
            # No possible way this can work.
 
1363
            return self._insert_real(stream, src_format)
 
1364
        path = repo.bzrdir._path_for_remote_call(client)
 
1365
        # XXX: Ugly but important for correctness, *will* be fixed during 1.13
 
1366
        # cycle. Pushing a stream that is interrupted results in a fallback to
 
1367
        # the _real_repositories sink *with a partial stream*. Thats bad
 
1368
        # because we insert less data than bzr expected. To avoid this we do a
 
1369
        # trial push to make sure the verb is accessible, and do not fallback
 
1370
        # when actually pushing the stream. A cleanup patch is going to look at
 
1371
        # rewinding/restarting the stream/partial buffering etc.
 
1372
        byte_stream = self._stream_to_byte_stream([], src_format)
 
1373
        try:
 
1374
            response = client.call_with_body_stream(
 
1375
                ('Repository.insert_stream', path), byte_stream)
 
1376
        except errors.UnknownSmartMethod:
 
1377
            medium._remember_remote_is_before((1,13))
 
1378
            return self._insert_real(stream, src_format)
 
1379
        byte_stream = self._stream_to_byte_stream(stream, src_format)
 
1380
        response = client.call_with_body_stream(
 
1381
            ('Repository.insert_stream', path), byte_stream)
 
1382
        if response[0][0] not in ('ok', ):
 
1383
            raise errors.UnexpectedSmartServerResponse(response)
 
1384
            
 
1385
    def _stream_to_byte_stream(self, stream, src_format):
 
1386
        bytes = []
 
1387
        pack_writer = pack.ContainerWriter(bytes.append)
 
1388
        pack_writer.begin()
 
1389
        pack_writer.add_bytes_record(src_format.network_name(), '')
 
1390
        adapters = {}
 
1391
        def get_adapter(adapter_key):
 
1392
            try:
 
1393
                return adapters[adapter_key]
 
1394
            except KeyError:
 
1395
                adapter_factory = adapter_registry.get(adapter_key)
 
1396
                adapter = adapter_factory(self)
 
1397
                adapters[adapter_key] = adapter
 
1398
                return adapter
 
1399
        for substream_type, substream in stream:
 
1400
            for record in substream:
 
1401
                if record.storage_kind in ('chunked', 'fulltext'):
 
1402
                    serialised = record_to_fulltext_bytes(record)
 
1403
                else:
 
1404
                    serialised = record.get_bytes_as(record.storage_kind)
 
1405
                pack_writer.add_bytes_record(serialised, [(substream_type,)])
 
1406
                for b in bytes:
 
1407
                    yield b
 
1408
                del bytes[:]
 
1409
        pack_writer.end()
 
1410
        for b in bytes:
 
1411
            yield b
 
1412
 
 
1413
 
1223
1414
class RemoteBranchLockableFiles(LockableFiles):
1224
1415
    """A 'LockableFiles' implementation that talks to a smart server.
1225
1416
    
1261
1452
        return a_bzrdir.open_branch()
1262
1453
 
1263
1454
    def initialize(self, a_bzrdir):
1264
 
        return a_bzrdir.create_branch()
 
1455
        # Delegate to a _real object here - the RemoteBzrDir format now
 
1456
        # supports delegating to parameterised branch formats and as such
 
1457
        # this RemoteBranchFormat method is only called when no specific format
 
1458
        # is selected.
 
1459
        if not isinstance(a_bzrdir, RemoteBzrDir):
 
1460
            result = a_bzrdir.create_branch()
 
1461
        else:
 
1462
            a_bzrdir._ensure_real()
 
1463
            result = a_bzrdir._real_bzrdir.create_branch()
 
1464
        if not isinstance(result, RemoteBranch):
 
1465
            result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result)
 
1466
        return result
1265
1467
 
1266
1468
    def supports_tags(self):
1267
1469
        # Remote branches might support tags, but we won't know until we
1580
1782
 
1581
1783
    def _set_last_revision_descendant(self, revision_id, other_branch,
1582
1784
            allow_diverged=False, allow_overwrite_descendant=False):
 
1785
        # This performs additional work to meet the hook contract; while its
 
1786
        # undesirable, we have to synthesise the revno to call the hook, and
 
1787
        # not calling the hook is worse as it means changes can't be prevented.
 
1788
        # Having calculated this though, we can't just call into
 
1789
        # set_last_revision_info as a simple call, because there is a set_rh
 
1790
        # hook that some folk may still be using.
 
1791
        old_revno, old_revid = self.last_revision_info()
 
1792
        history = self._lefthand_history(revision_id)
 
1793
        self._run_pre_change_branch_tip_hooks(len(history), revision_id)
1583
1794
        err_context = {'other_branch': other_branch}
1584
1795
        response = self._call('Branch.set_last_revision_ex',
1585
1796
            self._remote_path(), self._lock_token, self._repo_lock_token,
1590
1801
            raise errors.UnexpectedSmartServerResponse(response)
1591
1802
        new_revno, new_revision_id = response[1:]
1592
1803
        self._last_revision_info_cache = new_revno, new_revision_id
 
1804
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
1593
1805
        if self._real_branch is not None:
1594
1806
            cache = new_revno, new_revision_id
1595
1807
            self._real_branch._last_revision_info_cache = cache
1596
1808
 
1597
1809
    def _set_last_revision(self, revision_id):
 
1810
        old_revno, old_revid = self.last_revision_info()
 
1811
        # This performs additional work to meet the hook contract; while its
 
1812
        # undesirable, we have to synthesise the revno to call the hook, and
 
1813
        # not calling the hook is worse as it means changes can't be prevented.
 
1814
        # Having calculated this though, we can't just call into
 
1815
        # set_last_revision_info as a simple call, because there is a set_rh
 
1816
        # hook that some folk may still be using.
 
1817
        history = self._lefthand_history(revision_id)
 
1818
        self._run_pre_change_branch_tip_hooks(len(history), revision_id)
1598
1819
        self._clear_cached_state()
1599
1820
        response = self._call('Branch.set_last_revision',
1600
1821
            self._remote_path(), self._lock_token, self._repo_lock_token,
1601
1822
            revision_id)
1602
1823
        if response != ('ok',):
1603
1824
            raise errors.UnexpectedSmartServerResponse(response)
 
1825
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
1604
1826
 
1605
1827
    @needs_write_lock
1606
1828
    def set_revision_history(self, rev_history):
1612
1834
        else:
1613
1835
            rev_id = rev_history[-1]
1614
1836
        self._set_last_revision(rev_id)
 
1837
        for hook in branch.Branch.hooks['set_rh']:
 
1838
            hook(self, rev_history)
1615
1839
        self._cache_revision_history(rev_history)
1616
1840
 
1617
1841
    def get_parent(self):
1618
1842
        self._ensure_real()
1619
1843
        return self._real_branch.get_parent()
 
1844
 
 
1845
    def _get_parent_location(self):
 
1846
        # Used by tests, when checking normalisation of given vs stored paths.
 
1847
        self._ensure_real()
 
1848
        return self._real_branch._get_parent_location()
1620
1849
        
1621
1850
    def set_parent(self, url):
1622
1851
        self._ensure_real()
1623
1852
        return self._real_branch.set_parent(url)
 
1853
 
 
1854
    def _set_parent_location(self, url):
 
1855
        # Used by tests, to poke bad urls into branch configurations
 
1856
        if url is None:
 
1857
            self.set_parent(url)
 
1858
        else:
 
1859
            self._ensure_real()
 
1860
            return self._real_branch._set_parent_location(url)
1624
1861
        
1625
1862
    def set_stacked_on_url(self, stacked_location):
1626
1863
        """Set the URL this branch is stacked against.
1678
1915
 
1679
1916
    @needs_write_lock
1680
1917
    def set_last_revision_info(self, revno, revision_id):
 
1918
        # XXX: These should be returned by the set_last_revision_info verb
 
1919
        old_revno, old_revid = self.last_revision_info()
 
1920
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
1681
1921
        revision_id = ensure_null(revision_id)
1682
1922
        try:
1683
1923
            response = self._call('Branch.set_last_revision_info',
1692
1932
        if response == ('ok',):
1693
1933
            self._clear_cached_state()
1694
1934
            self._last_revision_info_cache = revno, revision_id
 
1935
            self._run_post_change_branch_tip_hooks(old_revno, old_revid)
1695
1936
            # Update the _real_branch's cache too.
1696
1937
            if self._real_branch is not None:
1697
1938
                cache = self._last_revision_info_cache
1704
1945
                                  other_branch=None):
1705
1946
        medium = self._client._medium
1706
1947
        if not medium._is_remote_before((1, 6)):
 
1948
            # Use a smart method for 1.6 and above servers
1707
1949
            try:
1708
1950
                self._set_last_revision_descendant(revision_id, other_branch,
1709
1951
                    allow_diverged=True, allow_overwrite_descendant=True)
1711
1953
            except errors.UnknownSmartMethod:
1712
1954
                medium._remember_remote_is_before((1, 6))
1713
1955
        self._clear_cached_state_of_remote_branch_only()
1714
 
        self._ensure_real()
1715
 
        self._real_branch.generate_revision_history(
1716
 
            revision_id, last_rev=last_rev, other_branch=other_branch)
 
1956
        self.set_revision_history(self._lefthand_history(revision_id,
 
1957
            last_rev=last_rev,other_branch=other_branch))
1717
1958
 
1718
1959
    @property
1719
1960
    def tags(self):