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 lazy_check_versions
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
21
from io import BytesIO
36
from ..errors import (
49
UninitializableFormat,
51
from ..revisiontree import RevisionTree
52
from ..sixish import text_type
53
from ..transport import (
55
register_urlparse_netloc_protocol,
60
user_agent_for_github,
27
62
lazy_check_versions()
29
from bzrlib.plugins.git.branch import GitBranch
30
from bzrlib.plugins.git.errors import NoSuchRef
31
from bzrlib.plugins.git.dir import GitDir
32
from bzrlib.plugins.git.foreign import ForeignBranch
33
from bzrlib.plugins.git.repository import GitFormat, GitRepository
74
BareLocalGitControlDirFormat,
77
GitSmartRemoteNotSupported,
80
from .mapping import (
83
from .object_store import (
89
from .repository import (
100
import dulwich.client
101
from dulwich.errors import (
104
from dulwich.pack import (
106
pack_objects_to_data,
108
from dulwich.protocol import ZERO_SHA
109
from dulwich.refs import (
113
from dulwich.repo import (
41
from dulwich.pack import PackData, Pack, PackIndex
43
# Don't run any tests on GitSmartTransport as it is not intended to be
122
import urllib.parse as urlparse
123
from urllib.parse import splituser, splitnport
126
from urllib import splituser, splitnport
128
# urlparse only supports a limited number of schemes by default
129
register_urlparse_netloc_protocol('git')
130
register_urlparse_netloc_protocol('git+ssh')
132
from dulwich.pack import load_pack_index
135
class GitPushResult(PushResult):
137
def _lookup_revno(self, revid):
139
return _quick_lookup_revno(self.source_branch, self.target_branch,
141
except GitSmartRemoteNotSupported:
146
return self._lookup_revno(self.old_revid)
150
return self._lookup_revno(self.new_revid)
153
# Don't run any tests on GitSmartTransport as it is not intended to be
44
154
# a full implementation of Transport
45
155
def get_test_permutations():
159
def split_git_url(url):
163
:return: Tuple with host, port, username, path.
165
(scheme, netloc, loc, _, _) = urlparse.urlsplit(url)
166
path = urlparse.unquote(loc)
167
if path.startswith("/~"):
169
(username, hostport) = splituser(netloc)
170
(host, port) = splitnport(hostport, None)
171
return (host, port, username, path)
174
class RemoteGitError(BzrError):
176
_fmt = "Remote server error: %(msg)s"
179
class HeadUpdateFailed(BzrError):
181
_fmt = ("Unable to update remote HEAD branch. To update the master "
182
"branch, specify the URL %(base_url)s,branch=master.")
184
def __init__(self, base_url):
185
super(HeadUpdateFailed, self).__init__()
186
self.base_url = base_url
189
def parse_git_error(url, message):
190
"""Parse a remote git server error and return a bzr exception.
192
:param url: URL of the remote repository
193
:param message: Message sent by the remote git server
195
message = str(message).strip()
196
if (message.startswith("Could not find Repository ") or
197
message == 'Repository not found.' or
198
(message.startswith('Repository ') and message.endswith(' not found.'))):
199
return NotBranchError(url, message)
200
if message == "HEAD failed to update":
201
base_url, _ = urlutils.split_segment_parameters(url)
202
return HeadUpdateFailed(base_url)
203
if message.startswith('access denied or repository not exported:'):
204
extra, path = message.split(': ', 1)
205
return PermissionDenied(path, extra)
206
if message.endswith('You are not allowed to push code to this project.'):
207
return PermissionDenied(url, message)
208
if message.endswith(' does not appear to be a git repository'):
209
return NotBranchError(url, message)
210
m = re.match(r'Permission to ([^ ]+) denied to ([^ ]+)\.', message)
212
return PermissionDenied(m.group(1), 'denied to %s' % m.group(2))
213
# Don't know, just return it to the user as-is
214
return RemoteGitError(message)
49
217
class GitSmartTransport(Transport):
51
219
def __init__(self, url, _client=None):
52
220
Transport.__init__(self, url)
53
(scheme, _, loc, _, _) = urlparse.urlsplit(url)
54
assert scheme == "git"
55
hostport, self._path = urllib.splithost(loc)
56
(self._host, self._port) = urllib.splitnport(hostport, git.protocol.TCP_GIT_PORT)
221
(self._host, self._port, self._username, self._path) = \
223
if 'transport' in debug.debug_flags:
224
trace.mutter('host: %r, user: %r, port: %r, path: %r',
225
self._host, self._username, self._port, self._path)
57
226
self._client = _client
227
self._stripped_path = self._path.rsplit(",", 1)[0]
229
def external_url(self):
232
def has(self, relpath):
59
235
def _get_client(self):
60
if self._client is not None:
64
return git.client.TCPGitClient(self._host, self._port,
65
capabilities=["multi_ack", "side-band-64k", "ofs-delta", "side-band"])
236
raise NotImplementedError(self._get_client)
67
def fetch_pack(self, determine_wants, graph_walker, pack_data, progress=None):
70
info("git: %s" % text)
71
self._get_client().fetch_pack(self._path, determine_wants,
72
graph_walker, pack_data, progress)
239
return self._stripped_path
74
241
def get(self, path):
75
242
raise NoSuchFile(path)
85
252
newurl = urlutils.join(self.base, offset)
87
return GitSmartTransport(newurl, self._client)
254
return self.__class__(newurl, self._client)
257
class TCPGitSmartTransport(GitSmartTransport):
261
def _get_client(self):
262
if self._client is not None:
267
# return dulwich.client.LocalGitClient()
268
return dulwich.client.SubprocessGitClient()
269
return dulwich.client.TCPGitClient(self._host, self._port,
270
report_activity=self._report_activity)
273
class SSHSocketWrapper(object):
275
def __init__(self, sock):
278
def read(self, len=None):
279
return self.sock.recv(len)
281
def write(self, data):
282
return self.sock.write(data)
285
return len(select.select([self.sock.fileno()], [], [], 0)[0]) > 0
288
class DulwichSSHVendor(dulwich.client.SSHVendor):
291
from ..transport import ssh
292
self.bzr_ssh_vendor = ssh._get_ssh_vendor()
294
def run_command(self, host, command, username=None, port=None):
295
connection = self.bzr_ssh_vendor.connect_ssh(username=username,
296
password=None, port=port, host=host, command=command)
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(self._host, self._port, self._username,
324
report_activity=self._report_activity)
325
# Set up alternate pack program paths
326
upload_pack = location_config.get_user_option('git_upload_pack')
328
client.alternative_paths["upload-pack"] = upload_pack
329
receive_pack = location_config.get_user_option('git_receive_pack')
331
client.alternative_paths["receive-pack"] = receive_pack
335
class RemoteGitBranchFormat(GitBranchFormat):
337
def get_format_description(self):
338
return 'Remote Git Branch'
341
def _matchingcontroldir(self):
342
return RemoteGitControlDirFormat()
344
def initialize(self, a_controldir, name=None, repository=None,
345
append_revisions_only=None):
346
raise UninitializableFormat(self)
349
class DefaultProgressReporter(object):
351
_GIT_PROGRESS_PARTIAL_RE = re.compile(r"(.*?): +(\d+)% \((\d+)/(\d+)\)")
352
_GIT_PROGRESS_TOTAL_RE = re.compile(r"(.*?): (\d+)")
354
def __init__(self, pb):
357
def progress(self, text):
358
text = text.rstrip(b"\r\n")
359
text = text.decode('utf-8')
360
if text.lower().startswith('error: '):
361
trace.show_error('git: %s', text[len(b'error: '):])
363
trace.mutter("git: %s", text)
364
g = self._GIT_PROGRESS_PARTIAL_RE.match(text)
366
(text, pct, current, total) = g.groups()
367
self.pb.update(text, int(current), int(total))
369
g = self._GIT_PROGRESS_TOTAL_RE.match(text)
371
(text, total) = g.groups()
372
self.pb.update(text, None, int(total))
374
trace.note("%s", text)
90
377
class RemoteGitDir(GitDir):
92
def __init__(self, transport, lockfiles, format):
379
def __init__(self, transport, format, client, client_path):
93
380
self._format = format
94
381
self.root_transport = transport
95
382
self.transport = transport
96
self._lockfiles = lockfiles
383
self._mode_check_done = None
384
self._client = client
385
self._client_path = client_path
386
self.base = self.root_transport.base
390
def _gitrepository_class(self):
391
return RemoteGitRepository
393
def archive(self, format, committish, write_data, progress=None, write_error=None,
394
subdirs=None, prefix=None):
395
if format not in ('tar', 'zip'):
396
raise errors.NoSuchExportFormat(format)
398
pb = ui.ui_factory.nested_progress_bar()
399
progress = DefaultProgressReporter(pb).progress
403
self._client.archive(self._client_path, committish,
404
write_data, progress, write_error, format=format,
405
subdirs=subdirs, prefix=prefix)
406
except GitProtocolError as e:
407
raise parse_git_error(self.transport.external_url(), e)
412
def fetch_pack(self, determine_wants, graph_walker, pack_data, progress=None):
414
pb = ui.ui_factory.nested_progress_bar()
415
progress = DefaultProgressReporter(pb).progress
419
result = self._client.fetch_pack(self._client_path, determine_wants,
420
graph_walker, pack_data, progress)
421
if result.refs is None:
423
self._refs = remote_refs_dict_to_container(result.refs, result.symrefs)
425
except GitProtocolError as e:
426
raise parse_git_error(self.transport.external_url(), e)
431
def send_pack(self, get_changed_refs, generate_pack_data, progress=None):
433
pb = ui.ui_factory.nested_progress_bar()
434
progress = DefaultProgressReporter(pb).progress
437
def get_changed_refs_wrapper(refs):
438
# TODO(jelmer): This drops symref information
439
self._refs = remote_refs_dict_to_container(refs)
440
return get_changed_refs(refs)
442
return self._client.send_pack(self._client_path,
443
get_changed_refs_wrapper, generate_pack_data, progress)
444
except GitProtocolError as e:
445
raise parse_git_error(self.transport.external_url(), e)
450
def create_branch(self, name=None, repository=None,
451
append_revisions_only=None, ref=None):
452
refname = self._get_selected_ref(name, ref)
453
if refname != b'HEAD' and refname in self.get_refs_container():
454
raise AlreadyBranchError(self.user_url)
455
if refname in self.get_refs_container():
456
ref_chain, unused_sha = self.get_refs_container().follow(self._get_selected_ref(None))
457
if ref_chain[0] == b'HEAD':
458
refname = ref_chain[1]
459
repo = self.open_repository()
460
return RemoteGitBranch(self, repo, refname)
462
def destroy_branch(self, name=None):
463
refname = self._get_selected_ref(name)
464
def get_changed_refs(old_refs):
466
if not refname in ret:
467
raise NotBranchError(self.user_url)
468
ret[refname] = dulwich.client.ZERO_SHA
470
def generate_pack_data(have, want, ofs_delta=False):
471
return pack_objects_to_data([])
472
self.send_pack(get_changed_refs, generate_pack_data)
476
return self.control_url
479
def user_transport(self):
480
return self.root_transport
483
def control_url(self):
484
return self.control_transport.base
487
def control_transport(self):
488
return self.root_transport
98
490
def open_repository(self):
99
return RemoteGitRepository(self, self._lockfiles)
491
return RemoteGitRepository(self)
101
def open_branch(self, _unsupported=False):
493
def open_branch(self, name=None, unsupported=False,
494
ignore_fallbacks=False, ref=None, possible_transports=None,
102
496
repo = self.open_repository()
103
# TODO: Support for multiple branches in one bzrdir in bzrlib!
104
return RemoteGitBranch(self, repo, "HEAD", self._lockfiles)
497
ref = self._get_selected_ref(name, ref)
498
if not nascent_ok and ref not in self.get_refs_container():
499
raise NotBranchError(self.root_transport.base,
501
ref_chain, unused_sha = self.get_refs_container().follow(ref)
502
return RemoteGitBranch(self, repo, ref_chain[-1])
106
def open_workingtree(self):
504
def open_workingtree(self, recommend_upgrade=False):
107
505
raise NotLocalUrl(self.transport.base)
507
def has_workingtree(self):
510
def get_peeled(self, name):
511
return self.get_refs_container().get_peeled(name)
513
def get_refs_container(self):
514
if self._refs is not None:
516
result = self.fetch_pack(lambda x: None, None,
517
lambda x: None, lambda x: trace.mutter("git: %s" % x))
518
self._refs = remote_refs_dict_to_container(
519
result.refs, result.symrefs)
522
def push_branch(self, source, revision_id=None, overwrite=False,
523
remember=False, create_prefix=False, lossy=False,
525
"""Push the source branch into this ControlDir."""
526
if revision_id is None:
527
# No revision supplied by the user, default to the branch
529
revision_id = source.last_revision()
531
push_result = GitPushResult()
532
push_result.workingtree_updated = None
533
push_result.master_branch = None
534
push_result.source_branch = source
535
push_result.stacked_on = None
536
push_result.branch_push_result = None
537
repo = self.find_repository()
538
refname = self._get_selected_ref(name)
539
if isinstance(source, GitBranch) and lossy:
540
raise errors.LossyPushToSameVCS(source.controldir, self)
541
source_store = get_object_store(source.repository)
542
with source_store.lock_read():
543
def get_changed_refs(refs):
544
self._refs = remote_refs_dict_to_container(refs)
546
# TODO(jelmer): Unpeel if necessary
547
push_result.new_original_revid = revision_id
549
new_sha = source_store._lookup_revision_sha1(revision_id)
551
new_sha = repo.lookup_bzr_revision_id(revision_id)[0]
553
if remote_divergence(ret.get(refname), new_sha, source_store):
554
raise DivergedBranches(
555
source, self.open_branch(name, nascent_ok=True))
556
ret[refname] = new_sha
559
generate_pack_data = source_store.generate_lossy_pack_data
561
generate_pack_data = source_store.generate_pack_data
562
new_refs = self.send_pack(get_changed_refs, generate_pack_data)
563
push_result.new_revid = repo.lookup_foreign_revision_id(
566
old_remote = self._refs[refname]
568
old_remote = ZERO_SHA
569
push_result.old_revid = repo.lookup_foreign_revision_id(old_remote)
570
self._refs = remote_refs_dict_to_container(new_refs)
571
push_result.target_branch = self.open_branch(name)
572
if old_remote != ZERO_SHA:
573
push_result.branch_push_result = GitBranchPushResult()
574
push_result.branch_push_result.source_branch = source
575
push_result.branch_push_result.target_branch = push_result.target_branch
576
push_result.branch_push_result.local_branch = None
577
push_result.branch_push_result.master_branch = push_result.target_branch
578
push_result.branch_push_result.old_revid = push_result.old_revid
579
push_result.branch_push_result.new_revid = push_result.new_revid
580
push_result.branch_push_result.new_original_revid = push_result.new_original_revid
581
if source.get_push_location() is None or remember:
582
source.set_push_location(push_result.target_branch.base)
585
def _find_commondir(self):
586
# There is no way to find the commondir, if there is any.
110
590
class EmptyObjectStoreIterator(dict):
116
596
class TemporaryPackIterator(Pack):
118
598
def __init__(self, path, resolve_ext_ref):
119
self.resolve_ext_ref = resolve_ext_ref
120
super(TemporaryPackIterator, self).__init__(path)
599
super(TemporaryPackIterator, self).__init__(
600
path, resolve_ext_ref=resolve_ext_ref)
601
self._idx_load = lambda: self._idx_load_or_generate(self._idx_path)
124
if self._idx is None:
125
self._data.create_index_v2(self._idx_path, self.resolve_ext_ref)
126
self._idx = PackIndex(self._idx_path)
603
def _idx_load_or_generate(self, path):
604
if not os.path.exists(path):
605
pb = ui.ui_factory.nested_progress_bar()
607
def report_progress(cur, total):
608
pb.update("generating index", cur, total)
609
self.data.create_index(path,
610
progress=report_progress)
613
return load_pack_index(path)
129
615
def __del__(self):
130
os.remove(self._data_path)
131
os.remove(self._idx_path)
616
if self._idx is not None:
618
os.remove(self._idx_path)
619
if self._data is not None:
621
os.remove(self._data_path)
624
class BzrGitHttpClient(dulwich.client.HttpGitClient):
626
def __init__(self, transport, *args, **kwargs):
627
self.transport = transport
628
super(BzrGitHttpClient, self).__init__(transport.external_url(), *args, **kwargs)
630
def _http_request(self, url, headers=None, data=None,
631
allow_compression=False):
632
"""Perform HTTP request.
634
:param url: Request URL.
635
:param headers: Optional custom headers to override defaults.
636
:param data: Request data.
637
:param allow_compression: Allow GZipped communication.
638
:return: Tuple (`response`, `read`), where response is an `urllib3`
639
response object with additional `content_type` and
640
`redirect_location` properties, and `read` is a consumable read
641
method for the response data.
643
from breezy.transport.http._urllib2_wrappers import Request
644
headers['User-agent'] = user_agent_for_github()
645
headers["Pragma"] = "no-cache"
646
if allow_compression:
647
headers["Accept-Encoding"] = "gzip"
649
headers["Accept-Encoding"] = "identity"
652
('GET' if data is None else 'POST'),
654
accepted_errors=[200, 404])
655
request.follow_redirections = True
657
response = self.transport._perform(request)
659
if response.code == 404:
660
raise NotGitRepository()
661
elif response.code != 200:
662
raise GitProtocolError("unexpected http resp %d for %s" %
663
(response.code, url))
665
# TODO: Optimization available by adding `preload_content=False` to the
666
# request and just passing the `read` method on instead of going via
667
# `BytesIO`, if we can guarantee that the entire response is consumed
668
# before issuing the next to still allow for connection reuse from the
670
if response.getheader("Content-Encoding") == "gzip":
671
read = gzip.GzipFile(fileobj=response).read
675
class WrapResponse(object):
677
def __init__(self, response):
678
self._response = response
679
self.status = response.code
680
self.content_type = response.getheader("Content-Type")
681
self.redirect_location = response.geturl()
684
self._response.close()
686
return WrapResponse(response), read
689
class RemoteGitControlDirFormat(GitControlDirFormat):
690
"""The .git directory control format."""
692
supports_workingtrees = False
695
def _known_formats(self):
696
return set([RemoteGitControlDirFormat()])
698
def get_branch_format(self):
699
return RemoteGitBranchFormat()
701
def is_initializable(self):
704
def is_supported(self):
707
def open(self, transport, _found=None):
708
"""Open this directory.
711
# we dont grok readonly - git isn't integrated with transport.
713
if url.startswith('readonly+'):
714
url = url[len('readonly+'):]
715
scheme = urlparse.urlsplit(transport.external_url())[0]
716
if isinstance(transport, GitSmartTransport):
717
client = transport._get_client()
718
client_path = transport._get_path()
719
elif scheme in ("http", "https"):
720
client = BzrGitHttpClient(transport)
721
client_path, _ = urlutils.split_segment_parameters(transport._path)
722
elif scheme == 'file':
723
client = dulwich.client.LocalGitClient()
724
client_path = transport.local_abspath('.')
726
raise NotBranchError(transport.base)
728
pass # TODO(jelmer): Actually probe for something
729
return RemoteGitDir(transport, self, client, client_path)
731
def get_format_description(self):
732
return "Remote Git Repository"
734
def initialize_on_transport(self, transport):
735
raise UninitializableFormat(self)
737
def supports_transport(self, transport):
739
external_url = transport.external_url()
740
except InProcessTransport:
741
raise NotBranchError(path=transport.base)
742
return (external_url.startswith("http:") or
743
external_url.startswith("https:") or
744
external_url.startswith("git+") or
745
external_url.startswith("git:"))
748
class GitRemoteRevisionTree(RevisionTree):
750
def archive(self, format, name, root=None, subdir=None, force_mtime=None):
751
"""Create an archive of this tree.
753
:param format: Format name (e.g. 'tar')
754
:param name: target file name
755
:param root: Root directory name (or None)
756
:param subdir: Subdirectory to export (or None)
757
:return: Iterator over archive chunks
759
commit = self._repository.lookup_bzr_revision_id(
760
self.get_revision_id())[0]
761
f = tempfile.SpooledTemporaryFile()
762
# git-upload-archive(1) generaly only supports refs. So let's see if we
766
self._repository.controldir.get_refs_container().as_dict().items()}
768
committish = reverse_refs[commit]
770
# No? Maybe the user has uploadArchive.allowUnreachable enabled.
771
# Let's hope for the best.
773
self._repository.archive(
774
format, committish, f.write,
775
subdirs=([subdir] if subdir else None),
776
prefix=(root+'/') if root else '')
778
return osutils.file_iterator(f)
134
781
class RemoteGitRepository(GitRepository):
136
def __init__(self, gitdir, lockfiles):
137
GitRepository.__init__(self, gitdir, lockfiles)
139
def fetch_pack(self, determine_wants, graph_walker, pack_data,
785
return self.control_url
787
def get_parent_map(self, revids):
788
raise GitSmartRemoteNotSupported(self.get_parent_map, self)
790
def archive(self, *args, **kwargs):
791
return self.controldir.archive(*args, **kwargs)
793
def fetch_pack(self, determine_wants, graph_walker, pack_data,
141
self._transport.fetch_pack(determine_wants, graph_walker, pack_data,
144
def fetch_objects(self, determine_wants, graph_walker, resolve_ext_ref, progress=None):
795
return self.controldir.fetch_pack(determine_wants, graph_walker,
798
def send_pack(self, get_changed_refs, generate_pack_data):
799
return self.controldir.send_pack(get_changed_refs, generate_pack_data)
801
def fetch_objects(self, determine_wants, graph_walker, resolve_ext_ref,
145
803
fd, path = tempfile.mkstemp(suffix=".pack")
146
self.fetch_pack(determine_wants, graph_walker, lambda x: os.write(fd, x), progress)
805
self.fetch_pack(determine_wants, graph_walker,
806
lambda x: os.write(fd, x), progress)
148
809
if os.path.getsize(path) == 0:
149
810
return EmptyObjectStoreIterator()
150
811
return TemporaryPackIterator(path[:-len(".pack")], resolve_ext_ref)
813
def lookup_bzr_revision_id(self, bzr_revid, mapping=None):
814
# This won't work for any round-tripped bzr revisions, but it's a start..
816
return mapping_registry.revision_id_bzr_to_foreign(bzr_revid)
817
except InvalidRevisionId:
818
raise NoSuchRevision(self, bzr_revid)
820
def lookup_foreign_revision_id(self, foreign_revid, mapping=None):
821
"""Lookup a revision id.
825
mapping = self.get_mapping()
826
# Not really an easy way to parse foreign revids here..
827
return mapping.revision_id_foreign_to_bzr(foreign_revid)
829
def revision_tree(self, revid):
830
return GitRemoteRevisionTree(self, revid)
832
def get_revisions(self, revids):
833
raise GitSmartRemoteNotSupported(self.get_revisions, self)
835
def has_revisions(self, revids):
836
raise GitSmartRemoteNotSupported(self.get_revisions, self)
839
class RemoteGitTagDict(GitTags):
841
def set_tag(self, name, revid):
842
sha = self.branch.lookup_bzr_revision_id(revid)[0]
843
self._set_ref(name, sha)
845
def delete_tag(self, name):
846
self._set_ref(name, dulwich.client.ZERO_SHA)
848
def _set_ref(self, name, sha):
849
ref = tag_name_to_ref(name)
850
def get_changed_refs(old_refs):
852
if sha == dulwich.client.ZERO_SHA and ref not in ret:
853
raise NoSuchTag(name)
856
def generate_pack_data(have, want, ofs_delta=False):
857
return pack_objects_to_data([])
858
self.repository.send_pack(get_changed_refs, generate_pack_data)
153
861
class RemoteGitBranch(GitBranch):
155
def __init__(self, bzrdir, repository, name, lockfiles):
156
def determine_wants(heads):
157
if not name in heads:
158
raise NoSuchRef(name)
159
self._ref = heads[name]
160
bzrdir.root_transport.fetch_pack(determine_wants, None, lambda x: None,
161
lambda x: mutter("git: %s" % x))
162
super(RemoteGitBranch, self).__init__(bzrdir, repository, name, self._ref, lockfiles)
863
def __init__(self, controldir, repository, name):
865
super(RemoteGitBranch, self).__init__(controldir, repository, name,
866
RemoteGitBranchFormat())
868
def last_revision_info(self):
869
raise GitSmartRemoteNotSupported(self.last_revision_info, self)
873
return self.control_url
876
def control_url(self):
879
def revision_id_to_revno(self, revision_id):
880
raise GitSmartRemoteNotSupported(self.revision_id_to_revno, self)
164
882
def last_revision(self):
165
return self.mapping.revision_id_foreign_to_bzr(self._ref)
883
return self.lookup_foreign_revision_id(self.head)
887
if self._sha is not None:
889
refs = self.controldir.get_refs_container()
890
name = branch_name_to_ref(self.name)
892
self._sha = refs[name]
894
raise NoSuchRef(name, self.repository.user_url, refs)
167
897
def _synchronize_history(self, destination, revision_id):
168
898
"""See Branch._synchronize_history()."""
169
899
destination.generate_revision_history(self.last_revision())
901
def _get_parent_location(self):
904
def get_push_location(self):
907
def set_push_location(self, url):
910
def _iter_tag_refs(self):
911
"""Iterate over the tag refs.
913
:param refs: Refs dictionary (name -> git sha1)
914
:return: iterator over (ref_name, tag_name, peeled_sha1, unpeeled_sha1)
916
refs = self.controldir.get_refs_container()
917
for ref_name, unpeeled in refs.as_dict().items():
919
tag_name = ref_to_tag_name(ref_name)
920
except (ValueError, UnicodeDecodeError):
922
peeled = refs.get_peeled(ref_name)
924
# Let's just hope it's a commit
926
if not isinstance(tag_name, text_type):
927
raise TypeError(tag_name)
928
yield (ref_name, tag_name, peeled, unpeeled)
931
def remote_refs_dict_to_container(refs_dict, symrefs_dict={}):
934
for k, v in refs_dict.items():
939
for name, target in symrefs_dict.items():
940
base[name] = SYMREF + target
941
ret = DictRefsContainer(base)