Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Steinhardt <psteinhardt@gitlab.com>2021-09-13 10:30:30 +0300
committerPatrick Steinhardt <psteinhardt@gitlab.com>2021-09-13 10:30:30 +0300
commit9608e75580e805da5258ad3c02df3ad1cde91176 (patch)
treedf209a176aa9f87a0f3fd3b9dc8049c78ef59b87
parentfa6eb13ccb4f2d5a36d3c010eeb197eb2bfdc825 (diff)
parent158715f9c7afd0934b1b930f87081d8ca1540b8d (diff)
Merge branch 'pks-find-all-tags' into 'master'
Miscellaneous tag-related refactorings See merge request gitlab-org/gitaly!3846
-rw-r--r--internal/git/catfile/tag_test.go4
-rw-r--r--internal/git/gittest/tag.go53
-rw-r--r--internal/git/objectpool/fetch_test.go4
-rw-r--r--internal/gitaly/service/ref/find_all_tags.go217
-rw-r--r--internal/gitaly/service/ref/find_all_tags_test.go617
-rw-r--r--internal/gitaly/service/ref/refs.go205
-rw-r--r--internal/gitaly/service/ref/refs_test.go642
-rw-r--r--internal/gitaly/service/ref/tag_messages_test.go10
-rw-r--r--internal/gitaly/service/ref/tag_signatures_test.go28
-rw-r--r--internal/gitaly/service/remote/update_remote_mirror_test.go16
-rw-r--r--internal/gitaly/service/repository/fetch_remote_test.go2
11 files changed, 923 insertions, 875 deletions
diff --git a/internal/git/catfile/tag_test.go b/internal/git/catfile/tag_test.go
index bebaad25e..0275a4f28 100644
--- a/internal/git/catfile/tag_test.go
+++ b/internal/git/catfile/tag_test.go
@@ -27,7 +27,7 @@ func TestGetTag(t *testing.T) {
testCases := []struct {
tagName string
- rev string
+ rev git.Revision
message string
trim bool
}{
@@ -53,7 +53,7 @@ func TestGetTag(t *testing.T) {
for _, testCase := range testCases {
t.Run(testCase.tagName, func(t *testing.T) {
- tagID := gittest.CreateTag(t, cfg, testRepoPath, testCase.tagName, testCase.rev, &gittest.CreateTagOpts{Message: testCase.message})
+ tagID := gittest.WriteTag(t, cfg, testRepoPath, testCase.tagName, testCase.rev, gittest.WriteTagConfig{Message: testCase.message})
tag, err := GetTag(ctx, c, git.Revision(tagID), testCase.tagName, testCase.trim, true)
require.NoError(t, err)
diff --git a/internal/git/gittest/tag.go b/internal/git/gittest/tag.go
index 309dad6fe..ecda217dc 100644
--- a/internal/git/gittest/tag.go
+++ b/internal/git/gittest/tag.go
@@ -5,34 +5,43 @@ import (
"fmt"
"testing"
+ "github.com/stretchr/testify/require"
+ "gitlab.com/gitlab-org/gitaly/v14/internal/git"
"gitlab.com/gitlab-org/gitaly/v14/internal/gitaly/config"
"gitlab.com/gitlab-org/gitaly/v14/internal/helper/text"
)
-// CreateTagOpts holds extra options for CreateTag.
-type CreateTagOpts struct {
+// WriteTagConfig holds extra options for WriteTag.
+type WriteTagConfig struct {
+ // Message is the message of an annotated tag. If left empty, then a lightweight tag will
+ // be created.
Message string
- Force bool
+ // Force indicates whether existing tags with the same name shall be overwritten.
+ Force bool
}
-// CreateTag creates a new tag.
-func CreateTag(t testing.TB, cfg config.Cfg, repoPath, tagName, targetID string, opts *CreateTagOpts) string {
- var message string
- force := false
+// WriteTag writes a new tag into the repository. This function either returns the tag ID in case
+// an annotated tag was created, or otherwise the target object ID when a lightweight tag was
+// created. Takes either no WriteTagConfig, in which case the default values will be used, or
+// exactly one.
+func WriteTag(
+ t testing.TB,
+ cfg config.Cfg,
+ repoPath string,
+ tagName string,
+ targetRevision git.Revision,
+ optionalConfig ...WriteTagConfig,
+) git.ObjectID {
+ require.Less(t, len(optionalConfig), 2, "only a single config may be passed")
- if opts != nil {
- if opts.Message != "" {
- message = opts.Message
- }
- force = opts.Force
+ var config WriteTagConfig
+ if len(optionalConfig) == 1 {
+ config = optionalConfig[0]
}
committerName := "Scrooge McDuck"
committerEmail := "scrooge@mcduck.com"
- // message can be very large, passing it directly in args would blow things up!
- stdin := bytes.NewBufferString(message)
-
args := []string{
"-C", repoPath,
"-c", fmt.Sprintf("user.name=%s", committerName),
@@ -40,17 +49,23 @@ func CreateTag(t testing.TB, cfg config.Cfg, repoPath, tagName, targetID string,
"tag",
}
- if force {
+ if config.Force {
args = append(args, "-f")
}
- if message != "" {
+ // The message can be very large, passing it directly in args would blow things up.
+ stdin := bytes.NewBufferString(config.Message)
+ if config.Message != "" {
args = append(args, "-F", "-")
}
- args = append(args, tagName, targetID)
+ args = append(args, tagName, targetRevision.String())
ExecStream(t, cfg, stdin, args...)
tagID := Exec(t, cfg, "-C", repoPath, "show-ref", "-s", tagName)
- return text.ChompBytes(tagID)
+
+ objectID, err := git.NewObjectIDFromHex(text.ChompBytes(tagID))
+ require.NoError(t, err)
+
+ return objectID
}
diff --git a/internal/git/objectpool/fetch_test.go b/internal/git/objectpool/fetch_test.go
index 491c4f8ab..d0dbb2fb7 100644
--- a/internal/git/objectpool/fetch_test.go
+++ b/internal/git/objectpool/fetch_test.go
@@ -51,7 +51,7 @@ func TestFetchFromOriginDangling(t *testing.T) {
// A tag with random hex characters in its name should be unique.
newTagName := "tag-" + nonce
- newTag := gittest.CreateTag(t, pool.cfg, pool.FullPath(), newTagName, existingCommit, &gittest.CreateTagOpts{
+ newTag := gittest.WriteTag(t, pool.cfg, pool.FullPath(), newTagName, existingCommit, gittest.WriteTagConfig{
Message: "msg",
})
@@ -77,7 +77,7 @@ func TestFetchFromOriginDangling(t *testing.T) {
refsAfter := gittest.Exec(t, pool.cfg, "-C", pool.FullPath(), "for-each-ref", "--format=%(refname) %(objectname)")
refsAfterLines := strings.Split(string(refsAfter), "\n")
- for _, id := range []string{newBlob.String(), newTree.String(), newCommit.String(), newTag} {
+ for _, id := range []git.ObjectID{newBlob, newTree, newCommit, newTag} {
require.Contains(t, refsAfterLines, fmt.Sprintf("refs/dangling/%s %s", id, id))
}
}
diff --git a/internal/gitaly/service/ref/find_all_tags.go b/internal/gitaly/service/ref/find_all_tags.go
new file mode 100644
index 000000000..057414302
--- /dev/null
+++ b/internal/gitaly/service/ref/find_all_tags.go
@@ -0,0 +1,217 @@
+package ref
+
+import (
+ "bytes"
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+
+ "gitlab.com/gitlab-org/gitaly/v14/internal/git/catfile"
+ "gitlab.com/gitlab-org/gitaly/v14/internal/git/gitpipe"
+ "gitlab.com/gitlab-org/gitaly/v14/internal/git/localrepo"
+ "gitlab.com/gitlab-org/gitaly/v14/internal/helper"
+ "gitlab.com/gitlab-org/gitaly/v14/internal/helper/chunk"
+ "gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb"
+ "google.golang.org/protobuf/proto"
+)
+
+func (s *server) FindAllTags(in *gitalypb.FindAllTagsRequest, stream gitalypb.RefService_FindAllTagsServer) error {
+ ctx := stream.Context()
+
+ if err := s.validateFindAllTagsRequest(in); err != nil {
+ return helper.ErrInvalidArgument(err)
+ }
+
+ sortField, err := getTagSortField(in.GetSortBy())
+ if err != nil {
+ return helper.ErrInvalidArgument(err)
+ }
+
+ repo := s.localrepo(in.GetRepository())
+
+ if err := s.findAllTags(ctx, repo, sortField, stream); err != nil {
+ return helper.ErrInternal(err)
+ }
+
+ return nil
+}
+
+func (s *server) findAllTags(ctx context.Context, repo *localrepo.Repo, sortField string, stream gitalypb.RefService_FindAllTagsServer) error {
+ c, err := s.catfileCache.BatchProcess(ctx, repo)
+ if err != nil {
+ return fmt.Errorf("error creating catfile: %v", err)
+ }
+
+ forEachRefIter := gitpipe.ForEachRef(ctx, repo, []string{"refs/tags/"}, sortField)
+ forEachRefIter = gitpipe.RevisionTransform(ctx, forEachRefIter,
+ func(r gitpipe.RevisionResult) []gitpipe.RevisionResult {
+ // We transform the pipeline to include each tag-reference twice: once for
+ // the "normal" object, and once we opportunistically peel the object to a
+ // non-tag object. This is required such that we can efficiently parse the
+ // tagged object.
+ return []gitpipe.RevisionResult{
+ r,
+ {OID: r.OID + "^{}"},
+ }
+ },
+ )
+
+ catfileInfoIter := gitpipe.CatfileInfo(ctx, c, forEachRefIter)
+
+ // In the previous pipeline step, we request information about both the object and the
+ // peeled object in case the object is a tag. Given that we now know about object types, we
+ // can filter out the second request in case the object is not a tag: peeling a non-tag
+ // object to a non-tag object is always going to end up with the same object anyway. And
+ // requesting the same object twice is moot.
+ type state int
+ const (
+ // stateTag indicates that the next object is going to be a tag.
+ stateTag = state(iota)
+ // statePeeledTag indicates that the next object is going to be the peeled object of
+ // the preceding tag.
+ statePeeledTag
+ // stateSkip indicates that the next object shall be skipped because it is the
+ // peeled version of a non-tag object, which is the same object anyway.
+ stateSkip
+ )
+
+ currentState := stateTag
+ catfileInfoIter = gitpipe.CatfileInfoFilter(ctx, catfileInfoIter,
+ func(r gitpipe.CatfileInfoResult) bool {
+ switch currentState {
+ case stateTag:
+ // If we've got a tag, then we want to also see its peeled object.
+ // Otherwise, we can skip over the peeled object.
+ currentState = statePeeledTag
+ if r.ObjectInfo.Type != "tag" {
+ currentState = stateSkip
+ }
+ return true
+ case statePeeledTag:
+ currentState = stateTag
+ return true
+ case stateSkip:
+ currentState = stateTag
+ return false
+ }
+
+ // We could try to gracefully handle this, but I don't see much of a point
+ // given that we can see above that it's never going to be anything else but
+ // a known state.
+ panic("invalid state")
+ },
+ )
+
+ catfileObjectsIter := gitpipe.CatfileObject(ctx, c, catfileInfoIter)
+
+ chunker := chunk.New(&tagSender{stream: stream})
+
+ for catfileObjectsIter.Next() {
+ tag := catfileObjectsIter.Result()
+
+ var result *gitalypb.Tag
+ switch tag.ObjectInfo.Type {
+ case "tag":
+ var err error
+ result, err = catfile.ParseTag(tag.ObjectReader, tag.ObjectInfo.Oid)
+ if err != nil {
+ return fmt.Errorf("parsing annotated tag: %w", err)
+ }
+
+ // For each tag, we expect both the tag itself as well as its
+ // potentially-peeled tagged object.
+ if !catfileObjectsIter.Next() {
+ return errors.New("expected peeled tag")
+ }
+
+ peeledTag := catfileObjectsIter.Result()
+
+ // We only need to parse the tagged object in case we have an annotated tag
+ // which refers to a commit object. Otherwise, we discard the object's
+ // contents.
+ if peeledTag.ObjectInfo.Type == "commit" {
+ result.TargetCommit, err = catfile.ParseCommit(peeledTag.ObjectReader, peeledTag.ObjectInfo.Oid)
+ if err != nil {
+ return fmt.Errorf("parsing tagged commit: %w", err)
+ }
+ } else {
+ if _, err := io.Copy(ioutil.Discard, peeledTag.ObjectReader); err != nil {
+ return fmt.Errorf("discarding tagged object contents: %w", err)
+ }
+ }
+ case "commit":
+ commit, err := catfile.ParseCommit(tag.ObjectReader, tag.ObjectInfo.Oid)
+ if err != nil {
+ return fmt.Errorf("parsing tagged commit: %w", err)
+ }
+
+ result = &gitalypb.Tag{
+ Id: tag.ObjectInfo.Oid.String(),
+ TargetCommit: commit,
+ }
+ default:
+ if _, err := io.Copy(ioutil.Discard, tag.ObjectReader); err != nil {
+ return fmt.Errorf("discarding tag object contents: %w", err)
+ }
+
+ result = &gitalypb.Tag{
+ Id: tag.ObjectInfo.Oid.String(),
+ }
+ }
+
+ // In case we can deduce the tag name from the object name (which should typically
+ // be the case), we always want to return the tag name. While annotated tags do have
+ // their name encoded in the object itself, we instead want to default to the name
+ // of the reference such that we can discern multiple refs pointing to the same tag.
+ if tagName := bytes.TrimPrefix(tag.ObjectName, []byte("refs/tags/")); len(tagName) > 0 {
+ result.Name = tagName
+ }
+
+ if err := chunker.Send(result); err != nil {
+ return fmt.Errorf("sending tag: %w", err)
+ }
+ }
+
+ if err := catfileObjectsIter.Err(); err != nil {
+ return fmt.Errorf("iterating over tags: %w", err)
+ }
+
+ if err := chunker.Flush(); err != nil {
+ return fmt.Errorf("flushing chunker: %w", err)
+ }
+
+ return nil
+}
+
+func (s *server) validateFindAllTagsRequest(request *gitalypb.FindAllTagsRequest) error {
+ if request.GetRepository() == nil {
+ return errors.New("empty Repository")
+ }
+
+ if _, err := s.locator.GetRepoPath(request.GetRepository()); err != nil {
+ return fmt.Errorf("invalid git directory: %v", err)
+ }
+
+ return nil
+}
+
+type tagSender struct {
+ tags []*gitalypb.Tag
+ stream gitalypb.RefService_FindAllTagsServer
+}
+
+func (t *tagSender) Reset() {
+ t.tags = t.tags[:0]
+}
+
+func (t *tagSender) Append(m proto.Message) {
+ t.tags = append(t.tags, m.(*gitalypb.Tag))
+}
+
+func (t *tagSender) Send() error {
+ return t.stream.Send(&gitalypb.FindAllTagsResponse{
+ Tags: t.tags,
+ })
+}
diff --git a/internal/gitaly/service/ref/find_all_tags_test.go b/internal/gitaly/service/ref/find_all_tags_test.go
new file mode 100644
index 000000000..628edc948
--- /dev/null
+++ b/internal/gitaly/service/ref/find_all_tags_test.go
@@ -0,0 +1,617 @@
+package ref
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "gitlab.com/gitlab-org/gitaly/v14/internal/git"
+ "gitlab.com/gitlab-org/gitaly/v14/internal/git/catfile"
+ "gitlab.com/gitlab-org/gitaly/v14/internal/git/gittest"
+ "gitlab.com/gitlab-org/gitaly/v14/internal/git/localrepo"
+ "gitlab.com/gitlab-org/gitaly/v14/internal/helper"
+ "gitlab.com/gitlab-org/gitaly/v14/internal/testhelper"
+ "gitlab.com/gitlab-org/gitaly/v14/internal/testhelper/testassert"
+ "gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+ "google.golang.org/protobuf/types/known/timestamppb"
+)
+
+func TestFindAllTags_successful(t *testing.T) {
+ cfg, client := setupRefServiceWithoutRepo(t)
+
+ repoProto, repoPath := gittest.CloneRepo(t, cfg, cfg.Storages[0])
+ repo := localrepo.NewTestRepo(t, cfg, repoProto)
+
+ ctx, cancel := testhelper.Context()
+ defer cancel()
+
+ // reconstruct the v1.1.2 tag from patches to test truncated tag message
+ // with partial PGP block
+ truncatedPGPTagMsg := testhelper.MustReadFile(t, "testdata/truncated_pgp_msg.patch")
+
+ truncatedPGPTagID := string(gittest.ExecStream(t, cfg, bytes.NewBuffer(truncatedPGPTagMsg), "-C", repoPath, "mktag"))
+ truncatedPGPTagID = strings.TrimSpace(truncatedPGPTagID) // remove trailing newline
+ gittest.Exec(t, cfg, "-C", repoPath, "update-ref", "refs/tags/pgp-long-tag-message", truncatedPGPTagID)
+
+ blobID := git.ObjectID("faaf198af3a36dbf41961466703cc1d47c61d051")
+ commitID := git.ObjectID("6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9")
+
+ gitCommit := testhelper.GitLabTestCommit(commitID.String())
+
+ bigCommitID := gittest.WriteCommit(t, cfg, repoPath,
+ gittest.WithBranch("local-big-commits"),
+ gittest.WithMessage("An empty commit with REALLY BIG message\n\n"+strings.Repeat("a", helper.MaxCommitOrTagMessageSize+1)),
+ gittest.WithParents("60ecb67744cb56576c30214ff52294f8ce2def98"),
+ )
+ bigCommit, err := repo.ReadCommit(ctx, git.Revision(bigCommitID))
+ require.NoError(t, err)
+
+ annotatedTagID := gittest.WriteTag(t, cfg, repoPath, "v1.2.0", blobID.Revision(), gittest.WriteTagConfig{Message: "Blob tag"})
+
+ gittest.WriteTag(t, cfg, repoPath, "v1.3.0", commitID.Revision())
+ gittest.WriteTag(t, cfg, repoPath, "v1.4.0", blobID.Revision())
+
+ // To test recursive resolving to a commit
+ gittest.WriteTag(t, cfg, repoPath, "v1.5.0", "v1.3.0")
+
+ // A tag to commit with a big message
+ gittest.WriteTag(t, cfg, repoPath, "v1.6.0", bigCommitID.Revision())
+
+ // A tag with a big message
+ bigMessage := strings.Repeat("a", 11*1024)
+ bigMessageTag1ID := gittest.WriteTag(t, cfg, repoPath, "v1.7.0", commitID.Revision(), gittest.WriteTagConfig{Message: bigMessage})
+
+ // A tag with a commit id as its name
+ commitTagID := gittest.WriteTag(t, cfg, repoPath, commitID.String(), commitID.Revision(), gittest.WriteTagConfig{Message: "commit tag with a commit sha as the name"})
+
+ // a tag of a tag
+ tagOfTagID := gittest.WriteTag(t, cfg, repoPath, "tag-of-tag", commitTagID.Revision(), gittest.WriteTagConfig{Message: "tag of a tag"})
+
+ rpcRequest := &gitalypb.FindAllTagsRequest{Repository: repoProto}
+
+ c, err := client.FindAllTags(ctx, rpcRequest)
+ require.NoError(t, err)
+
+ var receivedTags []*gitalypb.Tag
+ for {
+ r, err := c.Recv()
+ if err == io.EOF {
+ break
+ }
+ require.NoError(t, err)
+ receivedTags = append(receivedTags, r.GetTags()...)
+ }
+
+ expectedTags := []*gitalypb.Tag{
+ {
+ Name: []byte(commitID),
+ Id: commitTagID.String(),
+ TargetCommit: gitCommit,
+ Message: []byte("commit tag with a commit sha as the name"),
+ MessageSize: 40,
+ Tagger: &gitalypb.CommitAuthor{
+ Name: []byte("Scrooge McDuck"),
+ Email: []byte("scrooge@mcduck.com"),
+ Date: &timestamppb.Timestamp{Seconds: 1572776879},
+ Timezone: []byte("+0100"),
+ },
+ },
+ {
+ Name: []byte("tag-of-tag"),
+ Id: tagOfTagID.String(),
+ TargetCommit: gitCommit,
+ Message: []byte("tag of a tag"),
+ MessageSize: 12,
+ Tagger: &gitalypb.CommitAuthor{
+ Name: []byte("Scrooge McDuck"),
+ Email: []byte("scrooge@mcduck.com"),
+ Date: &timestamppb.Timestamp{Seconds: 1572776879},
+ Timezone: []byte("+0100"),
+ },
+ },
+ {
+ Name: []byte("v1.0.0"),
+ Id: "f4e6814c3e4e7a0de82a9e7cd20c626cc963a2f8",
+ TargetCommit: gitCommit,
+ Message: []byte("Release"),
+ MessageSize: 7,
+ Tagger: &gitalypb.CommitAuthor{
+ Name: []byte("Dmitriy Zaporozhets"),
+ Email: []byte("dmitriy.zaporozhets@gmail.com"),
+ Date: &timestamppb.Timestamp{Seconds: 1393491299},
+ Timezone: []byte("+0200"),
+ },
+ },
+ {
+ Name: []byte("v1.1.0"),
+ Id: "8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b",
+ TargetCommit: testhelper.GitLabTestCommit("5937ac0a7beb003549fc5fd26fc247adbce4a52e"),
+ Message: []byte("Version 1.1.0"),
+ MessageSize: 13,
+ Tagger: &gitalypb.CommitAuthor{
+ Name: []byte("Dmitriy Zaporozhets"),
+ Email: []byte("dmitriy.zaporozhets@gmail.com"),
+ Date: &timestamppb.Timestamp{Seconds: 1393505709},
+ Timezone: []byte("+0200"),
+ },
+ },
+ {
+ Name: []byte("v1.1.1"),
+ Id: "8f03acbcd11c53d9c9468078f32a2622005a4841",
+ TargetCommit: testhelper.GitLabTestCommit("189a6c924013fc3fe40d6f1ec1dc20214183bc97"),
+ Message: []byte("x509 signed tag\n-----BEGIN SIGNED MESSAGE-----\nMIISfwYJKoZIhvcNAQcCoIIScDCCEmwCAQExDTALBglghkgBZQMEAgEwCwYJKoZI\nhvcNAQcBoIIP8zCCB3QwggVcoAMCAQICBBXXLOIwDQYJKoZIhvcNAQELBQAwgbYx\nCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCYXllcm4xETAPBgNVBAcMCE11ZW5jaGVu\nMRAwDgYDVQQKDAdTaWVtZW5zMREwDwYDVQQFEwhaWlpaWlpBNjEdMBsGA1UECwwU\nU2llbWVucyBUcnVzdCBDZW50ZXIxPzA9BgNVBAMMNlNpZW1lbnMgSXNzdWluZyBD\nQSBNZWRpdW0gU3RyZW5ndGggQXV0aGVudGljYXRpb24gMjAxNjAeFw0xNzAyMDMw\nNjU4MzNaFw0yMDAyMDMwNjU4MzNaMFsxETAPBgNVBAUTCFowMDBOV0RIMQ4wDAYD\nVQQqDAVSb2dlcjEOMAwGA1UEBAwFTWVpZXIxEDAOBgNVBAoMB1NpZW1lbnMxFDAS\nBgNVBAMMC01laWVyIFJvZ2VyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC\nAQEAuBNea/68ZCnHYQjpm/k3ZBG0wBpEKSwG6lk9CEQlSxsqVLQHAoAKBIlJm1in\nYVLcK/Sq1yhYJ/qWcY/M53DhK2rpPuhtrWJUdOUy8EBWO20F4bd4Fw9pO7jt8bme\nu33TSrK772vKjuppzB6SeG13Cs08H+BIeD106G27h7ufsO00pvsxoSDL+uc4slnr\npBL+2TAL7nSFnB9QHWmRIK27SPqJE+lESdb0pse11x1wjvqKy2Q7EjL9fpqJdHzX\nNLKHXd2r024TOORTa05DFTNR+kQEKKV96XfpYdtSBomXNQ44cisiPBJjFtYvfnFE\nwgrHa8fogn/b0C+A+HAoICN12wIDAQABo4IC4jCCAt4wHQYDVR0OBBYEFCF+gkUp\nXQ6xGc0kRWXuDFxzA14zMEMGA1UdEQQ8MDqgIwYKKwYBBAGCNxQCA6AVDBNyLm1l\naWVyQHNpZW1lbnMuY29tgRNyLm1laWVyQHNpZW1lbnMuY29tMA4GA1UdDwEB/wQE\nAwIHgDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwgcoGA1UdHwSBwjCB\nvzCBvKCBuaCBtoYmaHR0cDovL2NoLnNpZW1lbnMuY29tL3BraT9aWlpaWlpBNi5j\ncmyGQWxkYXA6Ly9jbC5zaWVtZW5zLm5ldC9DTj1aWlpaWlpBNixMPVBLST9jZXJ0\naWZpY2F0ZVJldm9jYXRpb25MaXN0hklsZGFwOi8vY2wuc2llbWVucy5jb20vQ049\nWlpaWlpaQTYsbz1UcnVzdGNlbnRlcj9jZXJ0aWZpY2F0ZVJldm9jYXRpb25MaXN0\nMEUGA1UdIAQ+MDwwOgYNKwYBBAGhaQcCAgMBAzApMCcGCCsGAQUFBwIBFhtodHRw\nOi8vd3d3LnNpZW1lbnMuY29tL3BraS8wDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAW\ngBT4FV1HDGx3e3LEAheRaKK292oJRDCCAQQGCCsGAQUFBwEBBIH3MIH0MDIGCCsG\nAQUFBzAChiZodHRwOi8vYWguc2llbWVucy5jb20vcGtpP1paWlpaWkE2LmNydDBB\nBggrBgEFBQcwAoY1bGRhcDovL2FsLnNpZW1lbnMubmV0L0NOPVpaWlpaWkE2LEw9\nUEtJP2NBQ2VydGlmaWNhdGUwSQYIKwYBBQUHMAKGPWxkYXA6Ly9hbC5zaWVtZW5z\nLmNvbS9DTj1aWlpaWlpBNixvPVRydXN0Y2VudGVyP2NBQ2VydGlmaWNhdGUwMAYI\nKwYBBQUHMAGGJGh0dHA6Ly9vY3NwLnBraS1zZXJ2aWNlcy5zaWVtZW5zLmNvbTAN\nBgkqhkiG9w0BAQsFAAOCAgEAXPVcX6vaEcszJqg5IemF9aFTlwTrX5ITNIpzcqG+\nkD5haOf2mZYLjl+MKtLC1XfmIsGCUZNb8bjP6QHQEI+2d6x/ZOqPq7Kd7PwVu6x6\nxZrkDjUyhUbUntT5+RBy++l3Wf6Cq6Kx+K8ambHBP/bu90/p2U8KfFAG3Kr2gI2q\nfZrnNMOxmJfZ3/sXxssgLkhbZ7hRa+MpLfQ6uFsSiat3vlawBBvTyHnoZ/7oRc8y\nqi6QzWcd76CPpMElYWibl+hJzKbBZUWvc71AzHR6i1QeZ6wubYz7vr+FF5Y7tnxB\nVz6omPC9XAg0F+Dla6Zlz3Awj5imCzVXa+9SjtnsidmJdLcKzTAKyDewewoxYOOJ\nj3cJU7VSjJPl+2fVmDBaQwcNcUcu/TPAKApkegqO7tRF9IPhjhW8QkRnkqMetO3D\nOXmAFVIsEI0Hvb2cdb7B6jSpjGUuhaFm9TCKhQtCk2p8JCDTuaENLm1x34rrJKbT\n2vzyYN0CZtSkUdgD4yQxK9VWXGEzexRisWb4AnZjD2NAquLPpXmw8N0UwFD7MSpC\ndpaX7FktdvZmMXsnGiAdtLSbBgLVWOD1gmJFDjrhNbI8NOaOaNk4jrfGqNh5lhGU\n4DnBT2U6Cie1anLmFH/oZooAEXR2o3Nu+1mNDJChnJp0ovs08aa3zZvBdcloOvfU\nqdowggh3MIIGX6ADAgECAgQtyi/nMA0GCSqGSIb3DQEBCwUAMIGZMQswCQYDVQQG\nEwJERTEPMA0GA1UECAwGQmF5ZXJuMREwDwYDVQQHDAhNdWVuY2hlbjEQMA4GA1UE\nCgwHU2llbWVuczERMA8GA1UEBRMIWlpaWlpaQTExHTAbBgNVBAsMFFNpZW1lbnMg\nVHJ1c3QgQ2VudGVyMSIwIAYDVQQDDBlTaWVtZW5zIFJvb3QgQ0EgVjMuMCAyMDE2\nMB4XDTE2MDcyMDEzNDYxMFoXDTIyMDcyMDEzNDYxMFowgbYxCzAJBgNVBAYTAkRF\nMQ8wDQYDVQQIDAZCYXllcm4xETAPBgNVBAcMCE11ZW5jaGVuMRAwDgYDVQQKDAdT\naWVtZW5zMREwDwYDVQQFEwhaWlpaWlpBNjEdMBsGA1UECwwUU2llbWVucyBUcnVz\ndCBDZW50ZXIxPzA9BgNVBAMMNlNpZW1lbnMgSXNzdWluZyBDQSBNZWRpdW0gU3Ry\nZW5ndGggQXV0aGVudGljYXRpb24gMjAxNjCCAiIwDQYJKoZIhvcNAQEBBQADggIP\nADCCAgoCggIBAL9UfK+JAZEqVMVvECdYF9IK4KSw34AqyNl3rYP5x03dtmKaNu+2\n0fQqNESA1NGzw3s6LmrKLh1cR991nB2cvKOXu7AvEGpSuxzIcOROd4NpvRx+Ej1p\nJIPeqf+ScmVK7lMSO8QL/QzjHOpGV3is9sG+ZIxOW9U1ESooy4Hal6ZNs4DNItsz\npiCKqm6G3et4r2WqCy2RRuSqvnmMza7Y8BZsLy0ZVo5teObQ37E/FxqSrbDI8nxn\nB7nVUve5ZjrqoIGSkEOtyo11003dVO1vmWB9A0WQGDqE/q3w178hGhKfxzRaqzyi\nSoADUYS2sD/CglGTUxVq6u0pGLLsCFjItcCWqW+T9fPYfJ2CEd5b3hvqdCn+pXjZ\n/gdX1XAcdUF5lRnGWifaYpT9n4s4adzX8q6oHSJxTppuAwLRKH6eXALbGQ1I9lGQ\nDSOipD/09xkEsPw6HOepmf2U3YxZK1VU2sHqugFJboeLcHMzp6E1n2ctlNG1GKE9\nFDHmdyFzDi0Nnxtf/GgVjnHF68hByEE1MYdJ4nJLuxoT9hyjYdRW9MpeNNxxZnmz\nW3zh7QxIqP0ZfIz6XVhzrI9uZiqwwojDiM5tEOUkQ7XyW6grNXe75yt6mTj89LlB\nH5fOW2RNmCy/jzBXDjgyskgK7kuCvUYTuRv8ITXbBY5axFA+CpxZqokpAgMBAAGj\nggKmMIICojCCAQUGCCsGAQUFBwEBBIH4MIH1MEEGCCsGAQUFBzAChjVsZGFwOi8v\nYWwuc2llbWVucy5uZXQvQ049WlpaWlpaQTEsTD1QS0k/Y0FDZXJ0aWZpY2F0ZTAy\nBggrBgEFBQcwAoYmaHR0cDovL2FoLnNpZW1lbnMuY29tL3BraT9aWlpaWlpBMS5j\ncnQwSgYIKwYBBQUHMAKGPmxkYXA6Ly9hbC5zaWVtZW5zLmNvbS91aWQ9WlpaWlpa\nQTEsbz1UcnVzdGNlbnRlcj9jQUNlcnRpZmljYXRlMDAGCCsGAQUFBzABhiRodHRw\nOi8vb2NzcC5wa2ktc2VydmljZXMuc2llbWVucy5jb20wHwYDVR0jBBgwFoAUcG2g\nUOyp0CxnnRkV/v0EczXD4tQwEgYDVR0TAQH/BAgwBgEB/wIBADBABgNVHSAEOTA3\nMDUGCCsGAQQBoWkHMCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93d3cuc2llbWVucy5j\nb20vcGtpLzCBxwYDVR0fBIG/MIG8MIG5oIG2oIGzhj9sZGFwOi8vY2wuc2llbWVu\ncy5uZXQvQ049WlpaWlpaQTEsTD1QS0k/YXV0aG9yaXR5UmV2b2NhdGlvbkxpc3SG\nJmh0dHA6Ly9jaC5zaWVtZW5zLmNvbS9wa2k/WlpaWlpaQTEuY3JshkhsZGFwOi8v\nY2wuc2llbWVucy5jb20vdWlkPVpaWlpaWkExLG89VHJ1c3RjZW50ZXI/YXV0aG9y\naXR5UmV2b2NhdGlvbkxpc3QwJwYDVR0lBCAwHgYIKwYBBQUHAwIGCCsGAQUFBwME\nBggrBgEFBQcDCTAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFPgVXUcMbHd7csQC\nF5Foorb3aglEMA0GCSqGSIb3DQEBCwUAA4ICAQBw+sqMp3SS7DVKcILEmXbdRAg3\nlLO1r457KY+YgCT9uX4VG5EdRKcGfWXK6VHGCi4Dos5eXFV34Mq/p8nu1sqMuoGP\nYjHn604eWDprhGy6GrTYdxzcE/GGHkpkuE3Ir/45UcmZlOU41SJ9SNjuIVrSHMOf\nccSY42BCspR/Q1Z/ykmIqQecdT3/Kkx02GzzSN2+HlW6cEO4GBW5RMqsvd2n0h2d\nfe2zcqOgkLtx7u2JCR/U77zfyxG3qXtcymoz0wgSHcsKIl+GUjITLkHfS9Op8V7C\nGr/dX437sIg5pVHmEAWadjkIzqdHux+EF94Z6kaHywohc1xG0KvPYPX7iSNjkvhz\n4NY53DHmxl4YEMLffZnaS/dqyhe1GTpcpyN8WiR4KuPfxrkVDOsuzWFtMSvNdlOV\ngdI0MXcLMP+EOeANZWX6lGgJ3vWyemo58nzgshKd24MY3w3i6masUkxJH2KvI7UH\n/1Db3SC8oOUjInvSRej6M3ZhYWgugm6gbpUgFoDw/o9Cg6Qm71hY0JtcaPC13rzm\nN8a2Br0+Fa5e2VhwLmAxyfe1JKzqPwuHT0S5u05SQghL5VdzqfA8FCL/j4XC9yI6\ncsZTAQi73xFQYVjZt3+aoSz84lOlTmVo/jgvGMY/JzH9I4mETGgAJRNj34Z/0meh\nM+pKWCojNH/dgyJSwDGCAlIwggJOAgEBMIG/MIG2MQswCQYDVQQGEwJERTEPMA0G\nA1UECAwGQmF5ZXJuMREwDwYDVQQHDAhNdWVuY2hlbjEQMA4GA1UECgwHU2llbWVu\nczERMA8GA1UEBRMIWlpaWlpaQTYxHTAbBgNVBAsMFFNpZW1lbnMgVHJ1c3QgQ2Vu\ndGVyMT8wPQYDVQQDDDZTaWVtZW5zIElzc3VpbmcgQ0EgTWVkaXVtIFN0cmVuZ3Ro\nIEF1dGhlbnRpY2F0aW9uIDIwMTYCBBXXLOIwCwYJYIZIAWUDBAIBoGkwHAYJKoZI\nhvcNAQkFMQ8XDTE5MTEyMDE0NTYyMFowLwYJKoZIhvcNAQkEMSIEIJDnZUpcVLzC\nOdtpkH8gtxwLPIDE0NmAmFC9uM8q2z+OMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0B\nBwEwCwYJKoZIhvcNAQEBBIIBAH/Pqv2xp3a0jSPkwU1K3eGA/1lfoNJMUny4d/PS\nLVWlkgrmedXdLmuBzAGEaaZOJS0lEpNd01pR/reHs7xxZ+RZ0olTs2ufM0CijQSx\nOL9HDl2O3OoD77NWx4tl3Wy1yJCeV3XH/cEI7AkKHCmKY9QMoMYWh16ORBtr+YcS\nYK+gONOjpjgcgTJgZ3HSFgQ50xiD4WT1kFBHsuYsLqaOSbTfTN6Ayyg4edjrPQqa\nVcVf1OQcIrfWA3yMQrnEZfOYfN/D4EPjTfxBV+VCi/F2bdZmMbJ7jNk1FbewSwWO\nSDH1i0K32NyFbnh0BSos7njq7ELqKlYBsoB/sZfaH2vKy5U=\n-----END SIGNED MESSAGE-----"),
+ MessageSize: 6494,
+ Tagger: &gitalypb.CommitAuthor{
+ Name: []byte("Roger Meier"),
+ Email: []byte("r.meier@siemens.com"),
+ Date: &timestamppb.Timestamp{Seconds: 1574261780},
+ Timezone: []byte("+0100"),
+ },
+ SignatureType: gitalypb.SignatureType_X509,
+ },
+ {
+ Name: []byte("pgp-long-tag-message"),
+ Id: truncatedPGPTagID,
+ TargetCommit: gitCommit, // 6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9
+ Message: truncatedPGPTagMsg[146:10386], // first 10240 bytes of tag message
+ MessageSize: 11148,
+ Tagger: &gitalypb.CommitAuthor{
+ Name: []byte("Scrooge McDuck"),
+ Email: []byte("scrooge@mcduck.com"),
+ Date: &timestamppb.Timestamp{Seconds: 1393491261},
+ Timezone: []byte("+0100"),
+ },
+ SignatureType: gitalypb.SignatureType_PGP,
+ },
+ {
+ Name: []byte("v1.2.0"),
+ Id: annotatedTagID.String(),
+ Message: []byte("Blob tag"),
+ MessageSize: 8,
+ Tagger: &gitalypb.CommitAuthor{
+ Name: []byte("Scrooge McDuck"),
+ Email: []byte("scrooge@mcduck.com"),
+ Date: &timestamppb.Timestamp{Seconds: 1572776879},
+ Timezone: []byte("+0100"),
+ },
+ },
+ {
+ Name: []byte("v1.3.0"),
+ Id: commitID.String(),
+ TargetCommit: gitCommit,
+ },
+ {
+ Name: []byte("v1.4.0"),
+ Id: blobID.String(),
+ },
+ {
+ Name: []byte("v1.5.0"),
+ Id: commitID.String(),
+ TargetCommit: gitCommit,
+ },
+ {
+ Name: []byte("v1.6.0"),
+ Id: bigCommitID.String(),
+ TargetCommit: bigCommit,
+ },
+ {
+ Name: []byte("v1.7.0"),
+ Id: bigMessageTag1ID.String(),
+ Message: []byte(bigMessage[:helper.MaxCommitOrTagMessageSize]),
+ MessageSize: int64(len(bigMessage)),
+ TargetCommit: gitCommit,
+ Tagger: &gitalypb.CommitAuthor{
+ Name: []byte("Scrooge McDuck"),
+ Email: []byte("scrooge@mcduck.com"),
+ Date: &timestamppb.Timestamp{Seconds: 1572776879},
+ Timezone: []byte("+0100"),
+ },
+ },
+ }
+
+ require.Len(t, receivedTags, len(expectedTags))
+ require.ElementsMatch(t, expectedTags, receivedTags)
+}
+
+func TestFindAllTags_simpleNestedTags(t *testing.T) {
+ cfg, client := setupRefServiceWithoutRepo(t)
+
+ ctx, cancel := testhelper.Context()
+ defer cancel()
+
+ repoProto, repoPath := gittest.InitRepo(t, cfg, cfg.Storages[0])
+
+ commitID := gittest.WriteCommit(t, cfg, repoPath,
+ gittest.WithParents(),
+ )
+
+ tagID := gittest.WriteTag(t, cfg, repoPath, "my/nested/tag", commitID.Revision())
+
+ stream, err := client.FindAllTags(ctx, &gitalypb.FindAllTagsRequest{Repository: repoProto})
+ require.NoError(t, err)
+
+ response, err := stream.Recv()
+ require.NoError(t, err)
+ testassert.ProtoEqual(t, &gitalypb.FindAllTagsResponse{
+ Tags: []*gitalypb.Tag{
+ {
+ Name: []byte("my/nested/tag"),
+ Id: tagID.String(),
+ TargetCommit: &gitalypb.GitCommit{
+ Id: commitID.String(),
+ Body: []byte("message"),
+ BodySize: 7,
+ Subject: []byte("message"),
+ TreeId: git.EmptyTreeOID.String(),
+ Author: &gitalypb.CommitAuthor{
+ Name: []byte("Scrooge McDuck"),
+ Email: []byte("scrooge@mcduck.com"),
+ Date: &timestamppb.Timestamp{Seconds: 1572776879},
+ Timezone: []byte("+0100"),
+ },
+ Committer: &gitalypb.CommitAuthor{
+ Name: []byte("Scrooge McDuck"),
+ Email: []byte("scrooge@mcduck.com"),
+ Date: &timestamppb.Timestamp{Seconds: 1572776879},
+ Timezone: []byte("+0100"),
+ },
+ },
+ },
+ },
+ }, response)
+
+ response, err = stream.Recv()
+ require.Equal(t, io.EOF, err)
+ require.Nil(t, response)
+}
+
+func TestFindAllTags_duplicateAnnotatedTags(t *testing.T) {
+ cfg, client := setupRefServiceWithoutRepo(t)
+
+ repoProto, repoPath := gittest.InitRepo(t, cfg, cfg.Storages[0])
+ repo := localrepo.NewTestRepo(t, cfg, repoProto)
+
+ ctx, cancel := testhelper.Context()
+ defer cancel()
+
+ commitID := gittest.WriteCommit(t, cfg, repoPath, gittest.WithParents())
+ date := time.Unix(12345, 0)
+
+ tagID, err := repo.WriteTag(ctx, commitID, "commit", []byte("annotated"), []byte("message"),
+ gittest.TestUser, date)
+ require.NoError(t, err)
+
+ require.NoError(t, repo.UpdateRef(ctx, "refs/tags/annotated", tagID, git.ZeroOID))
+ require.NoError(t, repo.UpdateRef(ctx, "refs/tags/annotated-dup", tagID, git.ZeroOID))
+ require.NoError(t, repo.UpdateRef(ctx, "refs/tags/lightweight-1", commitID, git.ZeroOID))
+ require.NoError(t, repo.UpdateRef(ctx, "refs/tags/lightweight-2", commitID, git.ZeroOID))
+
+ c, err := client.FindAllTags(ctx, &gitalypb.FindAllTagsRequest{Repository: repoProto})
+ require.NoError(t, err)
+
+ var receivedTags []*gitalypb.Tag
+ for {
+ r, err := c.Recv()
+ if err == io.EOF {
+ break
+ }
+ require.NoError(t, err)
+ receivedTags = append(receivedTags, r.GetTags()...)
+ }
+
+ commitAuthor := &gitalypb.CommitAuthor{
+ Name: []byte("Scrooge McDuck"),
+ Email: []byte("scrooge@mcduck.com"),
+ Date: &timestamppb.Timestamp{Seconds: 1572776879},
+ Timezone: []byte("+0100"),
+ }
+ commit := &gitalypb.GitCommit{
+ Id: commitID.String(),
+ TreeId: "4b825dc642cb6eb9a060e54bf8d69288fbee4904",
+ Body: []byte("message"),
+ BodySize: 7,
+ Subject: []byte("message"),
+ Author: commitAuthor,
+ Committer: commitAuthor,
+ }
+
+ testassert.ProtoEqual(t, []*gitalypb.Tag{
+ {
+ Name: []byte("annotated"),
+ Id: tagID.String(),
+ Message: []byte("message"),
+ MessageSize: 7,
+ Tagger: &gitalypb.CommitAuthor{
+ Name: gittest.TestUser.Name,
+ Email: gittest.TestUser.Email,
+ Date: timestamppb.New(date),
+ Timezone: []byte("+0000"),
+ },
+ TargetCommit: commit,
+ },
+ {
+ Name: []byte("annotated-dup"),
+ Id: tagID.String(),
+ Message: []byte("message"),
+ MessageSize: 7,
+ Tagger: &gitalypb.CommitAuthor{
+ Name: gittest.TestUser.Name,
+ Email: gittest.TestUser.Email,
+ Date: timestamppb.New(date),
+ Timezone: []byte("+0000"),
+ },
+ TargetCommit: commit,
+ },
+ {Name: []byte("lightweight-1"), Id: commitID.String(), TargetCommit: commit},
+ {Name: []byte("lightweight-2"), Id: commitID.String(), TargetCommit: commit},
+ }, receivedTags)
+}
+
+func TestFindAllTags_nestedTags(t *testing.T) {
+ cfg, client := setupRefServiceWithoutRepo(t)
+
+ ctx, cancel := testhelper.Context()
+ defer cancel()
+
+ repoProto, repoPath := gittest.CloneRepo(t, cfg, cfg.Storages[0])
+ repo := localrepo.NewTestRepo(t, cfg, repoProto)
+
+ blobID := git.ObjectID("faaf198af3a36dbf41961466703cc1d47c61d051")
+ commitID := git.ObjectID("6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9")
+
+ testCases := []struct {
+ description string
+ depth int
+ originalOid git.ObjectID
+ }{
+ {
+ description: "nested 1 deep, points to a commit",
+ depth: 1,
+ originalOid: commitID,
+ },
+ {
+ description: "nested 4 deep, points to a commit",
+ depth: 4,
+ originalOid: commitID,
+ },
+ {
+ description: "nested 3 deep, points to a blob",
+ depth: 3,
+ originalOid: blobID,
+ },
+ {
+ description: "nested 20 deep, points to a commit",
+ depth: 20,
+ originalOid: commitID,
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.description, func(t *testing.T) {
+ tags := bytes.NewReader(gittest.Exec(t, cfg, "-C", repoPath, "tag"))
+ testhelper.MustRunCommand(t, tags, "xargs", cfg.Git.BinPath, "-C", repoPath, "tag", "-d")
+
+ catfileCache := catfile.NewCache(cfg)
+ batch, err := catfileCache.BatchProcess(ctx, repo)
+ require.NoError(t, err)
+
+ info, err := batch.Info(ctx, git.Revision(tc.originalOid))
+ require.NoError(t, err)
+
+ expectedTags := make(map[string]*gitalypb.Tag)
+ tagID := tc.originalOid
+
+ for depth := 0; depth < tc.depth; depth++ {
+ tagName := fmt.Sprintf("tag-depth-%d", depth)
+ tagMessage := fmt.Sprintf("a commit %d deep", depth)
+ tagID = gittest.WriteTag(t, cfg, repoPath, tagName, tagID.Revision(), gittest.WriteTagConfig{Message: tagMessage})
+
+ expectedTag := &gitalypb.Tag{
+ Name: []byte(tagName),
+ Id: tagID.String(),
+ Message: []byte(tagMessage),
+ MessageSize: int64(len([]byte(tagMessage))),
+ Tagger: &gitalypb.CommitAuthor{
+ Name: []byte("Scrooge McDuck"),
+ Email: []byte("scrooge@mcduck.com"),
+ Date: &timestamppb.Timestamp{Seconds: 1572776879},
+ Timezone: []byte("+0100"),
+ },
+ }
+
+ if info.Type == "commit" {
+ commit, err := catfile.GetCommit(ctx, batch, git.Revision(tc.originalOid))
+ require.NoError(t, err)
+ expectedTag.TargetCommit = commit
+ }
+
+ expectedTags[string(expectedTag.Name)] = expectedTag
+ }
+
+ rpcRequest := &gitalypb.FindAllTagsRequest{Repository: repoProto}
+
+ c, err := client.FindAllTags(ctx, rpcRequest)
+ require.NoError(t, err)
+
+ var receivedTags []*gitalypb.Tag
+ for {
+ r, err := c.Recv()
+ if err == io.EOF {
+ break
+ }
+ require.NoError(t, err)
+ receivedTags = append(receivedTags, r.GetTags()...)
+ }
+
+ require.Len(t, receivedTags, len(expectedTags))
+ for _, receivedTag := range receivedTags {
+ assert.Equal(t, expectedTags[string(receivedTag.Name)], receivedTag)
+ }
+ })
+ }
+}
+
+func TestFindAllTags_invalidRequest(t *testing.T) {
+ _, client := setupRefServiceWithoutRepo(t)
+
+ ctx, cancel := testhelper.Context()
+ defer cancel()
+
+ testCases := []struct {
+ desc string
+ request *gitalypb.FindAllTagsRequest
+ }{
+ {
+ desc: "empty request",
+ request: &gitalypb.FindAllTagsRequest{},
+ },
+ {
+ desc: "invalid repo",
+ request: &gitalypb.FindAllTagsRequest{
+ Repository: &gitalypb.Repository{
+ StorageName: "fake",
+ RelativePath: "repo",
+ },
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.desc, func(t *testing.T) {
+ c, err := client.FindAllTags(ctx, tc.request)
+ require.NoError(t, err)
+
+ var recvError error
+ for recvError == nil {
+ _, recvError = c.Recv()
+ }
+
+ testhelper.RequireGrpcError(t, recvError, codes.InvalidArgument)
+ })
+ }
+}
+
+func TestFindAllTags_sorted(t *testing.T) {
+ cfg, client := setupRefServiceWithoutRepo(t)
+
+ ctx, cancel := testhelper.Context()
+ defer cancel()
+
+ repoProto, _ := gittest.CloneRepo(t, cfg, cfg.Storages[0])
+
+ repo := localrepo.New(git.NewExecCommandFactory(cfg), catfile.NewCache(cfg), repoProto, cfg)
+ headCommit, err := repo.ReadCommit(ctx, "HEAD")
+ require.NoError(t, err)
+ annotatedTagID, err := repo.WriteTag(ctx, git.ObjectID(headCommit.Id), "commit", []byte("annotated"), []byte("message"), gittest.TestUser, time.Now())
+ require.NoError(t, err)
+ require.NoError(t, repo.UpdateRef(ctx, "refs/tags/annotated", annotatedTagID, git.ZeroOID))
+
+ require.NoError(t, repo.ExecAndWait(ctx, git.SubCmd{
+ Name: "tag",
+ Args: []string{"not-annotated", headCommit.Id},
+ }, git.WithDisabledHooks()))
+
+ for _, tc := range []struct {
+ desc string
+ sortBy *gitalypb.FindAllTagsRequest_SortBy
+ exp []string
+ }{
+ {
+ desc: "by name",
+ sortBy: &gitalypb.FindAllTagsRequest_SortBy{Key: gitalypb.FindAllTagsRequest_SortBy_REFNAME},
+ exp: []string{
+ annotatedTagID.String(),
+ headCommit.Id,
+ "f4e6814c3e4e7a0de82a9e7cd20c626cc963a2f8",
+ "8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b",
+ "8f03acbcd11c53d9c9468078f32a2622005a4841",
+ },
+ },
+ {
+ desc: "by updated in ascending order",
+ sortBy: &gitalypb.FindAllTagsRequest_SortBy{Key: gitalypb.FindAllTagsRequest_SortBy_CREATORDATE, Direction: gitalypb.SortDirection_ASCENDING},
+ exp: []string{
+ "f4e6814c3e4e7a0de82a9e7cd20c626cc963a2f8",
+ "8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b",
+ headCommit.Id,
+ "8f03acbcd11c53d9c9468078f32a2622005a4841",
+ annotatedTagID.String(),
+ },
+ },
+ {
+ desc: "by updated in descending order",
+ sortBy: &gitalypb.FindAllTagsRequest_SortBy{Key: gitalypb.FindAllTagsRequest_SortBy_CREATORDATE, Direction: gitalypb.SortDirection_DESCENDING},
+ exp: []string{
+ annotatedTagID.String(),
+ "8f03acbcd11c53d9c9468078f32a2622005a4841",
+ headCommit.Id,
+ "8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b",
+ "f4e6814c3e4e7a0de82a9e7cd20c626cc963a2f8",
+ },
+ },
+ } {
+ t.Run(tc.desc, func(t *testing.T) {
+ c, err := client.FindAllTags(ctx, &gitalypb.FindAllTagsRequest{
+ Repository: repoProto,
+ SortBy: tc.sortBy,
+ })
+ require.NoError(t, err)
+
+ var tags []string
+ for {
+ r, err := c.Recv()
+ if err == io.EOF {
+ break
+ }
+ require.NoError(t, err)
+ for _, tag := range r.GetTags() {
+ tags = append(tags, tag.Id)
+ }
+ }
+
+ require.Equal(t, tc.exp, tags)
+ })
+ }
+
+ t.Run("by unsupported key", func(t *testing.T) {
+ c, err := client.FindAllTags(ctx, &gitalypb.FindAllTagsRequest{
+ Repository: repoProto,
+ SortBy: &gitalypb.FindAllTagsRequest_SortBy{Key: gitalypb.FindAllTagsRequest_SortBy_Key(-1)},
+ })
+ require.NoError(t, err)
+ r, err := c.Recv()
+ testassert.GrpcEqualErr(t, status.Error(codes.InvalidArgument, "unsupported sorting key: -1"), err)
+ require.Nil(t, r)
+ })
+
+ t.Run("by unsupported direction", func(t *testing.T) {
+ c, err := client.FindAllTags(ctx, &gitalypb.FindAllTagsRequest{
+ Repository: repoProto,
+ SortBy: &gitalypb.FindAllTagsRequest_SortBy{Key: gitalypb.FindAllTagsRequest_SortBy_REFNAME, Direction: gitalypb.SortDirection(-1)},
+ })
+ require.NoError(t, err)
+ r, err := c.Recv()
+ testassert.GrpcEqualErr(t, status.Error(codes.InvalidArgument, "unsupported sorting direction: -1"), err)
+ require.Nil(t, r)
+ })
+
+ t.Run("no tags", func(t *testing.T) {
+ repoProto, _ := gittest.InitRepo(t, cfg, cfg.Storages[0])
+ c, err := client.FindAllTags(ctx, &gitalypb.FindAllTagsRequest{
+ Repository: repoProto,
+ SortBy: &gitalypb.FindAllTagsRequest_SortBy{Key: gitalypb.FindAllTagsRequest_SortBy_REFNAME},
+ })
+ require.NoError(t, err)
+
+ r, err := c.Recv()
+ require.Equal(t, io.EOF, err)
+ require.Nil(t, r)
+ })
+}
diff --git a/internal/gitaly/service/ref/refs.go b/internal/gitaly/service/ref/refs.go
index 7e149b674..c9fd8d56a 100644
--- a/internal/gitaly/service/ref/refs.go
+++ b/internal/gitaly/service/ref/refs.go
@@ -6,21 +6,15 @@ import (
"context"
"errors"
"fmt"
- "io"
- "io/ioutil"
"math"
"strings"
"gitlab.com/gitlab-org/gitaly/v14/internal/git"
"gitlab.com/gitlab-org/gitaly/v14/internal/git/catfile"
- "gitlab.com/gitlab-org/gitaly/v14/internal/git/gitpipe"
- "gitlab.com/gitlab-org/gitaly/v14/internal/git/localrepo"
"gitlab.com/gitlab-org/gitaly/v14/internal/gitaly/config"
"gitlab.com/gitlab-org/gitaly/v14/internal/helper"
- "gitlab.com/gitlab-org/gitaly/v14/internal/helper/chunk"
"gitlab.com/gitlab-org/gitaly/v14/internal/helper/lines"
"gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb"
- "google.golang.org/protobuf/proto"
)
const (
@@ -62,205 +56,6 @@ func (s *server) findRefs(ctx context.Context, writer lines.Sender, repo git.Rep
return cmd.Wait()
}
-type tagSender struct {
- tags []*gitalypb.Tag
- stream gitalypb.RefService_FindAllTagsServer
-}
-
-func (t *tagSender) Reset() {
- t.tags = t.tags[:0]
-}
-
-func (t *tagSender) Append(m proto.Message) {
- t.tags = append(t.tags, m.(*gitalypb.Tag))
-}
-
-func (t *tagSender) Send() error {
- return t.stream.Send(&gitalypb.FindAllTagsResponse{
- Tags: t.tags,
- })
-}
-
-func (s *server) FindAllTags(in *gitalypb.FindAllTagsRequest, stream gitalypb.RefService_FindAllTagsServer) error {
- ctx := stream.Context()
-
- if err := s.validateFindAllTagsRequest(in); err != nil {
- return helper.ErrInvalidArgument(err)
- }
-
- sortField, err := getTagSortField(in.GetSortBy())
- if err != nil {
- return helper.ErrInvalidArgument(err)
- }
-
- repo := s.localrepo(in.GetRepository())
-
- if err := s.findAllTags(ctx, repo, sortField, stream); err != nil {
- return helper.ErrInternal(err)
- }
-
- return nil
-}
-
-func (s *server) findAllTags(ctx context.Context, repo *localrepo.Repo, sortField string, stream gitalypb.RefService_FindAllTagsServer) error {
- c, err := s.catfileCache.BatchProcess(ctx, repo)
- if err != nil {
- return fmt.Errorf("error creating catfile: %v", err)
- }
-
- forEachRefIter := gitpipe.ForEachRef(ctx, repo, []string{"refs/tags/"}, sortField)
- forEachRefIter = gitpipe.RevisionTransform(ctx, forEachRefIter,
- func(r gitpipe.RevisionResult) []gitpipe.RevisionResult {
- // We transform the pipeline to include each tag-reference twice: once for
- // the "normal" object, and once we opportunistically peel the object to a
- // non-tag object. This is required such that we can efficiently parse the
- // tagged object.
- return []gitpipe.RevisionResult{
- r,
- {OID: r.OID + "^{}"},
- }
- },
- )
-
- catfileInfoIter := gitpipe.CatfileInfo(ctx, c, forEachRefIter)
-
- // In the previous pipeline step, we request information about both the object and the
- // peeled object in case the object is a tag. Given that we now know about object types, we
- // can filter out the second request in case the object is not a tag: peeling a non-tag
- // object to a non-tag object is always going to end up with the same object anyway. And
- // requesting the same object twice is moot.
- type state int
- const (
- // stateTag indicates that the next object is going to be a tag.
- stateTag = state(iota)
- // statePeeledTag indicates that the next object is going to be the peeled object of
- // the preceding tag.
- statePeeledTag
- // stateSkip indicates that the next object shall be skipped because it is the
- // peeled version of a non-tag object, which is the same object anyway.
- stateSkip
- )
-
- currentState := stateTag
- catfileInfoIter = gitpipe.CatfileInfoFilter(ctx, catfileInfoIter,
- func(r gitpipe.CatfileInfoResult) bool {
- switch currentState {
- case stateTag:
- // If we've got a tag, then we want to also see its peeled object.
- // Otherwise, we can skip over the peeled object.
- currentState = statePeeledTag
- if r.ObjectInfo.Type != "tag" {
- currentState = stateSkip
- }
- return true
- case statePeeledTag:
- currentState = stateTag
- return true
- case stateSkip:
- currentState = stateTag
- return false
- }
-
- // We could try to gracefully handle this, but I don't see much of a point
- // given that we can see above that it's never going to be anything else but
- // a known state.
- panic("invalid state")
- },
- )
-
- catfileObjectsIter := gitpipe.CatfileObject(ctx, c, catfileInfoIter)
-
- chunker := chunk.New(&tagSender{stream: stream})
-
- for catfileObjectsIter.Next() {
- tag := catfileObjectsIter.Result()
-
- var result *gitalypb.Tag
- switch tag.ObjectInfo.Type {
- case "tag":
- var err error
- result, err = catfile.ParseTag(tag.ObjectReader, tag.ObjectInfo.Oid)
- if err != nil {
- return fmt.Errorf("parsing annotated tag: %w", err)
- }
-
- // For each tag, we expect both the tag itself as well as its
- // potentially-peeled tagged object.
- if !catfileObjectsIter.Next() {
- return errors.New("expected peeled tag")
- }
-
- peeledTag := catfileObjectsIter.Result()
-
- // We only need to parse the tagged object in case we have an annotated tag
- // which refers to a commit object. Otherwise, we discard the object's
- // contents.
- if peeledTag.ObjectInfo.Type == "commit" {
- result.TargetCommit, err = catfile.ParseCommit(peeledTag.ObjectReader, peeledTag.ObjectInfo.Oid)
- if err != nil {
- return fmt.Errorf("parsing tagged commit: %w", err)
- }
- } else {
- if _, err := io.Copy(ioutil.Discard, peeledTag.ObjectReader); err != nil {
- return fmt.Errorf("discarding tagged object contents: %w", err)
- }
- }
- case "commit":
- commit, err := catfile.ParseCommit(tag.ObjectReader, tag.ObjectInfo.Oid)
- if err != nil {
- return fmt.Errorf("parsing tagged commit: %w", err)
- }
-
- result = &gitalypb.Tag{
- Id: tag.ObjectInfo.Oid.String(),
- TargetCommit: commit,
- }
- default:
- if _, err := io.Copy(ioutil.Discard, tag.ObjectReader); err != nil {
- return fmt.Errorf("discarding tag object contents: %w", err)
- }
-
- result = &gitalypb.Tag{
- Id: tag.ObjectInfo.Oid.String(),
- }
- }
-
- // In case we can deduce the tag name from the object name (which should typically
- // be the case), we always want to return the tag name. While annotated tags do have
- // their name encoded in the object itself, we instead want to default to the name
- // of the reference such that we can discern multiple refs pointing to the same tag.
- if tagName := bytes.TrimPrefix(tag.ObjectName, []byte("refs/tags/")); len(tagName) > 0 {
- result.Name = tagName
- }
-
- if err := chunker.Send(result); err != nil {
- return fmt.Errorf("sending tag: %w", err)
- }
- }
-
- if err := catfileObjectsIter.Err(); err != nil {
- return fmt.Errorf("iterating over tags: %w", err)
- }
-
- if err := chunker.Flush(); err != nil {
- return fmt.Errorf("flushing chunker: %w", err)
- }
-
- return nil
-}
-
-func (s *server) validateFindAllTagsRequest(request *gitalypb.FindAllTagsRequest) error {
- if request.GetRepository() == nil {
- return errors.New("empty Repository")
- }
-
- if _, err := s.locator.GetRepoPath(request.GetRepository()); err != nil {
- return fmt.Errorf("invalid git directory: %v", err)
- }
-
- return nil
-}
-
// FindBranchNames returns all branch names.
//
// Deprecated: Use localrepo.Repo.GetBranches instead.
diff --git a/internal/gitaly/service/ref/refs_test.go b/internal/gitaly/service/ref/refs_test.go
index cbfb7383c..d7560ec4c 100644
--- a/internal/gitaly/service/ref/refs_test.go
+++ b/internal/gitaly/service/ref/refs_test.go
@@ -7,9 +7,7 @@ import (
"io"
"strings"
"testing"
- "time"
- "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gitlab.com/gitlab-org/gitaly/v14/internal/git"
"gitlab.com/gitlab-org/gitaly/v14/internal/git/catfile"
@@ -22,7 +20,6 @@ import (
"gitlab.com/gitlab-org/gitaly/v14/internal/testhelper/testcfg"
"gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb"
"google.golang.org/grpc/codes"
- "google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/timestamppb"
)
@@ -313,599 +310,6 @@ func TestInvalidRepoFindDefaultBranchNameRequest(t *testing.T) {
}
}
-func TestSuccessfulFindAllTagsRequest(t *testing.T) {
- cfg, client := setupRefServiceWithoutRepo(t)
-
- repoProto, repoPath := gittest.CloneRepo(t, cfg, cfg.Storages[0])
- repo := localrepo.NewTestRepo(t, cfg, repoProto)
-
- ctx, cancel := testhelper.Context()
- defer cancel()
-
- // reconstruct the v1.1.2 tag from patches to test truncated tag message
- // with partial PGP block
- truncatedPGPTagMsg := testhelper.MustReadFile(t, "testdata/truncated_pgp_msg.patch")
-
- truncatedPGPTagID := string(gittest.ExecStream(t, cfg, bytes.NewBuffer(truncatedPGPTagMsg), "-C", repoPath, "mktag"))
- truncatedPGPTagID = strings.TrimSpace(truncatedPGPTagID) // remove trailing newline
- gittest.Exec(t, cfg, "-C", repoPath, "update-ref", "refs/tags/pgp-long-tag-message", truncatedPGPTagID)
-
- blobID := "faaf198af3a36dbf41961466703cc1d47c61d051"
- commitID := "6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9"
-
- gitCommit := testhelper.GitLabTestCommit(commitID)
-
- bigCommitID := gittest.WriteCommit(t, cfg, repoPath,
- gittest.WithBranch("local-big-commits"),
- gittest.WithMessage("An empty commit with REALLY BIG message\n\n"+strings.Repeat("a", helper.MaxCommitOrTagMessageSize+1)),
- gittest.WithParents("60ecb67744cb56576c30214ff52294f8ce2def98"),
- )
- bigCommit, err := repo.ReadCommit(ctx, git.Revision(bigCommitID))
- require.NoError(t, err)
-
- annotatedTagID := gittest.CreateTag(t, cfg, repoPath, "v1.2.0", blobID, &gittest.CreateTagOpts{Message: "Blob tag"})
-
- gittest.CreateTag(t, cfg, repoPath, "v1.3.0", commitID, nil)
- gittest.CreateTag(t, cfg, repoPath, "v1.4.0", blobID, nil)
-
- // To test recursive resolving to a commit
- gittest.CreateTag(t, cfg, repoPath, "v1.5.0", "v1.3.0", nil)
-
- // A tag to commit with a big message
- gittest.CreateTag(t, cfg, repoPath, "v1.6.0", bigCommitID.String(), nil)
-
- // A tag with a big message
- bigMessage := strings.Repeat("a", 11*1024)
- bigMessageTag1ID := gittest.CreateTag(t, cfg, repoPath, "v1.7.0", commitID, &gittest.CreateTagOpts{Message: bigMessage})
-
- // A tag with a commit id as its name
- commitTagID := gittest.CreateTag(t, cfg, repoPath, commitID, commitID, &gittest.CreateTagOpts{Message: "commit tag with a commit sha as the name"})
-
- // a tag of a tag
- tagOfTagID := gittest.CreateTag(t, cfg, repoPath, "tag-of-tag", commitTagID, &gittest.CreateTagOpts{Message: "tag of a tag"})
-
- rpcRequest := &gitalypb.FindAllTagsRequest{Repository: repoProto}
-
- c, err := client.FindAllTags(ctx, rpcRequest)
- require.NoError(t, err)
-
- var receivedTags []*gitalypb.Tag
- for {
- r, err := c.Recv()
- if err == io.EOF {
- break
- }
- require.NoError(t, err)
- receivedTags = append(receivedTags, r.GetTags()...)
- }
-
- expectedTags := []*gitalypb.Tag{
- {
- Name: []byte(commitID),
- Id: commitTagID,
- TargetCommit: gitCommit,
- Message: []byte("commit tag with a commit sha as the name"),
- MessageSize: 40,
- Tagger: &gitalypb.CommitAuthor{
- Name: []byte("Scrooge McDuck"),
- Email: []byte("scrooge@mcduck.com"),
- Date: &timestamppb.Timestamp{Seconds: 1572776879},
- Timezone: []byte("+0100"),
- },
- },
- {
- Name: []byte("tag-of-tag"),
- Id: tagOfTagID,
- TargetCommit: gitCommit,
- Message: []byte("tag of a tag"),
- MessageSize: 12,
- Tagger: &gitalypb.CommitAuthor{
- Name: []byte("Scrooge McDuck"),
- Email: []byte("scrooge@mcduck.com"),
- Date: &timestamppb.Timestamp{Seconds: 1572776879},
- Timezone: []byte("+0100"),
- },
- },
- {
- Name: []byte("v1.0.0"),
- Id: "f4e6814c3e4e7a0de82a9e7cd20c626cc963a2f8",
- TargetCommit: gitCommit,
- Message: []byte("Release"),
- MessageSize: 7,
- Tagger: &gitalypb.CommitAuthor{
- Name: []byte("Dmitriy Zaporozhets"),
- Email: []byte("dmitriy.zaporozhets@gmail.com"),
- Date: &timestamppb.Timestamp{Seconds: 1393491299},
- Timezone: []byte("+0200"),
- },
- },
- {
- Name: []byte("v1.1.0"),
- Id: "8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b",
- TargetCommit: testhelper.GitLabTestCommit("5937ac0a7beb003549fc5fd26fc247adbce4a52e"),
- Message: []byte("Version 1.1.0"),
- MessageSize: 13,
- Tagger: &gitalypb.CommitAuthor{
- Name: []byte("Dmitriy Zaporozhets"),
- Email: []byte("dmitriy.zaporozhets@gmail.com"),
- Date: &timestamppb.Timestamp{Seconds: 1393505709},
- Timezone: []byte("+0200"),
- },
- },
- {
- Name: []byte("v1.1.1"),
- Id: "8f03acbcd11c53d9c9468078f32a2622005a4841",
- TargetCommit: testhelper.GitLabTestCommit("189a6c924013fc3fe40d6f1ec1dc20214183bc97"),
- Message: []byte("x509 signed tag\n-----BEGIN SIGNED MESSAGE-----\nMIISfwYJKoZIhvcNAQcCoIIScDCCEmwCAQExDTALBglghkgBZQMEAgEwCwYJKoZI\nhvcNAQcBoIIP8zCCB3QwggVcoAMCAQICBBXXLOIwDQYJKoZIhvcNAQELBQAwgbYx\nCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCYXllcm4xETAPBgNVBAcMCE11ZW5jaGVu\nMRAwDgYDVQQKDAdTaWVtZW5zMREwDwYDVQQFEwhaWlpaWlpBNjEdMBsGA1UECwwU\nU2llbWVucyBUcnVzdCBDZW50ZXIxPzA9BgNVBAMMNlNpZW1lbnMgSXNzdWluZyBD\nQSBNZWRpdW0gU3RyZW5ndGggQXV0aGVudGljYXRpb24gMjAxNjAeFw0xNzAyMDMw\nNjU4MzNaFw0yMDAyMDMwNjU4MzNaMFsxETAPBgNVBAUTCFowMDBOV0RIMQ4wDAYD\nVQQqDAVSb2dlcjEOMAwGA1UEBAwFTWVpZXIxEDAOBgNVBAoMB1NpZW1lbnMxFDAS\nBgNVBAMMC01laWVyIFJvZ2VyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC\nAQEAuBNea/68ZCnHYQjpm/k3ZBG0wBpEKSwG6lk9CEQlSxsqVLQHAoAKBIlJm1in\nYVLcK/Sq1yhYJ/qWcY/M53DhK2rpPuhtrWJUdOUy8EBWO20F4bd4Fw9pO7jt8bme\nu33TSrK772vKjuppzB6SeG13Cs08H+BIeD106G27h7ufsO00pvsxoSDL+uc4slnr\npBL+2TAL7nSFnB9QHWmRIK27SPqJE+lESdb0pse11x1wjvqKy2Q7EjL9fpqJdHzX\nNLKHXd2r024TOORTa05DFTNR+kQEKKV96XfpYdtSBomXNQ44cisiPBJjFtYvfnFE\nwgrHa8fogn/b0C+A+HAoICN12wIDAQABo4IC4jCCAt4wHQYDVR0OBBYEFCF+gkUp\nXQ6xGc0kRWXuDFxzA14zMEMGA1UdEQQ8MDqgIwYKKwYBBAGCNxQCA6AVDBNyLm1l\naWVyQHNpZW1lbnMuY29tgRNyLm1laWVyQHNpZW1lbnMuY29tMA4GA1UdDwEB/wQE\nAwIHgDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwgcoGA1UdHwSBwjCB\nvzCBvKCBuaCBtoYmaHR0cDovL2NoLnNpZW1lbnMuY29tL3BraT9aWlpaWlpBNi5j\ncmyGQWxkYXA6Ly9jbC5zaWVtZW5zLm5ldC9DTj1aWlpaWlpBNixMPVBLST9jZXJ0\naWZpY2F0ZVJldm9jYXRpb25MaXN0hklsZGFwOi8vY2wuc2llbWVucy5jb20vQ049\nWlpaWlpaQTYsbz1UcnVzdGNlbnRlcj9jZXJ0aWZpY2F0ZVJldm9jYXRpb25MaXN0\nMEUGA1UdIAQ+MDwwOgYNKwYBBAGhaQcCAgMBAzApMCcGCCsGAQUFBwIBFhtodHRw\nOi8vd3d3LnNpZW1lbnMuY29tL3BraS8wDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAW\ngBT4FV1HDGx3e3LEAheRaKK292oJRDCCAQQGCCsGAQUFBwEBBIH3MIH0MDIGCCsG\nAQUFBzAChiZodHRwOi8vYWguc2llbWVucy5jb20vcGtpP1paWlpaWkE2LmNydDBB\nBggrBgEFBQcwAoY1bGRhcDovL2FsLnNpZW1lbnMubmV0L0NOPVpaWlpaWkE2LEw9\nUEtJP2NBQ2VydGlmaWNhdGUwSQYIKwYBBQUHMAKGPWxkYXA6Ly9hbC5zaWVtZW5z\nLmNvbS9DTj1aWlpaWlpBNixvPVRydXN0Y2VudGVyP2NBQ2VydGlmaWNhdGUwMAYI\nKwYBBQUHMAGGJGh0dHA6Ly9vY3NwLnBraS1zZXJ2aWNlcy5zaWVtZW5zLmNvbTAN\nBgkqhkiG9w0BAQsFAAOCAgEAXPVcX6vaEcszJqg5IemF9aFTlwTrX5ITNIpzcqG+\nkD5haOf2mZYLjl+MKtLC1XfmIsGCUZNb8bjP6QHQEI+2d6x/ZOqPq7Kd7PwVu6x6\nxZrkDjUyhUbUntT5+RBy++l3Wf6Cq6Kx+K8ambHBP/bu90/p2U8KfFAG3Kr2gI2q\nfZrnNMOxmJfZ3/sXxssgLkhbZ7hRa+MpLfQ6uFsSiat3vlawBBvTyHnoZ/7oRc8y\nqi6QzWcd76CPpMElYWibl+hJzKbBZUWvc71AzHR6i1QeZ6wubYz7vr+FF5Y7tnxB\nVz6omPC9XAg0F+Dla6Zlz3Awj5imCzVXa+9SjtnsidmJdLcKzTAKyDewewoxYOOJ\nj3cJU7VSjJPl+2fVmDBaQwcNcUcu/TPAKApkegqO7tRF9IPhjhW8QkRnkqMetO3D\nOXmAFVIsEI0Hvb2cdb7B6jSpjGUuhaFm9TCKhQtCk2p8JCDTuaENLm1x34rrJKbT\n2vzyYN0CZtSkUdgD4yQxK9VWXGEzexRisWb4AnZjD2NAquLPpXmw8N0UwFD7MSpC\ndpaX7FktdvZmMXsnGiAdtLSbBgLVWOD1gmJFDjrhNbI8NOaOaNk4jrfGqNh5lhGU\n4DnBT2U6Cie1anLmFH/oZooAEXR2o3Nu+1mNDJChnJp0ovs08aa3zZvBdcloOvfU\nqdowggh3MIIGX6ADAgECAgQtyi/nMA0GCSqGSIb3DQEBCwUAMIGZMQswCQYDVQQG\nEwJERTEPMA0GA1UECAwGQmF5ZXJuMREwDwYDVQQHDAhNdWVuY2hlbjEQMA4GA1UE\nCgwHU2llbWVuczERMA8GA1UEBRMIWlpaWlpaQTExHTAbBgNVBAsMFFNpZW1lbnMg\nVHJ1c3QgQ2VudGVyMSIwIAYDVQQDDBlTaWVtZW5zIFJvb3QgQ0EgVjMuMCAyMDE2\nMB4XDTE2MDcyMDEzNDYxMFoXDTIyMDcyMDEzNDYxMFowgbYxCzAJBgNVBAYTAkRF\nMQ8wDQYDVQQIDAZCYXllcm4xETAPBgNVBAcMCE11ZW5jaGVuMRAwDgYDVQQKDAdT\naWVtZW5zMREwDwYDVQQFEwhaWlpaWlpBNjEdMBsGA1UECwwUU2llbWVucyBUcnVz\ndCBDZW50ZXIxPzA9BgNVBAMMNlNpZW1lbnMgSXNzdWluZyBDQSBNZWRpdW0gU3Ry\nZW5ndGggQXV0aGVudGljYXRpb24gMjAxNjCCAiIwDQYJKoZIhvcNAQEBBQADggIP\nADCCAgoCggIBAL9UfK+JAZEqVMVvECdYF9IK4KSw34AqyNl3rYP5x03dtmKaNu+2\n0fQqNESA1NGzw3s6LmrKLh1cR991nB2cvKOXu7AvEGpSuxzIcOROd4NpvRx+Ej1p\nJIPeqf+ScmVK7lMSO8QL/QzjHOpGV3is9sG+ZIxOW9U1ESooy4Hal6ZNs4DNItsz\npiCKqm6G3et4r2WqCy2RRuSqvnmMza7Y8BZsLy0ZVo5teObQ37E/FxqSrbDI8nxn\nB7nVUve5ZjrqoIGSkEOtyo11003dVO1vmWB9A0WQGDqE/q3w178hGhKfxzRaqzyi\nSoADUYS2sD/CglGTUxVq6u0pGLLsCFjItcCWqW+T9fPYfJ2CEd5b3hvqdCn+pXjZ\n/gdX1XAcdUF5lRnGWifaYpT9n4s4adzX8q6oHSJxTppuAwLRKH6eXALbGQ1I9lGQ\nDSOipD/09xkEsPw6HOepmf2U3YxZK1VU2sHqugFJboeLcHMzp6E1n2ctlNG1GKE9\nFDHmdyFzDi0Nnxtf/GgVjnHF68hByEE1MYdJ4nJLuxoT9hyjYdRW9MpeNNxxZnmz\nW3zh7QxIqP0ZfIz6XVhzrI9uZiqwwojDiM5tEOUkQ7XyW6grNXe75yt6mTj89LlB\nH5fOW2RNmCy/jzBXDjgyskgK7kuCvUYTuRv8ITXbBY5axFA+CpxZqokpAgMBAAGj\nggKmMIICojCCAQUGCCsGAQUFBwEBBIH4MIH1MEEGCCsGAQUFBzAChjVsZGFwOi8v\nYWwuc2llbWVucy5uZXQvQ049WlpaWlpaQTEsTD1QS0k/Y0FDZXJ0aWZpY2F0ZTAy\nBggrBgEFBQcwAoYmaHR0cDovL2FoLnNpZW1lbnMuY29tL3BraT9aWlpaWlpBMS5j\ncnQwSgYIKwYBBQUHMAKGPmxkYXA6Ly9hbC5zaWVtZW5zLmNvbS91aWQ9WlpaWlpa\nQTEsbz1UcnVzdGNlbnRlcj9jQUNlcnRpZmljYXRlMDAGCCsGAQUFBzABhiRodHRw\nOi8vb2NzcC5wa2ktc2VydmljZXMuc2llbWVucy5jb20wHwYDVR0jBBgwFoAUcG2g\nUOyp0CxnnRkV/v0EczXD4tQwEgYDVR0TAQH/BAgwBgEB/wIBADBABgNVHSAEOTA3\nMDUGCCsGAQQBoWkHMCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93d3cuc2llbWVucy5j\nb20vcGtpLzCBxwYDVR0fBIG/MIG8MIG5oIG2oIGzhj9sZGFwOi8vY2wuc2llbWVu\ncy5uZXQvQ049WlpaWlpaQTEsTD1QS0k/YXV0aG9yaXR5UmV2b2NhdGlvbkxpc3SG\nJmh0dHA6Ly9jaC5zaWVtZW5zLmNvbS9wa2k/WlpaWlpaQTEuY3JshkhsZGFwOi8v\nY2wuc2llbWVucy5jb20vdWlkPVpaWlpaWkExLG89VHJ1c3RjZW50ZXI/YXV0aG9y\naXR5UmV2b2NhdGlvbkxpc3QwJwYDVR0lBCAwHgYIKwYBBQUHAwIGCCsGAQUFBwME\nBggrBgEFBQcDCTAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFPgVXUcMbHd7csQC\nF5Foorb3aglEMA0GCSqGSIb3DQEBCwUAA4ICAQBw+sqMp3SS7DVKcILEmXbdRAg3\nlLO1r457KY+YgCT9uX4VG5EdRKcGfWXK6VHGCi4Dos5eXFV34Mq/p8nu1sqMuoGP\nYjHn604eWDprhGy6GrTYdxzcE/GGHkpkuE3Ir/45UcmZlOU41SJ9SNjuIVrSHMOf\nccSY42BCspR/Q1Z/ykmIqQecdT3/Kkx02GzzSN2+HlW6cEO4GBW5RMqsvd2n0h2d\nfe2zcqOgkLtx7u2JCR/U77zfyxG3qXtcymoz0wgSHcsKIl+GUjITLkHfS9Op8V7C\nGr/dX437sIg5pVHmEAWadjkIzqdHux+EF94Z6kaHywohc1xG0KvPYPX7iSNjkvhz\n4NY53DHmxl4YEMLffZnaS/dqyhe1GTpcpyN8WiR4KuPfxrkVDOsuzWFtMSvNdlOV\ngdI0MXcLMP+EOeANZWX6lGgJ3vWyemo58nzgshKd24MY3w3i6masUkxJH2KvI7UH\n/1Db3SC8oOUjInvSRej6M3ZhYWgugm6gbpUgFoDw/o9Cg6Qm71hY0JtcaPC13rzm\nN8a2Br0+Fa5e2VhwLmAxyfe1JKzqPwuHT0S5u05SQghL5VdzqfA8FCL/j4XC9yI6\ncsZTAQi73xFQYVjZt3+aoSz84lOlTmVo/jgvGMY/JzH9I4mETGgAJRNj34Z/0meh\nM+pKWCojNH/dgyJSwDGCAlIwggJOAgEBMIG/MIG2MQswCQYDVQQGEwJERTEPMA0G\nA1UECAwGQmF5ZXJuMREwDwYDVQQHDAhNdWVuY2hlbjEQMA4GA1UECgwHU2llbWVu\nczERMA8GA1UEBRMIWlpaWlpaQTYxHTAbBgNVBAsMFFNpZW1lbnMgVHJ1c3QgQ2Vu\ndGVyMT8wPQYDVQQDDDZTaWVtZW5zIElzc3VpbmcgQ0EgTWVkaXVtIFN0cmVuZ3Ro\nIEF1dGhlbnRpY2F0aW9uIDIwMTYCBBXXLOIwCwYJYIZIAWUDBAIBoGkwHAYJKoZI\nhvcNAQkFMQ8XDTE5MTEyMDE0NTYyMFowLwYJKoZIhvcNAQkEMSIEIJDnZUpcVLzC\nOdtpkH8gtxwLPIDE0NmAmFC9uM8q2z+OMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0B\nBwEwCwYJKoZIhvcNAQEBBIIBAH/Pqv2xp3a0jSPkwU1K3eGA/1lfoNJMUny4d/PS\nLVWlkgrmedXdLmuBzAGEaaZOJS0lEpNd01pR/reHs7xxZ+RZ0olTs2ufM0CijQSx\nOL9HDl2O3OoD77NWx4tl3Wy1yJCeV3XH/cEI7AkKHCmKY9QMoMYWh16ORBtr+YcS\nYK+gONOjpjgcgTJgZ3HSFgQ50xiD4WT1kFBHsuYsLqaOSbTfTN6Ayyg4edjrPQqa\nVcVf1OQcIrfWA3yMQrnEZfOYfN/D4EPjTfxBV+VCi/F2bdZmMbJ7jNk1FbewSwWO\nSDH1i0K32NyFbnh0BSos7njq7ELqKlYBsoB/sZfaH2vKy5U=\n-----END SIGNED MESSAGE-----"),
- MessageSize: 6494,
- Tagger: &gitalypb.CommitAuthor{
- Name: []byte("Roger Meier"),
- Email: []byte("r.meier@siemens.com"),
- Date: &timestamppb.Timestamp{Seconds: 1574261780},
- Timezone: []byte("+0100"),
- },
- SignatureType: gitalypb.SignatureType_X509,
- },
- {
- Name: []byte("pgp-long-tag-message"),
- Id: truncatedPGPTagID,
- TargetCommit: gitCommit, // 6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9
- Message: truncatedPGPTagMsg[146:10386], // first 10240 bytes of tag message
- MessageSize: 11148,
- Tagger: &gitalypb.CommitAuthor{
- Name: []byte("Scrooge McDuck"),
- Email: []byte("scrooge@mcduck.com"),
- Date: &timestamppb.Timestamp{Seconds: 1393491261},
- Timezone: []byte("+0100"),
- },
- SignatureType: gitalypb.SignatureType_PGP,
- },
- {
- Name: []byte("v1.2.0"),
- Id: annotatedTagID,
- Message: []byte("Blob tag"),
- MessageSize: 8,
- Tagger: &gitalypb.CommitAuthor{
- Name: []byte("Scrooge McDuck"),
- Email: []byte("scrooge@mcduck.com"),
- Date: &timestamppb.Timestamp{Seconds: 1572776879},
- Timezone: []byte("+0100"),
- },
- },
- {
- Name: []byte("v1.3.0"),
- Id: commitID,
- TargetCommit: gitCommit,
- },
- {
- Name: []byte("v1.4.0"),
- Id: blobID,
- },
- {
- Name: []byte("v1.5.0"),
- Id: commitID,
- TargetCommit: gitCommit,
- },
- {
- Name: []byte("v1.6.0"),
- Id: bigCommitID.String(),
- TargetCommit: bigCommit,
- },
- {
- Name: []byte("v1.7.0"),
- Id: bigMessageTag1ID,
- Message: []byte(bigMessage[:helper.MaxCommitOrTagMessageSize]),
- MessageSize: int64(len(bigMessage)),
- TargetCommit: gitCommit,
- Tagger: &gitalypb.CommitAuthor{
- Name: []byte("Scrooge McDuck"),
- Email: []byte("scrooge@mcduck.com"),
- Date: &timestamppb.Timestamp{Seconds: 1572776879},
- Timezone: []byte("+0100"),
- },
- },
- }
-
- require.Len(t, receivedTags, len(expectedTags))
- require.ElementsMatch(t, expectedTags, receivedTags)
-}
-
-func TestFindAllTagsNestedTags(t *testing.T) {
- cfg, client := setupRefServiceWithoutRepo(t)
-
- ctx, cancel := testhelper.Context()
- defer cancel()
-
- repoProto, repoPath := gittest.InitRepo(t, cfg, cfg.Storages[0])
-
- commitID := gittest.WriteCommit(t, cfg, repoPath,
- gittest.WithParents(),
- )
-
- tagID := gittest.CreateTag(t, cfg, repoPath, "my/nested/tag", commitID.String(), nil)
-
- stream, err := client.FindAllTags(ctx, &gitalypb.FindAllTagsRequest{Repository: repoProto})
- require.NoError(t, err)
-
- response, err := stream.Recv()
- require.NoError(t, err)
- testassert.ProtoEqual(t, &gitalypb.FindAllTagsResponse{
- Tags: []*gitalypb.Tag{
- {
- Name: []byte("my/nested/tag"),
- Id: tagID,
- TargetCommit: &gitalypb.GitCommit{
- Id: commitID.String(),
- Body: []byte("message"),
- BodySize: 7,
- Subject: []byte("message"),
- TreeId: git.EmptyTreeOID.String(),
- Author: &gitalypb.CommitAuthor{
- Name: []byte("Scrooge McDuck"),
- Email: []byte("scrooge@mcduck.com"),
- Date: &timestamppb.Timestamp{Seconds: 1572776879},
- Timezone: []byte("+0100"),
- },
- Committer: &gitalypb.CommitAuthor{
- Name: []byte("Scrooge McDuck"),
- Email: []byte("scrooge@mcduck.com"),
- Date: &timestamppb.Timestamp{Seconds: 1572776879},
- Timezone: []byte("+0100"),
- },
- },
- },
- },
- }, response)
-
- response, err = stream.Recv()
- require.Equal(t, io.EOF, err)
- require.Nil(t, response)
-}
-
-func TestFindAllTags_duplicateAnnotatedTags(t *testing.T) {
- cfg, client := setupRefServiceWithoutRepo(t)
-
- repoProto, repoPath := gittest.InitRepo(t, cfg, cfg.Storages[0])
- repo := localrepo.NewTestRepo(t, cfg, repoProto)
-
- ctx, cancel := testhelper.Context()
- defer cancel()
-
- commitID := gittest.WriteCommit(t, cfg, repoPath, gittest.WithParents())
- date := time.Unix(12345, 0)
-
- tagID, err := repo.WriteTag(ctx, commitID, "commit", []byte("annotated"), []byte("message"),
- gittest.TestUser, date)
- require.NoError(t, err)
-
- require.NoError(t, repo.UpdateRef(ctx, "refs/tags/annotated", tagID, git.ZeroOID))
- require.NoError(t, repo.UpdateRef(ctx, "refs/tags/annotated-dup", tagID, git.ZeroOID))
- require.NoError(t, repo.UpdateRef(ctx, "refs/tags/lightweight-1", commitID, git.ZeroOID))
- require.NoError(t, repo.UpdateRef(ctx, "refs/tags/lightweight-2", commitID, git.ZeroOID))
-
- c, err := client.FindAllTags(ctx, &gitalypb.FindAllTagsRequest{Repository: repoProto})
- require.NoError(t, err)
-
- var receivedTags []*gitalypb.Tag
- for {
- r, err := c.Recv()
- if err == io.EOF {
- break
- }
- require.NoError(t, err)
- receivedTags = append(receivedTags, r.GetTags()...)
- }
-
- commitAuthor := &gitalypb.CommitAuthor{
- Name: []byte("Scrooge McDuck"),
- Email: []byte("scrooge@mcduck.com"),
- Date: &timestamppb.Timestamp{Seconds: 1572776879},
- Timezone: []byte("+0100"),
- }
- commit := &gitalypb.GitCommit{
- Id: commitID.String(),
- TreeId: "4b825dc642cb6eb9a060e54bf8d69288fbee4904",
- Body: []byte("message"),
- BodySize: 7,
- Subject: []byte("message"),
- Author: commitAuthor,
- Committer: commitAuthor,
- }
-
- testassert.ProtoEqual(t, []*gitalypb.Tag{
- {
- Name: []byte("annotated"),
- Id: tagID.String(),
- Message: []byte("message"),
- MessageSize: 7,
- Tagger: &gitalypb.CommitAuthor{
- Name: gittest.TestUser.Name,
- Email: gittest.TestUser.Email,
- Date: timestamppb.New(date),
- Timezone: []byte("+0000"),
- },
- TargetCommit: commit,
- },
- {
- Name: []byte("annotated-dup"),
- Id: tagID.String(),
- Message: []byte("message"),
- MessageSize: 7,
- Tagger: &gitalypb.CommitAuthor{
- Name: gittest.TestUser.Name,
- Email: gittest.TestUser.Email,
- Date: timestamppb.New(date),
- Timezone: []byte("+0000"),
- },
- TargetCommit: commit,
- },
- {Name: []byte("lightweight-1"), Id: commitID.String(), TargetCommit: commit},
- {Name: []byte("lightweight-2"), Id: commitID.String(), TargetCommit: commit},
- }, receivedTags)
-}
-
-func TestFindAllTagNestedTags(t *testing.T) {
- cfg, client := setupRefServiceWithoutRepo(t)
-
- ctx, cancel := testhelper.Context()
- defer cancel()
-
- repoProto, repoPath := gittest.CloneRepo(t, cfg, cfg.Storages[0])
- repo := localrepo.NewTestRepo(t, cfg, repoProto)
-
- blobID := "faaf198af3a36dbf41961466703cc1d47c61d051"
- commitID := "6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9"
-
- testCases := []struct {
- description string
- depth int
- originalOid string
- }{
- {
- description: "nested 1 deep, points to a commit",
- depth: 1,
- originalOid: commitID,
- },
- {
- description: "nested 4 deep, points to a commit",
- depth: 4,
- originalOid: commitID,
- },
- {
- description: "nested 3 deep, points to a blob",
- depth: 3,
- originalOid: blobID,
- },
- {
- description: "nested 20 deep, points to a commit",
- depth: 20,
- originalOid: commitID,
- },
- }
-
- for _, tc := range testCases {
- t.Run(tc.description, func(t *testing.T) {
- tags := bytes.NewReader(gittest.Exec(t, cfg, "-C", repoPath, "tag"))
- testhelper.MustRunCommand(t, tags, "xargs", cfg.Git.BinPath, "-C", repoPath, "tag", "-d")
-
- catfileCache := catfile.NewCache(cfg)
- batch, err := catfileCache.BatchProcess(ctx, repo)
- require.NoError(t, err)
-
- info, err := batch.Info(ctx, git.Revision(tc.originalOid))
- require.NoError(t, err)
-
- expectedTags := make(map[string]*gitalypb.Tag)
- tagID := tc.originalOid
-
- for depth := 0; depth < tc.depth; depth++ {
- tagName := fmt.Sprintf("tag-depth-%d", depth)
- tagMessage := fmt.Sprintf("a commit %d deep", depth)
- tagID = gittest.CreateTag(t, cfg, repoPath, tagName, tagID, &gittest.CreateTagOpts{Message: tagMessage})
-
- expectedTag := &gitalypb.Tag{
- Name: []byte(tagName),
- Id: tagID,
- Message: []byte(tagMessage),
- MessageSize: int64(len([]byte(tagMessage))),
- Tagger: &gitalypb.CommitAuthor{
- Name: []byte("Scrooge McDuck"),
- Email: []byte("scrooge@mcduck.com"),
- Date: &timestamppb.Timestamp{Seconds: 1572776879},
- Timezone: []byte("+0100"),
- },
- }
-
- if info.Type == "commit" {
- commit, err := catfile.GetCommit(ctx, batch, git.Revision(tc.originalOid))
- require.NoError(t, err)
- expectedTag.TargetCommit = commit
- }
-
- expectedTags[string(expectedTag.Name)] = expectedTag
- }
-
- rpcRequest := &gitalypb.FindAllTagsRequest{Repository: repoProto}
-
- c, err := client.FindAllTags(ctx, rpcRequest)
- require.NoError(t, err)
-
- var receivedTags []*gitalypb.Tag
- for {
- r, err := c.Recv()
- if err == io.EOF {
- break
- }
- require.NoError(t, err)
- receivedTags = append(receivedTags, r.GetTags()...)
- }
-
- require.Len(t, receivedTags, len(expectedTags))
- for _, receivedTag := range receivedTags {
- assert.Equal(t, expectedTags[string(receivedTag.Name)], receivedTag)
- }
- })
- }
-}
-
-func TestInvalidFindAllTagsRequest(t *testing.T) {
- _, client := setupRefServiceWithoutRepo(t)
-
- ctx, cancel := testhelper.Context()
- defer cancel()
-
- testCases := []struct {
- desc string
- request *gitalypb.FindAllTagsRequest
- }{
- {
- desc: "empty request",
- request: &gitalypb.FindAllTagsRequest{},
- },
- {
- desc: "invalid repo",
- request: &gitalypb.FindAllTagsRequest{
- Repository: &gitalypb.Repository{
- StorageName: "fake",
- RelativePath: "repo",
- },
- },
- },
- }
-
- for _, tc := range testCases {
- t.Run(tc.desc, func(t *testing.T) {
- c, err := client.FindAllTags(ctx, tc.request)
- require.NoError(t, err)
-
- var recvError error
- for recvError == nil {
- _, recvError = c.Recv()
- }
-
- testhelper.RequireGrpcError(t, recvError, codes.InvalidArgument)
- })
- }
-}
-
-func TestFindAllTagsSorted(t *testing.T) {
- cfg, client := setupRefServiceWithoutRepo(t)
-
- ctx, cancel := testhelper.Context()
- defer cancel()
-
- repoProto, _ := gittest.CloneRepo(t, cfg, cfg.Storages[0])
-
- repo := localrepo.New(git.NewExecCommandFactory(cfg), catfile.NewCache(cfg), repoProto, cfg)
- headCommit, err := repo.ReadCommit(ctx, "HEAD")
- require.NoError(t, err)
- annotatedTagID, err := repo.WriteTag(ctx, git.ObjectID(headCommit.Id), "commit", []byte("annotated"), []byte("message"), gittest.TestUser, time.Now())
- require.NoError(t, err)
- require.NoError(t, repo.UpdateRef(ctx, "refs/tags/annotated", annotatedTagID, git.ZeroOID))
-
- require.NoError(t, repo.ExecAndWait(ctx, git.SubCmd{
- Name: "tag",
- Args: []string{"not-annotated", headCommit.Id},
- }, git.WithDisabledHooks()))
-
- for _, tc := range []struct {
- desc string
- sortBy *gitalypb.FindAllTagsRequest_SortBy
- exp []string
- }{
- {
- desc: "by name",
- sortBy: &gitalypb.FindAllTagsRequest_SortBy{Key: gitalypb.FindAllTagsRequest_SortBy_REFNAME},
- exp: []string{
- annotatedTagID.String(),
- headCommit.Id,
- "f4e6814c3e4e7a0de82a9e7cd20c626cc963a2f8",
- "8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b",
- "8f03acbcd11c53d9c9468078f32a2622005a4841",
- },
- },
- {
- desc: "by updated in ascending order",
- sortBy: &gitalypb.FindAllTagsRequest_SortBy{Key: gitalypb.FindAllTagsRequest_SortBy_CREATORDATE, Direction: gitalypb.SortDirection_ASCENDING},
- exp: []string{
- "f4e6814c3e4e7a0de82a9e7cd20c626cc963a2f8",
- "8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b",
- headCommit.Id,
- "8f03acbcd11c53d9c9468078f32a2622005a4841",
- annotatedTagID.String(),
- },
- },
- {
- desc: "by updated in descending order",
- sortBy: &gitalypb.FindAllTagsRequest_SortBy{Key: gitalypb.FindAllTagsRequest_SortBy_CREATORDATE, Direction: gitalypb.SortDirection_DESCENDING},
- exp: []string{
- annotatedTagID.String(),
- "8f03acbcd11c53d9c9468078f32a2622005a4841",
- headCommit.Id,
- "8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b",
- "f4e6814c3e4e7a0de82a9e7cd20c626cc963a2f8",
- },
- },
- } {
- t.Run(tc.desc, func(t *testing.T) {
- c, err := client.FindAllTags(ctx, &gitalypb.FindAllTagsRequest{
- Repository: repoProto,
- SortBy: tc.sortBy,
- })
- require.NoError(t, err)
-
- var tags []string
- for {
- r, err := c.Recv()
- if err == io.EOF {
- break
- }
- require.NoError(t, err)
- for _, tag := range r.GetTags() {
- tags = append(tags, tag.Id)
- }
- }
-
- require.Equal(t, tc.exp, tags)
- })
- }
-
- t.Run("by unsupported key", func(t *testing.T) {
- c, err := client.FindAllTags(ctx, &gitalypb.FindAllTagsRequest{
- Repository: repoProto,
- SortBy: &gitalypb.FindAllTagsRequest_SortBy{Key: gitalypb.FindAllTagsRequest_SortBy_Key(-1)},
- })
- require.NoError(t, err)
- r, err := c.Recv()
- testassert.GrpcEqualErr(t, status.Error(codes.InvalidArgument, "unsupported sorting key: -1"), err)
- require.Nil(t, r)
- })
-
- t.Run("by unsupported direction", func(t *testing.T) {
- c, err := client.FindAllTags(ctx, &gitalypb.FindAllTagsRequest{
- Repository: repoProto,
- SortBy: &gitalypb.FindAllTagsRequest_SortBy{Key: gitalypb.FindAllTagsRequest_SortBy_REFNAME, Direction: gitalypb.SortDirection(-1)},
- })
- require.NoError(t, err)
- r, err := c.Recv()
- testassert.GrpcEqualErr(t, status.Error(codes.InvalidArgument, "unsupported sorting direction: -1"), err)
- require.Nil(t, r)
- })
-
- t.Run("no tags", func(t *testing.T) {
- repoProto, _ := gittest.InitRepo(t, cfg, cfg.Storages[0])
- c, err := client.FindAllTags(ctx, &gitalypb.FindAllTagsRequest{
- Repository: repoProto,
- SortBy: &gitalypb.FindAllTagsRequest_SortBy{Key: gitalypb.FindAllTagsRequest_SortBy_REFNAME},
- })
- require.NoError(t, err)
-
- r, err := c.Recv()
- require.Equal(t, io.EOF, err)
- require.Nil(t, r)
- })
-}
-
func TestSuccessfulFindLocalBranches(t *testing.T) {
_, repo, _, client := setupRefService(t)
@@ -1471,10 +875,10 @@ func TestSuccessfulFindTagRequest(t *testing.T) {
repo := localrepo.NewTestRepo(t, cfg, repoProto)
- blobID := "faaf198af3a36dbf41961466703cc1d47c61d051"
- commitID := "6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9"
+ blobID := git.ObjectID("faaf198af3a36dbf41961466703cc1d47c61d051")
+ commitID := git.ObjectID("6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9")
- gitCommit := testhelper.GitLabTestCommit(commitID)
+ gitCommit := testhelper.GitLabTestCommit(commitID.String())
ctx, cancel := testhelper.Context()
defer cancel()
@@ -1487,31 +891,31 @@ func TestSuccessfulFindTagRequest(t *testing.T) {
bigCommit, err := repo.ReadCommit(ctx, git.Revision(bigCommitID))
require.NoError(t, err)
- annotatedTagID := gittest.CreateTag(t, cfg, repoPath, "v1.2.0", blobID, &gittest.CreateTagOpts{Message: "Blob tag"})
+ annotatedTagID := gittest.WriteTag(t, cfg, repoPath, "v1.2.0", blobID.Revision(), gittest.WriteTagConfig{Message: "Blob tag"})
- gittest.CreateTag(t, cfg, repoPath, "v1.3.0", commitID, nil)
- gittest.CreateTag(t, cfg, repoPath, "v1.4.0", blobID, nil)
+ gittest.WriteTag(t, cfg, repoPath, "v1.3.0", commitID.Revision())
+ gittest.WriteTag(t, cfg, repoPath, "v1.4.0", blobID.Revision())
// To test recursive resolving to a commit
- gittest.CreateTag(t, cfg, repoPath, "v1.5.0", "v1.3.0", nil)
+ gittest.WriteTag(t, cfg, repoPath, "v1.5.0", "v1.3.0")
// A tag to commit with a big message
- gittest.CreateTag(t, cfg, repoPath, "v1.6.0", bigCommitID.String(), nil)
+ gittest.WriteTag(t, cfg, repoPath, "v1.6.0", bigCommitID.Revision())
// A tag with a big message
bigMessage := strings.Repeat("a", 11*1024)
- bigMessageTag1ID := gittest.CreateTag(t, cfg, repoPath, "v1.7.0", commitID, &gittest.CreateTagOpts{Message: bigMessage})
+ bigMessageTag1ID := gittest.WriteTag(t, cfg, repoPath, "v1.7.0", commitID.Revision(), gittest.WriteTagConfig{Message: bigMessage})
// A tag with a commit id as its name
- commitTagID := gittest.CreateTag(t, cfg, repoPath, commitID, commitID, &gittest.CreateTagOpts{Message: "commit tag with a commit sha as the name"})
+ commitTagID := gittest.WriteTag(t, cfg, repoPath, commitID.String(), commitID.Revision(), gittest.WriteTagConfig{Message: "commit tag with a commit sha as the name"})
// a tag of a tag
- tagOfTagID := gittest.CreateTag(t, cfg, repoPath, "tag-of-tag", commitTagID, &gittest.CreateTagOpts{Message: "tag of a tag"})
+ tagOfTagID := gittest.WriteTag(t, cfg, repoPath, "tag-of-tag", commitTagID.Revision(), gittest.WriteTagConfig{Message: "tag of a tag"})
expectedTags := []*gitalypb.Tag{
{
Name: []byte(commitID),
- Id: commitTagID,
+ Id: commitTagID.String(),
TargetCommit: gitCommit,
Message: []byte("commit tag with a commit sha as the name"),
MessageSize: 40,
@@ -1524,7 +928,7 @@ func TestSuccessfulFindTagRequest(t *testing.T) {
},
{
Name: []byte("tag-of-tag"),
- Id: tagOfTagID,
+ Id: tagOfTagID.String(),
TargetCommit: gitCommit,
Message: []byte("tag of a tag"),
MessageSize: 12,
@@ -1578,7 +982,7 @@ func TestSuccessfulFindTagRequest(t *testing.T) {
},
{
Name: []byte("v1.2.0"),
- Id: annotatedTagID,
+ Id: annotatedTagID.String(),
Message: []byte("Blob tag"),
MessageSize: 8,
Tagger: &gitalypb.CommitAuthor{
@@ -1590,16 +994,16 @@ func TestSuccessfulFindTagRequest(t *testing.T) {
},
{
Name: []byte("v1.3.0"),
- Id: commitID,
+ Id: commitID.String(),
TargetCommit: gitCommit,
},
{
Name: []byte("v1.4.0"),
- Id: blobID,
+ Id: blobID.String(),
},
{
Name: []byte("v1.5.0"),
- Id: commitID,
+ Id: commitID.String(),
TargetCommit: gitCommit,
},
{
@@ -1609,7 +1013,7 @@ func TestSuccessfulFindTagRequest(t *testing.T) {
},
{
Name: []byte("v1.7.0"),
- Id: bigMessageTag1ID,
+ Id: bigMessageTag1ID.String(),
Message: []byte(bigMessage[:helper.MaxCommitOrTagMessageSize]),
MessageSize: int64(len(bigMessage)),
TargetCommit: gitCommit,
@@ -1638,8 +1042,8 @@ func TestFindTagNestedTag(t *testing.T) {
repoProto, repoPath := gittest.CloneRepo(t, cfg, cfg.Storages[0])
repo := localrepo.NewTestRepo(t, cfg, repoProto)
- blobID := "faaf198af3a36dbf41961466703cc1d47c61d051"
- commitID := "6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9"
+ blobID := git.ObjectID("faaf198af3a36dbf41961466703cc1d47c61d051")
+ commitID := git.ObjectID("6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9")
ctx, cancel := testhelper.Context()
defer cancel()
@@ -1647,7 +1051,7 @@ func TestFindTagNestedTag(t *testing.T) {
testCases := []struct {
description string
depth int
- originalOid string
+ originalOid git.ObjectID
}{
{
description: "nested 1 deep, points to a commit",
@@ -1689,11 +1093,11 @@ func TestFindTagNestedTag(t *testing.T) {
for depth := 0; depth < tc.depth; depth++ {
tagName = fmt.Sprintf("tag-depth-%d", depth)
tagMessage = fmt.Sprintf("a commit %d deep", depth)
- tagID = gittest.CreateTag(t, cfg, repoPath, tagName, tagID, &gittest.CreateTagOpts{Message: tagMessage})
+ tagID = gittest.WriteTag(t, cfg, repoPath, tagName, tagID.Revision(), gittest.WriteTagConfig{Message: tagMessage})
}
expectedTag := &gitalypb.Tag{
Name: []byte(tagName),
- Id: tagID,
+ Id: tagID.String(),
Message: []byte(tagMessage),
MessageSize: int64(len([]byte(tagMessage))),
Tagger: &gitalypb.CommitAuthor{
diff --git a/internal/gitaly/service/ref/tag_messages_test.go b/internal/gitaly/service/ref/tag_messages_test.go
index a6b11df99..6eeeda895 100644
--- a/internal/gitaly/service/ref/tag_messages_test.go
+++ b/internal/gitaly/service/ref/tag_messages_test.go
@@ -23,21 +23,21 @@ func TestSuccessfulGetTagMessagesRequest(t *testing.T) {
message1 := strings.Repeat("a", helper.MaxCommitOrTagMessageSize*2)
message2 := strings.Repeat("b", helper.MaxCommitOrTagMessageSize)
- tag1ID := gittest.CreateTag(t, cfg, repoPath, "big-tag-1", "master", &gittest.CreateTagOpts{Message: message1})
- tag2ID := gittest.CreateTag(t, cfg, repoPath, "big-tag-2", "master~", &gittest.CreateTagOpts{Message: message2})
+ tag1ID := gittest.WriteTag(t, cfg, repoPath, "big-tag-1", "master", gittest.WriteTagConfig{Message: message1})
+ tag2ID := gittest.WriteTag(t, cfg, repoPath, "big-tag-2", "master~", gittest.WriteTagConfig{Message: message2})
request := &gitalypb.GetTagMessagesRequest{
Repository: repo,
- TagIds: []string{tag1ID, tag2ID},
+ TagIds: []string{tag1ID.String(), tag2ID.String()},
}
expectedMessages := []*gitalypb.GetTagMessagesResponse{
{
- TagId: tag1ID,
+ TagId: tag1ID.String(),
Message: []byte(message1 + "\n"),
},
{
- TagId: tag2ID,
+ TagId: tag2ID.String(),
Message: []byte(message2 + "\n"),
},
}
diff --git a/internal/gitaly/service/ref/tag_signatures_test.go b/internal/gitaly/service/ref/tag_signatures_test.go
index 6eee14723..08ce72d88 100644
--- a/internal/gitaly/service/ref/tag_signatures_test.go
+++ b/internal/gitaly/service/ref/tag_signatures_test.go
@@ -24,16 +24,16 @@ func TestGetTagSignatures(t *testing.T) {
message1 := strings.Repeat("a", helper.MaxCommitOrTagMessageSize) + "\n"
signature1 := string(testhelper.MustReadFile(t, "testdata/tag-1e292f8fedd741b75372e19097c76d327140c312-signature"))
- tag1ID := gittest.CreateTag(t, cfg, repoPath, "big-tag-1", "master", &gittest.CreateTagOpts{Message: message1 + signature1})
+ tag1ID := gittest.WriteTag(t, cfg, repoPath, "big-tag-1", "master", gittest.WriteTagConfig{Message: message1 + signature1})
content1 := "object 1e292f8fedd741b75372e19097c76d327140c312\ntype commit\ntag big-tag-1\ntagger Scrooge McDuck <scrooge@mcduck.com> 1572776879 +0100\n\n" + message1
message2 := strings.Repeat("b", helper.MaxCommitOrTagMessageSize) + "\n"
signature2 := string(testhelper.MustReadFile(t, "testdata/tag-7975be0116940bf2ad4321f79d02a55c5f7779aa-signature"))
- tag2ID := gittest.CreateTag(t, cfg, repoPath, "big-tag-2", "master~", &gittest.CreateTagOpts{Message: message2 + signature2})
+ tag2ID := gittest.WriteTag(t, cfg, repoPath, "big-tag-2", "master~", gittest.WriteTagConfig{Message: message2 + signature2})
content2 := "object 7975be0116940bf2ad4321f79d02a55c5f7779aa\ntype commit\ntag big-tag-2\ntagger Scrooge McDuck <scrooge@mcduck.com> 1572776879 +0100\n\n" + message2
message3 := "tag message\n"
- tag3ID := gittest.CreateTag(t, cfg, repoPath, "tag-3", "master~~", &gittest.CreateTagOpts{Message: message3})
+ tag3ID := gittest.WriteTag(t, cfg, repoPath, "tag-3", "master~~", gittest.WriteTagConfig{Message: message3})
content3 := "object 60ecb67744cb56576c30214ff52294f8ce2def98\ntype commit\ntag tag-3\ntagger Scrooge McDuck <scrooge@mcduck.com> 1572776879 +0100\n\n" + message3
for _, tc := range []struct {
@@ -78,11 +78,11 @@ func TestGetTagSignatures(t *testing.T) {
{
desc: "single tag signature",
revisions: []string{
- tag1ID,
+ tag1ID.String(),
},
expectedSignatures: []*gitalypb.GetTagSignaturesResponse_TagSignature{
{
- TagId: tag1ID,
+ TagId: tag1ID.String(),
Signature: []byte(signature1),
Content: []byte(content1),
},
@@ -91,11 +91,11 @@ func TestGetTagSignatures(t *testing.T) {
{
desc: "single tag signature by short SHA",
revisions: []string{
- tag1ID[:7],
+ tag1ID.String()[:7],
},
expectedSignatures: []*gitalypb.GetTagSignaturesResponse_TagSignature{
{
- TagId: tag1ID,
+ TagId: tag1ID.String(),
Signature: []byte(signature1),
Content: []byte(content1),
},
@@ -108,7 +108,7 @@ func TestGetTagSignatures(t *testing.T) {
},
expectedSignatures: []*gitalypb.GetTagSignaturesResponse_TagSignature{
{
- TagId: tag1ID,
+ TagId: tag1ID.String(),
Signature: []byte(signature1),
Content: []byte(content1),
},
@@ -117,17 +117,17 @@ func TestGetTagSignatures(t *testing.T) {
{
desc: "multiple tag signatures",
revisions: []string{
- tag1ID,
- tag2ID,
+ tag1ID.String(),
+ tag2ID.String(),
},
expectedSignatures: []*gitalypb.GetTagSignaturesResponse_TagSignature{
{
- TagId: tag1ID,
+ TagId: tag1ID.String(),
Signature: []byte(signature1),
Content: []byte(content1),
},
{
- TagId: tag2ID,
+ TagId: tag2ID.String(),
Signature: []byte(signature2),
Content: []byte(content2),
},
@@ -136,11 +136,11 @@ func TestGetTagSignatures(t *testing.T) {
{
desc: "tag without signature",
revisions: []string{
- tag3ID,
+ tag3ID.String(),
},
expectedSignatures: []*gitalypb.GetTagSignaturesResponse_TagSignature{
{
- TagId: tag3ID,
+ TagId: tag3ID.String(),
Signature: []byte(""),
Content: []byte(content3),
},
diff --git a/internal/gitaly/service/remote/update_remote_mirror_test.go b/internal/gitaly/service/remote/update_remote_mirror_test.go
index c817ed69a..e56171609 100644
--- a/internal/gitaly/service/remote/update_remote_mirror_test.go
+++ b/internal/gitaly/service/remote/update_remote_mirror_test.go
@@ -641,9 +641,9 @@ func TestSuccessfulUpdateRemoteMirrorRequest(t *testing.T) {
testRepo, testRepoPath := gittest.CloneRepo(t, cfg, cfg.Storages[0])
_, mirrorPath := gittest.CloneRepo(t, cfg, cfg.Storages[0])
- gittest.CreateTag(t, cfg, mirrorPath, "v0.0.1", "master", nil) // I needed another tag for the tests
- gittest.CreateTag(t, cfg, testRepoPath, "new-tag", "60ecb67744cb56576c30214ff52294f8ce2def98", nil)
- gittest.CreateTag(t, cfg, testRepoPath, "v1.0.0", "0b4bc9a49b562e85de7cc9e834518ea6828729b9", &gittest.CreateTagOpts{
+ gittest.WriteTag(t, cfg, mirrorPath, "v0.0.1", "master") // I needed another tag for the tests
+ gittest.WriteTag(t, cfg, testRepoPath, "new-tag", "60ecb67744cb56576c30214ff52294f8ce2def98")
+ gittest.WriteTag(t, cfg, testRepoPath, "v1.0.0", "0b4bc9a49b562e85de7cc9e834518ea6828729b9", gittest.WriteTagConfig{
Message: "Overriding tag", Force: true,
})
@@ -758,9 +758,9 @@ func TestSuccessfulUpdateRemoteMirrorRequestWithWildcards(t *testing.T) {
{"tag", "--delete", "v1.1.0"}, // v1.1.0 is ambiguous, maps to a branch and a tag in gitlab-test repository
}
- gittest.CreateTag(t, cfg, testRepoPath, "new-tag", "60ecb67744cb56576c30214ff52294f8ce2def98", nil) // Add tag
- gittest.CreateTag(t, cfg, testRepoPath, "v1.0.0", "0b4bc9a49b562e85de7cc9e834518ea6828729b9",
- &gittest.CreateTagOpts{Message: "Overriding tag", Force: true}) // Update tag
+ gittest.WriteTag(t, cfg, testRepoPath, "new-tag", "60ecb67744cb56576c30214ff52294f8ce2def98") // Add tag
+ gittest.WriteTag(t, cfg, testRepoPath, "v1.0.0", "0b4bc9a49b562e85de7cc9e834518ea6828729b9",
+ gittest.WriteTagConfig{Message: "Overriding tag", Force: true}) // Update tag
for _, args := range setupCommands {
gitArgs := []string{"-C", testRepoPath}
@@ -770,7 +770,7 @@ func TestSuccessfulUpdateRemoteMirrorRequestWithWildcards(t *testing.T) {
// Workaround for https://gitlab.com/gitlab-org/gitaly/issues/1439
// Create a tag on the remote to ensure it gets deleted later
- gittest.CreateTag(t, cfg, mirrorPath, "v1.2.0", "master", nil)
+ gittest.WriteTag(t, cfg, mirrorPath, "v1.2.0", "master")
newTagOid := string(gittest.Exec(t, cfg, "-C", testRepoPath, "rev-parse", "v1.0.0"))
newTagOid = strings.TrimSpace(newTagOid)
@@ -874,7 +874,7 @@ func TestSuccessfulUpdateRemoteMirrorRequestWithKeepDivergentRefs(t *testing.T)
testRepo, testRepoPath := gittest.CloneRepo(t, cfg, cfg.Storages[0])
_, mirrorPath := gittest.CloneRepo(t, cfg, cfg.Storages[0])
- gittest.CreateTag(t, cfg, mirrorPath, "v2.0.0", "master", nil)
+ gittest.WriteTag(t, cfg, mirrorPath, "v2.0.0", "master")
setupCommands := [][]string{
// Preconditions
diff --git a/internal/gitaly/service/repository/fetch_remote_test.go b/internal/gitaly/service/repository/fetch_remote_test.go
index 05618d95d..5fac9e4a5 100644
--- a/internal/gitaly/service/repository/fetch_remote_test.go
+++ b/internal/gitaly/service/repository/fetch_remote_test.go
@@ -57,7 +57,7 @@ func TestFetchRemoteSuccess(t *testing.T) {
cloneRepo := copyRepo(t, cfg, repo, repoPath)
// Ensure there's a new tag to fetch
- gittest.CreateTag(t, cfg, repoPath, "testtag", "master", nil)
+ gittest.WriteTag(t, cfg, repoPath, "testtag", "master")
req := &gitalypb.FetchRemoteRequest{Repository: cloneRepo, RemoteParams: &gitalypb.Remote{
Url: repoPath,