diff options
Diffstat (limited to 'app/services/merge_requests/conflicts')
3 files changed, 100 insertions, 0 deletions
diff --git a/app/services/merge_requests/conflicts/base_service.rb b/app/services/merge_requests/conflicts/base_service.rb new file mode 100644 index 00000000000..b50875347d9 --- /dev/null +++ b/app/services/merge_requests/conflicts/base_service.rb @@ -0,0 +1,11 @@ +module MergeRequests + module Conflicts + class BaseService + attr_reader :merge_request + + def initialize(merge_request) + @merge_request = merge_request + end + end + end +end diff --git a/app/services/merge_requests/conflicts/list_service.rb b/app/services/merge_requests/conflicts/list_service.rb new file mode 100644 index 00000000000..9835606812c --- /dev/null +++ b/app/services/merge_requests/conflicts/list_service.rb @@ -0,0 +1,36 @@ +module MergeRequests + module Conflicts + class ListService < MergeRequests::Conflicts::BaseService + delegate :file_for_path, :to_json, to: :conflicts + + def can_be_resolved_by?(user) + return false unless merge_request.source_project + + access = ::Gitlab::UserAccess.new(user, project: merge_request.source_project) + access.can_push_to_branch?(merge_request.source_branch) + end + + def can_be_resolved_in_ui? + return @conflicts_can_be_resolved_in_ui if defined?(@conflicts_can_be_resolved_in_ui) + + return @conflicts_can_be_resolved_in_ui = false unless merge_request.cannot_be_merged? + return @conflicts_can_be_resolved_in_ui = false unless merge_request.has_complete_diff_refs? + return @conflicts_can_be_resolved_in_ui = false if merge_request.branch_missing? + + begin + # Try to parse each conflict. If the MR's mergeable status hasn't been + # updated, ensure that we don't say there are conflicts to resolve + # when there are no conflict files. + conflicts.files.each(&:lines) + @conflicts_can_be_resolved_in_ui = conflicts.files.length > 0 + rescue Rugged::OdbError, Gitlab::Conflict::Parser::UnresolvableError, Gitlab::Conflict::FileCollection::ConflictSideMissing + @conflicts_can_be_resolved_in_ui = false + end + end + + def conflicts + @conflicts ||= Gitlab::Conflict::FileCollection.read_only(merge_request) + end + end + end +end diff --git a/app/services/merge_requests/conflicts/resolve_service.rb b/app/services/merge_requests/conflicts/resolve_service.rb new file mode 100644 index 00000000000..d74a82effd6 --- /dev/null +++ b/app/services/merge_requests/conflicts/resolve_service.rb @@ -0,0 +1,53 @@ +module MergeRequests + module Conflicts + class ResolveService < MergeRequests::Conflicts::BaseService + MissingFiles = Class.new(Gitlab::Conflict::ResolutionError) + + def execute(current_user, params) + rugged = merge_request.source_project.repository.rugged + + Gitlab::Conflict::FileCollection.for_resolution(merge_request) do |conflicts_for_resolution| + merge_index = conflicts_for_resolution.merge_index + + params[:files].each do |file_params| + conflict_file = conflicts_for_resolution.file_for_path(file_params[:old_path], file_params[:new_path]) + + write_resolved_file_to_index(merge_index, rugged, conflict_file, file_params) + end + + unless merge_index.conflicts.empty? + missing_files = merge_index.conflicts.map { |file| file[:ours][:path] } + + raise MissingFiles, "Missing resolutions for the following files: #{missing_files.join(', ')}" + end + + commit_params = { + message: params[:commit_message] || conflicts_for_resolution.default_commit_message, + parents: [conflicts_for_resolution.our_commit, conflicts_for_resolution.their_commit].map(&:oid), + tree: merge_index.write_tree(rugged) + } + + conflicts_for_resolution. + project. + repository. + resolve_conflicts(current_user, merge_request.source_branch, commit_params) + end + end + + private + + def write_resolved_file_to_index(merge_index, rugged, file, params) + new_file = if params[:sections] + file.resolve_lines(params[:sections]).map(&:text).join("\n") + elsif params[:content] + file.resolve_content(params[:content]) + end + + our_path = file.our_path + + merge_index.add(path: our_path, oid: rugged.write(new_file, :blob), mode: file.our_mode) + merge_index.conflict_remove(our_path) + end + end + end +end |