syntax = "proto3";

package gitaly;

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

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

// OperationService provides an interface for performing mutating git
// operations on a repository on behalf of a user. The user's operation is
// treated as untrusted. Any reference update is thus checked against GitLab's
// '/allowed' endpoint.
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
    };
  }

  // UserCreateTag creates a new tag.
  rpc UserCreateTag(UserCreateTagRequest) returns (UserCreateTagResponse) {
    option (op_type) = {
      op: MUTATOR
    };
  }
  rpc UserDeleteTag(UserDeleteTagRequest) returns (UserDeleteTagResponse) {
    option (op_type) = {
      op: MUTATOR
    };
  }

  // 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.
  rpc UserMergeToRef(UserMergeToRefRequest) returns (UserMergeToRefResponse) {
    option (op_type) = {
      op: MUTATOR
    };
  }

  // UserMergeBranch tries to merge the given commit into the target branch.
  // The merge commit is created with the given user as author/committer and
  // the given message.
  //
  // This RPC requires confirmation to make any user-visible changes to the
  // repository. The first request sent shall contain details about the
  // requested merge, which will result in a response with the created merge
  // commit ID. Only if a second message with `apply = true` is sent will the
  // merge be applied.
  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
    };
  }

  // UserCherryPick tries to perform a cherry-pick of a given commit onto a
  // branch.
  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
    };
  }

  // UserRebaseConfirmable rebases the given remote branch onto a target
  // branch. The remote branch may be part of another repository.
  //
  // This RPC requires confirmation to make any user-visible changes to the
  // repository. The first request sent shall contains details about the
  // requested rebase, which will result in a response with the created rebase
  // commit ID. Only if a second message with `apply = true` is sent will the
  // rebase be applied.
  rpc UserRebaseConfirmable(stream UserRebaseConfirmableRequest) returns (stream UserRebaseConfirmableResponse) {
    option (op_type) = {
      op: MUTATOR
    };
  }

  // UserRevert tries to perform a revert of a given commit onto a branch.
  rpc UserRevert(UserRevertRequest) returns (UserRevertResponse) {
    option (op_type) = {
      op: MUTATOR
    };
  }

  // UserSquash squashes a range of commits into a single commit. If
  // successful, it returns the object ID of the newly created squash commit.
  // On error, it returns a gRPC error. Some specific errors will have an
  // embedded UserSquashError such that clients can deduce what exactly has
  // failed.
  rpc UserSquash(UserSquashRequest) returns (UserSquashResponse) {
    option (op_type) = {
      op: MUTATOR
    };
  }

  // UserApplyPatch applies patches to a given branch.
  rpc UserApplyPatch(stream UserApplyPatchRequest) returns (UserApplyPatchResponse) {
    option (op_type) = {
      op: MUTATOR
    };
  }

  // UserUpdateSubmodule updates a submodule to point to a new commit.
  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 is the repository in which the tag shall be created.
  Repository repository = 1 [(target_repository)=true];
  // tag_name is the name of the tag that shall be created.
  bytes tag_name = 2;
  // user is the user as which the tag shall be created.
  User user = 3;
  // target_revision is the revision which the tag should point to.
  bytes target_revision = 4;
  // message is the message of the tag. If it is empty, a lightweight tag is
  // created. Otherwise, an annotated tag is created.
  bytes message = 5;
  // timestamp is the optional timestamp to use for the created tag tags. If
  // it's not set, the current time will be used. It's only used if an
  // annotated tag is being created.
  google.protobuf.Timestamp timestamp = 7;
}

message UserCreateTagResponse {
  // tag is the newly created tag.
  Tag tag = 1;
  // exists denotes whether the tag has existed already.
  bool exists = 2;
  // pre_receive_error contains an error message if updating the tag failed
  // because of a pre-receive error.
  string pre_receive_error = 3;
}

message UserMergeBranchRequest {
  // The following parameters must only be set in the first message to declare
  // parameters for the merge.

  // repository is the repository to compute the merge for.
  Repository repository = 1 [(target_repository)=true];
  // user is the user to compute the merge as. Its name and mail address are
  // used as author and committer of the merge.
  User user = 2;
  // commit_id is the object ID (hash) of the object that shall be merged into
  // the target branch.
  string commit_id = 3;
  // branch is the branch into which the given commit shall be merged and whose
  // reference is going to be updated.
  bytes branch = 4;
  // message is the message to use for the merge commit.
  bytes message = 5;
  // timestamp is the optional timestamp to use for the merge commit. If it's
  // not set, the current time will be used.
  google.protobuf.Timestamp timestamp = 7;

  // apply must only be set in the second message. Only if this second message
  // is sent and if apply is set to true will the branch be updated to point to
  // the merge commit.
  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;
  // PreReceiveError is never set. Instead, we return a proper gRPC error with
  // UserMergeBranchError details set to an AccessCheckError.
  string pre_receive_error = 4 [deprecated=true];
}

// UserMergeBranchError includes error descriptions which may be set as error
// details in case UserMergeBranch fails.
message UserMergeBranchError {
  oneof error {
    // AccessCheckError is set if the RPC failed because `/internal/allowed` failed.
    AccessCheckError access_check = 1;
    // ReferenceUpdateError is set if the RPC failed because updating the
    // reference to the new object ID has failed.
    ReferenceUpdateError reference_update = 2;
  }
}

message UserMergeToRefRequest {
  // repository is the repository in which the merge shall be computed.
  Repository repository = 1 [(target_repository)=true];
  // user is the user as which the merge commit shall be created.
  User user = 2;
  // source_sha is the object ID of the second parent of the computed merge.
  string source_sha = 3;
  // branch contains the name of the branch which should be used as the first
  // parent of the computed merge. It is deprecated in favor of
  // `first_parent_ref` and will be ignored in case it is set.
  bytes branch = 4;
  // target_ref contains the fully qualified reference which should be updated
  // with the computed merge commit.
  bytes target_ref = 5;
  // message is the message to use for the merge commit.
  bytes message = 6;
  // first_parent_ref is the name of the reference which should be used as the
  // first parent of the computed merge. Overrides `branch`.
  bytes first_parent_ref = 7;
  // Allow conflicts to occur. Any conflict markers will be part of the merge commit.
  // When tree-based conflicts occur, no conflict markers will be added to the
  // file on the merge commit. The `Their` side of the conflict will be kept and
  // `Our` and `Ancestor` will be ignored.
  bool allow_conflicts = 8;
  // timestamp is the optional timestamp to use for the merge commit. If it's
  // not set, the current time will be used.
  google.protobuf.Timestamp timestamp = 9;
}

message UserMergeToRefResponse {
  // commit_id is the object ID of the computed merge commit.
  string commit_id = 1;
  // PreReceiveError is never set because this RPC does not perform
  // authentication via `/internal/allowed`.
  string pre_receive_error = 2 [deprecated=true];
}

// 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 is the repository into which the cherry-pick shall be
  // performed.
  Repository repository = 1 [(target_repository)=true];
  // user is the user to perform the cherry-pick as. This is used for
  // authorization checks and as the committer of the computed cherry-pick.
  User user = 2;
  // commit is the commit to cherry-pick onto the given branch.
  GitCommit commit = 3;
  // branch_name is the name of the branch onto which the cherry-pick shall be
  // executed.
  bytes branch_name = 4;
  // message is the message to use for the cherry-picked commit.
  bytes message = 5;
  // start_branch_name is is used in case the branch_name branch does not
  // exist. In that case, it will be created from the start_branch_name.
  bytes start_branch_name = 6;
  // start_repository is used in case the branch_name branch does not exist. In
  // that case, it will be created from start_branch_name in the
  // start_repository.
  Repository start_repository = 7;
  // dry_run will compute the cherry-pick, but not update the target branch.
  bool dry_run = 8;
  // timestamp is the optional timestamp to use for the created cherry-picked
  // commit's committer date. If it's not set, the current time will be used.
  google.protobuf.Timestamp timestamp = 9;
}

message UserCherryPickResponse {
  // CreateTreeError represents an error which happened when computing the
  // cherry-pick.
  enum CreateTreeError {
    // NONE denotes that no error occurred.
    NONE = 0;
    // EMPTY denotes that the cherry-pick would've resulted in an empty commit,
    // typically because it has already been applied to the target branch.
    EMPTY = 1;
    // CONFLICT denotes that the cherry-pick resulted in a conflict.
    CONFLICT = 2;
  }

  // branch_update represents details about the updated branch.
  OperationBranchUpdate branch_update = 1;
  // create_tree_error contains the error message if creation of the tree
  // failed.
  string create_tree_error = 2;
  // commit_error contains the error message if updating the reference failed.
  string commit_error = 3;
  // pre_receive_error contains the error message if the pre-receive hook
  // failed.
  string pre_receive_error = 4;
  // create_tree_error_code contains the error code if creation of the tree
  // failed.
  CreateTreeError create_tree_error_code = 5;
}

message UserRevertRequest {
  // repository is the repository in which the revert shall be applied.
  Repository repository = 1 [(target_repository)=true];
  // user is the user to perform the revert as. This is used both for
  // authorization and as author/committer for the revert commit.
  User user = 2;
  // commit iis the commit to revert.
  GitCommit commit = 3;
  // branch_name is the name of the branch onto which the reverted commit shall
  // be committed.
  bytes branch_name = 4;
  // message is the message to use for the revert commit.
  bytes message = 5;
  // start_branch_name is is used in case the branch_name branch does not
  // exist. In that case, it will be created from the start_branch_name.
  bytes start_branch_name = 6;
  // start_repository is used in case the branch_name branch does not exist. In
  // that case, it will be created from start_branch_name in the
  // start_repository.
  Repository start_repository = 7;
  // dry_run  will compute the revert, but not update the target branch.
  bool dry_run = 8;
  // timestamp is the optional timestamp to use for the created cherry-picked
  // commit's committer date. If it's not set, the current time will be used.
  google.protobuf.Timestamp timestamp = 9;
}

message UserRevertResponse {
  // CreateTreeError represents an error which happened when computing the
  // revert.
  enum CreateTreeError {
    // NONE denotes that no error occurred.
    NONE = 0;
    // EMPTY denotes that the revert would've resulted in an empty commit,
    // typically because it has already been applied to the target branch.
    EMPTY = 1;
    // CONFLICT denotes that the revert resulted in a conflict.
    CONFLICT = 2;
  }

  // branch_update represents details about the updated branch.
  OperationBranchUpdate branch_update = 1;
  // create_tree_error contains the error message if creation of the tree
  // failed.
  string create_tree_error = 2;
  // commit_error contains the error message if updating the reference failed.
  string commit_error = 3;
  // pre_receive_error contains the error message if the pre-receive hook
  // failed.
  string pre_receive_error = 4;
  // create_tree_error_code contains the error code if creation of the tree
  // failed.
  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;
  // timestamp is the optional timestamp to use for the commits as author and
  // committer date. If it's not set, the current time will be used.
  google.protobuf.Timestamp timestamp = 11;
}

// 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 {
  // Header contains information to compute the rebase and must be sent as
  // first message.
  message Header {
    // repository is the repository in which the rebase will be computed and
    // applied.
    Repository repository = 1 [(target_repository)=true];
    // user is the user to compute the rebase as. It will be used as
    // "committer" of rebased commits.
    User user = 2;
    // RebaseId does nothing anymore.
    string rebase_id = 3 [deprecated=true];
    // branch is the branch onto which the rebase shall happen.
    bytes branch = 4;
    // branch_sha is the expected object ID which branch currently points to.
    // This is used as a safety guard to avoid races when branch has been
    // updated meanwhile.
    string branch_sha = 5;
    // remote_repository is the repository which contains the branch which
    // shall be rebased onto the local branch.
    Repository remote_repository = 6;
    // remote_branch contains the branch name which shall re rebased onto the
    // local branch.
    bytes remote_branch = 7;
    // git_push_options contain options which shall be passed to the git hooks
    // when the local branch gets updated.
    repeated string git_push_options = 8;
    // timestamp is the optional timestamp to use for the rebased commits as
    // committer date. If it's not set, the current time will be used.
    google.protobuf.Timestamp timestamp = 9;
  }

  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;
  }
  // pre_receive_error contains an error message if the rebase failed because
  // of an error raised by hooks.
  string pre_receive_error = 3;
  // git_error contains an error message if git operations have failed.
  string git_error = 4;
}

message UserSquashRequest {
  // repository is the repository into which the squashed commit shall be
  // written.
  Repository repository = 1 [(target_repository)=true];
  // user is used for authorization checks.
  User user = 2;
  // start_sha is the object ID of the start commit of the range which shall be
  // squashed. Must be an ancestor of end_sha.
  string start_sha = 5;
  // end_sha is the object ID of the end commit of the range which shall be
  // squashed.
  string end_sha = 6;
  // author will be used as the author of the squashed commit.
  User author = 7;
  // commit_message is the message to be used for the squashed commit.
  bytes commit_message = 8;
  // timestamp is the optional timestamp to use for the squashed commit as
  // committer date. If it's not set, the current time will be used.
  google.protobuf.Timestamp timestamp = 9;

  reserved 3, 4;
  reserved "squash_id";
}

message UserSquashResponse {
  // squash_sha is the object ID of the squashed commit.
  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 UserRebaseConfirmableError {
  oneof error {
    // RebaseConflict is returned in case rebasing commits on top of the start
    // commit fails with a merge conflict.
    MergeConflictError rebase_conflict = 1;
    // AccessCheckError is returned in case GitLab's `/internal/allowed` endpoint rejected
    // the change.
    AccessCheckError access_check = 2;
  }
}

// UserSquashError is an error that may be returned when the UserSquash RPC
// fails.
message UserSquashError {
  oneof error {
    // ResolveRevision is returned in case resolving either the start or end
    // revision has failed.
    ResolveRevisionError resolve_revision = 1;
    // RebaseConflict is returned in case rebasing commits on top of the start
    // commit fails with a merge conflict.
    MergeConflictError rebase_conflict = 2;
  }
}

message UserApplyPatchRequest {
  // Header contains information about how to apply the patches.
  message Header {
    // repository is the repository to which the patches shall be applied to.
    Repository repository = 1 [(target_repository)=true];
    // user is used for authentication.
    User user = 2;
    // target_branch is the branch onto which the patches shall be applied.
    bytes target_branch = 3;
    // timestamp is the optional timestamp to use for the squashed commit as
    // committer date. If it's not set, the current time will be used.
    google.protobuf.Timestamp timestamp = 4;
  }

  oneof user_apply_patch_request_payload {
    // header must be sent as the first message and contains information about
    // how to apply the patches.
    Header header = 1;
    // patches contains the patch data.
    bytes patches = 2;
  }
}

message UserApplyPatchResponse {
  // branch_update contains information about the updated branch.
  OperationBranchUpdate branch_update = 1;
}

message UserUpdateSubmoduleRequest {
  // repository is the repository in which the submodule shall be updated.
  Repository repository = 1 [(target_repository)=true];
  // user is used both for authorization and as author/committer of the
  // resulting commit.
  User user = 2;
  // commit_sha is the object ID the submodule shall be updated to.
  string commit_sha = 3;
  // branch is the branch which shall be updated. This is the unqualified name
  // of the branch, it must not have a "refs/heads/" prefix.
  bytes branch = 4;
  // submodule is the path to the submodule which shall be updated.
  bytes submodule = 5;
  // commit_message is the message updating the submodule.
  bytes commit_message = 6;
  // timestamp is the optional timestamp to use for the commit updating the
  // submodule as committer date. If it's not set, the current time will be
  // used.
  google.protobuf.Timestamp timestamp = 7;
}

message UserUpdateSubmoduleResponse {
  // branch_update contains information about the updated branch.
  OperationBranchUpdate branch_update = 1;
  // pre_receive_error contains an error message if the pre-receive hook
  // rejects the update.
  string pre_receive_error = 2;
  // DEPRECATED: https://gitlab.com/gitlab-org/gitaly/proto/merge_requests/237
  reserved 3;
  reserved "create_tree_error";
  // commit_error contains an error message if committing the update fails.
  string commit_error = 4;
}
