Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-02-18 13:34:06 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-02-18 13:34:06 +0300
commit859a6fb938bb9ee2a317c46dfa4fcc1af49608f0 (patch)
treed7f2700abe6b4ffcb2dcfc80631b2d87d0609239 /lib/gitlab/diff
parent446d496a6d000c73a304be52587cd9bbc7493136 (diff)
Add latest changes from gitlab-org/gitlab@13-9-stable-eev13.9.0-rc42
Diffstat (limited to 'lib/gitlab/diff')
-rw-r--r--lib/gitlab/diff/char_diff.rb74
-rw-r--r--lib/gitlab/diff/file_collection/base.rb2
-rw-r--r--lib/gitlab/diff/file_collection_sorter.rb14
-rw-r--r--lib/gitlab/diff/highlight.rb5
-rw-r--r--lib/gitlab/diff/highlight_cache.rb11
-rw-r--r--lib/gitlab/diff/inline_diff.rb43
6 files changed, 124 insertions, 25 deletions
diff --git a/lib/gitlab/diff/char_diff.rb b/lib/gitlab/diff/char_diff.rb
new file mode 100644
index 00000000000..c8bb39e9f5d
--- /dev/null
+++ b/lib/gitlab/diff/char_diff.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Diff
+ class CharDiff
+ include Gitlab::Utils::StrongMemoize
+
+ def initialize(old_string, new_string)
+ @old_string = old_string.to_s
+ @new_string = new_string.to_s
+ @changes = []
+ end
+
+ def generate_diff
+ @changes = diff_match_patch.diff_main(@old_string, @new_string)
+ diff_match_patch.diff_cleanupSemantic(@changes)
+
+ @changes
+ end
+
+ def changed_ranges(offset: 0)
+ old_diffs = []
+ new_diffs = []
+ new_pointer = old_pointer = offset
+
+ generate_diff.each do |(action, content)|
+ content_size = content.size
+
+ if action == :equal
+ new_pointer += content_size
+ old_pointer += content_size
+ end
+
+ if action == :delete
+ old_diffs << (old_pointer..(old_pointer + content_size - 1))
+ old_pointer += content_size
+ end
+
+ if action == :insert
+ new_diffs << (new_pointer..(new_pointer + content_size - 1))
+ new_pointer += content_size
+ end
+ end
+
+ [old_diffs, new_diffs]
+ end
+
+ def to_html
+ @changes.map do |op, text|
+ %{<span class="#{html_class_names(op)}">#{ERB::Util.html_escape(text)}</span>}
+ end.join.html_safe
+ end
+
+ private
+
+ def diff_match_patch
+ strong_memoize(:diff_match_patch) { DiffMatchPatch.new }
+ end
+
+ def html_class_names(operation)
+ class_names = ['idiff']
+
+ case operation
+ when :insert
+ class_names << 'addition'
+ when :delete
+ class_names << 'deletion'
+ end
+
+ class_names.join(' ')
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/diff/file_collection/base.rb b/lib/gitlab/diff/file_collection/base.rb
index 8f4f8febec0..627abfbfe7e 100644
--- a/lib/gitlab/diff/file_collection/base.rb
+++ b/lib/gitlab/diff/file_collection/base.rb
@@ -117,7 +117,7 @@ module Gitlab
end
def sort_diffs(diffs)
- return diffs unless Feature.enabled?(:sort_diffs, project, default_enabled: false)
+ return diffs unless Feature.enabled?(:sort_diffs, project, default_enabled: :yaml)
Gitlab::Diff::FileCollectionSorter.new(diffs).sort
end
diff --git a/lib/gitlab/diff/file_collection_sorter.rb b/lib/gitlab/diff/file_collection_sorter.rb
index 94626875580..7b099543c83 100644
--- a/lib/gitlab/diff/file_collection_sorter.rb
+++ b/lib/gitlab/diff/file_collection_sorter.rb
@@ -3,6 +3,10 @@
module Gitlab
module Diff
class FileCollectionSorter
+ B_FOLLOWS_A = 1
+ A_FOLLOWS_B = -1
+ EQUIVALENT = 0
+
attr_reader :diffs
def initialize(diffs)
@@ -29,14 +33,16 @@ module Gitlab
a_part = a_parts.shift
b_part = b_parts.shift
- return 1 if a_parts.size < b_parts.size && a_parts.empty?
- return -1 if a_parts.size > b_parts.size && b_parts.empty?
+ return B_FOLLOWS_A if a_parts.size < b_parts.size && a_parts.empty?
+ return A_FOLLOWS_B if a_parts.size > b_parts.size && b_parts.empty?
comparison = a_part <=> b_part
- return comparison unless comparison == 0
+ return comparison unless comparison == EQUIVALENT
+ return compare_path_parts(a_parts, b_parts) if a_parts.any? && b_parts.any?
- compare_path_parts(a_parts, b_parts)
+ # If A and B have the same name (e.g. symlink change), they are identical so return 0
+ EQUIVALENT
end
end
end
diff --git a/lib/gitlab/diff/highlight.rb b/lib/gitlab/diff/highlight.rb
index a5259079345..035084d4861 100644
--- a/lib/gitlab/diff/highlight.rb
+++ b/lib/gitlab/diff/highlight.rb
@@ -3,12 +3,13 @@
module Gitlab
module Diff
class Highlight
- attr_reader :diff_file, :diff_lines, :raw_lines, :repository
+ attr_reader :diff_file, :diff_lines, :raw_lines, :repository, :project
delegate :old_path, :new_path, :old_sha, :new_sha, to: :diff_file, prefix: :diff
def initialize(diff_lines, repository: nil)
@repository = repository
+ @project = repository&.project
if diff_lines.is_a?(Gitlab::Diff::File)
@diff_file = diff_lines
@@ -66,7 +67,7 @@ module Gitlab
end
def inline_diffs
- @inline_diffs ||= InlineDiff.for_lines(@raw_lines)
+ @inline_diffs ||= InlineDiff.for_lines(@raw_lines, project: project)
end
def old_lines
diff --git a/lib/gitlab/diff/highlight_cache.rb b/lib/gitlab/diff/highlight_cache.rb
index 90cb9c8638a..7932cd2a837 100644
--- a/lib/gitlab/diff/highlight_cache.rb
+++ b/lib/gitlab/diff/highlight_cache.rb
@@ -8,6 +8,7 @@ module Gitlab
EXPIRATION = 1.week
VERSION = 1
+ NEXT_VERSION = 2
delegate :diffable, to: :@diff_collection
delegate :diff_options, to: :@diff_collection
@@ -69,12 +70,20 @@ module Gitlab
def key
strong_memoize(:redis_key) do
- ['highlighted-diff-files', diffable.cache_key, VERSION, diff_options].join(":")
+ ['highlighted-diff-files', diffable.cache_key, version, diff_options].join(":")
end
end
private
+ def version
+ if Feature.enabled?(:improved_merge_diff_highlighting, diffable.project, default_enabled: :yaml)
+ NEXT_VERSION
+ else
+ VERSION
+ end
+ end
+
def set_highlighted_diff_lines(diff_file, content)
diff_file.highlighted_diff_lines = content.map do |line|
Gitlab::Diff::Line.safe_init_from_hash(line)
diff --git a/lib/gitlab/diff/inline_diff.rb b/lib/gitlab/diff/inline_diff.rb
index 5815d1bae4a..cf769262958 100644
--- a/lib/gitlab/diff/inline_diff.rb
+++ b/lib/gitlab/diff/inline_diff.rb
@@ -27,28 +27,19 @@ module Gitlab
@offset = offset
end
- def inline_diffs
+ def inline_diffs(project: nil)
# Skip inline diff if empty line was replaced with content
return if old_line == ""
- lcp = longest_common_prefix(old_line, new_line)
- lcs = longest_common_suffix(old_line[lcp..-1], new_line[lcp..-1])
-
- lcp += offset
- old_length = old_line.length + offset
- new_length = new_line.length + offset
-
- old_diff_range = lcp..(old_length - lcs - 1)
- new_diff_range = lcp..(new_length - lcs - 1)
-
- old_diffs = [old_diff_range] if old_diff_range.begin <= old_diff_range.end
- new_diffs = [new_diff_range] if new_diff_range.begin <= new_diff_range.end
-
- [old_diffs, new_diffs]
+ if Feature.enabled?(:improved_merge_diff_highlighting, project, default_enabled: :yaml)
+ CharDiff.new(old_line, new_line).changed_ranges(offset: offset)
+ else
+ deprecated_diff
+ end
end
class << self
- def for_lines(lines)
+ def for_lines(lines, project: nil)
changed_line_pairs = find_changed_line_pairs(lines)
inline_diffs = []
@@ -57,7 +48,7 @@ module Gitlab
old_line = lines[old_index]
new_line = lines[new_index]
- old_diffs, new_diffs = new(old_line, new_line, offset: 1).inline_diffs
+ old_diffs, new_diffs = new(old_line, new_line, offset: 1).inline_diffs(project: project)
inline_diffs[old_index] = old_diffs
inline_diffs[new_index] = new_diffs
@@ -97,6 +88,24 @@ module Gitlab
private
+ # See: https://gitlab.com/gitlab-org/gitlab/-/issues/299884
+ def deprecated_diff
+ lcp = longest_common_prefix(old_line, new_line)
+ lcs = longest_common_suffix(old_line[lcp..-1], new_line[lcp..-1])
+
+ lcp += offset
+ old_length = old_line.length + offset
+ new_length = new_line.length + offset
+
+ old_diff_range = lcp..(old_length - lcs - 1)
+ new_diff_range = lcp..(new_length - lcs - 1)
+
+ old_diffs = [old_diff_range] if old_diff_range.begin <= old_diff_range.end
+ new_diffs = [new_diff_range] if new_diff_range.begin <= new_diff_range.end
+
+ [old_diffs, new_diffs]
+ end
+
def longest_common_prefix(a, b) # rubocop:disable Naming/UncommunicativeMethodParamName
max_length = [a.length, b.length].max