diff options
| author | 2026-01-30 17:06:51 +0100 | |
|---|---|---|
| committer | 2026-01-30 17:06:51 +0100 | |
| commit | 8e320c9ca634e6b2431f9442b7d5191864735ae4 (patch) | |
| tree | b2e3d18144865fad0508a4f890dada5aee6ba940 /packed_write_pack.go | |
| parent | reachability: Add basic reachability API (diff) | |
| signature | No signature | |
packed, delta: Implement thin packs
Diffstat (limited to 'packed_write_pack.go')
| -rw-r--r-- | packed_write_pack.go | 102 |
1 files changed, 93 insertions, 9 deletions
diff --git a/packed_write_pack.go b/packed_write_pack.go index 329e2a7f..1d8cbc1e 100644 --- a/packed_write_pack.go +++ b/packed_write_pack.go @@ -283,11 +283,39 @@ func packOfsEncode(dist uint64) ([]byte, error) { // packWrite writes a pack stream for the provided object ids. func (repo *Repository) packWrite(w io.Writer, objects []Hash, opts packWriteOptions) (Hash, error) { + if opts.EnableThinPack { + return Hash{}, errThinPackUnimplemented + } + return repo.packWriteObjects(w, objects, opts, nil) +} + +// packWriteReachable writes a pack stream for objects reachable from the +// provided reachability query. +func (repo *Repository) packWriteReachable(w io.Writer, query ReachabilityQuery, opts packWriteOptions) (Hash, error) { if repo == nil { return Hash{}, ErrInvalidObject } - if opts.EnableThinPack { - return Hash{}, errThinPackUnimplemented + query.Mode = ReachabilityAllObjects + walk, err := repo.ReachableObjects(query) + if err != nil { + return Hash{}, err + } + var objects []Hash + for obj := range walk.Seq() { + objects = append(objects, obj.ID) + } + if err := walk.Err(); err != nil { + return Hash{}, err + } + return repo.packWriteObjects(w, objects, opts, walk) +} + +func (repo *Repository) packWriteObjects(w io.Writer, objects []Hash, opts packWriteOptions, have *ReachabilityWalk) (Hash, error) { + if repo == nil { + return Hash{}, ErrInvalidObject + } + if opts.EnableThinPack && have == nil { + return Hash{}, ErrInvalidObject } if len(objects) > int(^uint32(0)) { return Hash{}, ErrInvalidObject @@ -312,15 +340,22 @@ func (repo *Repository) packWrite(w io.Writer, objects []Hash, opts packWriteOpt deltaSeed = binary.LittleEndian.Uint64(seedBytes[:]) } + if opts.EnableDeltas && opts.EnableThinPack { + if err := repo.seedDeltaCandidatesFromHaves(&dctx, have.query.Haves); err != nil { + return Hash{}, err + } + } + for _, id := range objects { ty, body, err := repo.ReadObjectTypeRaw(id) if err != nil { return Hash{}, err } obj := &objectToPack{ - id: id, - ty: ty, - body: body, + id: id, + ty: ty, + body: body, + inPack: true, } startOffset := pw.bytesWritten wroteDelta := false @@ -328,11 +363,27 @@ func (repo *Repository) packWrite(w io.Writer, objects []Hash, opts packWriteOpt if opts.EnableDeltas && ty == ObjectTypeBlob { base, delta := pickDeltaBase(&dctx, obj, deltaSeed, opts.MinDeltaSavings, opts.MaxDeltaDepth) if base != nil && delta != nil { - if err := pw.WriteOfsDelta(base.offset, len(base.body), len(body), delta); err != nil { - return Hash{}, err + switch { + case base.inPack: + if err := pw.WriteOfsDelta(base.offset, len(base.body), len(body), delta); err != nil { + return Hash{}, err + } + wroteDelta = true + obj.deltaDepth = base.deltaDepth + 1 + case opts.EnableThinPack: + inHave, err := have.HaveContains(base.id) + if err != nil { + return Hash{}, err + } + if inHave { + if err := pw.WriteRefDelta(base.id, len(base.body), len(body), delta); err != nil { + return Hash{}, err + } + wroteDelta = true + obj.deltaDepth = base.deltaDepth + 1 + } + default: } - wroteDelta = true - obj.deltaDepth = base.deltaDepth + 1 } } if !wroteDelta { @@ -351,6 +402,39 @@ func (repo *Repository) packWrite(w io.Writer, objects []Hash, opts packWriteOpt return pw.Close() } +func (repo *Repository) seedDeltaCandidatesFromHaves(ctx *deltaContext, haves []Hash) error { + if repo == nil { + return ErrInvalidObject + } + if ctx == nil || ctx.window <= 0 || len(haves) == 0 { + return nil + } + walk, err := repo.ReachableObjects(ReachabilityQuery{ + Wants: haves, + Mode: ReachabilityAllObjects, + }) + if err != nil { + return err + } + for obj := range walk.Seq() { + if obj.Type != ObjectTypeBlob { + continue + } + ty, body, err := repo.ReadObjectTypeRaw(obj.ID) + if err != nil { + return err + } + candidate := &objectToPack{ + id: obj.ID, + ty: ty, + body: body, + inPack: false, + } + ctx.addCandidate(candidate) + } + return walk.Err() +} + type packWriteOptions struct { EnableDeltas bool EnableThinPack bool |
