Encapsulate git operations for conflict resolution into lib

This commit is contained in:
Alejandro Rodríguez 2017-10-05 20:22:24 -03:00
parent e49e443b70
commit 9fda629a34
3 changed files with 52 additions and 60 deletions

View file

@ -1,54 +1,10 @@
module MergeRequests module MergeRequests
module Conflicts module Conflicts
class ResolveService < MergeRequests::Conflicts::BaseService class ResolveService < MergeRequests::Conflicts::BaseService
MissingFiles = Class.new(Gitlab::Conflict::ResolutionError)
def execute(current_user, params) def execute(current_user, params)
rugged = merge_request.source_project.repository.rugged conflicts = Gitlab::Conflict::FileCollection.for_resolution(merge_request)
Gitlab::Conflict::FileCollection.for_resolution(merge_request) do |conflicts_for_resolution| conflicts.resolve(current_user, params[:commit_message], params[:files])
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)
if params[:sections]
new_file = file.resolve_lines(params[:sections]).map(&:text).join("\n")
new_file << "\n" if file.our_blob.data.ends_with?("\n")
elsif params[:content]
new_file = 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
end end

View file

@ -2,8 +2,9 @@ module Gitlab
module Conflict module Conflict
class FileCollection class FileCollection
ConflictSideMissing = Class.new(StandardError) ConflictSideMissing = Class.new(StandardError)
MissingFiles = Class.new(ResolutionError)
attr_reader :merge_request, :our_commit, :their_commit, :project attr_reader :merge_request, :our_commit, :their_commit, :project, :read_only
delegate :repository, to: :project delegate :repository, to: :project
@ -13,22 +14,41 @@ module Gitlab
# the time because this fetches a ref into the source project, which # the time because this fetches a ref into the source project, which
# isn't needed for reading. # isn't needed for reading.
def for_resolution(merge_request) def for_resolution(merge_request)
project = merge_request.source_project new(merge_request, merge_request.source_project, false)
new(merge_request, project).tap do |file_collection|
project
.repository
.with_repo_branch_commit(merge_request.target_project.repository.raw_repository, merge_request.target_branch) do
yield file_collection
end
end
end end
# We don't need to do `with_repo_branch_commit` here, because the target # We don't need to do `with_repo_branch_commit` here, because the target
# project always fetches source refs when creating merge request diffs. # project always fetches source refs when creating merge request diffs.
def read_only(merge_request) def read_only(merge_request)
new(merge_request, merge_request.target_project) new(merge_request, merge_request.target_project, true)
end
end
def resolve(user, commit_message, files)
raise "can't resolve a read-only Conflict File Collection" if read_only
repository.with_repo_branch_commit(merge_request.target_project.repository.raw, merge_request.target_branch) do
rugged = repository.rugged
files.each do |file_params|
conflict_file = 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: commit_message || default_commit_message,
parents: [our_commit, their_commit].map(&:oid),
tree: merge_index.write_tree(rugged)
}
repository.resolve_conflicts(user, merge_request.source_branch, commit_params)
end end
end end
@ -75,11 +95,27 @@ EOM
private private
def initialize(merge_request, project) def write_resolved_file_to_index(merge_index, rugged, file, params)
if params[:sections]
new_file = file.resolve_lines(params[:sections]).map(&:text).join("\n")
new_file << "\n" if file.our_blob.data.ends_with?("\n")
elsif params[:content]
new_file = 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
def initialize(merge_request, project, read_only)
@merge_request = merge_request @merge_request = merge_request
@our_commit = merge_request.source_branch_head.raw.rugged_commit @our_commit = merge_request.source_branch_head.raw.rugged_commit
@their_commit = merge_request.target_branch_head.raw.rugged_commit @their_commit = merge_request.target_branch_head.raw.rugged_commit
@project = project @project = project
@read_only = read_only
end end
end end
end end

View file

@ -248,7 +248,7 @@ describe MergeRequests::Conflicts::ResolveService do
it 'raises a MissingFiles error' do it 'raises a MissingFiles error' do
expect { service.execute(user, invalid_params) } expect { service.execute(user, invalid_params) }
.to raise_error(described_class::MissingFiles) .to raise_error(Gitlab::Conflict::FileCollection::MissingFiles)
end end
end end
end end