2016-07-27 11:42:18 +00:00
|
|
|
module Gitlab
|
|
|
|
module Conflict
|
|
|
|
class FileCollection
|
2017-03-01 11:00:37 +00:00
|
|
|
ConflictSideMissing = Class.new(StandardError)
|
2017-10-05 23:22:24 +00:00
|
|
|
MissingFiles = Class.new(ResolutionError)
|
2016-08-01 13:52:53 +00:00
|
|
|
|
2017-10-05 23:22:24 +00:00
|
|
|
attr_reader :merge_request, :our_commit, :their_commit, :project, :read_only
|
2016-07-27 11:42:18 +00:00
|
|
|
|
2017-05-11 15:23:02 +00:00
|
|
|
delegate :repository, to: :project
|
|
|
|
|
|
|
|
class << self
|
|
|
|
# We can only write when getting the merge index from the source
|
|
|
|
# project, because we will write to that project. We don't use this all
|
|
|
|
# the time because this fetches a ref into the source project, which
|
|
|
|
# isn't needed for reading.
|
|
|
|
def for_resolution(merge_request)
|
2017-10-05 23:22:24 +00:00
|
|
|
new(merge_request, merge_request.source_project, false)
|
2017-05-11 15:23:02 +00:00
|
|
|
end
|
2016-07-27 11:42:18 +00:00
|
|
|
|
2017-05-11 15:23:02 +00:00
|
|
|
# We don't need to do `with_repo_branch_commit` here, because the target
|
|
|
|
# project always fetches source refs when creating merge request diffs.
|
|
|
|
def read_only(merge_request)
|
2017-10-05 23:22:24 +00:00
|
|
|
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)
|
2017-05-11 15:23:02 +00:00
|
|
|
end
|
2016-07-27 11:42:18 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def merge_index
|
|
|
|
@merge_index ||= repository.rugged.merge_commits(our_commit, their_commit)
|
2016-07-27 16:54:04 +00:00
|
|
|
end
|
|
|
|
|
2016-07-27 11:42:18 +00:00
|
|
|
def files
|
|
|
|
@files ||= merge_index.conflicts.map do |conflict|
|
2016-08-01 13:52:53 +00:00
|
|
|
raise ConflictSideMissing unless conflict[:theirs] && conflict[:ours]
|
2016-07-27 11:42:18 +00:00
|
|
|
|
2016-08-01 13:52:53 +00:00
|
|
|
Gitlab::Conflict::File.new(merge_index.merge_file(conflict[:ours][:path]),
|
2016-07-27 11:42:18 +00:00
|
|
|
conflict,
|
2016-08-03 11:02:20 +00:00
|
|
|
merge_request: merge_request)
|
2016-07-27 11:42:18 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-09-01 12:59:10 +00:00
|
|
|
def file_for_path(old_path, new_path)
|
|
|
|
files.find { |file| file.their_path == old_path && file.our_path == new_path }
|
|
|
|
end
|
|
|
|
|
2016-07-27 11:42:18 +00:00
|
|
|
def as_json(opts = nil)
|
|
|
|
{
|
|
|
|
target_branch: merge_request.target_branch,
|
|
|
|
source_branch: merge_request.source_branch,
|
|
|
|
commit_sha: merge_request.diff_head_sha,
|
|
|
|
commit_message: default_commit_message,
|
|
|
|
files: files
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
def default_commit_message
|
|
|
|
conflict_filenames = merge_index.conflicts.map do |conflict|
|
|
|
|
"# #{conflict[:ours][:path]}"
|
|
|
|
end
|
|
|
|
|
|
|
|
<<EOM.chomp
|
2016-08-05 11:15:06 +00:00
|
|
|
Merge branch '#{merge_request.target_branch}' into '#{merge_request.source_branch}'
|
2016-07-27 11:42:18 +00:00
|
|
|
|
|
|
|
# Conflicts:
|
|
|
|
#{conflict_filenames.join("\n")}
|
|
|
|
EOM
|
|
|
|
end
|
2017-05-11 15:23:02 +00:00
|
|
|
|
|
|
|
private
|
|
|
|
|
2017-10-05 23:22:24 +00:00
|
|
|
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)
|
2017-05-11 15:23:02 +00:00
|
|
|
@merge_request = merge_request
|
2017-07-25 21:33:06 +00:00
|
|
|
@our_commit = merge_request.source_branch_head.raw.rugged_commit
|
|
|
|
@their_commit = merge_request.target_branch_head.raw.rugged_commit
|
2017-05-11 15:23:02 +00:00
|
|
|
@project = project
|
2017-10-05 23:22:24 +00:00
|
|
|
@read_only = read_only
|
2017-05-11 15:23:02 +00:00
|
|
|
end
|
2016-07-27 11:42:18 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|