package git import ( "bufio" "fmt" "io" "strconv" "strings" ) type PacketReader struct { reader *bufio.Reader } func NewPacketReader(r io.Reader) *PacketReader { return &PacketReader{ reader: bufio.NewReader(r), } } func (pr *PacketReader) ReadPacket() ([]byte, error) { // Read the 4-byte length prefix lengthBytes := make([]byte, 4) _, err := io.ReadFull(pr.reader, lengthBytes) if err != nil { return nil, err } lengthStr := string(lengthBytes) if lengthStr == "0000" { return nil, nil // Flush packet } length, err := strconv.ParseInt(lengthStr, 16, 32) if err != nil { return nil, fmt.Errorf("invalid packet length: %s", lengthStr) } if length < 4 { return nil, fmt.Errorf("packet length too short: %d", length) } // Read the packet data (length includes the 4-byte prefix) data := make([]byte, length-4) _, err = io.ReadFull(pr.reader, data) if err != nil { return nil, err } return data, nil } type PacketWriter struct { writer io.Writer } func NewPacketWriter(w io.Writer) *PacketWriter { return &PacketWriter{writer: w} } func (pw *PacketWriter) WritePacket(data []byte) error { length := len(data) + 4 _, err := fmt.Fprintf(pw.writer, "%04x%s", length, data) return err } func (pw *PacketWriter) WriteFlush() error { _, err := pw.writer.Write([]byte("0000")) return err } type UploadPackRequest struct { Wants []string Haves []string } func ParseUploadPackRequest(r io.Reader) (*UploadPackRequest, error) { pr := NewPacketReader(r) req := &UploadPackRequest{} for { packet, err := pr.ReadPacket() if err != nil { return nil, err } if packet == nil { break // Flush packet } line := strings.TrimSpace(string(packet)) if strings.HasPrefix(line, "want ") { sha := strings.Fields(line)[1] req.Wants = append(req.Wants, sha) } else if strings.HasPrefix(line, "have ") { sha := strings.Fields(line)[1] req.Haves = append(req.Haves, sha) } } return req, nil }