aboutsummaryrefslogtreecommitdiff
path: root/packed_write_pack.go
diff options
context:
space:
mode:
authorGravatar Runxi Yu2026-01-30 17:06:51 +0100
committerGravatar Runxi Yu2026-01-30 17:06:51 +0100
commit8e320c9ca634e6b2431f9442b7d5191864735ae4 (patch)
treeb2e3d18144865fad0508a4f890dada5aee6ba940 /packed_write_pack.go
parentreachability: Add basic reachability API (diff)
signatureNo signature
packed, delta: Implement thin packs
Diffstat (limited to 'packed_write_pack.go')
-rw-r--r--packed_write_pack.go102
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