summaryrefslogtreecommitdiff
path: root/lib/gitlab/conflict
diff options
context:
space:
mode:
authorAlejandro Rodríguez <alejorro70@gmail.com>2017-10-06 22:41:23 -0300
committerAlejandro Rodríguez <alejorro70@gmail.com>2017-10-12 22:03:14 -0300
commit3fcab51ebb0f3156b5d732d050b292cd3e081262 (patch)
tree7292079f50121aae6e457bfd3dadb944fb2feeec /lib/gitlab/conflict
parent9cc15172deaa4582c5fd956cc163539041d018b1 (diff)
downloadgitlab-ce-3fcab51ebb0f3156b5d732d050b292cd3e081262.tar.gz
Refactor conflict resolution to contain git ops within Gitlab::Git
This prepares the codebase for a Gitaly migration. See https://gitlab.com/gitlab-org/gitaly/issues/553
Diffstat (limited to 'lib/gitlab/conflict')
-rw-r--r--lib/gitlab/conflict/file.rb84
-rw-r--r--lib/gitlab/conflict/file_collection.rb102
-rw-r--r--lib/gitlab/conflict/parser.rb74
-rw-r--r--lib/gitlab/conflict/resolution_error.rb5
4 files changed, 38 insertions, 227 deletions
diff --git a/lib/gitlab/conflict/file.rb b/lib/gitlab/conflict/file.rb
index 52f8fe27f3b..2d8a21f4905 100644
--- a/lib/gitlab/conflict/file.rb
+++ b/lib/gitlab/conflict/file.rb
@@ -4,82 +4,26 @@ module Gitlab
include Gitlab::Routing
include IconsHelper
- MissingResolution = Class.new(ResolutionError)
-
CONTEXT_LINES = 3
- attr_reader :merge_file_result, :their_path, :our_path, :our_mode, :merge_request, :repository
+ attr_reader :merge_request, :raw
+
+ delegate :type, :content, :their_path, :our_path, :our_mode, :our_blob, :repository, to: :raw
- def initialize(merge_file_result, conflict, merge_request:)
- @merge_file_result = merge_file_result
- @their_path = conflict[:theirs][:path]
- @our_path = conflict[:ours][:path]
- @our_mode = conflict[:ours][:mode]
+ def initialize(raw, merge_request:)
+ @raw = raw
@merge_request = merge_request
- @repository = merge_request.project.repository
@match_line_headers = {}
end
- def content
- merge_file_result[:data]
- end
-
- def our_blob
- @our_blob ||= repository.blob_at(merge_request.diff_refs.head_sha, our_path)
- end
-
- def type
- lines unless @type
-
- @type.inquiry
- end
-
- # Array of Gitlab::Diff::Line objects
def lines
return @lines if defined?(@lines)
- begin
- @type = 'text'
- @lines = Gitlab::Conflict::Parser.new.parse(content,
- our_path: our_path,
- their_path: their_path,
- parent_file: self)
- rescue Gitlab::Conflict::Parser::ParserError
- @type = 'text-editor'
- @lines = nil
- end
+ @lines = raw.lines.nil? ? nil : map_raw_lines(raw.lines)
end
def resolve_lines(resolution)
- section_id = nil
-
- lines.map do |line|
- unless line.type
- section_id = nil
- next line
- end
-
- section_id ||= line_code(line)
-
- case resolution[section_id]
- when 'head'
- next unless line.type == 'new'
- when 'origin'
- next unless line.type == 'old'
- else
- raise MissingResolution, "Missing resolution for section ID: #{section_id}"
- end
-
- line
- end.compact
- end
-
- def resolve_content(resolution)
- if resolution == content
- raise MissingResolution, "Resolved content has no changes for file #{our_path}"
- end
-
- resolution
+ map_raw_lines(raw.resolve_lines(resolution))
end
def highlight_lines!
@@ -227,9 +171,9 @@ module Gitlab
new_path: our_path)
end
- # Don't try to print merge_request or repository.
+ # Don't try to print merge_request.
def inspect
- instance_variables = [:merge_file_result, :their_path, :our_path, :our_mode, :type].map do |instance_variable|
+ instance_variables = [:content, :their_path, :our_path, :our_mode, :type].map do |instance_variable|
value = instance_variable_get("@#{instance_variable}")
"#{instance_variable}=\"#{value}\""
@@ -237,6 +181,16 @@ module Gitlab
"#<#{self.class} #{instance_variables.join(' ')}>"
end
+
+ private
+
+ def map_raw_lines(raw_lines)
+ raw_lines.map do |raw_line|
+ Gitlab::Diff::Line.new(raw_line[:full_line], raw_line[:type],
+ raw_line[:line_obj_index], raw_line[:line_old],
+ raw_line[:line_new], parent_file: self)
+ end
+ end
end
end
end
diff --git a/lib/gitlab/conflict/file_collection.rb b/lib/gitlab/conflict/file_collection.rb
index 4fedfe8691a..573a953b2aa 100644
--- a/lib/gitlab/conflict/file_collection.rb
+++ b/lib/gitlab/conflict/file_collection.rb
@@ -1,68 +1,29 @@
module Gitlab
module Conflict
class FileCollection
- ConflictSideMissing = Class.new(StandardError)
- MissingFiles = Class.new(ResolutionError)
-
- attr_reader :merge_request, :our_commit, :their_commit, :project, :read_only
-
- 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)
- new(merge_request, merge_request.source_project, false)
- end
-
- # 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)
- new(merge_request, merge_request.target_project, true)
- end
+ attr_reader :merge_request, :merge
+
+ def initialize(merge_request)
+ source_repo = merge_request.source_project.repository.raw
+ our_commit = merge_request.source_branch_head.raw
+ their_commit = merge_request.target_branch_head.raw
+ target_repo = merge_request.target_project.repository.raw
+ @merge = Gitlab::Git::Merge.new(source_repo, our_commit, target_repo, their_commit)
+ @merge_request = merge_request
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
-
- def merge_index
- @merge_index ||= repository.rugged.merge_commits(our_commit, their_commit)
+ args = {
+ source_branch: merge_request.source_branch,
+ target_branch: merge_request.target_branch,
+ commit_message: commit_message || default_commit_message
+ }
+ merge.resolve_conflicts(user, files, args)
end
def files
- @files ||= merge_index.conflicts.map do |conflict|
- raise ConflictSideMissing unless conflict[:theirs] && conflict[:ours]
-
- Gitlab::Conflict::File.new(merge_index.merge_file(conflict[:ours][:path]),
- conflict,
- merge_request: merge_request)
+ @files ||= merge.conflicts.map do |conflict_file|
+ Gitlab::Conflict::File.new(conflict_file, merge_request: merge_request)
end
end
@@ -81,8 +42,8 @@ module Gitlab
end
def default_commit_message
- conflict_filenames = merge_index.conflicts.map do |conflict|
- "# #{conflict[:ours][:path]}"
+ conflict_filenames = files.map do |conflict|
+ "# #{conflict.our_path}"
end
<<EOM.chomp
@@ -92,31 +53,6 @@ Merge branch '#{merge_request.target_branch}' into '#{merge_request.source_branc
#{conflict_filenames.join("\n")}
EOM
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
-
- def initialize(merge_request, project, read_only)
- @merge_request = merge_request
- @our_commit = merge_request.source_branch_head.raw.rugged_commit
- @their_commit = merge_request.target_branch_head.raw.rugged_commit
- @project = project
- @read_only = read_only
- end
end
end
end
diff --git a/lib/gitlab/conflict/parser.rb b/lib/gitlab/conflict/parser.rb
deleted file mode 100644
index e3678c914db..00000000000
--- a/lib/gitlab/conflict/parser.rb
+++ /dev/null
@@ -1,74 +0,0 @@
-module Gitlab
- module Conflict
- class Parser
- UnresolvableError = Class.new(StandardError)
- UnmergeableFile = Class.new(UnresolvableError)
- UnsupportedEncoding = Class.new(UnresolvableError)
-
- # Recoverable errors - the conflict can be resolved in an editor, but not with
- # sections.
- ParserError = Class.new(StandardError)
- UnexpectedDelimiter = Class.new(ParserError)
- MissingEndDelimiter = Class.new(ParserError)
-
- def parse(text, our_path:, their_path:, parent_file: nil)
- validate_text!(text)
-
- line_obj_index = 0
- line_old = 1
- line_new = 1
- type = nil
- lines = []
- conflict_start = "<<<<<<< #{our_path}"
- conflict_middle = '======='
- conflict_end = ">>>>>>> #{their_path}"
-
- text.each_line.map do |line|
- full_line = line.delete("\n")
-
- if full_line == conflict_start
- validate_delimiter!(type.nil?)
-
- type = 'new'
- elsif full_line == conflict_middle
- validate_delimiter!(type == 'new')
-
- type = 'old'
- elsif full_line == conflict_end
- validate_delimiter!(type == 'old')
-
- type = nil
- elsif line[0] == '\\'
- type = 'nonewline'
- lines << Gitlab::Diff::Line.new(full_line, type, line_obj_index, line_old, line_new, parent_file: parent_file)
- else
- lines << Gitlab::Diff::Line.new(full_line, type, line_obj_index, line_old, line_new, parent_file: parent_file)
- line_old += 1 if type != 'new'
- line_new += 1 if type != 'old'
-
- line_obj_index += 1
- end
- end
-
- raise MissingEndDelimiter unless type.nil?
-
- lines
- end
-
- private
-
- def validate_text!(text)
- raise UnmergeableFile if text.blank? # Typically a binary file
- raise UnmergeableFile if text.length > 200.kilobytes
-
- text.force_encoding('UTF-8')
-
- raise UnsupportedEncoding unless text.valid_encoding?
- end
-
- def validate_delimiter!(condition)
- raise UnexpectedDelimiter unless condition
- end
- end
- end
-end
diff --git a/lib/gitlab/conflict/resolution_error.rb b/lib/gitlab/conflict/resolution_error.rb
deleted file mode 100644
index 0b61256b35a..00000000000
--- a/lib/gitlab/conflict/resolution_error.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-module Gitlab
- module Conflict
- ResolutionError = Class.new(StandardError)
- end
-end