syntax = "proto3";

package gitaly;

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

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

service CommitService {
  rpc CommitIsAncestor(CommitIsAncestorRequest) returns (CommitIsAncestorResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }
  rpc TreeEntry(TreeEntryRequest) returns (stream TreeEntryResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }
  rpc CommitsBetween(CommitsBetweenRequest) returns (stream CommitsBetweenResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }
  rpc CountCommits(CountCommitsRequest) returns (CountCommitsResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }
  rpc CountDivergingCommits(CountDivergingCommitsRequest) returns (CountDivergingCommitsResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }
  rpc GetTreeEntries(GetTreeEntriesRequest) returns (stream GetTreeEntriesResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }
  rpc ListFiles(ListFilesRequest) returns (stream ListFilesResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }
  rpc FindCommit(FindCommitRequest) returns (FindCommitResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }
  rpc CommitStats(CommitStatsRequest) returns (CommitStatsResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }
  // Use a stream to paginate the result set
  rpc FindAllCommits(FindAllCommitsRequest) returns (stream FindAllCommitsResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }
  rpc FindCommits(FindCommitsRequest) returns (stream FindCommitsResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }
  rpc CommitLanguages(CommitLanguagesRequest) returns (CommitLanguagesResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }
  rpc RawBlame(RawBlameRequest) returns (stream RawBlameResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }
  rpc LastCommitForPath(LastCommitForPathRequest) returns (LastCommitForPathResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }
  rpc ListLastCommitsForTree(ListLastCommitsForTreeRequest) returns (stream ListLastCommitsForTreeResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }
  rpc CommitsByMessage(CommitsByMessageRequest) returns (stream CommitsByMessageResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }
  rpc ListCommitsByOid(ListCommitsByOidRequest) returns (stream ListCommitsByOidResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }
  rpc ListCommitsByRefName(ListCommitsByRefNameRequest) returns (stream ListCommitsByRefNameResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }
  rpc FilterShasWithSignatures(stream FilterShasWithSignaturesRequest) returns (stream FilterShasWithSignaturesResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }
  rpc GetCommitSignatures(GetCommitSignaturesRequest) returns (stream GetCommitSignaturesResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }

  rpc GetCommitMessages(GetCommitMessagesRequest) returns (stream GetCommitMessagesResponse) {
    option (op_type) = {
      op: ACCESSOR
    };
  }
}

message CommitStatsRequest {
  Repository repository = 1 [(target_repository)=true];
  bytes revision = 2;
}

message CommitStatsResponse {
  // OID is the commit. Empty means not found
  string oid = 1;
  int32 additions = 2;
  int32 deletions = 3;
}

message CommitIsAncestorRequest {
  Repository repository = 1 [(target_repository)=true];
  string ancestor_id = 2;
  string child_id = 3;
}

message CommitIsAncestorResponse {
  bool value = 1;
}

message TreeEntryRequest {
  Repository repository = 1 [(target_repository)=true];
  // commit ID or refname
  bytes revision = 2;
  // entry path relative to repository root
  bytes path = 3;
  // Limit is the maximum number of bytes to fetch. If object is bigger, remaining bytes are not sent
  // 0 means there is no limit.
  int64 limit = 4;
  // MaxSize is the maximum allowed object size. If bigger, a FailedPrecondition error is returned
  // 0 means there is no maximum size.
  int64 max_size = 5;
}

message TreeEntryResponse {
  // TODO: Replace this enum with ObjectType in shared.proto
  enum ObjectType {
    COMMIT = 0;
    BLOB = 1;
    TREE = 2;
    TAG = 3;
  }
  ObjectType type = 1;
  // SHA1 object ID
  string oid = 2;
  int64 size = 3;
  // file mode
  int32 mode = 4;
  // raw object contents
  bytes data = 5;
}

message CommitsBetweenRequest {
  Repository repository = 1 [(target_repository)=true];
  bytes from = 2;
  bytes to = 3;

  // The page token is the last commit OID that was sent. It's expected to be the
  // full object ID to guard against ambigious OIDs. Using the page parameter is
  // effectivaly equivalent to setting the `from` parameter to the commit object
  // identifier.
  PaginationParameter pagination_params = 4;
}

message CommitsBetweenResponse {
  repeated GitCommit commits = 1;
}

message CountCommitsRequest {
  Repository repository = 1 [(target_repository)=true];
  bytes revision = 2;
  google.protobuf.Timestamp after = 3;
  google.protobuf.Timestamp before = 4;
  bytes path = 5;
  int32 max_count = 6;
  // all and revision are mutually exclusive
  bool all = 7;
  bool first_parent = 8;
  GlobalOptions global_options = 9;
}

message CountCommitsResponse {
  int32 count = 1;
}

message CountDivergingCommitsRequest {
  Repository repository = 1 [(target_repository)=true];
  bytes from = 2;
  bytes to = 3;
  reserved 4;
  reserved 5;
  reserved 6;
  int32 max_count = 7;
}

message CountDivergingCommitsResponse {
  int32 left_count = 1;
  int32 right_count = 2;
}

message TreeEntry {
  // TODO: Replace this enum with ObjectType in shared.proto
  enum EntryType {
    BLOB = 0;
    TREE = 1;
    COMMIT = 3;
  }
  // OID of the object this tree entry points to
  string oid = 1;
  // OID of the tree attached to commit_oid
  string root_oid = 2;
  // Path relative to repository root
  bytes path = 3;
  EntryType type = 4;
  // File mode e.g. 0644
  int32 mode = 5;
  // The commit object via which this entry was retrieved
  string commit_oid = 6;
  // Relative path of the first subdir that doesn't have only one directory descendant
  bytes flat_path = 7;
}

message GetTreeEntriesRequest {
  Repository repository = 1 [(target_repository)=true];
  bytes revision = 2;
  bytes path = 3;
  bool recursive = 4;
}

message GetTreeEntriesResponse {
  repeated TreeEntry entries = 1;
}

message ListFilesRequest {
  Repository repository = 1 [(target_repository)=true];
  bytes revision = 2;
}

// A single 'page' of the paginated response
message ListFilesResponse {
  // Remember to force encoding utf-8 on the client side
  repeated bytes paths = 1;
}

message FindCommitRequest {
  Repository repository = 1 [(target_repository)=true];
  bytes revision = 2;
  bool trailers = 3;
}

message FindCommitResponse {
  // commit is nil when the commit was not found
  GitCommit commit = 1;
}

message ListCommitsByOidRequest {
  Repository repository = 1 [(target_repository)=true];
  repeated string oid = 2;
}

message ListCommitsByOidResponse {
  repeated GitCommit commits = 1;
}

message ListCommitsByRefNameRequest {
  Repository repository = 1 [(target_repository)=true];
  repeated bytes ref_names = 2;
}

message ListCommitsByRefNameResponse {
  reserved 1;

  message CommitForRef {
    GitCommit commit = 1;
    bytes ref_name = 2;
  }

  repeated CommitForRef commit_refs = 2;
}

message FindAllCommitsRequest {
  Repository repository = 1 [(target_repository)=true];
  // When nil, return all commits reachable by any branch in the repo
  bytes revision = 2;
  int32 max_count = 3;
  int32 skip = 4;
  enum Order {
    NONE = 0;
    TOPO = 1;
    DATE = 2;
  }
  Order order = 5;
}

// A single 'page' of the result set
message FindAllCommitsResponse {
  repeated GitCommit commits = 1;
}

message FindCommitsRequest {
  Repository repository = 1 [(target_repository)=true];
  bytes revision = 2;
  int32 limit = 3;
  int32 offset = 4;
  repeated bytes paths = 5;
  bool follow = 6;
  bool skip_merges = 7;
  bool disable_walk = 8;
  google.protobuf.Timestamp after = 9;
  google.protobuf.Timestamp before = 10;
  // all and revision are mutually exclusive
  bool all = 11;
  bool first_parent = 12;
  bytes author = 13;
  enum Order {
    NONE = 0;
    TOPO = 1;
  }
  Order order = 14;
  GlobalOptions global_options = 15;
  bool trailers = 16;
}

// A single 'page' of the result set
message FindCommitsResponse {
  repeated GitCommit commits = 1;
}

message CommitLanguagesRequest {
  Repository repository = 1 [(target_repository)=true];
  bytes revision = 2;
}

message CommitLanguagesResponse {
  message Language {
    string name = 1;
    float share = 2;
    string color = 3;
    uint32 file_count = 4;
    uint64 bytes = 5;
  }
  repeated Language languages = 1;
}

message RawBlameRequest {
  Repository repository = 1 [(target_repository)=true];
  bytes revision = 2;
  bytes path = 3;
}

message RawBlameResponse {
  bytes data = 1;
}

message LastCommitForPathRequest {
  Repository repository = 1 [(target_repository)=true];
  bytes revision = 2;
  bytes path = 3;
  bool literal_pathspec = 4; // Deprecate after Rails stops using this
  GlobalOptions global_options = 5;
}

message LastCommitForPathResponse {
  // commit is nil when the commit was not found
  GitCommit commit = 1;
}

message ListLastCommitsForTreeRequest {
  Repository repository = 1 [(target_repository)=true];
  string revision = 2;
  bytes path = 3;
  int32 limit = 4;
  int32 offset = 5;
  bool literal_pathspec = 6 [deprecated = true];
  GlobalOptions global_options = 7;
}

message ListLastCommitsForTreeResponse {
  message CommitForTree {
    reserved 1;

    GitCommit commit = 2;
    reserved 3;
    bytes path_bytes = 4;
  }
  repeated CommitForTree commits = 1;
}

message CommitsByMessageRequest {
  Repository repository = 1 [(target_repository)=true];
  bytes revision = 2;
  int32 offset = 3;
  int32 limit = 4;
  bytes path = 5;
  string query = 6;
  GlobalOptions global_options = 7;
}

// One 'page' of the paginated response of CommitsByMessage
message CommitsByMessageResponse {
  repeated GitCommit commits = 1;
}

message FilterShasWithSignaturesRequest {
  Repository repository = 1 [(target_repository)=true];
  repeated bytes shas = 2;
}

message FilterShasWithSignaturesResponse {
  repeated bytes shas = 1;
}

message ExtractCommitSignatureRequest {
  Repository repository = 1 [(target_repository)=true];
  string commit_id = 2;
}

// Either of the 'signature' and 'signed_text' fields may be present. It
// is up to the caller to stitch them together.
message ExtractCommitSignatureResponse {
  bytes signature = 1;
  bytes signed_text = 2;
}

message GetCommitSignaturesRequest {
  Repository repository = 1 [(target_repository)=true];
  repeated string commit_ids = 2;
}

message GetCommitSignaturesResponse {
  // Only present for a new commit signature data.
  string commit_id = 1;
  // See ExtractCommitSignatureResponse above for how these fields should be handled.
  bytes signature = 2;
  bytes signed_text = 3;
}

message GetCommitMessagesRequest {
  Repository repository = 1 [(target_repository)=true];
  repeated string commit_ids = 2;
}

message GetCommitMessagesResponse {
  // Only present for a new commit message
  string commit_id = 1;
  bytes message = 2;
}
