Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/services/merge_requests/add_context_service.rb')
-rw-r--r--app/services/merge_requests/add_context_service.rb116
1 files changed, 116 insertions, 0 deletions
diff --git a/app/services/merge_requests/add_context_service.rb b/app/services/merge_requests/add_context_service.rb
new file mode 100644
index 00000000000..bb82fa23468
--- /dev/null
+++ b/app/services/merge_requests/add_context_service.rb
@@ -0,0 +1,116 @@
+# 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 }
+ duplicate_oids = existing_oids.select do |existing_oid|
+ commit_ids.select { |commit_id| existing_oid.start_with?(commit_id) }.count > 0
+ end
+
+ duplicate_oids
+ 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])
+ )
+ 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