115 lines
3.7 KiB
Ruby
115 lines
3.7 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module MergeRequests
|
|
class AddContextService < MergeRequests::BaseService
|
|
def execute
|
|
return error("You are not allowed to access the requested resource", 403) unless current_user&.can?(:update_merge_request, merge_request)
|
|
return error("Context commits: #{duplicates} are already created", 400) unless duplicates.empty?
|
|
return error("One or more context commits' sha is not valid.", 400) if commits.size != commit_ids.size
|
|
|
|
context_commit_ids = []
|
|
MergeRequestContextCommit.transaction do
|
|
context_commit_ids = MergeRequestContextCommit.bulk_insert(context_commit_rows, return_ids: true)
|
|
MergeRequestContextCommitDiffFile.bulk_insert(diff_rows(context_commit_ids))
|
|
end
|
|
|
|
commits
|
|
end
|
|
|
|
private
|
|
|
|
def raw_repository
|
|
project.repository.raw_repository
|
|
end
|
|
|
|
def merge_request
|
|
params[:merge_request]
|
|
end
|
|
|
|
def commit_ids
|
|
params[:commits]
|
|
end
|
|
|
|
def commits
|
|
project.repository.commits_by(oids: commit_ids)
|
|
end
|
|
|
|
def context_commit_rows
|
|
@context_commit_rows ||= build_context_commit_rows(merge_request.id, commits)
|
|
end
|
|
|
|
def diff_rows(context_commit_ids)
|
|
@diff_rows ||= build_diff_rows(raw_repository, commits, context_commit_ids)
|
|
end
|
|
|
|
def encode_in_base64?(diff_text)
|
|
(diff_text.encoding == Encoding::BINARY && !diff_text.ascii_only?) ||
|
|
diff_text.include?("\0")
|
|
end
|
|
|
|
def duplicates
|
|
existing_oids = merge_request.merge_request_context_commits.map { |commit| commit.sha.to_s }
|
|
existing_oids.select do |existing_oid|
|
|
commit_ids.count { |commit_id| existing_oid.start_with?(commit_id) } > 0
|
|
end
|
|
end
|
|
|
|
def build_context_commit_rows(merge_request_id, commits)
|
|
commits.map.with_index do |commit, index|
|
|
# generate context commit information for given commit
|
|
commit_hash = commit.to_hash.except(:parent_ids)
|
|
sha = Gitlab::Database::ShaAttribute.serialize(commit_hash.delete(:id))
|
|
commit_hash.merge(
|
|
merge_request_id: merge_request_id,
|
|
relative_order: index,
|
|
sha: sha,
|
|
authored_date: Gitlab::Database.sanitize_timestamp(commit_hash[:authored_date]),
|
|
committed_date: Gitlab::Database.sanitize_timestamp(commit_hash[:committed_date]),
|
|
trailers: Gitlab::Json.dump(commit_hash.fetch(:trailers, {}))
|
|
)
|
|
end
|
|
end
|
|
|
|
def build_diff_rows(raw_repository, commits, context_commit_ids)
|
|
diff_rows = []
|
|
diff_order = 0
|
|
|
|
commits.flat_map.with_index do |commit, index|
|
|
commit_hash = commit.to_hash.except(:parent_ids)
|
|
sha = Gitlab::Database::ShaAttribute.serialize(commit_hash.delete(:id))
|
|
# generate context commit diff information for given commit
|
|
diffs = commit.diffs
|
|
|
|
compare = Gitlab::Git::Compare.new(
|
|
raw_repository,
|
|
diffs.diff_refs.start_sha,
|
|
diffs.diff_refs.head_sha
|
|
)
|
|
compare.diffs.map do |diff|
|
|
diff_hash = diff.to_hash.merge(
|
|
sha: sha,
|
|
binary: false,
|
|
merge_request_context_commit_id: context_commit_ids[index],
|
|
relative_order: diff_order
|
|
)
|
|
|
|
# Compatibility with old diffs created with Psych.
|
|
diff_hash.tap do |hash|
|
|
diff_text = hash[:diff]
|
|
|
|
if encode_in_base64?(diff_text)
|
|
hash[:binary] = true
|
|
hash[:diff] = [diff_text].pack('m0')
|
|
end
|
|
end
|
|
|
|
# Increase order for commit so when present the diffs we can use it to keep order
|
|
diff_order += 1
|
|
diff_rows << diff_hash
|
|
end
|
|
end
|
|
|
|
diff_rows
|
|
end
|
|
end
|
|
end
|