123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- import SocketServer
- class Backend(object):
- def get_refs(self):
- """
- Get all the refs in the repository
- :return: list of tuple(name, sha)
- """
- raise NotImplementedError
- def has_revision(self, sha):
- """
- Is a given sha in this repository?
- :return: True or False
- """
- raise NotImplementedError
- def apply_pack(self, refs, read):
- """ Import a set of changes into a repository and update the refs
- :param refs: list of tuple(name, sha)
- :param read: callback to read from the incoming pack
- """
- raise NotImplementedError
- def generate_pack(self, want, have, write, progress):
- """
- Generate a pack containing all commits a client is missing
- :param want: is a list of sha's the client desires
- :param have: is a list of sha's the client has (allowing us to send the minimal pack)
- :param write: is a callback to write pack data to the client
- :param progress: is a callback to send progress messages to the client
- """
- 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 'pkt line' from the remote git process
- :return: The next string from the stream
- """
- sizestr = self.read(4)
- if not sizestr:
- return None
- size = int(sizestr, 16)
- if size == 0:
- return None
- return self.read(size-4)
- def write_pkt_line(self, line):
- """
- Sends a 'pkt line' to the remote git process
- :param line: A string containing the data to send
- """
- self.write("%04x%s" % (len(line)+4, line))
- def write_sideband(self, channel, blob):
- """
- Write data to the sideband (a git multiplexing method)
- :param channel: int specifying which channel to write to
- :param blob: a blob of data (as a string) to send on this channel
- """
-
-
-
- while blob:
- self.write_pkt_line("%s%s" % (chr(channel), blob[:65530]))
- blob = blob[65530:]
- def handle(self):
- """
- Deal with the request
- """
- raise NotImplementedError
- class UploadPackHandler(Handler):
- def handle(self):
- refs = self.backend.get_refs()
- if refs:
- self.write_pkt_line("%s %s\x00multi_ack side-band-64k thin-pack ofs-delta\n" % (refs[0][1], refs[0][0]))
- for i in range(1, len(refs)):
- ref = refs[i]
- self.write_pkt_line("%s %s\n" % (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:40]
-
- 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()
- if refs:
- self.write_pkt_line("%s %s\x00multi_ack side-band-64k thin-pack ofs-delta\n" % (refs[0][1], refs[0][0]))
- for i in range(1, len(refs)):
- ref = refs[i]
- self.write_pkt_line("%s %s\n" % (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()
- if len(client_refs) == 0:
- return None
- 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)
|