1package remoterepo
2
3import (
4	"context"
5	"fmt"
6
7	"gitlab.com/gitlab-org/gitaly/v14/client"
8	"gitlab.com/gitlab-org/gitaly/v14/internal/git"
9	"gitlab.com/gitlab-org/gitaly/v14/internal/gitaly/storage"
10	"gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb"
11	"google.golang.org/grpc"
12)
13
14// Repo represents a Git repository on a different Gitaly storage
15type Repo struct {
16	*gitalypb.Repository
17	conn *grpc.ClientConn
18}
19
20// New creates a new remote Repository from its protobuf representation.
21func New(ctx context.Context, repo *gitalypb.Repository, pool *client.Pool) (*Repo, error) {
22	server, err := storage.ExtractGitalyServer(ctx, repo.GetStorageName())
23	if err != nil {
24		return nil, fmt.Errorf("remote repository: %w", err)
25	}
26
27	cc, err := pool.Dial(ctx, server.Address, server.Token)
28	if err != nil {
29		return nil, fmt.Errorf("dial: %w", err)
30	}
31
32	return &Repo{
33		Repository: repo,
34		conn:       cc,
35	}, nil
36}
37
38// ResolveRevision will dial to the remote repository and attempt to resolve the
39// revision string via the gRPC interface.
40func (rr *Repo) ResolveRevision(ctx context.Context, revision git.Revision) (git.ObjectID, error) {
41	cli := gitalypb.NewCommitServiceClient(rr.conn)
42	resp, err := cli.FindCommit(ctx, &gitalypb.FindCommitRequest{
43		Repository: rr.Repository,
44		Revision:   []byte(revision.String()),
45	})
46	if err != nil {
47		return "", err
48	}
49
50	oidHex := resp.GetCommit().GetId()
51	if oidHex == "" {
52		return "", git.ErrReferenceNotFound
53	}
54
55	oid, err := git.NewObjectIDFromHex(oidHex)
56	if err != nil {
57		return "", err
58	}
59
60	return oid, nil
61}
62
63// HasBranches will dial to the remote repository and check whether the repository has any branches.
64func (rr *Repo) HasBranches(ctx context.Context) (bool, error) {
65	resp, err := gitalypb.NewRepositoryServiceClient(rr.conn).HasLocalBranches(
66		ctx, &gitalypb.HasLocalBranchesRequest{Repository: rr.Repository})
67	if err != nil {
68		return false, fmt.Errorf("has local branches: %w", err)
69	}
70
71	return resp.Value, nil
72}
73
74// GetDefaultBranch returns the default branch for the remote repository. It does so by invoking
75// `FindDefaultBranchName()`, which itself is a wrapper around `localrepo.GetDefaultBranch()`.
76// Semantics of this function thus match the localrepo semantics.
77func (rr *Repo) GetDefaultBranch(ctx context.Context) (git.ReferenceName, error) {
78	resp, err := gitalypb.NewRefServiceClient(rr.conn).FindDefaultBranchName(
79		ctx, &gitalypb.FindDefaultBranchNameRequest{Repository: rr.Repository})
80	if err != nil {
81		return "", err
82	}
83
84	return git.ReferenceName(resp.Name), nil
85}
86