2019-02-15 15:39:39 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
module Gitlab
|
|
|
|
module GitalyClient
|
|
|
|
class OperationService
|
|
|
|
include Gitlab::EncodingHelper
|
|
|
|
|
|
|
|
MAX_MSG_SIZE = 128.kilobytes.freeze
|
|
|
|
|
|
|
|
def initialize(repository)
|
|
|
|
@gitaly_repo = repository.gitaly_repository
|
|
|
|
@repository = repository
|
|
|
|
end
|
|
|
|
|
|
|
|
def rm_tag(tag_name, user)
|
|
|
|
request = Gitaly::UserDeleteTagRequest.new(
|
|
|
|
repository: @gitaly_repo,
|
|
|
|
tag_name: encode_binary(tag_name),
|
|
|
|
user: Gitlab::Git::User.from_gitlab(user).to_gitaly
|
|
|
|
)
|
|
|
|
|
2019-12-21 20:55:43 +05:30
|
|
|
response = GitalyClient.call(@repository.storage, :operation_service, :user_delete_tag, request, timeout: GitalyClient.long_timeout)
|
2018-03-17 18:26:18 +05:30
|
|
|
|
|
|
|
if pre_receive_error = response.pre_receive_error.presence
|
2018-11-08 19:23:39 +05:30
|
|
|
raise Gitlab::Git::PreReceiveError, pre_receive_error
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def add_tag(tag_name, user, target, message)
|
|
|
|
request = Gitaly::UserCreateTagRequest.new(
|
|
|
|
repository: @gitaly_repo,
|
|
|
|
user: Gitlab::Git::User.from_gitlab(user).to_gitaly,
|
|
|
|
tag_name: encode_binary(tag_name),
|
|
|
|
target_revision: encode_binary(target),
|
2021-03-11 19:13:27 +05:30
|
|
|
message: encode_binary(message.to_s),
|
|
|
|
timestamp: Google::Protobuf::Timestamp.new(seconds: Time.now.utc.to_i)
|
2018-03-17 18:26:18 +05:30
|
|
|
)
|
|
|
|
|
2019-12-21 20:55:43 +05:30
|
|
|
response = GitalyClient.call(@repository.storage, :operation_service, :user_create_tag, request, timeout: GitalyClient.long_timeout)
|
2018-03-17 18:26:18 +05:30
|
|
|
if pre_receive_error = response.pre_receive_error.presence
|
2018-11-08 19:23:39 +05:30
|
|
|
raise Gitlab::Git::PreReceiveError, pre_receive_error
|
2018-03-17 18:26:18 +05:30
|
|
|
elsif response.exists
|
|
|
|
raise Gitlab::Git::Repository::TagExistsError
|
|
|
|
end
|
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
Gitlab::Git::Tag.new(@repository, response.tag)
|
2022-08-27 11:52:29 +05:30
|
|
|
rescue GRPC::BadStatus => e
|
|
|
|
detailed_error = GitalyClient.decode_detailed_error(e)
|
|
|
|
|
|
|
|
case detailed_error&.error
|
|
|
|
when :access_check
|
|
|
|
access_check_error = detailed_error.access_check
|
|
|
|
# These messages were returned from internal/allowed API calls
|
|
|
|
raise Gitlab::Git::PreReceiveError.new(fallback_message: access_check_error.error_message)
|
|
|
|
when :custom_hook
|
|
|
|
raise Gitlab::Git::PreReceiveError.new(custom_hook_error_message(detailed_error.custom_hook),
|
|
|
|
fallback_message: e.details)
|
|
|
|
when :reference_exists
|
|
|
|
raise Gitlab::Git::Repository::TagExistsError
|
|
|
|
else
|
|
|
|
if e.code == GRPC::Core::StatusCodes::FAILED_PRECONDITION
|
|
|
|
raise Gitlab::Git::Repository::InvalidRef, e
|
|
|
|
end
|
|
|
|
|
|
|
|
raise
|
|
|
|
end
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
def user_create_branch(branch_name, user, start_point)
|
|
|
|
request = Gitaly::UserCreateBranchRequest.new(
|
|
|
|
repository: @gitaly_repo,
|
|
|
|
branch_name: encode_binary(branch_name),
|
|
|
|
user: Gitlab::Git::User.from_gitlab(user).to_gitaly,
|
|
|
|
start_point: encode_binary(start_point)
|
|
|
|
)
|
|
|
|
response = GitalyClient.call(@repository.storage, :operation_service,
|
2019-12-21 20:55:43 +05:30
|
|
|
:user_create_branch, request, timeout: GitalyClient.long_timeout)
|
2018-03-17 18:26:18 +05:30
|
|
|
|
|
|
|
if response.pre_receive_error.present?
|
2021-06-08 01:23:25 +05:30
|
|
|
raise Gitlab::Git::PreReceiveError, response.pre_receive_error
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
branch = response.branch
|
2019-07-07 11:18:12 +05:30
|
|
|
return unless branch
|
2018-03-17 18:26:18 +05:30
|
|
|
|
|
|
|
target_commit = Gitlab::Git::Commit.decorate(@repository, branch.target_commit)
|
|
|
|
Gitlab::Git::Branch.new(@repository, branch.name, target_commit.id, target_commit)
|
2022-10-11 01:57:18 +05:30
|
|
|
rescue GRPC::BadStatus => e
|
|
|
|
detailed_error = GitalyClient.decode_detailed_error(e)
|
|
|
|
|
|
|
|
case detailed_error&.error
|
|
|
|
when :custom_hook
|
|
|
|
raise Gitlab::Git::PreReceiveError.new(custom_hook_error_message(detailed_error.custom_hook),
|
|
|
|
fallback_message: e.details)
|
|
|
|
else
|
|
|
|
if e.code == GRPC::Core::StatusCodes::FAILED_PRECONDITION
|
|
|
|
raise Gitlab::Git::Repository::InvalidRef, e
|
|
|
|
end
|
|
|
|
|
|
|
|
raise
|
|
|
|
end
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
|
|
|
|
2018-11-18 11:00:15 +05:30
|
|
|
def user_update_branch(branch_name, user, newrev, oldrev)
|
|
|
|
request = Gitaly::UserUpdateBranchRequest.new(
|
|
|
|
repository: @gitaly_repo,
|
|
|
|
branch_name: encode_binary(branch_name),
|
|
|
|
user: Gitlab::Git::User.from_gitlab(user).to_gitaly,
|
|
|
|
newrev: encode_binary(newrev),
|
|
|
|
oldrev: encode_binary(oldrev)
|
|
|
|
)
|
|
|
|
|
2019-12-21 20:55:43 +05:30
|
|
|
response = GitalyClient.call(@repository.storage, :operation_service,
|
|
|
|
:user_update_branch, request, timeout: GitalyClient.long_timeout)
|
2018-11-18 11:00:15 +05:30
|
|
|
|
|
|
|
if pre_receive_error = response.pre_receive_error.presence
|
|
|
|
raise Gitlab::Git::PreReceiveError, pre_receive_error
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
def user_delete_branch(branch_name, user)
|
|
|
|
request = Gitaly::UserDeleteBranchRequest.new(
|
|
|
|
repository: @gitaly_repo,
|
|
|
|
branch_name: encode_binary(branch_name),
|
|
|
|
user: Gitlab::Git::User.from_gitlab(user).to_gitaly
|
|
|
|
)
|
|
|
|
|
2019-12-21 20:55:43 +05:30
|
|
|
response = GitalyClient.call(@repository.storage, :operation_service,
|
|
|
|
:user_delete_branch, request, timeout: GitalyClient.long_timeout)
|
2018-03-17 18:26:18 +05:30
|
|
|
|
|
|
|
if pre_receive_error = response.pre_receive_error.presence
|
2018-11-08 19:23:39 +05:30
|
|
|
raise Gitlab::Git::PreReceiveError, pre_receive_error
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
2022-07-23 23:45:48 +05:30
|
|
|
rescue GRPC::BadStatus => e
|
2022-08-13 15:12:31 +05:30
|
|
|
detailed_error = GitalyClient.decode_detailed_error(e)
|
2022-07-23 23:45:48 +05:30
|
|
|
|
|
|
|
case detailed_error&.error
|
|
|
|
when :custom_hook
|
|
|
|
raise Gitlab::Git::PreReceiveError.new(custom_hook_error_message(detailed_error.custom_hook),
|
|
|
|
fallback_message: e.details)
|
|
|
|
else
|
|
|
|
raise
|
|
|
|
end
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
|
|
|
|
2021-04-29 21:17:54 +05:30
|
|
|
def user_merge_to_ref(user, source_sha:, branch:, target_ref:, message:, first_parent_ref:, allow_conflicts: false)
|
2019-07-07 11:18:12 +05:30
|
|
|
request = Gitaly::UserMergeToRefRequest.new(
|
|
|
|
repository: @gitaly_repo,
|
|
|
|
source_sha: source_sha,
|
|
|
|
branch: encode_binary(branch),
|
|
|
|
target_ref: encode_binary(target_ref),
|
|
|
|
user: Gitlab::Git::User.from_gitlab(user).to_gitaly,
|
2019-09-30 21:07:59 +05:30
|
|
|
message: encode_binary(message),
|
2021-01-03 14:25:43 +05:30
|
|
|
first_parent_ref: encode_binary(first_parent_ref),
|
2021-03-11 19:13:27 +05:30
|
|
|
allow_conflicts: allow_conflicts,
|
|
|
|
timestamp: Google::Protobuf::Timestamp.new(seconds: Time.now.utc.to_i)
|
2019-07-07 11:18:12 +05:30
|
|
|
)
|
|
|
|
|
2019-12-21 20:55:43 +05:30
|
|
|
response = GitalyClient.call(@repository.storage, :operation_service,
|
|
|
|
:user_merge_to_ref, request, timeout: GitalyClient.long_timeout)
|
2019-07-07 11:18:12 +05:30
|
|
|
|
|
|
|
response.commit_id
|
|
|
|
end
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
def user_merge_branch(user, source_sha, target_branch, message)
|
|
|
|
request_enum = QueueEnumerator.new
|
|
|
|
response_enum = GitalyClient.call(
|
|
|
|
@repository.storage,
|
|
|
|
:operation_service,
|
|
|
|
:user_merge_branch,
|
2019-12-21 20:55:43 +05:30
|
|
|
request_enum.each,
|
|
|
|
timeout: GitalyClient.long_timeout
|
2018-03-17 18:26:18 +05:30
|
|
|
)
|
|
|
|
|
|
|
|
request_enum.push(
|
|
|
|
Gitaly::UserMergeBranchRequest.new(
|
|
|
|
repository: @gitaly_repo,
|
|
|
|
user: Gitlab::Git::User.from_gitlab(user).to_gitaly,
|
|
|
|
commit_id: source_sha,
|
|
|
|
branch: encode_binary(target_branch),
|
2021-03-11 19:13:27 +05:30
|
|
|
message: encode_binary(message),
|
|
|
|
timestamp: Google::Protobuf::Timestamp.new(seconds: Time.now.utc.to_i)
|
2018-03-17 18:26:18 +05:30
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
yield response_enum.next.commit_id
|
|
|
|
|
|
|
|
request_enum.push(Gitaly::UserMergeBranchRequest.new(apply: true))
|
|
|
|
|
|
|
|
second_response = response_enum.next
|
|
|
|
|
|
|
|
branch_update = second_response.branch_update
|
|
|
|
return if branch_update.nil?
|
2021-06-08 01:23:25 +05:30
|
|
|
raise Gitlab::Git::CommitError, 'failed to apply merge to branch' unless branch_update.commit_id.present?
|
2018-03-17 18:26:18 +05:30
|
|
|
|
|
|
|
Gitlab::Git::OperationService::BranchUpdate.from_gitaly(branch_update)
|
2021-11-18 22:05:49 +05:30
|
|
|
|
|
|
|
rescue GRPC::BadStatus => e
|
2022-08-13 15:12:31 +05:30
|
|
|
detailed_error = GitalyClient.decode_detailed_error(e)
|
2021-11-18 22:05:49 +05:30
|
|
|
|
2022-05-07 20:08:51 +05:30
|
|
|
case detailed_error&.error
|
|
|
|
when :access_check
|
|
|
|
access_check_error = detailed_error.access_check
|
|
|
|
# These messages were returned from internal/allowed API calls
|
|
|
|
raise Gitlab::Git::PreReceiveError.new(fallback_message: access_check_error.error_message)
|
2022-07-23 23:45:48 +05:30
|
|
|
when :custom_hook
|
|
|
|
raise Gitlab::Git::PreReceiveError.new(custom_hook_error_message(detailed_error.custom_hook),
|
|
|
|
fallback_message: e.details)
|
2022-05-07 20:08:51 +05:30
|
|
|
when :reference_update
|
|
|
|
# We simply ignore any reference update errors which are typically an
|
|
|
|
# indicator of multiple RPC calls trying to update the same reference
|
|
|
|
# at the same point in time.
|
|
|
|
else
|
|
|
|
raise
|
|
|
|
end
|
2018-03-17 18:26:18 +05:30
|
|
|
ensure
|
|
|
|
request_enum.close
|
|
|
|
end
|
|
|
|
|
|
|
|
def user_ff_branch(user, source_sha, target_branch)
|
|
|
|
request = Gitaly::UserFFBranchRequest.new(
|
|
|
|
repository: @gitaly_repo,
|
|
|
|
user: Gitlab::Git::User.from_gitlab(user).to_gitaly,
|
|
|
|
commit_id: source_sha,
|
|
|
|
branch: encode_binary(target_branch)
|
|
|
|
)
|
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
response = GitalyClient.call(
|
2018-03-17 18:26:18 +05:30
|
|
|
@repository.storage,
|
|
|
|
:operation_service,
|
|
|
|
:user_ff_branch,
|
2019-12-21 20:55:43 +05:30
|
|
|
request,
|
|
|
|
timeout: GitalyClient.long_timeout
|
2018-11-08 19:23:39 +05:30
|
|
|
)
|
|
|
|
|
2020-07-28 23:09:34 +05:30
|
|
|
if response.pre_receive_error.present?
|
2020-11-24 15:15:51 +05:30
|
|
|
raise Gitlab::Git::PreReceiveError.new(response.pre_receive_error, fallback_message: "pre-receive hook failed.")
|
2020-07-28 23:09:34 +05:30
|
|
|
end
|
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
Gitlab::Git::OperationService::BranchUpdate.from_gitaly(response.branch_update)
|
|
|
|
rescue GRPC::FailedPrecondition => e
|
|
|
|
raise Gitlab::Git::CommitError, e
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
|
|
|
|
2020-10-24 23:57:45 +05:30
|
|
|
def user_cherry_pick(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:, dry_run: false)
|
2018-03-17 18:26:18 +05:30
|
|
|
call_cherry_pick_or_revert(:cherry_pick,
|
|
|
|
user: user,
|
|
|
|
commit: commit,
|
|
|
|
branch_name: branch_name,
|
|
|
|
message: message,
|
|
|
|
start_branch_name: start_branch_name,
|
2020-10-24 23:57:45 +05:30
|
|
|
start_repository: start_repository,
|
|
|
|
dry_run: dry_run)
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
|
|
|
|
2020-10-24 23:57:45 +05:30
|
|
|
def user_revert(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:, dry_run: false)
|
2018-03-17 18:26:18 +05:30
|
|
|
call_cherry_pick_or_revert(:revert,
|
|
|
|
user: user,
|
|
|
|
commit: commit,
|
|
|
|
branch_name: branch_name,
|
|
|
|
message: message,
|
|
|
|
start_branch_name: start_branch_name,
|
2020-10-24 23:57:45 +05:30
|
|
|
start_repository: start_repository,
|
|
|
|
dry_run: dry_run)
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
def rebase(user, rebase_id, branch:, branch_sha:, remote_repository:, remote_branch:, push_options: [])
|
2019-07-31 22:56:46 +05:30
|
|
|
request_enum = QueueEnumerator.new
|
|
|
|
rebase_sha = nil
|
|
|
|
|
|
|
|
response_enum = GitalyClient.call(
|
|
|
|
@repository.storage,
|
|
|
|
:operation_service,
|
|
|
|
:user_rebase_confirmable,
|
|
|
|
request_enum.each,
|
2019-12-21 20:55:43 +05:30
|
|
|
timeout: GitalyClient.long_timeout,
|
2019-07-31 22:56:46 +05:30
|
|
|
remote_storage: remote_repository.storage
|
|
|
|
)
|
|
|
|
|
|
|
|
# First request
|
|
|
|
request_enum.push(
|
|
|
|
Gitaly::UserRebaseConfirmableRequest.new(
|
|
|
|
header: Gitaly::UserRebaseConfirmableRequest::Header.new(
|
|
|
|
repository: @gitaly_repo,
|
|
|
|
user: Gitlab::Git::User.from_gitlab(user).to_gitaly,
|
|
|
|
rebase_id: rebase_id.to_s,
|
|
|
|
branch: encode_binary(branch),
|
|
|
|
branch_sha: branch_sha,
|
|
|
|
remote_repository: remote_repository.gitaly_repository,
|
2020-03-13 15:44:24 +05:30
|
|
|
remote_branch: encode_binary(remote_branch),
|
2021-03-11 19:13:27 +05:30
|
|
|
git_push_options: push_options,
|
|
|
|
timestamp: Google::Protobuf::Timestamp.new(seconds: Time.now.utc.to_i)
|
2019-07-31 22:56:46 +05:30
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
perform_next_gitaly_rebase_request(response_enum) do |response|
|
|
|
|
rebase_sha = response.rebase_sha
|
|
|
|
end
|
|
|
|
|
|
|
|
yield rebase_sha
|
|
|
|
|
|
|
|
# Second request confirms with gitaly to finalize the rebase
|
|
|
|
request_enum.push(Gitaly::UserRebaseConfirmableRequest.new(apply: true))
|
|
|
|
|
|
|
|
perform_next_gitaly_rebase_request(response_enum)
|
|
|
|
|
|
|
|
rebase_sha
|
2022-05-07 20:08:51 +05:30
|
|
|
rescue GRPC::BadStatus => e
|
2022-08-13 15:12:31 +05:30
|
|
|
detailed_error = GitalyClient.decode_detailed_error(e)
|
2022-05-07 20:08:51 +05:30
|
|
|
|
|
|
|
case detailed_error&.error
|
|
|
|
when :access_check
|
|
|
|
access_check_error = detailed_error.access_check
|
|
|
|
# These messages were returned from internal/allowed API calls
|
|
|
|
raise Gitlab::Git::PreReceiveError.new(fallback_message: access_check_error.error_message)
|
|
|
|
when :rebase_conflict
|
|
|
|
raise Gitlab::Git::Repository::GitError, e.details
|
|
|
|
else
|
|
|
|
raise e
|
|
|
|
end
|
2019-07-31 22:56:46 +05:30
|
|
|
ensure
|
|
|
|
request_enum.close
|
|
|
|
end
|
|
|
|
|
2021-11-18 22:05:49 +05:30
|
|
|
def user_squash(user, start_sha, end_sha, author, message, time = Time.now.utc)
|
2018-03-17 18:26:18 +05:30
|
|
|
request = Gitaly::UserSquashRequest.new(
|
|
|
|
repository: @gitaly_repo,
|
|
|
|
user: Gitlab::Git::User.from_gitlab(user).to_gitaly,
|
|
|
|
start_sha: start_sha,
|
|
|
|
end_sha: end_sha,
|
|
|
|
author: Gitlab::Git::User.from_gitlab(author).to_gitaly,
|
2021-03-11 19:13:27 +05:30
|
|
|
commit_message: encode_binary(message),
|
|
|
|
timestamp: Google::Protobuf::Timestamp.new(seconds: time.to_i)
|
2018-03-17 18:26:18 +05:30
|
|
|
)
|
|
|
|
|
|
|
|
response = GitalyClient.call(
|
|
|
|
@repository.storage,
|
|
|
|
:operation_service,
|
|
|
|
:user_squash,
|
2019-12-21 20:55:43 +05:30
|
|
|
request,
|
|
|
|
timeout: GitalyClient.long_timeout
|
2018-03-17 18:26:18 +05:30
|
|
|
)
|
|
|
|
|
|
|
|
response.squash_sha
|
2022-05-07 20:08:51 +05:30
|
|
|
rescue GRPC::BadStatus => e
|
2022-08-13 15:12:31 +05:30
|
|
|
detailed_error = GitalyClient.decode_detailed_error(e)
|
2022-05-07 20:08:51 +05:30
|
|
|
|
|
|
|
case detailed_error&.error
|
|
|
|
when :resolve_revision, :rebase_conflict
|
|
|
|
# Theoretically, we could now raise specific errors based on the type
|
|
|
|
# of the detailed error. Most importantly, we get error details when
|
|
|
|
# Gitaly was not able to resolve the `start_sha` or `end_sha` via a
|
|
|
|
# ResolveRevisionError, and we get information about which files are
|
|
|
|
# conflicting via a MergeConflictError.
|
|
|
|
#
|
|
|
|
# We don't do this now though such that we can maintain backwards
|
|
|
|
# compatibility with the minimum required set of changes during the
|
|
|
|
# transitory period where we're migrating UserSquash to use
|
|
|
|
# structured errors. We thus continue to just return a GitError, like
|
|
|
|
# we previously did.
|
|
|
|
raise Gitlab::Git::Repository::GitError, e.details
|
|
|
|
else
|
|
|
|
raise
|
|
|
|
end
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
|
|
|
|
2018-12-13 13:39:08 +05:30
|
|
|
def user_update_submodule(user:, submodule:, commit_sha:, branch:, message:)
|
|
|
|
request = Gitaly::UserUpdateSubmoduleRequest.new(
|
|
|
|
repository: @gitaly_repo,
|
|
|
|
user: Gitlab::Git::User.from_gitlab(user).to_gitaly,
|
|
|
|
commit_sha: commit_sha,
|
|
|
|
branch: encode_binary(branch),
|
|
|
|
submodule: encode_binary(submodule),
|
2021-03-11 19:13:27 +05:30
|
|
|
commit_message: encode_binary(message),
|
|
|
|
timestamp: Google::Protobuf::Timestamp.new(seconds: Time.now.utc.to_i)
|
2018-12-13 13:39:08 +05:30
|
|
|
)
|
|
|
|
|
|
|
|
response = GitalyClient.call(
|
|
|
|
@repository.storage,
|
|
|
|
:operation_service,
|
|
|
|
:user_update_submodule,
|
2019-12-21 20:55:43 +05:30
|
|
|
request,
|
|
|
|
timeout: GitalyClient.long_timeout
|
2018-12-13 13:39:08 +05:30
|
|
|
)
|
|
|
|
|
|
|
|
if response.pre_receive_error.present?
|
|
|
|
raise Gitlab::Git::PreReceiveError, response.pre_receive_error
|
|
|
|
elsif response.commit_error.present?
|
|
|
|
raise Gitlab::Git::CommitError, response.commit_error
|
|
|
|
else
|
|
|
|
Gitlab::Git::OperationService::BranchUpdate.from_gitaly(response.branch_update)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
# rubocop:disable Metrics/ParameterLists
|
2018-03-17 18:26:18 +05:30
|
|
|
def user_commit_files(
|
|
|
|
user, branch_name, commit_message, actions, author_email, author_name,
|
2019-10-12 21:52:04 +05:30
|
|
|
start_branch_name, start_repository, force = false, start_sha = nil)
|
2018-03-17 18:26:18 +05:30
|
|
|
req_enum = Enumerator.new do |y|
|
|
|
|
header = user_commit_files_request_header(user, branch_name,
|
|
|
|
commit_message, actions, author_email, author_name,
|
2019-10-12 21:52:04 +05:30
|
|
|
start_branch_name, start_repository, force, start_sha)
|
2018-03-17 18:26:18 +05:30
|
|
|
|
|
|
|
y.yield Gitaly::UserCommitFilesRequest.new(header: header)
|
|
|
|
|
|
|
|
actions.each do |action|
|
|
|
|
action_header = user_commit_files_action_header(action)
|
|
|
|
y.yield Gitaly::UserCommitFilesRequest.new(
|
|
|
|
action: Gitaly::UserCommitFilesAction.new(header: action_header)
|
|
|
|
)
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
reader = binary_io(action[:content])
|
2018-03-17 18:26:18 +05:30
|
|
|
|
|
|
|
until reader.eof?
|
|
|
|
chunk = reader.read(MAX_MSG_SIZE)
|
|
|
|
|
|
|
|
y.yield Gitaly::UserCommitFilesRequest.new(
|
|
|
|
action: Gitaly::UserCommitFilesAction.new(content: chunk)
|
|
|
|
)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-10-11 01:57:18 +05:30
|
|
|
response = GitalyClient.call(
|
|
|
|
@repository.storage, :operation_service, :user_commit_files, req_enum,
|
|
|
|
timeout: GitalyClient.long_timeout, remote_storage: start_repository&.storage)
|
2018-03-17 18:26:18 +05:30
|
|
|
|
|
|
|
if (pre_receive_error = response.pre_receive_error.presence)
|
2018-11-08 19:23:39 +05:30
|
|
|
raise Gitlab::Git::PreReceiveError, pre_receive_error
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
if (index_error = response.index_error.presence)
|
|
|
|
raise Gitlab::Git::Index::IndexError, index_error
|
|
|
|
end
|
|
|
|
|
|
|
|
Gitlab::Git::OperationService::BranchUpdate.from_gitaly(response.branch_update)
|
|
|
|
end
|
2019-07-07 11:18:12 +05:30
|
|
|
# rubocop:enable Metrics/ParameterLists
|
2018-03-17 18:26:18 +05:30
|
|
|
|
2018-12-13 13:39:08 +05:30
|
|
|
def user_commit_patches(user, branch_name, patches)
|
|
|
|
header = Gitaly::UserApplyPatchRequest::Header.new(
|
|
|
|
repository: @gitaly_repo,
|
|
|
|
user: Gitlab::Git::User.from_gitlab(user).to_gitaly,
|
2021-03-11 19:13:27 +05:30
|
|
|
target_branch: encode_binary(branch_name),
|
|
|
|
timestamp: Google::Protobuf::Timestamp.new(seconds: Time.now.utc.to_i)
|
2018-12-13 13:39:08 +05:30
|
|
|
)
|
2019-07-07 11:18:12 +05:30
|
|
|
reader = binary_io(patches)
|
2018-12-13 13:39:08 +05:30
|
|
|
|
|
|
|
chunks = Enumerator.new do |chunk|
|
|
|
|
chunk.yield Gitaly::UserApplyPatchRequest.new(header: header)
|
|
|
|
|
|
|
|
until reader.eof?
|
|
|
|
patch_chunk = reader.read(MAX_MSG_SIZE)
|
|
|
|
|
|
|
|
chunk.yield(Gitaly::UserApplyPatchRequest.new(patches: patch_chunk))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-12-21 20:55:43 +05:30
|
|
|
response = GitalyClient.call(@repository.storage, :operation_service,
|
|
|
|
:user_apply_patch, chunks, timeout: GitalyClient.long_timeout)
|
2018-12-13 13:39:08 +05:30
|
|
|
|
|
|
|
Gitlab::Git::OperationService::BranchUpdate.from_gitaly(response.branch_update)
|
|
|
|
end
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
private
|
|
|
|
|
2019-07-31 22:56:46 +05:30
|
|
|
def perform_next_gitaly_rebase_request(response_enum)
|
|
|
|
response = response_enum.next
|
|
|
|
|
|
|
|
if response.pre_receive_error.present?
|
|
|
|
raise Gitlab::Git::PreReceiveError, response.pre_receive_error
|
|
|
|
elsif response.git_error.present?
|
|
|
|
raise Gitlab::Git::Repository::GitError, response.git_error
|
|
|
|
end
|
|
|
|
|
|
|
|
yield response if block_given?
|
|
|
|
|
|
|
|
response
|
|
|
|
end
|
|
|
|
|
2020-10-24 23:57:45 +05:30
|
|
|
def call_cherry_pick_or_revert(rpc, user:, commit:, branch_name:, message:, start_branch_name:, start_repository:, dry_run:)
|
2018-03-17 18:26:18 +05:30
|
|
|
request_class = "Gitaly::User#{rpc.to_s.camelcase}Request".constantize
|
|
|
|
|
|
|
|
request = request_class.new(
|
|
|
|
repository: @gitaly_repo,
|
|
|
|
user: Gitlab::Git::User.from_gitlab(user).to_gitaly,
|
|
|
|
commit: commit.to_gitaly_commit,
|
|
|
|
branch_name: encode_binary(branch_name),
|
|
|
|
message: encode_binary(message),
|
|
|
|
start_branch_name: encode_binary(start_branch_name.to_s),
|
2020-10-24 23:57:45 +05:30
|
|
|
start_repository: start_repository.gitaly_repository,
|
|
|
|
dry_run: dry_run
|
2018-03-17 18:26:18 +05:30
|
|
|
)
|
|
|
|
|
|
|
|
response = GitalyClient.call(
|
|
|
|
@repository.storage,
|
|
|
|
:operation_service,
|
|
|
|
:"user_#{rpc}",
|
|
|
|
request,
|
2018-11-08 19:23:39 +05:30
|
|
|
remote_storage: start_repository.storage,
|
2019-12-21 20:55:43 +05:30
|
|
|
timeout: GitalyClient.long_timeout
|
2018-03-17 18:26:18 +05:30
|
|
|
)
|
|
|
|
|
|
|
|
handle_cherry_pick_or_revert_response(response)
|
2022-07-23 23:45:48 +05:30
|
|
|
rescue GRPC::BadStatus => e
|
2022-08-13 15:12:31 +05:30
|
|
|
detailed_error = GitalyClient.decode_detailed_error(e)
|
2022-07-23 23:45:48 +05:30
|
|
|
|
|
|
|
case detailed_error&.error
|
|
|
|
when :access_check
|
|
|
|
access_check_error = detailed_error.access_check
|
|
|
|
# These messages were returned from internal/allowed API calls
|
|
|
|
raise Gitlab::Git::PreReceiveError.new(fallback_message: access_check_error.error_message)
|
|
|
|
when :cherry_pick_conflict
|
|
|
|
raise Gitlab::Git::Repository::CreateTreeError, 'CONFLICT'
|
2022-08-13 15:12:31 +05:30
|
|
|
when :changes_already_applied
|
|
|
|
raise Gitlab::Git::Repository::CreateTreeError, 'EMPTY'
|
2022-07-23 23:45:48 +05:30
|
|
|
when :target_branch_diverged
|
|
|
|
raise Gitlab::Git::CommitError, 'branch diverged'
|
|
|
|
else
|
|
|
|
raise e
|
|
|
|
end
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
def handle_cherry_pick_or_revert_response(response)
|
|
|
|
if response.pre_receive_error.presence
|
2018-11-08 19:23:39 +05:30
|
|
|
raise Gitlab::Git::PreReceiveError, response.pre_receive_error
|
2018-03-17 18:26:18 +05:30
|
|
|
elsif response.commit_error.presence
|
|
|
|
raise Gitlab::Git::CommitError, response.commit_error
|
|
|
|
elsif response.create_tree_error.presence
|
2019-12-26 22:10:19 +05:30
|
|
|
raise Gitlab::Git::Repository::CreateTreeError, response.create_tree_error_code
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
2018-11-08 19:23:39 +05:30
|
|
|
|
|
|
|
Gitlab::Git::OperationService::BranchUpdate.from_gitaly(response.branch_update)
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
# rubocop:disable Metrics/ParameterLists
|
2018-03-17 18:26:18 +05:30
|
|
|
def user_commit_files_request_header(
|
|
|
|
user, branch_name, commit_message, actions, author_email, author_name,
|
2019-10-12 21:52:04 +05:30
|
|
|
start_branch_name, start_repository, force, start_sha)
|
2018-03-17 18:26:18 +05:30
|
|
|
|
|
|
|
Gitaly::UserCommitFilesRequestHeader.new(
|
|
|
|
repository: @gitaly_repo,
|
|
|
|
user: Gitlab::Git::User.from_gitlab(user).to_gitaly,
|
|
|
|
branch_name: encode_binary(branch_name),
|
|
|
|
commit_message: encode_binary(commit_message),
|
|
|
|
commit_author_name: encode_binary(author_name),
|
|
|
|
commit_author_email: encode_binary(author_email),
|
|
|
|
start_branch_name: encode_binary(start_branch_name),
|
2022-08-27 11:52:29 +05:30
|
|
|
start_repository: start_repository&.gitaly_repository,
|
2019-10-12 21:52:04 +05:30
|
|
|
force: force,
|
2021-03-11 19:13:27 +05:30
|
|
|
start_sha: encode_binary(start_sha),
|
|
|
|
timestamp: Google::Protobuf::Timestamp.new(seconds: Time.now.utc.to_i)
|
2018-03-17 18:26:18 +05:30
|
|
|
)
|
|
|
|
end
|
2019-07-07 11:18:12 +05:30
|
|
|
# rubocop:enable Metrics/ParameterLists
|
2018-03-17 18:26:18 +05:30
|
|
|
|
|
|
|
def user_commit_files_action_header(action)
|
|
|
|
Gitaly::UserCommitFilesActionHeader.new(
|
|
|
|
action: action[:action].upcase.to_sym,
|
|
|
|
file_path: encode_binary(action[:file_path]),
|
|
|
|
previous_path: encode_binary(action[:previous_path]),
|
2018-12-05 23:21:45 +05:30
|
|
|
base64_content: action[:encoding] == 'base64',
|
2019-02-15 15:39:39 +05:30
|
|
|
execute_filemode: !!action[:execute_filemode],
|
|
|
|
infer_content: !!action[:infer_content]
|
2018-03-17 18:26:18 +05:30
|
|
|
)
|
|
|
|
rescue RangeError
|
|
|
|
raise ArgumentError, "Unknown action '#{action[:action]}'"
|
|
|
|
end
|
2021-11-18 22:05:49 +05:30
|
|
|
|
2022-07-23 23:45:48 +05:30
|
|
|
def custom_hook_error_message(custom_hook_error)
|
|
|
|
# Custom hooks may return messages via either stdout or stderr which have a specific prefix. If
|
|
|
|
# that prefix is present we'll want to print the hook's output, otherwise we'll want to print the
|
|
|
|
# Gitaly error as a fallback.
|
|
|
|
custom_hook_output = custom_hook_error.stderr.presence || custom_hook_error.stdout
|
|
|
|
EncodingHelper.encode!(custom_hook_output)
|
|
|
|
end
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|