syntax = "proto3";

package gitaly;

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

import "lint.proto";
import "shared.proto";

service OperationService {
  rpc UserCreateBranch(UserCreateBranchRequest) returns (UserCreateBranchResponse) {
    option (op_type) = {
      op: MUTATOR
    };
  }
  rpc UserUpdateBranch(UserUpdateBranchRequest) returns (UserUpdateBranchResponse) {
    option (op_type) = {
      op: MUTATOR
    };
  }
  rpc UserDeleteBranch(UserDeleteBranchRequest) returns (UserDeleteBranchResponse) {
    option (op_type) = {
      op: MUTATOR
    };
  }
  rpc UserCreateTag(UserCreateTagRequest) returns (UserCreateTagResponse) {
    option (op_type) = {
      op: MUTATOR
    };
  }
  rpc UserDeleteTag(UserDeleteTagRequest) returns (UserDeleteTagResponse) {
    option (op_type) = {
      op: MUTATOR
    };
  }
  rpc UserMergeToRef(UserMergeToRefRequest) returns (UserMergeToRefResponse) {
    option (op_type) = {
      op: MUTATOR
    };
  }
  rpc UserMergeBranch(stream UserMergeBranchRequest) returns (stream UserMergeBranchResponse) {
    option (op_type) = {
      op: MUTATOR
    };
  }

  // UserFFBranch tries to perform a fast-forward merge of the given branch to
  // the given commit. If the merge is not a fast-forward merge, the request
  // will fail. The RPC will return an empty response in case updating the
  // reference fails e.g. because of a race.
  rpc UserFFBranch(UserFFBranchRequest) returns (UserFFBranchResponse) {
    option (op_type) = {
      op: MUTATOR
    };
  }

  rpc UserCherryPick(UserCherryPickRequest) returns (UserCherryPickResponse) {
    option (op_type) = {
      op: MUTATOR
    };
  }

  // UserCommitFiles builds a commit from a stream of actions and updates the target branch to point to it.
  // UserCommitFilesRequest with a UserCommitFilesRequestHeader must be sent as the first message of the stream.
  // Following that, a variable number of actions can be sent to build a new commit. Each action consists of
  // a header followed by content if used by the action.
  rpc UserCommitFiles(stream UserCommitFilesRequest) returns (UserCommitFilesResponse) {
    option (op_type) = {
      op: MUTATOR
    };
  }
  rpc UserRebaseConfirmable(stream UserRebaseConfirmableRequest) returns (stream UserRebaseConfirmableResponse) {
    option (op_type) = {
      op: MUTATOR
    };
  }
  rpc UserRevert(UserRevertRequest) returns (UserRevertResponse) {
    option (op_type) = {
      op: MUTATOR
    };
  }
  rpc UserSquash(UserSquashRequest) returns (UserSquashResponse) {
    option (op_type) = {
      op: MUTATOR
    };
  }
  rpc UserApplyPatch(stream UserApplyPatchRequest) returns (UserApplyPatchResponse) {
    option (op_type) = {
      op: MUTATOR
    };
  }
  rpc UserUpdateSubmodule(UserUpdateSubmoduleRequest) returns (UserUpdateSubmoduleResponse) {
    option (op_type) = {
      op: MUTATOR
    };
  }
}

message UserCreateBranchRequest {
  Repository repository = 1 [(target_repository)=true];
  bytes branch_name = 2;
  User user = 3;
  bytes start_point = 4;
}

message UserCreateBranchResponse {
  Branch branch = 1;
  // Error returned by the pre-receive hook. If no error was thrown,
  // it's the empty string ("")
  string pre_receive_error = 2;
}

message UserUpdateBranchRequest {
  Repository repository = 1 [(target_repository)=true];
  bytes branch_name = 2;
  User user = 3;
  bytes newrev = 4;
  bytes oldrev = 5;
}

message UserUpdateBranchResponse {
  string pre_receive_error = 1;
}

message UserDeleteBranchRequest {
  Repository repository = 1 [(target_repository)=true];
  bytes branch_name = 2;
  User user = 3;
}

message UserDeleteBranchResponse {
  string pre_receive_error = 1;
}

message UserDeleteTagRequest {
  Repository repository = 1 [(target_repository)=true];
  bytes tag_name = 2;
  User user = 3;
}

message UserDeleteTagResponse {
  string pre_receive_error = 1;
}

message UserCreateTagRequest {
  Repository repository = 1 [(target_repository)=true];
  bytes tag_name = 2;
  User user = 3;
  bytes target_revision = 4;
  bytes message = 5;
}

message UserCreateTagResponse {
  Tag tag = 1;
  bool exists = 2;
  string pre_receive_error = 3;
}

message UserMergeBranchRequest {
  // First message
  Repository repository = 1 [(target_repository)=true];
  User user = 2;
  string commit_id = 3;
  bytes branch = 4;
  bytes message = 5;

  // Second message
  // Tell the server to apply the merge to the branch
  bool apply = 6;
}

message UserMergeBranchResponse {
  // First message
  // The merge commit the branch will be updated to. The caller can still abort the merge.
  string commit_id = 1;

  reserved 2;
  // Second message
  // If set, the merge has been applied to the branch.
  OperationBranchUpdate branch_update = 3;
  string pre_receive_error = 4;
}

message UserMergeToRefRequest {
  // UserMergeRef creates a merge commit and updates target_ref to point to that
  // new commit. The first parent of the merge commit (the main line) is taken
  // from first_parent_ref. The second parent is specified by its commit ID in source_sha.
  // If target_ref already exists it will be overwritten.
  Repository repository = 1 [(target_repository)=true];
  User user = 2;
  string source_sha = 3;
  // branch is deprecated in favor of `first_parent_ref`.
  bytes branch = 4;
  bytes target_ref = 5;
  bytes message = 6;
  bytes first_parent_ref = 7;
  // Allow conflicts to occur. Any conflict markers will be part of the merge commit.
  // Only text conflicts are handled, tree-based conflicts are not supported.
  bool allow_conflicts = 8;
}

message UserMergeToRefResponse {
  string commit_id = 1;
  string pre_receive_error = 2;
}

// OperationBranchUpdate contains the details of a branch update.
message OperationBranchUpdate {
  // commit_id is set to the OID of the created commit if a branch was created or updated.
  string commit_id = 1;
  // repo_created indicates whether the branch created was the first one in the repository.
  // Used for cache invalidation in GitLab.
  bool repo_created = 2;
  // branch_created indicates whether the branch already existed in the repository
  // and was updated or whether it was created. Used for cache invalidation in GitLab.
  bool branch_created = 3;
}

// UserFFBranchRequest contains parameters for the UserFFBranch RPC.
message UserFFBranchRequest {
  // repository is the repository for which to perform the fast-forward merge.
  Repository repository = 1 [(target_repository)=true];
  // user is the user which to perform the fast-forward merge as. This is used
  // for authorization checks.
  User user = 2;
  // commit_id is the commit ID to update the branch to.
  string commit_id = 3;
  // branch is the name of the branch that shall be update. This must be the
  // branch name only and not a fully qualified reference, e.g. "master"
  // instead of "refs/heads/master".
  bytes branch = 4;
}

message UserFFBranchResponse {
  OperationBranchUpdate branch_update = 1;
  string pre_receive_error = 2;
}

message UserCherryPickRequest {
  Repository repository = 1 [(target_repository)=true];
  User user = 2;
  GitCommit commit = 3;
  bytes branch_name = 4;
  bytes message = 5;
  bytes start_branch_name = 6;
  Repository start_repository = 7;
  bool dry_run = 8;
}

message UserCherryPickResponse {
  enum CreateTreeError {
    NONE = 0;
    EMPTY = 1;
    CONFLICT = 2;
  }

  OperationBranchUpdate branch_update = 1;
  string create_tree_error = 2;
  string commit_error = 3;
  string pre_receive_error = 4;
  CreateTreeError create_tree_error_code = 5;
}

message UserRevertRequest {
  Repository repository = 1 [(target_repository)=true];
  User user = 2;
  GitCommit commit = 3;
  bytes branch_name = 4;
  bytes message = 5;
  bytes start_branch_name = 6;
  Repository start_repository = 7;
  bool dry_run = 8;
}

message UserRevertResponse {
  enum CreateTreeError {
    NONE = 0;
    EMPTY = 1;
    CONFLICT = 2;
  }

  OperationBranchUpdate branch_update = 1;
  string create_tree_error = 2;
  string commit_error = 3;
  string pre_receive_error = 4;
  CreateTreeError create_tree_error_code = 5;
}

// UserCommitFilesActionHeader contains the details of the action to be performed.
message UserCommitFilesActionHeader {
  enum ActionType {
    // CREATE creates a new file.
    CREATE = 0;
    // CREATE_DIR creates a new directory.
    CREATE_DIR = 1;
    // UPDATE updates an existing file.
    UPDATE = 2;
    // MOVE moves an existing file to a new path.
    MOVE = 3;
    // DELETE deletes an existing file.
    DELETE = 4;
    // CHMOD changes the permissions of an existing file.
    CHMOD = 5;
  }
  // action is the type of the action taken to build a commit. Not all fields are
  // used for all of the actions.
  ActionType action = 1;
  // file_path refers to the file or directory being modified. The meaning differs for each
  // action:
  //   1. CREATE: path of the file to create
  //   2. CREATE_DIR: path of the directory to create
  //   3. UPDATE: path of the file to update
  //   4. MOVE: the new path of the moved file
  //   5. DELETE: path of the file to delete
  //   6. CHMOD: path of the file to modify permissions for
  bytes file_path = 2;
  // previous_path is used in MOVE action to specify the path of the file to move.
  bytes previous_path = 3;
  // base64_content indicates the content of the file is base64 encoded. The encoding
  // must be the standard base64 encoding defined in RFC 4648. Only used for CREATE and
  // UPDATE actions.
  bool base64_content = 4;
  // execute_filemode determines whether the file is created with execute permissions.
  // The field is only used in CREATE and CHMOD actions.
  bool execute_filemode = 5;
  // Move actions that change the file path, but not its content, should set
  // infer_content to true instead of populating the content field. Ignored for
  // other action types.
  bool infer_content = 6;
}

// UserCommitFilesAction is the request message used to stream in the actions to build a commit.
message UserCommitFilesAction {
  oneof user_commit_files_action_payload {
    // header contains the details of action being performed. Header must be sent before the
    // content if content is used by the action.
    UserCommitFilesActionHeader header = 1;
    // content is the content of the file streamed in one or more messages. Only used with CREATE
    // and UPDATE actions.
    bytes content = 2;
  }
}

// UserCommitFilesRequestHeader is the header of the UserCommitFiles that defines the commit details,
// parent and other information related to the call.
message UserCommitFilesRequestHeader {
  // repository is the target repository where to apply the commit.
  Repository repository = 1 [(target_repository)=true];
  // user is the user peforming the call.
  User user = 2;
  // branch_name is the name of the branch to point to the new commit. If start_sha and start_branch_name
  // are not defined, the commit of branch_name is used as the parent commit.
  bytes branch_name = 3;
  // commit_message is the message to use in the commit.
  bytes commit_message = 4;
  // commit_author_name is the commit author's name. If not provided, the user's name is
  // used instead.
  bytes commit_author_name = 5;
  // commit_author_email is the commit author's email. If not provided, the user's email is
  // used instead.
  bytes commit_author_email = 6;
  // start_branch_name specifies the branch whose commit to use as the parent commit. Takes priority
  // over branch_name. Optional.
  bytes start_branch_name = 7;
  // start_repository specifies which contains the parent commit. If not specified, repository itself
  // is used to look up the parent commit. Optional.
  Repository start_repository = 8;
  // force determines whether to force update the target branch specified by branch_name to
  // point to the new commit.
  bool force = 9;
  // start_sha specifies the SHA of the commit to use as the parent of new commit. Takes priority
  // over start_branch_name and branc_name. Optional.
  string start_sha = 10;
}

// UserCommitFiles is the request of UserCommitFiles.
message UserCommitFilesRequest {
  oneof user_commit_files_request_payload {
    // header defines the details of where to comnit, the details and which commit to use as the parent.
    // header must always be sent as the first request of the stream.
    UserCommitFilesRequestHeader header = 1;
    // action contains an action to build a commit. There can be multiple actions per stream.
    UserCommitFilesAction action = 2;
  }
}

// UserCommitFilesResponse is the response object of UserCommitFiles.
message UserCommitFilesResponse {
  // branch_update contains the details of the commit and the branch update.
  OperationBranchUpdate branch_update = 1;
  // index_error is set to the error message when an invalid action was attempted, such as
  // trying to create a file that already existed.
  string index_error = 2;
  // pre_receive_error is set when the pre-receive hook errored.
  string pre_receive_error = 3;
}

message UserRebaseConfirmableRequest {
  message Header {
    Repository repository = 1 [(target_repository)=true];
    User user = 2;
    string rebase_id = 3;
    bytes branch = 4;
    string branch_sha = 5;
    Repository remote_repository = 6;
    bytes remote_branch = 7;
    repeated string git_push_options = 8;
  }

  oneof user_rebase_confirmable_request_payload {
    // For each request stream there must be first a request with a header
    // containing details about the rebase to perform.
    Header header = 1;
    // A second request must be made to confirm that the rebase should
    // be applied to the branch.
    bool apply = 2;
  }
}

message UserRebaseConfirmableResponse {
  oneof user_rebase_confirmable_response_payload {
    // The first response will contain the rebase commit the branch will
    // be updated to. The caller can still abort the rebase.
    string rebase_sha = 1;
    // The second response confirms that the rebase has been applied to
    // the branch.
    bool rebase_applied = 2;
  }
  string pre_receive_error = 3;
  string git_error = 4;
}

message UserSquashRequest {
  Repository repository = 1 [(target_repository)=true];
  User user = 2;
  string squash_id = 3;
  reserved 4;
  string start_sha = 5;
  string end_sha = 6;
  User author = 7;
  bytes commit_message = 8;
}

message UserSquashResponse {
  string squash_sha = 1;
  // DEPRECATED: https://gitlab.com/gitlab-org/gitaly/proto/merge_requests/161
  reserved 2;
  reserved "pre_receive_error";
  string git_error = 3;
}

message UserApplyPatchRequest {
  message Header {
    Repository repository = 1 [(target_repository)=true];
    User user = 2;
    bytes target_branch = 3;
  }

  oneof user_apply_patch_request_payload {
    Header header = 1;
    bytes patches = 2;
  }
}

message UserApplyPatchResponse {
  OperationBranchUpdate branch_update = 1;
}

message UserUpdateSubmoduleRequest {
  Repository repository = 1 [(target_repository)=true];
  User user = 2;
  string commit_sha = 3;
  bytes branch = 4;
  bytes submodule = 5;
  bytes commit_message = 6;
}

message UserUpdateSubmoduleResponse {
  OperationBranchUpdate branch_update = 1;
  string pre_receive_error = 2;
  // DEPRECATED: https://gitlab.com/gitlab-org/gitaly/proto/merge_requests/237
  reserved 3;
  reserved "create_tree_error";
  string commit_error = 4;
}
