|
@@ -0,0 +1,198 @@
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+import SocketServer
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+class Backend(object):
|
|
|
+
|
|
|
+ def __init__(self):
|
|
|
+ pass
|
|
|
+
|
|
|
+ def get_refs(self):
|
|
|
+ raise NotImplementedError
|
|
|
+
|
|
|
+ def has_revision(self):
|
|
|
+ raise NotImplementedError
|
|
|
+
|
|
|
+ def apply_pack(self, refs, read):
|
|
|
+ raise NotImplementedError
|
|
|
+
|
|
|
+ def generate_pack(self, want, have, write, progress):
|
|
|
+ raise NotImplementedError
|
|
|
+
|
|
|
+
|
|
|
+class Handler(object):
|
|
|
+
|
|
|
+ def __init__(self, backend, read, write):
|
|
|
+ self.backend = backend
|
|
|
+ self.read = read
|
|
|
+ self.write = write
|
|
|
+
|
|
|
+ def read_pkt_line(self):
|
|
|
+ """
|
|
|
+ reads a 'git line' of info from the stream
|
|
|
+ """
|
|
|
+ size = int(self.read(4), 16)
|
|
|
+ if size == 0:
|
|
|
+ return None
|
|
|
+ return self.read(size-4)
|
|
|
+
|
|
|
+ def write_pkt_line(self, line):
|
|
|
+ self.write("%04x%s" % (len(line)+4, line))
|
|
|
+
|
|
|
+ def write_sideband(self, channel, blob):
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ while blob:
|
|
|
+ self.write_pkt_line("%s%s" % (chr(channel), blob[:65530]))
|
|
|
+ blob = blob[65530:]
|
|
|
+
|
|
|
+ def handle(self):
|
|
|
+ raise NotImplementedError
|
|
|
+
|
|
|
+
|
|
|
+class UploadPackHandler(Handler):
|
|
|
+
|
|
|
+ def handle(self):
|
|
|
+ refs = self.backend.get_refs()
|
|
|
+
|
|
|
+ self.write_pkt_line("%s %s\x00multi_ack side-band-64k thin-pack ofs-delta\0x0a" % (refs[0][1], refs[0][0]))
|
|
|
+ for i in range(1, len(refs)-1):
|
|
|
+ ref = refs[i]
|
|
|
+ self.write_pkt_line("%s %s\0x0a" % (ref[1], ref[0]))
|
|
|
+
|
|
|
+
|
|
|
+ self.write("0000")
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ want = self.read_pkt_line()
|
|
|
+ if want == None:
|
|
|
+ return
|
|
|
+
|
|
|
+
|
|
|
+ want_revs = []
|
|
|
+ while want and want[:4] == 'want':
|
|
|
+ want_rev = want[5:]
|
|
|
+
|
|
|
+ if self.backend.has_revision(want_rev):
|
|
|
+ want_revs.append(want_rev)
|
|
|
+ want = self.read_pkt_line()
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ last_sha = None
|
|
|
+ have_revs = []
|
|
|
+ have = self.read_pkt_line()
|
|
|
+ while have and have[:4] == 'have':
|
|
|
+ have_ref = have[6:40]
|
|
|
+ if self.backend.has_revision(hav_rev):
|
|
|
+ self.write_pkt_line("ACK %s continue\n" % sha)
|
|
|
+ last_sha = sha
|
|
|
+ have_revs.append(rev_id)
|
|
|
+ have = self.read_pkt_line()
|
|
|
+
|
|
|
+
|
|
|
+ assert(have[:4] == "done")
|
|
|
+
|
|
|
+
|
|
|
+ if last_sha:
|
|
|
+ self.write_pkt_line("ACK %s\n" % last_sha)
|
|
|
+
|
|
|
+
|
|
|
+ self.write_pkt_line("NAK\n")
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ self.backend.generate_pack(want_revs, have_revs, self.write, None)
|
|
|
+
|
|
|
+
|
|
|
+class ReceivePackHandler(Handler):
|
|
|
+
|
|
|
+ def handle(self):
|
|
|
+ refs = self.backend.get_refs()
|
|
|
+
|
|
|
+ self.write_pkt_line("%s %s\x00multi_ack side-band-64k thin-pack ofs-delta\0x0a" % (refs[0][1], refs[0][0]))
|
|
|
+ for i in range(1, len(refs)-1):
|
|
|
+ ref = refs[i]
|
|
|
+ self.write_pkt_line("%s %s\0x0a" % (ref[1], ref[0]))
|
|
|
+
|
|
|
+ self.write("0000")
|
|
|
+
|
|
|
+ client_refs = []
|
|
|
+ ref = self.read_pkt_line()
|
|
|
+ while ref:
|
|
|
+ client_refs.append(ref.split())
|
|
|
+ ref = self.read_pkt_line()
|
|
|
+
|
|
|
+ self.backend.apply_pack(client_refs, self.read)
|
|
|
+
|
|
|
+
|
|
|
+class TCPGitRequestHandler(SocketServer.StreamRequestHandler, Handler):
|
|
|
+
|
|
|
+ def __init__(self, request, client_address, server):
|
|
|
+ SocketServer.StreamRequestHandler.__init__(self, request, client_address, server)
|
|
|
+
|
|
|
+ def handle(self):
|
|
|
+
|
|
|
+
|
|
|
+ Handler.__init__(self, self.server.backend, self.rfile.read, self.wfile.write)
|
|
|
+
|
|
|
+ request = self.read_pkt_line()
|
|
|
+
|
|
|
+
|
|
|
+ splice_point = request.find(' ')
|
|
|
+ command, params = request[:splice_point], request[splice_point+1:]
|
|
|
+
|
|
|
+
|
|
|
+ params = params.split(chr(0))
|
|
|
+
|
|
|
+
|
|
|
+ if command == 'git-upload-pack':
|
|
|
+ cls = UploadPackHandler
|
|
|
+ elif command == 'git-receive-pack':
|
|
|
+ cls = ReceivePackHandler
|
|
|
+ else:
|
|
|
+ return
|
|
|
+
|
|
|
+ h = cls(self.backend, self.read, self.write)
|
|
|
+ h.handle()
|
|
|
+
|
|
|
+
|
|
|
+class TCPGitServer(SocketServer.TCPServer):
|
|
|
+
|
|
|
+ allow_reuse_address = True
|
|
|
+ serve = SocketServer.TCPServer.serve_forever
|
|
|
+
|
|
|
+ def __init__(self, backend, addr):
|
|
|
+ self.backend = backend
|
|
|
+ SocketServer.TCPServer.__init__(self, addr, TCPGitRequestHandler)
|
|
|
+
|
|
|
+
|