Skip to content

Commit 19b39e1

Browse files
committed
git: Add support to ls-remote with peeled references. Fixes go-git#749
A new PeelingOption field was introduced into ListOptions. The new options include the default (and backwards compatible) IgnorePeeled. Plus another two variations which either only returns peeled references (OnlyPeeled), or append peeled references to the list (AppendPeeled). The ls-remote example was updated to align with upstream, in which peeled references are appended to the results by default. A new ErrEmptyUrls error is now returned when List or ListContext do not receive a URL to work with, to improve overall execution flow. Signed-off-by: Paulo Gomes <[email protected]>
1 parent 0542a30 commit 19b39e1

File tree

4 files changed

+92
-8
lines changed

4 files changed

+92
-8
lines changed

_examples/ls-remote/main.go

+12-2
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,35 @@ package main
22

33
import (
44
"log"
5+
"os"
56

67
"github.com/go-git/go-git/v5"
78
"github.com/go-git/go-git/v5/config"
89
"github.com/go-git/go-git/v5/storage/memory"
10+
11+
. "github.com/go-git/go-git/v5/_examples"
912
)
1013

1114
// Retrieve remote tags without cloning repository
1215
func main() {
16+
CheckArgs("<url>")
17+
url := os.Args[1]
18+
19+
Info("git ls-remote --tags %s", url)
1320

1421
// Create the remote with repository URL
1522
rem := git.NewRemote(memory.NewStorage(), &config.RemoteConfig{
1623
Name: "origin",
17-
URLs: []string{"https://github.com/Zenika/MARCEL"},
24+
URLs: []string{url},
1825
})
1926

2027
log.Print("Fetching tags...")
2128

2229
// We can then use every Remote functions to retrieve wanted information
23-
refs, err := rem.List(&git.ListOptions{})
30+
refs, err := rem.List(&git.ListOptions{
31+
// Returns all references, including peeled references.
32+
PeelingOption: git.AppendPeeled,
33+
})
2434
if err != nil {
2535
log.Fatal(err)
2636
}

options.go

+20
Original file line numberDiff line numberDiff line change
@@ -624,8 +624,28 @@ type ListOptions struct {
624624
InsecureSkipTLS bool
625625
// CABundle specify additional ca bundle with system cert pool
626626
CABundle []byte
627+
628+
// PeelingOption defines how peeled objects are handled during a
629+
// remote list.
630+
PeelingOption PeelingOption
627631
}
628632

633+
// PeelingOption represents the different ways to handle peeled references.
634+
//
635+
// Peeled references represent the underlying object of an annotated
636+
// (or signed) tag. Refer to upstream documentation for more info:
637+
// https://github.com/git/git/blob/master/Documentation/technical/reftable.txt
638+
type PeelingOption uint8
639+
640+
const (
641+
// IgnorePeeled ignores all peeled reference names. This is the default behavior.
642+
IgnorePeeled PeelingOption = 0
643+
// OnlyPeeled returns only peeled reference names.
644+
OnlyPeeled PeelingOption = 1
645+
// AppendPeeled appends peeled reference names to the reference list.
646+
AppendPeeled PeelingOption = 2
647+
)
648+
629649
// CleanOptions describes how a clean should be performed.
630650
type CleanOptions struct {
631651
Dir bool

remote.go

+23-6
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ var (
3333
ErrDeleteRefNotSupported = errors.New("server does not support delete-refs")
3434
ErrForceNeeded = errors.New("some refs were not updated")
3535
ErrExactSHA1NotSupported = errors.New("server does not support exact SHA1 refspec")
36+
ErrEmptyUrls = errors.New("URLs cannot be empty")
3637
)
3738

3839
type NoMatchingRefSpecError struct {
@@ -54,6 +55,9 @@ const (
5455
// repo containing this remote, when not using the multi-ack
5556
// protocol. Setting this to 0 means there is no limit.
5657
maxHavesToVisitPerRef = 100
58+
59+
// peeledSuffix is the suffix used to build peeled reference names.
60+
peeledSuffix = "^{}"
5761
)
5862

5963
// Remote represents a connection to a remote repository.
@@ -1259,6 +1263,10 @@ func (r *Remote) List(o *ListOptions) (rfs []*plumbing.Reference, err error) {
12591263
}
12601264

12611265
func (r *Remote) list(ctx context.Context, o *ListOptions) (rfs []*plumbing.Reference, err error) {
1266+
if r.c == nil || len(r.c.URLs) == 0 {
1267+
return nil, ErrEmptyUrls
1268+
}
1269+
12621270
s, err := newUploadPackSession(r.c.URLs[0], o.Auth, o.InsecureSkipTLS, o.CABundle)
12631271
if err != nil {
12641272
return nil, err
@@ -1282,13 +1290,22 @@ func (r *Remote) list(ctx context.Context, o *ListOptions) (rfs []*plumbing.Refe
12821290
}
12831291

12841292
var resultRefs []*plumbing.Reference
1285-
err = refs.ForEach(func(ref *plumbing.Reference) error {
1286-
resultRefs = append(resultRefs, ref)
1287-
return nil
1288-
})
1289-
if err != nil {
1290-
return nil, err
1293+
if o.PeelingOption == AppendPeeled || o.PeelingOption == IgnorePeeled {
1294+
err = refs.ForEach(func(ref *plumbing.Reference) error {
1295+
resultRefs = append(resultRefs, ref)
1296+
return nil
1297+
})
1298+
if err != nil {
1299+
return nil, err
1300+
}
12911301
}
1302+
1303+
if o.PeelingOption == AppendPeeled || o.PeelingOption == OnlyPeeled {
1304+
for k, v := range ar.Peeled {
1305+
resultRefs = append(resultRefs, plumbing.NewReferenceFromStrings(k+"^{}", v.String()))
1306+
}
1307+
}
1308+
12921309
return resultRefs, nil
12931310
}
12941311

remote_test.go

+37
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"os"
1010
"path/filepath"
1111
"runtime"
12+
"strings"
1213
"time"
1314

1415
"github.com/go-git/go-git/v5/config"
@@ -865,6 +866,7 @@ func (s *RemoteSuite) TestPushForceWithLease_success(c *C) {
865866
c.Assert(sto.SetReference(newCommit), IsNil)
866867

867868
ref, err := sto.Reference("refs/heads/branch")
869+
c.Assert(err, IsNil)
868870
c.Log(ref.String())
869871

870872
url := dstFs.Root()
@@ -1210,6 +1212,41 @@ func (s *RemoteSuite) TestList(c *C) {
12101212
}
12111213
}
12121214

1215+
func (s *RemoteSuite) TestListPeeling(c *C) {
1216+
remote := NewRemote(memory.NewStorage(), &config.RemoteConfig{
1217+
Name: DefaultRemoteName,
1218+
URLs: []string{"https://github.com/git-fixtures/tags.git"},
1219+
})
1220+
1221+
for _, tc := range []struct {
1222+
peelingOption PeelingOption
1223+
expectPeeled bool
1224+
expectNonPeeled bool
1225+
}{
1226+
{peelingOption: AppendPeeled, expectPeeled: true, expectNonPeeled: true},
1227+
{peelingOption: IgnorePeeled, expectPeeled: false, expectNonPeeled: true},
1228+
{peelingOption: OnlyPeeled, expectPeeled: true, expectNonPeeled: false},
1229+
} {
1230+
refs, err := remote.List(&ListOptions{
1231+
PeelingOption: tc.peelingOption,
1232+
})
1233+
c.Assert(err, IsNil)
1234+
c.Assert(len(refs) > 0, Equals, true)
1235+
1236+
foundPeeled, foundNonPeeled := false, false
1237+
for _, ref := range refs {
1238+
if strings.HasSuffix(ref.Name().String(), peeledSuffix) {
1239+
foundPeeled = true
1240+
} else {
1241+
foundNonPeeled = true
1242+
}
1243+
}
1244+
1245+
c.Assert(foundPeeled, Equals, tc.expectPeeled)
1246+
c.Assert(foundNonPeeled, Equals, tc.expectNonPeeled)
1247+
}
1248+
}
1249+
12131250
func (s *RemoteSuite) TestListTimeout(c *C) {
12141251
remote := NewRemote(memory.NewStorage(), &config.RemoteConfig{
12151252
Name: DefaultRemoteName,

0 commit comments

Comments
 (0)