13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
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
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
35
from dulwich.pack import PackData
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
"""Remote dirs, repositories and branches."""
19
from __future__ import absolute_import
36
from ..errors import (
48
UninitializableFormat,
50
from ..revisiontree import RevisionTree
51
from ..sixish import text_type
52
from ..transport import (
54
register_urlparse_netloc_protocol,
59
user_agent_for_github,
75
GitSmartRemoteNotSupported,
78
from .mapping import (
81
from .object_store import (
87
from .repository import (
99
from dulwich.errors import (
103
from dulwich.pack import (
105
pack_objects_to_data,
107
from dulwich.protocol import ZERO_SHA
108
from dulwich.refs import (
112
from dulwich.repo import (
120
import urllib.parse as urlparse
121
from urllib.parse import splituser, splitnport
124
from urllib import splituser, splitnport
126
# urlparse only supports a limited number of schemes by default
127
register_urlparse_netloc_protocol('git')
128
register_urlparse_netloc_protocol('git+ssh')
130
from dulwich.pack import load_pack_index
133
class GitPushResult(PushResult):
135
def _lookup_revno(self, revid):
137
return _quick_lookup_revno(self.source_branch, self.target_branch,
139
except GitSmartRemoteNotSupported:
144
return self._lookup_revno(self.old_revid)
148
return self._lookup_revno(self.new_revid)
151
# Don't run any tests on GitSmartTransport as it is not intended to be
152
# a full implementation of Transport
153
def get_test_permutations():
157
def split_git_url(url):
161
:return: Tuple with host, port, username, path.
163
(scheme, netloc, loc, _, _) = urlparse.urlsplit(url)
164
path = urlparse.unquote(loc)
165
if path.startswith("/~"):
167
(username, hostport) = splituser(netloc)
168
(host, port) = splitnport(hostport, None)
169
return (host, port, username, path)
172
class RemoteGitError(BzrError):
174
_fmt = "Remote server error: %(msg)s"
177
class HeadUpdateFailed(BzrError):
179
_fmt = ("Unable to update remote HEAD branch. To update the master "
180
"branch, specify the URL %(base_url)s,branch=master.")
182
def __init__(self, base_url):
183
super(HeadUpdateFailed, self).__init__()
184
self.base_url = base_url
187
def parse_git_error(url, message):
188
"""Parse a remote git server error and return a bzr exception.
190
:param url: URL of the remote repository
191
:param message: Message sent by the remote git server
193
message = str(message).strip()
194
if (message.startswith("Could not find Repository ")
195
or message == 'Repository not found.'
196
or (message.startswith('Repository ') and
197
message.endswith(' not found.'))):
198
return NotBranchError(url, message)
199
if message == "HEAD failed to update":
200
base_url, _ = urlutils.split_segment_parameters(url)
201
return HeadUpdateFailed(base_url)
202
if message.startswith('access denied or repository not exported:'):
203
extra, path = message.split(': ', 1)
204
return PermissionDenied(path, extra)
205
if message.endswith('You are not allowed to push code to this project.'):
206
return PermissionDenied(url, message)
207
if message.endswith(' does not appear to be a git repository'):
208
return NotBranchError(url, message)
209
m = re.match(r'Permission to ([^ ]+) denied to ([^ ]+)\.', message)
211
return PermissionDenied(m.group(1), 'denied to %s' % m.group(2))
212
# Don't know, just return it to the user as-is
213
return RemoteGitError(message)
38
216
class GitSmartTransport(Transport):
40
218
def __init__(self, url, _client=None):
41
219
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
49
self._client = git.client.TCPGitClient(self._host, self._port)
51
def fetch_pack(self, determine_wants, graph_walker, pack_data, progress=None):
54
info("git: %s" % text)
55
self._client.fetch_pack(self._path, determine_wants, graph_walker,
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)
64
for o in p.iterobjects():
220
(self._host, self._port, self._username, self._path) = \
222
if 'transport' in debug.debug_flags:
223
trace.mutter('host: %r, user: %r, port: %r, path: %r',
224
self._host, self._username, self._port, self._path)
225
self._client = _client
226
self._stripped_path = self._path.rsplit(",", 1)[0]
228
def external_url(self):
231
def has(self, relpath):
234
def _get_client(self):
235
raise NotImplementedError(self._get_client)
238
return self._stripped_path
69
240
def get(self, path):
70
241
raise NoSuchFile(path)
243
def abspath(self, relpath):
244
return urlutils.join(self.base, relpath)
72
246
def clone(self, offset=None):
73
247
"""See Transport.clone()."""
74
248
if offset is None:
77
251
newurl = urlutils.join(self.base, offset)
79
return GitSmartTransport(newurl, self._client)
253
return self.__class__(newurl, self._client)
256
class TCPGitSmartTransport(GitSmartTransport):
260
def _get_client(self):
261
if self._client is not None:
266
# return dulwich.client.LocalGitClient()
267
return dulwich.client.SubprocessGitClient()
268
return dulwich.client.TCPGitClient(
269
self._host, self._port, report_activity=self._report_activity)
272
class SSHSocketWrapper(object):
274
def __init__(self, sock):
277
def read(self, len=None):
278
return self.sock.recv(len)
280
def write(self, data):
281
return self.sock.write(data)
284
return len(select.select([self.sock.fileno()], [], [], 0)[0]) > 0
287
class DulwichSSHVendor(dulwich.client.SSHVendor):
290
from ..transport import ssh
291
self.bzr_ssh_vendor = ssh._get_ssh_vendor()
293
def run_command(self, host, command, username=None, port=None):
294
connection = self.bzr_ssh_vendor.connect_ssh(
295
username=username, password=None, port=port, host=host,
297
(kind, io_object) = connection.get_sock_or_pipes()
299
return SSHSocketWrapper(io_object)
301
raise AssertionError("Unknown io object kind %r'" % kind)
304
# dulwich.client.get_ssh_vendor = DulwichSSHVendor
307
class SSHGitSmartTransport(GitSmartTransport):
312
path = self._stripped_path
313
if path.startswith("/~/"):
317
def _get_client(self):
318
if self._client is not None:
322
location_config = config.LocationConfig(self.base)
323
client = dulwich.client.SSHGitClient(
324
self._host, self._port, self._username,
325
report_activity=self._report_activity)
326
# Set up alternate pack program paths
327
upload_pack = location_config.get_user_option('git_upload_pack')
329
client.alternative_paths["upload-pack"] = upload_pack
330
receive_pack = location_config.get_user_option('git_receive_pack')
332
client.alternative_paths["receive-pack"] = receive_pack
336
class RemoteGitBranchFormat(GitBranchFormat):
338
def get_format_description(self):
339
return 'Remote Git Branch'
342
def _matchingcontroldir(self):
343
return RemoteGitControlDirFormat()
345
def initialize(self, a_controldir, name=None, repository=None,
346
append_revisions_only=None):
347
raise UninitializableFormat(self)
350
class DefaultProgressReporter(object):
352
_GIT_PROGRESS_PARTIAL_RE = re.compile(r"(.*?): +(\d+)% \((\d+)/(\d+)\)")
353
_GIT_PROGRESS_TOTAL_RE = re.compile(r"(.*?): (\d+)")
355
def __init__(self, pb):
358
def progress(self, text):
359
text = text.rstrip(b"\r\n")
360
text = text.decode('utf-8')
361
if text.lower().startswith('error: '):
362
trace.show_error('git: %s', text[len(b'error: '):])
364
trace.mutter("git: %s", text)
365
g = self._GIT_PROGRESS_PARTIAL_RE.match(text)
367
(text, pct, current, total) = g.groups()
368
self.pb.update(text, int(current), int(total))
370
g = self._GIT_PROGRESS_TOTAL_RE.match(text)
372
(text, total) = g.groups()
373
self.pb.update(text, None, int(total))
375
trace.note("%s", text)
82
378
class RemoteGitDir(GitDir):
84
def __init__(self, transport, lockfiles, format):
380
def __init__(self, transport, format, client, client_path):
85
381
self._format = format
86
382
self.root_transport = transport
87
383
self.transport = transport
88
self._lockfiles = lockfiles
384
self._mode_check_done = None
385
self._client = client
386
self._client_path = client_path
387
self.base = self.root_transport.base
391
def _gitrepository_class(self):
392
return RemoteGitRepository
394
def archive(self, format, committish, write_data, progress=None,
395
write_error=None, subdirs=None, prefix=None):
397
pb = ui.ui_factory.nested_progress_bar()
398
progress = DefaultProgressReporter(pb).progress
401
def progress_wrapper(message):
402
if message.startswith(b"fatal: Unknown archive format \'"):
403
format = message.strip()[len(b"fatal: Unknown archive format '"):-1]
404
raise errors.NoSuchExportFormat(format.decode('ascii'))
405
return progress(message)
407
self._client.archive(
408
self._client_path, committish, write_data, progress_wrapper,
410
format=(format.encode('ascii') if format else None),
412
prefix=(prefix.encode('utf-8') if prefix else None))
413
except GitProtocolError as e:
414
raise parse_git_error(self.transport.external_url(), e)
419
def fetch_pack(self, determine_wants, graph_walker, pack_data,
422
pb = ui.ui_factory.nested_progress_bar()
423
progress = DefaultProgressReporter(pb).progress
427
result = self._client.fetch_pack(
428
self._client_path, determine_wants, graph_walker, pack_data,
430
if result.refs is None:
432
self._refs = remote_refs_dict_to_container(
433
result.refs, result.symrefs)
435
except GitProtocolError as e:
436
raise parse_git_error(self.transport.external_url(), e)
441
def send_pack(self, get_changed_refs, generate_pack_data, progress=None):
443
pb = ui.ui_factory.nested_progress_bar()
444
progress = DefaultProgressReporter(pb).progress
448
def get_changed_refs_wrapper(refs):
449
# TODO(jelmer): This drops symref information
450
self._refs = remote_refs_dict_to_container(refs)
451
return get_changed_refs(refs)
453
return self._client.send_pack(
454
self._client_path, get_changed_refs_wrapper,
455
generate_pack_data, progress)
456
except GitProtocolError as e:
457
raise parse_git_error(self.transport.external_url(), e)
462
def create_branch(self, name=None, repository=None,
463
append_revisions_only=None, ref=None):
464
refname = self._get_selected_ref(name, ref)
465
if refname != b'HEAD' and refname in self.get_refs_container():
466
raise AlreadyBranchError(self.user_url)
467
if refname in self.get_refs_container():
468
ref_chain, unused_sha = self.get_refs_container().follow(
469
self._get_selected_ref(None))
470
if ref_chain[0] == b'HEAD':
471
refname = ref_chain[1]
472
repo = self.open_repository()
473
return RemoteGitBranch(self, repo, refname)
475
def destroy_branch(self, name=None):
476
refname = self._get_selected_ref(name)
478
def get_changed_refs(old_refs):
480
if refname not in ret:
481
raise NotBranchError(self.user_url)
482
ret[refname] = dulwich.client.ZERO_SHA
485
def generate_pack_data(have, want, ofs_delta=False):
486
return pack_objects_to_data([])
487
self.send_pack(get_changed_refs, generate_pack_data)
491
return self.control_url
494
def user_transport(self):
495
return self.root_transport
498
def control_url(self):
499
return self.control_transport.base
502
def control_transport(self):
503
return self.root_transport
90
505
def open_repository(self):
91
return RemoteGitRepository(self, self._lockfiles)
506
return RemoteGitRepository(self)
93
def open_branch(self):
508
def open_branch(self, name=None, unsupported=False,
509
ignore_fallbacks=False, ref=None, possible_transports=None,
94
511
repo = self.open_repository()
95
# TODO: Support for multiple branches in one bzrdir in bzrlib!
96
return RemoteGitBranch(self, repo, "HEAD", self._lockfiles)
512
ref = self._get_selected_ref(name, ref)
514
if not nascent_ok and ref not in self.get_refs_container():
515
raise NotBranchError(self.root_transport.base,
517
except NotGitRepository:
518
raise NotBranchError(self.root_transport.base,
520
ref_chain, unused_sha = self.get_refs_container().follow(ref)
521
return RemoteGitBranch(self, repo, ref_chain[-1])
98
def open_workingtree(self):
523
def open_workingtree(self, recommend_upgrade=False):
99
524
raise NotLocalUrl(self.transport.base)
526
def has_workingtree(self):
529
def get_peeled(self, name):
530
return self.get_refs_container().get_peeled(name)
532
def get_refs_container(self):
533
if self._refs is not None:
535
result = self.fetch_pack(lambda x: None, None,
537
lambda x: trace.mutter("git: %s" % x))
538
self._refs = remote_refs_dict_to_container(
539
result.refs, result.symrefs)
542
def push_branch(self, source, revision_id=None, overwrite=False,
543
remember=False, create_prefix=False, lossy=False,
545
"""Push the source branch into this ControlDir."""
546
if revision_id is None:
547
# No revision supplied by the user, default to the branch
549
revision_id = source.last_revision()
551
push_result = GitPushResult()
552
push_result.workingtree_updated = None
553
push_result.master_branch = None
554
push_result.source_branch = source
555
push_result.stacked_on = None
556
push_result.branch_push_result = None
557
repo = self.find_repository()
558
refname = self._get_selected_ref(name)
559
if isinstance(source, GitBranch) and lossy:
560
raise errors.LossyPushToSameVCS(source.controldir, self)
561
source_store = get_object_store(source.repository)
562
with source_store.lock_read():
563
def get_changed_refs(refs):
564
self._refs = remote_refs_dict_to_container(refs)
566
# TODO(jelmer): Unpeel if necessary
567
push_result.new_original_revid = revision_id
569
new_sha = source_store._lookup_revision_sha1(revision_id)
572
new_sha = repo.lookup_bzr_revision_id(revision_id)[0]
573
except errors.NoSuchRevision:
574
raise errors.NoRoundtrippingSupport(
575
source, self.open_branch(name=name, nascent_ok=True))
577
if remote_divergence(ret.get(refname), new_sha,
579
raise DivergedBranches(
580
source, self.open_branch(name, nascent_ok=True))
581
ret[refname] = new_sha
584
generate_pack_data = source_store.generate_lossy_pack_data
586
generate_pack_data = source_store.generate_pack_data
587
new_refs = self.send_pack(get_changed_refs, generate_pack_data)
588
push_result.new_revid = repo.lookup_foreign_revision_id(
591
old_remote = self._refs[refname]
593
old_remote = ZERO_SHA
594
push_result.old_revid = repo.lookup_foreign_revision_id(old_remote)
595
self._refs = remote_refs_dict_to_container(new_refs)
596
push_result.target_branch = self.open_branch(name)
597
if old_remote != ZERO_SHA:
598
push_result.branch_push_result = GitBranchPushResult()
599
push_result.branch_push_result.source_branch = source
600
push_result.branch_push_result.target_branch = (
601
push_result.target_branch)
602
push_result.branch_push_result.local_branch = None
603
push_result.branch_push_result.master_branch = (
604
push_result.target_branch)
605
push_result.branch_push_result.old_revid = push_result.old_revid
606
push_result.branch_push_result.new_revid = push_result.new_revid
607
push_result.branch_push_result.new_original_revid = (
608
push_result.new_original_revid)
609
if source.get_push_location() is None or remember:
610
source.set_push_location(push_result.target_branch.base)
613
def _find_commondir(self):
614
# There is no way to find the commondir, if there is any.
618
class EmptyObjectStoreIterator(dict):
620
def iterobjects(self):
624
class TemporaryPackIterator(Pack):
626
def __init__(self, path, resolve_ext_ref):
627
super(TemporaryPackIterator, self).__init__(
628
path, resolve_ext_ref=resolve_ext_ref)
629
self._idx_load = lambda: self._idx_load_or_generate(self._idx_path)
631
def _idx_load_or_generate(self, path):
632
if not os.path.exists(path):
633
pb = ui.ui_factory.nested_progress_bar()
635
def report_progress(cur, total):
636
pb.update("generating index", cur, total)
637
self.data.create_index(path,
638
progress=report_progress)
641
return load_pack_index(path)
644
if self._idx is not None:
646
os.remove(self._idx_path)
647
if self._data is not None:
649
os.remove(self._data_path)
652
class BzrGitHttpClient(dulwich.client.HttpGitClient):
654
def __init__(self, transport, *args, **kwargs):
655
self.transport = transport
656
super(BzrGitHttpClient, self).__init__(
657
transport.external_url(), *args, **kwargs)
659
def _http_request(self, url, headers=None, data=None,
660
allow_compression=False):
661
"""Perform HTTP request.
663
:param url: Request URL.
664
:param headers: Optional custom headers to override defaults.
665
:param data: Request data.
666
:param allow_compression: Allow GZipped communication.
667
:return: Tuple (`response`, `read`), where response is an `urllib3`
668
response object with additional `content_type` and
669
`redirect_location` properties, and `read` is a consumable read
670
method for the response data.
672
from breezy.transport.http._urllib2_wrappers import Request
673
headers['User-agent'] = user_agent_for_github()
674
headers["Pragma"] = "no-cache"
675
if allow_compression:
676
headers["Accept-Encoding"] = "gzip"
678
headers["Accept-Encoding"] = "identity"
681
('GET' if data is None else 'POST'),
683
accepted_errors=[200, 404])
684
request.follow_redirections = True
686
response = self.transport._perform(request)
688
if response.code == 404:
689
raise NotGitRepository()
690
elif response.code != 200:
691
raise GitProtocolError("unexpected http resp %d for %s" %
692
(response.code, url))
694
# TODO: Optimization available by adding `preload_content=False` to the
695
# request and just passing the `read` method on instead of going via
696
# `BytesIO`, if we can guarantee that the entire response is consumed
697
# before issuing the next to still allow for connection reuse from the
699
if response.getheader("Content-Encoding") == "gzip":
700
read = gzip.GzipFile(fileobj=response).read
704
class WrapResponse(object):
706
def __init__(self, response):
707
self._response = response
708
self.status = response.code
709
self.content_type = response.getheader("Content-Type")
710
self.redirect_location = response.geturl()
713
return self._response.readlines()
716
self._response.close()
718
return WrapResponse(response), read
721
class RemoteGitControlDirFormat(GitControlDirFormat):
722
"""The .git directory control format."""
724
supports_workingtrees = False
727
def _known_formats(self):
728
return set([RemoteGitControlDirFormat()])
730
def get_branch_format(self):
731
return RemoteGitBranchFormat()
733
def is_initializable(self):
736
def is_supported(self):
739
def open(self, transport, _found=None):
740
"""Open this directory.
743
# we dont grok readonly - git isn't integrated with transport.
745
if url.startswith('readonly+'):
746
url = url[len('readonly+'):]
747
scheme = urlparse.urlsplit(transport.external_url())[0]
748
if isinstance(transport, GitSmartTransport):
749
client = transport._get_client()
750
client_path = transport._get_path()
751
elif scheme in ("http", "https"):
752
client = BzrGitHttpClient(transport)
753
client_path, _ = urlutils.split_segment_parameters(transport._path)
754
elif scheme == 'file':
755
client = dulwich.client.LocalGitClient()
756
client_path = transport.local_abspath('.')
758
raise NotBranchError(transport.base)
760
pass # TODO(jelmer): Actually probe for something
761
return RemoteGitDir(transport, self, client, client_path)
763
def get_format_description(self):
764
return "Remote Git Repository"
766
def initialize_on_transport(self, transport):
767
raise UninitializableFormat(self)
769
def supports_transport(self, transport):
771
external_url = transport.external_url()
772
except InProcessTransport:
773
raise NotBranchError(path=transport.base)
774
return (external_url.startswith("http:")
775
or external_url.startswith("https:")
776
or external_url.startswith("git+")
777
or external_url.startswith("git:"))
780
class GitRemoteRevisionTree(RevisionTree):
782
def archive(self, format, name, root=None, subdir=None, force_mtime=None):
783
"""Create an archive of this tree.
785
:param format: Format name (e.g. 'tar')
786
:param name: target file name
787
:param root: Root directory name (or None)
788
:param subdir: Subdirectory to export (or None)
789
:return: Iterator over archive chunks
791
commit = self._repository.lookup_bzr_revision_id(
792
self.get_revision_id())[0]
793
f = tempfile.SpooledTemporaryFile()
794
# git-upload-archive(1) generaly only supports refs. So let's see if we
798
self._repository.controldir.get_refs_container().as_dict().items()}
800
committish = reverse_refs[commit]
802
# No? Maybe the user has uploadArchive.allowUnreachable enabled.
803
# Let's hope for the best.
805
self._repository.archive(
806
format, committish, f.write,
807
subdirs=([subdir] if subdir else None),
808
prefix=(root + '/') if root else '')
810
return osutils.file_iterator(f)
812
def is_versioned(self, path, file_id=None):
813
raise GitSmartRemoteNotSupported(self.is_versioned, self)
815
def has_filename(self, path):
816
raise GitSmartRemoteNotSupported(self.has_filename, self)
818
def get_file_text(self, path, file_id=None):
819
raise GitSmartRemoteNotSupported(self.get_file_text, self)
102
822
class RemoteGitRepository(GitRepository):
104
def __init__(self, gitdir, lockfiles):
105
GitRepository.__init__(self, gitdir, lockfiles)
107
def fetch_pack(self, determine_wants, graph_walker, pack_data,
826
return self.control_url
828
def get_parent_map(self, revids):
829
raise GitSmartRemoteNotSupported(self.get_parent_map, self)
831
def archive(self, *args, **kwargs):
832
return self.controldir.archive(*args, **kwargs)
834
def fetch_pack(self, determine_wants, graph_walker, pack_data,
109
self._transport.fetch_pack(determine_wants, graph_walker, pack_data,
836
return self.controldir.fetch_pack(
837
determine_wants, graph_walker, pack_data, progress)
839
def send_pack(self, get_changed_refs, generate_pack_data):
840
return self.controldir.send_pack(get_changed_refs, generate_pack_data)
842
def fetch_objects(self, determine_wants, graph_walker, resolve_ext_ref,
844
fd, path = tempfile.mkstemp(suffix=".pack")
846
self.fetch_pack(determine_wants, graph_walker,
847
lambda x: os.write(fd, x), progress)
850
if os.path.getsize(path) == 0:
851
return EmptyObjectStoreIterator()
852
return TemporaryPackIterator(path[:-len(".pack")], resolve_ext_ref)
854
def lookup_bzr_revision_id(self, bzr_revid, mapping=None):
855
# This won't work for any round-tripped bzr revisions, but it's a
858
return mapping_registry.revision_id_bzr_to_foreign(bzr_revid)
859
except InvalidRevisionId:
860
raise NoSuchRevision(self, bzr_revid)
862
def lookup_foreign_revision_id(self, foreign_revid, mapping=None):
863
"""Lookup a revision id.
867
mapping = self.get_mapping()
868
# Not really an easy way to parse foreign revids here..
869
return mapping.revision_id_foreign_to_bzr(foreign_revid)
871
def revision_tree(self, revid):
872
return GitRemoteRevisionTree(self, revid)
874
def get_revisions(self, revids):
875
raise GitSmartRemoteNotSupported(self.get_revisions, self)
877
def has_revisions(self, revids):
878
raise GitSmartRemoteNotSupported(self.get_revisions, self)
881
class RemoteGitTagDict(GitTags):
883
def set_tag(self, name, revid):
884
sha = self.branch.lookup_bzr_revision_id(revid)[0]
885
self._set_ref(name, sha)
887
def delete_tag(self, name):
888
self._set_ref(name, dulwich.client.ZERO_SHA)
890
def _set_ref(self, name, sha):
891
ref = tag_name_to_ref(name)
893
def get_changed_refs(old_refs):
895
if sha == dulwich.client.ZERO_SHA and ref not in ret:
896
raise NoSuchTag(name)
900
def generate_pack_data(have, want, ofs_delta=False):
901
return pack_objects_to_data([])
902
self.repository.send_pack(get_changed_refs, generate_pack_data)
113
905
class RemoteGitBranch(GitBranch):
115
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)
907
def __init__(self, controldir, repository, name):
909
super(RemoteGitBranch, self).__init__(controldir, repository, name,
910
RemoteGitBranchFormat())
912
def last_revision_info(self):
913
raise GitSmartRemoteNotSupported(self.last_revision_info, self)
917
return self.control_url
920
def control_url(self):
923
def revision_id_to_revno(self, revision_id):
924
raise GitSmartRemoteNotSupported(self.revision_id_to_revno, self)
122
926
def last_revision(self):
123
return self.mapping.revision_id_foreign_to_bzr(self._ref)
927
return self.lookup_foreign_revision_id(self.head)
931
if self._sha is not None:
933
refs = self.controldir.get_refs_container()
934
name = branch_name_to_ref(self.name)
936
self._sha = refs[name]
938
raise NoSuchRef(name, self.repository.user_url, refs)
941
def _synchronize_history(self, destination, revision_id):
942
"""See Branch._synchronize_history()."""
943
if revision_id is None:
944
revision_id = self.last_revision()
945
destination.generate_revision_history(revision_id)
947
def _get_parent_location(self):
950
def get_push_location(self):
953
def set_push_location(self, url):
956
def _iter_tag_refs(self):
957
"""Iterate over the tag refs.
959
:param refs: Refs dictionary (name -> git sha1)
960
:return: iterator over (ref_name, tag_name, peeled_sha1, unpeeled_sha1)
962
refs = self.controldir.get_refs_container()
963
for ref_name, unpeeled in refs.as_dict().items():
965
tag_name = ref_to_tag_name(ref_name)
966
except (ValueError, UnicodeDecodeError):
968
peeled = refs.get_peeled(ref_name)
970
# Let's just hope it's a commit
972
if not isinstance(tag_name, text_type):
973
raise TypeError(tag_name)
974
yield (ref_name, tag_name, peeled, unpeeled)
976
def set_last_revision_info(self, revno, revid):
977
self.generate_revision_history(revid)
979
def generate_revision_history(self, revision_id, last_rev=None,
981
sha = self.lookup_bzr_revision_id(revision_id)[0]
982
def get_changed_refs(old_refs):
983
return {self.ref: sha}
984
def generate_pack_data(have, want, ofs_delta=False):
985
return pack_objects_to_data([])
986
self.repository.send_pack(get_changed_refs, generate_pack_data)
990
def remote_refs_dict_to_container(refs_dict, symrefs_dict={}):
993
for k, v in refs_dict.items():
998
for name, target in symrefs_dict.items():
999
base[name] = SYMREF + target
1000
ret = DictRefsContainer(base)
1001
ret._peeled = peeled