diff options
author | Alejandro RodrÃguez <alejorro70@gmail.com> | 2017-10-07 04:41:23 +0300 |
---|---|---|
committer | Alejandro RodrÃguez <alejorro70@gmail.com> | 2017-10-13 04:03:14 +0300 |
commit | 3fcab51ebb0f3156b5d732d050b292cd3e081262 (patch) | |
tree | 7292079f50121aae6e457bfd3dadb944fb2feeec /lib/gitlab/conflict | |
parent | 9cc15172deaa4582c5fd956cc163539041d018b1 (diff) |
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.rb | 84 | ||||
-rw-r--r-- | lib/gitlab/conflict/file_collection.rb | 102 | ||||
-rw-r--r-- | lib/gitlab/conflict/parser.rb | 74 | ||||
-rw-r--r-- | lib/gitlab/conflict/resolution_error.rb | 5 |
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 |