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 (
48
UninitializableFormat,
50
from ..revisiontree import RevisionTree
51
from ..transport import (
53
register_urlparse_netloc_protocol,
58
user_agent_for_github,
27
60
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
72
BareLocalGitControlDirFormat,
75
GitSmartRemoteNotSupported,
78
from .mapping import (
81
from .object_store import (
87
from .repository import (
99
from dulwich.errors import (
102
from dulwich.pack import (
104
pack_objects_to_data,
106
from dulwich.protocol import ZERO_SHA
107
from dulwich.refs import (
111
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
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
44
152
# a full implementation of Transport
45
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
def parse_git_error(url, message):
178
"""Parse a remote git server error and return a bzr exception.
180
:param url: URL of the remote repository
181
:param message: Message sent by the remote git server
183
message = str(message).strip()
184
if message.startswith("Could not find Repository "):
185
return NotBranchError(url, message)
186
if message == "HEAD failed to update":
187
base_url, _ = urlutils.split_segment_parameters(url)
189
("Unable to update remote HEAD branch. To update the master "
190
"branch, specify the URL %s,branch=master.") % base_url)
191
# Don't know, just return it to the user as-is
192
return RemoteGitError(message)
49
195
class GitSmartTransport(Transport):
51
197
def __init__(self, url, _client=None):
52
198
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)
199
(self._host, self._port, self._username, self._path) = \
201
if 'transport' in debug.debug_flags:
202
trace.mutter('host: %r, user: %r, port: %r, path: %r',
203
self._host, self._username, self._port, self._path)
57
204
self._client = _client
205
self._stripped_path = self._path.rsplit(",", 1)[0]
207
def external_url(self):
210
def has(self, relpath):
59
213
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"])
214
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)
217
return self._stripped_path
74
219
def get(self, path):
75
220
raise NoSuchFile(path)
85
230
newurl = urlutils.join(self.base, offset)
87
return GitSmartTransport(newurl, self._client)
232
return self.__class__(newurl, self._client)
235
class TCPGitSmartTransport(GitSmartTransport):
239
def _get_client(self):
240
if self._client is not None:
245
# return dulwich.client.LocalGitClient()
246
return dulwich.client.SubprocessGitClient()
247
return dulwich.client.TCPGitClient(self._host, self._port,
248
report_activity=self._report_activity)
251
class SSHSocketWrapper(object):
253
def __init__(self, sock):
256
def read(self, len=None):
257
return self.sock.recv(len)
259
def write(self, data):
260
return self.sock.write(data)
263
return len(select.select([self.sock.fileno()], [], [], 0)[0]) > 0
266
class DulwichSSHVendor(dulwich.client.SSHVendor):
269
from ..transport import ssh
270
self.bzr_ssh_vendor = ssh._get_ssh_vendor()
272
def run_command(self, host, command, username=None, port=None):
273
connection = self.bzr_ssh_vendor.connect_ssh(username=username,
274
password=None, port=port, host=host, command=command)
275
(kind, io_object) = connection.get_sock_or_pipes()
277
return SSHSocketWrapper(io_object)
279
raise AssertionError("Unknown io object kind %r'" % kind)
282
#dulwich.client.get_ssh_vendor = DulwichSSHVendor
285
class SSHGitSmartTransport(GitSmartTransport):
290
path = self._stripped_path
291
if path.startswith("/~/"):
295
def _get_client(self):
296
if self._client is not None:
300
location_config = config.LocationConfig(self.base)
301
client = dulwich.client.SSHGitClient(self._host, self._port, self._username,
302
report_activity=self._report_activity)
303
# Set up alternate pack program paths
304
upload_pack = location_config.get_user_option('git_upload_pack')
306
client.alternative_paths["upload-pack"] = upload_pack
307
receive_pack = location_config.get_user_option('git_receive_pack')
309
client.alternative_paths["receive-pack"] = receive_pack
313
class RemoteGitBranchFormat(GitBranchFormat):
315
def get_format_description(self):
316
return 'Remote Git Branch'
319
def _matchingcontroldir(self):
320
return RemoteGitControlDirFormat()
322
def initialize(self, a_controldir, name=None, repository=None,
323
append_revisions_only=None):
324
raise UninitializableFormat(self)
327
class DefaultProgressReporter(object):
329
_GIT_PROGRESS_PARTIAL_RE = re.compile(r"(.*?): +(\d+)% \((\d+)/(\d+)\)")
330
_GIT_PROGRESS_TOTAL_RE = re.compile(r"(.*?): (\d+)")
332
def __init__(self, pb):
335
def progress(self, text):
336
text = text.rstrip("\r\n")
337
if text.startswith('error: '):
338
trace.show_error('git: %s', text[len('error: '):])
340
trace.mutter("git: %s", text)
341
g = self._GIT_PROGRESS_PARTIAL_RE.match(text)
343
(text, pct, current, total) = g.groups()
344
self.pb.update(text, int(current), int(total))
346
g = self._GIT_PROGRESS_TOTAL_RE.match(text)
348
(text, total) = g.groups()
349
self.pb.update(text, None, int(total))
351
trace.note("%s", text)
90
354
class RemoteGitDir(GitDir):
92
def __init__(self, transport, lockfiles, format):
356
def __init__(self, transport, format, client, client_path):
93
357
self._format = format
94
358
self.root_transport = transport
95
359
self.transport = transport
96
self._lockfiles = lockfiles
360
self._mode_check_done = None
361
self._client = client
362
self._client_path = client_path
363
self.base = self.root_transport.base
367
def _gitrepository_class(self):
368
return RemoteGitRepository
370
def archive(self, format, committish, write_data, progress=None, write_error=None,
371
subdirs=None, prefix=None):
372
if format not in ('tar', 'zip'):
373
raise errors.NoSuchExportFormat(format)
375
pb = ui.ui_factory.nested_progress_bar()
376
progress = DefaultProgressReporter(pb).progress
380
self._client.archive(self._client_path, committish,
381
write_data, progress, write_error, format=format,
382
subdirs=subdirs, prefix=prefix)
383
except GitProtocolError as e:
384
raise parse_git_error(self.transport.external_url(), e)
389
def fetch_pack(self, determine_wants, graph_walker, pack_data, progress=None):
391
pb = ui.ui_factory.nested_progress_bar()
392
progress = DefaultProgressReporter(pb).progress
396
result = self._client.fetch_pack(self._client_path, determine_wants,
397
graph_walker, pack_data, progress)
398
if result.refs is None:
400
self._refs = remote_refs_dict_to_container(result.refs, result.symrefs)
402
except GitProtocolError as e:
403
raise parse_git_error(self.transport.external_url(), e)
408
def send_pack(self, get_changed_refs, generate_pack_data, progress=None):
410
pb = ui.ui_factory.nested_progress_bar()
411
progress = DefaultProgressReporter(pb).progress
414
def get_changed_refs_wrapper(refs):
415
# TODO(jelmer): This drops symref information
416
self._refs = remote_refs_dict_to_container(refs)
417
return get_changed_refs(refs)
419
return self._client.send_pack(self._client_path,
420
get_changed_refs_wrapper, generate_pack_data, progress)
421
except GitProtocolError as e:
422
raise parse_git_error(self.transport.external_url(), e)
427
def create_branch(self, name=None, repository=None,
428
append_revisions_only=None, ref=None):
429
refname = self._get_selected_ref(name, ref)
430
if refname != b'HEAD' and refname in self.get_refs_container():
431
raise AlreadyBranchError(self.user_url)
432
if refname in self.get_refs_container():
433
ref_chain, unused_sha = self.get_refs_container().follow(self._get_selected_ref(None))
434
if ref_chain[0] == b'HEAD':
435
refname = ref_chain[1]
436
repo = self.open_repository()
437
return RemoteGitBranch(self, repo, refname)
439
def destroy_branch(self, name=None):
440
refname = self._get_selected_ref(name)
441
def get_changed_refs(old_refs):
443
if not refname in ret:
444
raise NotBranchError(self.user_url)
445
ret[refname] = dulwich.client.ZERO_SHA
447
def generate_pack_data(have, want, ofs_delta=False):
448
return pack_objects_to_data([])
449
self.send_pack(get_changed_refs, generate_pack_data)
453
return self.control_url
456
def user_transport(self):
457
return self.root_transport
460
def control_url(self):
461
return self.control_transport.base
464
def control_transport(self):
465
return self.root_transport
98
467
def open_repository(self):
99
return RemoteGitRepository(self, self._lockfiles)
468
return RemoteGitRepository(self)
101
def open_branch(self, _unsupported=False):
470
def open_branch(self, name=None, unsupported=False,
471
ignore_fallbacks=False, ref=None, possible_transports=None,
102
473
repo = self.open_repository()
103
# TODO: Support for multiple branches in one bzrdir in bzrlib!
104
return RemoteGitBranch(self, repo, "HEAD", self._lockfiles)
474
ref = self._get_selected_ref(name, ref)
475
if not nascent_ok and ref not in self.get_refs_container():
476
raise NotBranchError(self.root_transport.base,
478
ref_chain, unused_sha = self.get_refs_container().follow(ref)
479
return RemoteGitBranch(self, repo, ref_chain[-1])
106
def open_workingtree(self):
481
def open_workingtree(self, recommend_upgrade=False):
107
482
raise NotLocalUrl(self.transport.base)
484
def has_workingtree(self):
487
def get_peeled(self, name):
488
return self.get_refs_container().get_peeled(name)
490
def get_refs_container(self):
491
if self._refs is not None:
493
result = self.fetch_pack(lambda x: None, None,
494
lambda x: None, lambda x: trace.mutter("git: %s" % x))
495
self._refs = remote_refs_dict_to_container(
496
result.refs, result.symrefs)
499
def push_branch(self, source, revision_id=None, overwrite=False,
500
remember=False, create_prefix=False, lossy=False,
502
"""Push the source branch into this ControlDir."""
503
if revision_id is None:
504
# No revision supplied by the user, default to the branch
506
revision_id = source.last_revision()
508
push_result = GitPushResult()
509
push_result.workingtree_updated = None
510
push_result.master_branch = None
511
push_result.source_branch = source
512
push_result.stacked_on = None
513
push_result.branch_push_result = None
514
repo = self.find_repository()
515
refname = self._get_selected_ref(name)
516
if isinstance(source, GitBranch) and lossy:
517
raise errors.LossyPushToSameVCS(source.controldir, self)
518
source_store = get_object_store(source.repository)
519
with source_store.lock_read():
520
def get_changed_refs(refs):
521
self._refs = remote_refs_dict_to_container(refs)
523
# TODO(jelmer): Unpeel if necessary
524
push_result.new_original_revid = revision_id
526
new_sha = source_store._lookup_revision_sha1(revision_id)
528
new_sha = repo.lookup_bzr_revision_id(revision_id)[0]
530
if remote_divergence(ret.get(refname), new_sha, source_store):
531
raise DivergedBranches(
532
source, self.open_branch(name, nascent_ok=True))
533
ret[refname] = new_sha
536
generate_pack_data = source_store.generate_lossy_pack_data
538
generate_pack_data = source_store.generate_pack_data
539
new_refs = self.send_pack(get_changed_refs, generate_pack_data)
540
push_result.new_revid = repo.lookup_foreign_revision_id(
543
old_remote = self._refs[refname]
545
old_remote = ZERO_SHA
546
push_result.old_revid = repo.lookup_foreign_revision_id(old_remote)
547
self._refs = remote_refs_dict_to_container(new_refs)
548
push_result.target_branch = self.open_branch(name)
549
if old_remote != ZERO_SHA:
550
push_result.branch_push_result = GitBranchPushResult()
551
push_result.branch_push_result.source_branch = source
552
push_result.branch_push_result.target_branch = push_result.target_branch
553
push_result.branch_push_result.local_branch = None
554
push_result.branch_push_result.master_branch = push_result.target_branch
555
push_result.branch_push_result.old_revid = push_result.old_revid
556
push_result.branch_push_result.new_revid = push_result.new_revid
557
push_result.branch_push_result.new_original_revid = push_result.new_original_revid
558
if source.get_push_location() is None or remember:
559
source.set_push_location(push_result.target_branch.base)
562
def _find_commondir(self):
563
# There is no way to find the commondir, if there is any.
110
567
class EmptyObjectStoreIterator(dict):
116
573
class TemporaryPackIterator(Pack):
118
575
def __init__(self, path, resolve_ext_ref):
119
self.resolve_ext_ref = resolve_ext_ref
120
super(TemporaryPackIterator, self).__init__(path)
576
super(TemporaryPackIterator, self).__init__(
577
path, resolve_ext_ref=resolve_ext_ref)
578
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)
580
def _idx_load_or_generate(self, path):
581
if not os.path.exists(path):
582
pb = ui.ui_factory.nested_progress_bar()
584
def report_progress(cur, total):
585
pb.update("generating index", cur, total)
586
self.data.create_index(path,
587
progress=report_progress)
590
return load_pack_index(path)
129
592
def __del__(self):
130
os.remove(self._data_path)
131
os.remove(self._idx_path)
593
if self._idx is not None:
595
os.remove(self._idx_path)
596
if self._data is not None:
598
os.remove(self._data_path)
601
class BzrGitHttpClient(dulwich.client.HttpGitClient):
603
def __init__(self, transport, *args, **kwargs):
604
self.transport = transport
605
super(BzrGitHttpClient, self).__init__(transport.external_url(), *args, **kwargs)
607
def _http_request(self, url, headers=None, data=None,
608
allow_compression=False):
609
"""Perform HTTP request.
611
:param url: Request URL.
612
:param headers: Optional custom headers to override defaults.
613
:param data: Request data.
614
:param allow_compression: Allow GZipped communication.
615
:return: Tuple (`response`, `read`), where response is an `urllib3`
616
response object with additional `content_type` and
617
`redirect_location` properties, and `read` is a consumable read
618
method for the response data.
620
from breezy.transport.http._urllib2_wrappers import Request
621
headers['User-agent'] = user_agent_for_github()
622
headers["Pragma"] = "no-cache"
623
if allow_compression:
624
headers["Accept-Encoding"] = "gzip"
626
headers["Accept-Encoding"] = "identity"
629
('GET' if data is None else 'POST'),
631
accepted_errors=[200, 404])
633
response = self.transport._perform(request)
635
if response.code == 404:
636
raise NotGitRepository()
637
elif response.code != 200:
638
raise GitProtocolError("unexpected http resp %d for %s" %
639
(response.code, url))
641
# TODO: Optimization available by adding `preload_content=False` to the
642
# request and just passing the `read` method on instead of going via
643
# `BytesIO`, if we can guarantee that the entire response is consumed
644
# before issuing the next to still allow for connection reuse from the
646
if response.getheader("Content-Encoding") == "gzip":
647
read = gzip.GzipFile(fileobj=response).read
651
class WrapResponse(object):
653
def __init__(self, response):
654
self._response = response
655
self.status = response.code
656
self.content_type = response.getheader("Content-Type")
657
self.redirect_location = response.geturl()
660
self._response.close()
662
return WrapResponse(response), read
665
class RemoteGitControlDirFormat(GitControlDirFormat):
666
"""The .git directory control format."""
668
supports_workingtrees = False
671
def _known_formats(self):
672
return set([RemoteGitControlDirFormat()])
674
def get_branch_format(self):
675
return RemoteGitBranchFormat()
677
def is_initializable(self):
680
def is_supported(self):
683
def open(self, transport, _found=None):
684
"""Open this directory.
687
# we dont grok readonly - git isn't integrated with transport.
689
if url.startswith('readonly+'):
690
url = url[len('readonly+'):]
691
scheme = urlparse.urlsplit(transport.external_url())[0]
692
if isinstance(transport, GitSmartTransport):
693
client = transport._get_client()
694
client_path = transport._get_path()
695
elif scheme in ("http", "https"):
696
client = BzrGitHttpClient(transport)
697
client_path, _ = urlutils.split_segment_parameters(transport._path)
698
elif scheme == 'file':
699
client = dulwich.client.LocalGitClient()
700
client_path = transport.local_abspath('.')
702
raise NotBranchError(transport.base)
704
pass # TODO(jelmer): Actually probe for something
705
return RemoteGitDir(transport, self, client, client_path)
707
def get_format_description(self):
708
return "Remote Git Repository"
710
def initialize_on_transport(self, transport):
711
raise UninitializableFormat(self)
713
def supports_transport(self, transport):
715
external_url = transport.external_url()
716
except InProcessTransport:
717
raise NotBranchError(path=transport.base)
718
return (external_url.startswith("http:") or
719
external_url.startswith("https:") or
720
external_url.startswith("git+") or
721
external_url.startswith("git:"))
724
class GitRemoteRevisionTree(RevisionTree):
726
def archive(self, format, name, root=None, subdir=None, force_mtime=None):
727
"""Create an archive of this tree.
729
:param format: Format name (e.g. 'tar')
730
:param name: target file name
731
:param root: Root directory name (or None)
732
:param subdir: Subdirectory to export (or None)
733
:return: Iterator over archive chunks
735
commit = self._repository.lookup_bzr_revision_id(
736
self.get_revision_id())[0]
737
f = tempfile.SpooledTemporaryFile()
738
# git-upload-archive(1) generaly only supports refs. So let's see if we
742
self._repository.controldir.get_refs_container().as_dict().items()}
744
committish = reverse_refs[commit]
746
# No? Maybe the user has uploadArchive.allowUnreachable enabled.
747
# Let's hope for the best.
749
self._repository.archive(
750
format, committish, f.write,
751
subdirs=([subdir] if subdir else None),
752
prefix=(root+'/') if root else '')
754
return osutils.file_iterator(f)
134
757
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,
761
return self.control_url
763
def get_parent_map(self, revids):
764
raise GitSmartRemoteNotSupported(self.get_parent_map, self)
766
def archive(self, *args, **kwargs):
767
return self.controldir.archive(*args, **kwargs)
769
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):
771
return self.controldir.fetch_pack(determine_wants, graph_walker,
774
def send_pack(self, get_changed_refs, generate_pack_data):
775
return self.controldir.send_pack(get_changed_refs, generate_pack_data)
777
def fetch_objects(self, determine_wants, graph_walker, resolve_ext_ref,
145
779
fd, path = tempfile.mkstemp(suffix=".pack")
146
self.fetch_pack(determine_wants, graph_walker, lambda x: os.write(fd, x), progress)
781
self.fetch_pack(determine_wants, graph_walker,
782
lambda x: os.write(fd, x), progress)
148
785
if os.path.getsize(path) == 0:
149
786
return EmptyObjectStoreIterator()
150
787
return TemporaryPackIterator(path[:-len(".pack")], resolve_ext_ref)
789
def lookup_bzr_revision_id(self, bzr_revid, mapping=None):
790
# This won't work for any round-tripped bzr revisions, but it's a start..
792
return mapping_registry.revision_id_bzr_to_foreign(bzr_revid)
793
except InvalidRevisionId:
794
raise NoSuchRevision(self, bzr_revid)
796
def lookup_foreign_revision_id(self, foreign_revid, mapping=None):
797
"""Lookup a revision id.
801
mapping = self.get_mapping()
802
# Not really an easy way to parse foreign revids here..
803
return mapping.revision_id_foreign_to_bzr(foreign_revid)
805
def revision_tree(self, revid):
806
return GitRemoteRevisionTree(self, revid)
808
def get_revisions(self, revids):
809
raise GitSmartRemoteNotSupported(self.get_revisions, self)
811
def has_revisions(self, revids):
812
raise GitSmartRemoteNotSupported(self.get_revisions, self)
815
class RemoteGitTagDict(GitTags):
817
def set_tag(self, name, revid):
818
sha = self.branch.lookup_bzr_revision_id(revid)[0]
819
self._set_ref(name, sha)
821
def delete_tag(self, name):
822
self._set_ref(name, dulwich.client.ZERO_SHA)
824
def _set_ref(self, name, sha):
825
ref = tag_name_to_ref(name)
826
def get_changed_refs(old_refs):
828
if sha == dulwich.client.ZERO_SHA and ref not in ret:
829
raise NoSuchTag(name)
832
def generate_pack_data(have, want, ofs_delta=False):
833
return pack_objects_to_data([])
834
self.repository.send_pack(get_changed_refs, generate_pack_data)
153
837
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)
839
def __init__(self, controldir, repository, name):
841
super(RemoteGitBranch, self).__init__(controldir, repository, name,
842
RemoteGitBranchFormat())
844
def last_revision_info(self):
845
raise GitSmartRemoteNotSupported(self.last_revision_info, self)
849
return self.control_url
852
def control_url(self):
855
def revision_id_to_revno(self, revision_id):
856
raise GitSmartRemoteNotSupported(self.revision_id_to_revno, self)
164
858
def last_revision(self):
165
return self.mapping.revision_id_foreign_to_bzr(self._ref)
859
return self.lookup_foreign_revision_id(self.head)
863
if self._sha is not None:
865
refs = self.controldir.get_refs_container()
866
name = branch_name_to_ref(self.name)
868
self._sha = refs[name]
870
raise NoSuchRef(name, self.repository.user_url, refs)
167
873
def _synchronize_history(self, destination, revision_id):
168
874
"""See Branch._synchronize_history()."""
169
875
destination.generate_revision_history(self.last_revision())
877
def _get_parent_location(self):
880
def get_push_location(self):
883
def set_push_location(self, url):
886
def _iter_tag_refs(self):
887
"""Iterate over the tag refs.
889
:param refs: Refs dictionary (name -> git sha1)
890
:return: iterator over (ref_name, tag_name, peeled_sha1, unpeeled_sha1)
892
refs = self.controldir.get_refs_container()
893
for ref_name, unpeeled in refs.as_dict().items():
895
tag_name = ref_to_tag_name(ref_name)
896
except (ValueError, UnicodeDecodeError):
898
peeled = refs.get_peeled(ref_name)
901
peeled = refs.peel_sha(unpeeled).id
903
# Let's just hope it's a commit
905
if type(tag_name) is not unicode:
906
raise TypeError(tag_name)
907
yield (ref_name, tag_name, peeled, unpeeled)
910
def remote_refs_dict_to_container(refs_dict, symrefs_dict={}):
913
for k, v in refs_dict.items():
919
for name, target in symrefs_dict.items():
920
base[name] = SYMREF + target
921
ret = DictRefsContainer(base)