from io import BytesIO, BufferedReader
import dulwich
import select
+import shlex
import socket
import subprocess
import sys
"""
if determine_wants is None:
determine_wants = target.object_store.determine_wants_all
+
f, commit, abort = target.object_store.add_pack()
try:
result = self.fetch_pack(
"""
raise NotImplementedError(self.fetch_pack)
+ def get_refs(self, path):
+ """Retrieve the current refs from a git smart server.
+
+ :param path: Path to the repo to fetch from.
+ """
+ raise NotImplementedError(self.get_refs)
+
def _parse_status_report(self, proto):
unpack = proto.read_pkt_line().strip()
if unpack != b'unpack ok':
proto, negotiated_capabilities, graph_walker, pack_data, progress)
return refs
+ def get_refs(self, path):
+ """Retrieve the current refs from a git smart server."""
+ # stock `git ls-remote` uses upload-pack
+ proto, _ = self._connect(b'upload-pack', path)
+ with proto:
+ refs, _ = read_pkt_refs(proto)
+ return refs
+
def archive(self, path, committish, write_data, progress=None,
write_error=None):
proto, can_read = self._connect(b'upload-archive', path)
TraditionalGitClient.__init__(self, *args, **kwargs)
def _connect(self, cmd, path):
+ if type(cmd) is not bytes:
+ raise TypeError(path)
+ if type(path) is not bytes:
+ raise TypeError(path)
sockaddrs = socket.getaddrinfo(
self._host, self._port, socket.AF_UNSPEC, socket.SOCK_STREAM)
s = None
report_activity=self._report_activity)
if path.startswith(b"/~"):
path = path[1:]
- proto.send_cmd(b'git-' + cmd, path, b'host=' + self._host)
+ # TODO(jelmer): Alternative to ascii?
+ proto.send_cmd(b'git-' + cmd, path, b'host=' + self._host.encode('ascii'))
return proto, lambda: _fileno_can_read(s)
self.proc.wait()
+def find_git_command():
+ """Find command to run for system Git (usually C Git).
+ """
+ if sys.platform == 'win32': # support .exe, .bat and .cmd
+ try: # to avoid overhead
+ import win32api
+ except ImportError: # run through cmd.exe with some overhead
+ return ['cmd', '/c', 'git']
+ else:
+ status, git = win32api.FindExecutable('git')
+ return [git]
+ else:
+ return ['git']
+
+
class SubprocessGitClient(TraditionalGitClient):
"""Git client that talks to a server using a subprocess."""
del kwargs['stderr']
TraditionalGitClient.__init__(self, *args, **kwargs)
+ git_command = None
+
def _connect(self, service, path):
+ if type(service) is not bytes:
+ raise TypeError(path)
+ if type(path) is not bytes:
+ raise TypeError(path)
import subprocess
- argv = ['git', service, path]
+ if self.git_command is None:
+ git_command = find_git_command()
+ argv = git_command + [service.decode('ascii'), path]
p = SubprocessWrapper(
subprocess.Popen(argv, bufsize=0, stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
with closing(Repo(path)) as target:
old_refs = target.get_refs()
- new_refs = determine_wants(old_refs)
+ new_refs = determine_wants(dict(old_refs))
have = [sha1 for sha1 in old_refs.values() if sha1 != ZERO_SHA]
want = []
return
write_pack_objects(ProtocolFile(None, pack_data), objects_iter)
+ def get_refs(self, path):
+ """Retrieve the current refs from a git smart server."""
+ from dulwich.repo import Repo
+
+ with closing(Repo(path)) as target:
+ return target.get_refs()
+
# What Git client to use for local access
-default_local_git_client_cls = SubprocessGitClient
+default_local_git_client_cls = LocalGitClient
class SSHVendor(object):
with the remote command.
:param host: Host name
- :param command: Command to run
+ :param command: Command to run (as argv array)
:param username: Optional ame of user to log in as
:param port: Optional SSH port to use
"""
"""SSH vendor that shells out to the local 'ssh' command."""
def run_command(self, host, command, username=None, port=None):
+ if (type(command) is not list or
+ not all([isinstance(b, bytes) for b in command])):
+ raise TypeError(command)
+
import subprocess
#FIXME: This has no way to deal with passwords..
args = ['ssh', '-x']
def run_command(self, host, command, username=None, port=None,
progress_stderr=None):
-
+ if (type(command) is not list or
+ not all([isinstance(b, bytes) for b in command])):
+ raise TypeError(command)
# Paramiko needs an explicit port. None is not valid
if port is None:
port = 22
channel = client.get_transport().open_session()
# Run commands
- channel.exec_command(*command)
+ channel.exec_command(subprocess.list2cmdline(command))
return ParamikoWrapper(
client, channel, progress_stderr=progress_stderr)
self.alternative_paths = {}
def _get_cmd_path(self, cmd):
- return self.alternative_paths.get(cmd, b'git-' + cmd)
+ cmd = self.alternative_paths.get(cmd, b'git-' + cmd)
+ assert isinstance(cmd, bytes)
+ if sys.version_info[:2] <= (2, 6):
+ return shlex.split(cmd)
+ else:
+ # TODO(jelmer): Don't decode/encode here
+ return [x.encode('ascii') for x in shlex.split(cmd.decode('ascii'))]
def _connect(self, cmd, path):
+ if type(cmd) is not bytes:
+ raise TypeError(path)
+ if type(path) is not bytes:
+ raise TypeError(path)
if path.startswith(b"/~"):
path = path[1:]
+ argv = self._get_cmd_path(cmd) + [path]
con = get_ssh_vendor().run_command(
- self.host, [self._get_cmd_path(cmd) + b" '" + path + b"'"],
- port=self.port, username=self.username)
+ self.host, argv, port=self.port, username=self.username)
return (Protocol(con.read, con.write, con.close,
report_activity=self._report_activity),
con.can_read)
finally:
resp.close()
+ def get_refs(self, path):
+ """Retrieve the current refs from a git smart server."""
+ url = self._get_url(path)
+ refs, _ = self._discover_references(
+ b"git-upload-pack", url)
+ return refs
+
def get_transport_and_path_from_url(url, config=None, **kwargs):
"""Obtain a git client from a URL.