Commit af5faaf0e1e001558302704a421eb01f5e7a26ea

Authored by Dmitriy Zaporozhets
1 parent 8f05fbba

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

app/helpers/commits_helper.rb
... ... @@ -15,63 +15,9 @@ module CommitsHelper
15 15 commit_person_link(commit, options.merge(source: :committer))
16 16 end
17 17  
18   - def identification_type(line)
19   - if line[0] == "+"
20   - "new"
21   - elsif line[0] == "-"
22   - "old"
23   - else
24   - nil
25   - end
26   - end
27   -
28   - def build_line_anchor(diff, line_new, line_old)
29   - "#{hexdigest(diff.new_path)}_#{line_old}_#{line_new}"
30   - end
31   -
32 18 def each_diff_line(diff, index)
33   - diff_arr = diff.diff.lines.to_a
34   -
35   - line_old = 1
36   - line_new = 1
37   - type = nil
38   -
39   - lines_arr = ::Gitlab::InlineDiff.processing diff_arr
40   - lines_arr.each do |line|
41   - raw_line = line.dup
42   -
43   - next if line.match(/^\-\-\- \/dev\/null/)
44   - next if line.match(/^\+\+\+ \/dev\/null/)
45   - next if line.match(/^\-\-\- a/)
46   - next if line.match(/^\+\+\+ b/)
47   -
48   - full_line = html_escape(line.gsub(/\n/, ''))
49   - full_line = ::Gitlab::InlineDiff.replace_markers full_line
50   -
51   - if line.match(/^@@ -/)
52   - type = "match"
53   -
54   - line_old = line.match(/\-[0-9]*/)[0].to_i.abs rescue 0
55   - line_new = line.match(/\+[0-9]*/)[0].to_i.abs rescue 0
56   -
57   - next if line_old == 1 && line_new == 1 #top of file
58   - yield(full_line, type, nil, nil, nil)
59   - next
60   - else
61   - type = identification_type(line)
62   - line_code = build_line_anchor(diff, line_new, line_old)
63   - yield(full_line, type, line_code, line_new, line_old, raw_line)
64   - end
65   -
66   -
67   - if line[0] == "+"
68   - line_new += 1
69   - elsif line[0] == "-"
70   - line_old += 1
71   - else
72   - line_new += 1
73   - line_old += 1
74   - end
  19 + Gitlab::DiffParser.new(diff).each do |full_line, type, line_code, line_new, line_old|
  20 + yield(full_line, type, line_code, line_new, line_old)
75 21 end
76 22 end
77 23  
... ...
app/models/note.rb
... ... @@ -51,7 +51,7 @@ class Note < ActiveRecord::Base
51 51 scope :inc_author, ->{ includes(:author) }
52 52  
53 53 serialize :st_diff
54   - before_create :set_diff, if: ->(n) { n.noteable_type == 'MergeRequest' && n.line_code.present? }
  54 + before_create :set_diff, if: ->(n) { n.line_code.present? }
55 55  
56 56 def self.create_status_change_note(noteable, author, status)
57 57 create({
... ... @@ -81,7 +81,7 @@ class Note < ActiveRecord::Base
81 81 def set_diff
82 82 # First lets find notes with same diff
83 83 # before iterating over all mr diffs
84   - diff = self.noteable.notes.where(line_code: self.line_code).last.try(:diff)
  84 + diff = Note.where(noteable_id: self.noteable_id, noteable_type: self.noteable_type, line_code: self.line_code).last.try(:diff)
85 85 diff ||= find_diff
86 86  
87 87 self.st_diff = diff.to_hash if diff
... ... @@ -91,6 +91,12 @@ class Note < ActiveRecord::Base
91 91 @diff ||= Gitlab::Git::Diff.new(st_diff) if st_diff.respond_to?(:map)
92 92 end
93 93  
  94 + def active?
  95 + # TODO: determine if discussion is outdated
  96 + # according to recent MR diff or not
  97 + true
  98 + end
  99 +
94 100 def diff_file_index
95 101 line_code.split('_')[0]
96 102 end
... ... @@ -108,10 +114,15 @@ class Note < ActiveRecord::Base
108 114 end
109 115  
110 116 def diff_line
  117 + return @diff_line if @diff_line
  118 +
111 119 if diff
112   - @diff_line ||= diff.diff.lines.select { |line| line =~ /\A\+/ }[diff_new_line] ||
113   - diff.diff.lines.select { |line| line =~ /\A\-/ }[diff_old_line]
  120 + Gitlab::DiffParser.new(diff).each do |full_line, type, line_code, line_new, line_old|
  121 + @diff_line = full_line if line_code == self.line_code
  122 + end
114 123 end
  124 +
  125 + @diff_line
115 126 end
116 127  
117 128 def discussion_id
... ...
app/views/projects/commits/_text_file.html.haml
... ... @@ -20,4 +20,4 @@
20 20 - if @reply_allowed
21 21 - comments = @line_notes.select { |n| n.line_code == line_code }.sort_by(&:created_at)
22 22 - unless comments.empty?
23   - = render "projects/notes/diff_notes_with_reply", notes: comments, raw_line: raw_line
  23 + = render "projects/notes/diff_notes_with_reply", notes: comments, line: line
... ...
app/views/projects/notes/_diff_notes_with_reply.html.haml
1 1 - note = notes.first # example note
2 2 -# Check if line want not changed since comment was left
3   -- if !defined?(raw_line) || raw_line == note.diff_line
  3 +- if !defined?(line) || line == note.diff_line
4 4 %tr.notes_holder
5 5 %td.notes_line{ colspan: 2 }
6 6 %span.btn.disabled
... ...
app/views/projects/notes/_discussion.html.haml
... ... @@ -36,7 +36,7 @@
36 36 ago
37 37 .discussion-body
38 38 - if note.for_diff_line?
39   - - if note.diff
  39 + - if note.active?
40 40 .content
41 41 .file= render "projects/notes/discussion_diff", discussion_notes: discussion_notes, note: note
42 42 - else
... ...
doc/update/5.4-to-6.0.md
... ... @@ -61,6 +61,7 @@ sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
61 61 sudo -u git -H bundle exec rake migrate_groups RAILS_ENV=production
62 62 sudo -u git -H bundle exec rake migrate_global_projects RAILS_ENV=production
63 63 sudo -u git -H bundle exec rake migrate_keys RAILS_ENV=production
  64 +sudo -u git -H bundle exec rake migrate_inline_notes RAILS_ENV=production
64 65  
65 66 ```
66 67  
... ...
lib/gitlab/diff_parser.rb 0 → 100644
... ... @@ -0,0 +1,77 @@
  1 +module Gitlab
  2 + class DiffParser
  3 + include Enumerable
  4 +
  5 + attr_reader :lines, :new_path
  6 +
  7 + def initialize(diff)
  8 + @lines = diff.diff.lines.to_a
  9 + @new_path = diff.new_path
  10 + end
  11 +
  12 + def each
  13 + line_old = 1
  14 + line_new = 1
  15 + type = nil
  16 +
  17 + lines_arr = ::Gitlab::InlineDiff.processing lines
  18 + lines_arr.each do |line|
  19 + raw_line = line.dup
  20 +
  21 + next if line.match(/^\-\-\- \/dev\/null/)
  22 + next if line.match(/^\+\+\+ \/dev\/null/)
  23 + next if line.match(/^\-\-\- a/)
  24 + next if line.match(/^\+\+\+ b/)
  25 +
  26 + full_line = html_escape(line.gsub(/\n/, ''))
  27 + full_line = ::Gitlab::InlineDiff.replace_markers full_line
  28 +
  29 + if line.match(/^@@ -/)
  30 + type = "match"
  31 +
  32 + line_old = line.match(/\-[0-9]*/)[0].to_i.abs rescue 0
  33 + line_new = line.match(/\+[0-9]*/)[0].to_i.abs rescue 0
  34 +
  35 + next if line_old == 1 && line_new == 1 #top of file
  36 + yield(full_line, type, nil, nil, nil)
  37 + next
  38 + else
  39 + type = identification_type(line)
  40 + line_code = generate_line_code(new_path, line_new, line_old)
  41 + yield(full_line, type, line_code, line_new, line_old, raw_line)
  42 + end
  43 +
  44 +
  45 + if line[0] == "+"
  46 + line_new += 1
  47 + elsif line[0] == "-"
  48 + line_old += 1
  49 + else
  50 + line_new += 1
  51 + line_old += 1
  52 + end
  53 + end
  54 + end
  55 +
  56 + private
  57 +
  58 + def identification_type(line)
  59 + if line[0] == "+"
  60 + "new"
  61 + elsif line[0] == "-"
  62 + "old"
  63 + else
  64 + nil
  65 + end
  66 + end
  67 +
  68 + def generate_line_code(path, line_new, line_old)
  69 + "#{Digest::SHA1.hexdigest(path)}_#{line_old}_#{line_new}"
  70 + end
  71 +
  72 + def html_escape str
  73 + replacements = { '&' => '&amp;', '>' => '&gt;', '<' => '&lt;', '"' => '&quot;', "'" => '&#39;' }
  74 + str.gsub(/[&"'><]/, replacements)
  75 + end
  76 + end
  77 +end
... ...
lib/tasks/migrate/migrate_inline_notes.rake
1 1 desc "GITLAB | Migrate inline notes"
2 2 task migrate_inline_notes: :environment do
3   - Note.where(noteable_type: 'MergeRequest').find_each(batch_size: 100) do |note|
  3 + Note.where('line_code IS NOT NULL').find_each(batch_size: 100) do |note|
4 4 begin
5 5 note.set_diff
6 6 if note.save
... ...