aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaulo Gomes <pjbgf@linux.com>2023-05-03 08:39:07 +0100
committerPaulo Gomes <pjbgf@linux.com>2023-05-03 08:39:07 +0100
commit19b39e150071832541f2cdcb669446a8609f57e7 (patch)
tree86f0f043a8176a790c132d2862f8f3f4565c398e
parent0542a302c2be7ed7de276411f7e20b87309734b9 (diff)
downloadgo-git-19b39e150071832541f2cdcb669446a8609f57e7.tar.gz
git: Add support to ls-remote with peeled references. Fixes #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 <pjbgf@linux.com>
-rw-r--r--_examples/ls-remote/main.go14
-rw-r--r--options.go20
-rw-r--r--remote.go29
-rw-r--r--remote_test.go37
4 files changed, 92 insertions, 8 deletions
diff --git a/_examples/ls-remote/main.go b/_examples/ls-remote/main.go
index af038d6..e49e8c9 100644
--- a/_examples/ls-remote/main.go
+++ b/_examples/ls-remote/main.go
@@ -2,25 +2,35 @@ package main
import (
"log"
+ "os"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/storage/memory"
+
+ . "github.com/go-git/go-git/v5/_examples"
)
// Retrieve remote tags without cloning repository
func main() {
+ CheckArgs("<url>")
+ url := os.Args[1]
+
+ Info("git ls-remote --tags %s", url)
// Create the remote with repository URL
rem := git.NewRemote(memory.NewStorage(), &config.RemoteConfig{
Name: "origin",
- URLs: []string{"https://github.com/Zenika/MARCEL"},
+ URLs: []string{url},
})
log.Print("Fetching tags...")
// We can then use every Remote functions to retrieve wanted information
- refs, err := rem.List(&git.ListOptions{})
+ refs, err := rem.List(&git.ListOptions{
+ // Returns all references, including peeled references.
+ PeelingOption: git.AppendPeeled,
+ })
if err != nil {
log.Fatal(err)
}
diff --git a/options.go b/options.go
index e79cf9d..04c83de 100644
--- a/options.go
+++ b/options.go
@@ -624,8 +624,28 @@ type ListOptions struct {
InsecureSkipTLS bool
// CABundle specify additional ca bundle with system cert pool
CABundle []byte
+
+ // PeelingOption defines how peeled objects are handled during a
+ // remote list.
+ PeelingOption PeelingOption
}
+// PeelingOption represents the different ways to handle peeled references.
+//
+// Peeled references represent the underlying object of an annotated
+// (or signed) tag. Refer to upstream documentation for more info:
+// https://github.com/git/git/blob/master/Documentation/technical/reftable.txt
+type PeelingOption uint8
+
+const (
+ // IgnorePeeled ignores all peeled reference names. This is the default behavior.
+ IgnorePeeled PeelingOption = 0
+ // OnlyPeeled returns only peeled reference names.
+ OnlyPeeled PeelingOption = 1
+ // AppendPeeled appends peeled reference names to the reference list.
+ AppendPeeled PeelingOption = 2
+)
+
// CleanOptions describes how a clean should be performed.
type CleanOptions struct {
Dir bool
diff --git a/remote.go b/remote.go
index db78ae7..ff72bdf 100644
--- a/remote.go
+++ b/remote.go
@@ -33,6 +33,7 @@ var (
ErrDeleteRefNotSupported = errors.New("server does not support delete-refs")
ErrForceNeeded = errors.New("some refs were not updated")
ErrExactSHA1NotSupported = errors.New("server does not support exact SHA1 refspec")
+ ErrEmptyUrls = errors.New("URLs cannot be empty")
)
type NoMatchingRefSpecError struct {
@@ -54,6 +55,9 @@ const (
// repo containing this remote, when not using the multi-ack
// protocol. Setting this to 0 means there is no limit.
maxHavesToVisitPerRef = 100
+
+ // peeledSuffix is the suffix used to build peeled reference names.
+ peeledSuffix = "^{}"
)
// Remote represents a connection to a remote repository.
@@ -1259,6 +1263,10 @@ func (r *Remote) List(o *ListOptions) (rfs []*plumbing.Reference, err error) {
}
func (r *Remote) list(ctx context.Context, o *ListOptions) (rfs []*plumbing.Reference, err error) {
+ if r.c == nil || len(r.c.URLs) == 0 {
+ return nil, ErrEmptyUrls
+ }
+
s, err := newUploadPackSession(r.c.URLs[0], o.Auth, o.InsecureSkipTLS, o.CABundle)
if err != nil {
return nil, err
@@ -1282,13 +1290,22 @@ func (r *Remote) list(ctx context.Context, o *ListOptions) (rfs []*plumbing.Refe
}
var resultRefs []*plumbing.Reference
- err = refs.ForEach(func(ref *plumbing.Reference) error {
- resultRefs = append(resultRefs, ref)
- return nil
- })
- if err != nil {
- return nil, err
+ if o.PeelingOption == AppendPeeled || o.PeelingOption == IgnorePeeled {
+ err = refs.ForEach(func(ref *plumbing.Reference) error {
+ resultRefs = append(resultRefs, ref)
+ return nil
+ })
+ if err != nil {
+ return nil, err
+ }
}
+
+ if o.PeelingOption == AppendPeeled || o.PeelingOption == OnlyPeeled {
+ for k, v := range ar.Peeled {
+ resultRefs = append(resultRefs, plumbing.NewReferenceFromStrings(k+"^{}", v.String()))
+ }
+ }
+
return resultRefs, nil
}
diff --git a/remote_test.go b/remote_test.go
index 751c89a..164e4e5 100644
--- a/remote_test.go
+++ b/remote_test.go
@@ -9,6 +9,7 @@ import (
"os"
"path/filepath"
"runtime"
+ "strings"
"time"
"github.com/go-git/go-git/v5/config"
@@ -865,6 +866,7 @@ func (s *RemoteSuite) TestPushForceWithLease_success(c *C) {
c.Assert(sto.SetReference(newCommit), IsNil)
ref, err := sto.Reference("refs/heads/branch")
+ c.Assert(err, IsNil)
c.Log(ref.String())
url := dstFs.Root()
@@ -1210,6 +1212,41 @@ func (s *RemoteSuite) TestList(c *C) {
}
}
+func (s *RemoteSuite) TestListPeeling(c *C) {
+ remote := NewRemote(memory.NewStorage(), &config.RemoteConfig{
+ Name: DefaultRemoteName,
+ URLs: []string{"https://github.com/git-fixtures/tags.git"},
+ })
+
+ for _, tc := range []struct {
+ peelingOption PeelingOption
+ expectPeeled bool
+ expectNonPeeled bool
+ }{
+ {peelingOption: AppendPeeled, expectPeeled: true, expectNonPeeled: true},
+ {peelingOption: IgnorePeeled, expectPeeled: false, expectNonPeeled: true},
+ {peelingOption: OnlyPeeled, expectPeeled: true, expectNonPeeled: false},
+ } {
+ refs, err := remote.List(&ListOptions{
+ PeelingOption: tc.peelingOption,
+ })
+ c.Assert(err, IsNil)
+ c.Assert(len(refs) > 0, Equals, true)
+
+ foundPeeled, foundNonPeeled := false, false
+ for _, ref := range refs {
+ if strings.HasSuffix(ref.Name().String(), peeledSuffix) {
+ foundPeeled = true
+ } else {
+ foundNonPeeled = true
+ }
+ }
+
+ c.Assert(foundPeeled, Equals, tc.expectPeeled)
+ c.Assert(foundNonPeeled, Equals, tc.expectNonPeeled)
+ }
+}
+
func (s *RemoteSuite) TestListTimeout(c *C) {
remote := NewRemote(memory.NewStorage(), &config.RemoteConfig{
Name: DefaultRemoteName,