func (pk *PackReader) extractAt(off int64) (typ int, data []byte, err error) { var buf [16]byte // 109-bit sizes should be enough for everybody. _, err = pk.pack.ReadAt(buf[:], off) if err == io.EOF || err == io.ErrUnexpectedEOF { err = nil } if err != nil { return } varint, n := binary.Uvarint(buf[:]) objsize := int64((varint>>7)<<4 | (varint & 0xf)) objtype := int(varint>>4) & 0x7 // 3 bits. switch objtype { case pkCommit, pkTree, pkBlob, pkTag: // objsize is the *uncompressed* size. data = make([]byte, objsize) n, err := readCompressed(pk.pack, off+int64(n), data) return objtype, data[:n], err case pkRefDelta: // Ref delta: parent hash (20 bytes) + deflated delta (objsize bytes) var parent Hash _, err := pk.pack.ReadAt(parent[:], off+int64(n)) if err != nil { return typ, data, err } patch := make([]byte, objsize) _, err = readCompressed(pk.pack, off+int64(n)+20, patch) // FIXME: check that parent object is always in the same pack. typ, data, err = pk.extract(parent) if err != nil { return typ, patch, err } data, err = gitdelta.Patch(data, patch) if err != nil { return typ, patch, err } return typ, data, err case pkOfsDelta: // Offset delta: distance to parent (varint bytes) + deflated delta (objsize bytes) parentOff, n2, err := readVaroffset(pk.pack, off+int64(n)) if err != nil { return objtype, data, err } patch := make([]byte, objsize) _, err = readCompressed(pk.pack, off+int64(n+n2), patch) typ, data, err = pk.extractAt(off - parentOff) if err != nil { return typ, patch, err } data, err = gitdelta.Patch(data, patch) if err != nil { return typ, patch, err } return typ, data, err } return typ, data, errInvalidPackEntryType }
// Patch applies a diff on a revision and returns a new revision patched to the // latest version. // // TODO NOTE TODO NOTE This function will not be compleeted until the Revision // struct is moved inside shared application code so that the client // application can use it. func (r *Revision) Patch(diff []byte) *Revision { oldbytes := r.bytes() patched, err := gitdelta.Patch(oldbytes, diff) if err != nil { panic(err) } content := make([]string, 0) reader := bytes.NewReader(patched) scanner := bufio.NewScanner(reader) for scanner.Scan() { item := scanner.Text() if item != "" { content = append(content, item) } } if err := scanner.Err(); err != nil { panic(err) } return NewRevision(content) }