Skip to content

Commit 89cce89

Browse files
authored
plumbing: protocol/packp, client-side filter capability support (#1000)
* plumbing: protocol/packp, client-side filter capability support
1 parent 7d39843 commit 89cce89

File tree

5 files changed

+160
-0
lines changed

5 files changed

+160
-0
lines changed

plumbing/protocol/packp/filter.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package packp
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"github.com/go-git/go-git/v5/plumbing"
7+
"net/url"
8+
"strings"
9+
)
10+
11+
var ErrUnsupportedObjectFilterType = errors.New("unsupported object filter type")
12+
13+
// Filter values enable the partial clone capability which causes
14+
// the server to omit objects that match the filter.
15+
//
16+
// See [Git's documentation] for more details.
17+
//
18+
// [Git's documentation]: https://github.com/git/git/blob/e02ecfcc534e2021aae29077a958dd11c3897e4c/Documentation/rev-list-options.txt#L948
19+
type Filter string
20+
21+
type BlobLimitPrefix string
22+
23+
const (
24+
BlobLimitPrefixNone BlobLimitPrefix = ""
25+
BlobLimitPrefixKibi BlobLimitPrefix = "k"
26+
BlobLimitPrefixMebi BlobLimitPrefix = "m"
27+
BlobLimitPrefixGibi BlobLimitPrefix = "g"
28+
)
29+
30+
// FilterBlobNone omits all blobs.
31+
func FilterBlobNone() Filter {
32+
return "blob:none"
33+
}
34+
35+
// FilterBlobLimit omits blobs of size at least n bytes (when prefix is
36+
// BlobLimitPrefixNone), n kibibytes (when prefix is BlobLimitPrefixKibi),
37+
// n mebibytes (when prefix is BlobLimitPrefixMebi) or n gibibytes (when
38+
// prefix is BlobLimitPrefixGibi). n can be zero, in which case all blobs
39+
// will be omitted.
40+
func FilterBlobLimit(n uint64, prefix BlobLimitPrefix) Filter {
41+
return Filter(fmt.Sprintf("blob:limit=%d%s", n, prefix))
42+
}
43+
44+
// FilterTreeDepth omits all blobs and trees whose depth from the root tree
45+
// is larger or equal to depth.
46+
func FilterTreeDepth(depth uint64) Filter {
47+
return Filter(fmt.Sprintf("tree:%d", depth))
48+
}
49+
50+
// FilterObjectType omits all objects which are not of the requested type t.
51+
// Supported types are TagObject, CommitObject, TreeObject and BlobObject.
52+
func FilterObjectType(t plumbing.ObjectType) (Filter, error) {
53+
switch t {
54+
case plumbing.TagObject:
55+
fallthrough
56+
case plumbing.CommitObject:
57+
fallthrough
58+
case plumbing.TreeObject:
59+
fallthrough
60+
case plumbing.BlobObject:
61+
return Filter(fmt.Sprintf("object:type=%s", t.String())), nil
62+
default:
63+
return "", fmt.Errorf("%w: %s", ErrUnsupportedObjectFilterType, t.String())
64+
}
65+
}
66+
67+
// FilterCombine combines multiple Filter values together.
68+
func FilterCombine(filters ...Filter) Filter {
69+
var escapedFilters []string
70+
71+
for _, filter := range filters {
72+
escapedFilters = append(escapedFilters, url.QueryEscape(string(filter)))
73+
}
74+
75+
return Filter(fmt.Sprintf("combine:%s", strings.Join(escapedFilters, "+")))
76+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package packp
2+
3+
import (
4+
"github.com/go-git/go-git/v5/plumbing"
5+
"github.com/stretchr/testify/require"
6+
"testing"
7+
)
8+
9+
func TestFilterBlobNone(t *testing.T) {
10+
require.EqualValues(t, "blob:none", FilterBlobNone())
11+
}
12+
13+
func TestFilterBlobLimit(t *testing.T) {
14+
require.EqualValues(t, "blob:limit=0", FilterBlobLimit(0, BlobLimitPrefixNone))
15+
require.EqualValues(t, "blob:limit=1000", FilterBlobLimit(1000, BlobLimitPrefixNone))
16+
require.EqualValues(t, "blob:limit=4k", FilterBlobLimit(4, BlobLimitPrefixKibi))
17+
require.EqualValues(t, "blob:limit=4m", FilterBlobLimit(4, BlobLimitPrefixMebi))
18+
require.EqualValues(t, "blob:limit=4g", FilterBlobLimit(4, BlobLimitPrefixGibi))
19+
}
20+
21+
func TestFilterTreeDepth(t *testing.T) {
22+
require.EqualValues(t, "tree:0", FilterTreeDepth(0))
23+
require.EqualValues(t, "tree:1", FilterTreeDepth(1))
24+
require.EqualValues(t, "tree:2", FilterTreeDepth(2))
25+
}
26+
27+
func TestFilterObjectType(t *testing.T) {
28+
filter, err := FilterObjectType(plumbing.TagObject)
29+
require.NoError(t, err)
30+
require.EqualValues(t, "object:type=tag", filter)
31+
32+
filter, err = FilterObjectType(plumbing.CommitObject)
33+
require.NoError(t, err)
34+
require.EqualValues(t, "object:type=commit", filter)
35+
36+
filter, err = FilterObjectType(plumbing.TreeObject)
37+
require.NoError(t, err)
38+
require.EqualValues(t, "object:type=tree", filter)
39+
40+
filter, err = FilterObjectType(plumbing.BlobObject)
41+
require.NoError(t, err)
42+
require.EqualValues(t, "object:type=blob", filter)
43+
44+
_, err = FilterObjectType(plumbing.InvalidObject)
45+
require.Error(t, err)
46+
47+
_, err = FilterObjectType(plumbing.OFSDeltaObject)
48+
require.Error(t, err)
49+
}
50+
51+
func TestFilterCombine(t *testing.T) {
52+
require.EqualValues(t, "combine:tree%3A2+blob%3Anone",
53+
FilterCombine(
54+
FilterTreeDepth(2),
55+
FilterBlobNone(),
56+
),
57+
)
58+
}

plumbing/protocol/packp/ulreq.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ type UploadRequest struct {
1717
Wants []plumbing.Hash
1818
Shallows []plumbing.Hash
1919
Depth Depth
20+
Filter Filter
2021
}
2122

2223
// Depth values stores the desired depth of the requested packfile: see

plumbing/protocol/packp/ulreq_encode.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,17 @@ func (e *ulReqEncoder) encodeDepth() stateFn {
132132
return nil
133133
}
134134

135+
return e.encodeFilter
136+
}
137+
138+
func (e *ulReqEncoder) encodeFilter() stateFn {
139+
if filter := e.data.Filter; filter != "" {
140+
if err := e.pe.Encodef("filter %s\n", filter); err != nil {
141+
e.err = fmt.Errorf("encoding filter %s: %s", filter, err)
142+
return nil
143+
}
144+
}
145+
135146
return e.encodeFlush
136147
}
137148

plumbing/protocol/packp/ulreq_encode_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,20 @@ func (s *UlReqEncodeSuite) TestDepthReference(c *C) {
273273
testUlReqEncode(c, ur, expected)
274274
}
275275

276+
func (s *UlReqEncodeSuite) TestFilter(c *C) {
277+
ur := NewUploadRequest()
278+
ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111"))
279+
ur.Filter = FilterTreeDepth(0)
280+
281+
expected := []string{
282+
"want 1111111111111111111111111111111111111111\n",
283+
"filter tree:0\n",
284+
pktline.FlushString,
285+
}
286+
287+
testUlReqEncode(c, ur, expected)
288+
}
289+
276290
func (s *UlReqEncodeSuite) TestAll(c *C) {
277291
ur := NewUploadRequest()
278292
ur.Wants = append(ur.Wants,

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy