Commit af5faaf0 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Move diff parsing to own class. Correctly identify note diff line

parent 8f05fbba
...@@ -15,63 +15,9 @@ module CommitsHelper ...@@ -15,63 +15,9 @@ module CommitsHelper
commit_person_link(commit, options.merge(source: :committer)) commit_person_link(commit, options.merge(source: :committer))
end end
def identification_type(line)
if line[0] == "+"
"new"
elsif line[0] == "-"
"old"
else
nil
end
end
def build_line_anchor(diff, line_new, line_old)
"#{hexdigest(diff.new_path)}_#{line_old}_#{line_new}"
end
def each_diff_line(diff, index) def each_diff_line(diff, index)
diff_arr = diff.diff.lines.to_a Gitlab::DiffParser.new(diff).each do |full_line, type, line_code, line_new, line_old|
yield(full_line, type, line_code, line_new, line_old)
line_old = 1
line_new = 1
type = nil
lines_arr = ::Gitlab::InlineDiff.processing diff_arr
lines_arr.each do |line|
raw_line = line.dup
next if line.match(/^\-\-\- \/dev\/null/)
next if line.match(/^\+\+\+ \/dev\/null/)
next if line.match(/^\-\-\- a/)
next if line.match(/^\+\+\+ b/)
full_line = html_escape(line.gsub(/\n/, ''))
full_line = ::Gitlab::InlineDiff.replace_markers full_line
if line.match(/^@@ -/)
type = "match"
line_old = line.match(/\-[0-9]*/)[0].to_i.abs rescue 0
line_new = line.match(/\+[0-9]*/)[0].to_i.abs rescue 0
next if line_old == 1 && line_new == 1 #top of file
yield(full_line, type, nil, nil, nil)
next
else
type = identification_type(line)
line_code = build_line_anchor(diff, line_new, line_old)
yield(full_line, type, line_code, line_new, line_old, raw_line)
end
if line[0] == "+"
line_new += 1
elsif line[0] == "-"
line_old += 1
else
line_new += 1
line_old += 1
end
end end
end end
......
...@@ -51,7 +51,7 @@ class Note < ActiveRecord::Base ...@@ -51,7 +51,7 @@ class Note < ActiveRecord::Base
scope :inc_author, ->{ includes(:author) } scope :inc_author, ->{ includes(:author) }
serialize :st_diff serialize :st_diff
before_create :set_diff, if: ->(n) { n.noteable_type == 'MergeRequest' && n.line_code.present? } before_create :set_diff, if: ->(n) { n.line_code.present? }
def self.create_status_change_note(noteable, author, status) def self.create_status_change_note(noteable, author, status)
create({ create({
...@@ -81,7 +81,7 @@ class Note < ActiveRecord::Base ...@@ -81,7 +81,7 @@ class Note < ActiveRecord::Base
def set_diff def set_diff
# First lets find notes with same diff # First lets find notes with same diff
# before iterating over all mr diffs # before iterating over all mr diffs
diff = self.noteable.notes.where(line_code: self.line_code).last.try(:diff) diff = Note.where(noteable_id: self.noteable_id, noteable_type: self.noteable_type, line_code: self.line_code).last.try(:diff)
diff ||= find_diff diff ||= find_diff
self.st_diff = diff.to_hash if diff self.st_diff = diff.to_hash if diff
...@@ -91,6 +91,12 @@ class Note < ActiveRecord::Base ...@@ -91,6 +91,12 @@ class Note < ActiveRecord::Base
@diff ||= Gitlab::Git::Diff.new(st_diff) if st_diff.respond_to?(:map) @diff ||= Gitlab::Git::Diff.new(st_diff) if st_diff.respond_to?(:map)
end end
def active?
# TODO: determine if discussion is outdated
# according to recent MR diff or not
true
end
def diff_file_index def diff_file_index
line_code.split('_')[0] line_code.split('_')[0]
end end
...@@ -108,10 +114,15 @@ class Note < ActiveRecord::Base ...@@ -108,10 +114,15 @@ class Note < ActiveRecord::Base
end end
def diff_line def diff_line
return @diff_line if @diff_line
if diff if diff
@diff_line ||= diff.diff.lines.select { |line| line =~ /\A\+/ }[diff_new_line] || Gitlab::DiffParser.new(diff).each do |full_line, type, line_code, line_new, line_old|
diff.diff.lines.select { |line| line =~ /\A\-/ }[diff_old_line] @diff_line = full_line if line_code == self.line_code
end
end end
@diff_line
end end
def discussion_id def discussion_id
......
...@@ -20,4 +20,4 @@ ...@@ -20,4 +20,4 @@
- if @reply_allowed - if @reply_allowed
- comments = @line_notes.select { |n| n.line_code == line_code }.sort_by(&:created_at) - comments = @line_notes.select { |n| n.line_code == line_code }.sort_by(&:created_at)
- unless comments.empty? - unless comments.empty?
= render "projects/notes/diff_notes_with_reply", notes: comments, raw_line: raw_line = render "projects/notes/diff_notes_with_reply", notes: comments, line: line
- note = notes.first # example note - note = notes.first # example note
-# Check if line want not changed since comment was left -# Check if line want not changed since comment was left
- if !defined?(raw_line) || raw_line == note.diff_line - if !defined?(line) || line == note.diff_line
%tr.notes_holder %tr.notes_holder
%td.notes_line{ colspan: 2 } %td.notes_line{ colspan: 2 }
%span.btn.disabled %span.btn.disabled
......
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
ago ago
.discussion-body .discussion-body
- if note.for_diff_line? - if note.for_diff_line?
- if note.diff - if note.active?
.content .content
.file= render "projects/notes/discussion_diff", discussion_notes: discussion_notes, note: note .file= render "projects/notes/discussion_diff", discussion_notes: discussion_notes, note: note
- else - else
......
...@@ -61,6 +61,7 @@ sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production ...@@ -61,6 +61,7 @@ sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
sudo -u git -H bundle exec rake migrate_groups RAILS_ENV=production sudo -u git -H bundle exec rake migrate_groups RAILS_ENV=production
sudo -u git -H bundle exec rake migrate_global_projects RAILS_ENV=production sudo -u git -H bundle exec rake migrate_global_projects RAILS_ENV=production
sudo -u git -H bundle exec rake migrate_keys RAILS_ENV=production sudo -u git -H bundle exec rake migrate_keys RAILS_ENV=production
sudo -u git -H bundle exec rake migrate_inline_notes RAILS_ENV=production
``` ```
......
module Gitlab
class DiffParser
include Enumerable
attr_reader :lines, :new_path
def initialize(diff)
@lines = diff.diff.lines.to_a
@new_path = diff.new_path
end
def each
line_old = 1
line_new = 1
type = nil
lines_arr = ::Gitlab::InlineDiff.processing lines
lines_arr.each do |line|
raw_line = line.dup
next if line.match(/^\-\-\- \/dev\/null/)
next if line.match(/^\+\+\+ \/dev\/null/)
next if line.match(/^\-\-\- a/)
next if line.match(/^\+\+\+ b/)
full_line = html_escape(line.gsub(/\n/, ''))
full_line = ::Gitlab::InlineDiff.replace_markers full_line
if line.match(/^@@ -/)
type = "match"
line_old = line.match(/\-[0-9]*/)[0].to_i.abs rescue 0
line_new = line.match(/\+[0-9]*/)[0].to_i.abs rescue 0
next if line_old == 1 && line_new == 1 #top of file
yield(full_line, type, nil, nil, nil)
next
else
type = identification_type(line)
line_code = generate_line_code(new_path, line_new, line_old)
yield(full_line, type, line_code, line_new, line_old, raw_line)
end
if line[0] == "+"
line_new += 1
elsif line[0] == "-"
line_old += 1
else
line_new += 1
line_old += 1
end
end
end
private
def identification_type(line)
if line[0] == "+"
"new"
elsif line[0] == "-"
"old"
else
nil
end
end
def generate_line_code(path, line_new, line_old)
"#{Digest::SHA1.hexdigest(path)}_#{line_old}_#{line_new}"
end
def html_escape str
replacements = { '&' => '&amp;', '>' => '&gt;', '<' => '&lt;', '"' => '&quot;', "'" => '&#39;' }
str.gsub(/[&"'><]/, replacements)
end
end
end
desc "GITLAB | Migrate inline notes" desc "GITLAB | Migrate inline notes"
task migrate_inline_notes: :environment do task migrate_inline_notes: :environment do
Note.where(noteable_type: 'MergeRequest').find_each(batch_size: 100) do |note| Note.where('line_code IS NOT NULL').find_each(batch_size: 100) do |note|
begin begin
note.set_diff note.set_diff
if note.save if note.save
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment