syntax = "proto3";

package gitaly;

import "errors.proto";
import "google/protobuf/timestamp.proto";
import "lint.proto";
import "shared.proto";

option go_package = "gitlab.com/gitlab-org/gitaly/v15/proto/go/gitalypb";

// RefService is a service that provides RPCs to list and modify Git references.
service RefService {

  // This comment is left unintentionally blank.
  rpc FindDefaultBranchName(FindDefaultBranchNameRequest) returns (FindDefaultBranchNameResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }

  // FindAllBranchNames is deprecated in favor of ListRefs
  //
  // https://gitlab.com/gitlab-org/gitaly/-/issues/3966
  rpc FindAllBranchNames(FindAllBranchNamesRequest) returns (stream FindAllBranchNamesResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
    option deprecated = true;
  }

  // FindAllTagNames is deprecated in favor of ListRefs
  //
  // https://gitlab.com/gitlab-org/gitaly/-/issues/3966
  rpc FindAllTagNames(FindAllTagNamesRequest) returns (stream FindAllTagNamesResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
    option deprecated = true;
  }

  // Return a stream so we can divide the response in chunks of branches
  rpc FindLocalBranches(FindLocalBranchesRequest) returns (stream FindLocalBranchesResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }

  // This comment is left unintentionally blank.
  rpc FindAllBranches(FindAllBranchesRequest) returns (stream FindAllBranchesResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }

  // Returns a stream of tags repository has.
  rpc FindAllTags(FindAllTagsRequest) returns (stream FindAllTagsResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }

  // FindTag looks up a tag by its name and returns it to the caller if it exists. This RPC supports
  // both lightweight and annotated tags. Note: this RPC returns an `Internal` error if the tag was
  // not found.
  rpc FindTag(FindTagRequest) returns (FindTagResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }

  // This comment is left unintentionally blank.
  rpc FindAllRemoteBranches(FindAllRemoteBranchesRequest) returns (stream FindAllRemoteBranchesResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }

  // This comment is left unintentionally blank.
  rpc RefExists(RefExistsRequest) returns (RefExistsResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }

  // FindBranch finds a branch by its unqualified name (like "master") and
  // returns the commit it currently points to.
  rpc FindBranch(FindBranchRequest) returns (FindBranchResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }

  // This comment is left unintentionally blank.
  rpc DeleteRefs(DeleteRefsRequest) returns (DeleteRefsResponse) {
    option (op_type) = {
      op: MUTATOR
    };
  }

  // This comment is left unintentionally blank.
  rpc ListBranchNamesContainingCommit(ListBranchNamesContainingCommitRequest) returns (stream ListBranchNamesContainingCommitResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }

  // This comment is left unintentionally blank.
  rpc ListTagNamesContainingCommit(ListTagNamesContainingCommitRequest) returns (stream ListTagNamesContainingCommitResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }

  // GetTagSignatures returns signatures for annotated tags resolved from a set of revisions. Revisions
  // which don't resolve to an annotated tag are silently discarded. Revisions which cannot be resolved
  // result in an error. Tags which are annotated but not signed will return a TagSignature response
  // which has no signature, but its unsigned contents will still be returned.
  rpc GetTagSignatures(GetTagSignaturesRequest) returns (stream GetTagSignaturesResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }

  // This comment is left unintentionally blank.
  rpc GetTagMessages(GetTagMessagesRequest) returns (stream GetTagMessagesResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }

  // PackRefs is deprecated in favor of OptimizeRepository.
  rpc PackRefs(PackRefsRequest) returns (PackRefsResponse) {
    option deprecated = true;
    option (op_type) = {
      op: MAINTENANCE
    };
  }

  // ListRefs returns a stream of all references in the repository. By default, pseudo-revisions like HEAD
  // will not be returned by this RPC. Any symbolic references will be resolved to the object ID it is
  // pointing at.
  rpc ListRefs(ListRefsRequest) returns (stream ListRefsResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }

  // FindRefsByOID returns an array of fully qualified reference names that point to an object ID.
  // It returns nothing if the object ID doesn't exist, or doesn't point to
  // any branches or tags. Prefixes can be also be used as the object ID.
  rpc FindRefsByOID(FindRefsByOIDRequest) returns (FindRefsByOIDResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }

}

// This comment is left unintentionally blank.
message FindDefaultBranchNameRequest {
  // This comment is left unintentionally blank.
  Repository repository = 1 [(target_repository)=true];
}

// This comment is left unintentionally blank.
message FindDefaultBranchNameResponse {
  // This comment is left unintentionally blank.
  bytes name = 1;
}

// This comment is left unintentionally blank.
message FindAllBranchNamesRequest {
  // This comment is left unintentionally blank.
  Repository repository = 1 [(target_repository)=true];
}

// This comment is left unintentionally blank.
message FindAllBranchNamesResponse {
  // This comment is left unintentionally blank.
  repeated bytes names = 1;
}

// This comment is left unintentionally blank.
message FindAllTagNamesRequest {
  // This comment is left unintentionally blank.
  Repository repository = 1 [(target_repository)=true];
}

// This comment is left unintentionally blank.
message FindAllTagNamesResponse {
  // This comment is left unintentionally blank.
  repeated bytes names = 1;
}

// This comment is left unintentionally blank.
message FindLocalBranchesRequest {
  // This comment is left unintentionally blank.
  enum SortBy {
    // This comment is left unintentionally blank.
    NAME = 0; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX ENUM_FIELD_NAMES_ZERO_VALUE_END_WITH
    // This comment is left unintentionally blank.
    UPDATED_ASC = 1; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX
    // This comment is left unintentionally blank.
    UPDATED_DESC = 2; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX
  }

  // This comment is left unintentionally blank.
  Repository repository = 1 [(target_repository)=true];
  // This comment is left unintentionally blank.
  SortBy sort_by = 2;
  // The page token is the branch name, with the `refs/heads/` prefix, for
  // example "refs/heads/master". After the first branch name is encountered
  // which lexicographically exceeds the page token, it will be the first result
  // send as part of the response.
  PaginationParameter pagination_params = 3;
}

// This comment is left unintentionally blank.
message FindLocalBranchesResponse {
  // This field is duplicated by 'local_branches' and will be marked deprecated
  // as we add the implementation for 'local_branches'.
  // Issue: https://gitlab.com/gitlab-org/gitaly/-/issues/1294
  repeated FindLocalBranchResponse branches = 1;
  // This comment is left unintentionally blank.
  repeated Branch local_branches = 2;
}

// This comment is left unintentionally blank.
message FindLocalBranchResponse {
  // This comment is left unintentionally blank.
  bytes name = 1;
  // This comment is left unintentionally blank.
  string commit_id = 2;
  // This comment is left unintentionally blank.
  bytes commit_subject = 3;
  // This comment is left unintentionally blank.
  FindLocalBranchCommitAuthor commit_author = 4;
  // This comment is left unintentionally blank.
  FindLocalBranchCommitAuthor commit_committer = 5;
  // This comment is left unintentionally blank.
  GitCommit commit = 6;
}

// This comment is left unintentionally blank.
message FindLocalBranchCommitAuthor {
  // This comment is left unintentionally blank.
  bytes name = 1;
  // This comment is left unintentionally blank.
  bytes email = 2;
  // This comment is left unintentionally blank.
  google.protobuf.Timestamp date = 3;
  // This comment is left unintentionally blank.
  bytes timezone = 4;
}

// This comment is left unintentionally blank.
message FindAllBranchesRequest {
  // This comment is left unintentionally blank.
  Repository repository = 1 [(target_repository)=true];
  // Only return branches that are merged into root ref
  bool merged_only = 2;
  // If merged_only is true, this is a list of branches from which we
  // return those merged into the root ref
  repeated bytes merged_branches = 3;
}

// This comment is left unintentionally blank.
message FindAllBranchesResponse {
  // This comment is left unintentionally blank.
  message Branch {
    // This comment is left unintentionally blank.
    bytes name = 1;
    // This comment is left unintentionally blank.
    GitCommit target = 2;
  }

  // This comment is left unintentionally blank.
  repeated Branch branches = 1;
}

// FindTagRequest is a request for the FindTag RPC.
message FindTagRequest {
  // Repository is the repository to look up the tag in.
  Repository repository = 1 [(target_repository)=true];
  // TagName is the name of the tag that should be looked up. The caller is supposed to pass in the
  // tag name only, so if e.g. a tag `refs/tags/v1.0.0` exists, then the caller should pass `v1.0.0`
  // as argument.
  bytes tag_name = 2;
}

// FindTagResponse is a response for the FindTag RPC.
message FindTagResponse {
  // Tag is the tag that was found.
  Tag tag = 1;
}

// FindTagError is an error that will be returned by the FindTag RPC under specific error
// conditions.
message FindTagError {
  oneof error {
    // TagNotFound indicates that the tag was not found.
    ReferenceNotFoundError tag_not_found = 1;
  }
}

// This comment is left unintentionally blank.
message FindAllTagsRequest {
  // SortBy allows to specify desired order of the elements.
  message SortBy {
    // Key is a key used for sorting.
    enum Key {
      // This comment is left unintentionally blank.
      REFNAME     = 0; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX ENUM_FIELD_NAMES_ZERO_VALUE_END_WITH
      // This comment is left unintentionally blank.
      CREATORDATE = 1; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX
      // VERSION_REFNAME sorts tags by their semantic versions (https://semver.org/).
      // Tag names that are not semantic versions are sorted lexicographically. They come before
      // the semantic versions if the direction is ascending and after the semantic versions if
      // the direction is descending.
      VERSION_REFNAME = 2; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX
    }

    // This comment is left unintentionally blank.
    Key           key       = 1;
    // This comment is left unintentionally blank.
    SortDirection direction = 2;
  }

  // This comment is left unintentionally blank.
  Repository repository = 1 [(target_repository)=true];
  // SortBy allows to request tags in particular order.
  SortBy sort_by = 2;
  // The page token is the tags name, with the `refs/tags/` prefix, for
  // example "refs/tags/v1.0.0". When the tag name matches the page token,
  // the tag following it will be the first result send as part of the response.
  PaginationParameter pagination_params = 3;
}

// This comment is left unintentionally blank.
message FindAllTagsResponse {
  // This comment is left unintentionally blank.
  repeated Tag tags = 1;
}

// This comment is left unintentionally blank.
message RefExistsRequest {
  // This comment is left unintentionally blank.
  Repository repository = 1 [(target_repository)=true];
  // Any ref, e.g. 'refs/heads/master' or 'refs/tags/v1.0.1'. Must start with 'refs/'.
  bytes ref = 2;
}

// This comment is left unintentionally blank.
message RefExistsResponse {
  // This comment is left unintentionally blank.
  bool value = 1;
}

// This comment is left unintentionally blank.
message CreateBranchRequest {
  // This comment is left unintentionally blank.
  Repository repository = 1 [(target_repository)=true];
  // This comment is left unintentionally blank.
  bytes name = 2;
  // This comment is left unintentionally blank.
  bytes start_point = 3;
}

// This comment is left unintentionally blank.
message CreateBranchResponse {
  // This comment is left unintentionally blank.
  enum Status {
    // This comment is left unintentionally blank.
    OK = 0; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX ENUM_FIELD_NAMES_ZERO_VALUE_END_WITH
    // This comment is left unintentionally blank.
    ERR_EXISTS = 1; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX
    // This comment is left unintentionally blank.
    ERR_INVALID = 2; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX
    // This comment is left unintentionally blank.
    ERR_INVALID_START_POINT = 3; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX
  }

  // This comment is left unintentionally blank.
  Status status = 1;
  // This comment is left unintentionally blank.
  Branch branch = 2;
}

// This comment is left unintentionally blank.
message DeleteBranchRequest {
  // This comment is left unintentionally blank.
  Repository repository = 1 [(target_repository)=true];
  // This comment is left unintentionally blank.
  bytes name = 2;
}

// Not clear if we need to do status signaling; we can add fields later.
message DeleteBranchResponse {
}

// This comment is left unintentionally blank.
message FindBranchRequest {
  // repository is the repository in which the branch should be looked up.
  Repository repository = 1 [(target_repository)=true];
  // name is the name of the branch which should be looked up. This must be the
  // branch name only, it must not have the "refs/heads/" prefix.
  bytes name = 2;
}

// This comment is left unintentionally blank.
message FindBranchResponse {
  // This comment is left unintentionally blank.
  Branch branch = 1;
}

// This comment is left unintentionally blank.
message DeleteRefsRequest{
  // This comment is left unintentionally blank.
  Repository repository = 1 [(target_repository)=true];
  // The following two fields are mutually exclusive
  repeated bytes except_with_prefix = 2; // protolint:disable:this REPEATED_FIELD_NAMES_PLURALIZED
  // This comment is left unintentionally blank.
  repeated bytes refs = 3;
}

// This comment is left unintentionally blank.
message DeleteRefsResponse {
  // This comment is left unintentionally blank.
  string git_error = 1;
}

// DeleteRefsError is returned when DeleteRefs fails to delete refs
message DeleteRefsError {
  oneof error {
    // InvalidFormat is returned when one or more of the refs to be deleted
    // have an invalid format.
    InvalidRefFormatError invalid_format = 1;
    // ReferencesLocked is returned when the references to be deleted are already
    // locked by another process.
    ReferencesLockedError references_locked = 2;
  }
}

// This comment is left unintentionally blank.
message ListBranchNamesContainingCommitRequest {
  // This comment is left unintentionally blank.
  Repository repository = 1 [(target_repository)=true];
  // This comment is left unintentionally blank.
  string commit_id = 2;
  // Limit the number of tag names to be returned
  // If the limit is set to zero, all items will be returned
  uint32 limit = 3;
}

// This comment is left unintentionally blank.
message ListBranchNamesContainingCommitResponse {
  reserved 1;
  // This comment is left unintentionally blank.
  repeated bytes branch_names = 2;
}

// This comment is left unintentionally blank.
message ListTagNamesContainingCommitRequest {
  // This comment is left unintentionally blank.
  Repository repository = 1 [(target_repository)=true];
  // This comment is left unintentionally blank.
  string commit_id = 2;
  // Limit the number of tag names to be returned
  // If the limit is set to zero, all items will be returned
  uint32 limit = 3;
}

// This comment is left unintentionally blank.
message ListTagNamesContainingCommitResponse {
  reserved 1;
  // This comment is left unintentionally blank.
  repeated bytes tag_names = 2;
}

// GetTagSignaturesRequest is a request for the GetTagSignatures RPC.
message GetTagSignaturesRequest {
  // Repository is the repository in which tag signatures should be looked up.
  Repository repository = 1 [(target_repository)=true];
  // TagRevisions is the set of revisions which that should be looked up. Revisions
  // supports the syntax as specified by gitrevisions(7). All revisions are expected
  // to resolve to annotated tag objects. At least one revision must be provided.
  repeated string tag_revisions = 2;
}

// GetTagSignaturesResponse is a response for a GetTagSignatures request. Each response
// may contain multiple TagSignatures. In case TagSignatures don't fit into a single
// response, signatures will be batched in multiple responses.
message GetTagSignaturesResponse {
  // TagSignature represents the signature of a signed tag.
  message TagSignature {
    // TagId is the resolved object ID of the tag.
    string tag_id = 1;
    // Signature contains the cryptographic signature of the tag. If the tag is not
    // cryptographically signed, then the signature is unset.
    bytes signature = 2;
    // Content contains the contents which are signed by the signature. Contents
    // include both the commit message, but also the commit metadata like author and
    // subject.
    bytes content = 3;
  }

  // Signatures is the set of signatures found.
  repeated TagSignature signatures = 1;
}

// This comment is left unintentionally blank.
message GetTagMessagesRequest {
  reserved 2;
  reserved "tag_names";

  // This comment is left unintentionally blank.
  Repository repository = 1 [(target_repository)=true];
  // This comment is left unintentionally blank.
  repeated string tag_ids = 3;
}

// This comment is left unintentionally blank.
message GetTagMessagesResponse {
  reserved 1;
  reserved "tag_name";

  // This comment is left unintentionally blank.
  bytes message = 2;
  // Only present for a new tag message
  string tag_id = 3;
}

// This comment is left unintentionally blank.
message FindAllRemoteBranchesRequest {
  // This comment is left unintentionally blank.
  Repository repository = 1 [(target_repository)=true];
  // This comment is left unintentionally blank.
  string remote_name = 2;
}

// This comment is left unintentionally blank.
message FindAllRemoteBranchesResponse {
  // This comment is left unintentionally blank.
  repeated Branch branches = 1;
}

// This comment is left unintentionally blank.
message PackRefsRequest {
  // This comment is left unintentionally blank.
  Repository repository = 1 [(target_repository)=true];

  // DEPRECATED: https://gitlab.com/gitlab-org/gitaly/-/issues/3997
  reserved 2;
  reserved "all_refs";
}

// This comment is left unintentionally blank.
message PackRefsResponse{
}

// ListRefsRequest is a request for the ListRefs RPC.
message ListRefsRequest {
  // This comment is left unintentionally blank.
  message SortBy {
    // This comment is left unintentionally blank.
    enum Key {
      // This comment is left unintentionally blank.
      REFNAME       = 0; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX ENUM_FIELD_NAMES_ZERO_VALUE_END_WITH
      // This comment is left unintentionally blank.
      CREATORDATE   = 1; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX
      // This comment is left unintentionally blank.
      AUTHORDATE    = 2; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX
      // This comment is left unintentionally blank.
      COMMITTERDATE = 3; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX
    }

    // Key is a key used for sorting.
    Key           key       = 1;
    // This comment is left unintentionally blank.
    SortDirection direction = 2;
  }

  // Repository is the repository in which references should be listed in.
  Repository repository = 1 [(target_repository)=true];
  // Patterns contains all patterns which shall be listed. Patterns should be in the format
  // accepted by git-for-each-ref(1). At least one pattern must be given, otherwise an error
  // is returned. Patterns which don't match any reference will be silently ignored.
  repeated bytes patterns = 2;
  // Head determines whether the RPC should also return the HEAD reference. By default,
  // pseudo-refs are not included in the response.
  bool head = 3;
  // SortBy allows to request SHAs in particular order.
  SortBy sort_by = 4;
  // PointingAtOids is a list of OIDs that can optionally be passed to only return refs
  // pointing at the given OIDs. This corresponds to the --points-at option of git-for-each-ref(1).
  repeated bytes pointing_at_oids = 5;
  // PeelTags controls whether annotated tags should be peeled to their target objects so that the
  // `PeeledTarget` returned for the reference is the ID of the target object. Note that this
  // will significantly slow down the request by a factor of 3 to 4.
  bool peel_tags = 6;
}

// ListRefsResponse is a response for the ListRefs RPC. The RPC can return multiple responses
// in case there are more references than fit into a single gRPC message.
message ListRefsResponse{
  // Reference is a direct Git reference. No symbolic references will ever be returned by this RPC.
  message Reference {
    // Name is the fully qualified name of the reference.
    bytes name = 1;
    // Target is the object ID the reference points to.
    string target = 2;
    // PeeledTarget is the object ID an annotated tag points to. This field is only set when
    // `PeelTags=true`. This field is empty in case the object is not an annotated tag.
    string peeled_target = 3;
  }

  // References is the set of references returned by the RPC.
  repeated Reference references = 1;
}

// This comment is left unintentionally blank.
message FindRefsByOIDRequest {
  // repository is the repository in which references will be looked for.
  Repository repository = 1 [(target_repository)=true];
  // oid is an object ID to find references for.
  string oid = 2;
  // ref_patterns can be one of branch name, tag name or fully qualified ref name.
  // Providing more than one pattern will yield refs that match any of the given patterns.
  // If left empty, defaults to "refs/heads/" and "refs/tags/"
  repeated string ref_patterns = 3;
  // sort_field determines the sort order of the resulting refs.
  // If left empty, defaults to "refname" (lexicographic refname order)
  string sort_field = 4;
  // limit limits the amount of results returned. 0 means no limit.
  uint32 limit = 5;
}

// This comment is left unintentionally blank.
message FindRefsByOIDResponse {
  // refs is the set of fully-qualified references which have been found.
  repeated string refs = 1;
}
