def _discover_references(self, service, url): assert url[-1] == "/" url = urlparse.urljoin(url, "info/refs") headers = {} if self.dumb is not False: url += "?service=%s" % service.decode('ascii') headers["Content-Type"] = "application/x-%s-request" % ( service.decode('ascii')) resp = self._http_request(url, headers) try: content_type = resp.info().gettype() except AttributeError: content_type = resp.info().get_content_type() try: self.dumb = (not content_type.startswith("application/x-git-")) if not self.dumb: proto = Protocol(resp.read, None) # The first line should mention the service pkts = list(proto.read_pkt_seq()) if pkts != [b'# service=' + service + b'\n']: raise GitProtocolError( "unexpected first line %r from smart server" % pkts) return read_pkt_refs(proto) else: return read_info_refs(resp), set() finally: resp.close()
def _discover_references(self, service, url): assert url[-1] == "/" url = urlparse.urljoin(url, "info/refs") headers = {} if self.dumb is not False: url += "?service=%s" % service.decode('ascii') headers["Content-Type"] = "application/x-%s-request" % ( service.decode('ascii')) resp = self._http_request(url, headers) try: content_type = resp.info().gettype() except AttributeError: content_type = resp.info().get_content_type() try: self.dumb = (not content_type.startswith("application/x-git-")) if not self.dumb: proto = Protocol(resp.read, None) # The first line should mention the service try: [pkt] = list(proto.read_pkt_seq()) except ValueError: raise GitProtocolError( "unexpected number of packets received") if pkt.rstrip(b'\n') != (b'# service=' + service): raise GitProtocolError( "unexpected first line %r from smart server" % pkt) return read_pkt_refs(proto) else: return read_info_refs(resp), set() finally: resp.close()
def _discover_references(self, service, url): assert url[-1] == "/" url = urlparse.urljoin(url, "info/refs") headers = {} if self.dumb != False: url += "?service=%s" % service headers["Content-Type"] = "application/x-%s-request" % service resp = self._http_request(url, headers) self.dumb = not resp.info().gettype().startswith("application/x-git-") proto = Protocol(resp.read, None) if not self.dumb: # The first line should mention the service pkts = list(proto.read_pkt_seq()) if pkts != [("# service=%s\n" % service)]: raise GitProtocolError("unexpected first line %r from smart server" % pkts) return self._read_refs(proto)
class ProtocolTests(TestCase): def setUp(self): self.rout = StringIO() self.rin = StringIO() self.proto = Protocol(self.rin.read, self.rout.write) def test_write_pkt_line_none(self): self.proto.write_pkt_line(None) self.assertEquals(self.rout.getvalue(), "0000") def test_write_pkt_line(self): self.proto.write_pkt_line("bla") self.assertEquals(self.rout.getvalue(), "0007bla") def test_read_pkt_line(self): self.rin.write("0008cmd ") self.rin.seek(0) self.assertEquals("cmd ", self.proto.read_pkt_line()) def test_read_pkt_seq(self): self.rin.write("0008cmd 0005l0000") self.rin.seek(0) self.assertEquals(["cmd ", "l"], list(self.proto.read_pkt_seq())) def test_read_pkt_line_none(self): self.rin.write("0000") self.rin.seek(0) self.assertEquals(None, self.proto.read_pkt_line()) def test_write_sideband(self): self.proto.write_sideband(3, "bloe") self.assertEquals(self.rout.getvalue(), "0009\x03bloe") def test_send_cmd(self): self.proto.send_cmd("fetch", "a", "b") self.assertEquals(self.rout.getvalue(), "000efetch a\x00b\x00") def test_read_cmd(self): self.rin.write("0012cmd arg1\x00arg2\x00") self.rin.seek(0) self.assertEquals(("cmd", ["arg1", "arg2"]), self.proto.read_cmd()) def test_read_cmd_noend0(self): self.rin.write("0011cmd arg1\x00arg2") self.rin.seek(0) self.assertRaises(AssertionError, self.proto.read_cmd)
def _discover_references(self, service, url): assert url[-1] == "/" url = urlparse.urljoin(url, "info/refs") headers = {} if self.dumb != False: url += "?service=%s" % service headers["Content-Type"] = "application/x-%s-request" % service resp = self._http_request(url, headers) self.dumb = (not resp.info().gettype().startswith("application/x-git-")) proto = Protocol(resp.read, None) if not self.dumb: # The first line should mention the service pkts = list(proto.read_pkt_seq()) if pkts != [('# service=%s\n' % service)]: raise GitProtocolError( "unexpected first line %r from smart server" % pkts) return self._read_refs(proto)
def _discover_references(self, service, url): url = urlparse.urljoin(url+"/", "info/refs") if not self.dumb: url += "?service=%s" % service req = urllib2.Request(url) resp = self._perform(req) if resp.getcode() == 404: raise NotGitRepository() if resp.getcode() != 200: raise GitProtocolError("unexpected http response %d" % resp.getcode()) self.dumb = (not resp.info().gettype().startswith("application/x-git-")) proto = Protocol(resp.read, None) if not self.dumb: # The first line should mention the service pkts = list(proto.read_pkt_seq()) if pkts != [('# service=%s\n' % service)]: raise GitProtocolError( "unexpected first line %r from smart server" % pkts) return self._read_refs(proto)
def _discover_references(self, service, url): assert url[-1] == "/" url = urllib.parse.urljoin(url, "info/refs") headers = {} if self.dumb != False: url += "?service=%s" % service headers["Content-Type"] = "application/x-%s-request" % service req = urllib.request.Request(url, headers=headers) resp = self._perform(req) if resp.getcode() == 404: raise NotGitRepository() if resp.getcode() != 200: raise GitProtocolError("unexpected http response %d" % resp.getcode()) self.dumb = (not resp.info().get_content_type().startswith("application/x-git-")) proto = Protocol(resp.read, None, resp.close) if not self.dumb: # The first line should mention the service pkts = list(proto.read_pkt_seq()) if pkts != [(('# service=%s\n' % service).encode('utf-8'))]: raise GitProtocolError( "unexpected first line %r from smart server" % pkts) return self._read_refs(proto)
class GitClient(object): """Git smart server client. """ def __init__(self, can_read, read, write, thin_packs=True, report_activity=None): """Create a new GitClient instance. :param can_read: Function that returns True if there is data available to be read. :param read: Callback for reading data, takes number of bytes to read :param write: Callback for writing data :param thin_packs: Whether or not thin packs should be retrieved :param report_activity: Optional callback for reporting transport activity. """ self.proto = Protocol(read, write, report_activity) self._can_read = can_read self._capabilities = list(CAPABILITIES) if thin_packs: self._capabilities.append("thin-pack") def capabilities(self): return " ".join(self._capabilities) def read_refs(self): server_capabilities = None refs = {} # Receive refs from server for pkt in self.proto.read_pkt_seq(): (sha, ref) = pkt.rstrip("\n").split(" ", 1) if server_capabilities is None: (ref, server_capabilities) = extract_capabilities(ref) refs[ref] = sha return refs, server_capabilities def send_pack(self, path, determine_wants, generate_pack_contents): """Upload a pack to a remote repository. :param path: Repository path :param generate_pack_contents: Function that can return the shas of the objects to upload. """ old_refs, server_capabilities = self.read_refs() new_refs = determine_wants(old_refs) if not new_refs: self.proto.write_pkt_line(None) return {} want = [] have = [x for x in old_refs.values() if not x == "0" * 40] sent_capabilities = False for refname in set(new_refs.keys() + old_refs.keys()): old_sha1 = old_refs.get(refname, "0" * 40) new_sha1 = new_refs.get(refname, "0" * 40) if old_sha1 != new_sha1: if sent_capabilities: self.proto.write_pkt_line("%s %s %s" % (old_sha1, new_sha1, refname)) else: self.proto.write_pkt_line("%s %s %s\0%s" % (old_sha1, new_sha1, refname, self.capabilities())) sent_capabilities = True if not new_sha1 in (have, "0" * 40): want.append(new_sha1) self.proto.write_pkt_line(None) if not want: return new_refs objects = generate_pack_contents(have, want) (entries, sha) = write_pack_data(self.proto.write_file(), objects, len(objects)) # read the final confirmation sha client_sha = self.proto.read(20) if not client_sha in (None, "", sha): raise ChecksumMismatch(sha, client_sha) return new_refs def fetch_pack(self, path, determine_wants, graph_walker, pack_data, progress): """Retrieve a pack from a git smart server. :param determine_wants: Callback that returns list of commits to fetch :param graph_walker: Object with next() and ack(). :param pack_data: Callback called for each bit of data in the pack :param progress: Callback for progress reports (strings) """ (refs, server_capabilities) = self.read_refs() wants = determine_wants(refs) if not wants: self.proto.write_pkt_line(None) return refs assert isinstance(wants, list) and type(wants[0]) == str self.proto.write_pkt_line("want %s %s\n" % (wants[0], self.capabilities())) for want in wants[1:]: self.proto.write_pkt_line("want %s\n" % want) self.proto.write_pkt_line(None) have = graph_walker.next() while have: self.proto.write_pkt_line("have %s\n" % have) if self._can_read(): pkt = self.proto.read_pkt_line() parts = pkt.rstrip("\n").split(" ") if parts[0] == "ACK": graph_walker.ack(parts[1]) assert parts[2] == "continue" have = graph_walker.next() self.proto.write_pkt_line("done\n") pkt = self.proto.read_pkt_line() while pkt: parts = pkt.rstrip("\n").split(" ") if parts[0] == "ACK": graph_walker.ack(pkt.split(" ")[1]) if len(parts) < 3 or parts[2] != "continue": break pkt = self.proto.read_pkt_line() for pkt in self.proto.read_pkt_seq(): channel = ord(pkt[0]) pkt = pkt[1:] if channel == 1: pack_data(pkt) elif channel == 2: progress(pkt) else: raise AssertionError("Invalid sideband channel %d" % channel) return refs
class GitClient(object): """Git smart server client. """ def __init__(self, can_read, read, write, thin_packs=True, report_activity=None): """Create a new GitClient instance. :param can_read: Function that returns True if there is data available to be read. :param read: Callback for reading data, takes number of bytes to read :param write: Callback for writing data :param thin_packs: Whether or not thin packs should be retrieved :param report_activity: Optional callback for reporting transport activity. """ self.proto = Protocol(read, write, report_activity) self._can_read = can_read self._capabilities = list(CAPABILITIES) if thin_packs: self._capabilities.append("thin-pack") def capabilities(self): return " ".join(self._capabilities) def read_refs(self): server_capabilities = None refs = {} # Receive refs from server for pkt in self.proto.read_pkt_seq(): (sha, ref) = pkt.rstrip("\n").split(" ", 1) if server_capabilities is None: (ref, server_capabilities) = extract_capabilities(ref) refs[ref] = sha return refs, server_capabilities def send_pack(self, path, determine_wants, generate_pack_contents): """Upload a pack to a remote repository. :param path: Repository path :param generate_pack_contents: Function that can return the shas of the objects to upload. """ old_refs, server_capabilities = self.read_refs() new_refs = determine_wants(old_refs) if not new_refs: self.proto.write_pkt_line(None) return {} want = [] have = [x for x in old_refs.values() if not x == "0" * 40] sent_capabilities = False for refname in set(new_refs.keys() + old_refs.keys()): old_sha1 = old_refs.get(refname, "0" * 40) new_sha1 = new_refs.get(refname, "0" * 40) if old_sha1 != new_sha1: if sent_capabilities: self.proto.write_pkt_line("%s %s %s" % (old_sha1, new_sha1, refname)) else: self.proto.write_pkt_line( "%s %s %s\0%s" % (old_sha1, new_sha1, refname, self.capabilities())) sent_capabilities = True if not new_sha1 in (have, "0" * 40): want.append(new_sha1) self.proto.write_pkt_line(None) if not want: return new_refs objects = generate_pack_contents(have, want) (entries, sha) = write_pack_data(self.proto.write_file(), objects, len(objects)) # read the final confirmation sha client_sha = self.proto.read(20) if not client_sha in (None, "", sha): raise ChecksumMismatch(sha, client_sha) return new_refs def fetch_pack(self, path, determine_wants, graph_walker, pack_data, progress): """Retrieve a pack from a git smart server. :param determine_wants: Callback that returns list of commits to fetch :param graph_walker: Object with next() and ack(). :param pack_data: Callback called for each bit of data in the pack :param progress: Callback for progress reports (strings) """ (refs, server_capabilities) = self.read_refs() wants = determine_wants(refs) if not wants: self.proto.write_pkt_line(None) return refs assert isinstance(wants, list) and type(wants[0]) == str self.proto.write_pkt_line("want %s %s\n" % (wants[0], self.capabilities())) for want in wants[1:]: self.proto.write_pkt_line("want %s\n" % want) self.proto.write_pkt_line(None) have = graph_walker.next() while have: self.proto.write_pkt_line("have %s\n" % have) if self._can_read(): pkt = self.proto.read_pkt_line() parts = pkt.rstrip("\n").split(" ") if parts[0] == "ACK": graph_walker.ack(parts[1]) assert parts[2] == "continue" have = graph_walker.next() self.proto.write_pkt_line("done\n") pkt = self.proto.read_pkt_line() while pkt: parts = pkt.rstrip("\n").split(" ") if parts[0] == "ACK": graph_walker.ack(pkt.split(" ")[1]) if len(parts) < 3 or parts[2] != "continue": break pkt = self.proto.read_pkt_line() for pkt in self.proto.read_pkt_seq(): channel = ord(pkt[0]) pkt = pkt[1:] if channel == 1: pack_data(pkt) elif channel == 2: progress(pkt) else: raise AssertionError("Invalid sideband channel %d" % channel) return refs
class GitClient(object): """Git smart server client. """ def __init__(self, fileno, read, write): self.proto = Protocol(read, write) self.fileno = fileno def capabilities(self): return "multi_ack side-band-64k thin-pack ofs-delta" def read_refs(self): server_capabilities = None refs = {} # Receive refs from server for pkt in self.proto.read_pkt_seq(): (sha, ref) = pkt.rstrip("\n").split(" ", 1) if server_capabilities is None: (ref, server_capabilities) = extract_capabilities(ref) if not (ref == "capabilities^{}" and sha == "0" * 40): refs[ref] = sha return refs, server_capabilities def send_pack(self, path): refs, server_capabilities = self.read_refs() changed_refs = [] # FIXME if not changed_refs: self.proto.write_pkt_line(None) return self.proto.write_pkt_line("%s %s %s\0%s" % (changed_refs[0][0], changed_refs[0][1], changed_refs[0][2], self.capabilities())) want = [] have = [] for changed_ref in changed_refs[:]: self.proto.write_pkt_line("%s %s %s" % changed_refs) want.append(changed_refs[1]) if changed_refs[0] != "0"*40: have.append(changed_refs[0]) self.proto.write_pkt_line(None) # FIXME: This is implementation specific # shas = generate_pack_contents(want, have, None) # write_pack_data(self.write, shas, len(shas)) def fetch_pack(self, path, determine_wants, graph_walker, pack_data, progress): """Retrieve a pack from a git smart server. :param determine_wants: Callback that returns list of commits to fetch :param graph_walker: Object with next() and ack(). :param pack_data: Callback called for each bit of data in the pack :param progress: Callback for progress reports (strings) """ (refs, server_capabilities) = self.read_refs() wants = determine_wants(refs) if not wants: self.proto.write_pkt_line(None) return self.proto.write_pkt_line("want %s %s\n" % (wants[0], self.capabilities())) for want in wants[1:]: self.proto.write_pkt_line("want %s\n" % want) self.proto.write_pkt_line(None) have = graph_walker.next() while have: self.proto.write_pkt_line("have %s\n" % have) if len(select.select([self.fileno], [], [], 0)[0]) > 0: pkt = self.proto.read_pkt_line() parts = pkt.rstrip("\n").split(" ") if parts[0] == "ACK": graph_walker.ack(parts[1]) assert parts[2] == "continue" have = graph_walker.next() self.proto.write_pkt_line("done\n") pkt = self.proto.read_pkt_line() while pkt: parts = pkt.rstrip("\n").split(" ") if parts[0] == "ACK": graph_walker.ack(pkt.split(" ")[1]) if len(parts) < 3 or parts[2] != "continue": break pkt = self.proto.read_pkt_line() for pkt in self.proto.read_pkt_seq(): channel = ord(pkt[0]) pkt = pkt[1:] if channel == 1: pack_data(pkt) elif channel == 2: progress(pkt) else: raise AssertionError("Invalid sideband channel %d" % channel)
class GitClient(object): """Git smart server client. """ def __init__(self, fileno, read, write): self.proto = Protocol(read, write) self.fileno = fileno def capabilities(self): return "multi_ack side-band-64k thin-pack ofs-delta" def read_refs(self): server_capabilities = None refs = {} # Receive refs from server for pkt in self.proto.read_pkt_seq(): (sha, ref) = pkt.rstrip("\n").split(" ", 1) if server_capabilities is None: (ref, server_capabilities) = extract_capabilities(ref) if not (ref == "capabilities^{}" and sha == "0" * 40): refs[ref] = sha return refs, server_capabilities def send_pack(self, path): refs, server_capabilities = self.read_refs() changed_refs = [] # FIXME if not changed_refs: self.proto.write_pkt_line(None) return self.proto.write_pkt_line("%s %s %s\0%s" % (changed_refs[0][0], changed_refs[0][1], changed_refs[0][2], self.capabilities())) want = [] have = [] for changed_ref in changed_refs[:]: self.proto.write_pkt_line("%s %s %s" % changed_refs) want.append(changed_refs[1]) if changed_refs[0] != "0" * 40: have.append(changed_refs[0]) self.proto.write_pkt_line(None) # FIXME: This is implementation specific # shas = generate_pack_contents(want, have, None) # write_pack_data(self.write, shas, len(shas)) def fetch_pack(self, path, determine_wants, graph_walker, pack_data, progress): """Retrieve a pack from a git smart server. :param determine_wants: Callback that returns list of commits to fetch :param graph_walker: Object with next() and ack(). :param pack_data: Callback called for each bit of data in the pack :param progress: Callback for progress reports (strings) """ (refs, server_capabilities) = self.read_refs() wants = determine_wants(refs) if not wants: self.proto.write_pkt_line(None) return self.proto.write_pkt_line("want %s %s\n" % (wants[0], self.capabilities())) for want in wants[1:]: self.proto.write_pkt_line("want %s\n" % want) self.proto.write_pkt_line(None) have = graph_walker.next() while have: self.proto.write_pkt_line("have %s\n" % have) if len(select.select([self.fileno], [], [], 0)[0]) > 0: pkt = self.proto.read_pkt_line() parts = pkt.rstrip("\n").split(" ") if parts[0] == "ACK": graph_walker.ack(parts[1]) assert parts[2] == "continue" have = graph_walker.next() self.proto.write_pkt_line("done\n") pkt = self.proto.read_pkt_line() while pkt: parts = pkt.rstrip("\n").split(" ") if parts[0] == "ACK": graph_walker.ack(pkt.split(" ")[1]) if len(parts) < 3 or parts[2] != "continue": break pkt = self.proto.read_pkt_line() for pkt in self.proto.read_pkt_seq(): channel = ord(pkt[0]) pkt = pkt[1:] if channel == 1: pack_data(pkt) elif channel == 2: progress(pkt) else: raise AssertionError("Invalid sideband channel %d" % channel)