Skip to content
Snippets Groups Projects
Commit ab91f76e authored by Douwe Maan's avatar Douwe Maan
Browse files

Add system note with link to diff comparison when MR discussion becomes outdated

parent 52527be4
No related branches found
No related tags found
No related merge requests found
Showing
with 156 additions and 74 deletions
Loading
Loading
@@ -5,7 +5,7 @@
 
.note-text {
p:last-child {
margin-bottom: 0;
margin-bottom: 0 !important;
}
}
 
Loading
Loading
Loading
Loading
@@ -164,10 +164,6 @@
 
.discussion-body,
.diff-file {
.notes .note {
padding: 10px 15px;
}
.discussion-reply-holder {
background-color: $white-light;
padding: 10px 16px;
Loading
Loading
Loading
Loading
@@ -80,10 +80,6 @@ ul.notes {
&.timeline-entry {
padding: 14px 10px;
}
.system-note {
padding: 0;
}
}
 
&.is-editing {
Loading
Loading
@@ -380,6 +376,10 @@ ul.notes {
padding-bottom: 5px;
}
 
.system-note .note-header-info {
padding-bottom: 0;
}
.note-headline-light {
display: inline;
 
Loading
Loading
@@ -582,6 +582,17 @@ ul.notes {
}
}
 
.discussion-body,
.diff-file {
.notes .note {
padding: 10px 15px;
&.system-note {
padding: 0;
}
}
}
.diff-file {
.is-over {
.add-diff-note {
Loading
Loading
Loading
Loading
@@ -17,7 +17,8 @@ module SystemNoteHelper
'visible' => 'icon_eye',
'milestone' => 'icon_clock_o',
'discussion' => 'icon_comment_o',
'moved' => 'icon_arrow_circle_o_right'
'moved' => 'icon_arrow_circle_o_right',
'outdated' => 'icon_edit'
}.freeze
 
def icon_for_system_note(note)
Loading
Loading
Loading
Loading
@@ -19,21 +19,9 @@ class DiffDiscussion < Discussion
 
def merge_request_version_params
return unless for_merge_request?
return {} if active?
 
if active?
{}
else
diff_refs = position.diff_refs
if diff = noteable.merge_request_diff_for(diff_refs)
{ diff_id: diff.id }
elsif diff = noteable.merge_request_diff_for(diff_refs.head_sha)
{
diff_id: diff.id,
start_sha: diff_refs.start_sha
}
end
end
noteable.version_params_for(position.diff_refs)
end
 
def reply_attributes
Loading
Loading
Loading
Loading
@@ -8,6 +8,7 @@ class DiffNote < Note
 
serialize :original_position, Gitlab::Diff::Position
serialize :position, Gitlab::Diff::Position
serialize :change_position, Gitlab::Diff::Position
 
validates :original_position, presence: true
validates :position, presence: true
Loading
Loading
@@ -25,7 +26,7 @@ class DiffNote < Note
DiffDiscussion
end
 
%i(original_position position).each do |meth|
%i(original_position position change_position).each do |meth|
define_method "#{meth}=" do |new_position|
if new_position.is_a?(String)
new_position = JSON.parse(new_position) rescue nil
Loading
Loading
@@ -36,6 +37,8 @@ class DiffNote < Note
new_position = Gitlab::Diff::Position.new(new_position)
end
 
return if new_position == read_attribute(meth)
super(new_position)
end
end
Loading
Loading
@@ -45,7 +48,7 @@ class DiffNote < Note
end
 
def diff_line
@diff_line ||= diff_file.line_for_position(self.original_position) if diff_file
@diff_line ||= diff_file&.line_for_position(self.original_position)
end
 
def for_line?(line)
Loading
Loading
Loading
Loading
@@ -416,13 +416,24 @@ class MergeRequest < ActiveRecord::Base
@merge_request_diffs_by_diff_refs_or_sha[diff_refs_or_sha]
end
 
def version_params_for(diff_refs)
if diff = merge_request_diff_for(diff_refs)
{ diff_id: diff.id }
elsif diff = merge_request_diff_for(diff_refs.head_sha)
{
diff_id: diff.id,
start_sha: diff_refs.start_sha
}
end
end
def reload_diff_if_branch_changed
if source_branch_changed? || target_branch_changed?
reload_diff
end
end
 
def reload_diff
def reload_diff(current_user = nil)
return unless open?
 
old_diff_refs = self.diff_refs
Loading
Loading
@@ -432,7 +443,8 @@ class MergeRequest < ActiveRecord::Base
 
update_diff_notes_positions(
old_diff_refs: old_diff_refs,
new_diff_refs: new_diff_refs
new_diff_refs: new_diff_refs,
current_user: current_user
)
end
 
Loading
Loading
@@ -861,7 +873,7 @@ class MergeRequest < ActiveRecord::Base
diff_sha_refs && diff_sha_refs.complete?
end
 
def update_diff_notes_positions(old_diff_refs:, new_diff_refs:)
def update_diff_notes_positions(old_diff_refs:, new_diff_refs:, current_user: nil)
return unless has_complete_diff_refs?
return if new_diff_refs == old_diff_refs
 
Loading
Loading
@@ -875,7 +887,7 @@ class MergeRequest < ActiveRecord::Base
 
service = Notes::DiffPositionUpdateService.new(
self.project,
nil,
current_user,
old_diff_refs: old_diff_refs,
new_diff_refs: new_diff_refs,
paths: paths
Loading
Loading
Loading
Loading
@@ -175,12 +175,11 @@ class MergeRequestDiff < ActiveRecord::Base
self == merge_request.merge_request_diff
end
 
def compare_with(sha, straight: true)
def compare_with(sha)
# When compare merge request versions we want diff A..B instead of A...B
# so we handle cases when user does squash and rebase of the commits between versions.
# For this reason we set straight to true by default.
CompareService.new(project, head_commit_sha)
.execute(project, sha, straight: straight)
CompareService.new(project, head_commit_sha).execute(project, sha, straight: true)
end
 
def commits_count
Loading
Loading
Loading
Loading
@@ -121,16 +121,17 @@ class Note < ActiveRecord::Base
end
 
def grouped_diff_discussions(diff_refs = nil)
groups = {}
groups = Hash.new { |h, k| h[k] = [] }
 
diff_notes.fresh.discussions.each do |discussion|
if discussion.active?(diff_refs)
discussions = groups[discussion.line_code] ||= []
elsif diff_refs && discussion.created_at_diff?(diff_refs)
discussions = groups[discussion.original_line_code] ||= []
end
discussions << discussion if discussions
line_code =
if discussion.active?(diff_refs)
discussion.line_code
elsif diff_refs && discussion.created_at_diff?(diff_refs)
discussion.original_line_code
end
groups[line_code] << discussion if line_code
end
 
groups
Loading
Loading
Loading
Loading
@@ -2,6 +2,7 @@ class SystemNoteMetadata < ActiveRecord::Base
ICON_TYPES = %w[
commit description merge confidential visible label assignee cross_reference
title time_tracking branch milestone discussion task moved opened closed merged
outdated
].freeze
 
validates :note, presence: true
Loading
Loading
Loading
Loading
@@ -66,12 +66,12 @@ module MergeRequests
 
filter_merge_requests(merge_requests).each do |merge_request|
if merge_request.source_branch == @branch_name || force_push?
merge_request.reload_diff
merge_request.reload_diff(current_user)
else
mr_commit_ids = merge_request.commits_sha
push_commit_ids = @commits.map(&:id)
matches = mr_commit_ids & push_commit_ids
merge_request.reload_diff if matches.any?
merge_request.reload_diff(current_user) if matches.any?
end
 
merge_request.mark_as_unchecked
Loading
Loading
Loading
Loading
@@ -8,7 +8,7 @@ module MergeRequests
create_note(merge_request)
notification_service.reopen_mr(merge_request, current_user)
execute_hooks(merge_request, 'reopen')
merge_request.reload_diff
merge_request.reload_diff(current_user)
merge_request.mark_as_unchecked
end
 
Loading
Loading
module Notes
class DiffPositionUpdateService < BaseService
def execute(note)
new_position = tracer.trace(note.position)
results = tracer.trace(note.position)
return unless results
 
# Don't update the position if the type doesn't match, since that means
# the diff line commented on was changed, and the comment is now outdated
old_position = note.position
if new_position &&
new_position != old_position &&
new_position.type == old_position.type
position = results[:position]
outdated = results[:outdated]
 
note.position = new_position
end
if outdated
note.change_position = position
 
note
if note.persisted? && current_user
SystemNoteService.diff_discussion_outdated(note.to_discussion, project, current_user, position)
end
else
note.position = position
note.change_position = nil
end
end
 
private
 
def tracer
@tracer ||= Gitlab::Diff::PositionTracer.new(
repository: project.repository,
project: project,
old_diff_refs: params[:old_diff_refs],
new_diff_refs: params[:new_diff_refs],
paths: params[:paths]
Loading
Loading
Loading
Loading
@@ -258,7 +258,7 @@ module SystemNoteService
create_note(NoteSummary.new(noteable, project, author, body, action: 'title'))
end
 
def self.resolve_all_discussions(merge_request, project, author)
def resolve_all_discussions(merge_request, project, author)
body = "resolved all discussions"
 
create_note(NoteSummary.new(merge_request, project, author, body, action: 'discussion'))
Loading
Loading
@@ -274,6 +274,28 @@ module SystemNoteService
note
end
 
def diff_discussion_outdated(discussion, project, author, change_position)
merge_request = discussion.noteable
diff_refs = change_position.diff_refs
version_index = merge_request.merge_request_diffs.viewable.count
body = "changed this line in"
if version_params = merge_request.version_params_for(diff_refs)
line_code = change_position.line_code(project.repository)
url = url_helpers.diffs_namespace_project_merge_request_url(project.namespace, project, merge_request, version_params.merge(anchor: line_code))
body << " [version #{version_index} of the diff](#{url})"
else
body << " version #{version_index} of the diff"
end
note_attributes = discussion.reply_attributes.merge(project: project, author: author, note: body)
note = Note.create(note_attributes.merge(system: true))
note.system_note_metadata = SystemNoteMetadata.new(action: 'outdated')
note
end
# Called when the title of a Noteable is changed
#
# noteable - Noteable object that responds to `title`
Loading
Loading
Loading
Loading
@@ -32,10 +32,9 @@
- elsif discussion.diff_discussion?
on
= conditional_link_to url.present?, url do
- if discussion.active?
the diff
- else
an outdated diff
- unless discussion.active?
an old version of
the diff
 
= time_ago_with_tooltip(discussion.created_at, placement: "bottom", html_class: "note-created-ago")
= render "discussions/headline", discussion: discussion
Loading
Loading
Loading
Loading
@@ -91,7 +91,7 @@
comparing two versions
- else
viewing an old version
of this merge request.
of the diff.
 
.pull-right
= link_to 'Show latest version', diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'btn btn-sm'
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddChangePositionToNotes < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
# When a migration requires downtime you **must** uncomment the following
# constant and define a short and easy to understand explanation as to why the
# migration requires downtime.
# DOWNTIME_REASON = ''
# When using the methods "add_concurrent_index", "remove_concurrent_index" or
# "add_column_with_default" you must disable the use of transactions
# as these methods can not run in an existing transaction.
# When using "add_concurrent_index" or "remove_concurrent_index" methods make sure
# that either of them is the _only_ method called in the migration,
# any other changes should go in a separate migration.
# This ensures that upon failure _only_ the index creation or removing fails
# and can be retried or reverted easily.
#
# To disable transactions uncomment the following line and remove these
# comments:
# disable_ddl_transaction!
def change
add_column :notes, :change_position, :text
end
end
Loading
Loading
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
 
ActiveRecord::Schema.define(version: 20170518231126) do
ActiveRecord::Schema.define(version: 20170521184006) do
 
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
Loading
Loading
@@ -794,6 +794,7 @@ ActiveRecord::Schema.define(version: 20170518231126) do
t.string "discussion_id"
t.text "note_html"
t.integer "cached_markdown_version"
t.text "change_position"
end
 
add_index "notes", ["author_id"], name: "index_notes_on_author_id", using: :btree
Loading
Loading
Loading
Loading
@@ -24,6 +24,14 @@ module Gitlab
@diff_files ||= @diffs.decorate! { |diff| decorate_diff!(diff) }
end
 
def diff_file_with_old_path(old_path)
diff_files.find { |diff_file| diff_file.old_path == old_path }
end
def diff_file_with_new_path(new_path)
diff_files.find { |diff_file| diff_file.new_path == new_path }
end
private
 
def decorate_diff!(diff)
Loading
Loading
Loading
Loading
@@ -12,20 +12,26 @@ module Gitlab
attr_reader :head_sha
 
def initialize(attrs = {})
if diff_file = attrs[:diff_file]
attrs[:diff_refs] = diff_file.diff_refs
attrs[:old_path] = diff_file.old_path
attrs[:new_path] = diff_file.new_path
end
if diff_refs = attrs[:diff_refs]
attrs[:base_sha] = diff_refs.base_sha
attrs[:start_sha] = diff_refs.start_sha
attrs[:head_sha] = diff_refs.head_sha
end
@old_path = attrs[:old_path]
@new_path = attrs[:new_path]
@base_sha = attrs[:base_sha]
@start_sha = attrs[:start_sha]
@head_sha = attrs[:head_sha]
@old_line = attrs[:old_line]
@new_line = attrs[:new_line]
if attrs[:diff_refs]
@base_sha = attrs[:diff_refs].base_sha
@start_sha = attrs[:diff_refs].start_sha
@head_sha = attrs[:diff_refs].head_sha
else
@base_sha = attrs[:base_sha]
@start_sha = attrs[:start_sha]
@head_sha = attrs[:head_sha]
end
end
 
# `Gitlab::Diff::Position` objects are stored as serialized attributes in
Loading
Loading
@@ -129,11 +135,11 @@ module Gitlab
end
 
def diff_line(repository)
@diff_line ||= diff_file(repository).line_for_position(self)
@diff_line ||= diff_file(repository)&.line_for_position(self)
end
 
def line_code(repository)
@line_code ||= diff_file(repository).line_code_for_position(self)
@line_code ||= diff_file(repository)&.line_code_for_position(self)
end
 
private
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment