61
63
from dulwich.protocol import (
65
CAPABILITY_DELETE_REFS,
67
CAPABILITY_MULTI_ACK_DETAILED,
69
CAPABILITY_REPORT_STATUS,
70
CAPABILITY_SIDE_BAND_64K,
75
SIDE_BAND_CHANNEL_DATA,
76
SIDE_BAND_CHANNEL_PROGRESS,
77
SIDE_BAND_CHANNEL_FATAL,
79
94
"""Check if a file descriptor is readable."""
80
95
return len(select.select([fileno], [], [], 0)[0]) > 0
82
COMMON_CAPABILITIES = ['ofs-delta', 'side-band-64k']
83
FETCH_CAPABILITIES = (['thin-pack', 'multi_ack', 'multi_ack_detailed'] +
97
COMMON_CAPABILITIES = [CAPABILITY_OFS_DELTA, CAPABILITY_SIDE_BAND_64K]
98
FETCH_CAPABILITIES = ([CAPABILITY_THIN_PACK, CAPABILITY_MULTI_ACK,
99
CAPABILITY_MULTI_ACK_DETAILED] +
84
100
COMMON_CAPABILITIES)
85
SEND_CAPABILITIES = ['report-status'] + COMMON_CAPABILITIES
101
SEND_CAPABILITIES = [CAPABILITY_REPORT_STATUS] + COMMON_CAPABILITIES
88
104
class ReportStatusParser(object):
101
117
:raise SendPackError: Raised when the server could not unpack
102
118
:raise UpdateRefsError: Raised when refs could not be updated
104
if self._pack_status not in ('unpack ok', None):
120
if self._pack_status not in (b'unpack ok', None):
105
121
raise SendPackError(self._pack_status)
106
122
if not self._ref_status_ok:
109
125
for status in self._ref_statuses:
110
if ' ' not in status:
126
if b' ' not in status:
111
127
# malformed response, move on to the next one
113
status, ref = status.split(' ', 1)
129
status, ref = status.split(b' ', 1)
117
ref, status = ref.split(' ', 1)
133
ref, status = ref.split(b' ', 1)
120
136
ref_status[ref] = status
121
raise UpdateRefsError('%s failed to update' %
122
', '.join([ref for ref in ref_status
124
ref_status=ref_status)
137
# TODO(jelmer): don't assume encoding of refs is ascii.
138
raise UpdateRefsError(', '.join([
139
ref.decode('ascii') for ref in ref_status if ref not in ok]) +
140
' failed to update', ref_status=ref_status)
126
142
def handle_packet(self, pkt):
127
143
"""Handle a packet.
149
165
# Receive refs from server
150
166
for pkt in proto.read_pkt_seq():
151
(sha, ref) = pkt.rstrip('\n').split(None, 1)
167
(sha, ref) = pkt.rstrip(b'\n').split(None, 1)
153
169
raise GitProtocolError(ref)
154
170
if server_capabilities is None:
155
171
(ref, server_capabilities) = extract_capabilities(ref)
180
196
self._fetch_capabilities = set(FETCH_CAPABILITIES)
181
197
self._send_capabilities = set(SEND_CAPABILITIES)
182
198
if not thin_packs:
183
self._fetch_capabilities.remove('thin-pack')
199
self._fetch_capabilities.remove(CAPABILITY_THIN_PACK)
185
201
def send_pack(self, path, determine_wants, generate_pack_contents,
186
202
progress=None, write_pack=write_pack_objects):
187
203
"""Upload a pack to a remote repository.
189
:param path: Repository path
205
:param path: Repository path (as bytestring)
190
206
:param generate_pack_contents: Function that can return a sequence of
191
207
the shas of the objects to upload.
192
208
:param progress: Optional progress function
202
218
def fetch(self, path, target, determine_wants=None, progress=None):
203
219
"""Fetch into a target repository.
205
:param path: Path to fetch from
221
:param path: Path to fetch from (as bytestring)
206
222
:param target: Target repository to fetch into
207
223
:param determine_wants: Optional function to determine what refs
212
228
if determine_wants is None:
213
229
determine_wants = target.object_store.determine_wants_all
214
231
f, commit, abort = target.object_store.add_pack()
216
233
result = self.fetch_pack(
235
252
raise NotImplementedError(self.fetch_pack)
254
def get_refs(self, path):
255
"""Retrieve the current refs from a git smart server.
257
:param path: Path to the repo to fetch from. (as bytestring)
259
raise NotImplementedError(self.get_refs)
237
261
def _parse_status_report(self, proto):
238
262
unpack = proto.read_pkt_line().strip()
239
if unpack != 'unpack ok':
263
if unpack != b'unpack ok':
241
265
# flush remaining error data
242
266
while st is not None:
248
272
while ref_status:
249
273
ref_status = ref_status.strip()
250
274
statuses.append(ref_status)
251
if not ref_status.startswith('ok '):
275
if not ref_status.startswith(b'ok '):
253
277
ref_status = proto.read_pkt_line()
258
282
for status in statuses:
259
if ' ' not in status:
283
if b' ' not in status:
260
284
# malformed response, move on to the next one
262
status, ref = status.split(' ', 1)
286
status, ref = status.split(b' ', 1)
266
ref, status = ref.split(' ', 1)
290
ref, status = ref.split(b' ', 1)
269
293
ref_status[ref] = status
270
raise UpdateRefsError('%s failed to update' %
271
', '.join([ref for ref in ref_status
294
raise UpdateRefsError(', '.join([ref for ref in ref_status
296
b' failed to update',
273
297
ref_status=ref_status)
275
299
def _read_side_band64k_data(self, proto, channel_callbacks):
306
330
have = [x for x in old_refs.values() if not x == ZERO_SHA]
307
331
sent_capabilities = False
309
for refname in set(new_refs.keys() + old_refs.keys()):
333
all_refs = set(new_refs.keys()).union(set(old_refs.keys()))
334
for refname in all_refs:
310
335
old_sha1 = old_refs.get(refname, ZERO_SHA)
311
336
new_sha1 = new_refs.get(refname, ZERO_SHA)
313
338
if old_sha1 != new_sha1:
314
339
if sent_capabilities:
315
proto.write_pkt_line('%s %s %s' % (
316
old_sha1, new_sha1, refname))
340
proto.write_pkt_line(old_sha1 + b' ' + new_sha1 + b' ' + refname)
318
342
proto.write_pkt_line(
319
'%s %s %s\0%s' % (old_sha1, new_sha1, refname,
320
' '.join(capabilities)))
343
old_sha1 + b' ' + new_sha1 + b' ' + refname + b'\0' +
344
b' '.join(capabilities))
321
345
sent_capabilities = True
322
346
if new_sha1 not in have and new_sha1 != ZERO_SHA:
323
347
want.append(new_sha1)
331
355
:param capabilities: List of negotiated capabilities
332
356
:param progress: Optional progress reporting function
334
if "side-band-64k" in capabilities:
358
if b"side-band-64k" in capabilities:
335
359
if progress is None:
336
360
progress = lambda x: None
337
361
channel_callbacks = {2: progress}
338
if 'report-status' in capabilities:
362
if CAPABILITY_REPORT_STATUS in capabilities:
339
363
channel_callbacks[1] = PktLineParser(
340
364
self._report_status_parser.handle_packet).parse
341
365
self._read_side_band64k_data(proto, channel_callbacks)
343
if 'report-status' in capabilities:
367
if CAPABILITY_REPORT_STATUS in capabilities:
344
368
for pkt in proto.read_pkt_seq():
345
369
self._report_status_parser.handle_packet(pkt)
346
370
if self._report_status_parser is not None:
357
381
:param can_read: function that returns a boolean that indicates
358
382
whether there is extra graph data to read on proto
360
assert isinstance(wants, list) and isinstance(wants[0], str)
361
proto.write_pkt_line('want %s %s\n' % (
362
wants[0], ' '.join(capabilities)))
384
assert isinstance(wants, list) and isinstance(wants[0], bytes)
385
proto.write_pkt_line(COMMAND_WANT + b' ' + wants[0] + b' ' + b' '.join(capabilities) + b'\n')
363
386
for want in wants[1:]:
364
proto.write_pkt_line('want %s\n' % want)
387
proto.write_pkt_line(COMMAND_WANT + b' ' + want + b'\n')
365
388
proto.write_pkt_line(None)
366
389
have = next(graph_walker)
368
proto.write_pkt_line('have %s\n' % have)
391
proto.write_pkt_line(COMMAND_HAVE + b' ' + have + b'\n')
370
393
pkt = proto.read_pkt_line()
371
parts = pkt.rstrip('\n').split(' ')
372
if parts[0] == 'ACK':
394
parts = pkt.rstrip(b'\n').split(b' ')
395
if parts[0] == b'ACK':
373
396
graph_walker.ack(parts[1])
374
if parts[2] in ('continue', 'common'):
397
if parts[2] in (b'continue', b'common'):
376
elif parts[2] == 'ready':
399
elif parts[2] == b'ready':
379
402
raise AssertionError(
380
403
"%s not in ('continue', 'ready', 'common)" %
382
405
have = next(graph_walker)
383
proto.write_pkt_line('done\n')
406
proto.write_pkt_line(COMMAND_DONE + b'\n')
385
408
def _handle_upload_pack_tail(self, proto, capabilities, graph_walker,
386
409
pack_data, progress=None, rbufsize=_RBUFSIZE):
396
419
pkt = proto.read_pkt_line()
398
parts = pkt.rstrip('\n').split(' ')
399
if parts[0] == 'ACK':
421
parts = pkt.rstrip(b'\n').split(b' ')
422
if parts[0] == b'ACK':
400
423
graph_walker.ack(parts[1])
401
424
if len(parts) < 3 or parts[2] not in (
402
'ready', 'continue', 'common'):
425
b'ready', b'continue', b'common'):
404
427
pkt = proto.read_pkt_line()
405
if "side-band-64k" in capabilities:
428
if CAPABILITY_SIDE_BAND_64K in capabilities:
406
429
if progress is None:
407
430
# Just ignore progress data
408
431
progress = lambda x: None
409
self._read_side_band64k_data(proto, {1: pack_data, 2: progress})
432
self._read_side_band64k_data(proto, {
433
SIDE_BAND_CHANNEL_DATA: pack_data,
434
SIDE_BAND_CHANNEL_PROGRESS: progress}
412
438
data = proto.read(rbufsize)
428
454
reads would block.
430
456
:param cmd: The git service name to which we should connect.
431
:param path: The path we should pass to the service.
457
:param path: The path we should pass to the service. (as bytestirng)
433
459
raise NotImplementedError()
436
462
progress=None, write_pack=write_pack_objects):
437
463
"""Upload a pack to a remote repository.
439
:param path: Repository path
465
:param path: Repository path (as bytestring)
440
466
:param generate_pack_contents: Function that can return a sequence of
441
467
the shas of the objects to upload.
442
468
:param progress: Optional callback called with progress updates
447
473
:raises UpdateRefsError: if the server supports report-status
448
474
and rejects ref updates
450
proto, unused_can_read = self._connect('receive-pack', path)
476
proto, unused_can_read = self._connect(b'receive-pack', path)
452
478
old_refs, server_capabilities = read_pkt_refs(proto)
453
479
negotiated_capabilities = self._send_capabilities & server_capabilities
455
if 'report-status' in negotiated_capabilities:
481
if CAPABILITY_REPORT_STATUS in negotiated_capabilities:
456
482
self._report_status_parser = ReportStatusParser()
457
483
report_status_parser = self._report_status_parser
462
488
proto.write_pkt_line(None)
465
if not 'delete-refs' in server_capabilities:
491
if not CAPABILITY_DELETE_REFS in server_capabilities:
466
492
# Server does not support deletions. Fail later.
467
493
new_refs = dict(orig_new_refs)
468
for ref, sha in orig_new_refs.iteritems():
494
for ref, sha in orig_new_refs.items():
469
495
if sha == ZERO_SHA:
470
if 'report-status' in negotiated_capabilities:
496
if CAPABILITY_REPORT_STATUS in negotiated_capabilities:
471
497
report_status_parser._ref_statuses.append(
472
'ng %s remote does not support deleting refs'
498
b'ng ' + sha + b' remote does not support deleting refs')
474
499
report_status_parser._ref_status_ok = False
475
500
del new_refs[ref]
494
519
dowrite = len(objects) > 0
495
520
dowrite = dowrite or any(old_refs.get(ref) != sha
496
for (ref, sha) in new_refs.iteritems()
521
for (ref, sha) in new_refs.items()
497
522
if sha != ZERO_SHA)
499
524
write_pack(proto.write_file(), objects)
511
536
:param pack_data: Callback called for each bit of data in the pack
512
537
:param progress: Callback for progress reports (strings)
514
proto, can_read = self._connect('upload-pack', path)
539
proto, can_read = self._connect(b'upload-pack', path)
516
541
refs, server_capabilities = read_pkt_refs(proto)
517
542
negotiated_capabilities = (
537
562
proto, negotiated_capabilities, graph_walker, pack_data, progress)
565
def get_refs(self, path):
566
"""Retrieve the current refs from a git smart server."""
567
# stock `git ls-remote` uses upload-pack
568
proto, _ = self._connect(b'upload-pack', path)
570
refs, _ = read_pkt_refs(proto)
540
573
def archive(self, path, committish, write_data, progress=None,
541
574
write_error=None):
542
575
proto, can_read = self._connect(b'upload-archive', path)
544
proto.write_pkt_line("argument %s" % committish)
577
proto.write_pkt_line(b"argument " + committish)
545
578
proto.write_pkt_line(None)
546
579
pkt = proto.read_pkt_line()
582
elif pkt == b"ACK\n":
551
elif pkt.startswith("ERR "):
552
raise GitProtocolError(pkt[4:].rstrip("\n"))
584
elif pkt.startswith(b"ERR "):
585
raise GitProtocolError(pkt[4:].rstrip(b"\n"))
554
587
raise AssertionError("invalid response %r" % pkt)
555
588
ret = proto.read_pkt_line()
556
589
if ret is not None:
557
590
raise AssertionError("expected pkt tail")
558
591
self._read_side_band64k_data(proto, {
559
1: write_data, 2: progress, 3: write_error})
592
SIDE_BAND_CHANNEL_DATA: write_data,
593
SIDE_BAND_CHANNEL_PROGRESS: progress,
594
SIDE_BAND_CHANNEL_FATAL: write_error})
562
597
class TCPGitClient(TraditionalGitClient):
570
605
TraditionalGitClient.__init__(self, *args, **kwargs)
572
607
def _connect(self, cmd, path):
608
if type(cmd) is not bytes:
609
raise TypeError(path)
610
if type(path) is not bytes:
611
raise TypeError(path)
573
612
sockaddrs = socket.getaddrinfo(
574
613
self._host, self._port, socket.AF_UNSPEC, socket.SOCK_STREAM)
598
637
proto = Protocol(rfile.read, wfile.write, close,
599
638
report_activity=self._report_activity)
600
if path.startswith("/~"):
639
if path.startswith(b"/~"):
602
proto.send_cmd('git-%s' % cmd, path, 'host=%s' % self._host)
641
# TODO(jelmer): Alternative to ascii?
642
proto.send_cmd(b'git-' + cmd, path, b'host=' + self._host.encode('ascii'))
603
643
return proto, lambda: _fileno_can_read(s)
609
649
def __init__(self, proc):
611
self.read = proc.stdout.read
651
if sys.version_info[0] == 2:
652
self.read = proc.stdout.read
654
self.read = BufferedReader(proc.stdout).read
612
655
self.write = proc.stdin.write
614
657
def can_read(self):
675
def find_git_command():
676
"""Find command to run for system Git (usually C Git).
678
if sys.platform == 'win32': # support .exe, .bat and .cmd
679
try: # to avoid overhead
681
except ImportError: # run through cmd.exe with some overhead
682
return ['cmd', '/c', 'git']
684
status, git = win32api.FindExecutable('git')
632
690
class SubprocessGitClient(TraditionalGitClient):
633
691
"""Git client that talks to a server using a subprocess."""
640
698
del kwargs['stderr']
641
699
TraditionalGitClient.__init__(self, *args, **kwargs)
643
703
def _connect(self, service, path):
704
if type(service) is not bytes:
705
raise TypeError(path)
706
if type(path) is not bytes:
707
raise TypeError(path)
644
708
import subprocess
645
argv = ['git', service, path]
709
if self.git_command is None:
710
git_command = find_git_command()
711
argv = git_command + [service.decode('ascii'), path]
646
712
p = SubprocessWrapper(
647
713
subprocess.Popen(argv, bufsize=0, stdin=subprocess.PIPE,
648
714
stdout=subprocess.PIPE,
657
723
def __init__(self, thin_packs=True, report_activity=None):
658
724
"""Create a new LocalGitClient instance.
660
:param path: Path to the local repository
661
726
:param thin_packs: Whether or not thin packs should be retrieved
662
727
:param report_activity: Optional callback for reporting transport
669
734
progress=None, write_pack=write_pack_objects):
670
735
"""Upload a pack to a remote repository.
672
:param path: Repository path
737
:param path: Repository path (as bytestring)
673
738
:param generate_pack_contents: Function that can return a sequence of
674
739
the shas of the objects to upload.
675
740
:param progress: Optional progress function
683
748
from dulwich.repo import Repo
686
old_refs = target.get_refs()
687
new_refs = determine_wants(old_refs)
689
have = [sha1 for sha1 in old_refs.values() if sha1 != ZERO_SHA]
691
for refname in set(new_refs.keys() + old_refs.keys()):
692
old_sha1 = old_refs.get(refname, ZERO_SHA)
693
new_sha1 = new_refs.get(refname, ZERO_SHA)
694
if new_sha1 not in have and new_sha1 != ZERO_SHA:
695
want.append(new_sha1)
697
if not want and old_refs == new_refs:
700
target.object_store.add_objects(generate_pack_contents(have, want))
702
for name, sha in new_refs.iteritems():
703
target.refs[name] = sha
750
with closing(Repo(path)) as target:
751
old_refs = target.get_refs()
752
new_refs = determine_wants(dict(old_refs))
754
have = [sha1 for sha1 in old_refs.values() if sha1 != ZERO_SHA]
756
all_refs = set(new_refs.keys()).union(set(old_refs.keys()))
757
for refname in all_refs:
758
old_sha1 = old_refs.get(refname, ZERO_SHA)
759
new_sha1 = new_refs.get(refname, ZERO_SHA)
760
if new_sha1 not in have and new_sha1 != ZERO_SHA:
761
want.append(new_sha1)
763
if not want and old_refs == new_refs:
766
target.object_store.add_objects(generate_pack_contents(have, want))
768
for name, sha in new_refs.items():
769
target.refs[name] = sha
707
773
def fetch(self, path, target, determine_wants=None, progress=None):
708
774
"""Fetch into a target repository.
710
:param path: Path to fetch from
776
:param path: Path to fetch from (as bytestring)
711
777
:param target: Target repository to fetch into
712
778
:param determine_wants: Optional function to determine what refs
715
781
:return: remote refs as dictionary
717
783
from dulwich.repo import Repo
719
return r.fetch(target, determine_wants=determine_wants,
784
with closing(Repo(path)) as r:
785
return r.fetch(target, determine_wants=determine_wants,
722
788
def fetch_pack(self, path, determine_wants, graph_walker, pack_data,
729
795
:param progress: Callback for progress reports (strings)
731
797
from dulwich.repo import Repo
733
objects_iter = r.fetch_objects(determine_wants, graph_walker, progress)
735
# Did the process short-circuit (e.g. in a stateless RPC call)? Note
736
# that the client still expects a 0-object pack in most cases.
737
if objects_iter is None:
739
write_pack_objects(ProtocolFile(None, pack_data), objects_iter)
798
with closing(Repo(path)) as r:
799
objects_iter = r.fetch_objects(determine_wants, graph_walker, progress)
801
# Did the process short-circuit (e.g. in a stateless RPC call)? Note
802
# that the client still expects a 0-object pack in most cases.
803
if objects_iter is None:
805
write_pack_objects(ProtocolFile(None, pack_data), objects_iter)
807
def get_refs(self, path):
808
"""Retrieve the current refs from a git smart server."""
809
from dulwich.repo import Repo
811
with closing(Repo(path)) as target:
812
return target.get_refs()
742
815
# What Git client to use for local access
743
default_local_git_client_cls = SubprocessGitClient
816
default_local_git_client_cls = LocalGitClient
746
819
class SSHVendor(object):
771
844
"""SSH vendor that shells out to the local 'ssh' command."""
773
846
def run_command(self, host, command, username=None, port=None):
847
if (type(command) is not list or
848
not all([isinstance(b, bytes) for b in command])):
849
raise TypeError(command)
774
851
import subprocess
775
852
#FIXME: This has no way to deal with passwords..
776
853
args = ['ssh', '-x']
874
951
def run_command(self, host, command, username=None, port=None,
875
952
progress_stderr=None):
953
if (type(command) is not list or
954
not all([isinstance(b, bytes) for b in command])):
955
raise TypeError(command)
877
956
# Paramiko needs an explicit port. None is not valid
889
968
channel = client.get_transport().open_session()
892
channel.exec_command(*command)
971
channel.exec_command(subprocess.list2cmdline(command))
894
973
return ParamikoWrapper(
895
974
client, channel, progress_stderr=progress_stderr)
909
988
self.alternative_paths = {}
911
990
def _get_cmd_path(self, cmd):
912
return self.alternative_paths.get(cmd, 'git-%s' % cmd)
991
cmd = self.alternative_paths.get(cmd, b'git-' + cmd)
992
assert isinstance(cmd, bytes)
993
if sys.version_info[:2] <= (2, 6):
994
return shlex.split(cmd)
996
# TODO(jelmer): Don't decode/encode here
997
return [x.encode('ascii') for x in shlex.split(cmd.decode('ascii'))]
914
999
def _connect(self, cmd, path):
915
if path.startswith("/~"):
1000
if type(cmd) is not bytes:
1001
raise TypeError(path)
1002
if type(path) is not bytes:
1003
raise TypeError(path)
1004
if path.startswith(b"/~"):
1006
argv = self._get_cmd_path(cmd) + [path]
917
1007
con = get_ssh_vendor().run_command(
918
self.host, ["%s '%s'" % (self._get_cmd_path(cmd), path)],
919
port=self.port, username=self.username)
920
return (Protocol(con.read, con.write, con.close,
921
report_activity=self._report_activity),
1008
self.host, argv, port=self.port, username=self.username)
1009
return (Protocol(con.read, con.write, con.close,
1010
report_activity=self._report_activity),
957
1046
self.opener = opener
958
1047
GitClient.__init__(self, *args, **kwargs)
1050
return "%s(%r, dumb=%r)" % (type(self).__name__, self.base_url, self.dumb)
960
1052
def _get_url(self, path):
961
1053
return urlparse.urljoin(self.base_url, path).rstrip("/") + "/"
1008
1100
progress=None, write_pack=write_pack_objects):
1009
1101
"""Upload a pack to a remote repository.
1011
:param path: Repository path
1103
:param path: Repository path (as bytestring)
1012
1104
:param generate_pack_contents: Function that can return a sequence of
1013
1105
the shas of the objects to upload.
1014
1106
:param progress: Optional progress function
1022
1114
url = self._get_url(path)
1023
1115
old_refs, server_capabilities = self._discover_references(
1024
"git-receive-pack", url)
1116
b"git-receive-pack", url)
1025
1117
negotiated_capabilities = self._send_capabilities & server_capabilities
1027
if 'report-status' in negotiated_capabilities:
1119
if CAPABILITY_REPORT_STATUS in negotiated_capabilities:
1028
1120
self._report_status_parser = ReportStatusParser()
1030
1122
new_refs = determine_wants(dict(old_refs))
1041
1133
objects = generate_pack_contents(have, want)
1042
1134
if len(objects) > 0:
1043
1135
write_pack(req_proto.write_file(), objects)
1044
resp = self._smart_request("git-receive-pack", url,
1136
resp = self._smart_request(b"git-receive-pack", url,
1045
1137
data=req_data.getvalue())
1047
1139
resp_proto = Protocol(resp.read, None)
1065
1157
url = self._get_url(path)
1066
1158
refs, server_capabilities = self._discover_references(
1067
"git-upload-pack", url)
1159
b"git-upload-pack", url)
1068
1160
negotiated_capabilities = self._fetch_capabilities & server_capabilities
1069
1161
wants = determine_wants(refs)
1070
1162
if wants is not None:
1079
1171
req_proto, negotiated_capabilities, graph_walker, wants,
1081
1173
resp = self._smart_request(
1082
"git-upload-pack", url, data=req_data.getvalue())
1174
b"git-upload-pack", url, data=req_data.getvalue())
1084
1176
resp_proto = Protocol(resp.read, None)
1085
1177
self._handle_upload_pack_tail(resp_proto, negotiated_capabilities,
1183
def get_refs(self, path):
1184
"""Retrieve the current refs from a git smart server."""
1185
url = self._get_url(path)
1186
refs, _ = self._discover_references(
1187
b"git-upload-pack", url)
1092
1191
def get_transport_and_path_from_url(url, config=None, **kwargs):
1093
1192
"""Obtain a git client from a URL.
1095
:param url: URL to open
1194
:param url: URL to open (a unicode string)
1096
1195
:param config: Optional config object
1097
1196
:param thin_packs: Whether or not thin packs should be retrieved
1098
1197
:param report_activity: Optional callback for reporting transport
1121
1220
def get_transport_and_path(location, **kwargs):
1122
1221
"""Obtain a git client from a URL.
1124
:param location: URL or path
1223
:param location: URL or path (a string)
1125
1224
:param config: Optional config object
1126
1225
:param thin_packs: Whether or not thin packs should be retrieved
1127
1226
:param report_activity: Optional callback for reporting transport
1137
1236
if (sys.platform == 'win32' and
1138
location[0].isalpha() and location[1:2] == ':\\'):
1237
location[0].isalpha() and location[1:3] == ':\\'):
1139
1238
# Windows local path
1140
1239
return default_local_git_client_cls(**kwargs), location