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

Only actually fetch tags if "branch.fetch_tags" is set to true.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007-2008 Canonical Ltd
 
1
# Copyright (C) 2007-2010 Jelmer Vernooij <jelmer@samba.org>
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
 
import bzrlib
18
 
from bzrlib import urlutils
19
 
from bzrlib.bzrdir import BzrDir, BzrDirFormat
20
 
from bzrlib.errors import NoSuchFile, NotLocalUrl
21
 
from bzrlib.lockable_files import TransportLock
22
 
from bzrlib.repository import Repository
23
 
from bzrlib.trace import info
24
 
from bzrlib.transport import Transport
25
 
 
26
 
from bzrlib.plugins.git import git
27
 
from bzrlib.plugins.git.branch import GitBranch
28
 
from bzrlib.plugins.git.dir import GitDir
29
 
from bzrlib.plugins.git.foreign import ForeignBranch
30
 
from bzrlib.plugins.git.repository import GitFormat, GitRepository
31
 
 
 
17
from bzrlib import (
 
18
    config,
 
19
    debug,
 
20
    trace,
 
21
    ui,
 
22
    urlutils,
 
23
    )
 
24
from bzrlib.errors import (
 
25
    BzrError,
 
26
    InvalidRevisionId,
 
27
    NoSuchFile,
 
28
    NoSuchRevision,
 
29
    NotBranchError,
 
30
    NotLocalUrl,
 
31
    UninitializableFormat,
 
32
    )
 
33
from bzrlib.transport import (
 
34
    Transport,
 
35
    )
 
36
 
 
37
from bzrlib.plugins.git import (
 
38
    lazy_check_versions,
 
39
    )
 
40
lazy_check_versions()
 
41
 
 
42
from bzrlib.plugins.git.branch import (
 
43
    GitBranch,
 
44
    GitTags,
 
45
    )
 
46
from bzrlib.plugins.git.dir import (
 
47
    GitControlDirFormat,
 
48
    GitDir,
 
49
    GitLockableFiles,
 
50
    GitLock,
 
51
    )
 
52
from bzrlib.plugins.git.errors import (
 
53
    GitSmartRemoteNotSupported,
 
54
    NoSuchRef,
 
55
    )
 
56
from bzrlib.plugins.git.mapping import (
 
57
    mapping_registry,
 
58
    )
 
59
from bzrlib.plugins.git.repository import (
 
60
    GitRepository,
 
61
    )
 
62
from bzrlib.plugins.git.refs import (
 
63
    extract_tags,
 
64
    branch_name_to_ref,
 
65
    )
 
66
 
 
67
import dulwich as git
 
68
from dulwich.errors import (
 
69
    GitProtocolError,
 
70
    )
 
71
from dulwich.pack import (
 
72
    Pack,
 
73
    PackData,
 
74
    )
 
75
import os
 
76
import tempfile
32
77
import urllib
33
78
import urlparse
34
 
 
35
 
from dulwich.pack import PackData
 
79
urlparse.uses_netloc.extend(['git', 'git+ssh'])
 
80
 
 
81
from dulwich.pack import load_pack_index
 
82
 
 
83
 
 
84
# Don't run any tests on GitSmartTransport as it is not intended to be
 
85
# a full implementation of Transport
 
86
def get_test_permutations():
 
87
    return []
 
88
 
 
89
 
 
90
def split_git_url(url):
 
91
    """Split a Git URL.
 
92
 
 
93
    :param url: Git URL
 
94
    :return: Tuple with host, port, username, path.
 
95
    """
 
96
    (scheme, netloc, loc, _, _) = urlparse.urlsplit(url)
 
97
    path = urllib.unquote(loc)
 
98
    if path.startswith("/~"):
 
99
        path = path[1:]
 
100
    (username, hostport) = urllib.splituser(netloc)
 
101
    (host, port) = urllib.splitnport(hostport, None)
 
102
    return (host, port, username, path)
 
103
 
 
104
 
 
105
def parse_git_error(url, message):
 
106
    """Parse a remote git server error and return a bzr exception.
 
107
 
 
108
    :param url: URL of the remote repository
 
109
    :param message: Message sent by the remote git server
 
110
    """
 
111
    message = str(message).strip()
 
112
    if message.startswith("Could not find Repository "):
 
113
        return NotBranchError(url, message)
 
114
    # Don't know, just return it to the user as-is
 
115
    return BzrError(message)
36
116
 
37
117
 
38
118
class GitSmartTransport(Transport):
39
119
 
40
120
    def __init__(self, url, _client=None):
41
121
        Transport.__init__(self, url)
42
 
        (scheme, _, loc, _, _) = urlparse.urlsplit(url)
43
 
        assert scheme == "git"
44
 
        hostport, self._path = urllib.splithost(loc)
45
 
        (self._host, self._port) = urllib.splitnport(hostport, git.protocol.TCP_GIT_PORT)
46
 
        if _client is not None:
47
 
            self._client = _client
48
 
        else:
49
 
            self._client = git.client.TCPGitClient(self._host, self._port)
 
122
        (self._host, self._port, self._username, self._path) = \
 
123
            split_git_url(url)
 
124
        if 'transport' in debug.debug_flags:
 
125
            trace.mutter('host: %r, user: %r, port: %r, path: %r',
 
126
                         self._host, self._username, self._port, self._path)
 
127
        self._client = _client
 
128
 
 
129
    def external_url(self):
 
130
        return self.base
 
131
 
 
132
    def has(self, relpath):
 
133
        return False
 
134
 
 
135
    def _get_client(self, thin_packs):
 
136
        raise NotImplementedError(self._get_client)
 
137
 
 
138
    def _get_path(self):
 
139
        return self._path
50
140
 
51
141
    def fetch_pack(self, determine_wants, graph_walker, pack_data, progress=None):
52
142
        if progress is None:
53
143
            def progress(text):
54
 
                info("git: %s" % text)
55
 
        self._client.fetch_pack(self._path, determine_wants, graph_walker, 
56
 
                pack_data, progress)
 
144
                trace.info("git: %s" % text)
 
145
        client = self._get_client(thin_packs=False)
 
146
        try:
 
147
            return client.fetch_pack(self._get_path(), determine_wants,
 
148
                graph_walker, pack_data, progress)
 
149
        except GitProtocolError, e:
 
150
            raise parse_git_error(self.external_url(), e)
57
151
 
58
 
    def fetch_objects(self, determine_wants, graph_walker, progress=None):
59
 
        fd, path = tempfile.mkstemp(dir=self.pack_dir(), suffix=".pack")
60
 
        self.fetch_pack(determine_wants, graph_walker, lambda x: os.write(fd, x), progress)
61
 
        os.close(fd)
 
152
    def send_pack(self, get_changed_refs, generate_pack_contents):
 
153
        client = self._get_client(thin_packs=False)
62
154
        try:
63
 
            p = PackData(path)
64
 
            for o in p.iterobjects():
65
 
                yield o
66
 
        finally:
67
 
            os.remove(path)
 
155
            return client.send_pack(self._get_path(), get_changed_refs,
 
156
                generate_pack_contents)
 
157
        except GitProtocolError, e:
 
158
            raise parse_git_error(self.external_url(), e)
68
159
 
69
160
    def get(self, path):
70
161
        raise NoSuchFile(path)
71
162
 
 
163
    def abspath(self, relpath):
 
164
        return urlutils.join(self.base, relpath)
 
165
 
72
166
    def clone(self, offset=None):
73
167
        """See Transport.clone()."""
74
168
        if offset is None:
76
170
        else:
77
171
            newurl = urlutils.join(self.base, offset)
78
172
 
79
 
        return GitSmartTransport(newurl, self._client)
 
173
        return self.__class__(newurl, self._client)
 
174
 
 
175
 
 
176
class TCPGitSmartTransport(GitSmartTransport):
 
177
 
 
178
    _scheme = 'git'
 
179
 
 
180
    def _get_client(self, thin_packs):
 
181
        if self._client is not None:
 
182
            ret = self._client
 
183
            self._client = None
 
184
            return ret
 
185
        return git.client.TCPGitClient(self._host, self._port,
 
186
            thin_packs=thin_packs, report_activity=self._report_activity)
 
187
 
 
188
 
 
189
class SSHGitSmartTransport(GitSmartTransport):
 
190
 
 
191
    _scheme = 'git+ssh'
 
192
 
 
193
    def _get_path(self):
 
194
        if self._path.startswith("/~/"):
 
195
            return self._path[3:]
 
196
        return self._path
 
197
 
 
198
    def _get_client(self, thin_packs):
 
199
        if self._client is not None:
 
200
            ret = self._client
 
201
            self._client = None
 
202
            return ret
 
203
        location_config = config.LocationConfig(self.base)
 
204
        client = git.client.SSHGitClient(self._host, self._port, self._username,
 
205
            thin_packs=thin_packs, report_activity=self._report_activity)
 
206
        # Set up alternate pack program paths
 
207
        upload_pack = location_config.get_user_option('git_upload_pack')
 
208
        if upload_pack:
 
209
            client.alternative_paths["upload-pack"] = upload_pack
 
210
        receive_pack = location_config.get_user_option('git_receive_pack')
 
211
        if receive_pack:
 
212
            client.alternative_paths["receive-pack"] = receive_pack
 
213
        return client
80
214
 
81
215
 
82
216
class RemoteGitDir(GitDir):
86
220
        self.root_transport = transport
87
221
        self.transport = transport
88
222
        self._lockfiles = lockfiles
 
223
        self._mode_check_done = None
 
224
 
 
225
    @property
 
226
    def user_url(self):
 
227
        return self.control_url
 
228
 
 
229
    def _branch_name_to_ref(self, name, default=None):
 
230
        return branch_name_to_ref(name, default=default)
89
231
 
90
232
    def open_repository(self):
91
233
        return RemoteGitRepository(self, self._lockfiles)
92
234
 
93
 
    def open_branch(self):
 
235
    def open_branch(self, name=None, unsupported=False, ignore_fallbacks=False):
94
236
        repo = self.open_repository()
95
 
        # TODO: Support for multiple branches in one bzrdir in bzrlib!
96
 
        return RemoteGitBranch(self, repo, "HEAD", self._lockfiles)
 
237
        refname = self._branch_name_to_ref(name)
 
238
        return RemoteGitBranch(self, repo, refname, self._lockfiles)
97
239
 
98
 
    def open_workingtree(self):
 
240
    def open_workingtree(self, recommend_upgrade=False):
99
241
        raise NotLocalUrl(self.transport.base)
100
242
 
101
243
 
 
244
class EmptyObjectStoreIterator(dict):
 
245
 
 
246
    def iterobjects(self):
 
247
        return []
 
248
 
 
249
 
 
250
class TemporaryPackIterator(Pack):
 
251
 
 
252
    def __init__(self, path, resolve_ext_ref):
 
253
        super(TemporaryPackIterator, self).__init__(path)
 
254
        self.resolve_ext_ref = resolve_ext_ref
 
255
 
 
256
    @property
 
257
    def data(self):
 
258
        if self._data is None:
 
259
            self._data = PackData(self._data_path)
 
260
        return self._data
 
261
 
 
262
    @property
 
263
    def index(self):
 
264
        if self._idx is None:
 
265
            if not os.path.exists(self._idx_path):
 
266
                pb = ui.ui_factory.nested_progress_bar()
 
267
                try:
 
268
                    def report_progress(cur, total):
 
269
                        pb.update("generating index", cur, total)
 
270
                    self.data.create_index(self._idx_path, 
 
271
                        progress=report_progress)
 
272
                finally:
 
273
                    pb.finished()
 
274
            self._idx = load_pack_index(self._idx_path)
 
275
        return self._idx
 
276
 
 
277
    def __del__(self):
 
278
        if self._idx is not None:
 
279
            self._idx.close()
 
280
            os.remove(self._idx_path)
 
281
        if self._data is not None:
 
282
            self._data.close()
 
283
            os.remove(self._data_path)
 
284
 
 
285
 
 
286
class RemoteGitControlDirFormat(GitControlDirFormat):
 
287
    """The .git directory control format."""
 
288
 
 
289
    supports_workingtrees = False
 
290
 
 
291
    @classmethod
 
292
    def _known_formats(self):
 
293
        return set([RemoteGitControlDirFormat()])
 
294
 
 
295
    def open(self, transport, _found=None):
 
296
        """Open this directory.
 
297
 
 
298
        """
 
299
        # we dont grok readonly - git isn't integrated with transport.
 
300
        url = transport.base
 
301
        if url.startswith('readonly+'):
 
302
            url = url[len('readonly+'):]
 
303
        if (not url.startswith("git://") and not url.startswith("git+")):
 
304
            raise NotBranchError(transport.base)
 
305
        if not isinstance(transport, GitSmartTransport):
 
306
            raise NotBranchError(transport.base)
 
307
        lockfiles = GitLockableFiles(transport, GitLock())
 
308
        return RemoteGitDir(transport, lockfiles, self)
 
309
 
 
310
    def get_format_description(self):
 
311
        return "Remote Git Repository"
 
312
 
 
313
    def initialize_on_transport(self, transport):
 
314
        raise UninitializableFormat(self)
 
315
 
 
316
 
102
317
class RemoteGitRepository(GitRepository):
103
318
 
104
319
    def __init__(self, gitdir, lockfiles):
105
320
        GitRepository.__init__(self, gitdir, lockfiles)
106
 
 
107
 
    def fetch_pack(self, determine_wants, graph_walker, pack_data, 
 
321
        self._refs = None
 
322
 
 
323
    @property
 
324
    def user_url(self):
 
325
        return self.control_url
 
326
 
 
327
    def get_parent_map(self, revids):
 
328
        raise GitSmartRemoteNotSupported()
 
329
 
 
330
    def get_refs(self):
 
331
        if self._refs is not None:
 
332
            return self._refs
 
333
        self._refs = self.bzrdir.root_transport.fetch_pack(lambda x: [], None,
 
334
            lambda x: None, lambda x: trace.mutter("git: %s" % x))
 
335
        return self._refs
 
336
 
 
337
    def fetch_pack(self, determine_wants, graph_walker, pack_data,
108
338
                   progress=None):
109
 
        self._transport.fetch_pack(determine_wants, graph_walker, pack_data, 
110
 
            progress)
 
339
        return self._transport.fetch_pack(determine_wants, graph_walker,
 
340
                                          pack_data, progress)
 
341
 
 
342
    def send_pack(self, get_changed_refs, generate_pack_contents):
 
343
        return self._transport.send_pack(get_changed_refs, generate_pack_contents)
 
344
 
 
345
    def fetch_objects(self, determine_wants, graph_walker, resolve_ext_ref,
 
346
                      progress=None):
 
347
        fd, path = tempfile.mkstemp(suffix=".pack")
 
348
        try:
 
349
            self.fetch_pack(determine_wants, graph_walker,
 
350
                lambda x: os.write(fd, x), progress)
 
351
        finally:
 
352
            os.close(fd)
 
353
        if os.path.getsize(path) == 0:
 
354
            return EmptyObjectStoreIterator()
 
355
        return TemporaryPackIterator(path[:-len(".pack")], resolve_ext_ref)
 
356
 
 
357
    def lookup_bzr_revision_id(self, bzr_revid):
 
358
        # This won't work for any round-tripped bzr revisions, but it's a start..
 
359
        try:
 
360
            return mapping_registry.revision_id_bzr_to_foreign(bzr_revid)
 
361
        except InvalidRevisionId:
 
362
            raise NoSuchRevision(self, bzr_revid)
 
363
 
 
364
    def lookup_foreign_revision_id(self, foreign_revid, mapping=None):
 
365
        """Lookup a revision id.
 
366
 
 
367
        """
 
368
        if mapping is None:
 
369
            mapping = self.get_mapping()
 
370
        # Not really an easy way to parse foreign revids here..
 
371
        return mapping.revision_id_foreign_to_bzr(foreign_revid)
 
372
 
 
373
 
 
374
class RemoteGitTagDict(GitTags):
 
375
 
 
376
    def get_refs(self):
 
377
        return self.repository.get_refs()
 
378
 
 
379
    def _iter_tag_refs(self, refs):
 
380
        for k, (peeled, unpeeled) in extract_tags(refs).iteritems():
 
381
            yield (k, peeled, unpeeled,
 
382
                  self.branch.mapping.revision_id_foreign_to_bzr(peeled))
 
383
 
 
384
    def set_tag(self, name, revid):
 
385
        # FIXME: Not supported yet, should do a push of a new ref
 
386
        raise NotImplementedError(self.set_tag)
111
387
 
112
388
 
113
389
class RemoteGitBranch(GitBranch):
114
390
 
115
391
    def __init__(self, bzrdir, repository, name, lockfiles):
116
 
        def determine_wants(heads):
117
 
            self._ref = heads[name]
118
 
        bzrdir.root_transport.fetch_pack(determine_wants, None, lambda x: None, 
119
 
                             lambda x: mutter("git: %s" % x))
120
 
        super(RemoteGitBranch, self).__init__(bzrdir, repository, name, self._ref, lockfiles)
 
392
        self._sha = None
 
393
        super(RemoteGitBranch, self).__init__(bzrdir, repository, name,
 
394
                lockfiles)
 
395
 
 
396
    @property
 
397
    def user_url(self):
 
398
        return self.control_url
 
399
 
 
400
    @property
 
401
    def control_url(self):
 
402
        return self.base
 
403
 
 
404
    def revision_history(self):
 
405
        raise GitSmartRemoteNotSupported()
121
406
 
122
407
    def last_revision(self):
123
 
        return self.mapping.revision_id_foreign_to_bzr(self._ref)
124
 
 
 
408
        return self.lookup_foreign_revision_id(self.head)
 
409
 
 
410
    def _get_config(self):
 
411
        class EmptyConfig(object):
 
412
 
 
413
            def _get_configobj(self):
 
414
                return config.ConfigObj()
 
415
 
 
416
        return EmptyConfig()
 
417
 
 
418
    @property
 
419
    def head(self):
 
420
        if self._sha is not None:
 
421
            return self._sha
 
422
        heads = self.repository.get_refs()
 
423
        name = self.bzrdir._branch_name_to_ref(self.name, "HEAD")
 
424
        if name in heads:
 
425
            self._sha = heads[name]
 
426
        else:
 
427
            raise NoSuchRef(self.name)
 
428
        return self._sha
 
429
 
 
430
    def _synchronize_history(self, destination, revision_id):
 
431
        """See Branch._synchronize_history()."""
 
432
        destination.generate_revision_history(self.last_revision())
 
433
 
 
434
    def get_push_location(self):
 
435
        return None
 
436
 
 
437
    def set_push_location(self, url):
 
438
        pass