diff --git a/CHANGELOG b/CHANGELOG index 981fd0e77abfadb8ebe6a0cc724110dd165f5310..2e5cd78c8a8a7c9bf099c519fecf9e6d4ab2a1bc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +v 6.5.1 + - Fix branch selectbox when create merge request from fork + v 6.5.0 - Dropdown menus on issue#show page for assignee and milestone (Jason Blanchard) - Add color custimization and previewing to broadcast messages diff --git a/VERSION b/VERSION index f22d756da39d4c5f688b56060df98ea326cc0376..13673ab4fa39960e175d96c11b917fb90b53c574 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.5.0 +6.5.1-001 diff --git a/app/assets/stylesheets/sections/diff.scss b/app/assets/stylesheets/sections/diff.scss new file mode 100644 index 0000000000000000000000000000000000000000..eb272f20f40c23d5b1e473b85bd642c70f5ad27f --- /dev/null +++ b/app/assets/stylesheets/sections/diff.scss @@ -0,0 +1,350 @@ +.diff-file { + border: 1px solid #CCC; + margin-bottom: 1em; + + .diff-header { + @extend .clearfix; + background: #DDD; + border-bottom: 1px solid #CCC; + padding: 5px 5px 5px 10px; + color: #555; + + > span { + font-family: $monospace_font; + font-size: 14px; + line-height: 2; + } + + .diff-btn-group { + float: right; + + .btn { + background-color: #EEE; + color: #666; + font-weight: bolder; + } + } + + .commit-short-id { + font-family: $monospace_font; + font-size: smaller; + } + + .file-mode { + font-family: $monospace_font; + } + } + .diff-content { + overflow: auto; + overflow-y: hidden; + background: #FFF; + color: #333; + font-size: 12px; + .old { + span.idiff { + background-color: #FAA; + } + } + .new { + span.idiff { + background-color: #AFA; + } + } + + table { + width: 100%; + font-family: $monospace_font; + border: none; + margin: 0px; + padding: 0px; + td { + line-height: 18px; + font-size: 12px; + } + } + + .text-file-parallel div { + display: inline-block; + padding-bottom: 16px; + } + .diff-side { + overflow-x: scroll; + width: 508px; + height: 700px; + } + .diff-side.diff-side-left{ + overflow-y:hidden; + } + .diff-side table, td.diff-middle table { + height: 700px; + } + .diff-middle { + width: 114px; + vertical-align: top; + height: 700px; + overflow: hidden + } + + .old_line, .new_line, .diff_line { + margin: 0px; + padding: 0px; + border: none; + background: #EEE; + color: #666; + padding: 0px 5px; + border-right: 1px solid #ccc; + text-align: right; + min-width: 35px; + max-width: 50px; + width: 35px; + @include user-select(none); + a { + float: left; + width: 35px; + font-weight: normal; + color: #666; + &:hover { + text-decoration: underline; + } + } + &.new { + background: #CFD; + } + &.old { + background: #FDD; + } + } + .diff_line { + padding: 0; + } + .line_holder { + &.old .old_line, + &.old .new_line { + background: #FCC; + border-color: #E7BABA; + } + &.new .old_line, + &.new .new_line { + background: #CFC; + border-color: #B9ECB9; + } + } + .line_content { + display: block; + white-space: pre; + height: 18px; + margin: 0px; + padding: 0px 0.5em; + border: none; + &.new { + background: #CFD; + } + &.old { + background: #FDD; + } + &.matched { + color: #ccc; + background: #fafafa; + } + &.parallel { + display: table-cell; + } + } + } + .image { + background: #ddd; + text-align: center; + padding: 30px; + .wrap{ + display: inline-block; + } + + .frame { + display: inline-block; + background-color: #fff; + line-height: 0; + img{ + border: 1px solid #FFF; + background: image-url('trans_bg.gif'); + max-width: 100%; + } + &.deleted { + border: 1px solid $deleted; + } + + &.added { + border: 1px solid $added; + } + } + .image-info{ + font-size: 12px; + margin: 5px 0 0 0; + color: grey; + } + + .view.swipe{ + position: relative; + + .swipe-frame{ + display: block; + margin: auto; + position: relative; + } + .swipe-wrap{ + overflow: hidden; + border-left: 1px solid #999; + position: absolute; + display: block; + top: 13px; + right: 7px; + } + .frame{ + top: 0; + right: 0; + position: absolute; + &.deleted{ + margin: 0; + display: block; + top: 13px; + right: 7px; + } + } + .swipe-bar{ + display: block; + height: 100%; + width: 15px; + z-index: 100; + position: absolute; + cursor: pointer; + &:hover{ + .top-handle{ + background-position: -15px 3px; + } + .bottom-handle{ + background-position: -15px -11px; + } + }; + .top-handle{ + display: block; + height: 14px; + width: 15px; + position: absolute; + top: 0px; + background: image-url('swipemode_sprites.gif') 0 3px no-repeat; + } + .bottom-handle{ + display: block; + height: 14px; + width: 15px; + position: absolute; + bottom: 0px; + background: image-url('swipemode_sprites.gif') 0 -11px no-repeat; + } + } + } //.view.swipe + .view.onion-skin{ + .onion-skin-frame{ + display: block; + margin: auto; + position: relative; + } + .frame.added, .frame.deleted { + position: absolute; + display: block; + top: 0px; + left: 0px; + } + .controls{ + display: block; + height: 14px; + width: 300px; + z-index: 100; + position: absolute; + bottom: 0px; + left: 50%; + margin-left: -150px; + + .drag-track{ + display: block; + position: absolute; + left: 12px; + height: 10px; + width: 276px; + background: image-url('onion_skin_sprites.gif') -4px -20px repeat-x; + } + + .dragger { + display: block; + position: absolute; + left: 0px; + top: 0px; + height: 14px; + width: 14px; + background: image-url('onion_skin_sprites.gif') 0px -34px repeat-x; + cursor: pointer; + } + + .transparent { + display: block; + position: absolute; + top: 2px; + right: 0px; + height: 10px; + width: 10px; + background: image-url('onion_skin_sprites.gif') -2px 0px no-repeat; + } + + .opaque { + display: block; + position: absolute; + top: 2px; + left: 0px; + height: 10px; + width: 10px; + background: image-url('onion_skin_sprites.gif') -2px -10px no-repeat; + } + } + } //.view.onion-skin + } + .view-modes{ + + padding: 10px; + text-align: center; + + background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf)); + background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf); + background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf); + background-image: -ms-linear-gradient(#eee 6.6%, #dfdfdf); + background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf); + + ul, li{ + list-style: none; + margin: 0; + padding: 0; + display: inline-block; + } + + li{ + color: grey; + border-left: 1px solid #c1c1c1; + padding: 0 12px 0 16px; + cursor: pointer; + &:first-child{ + border-left: none; + } + &:hover{ + text-decoration: underline; + } + &.active{ + &:hover{ + text-decoration: none; + } + cursor: default; + color: #333; + } + &.disabled{ + display: none; + } + } + } +} diff --git a/app/assets/stylesheets/sections/merge_requests.scss b/app/assets/stylesheets/sections/merge_requests.scss index d3462d6aaa7fd37f94ac18a07c2dc7fa07c81e44..22ee2f818240171d16f9e0c474d2b090c162b8c1 100644 --- a/app/assets/stylesheets/sections/merge_requests.scss +++ b/app/assets/stylesheets/sections/merge_requests.scss @@ -102,3 +102,8 @@ } } } + +.merge-request-review-button { + float: left; + margin: 0 8px 15px 0; +} diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 0792dbf041f35c1c511d2d4e2fd77e2be59d8151..9d2e4ddd19ba0a84b48e0cf97b93b4d0f138fbf8 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -2,7 +2,7 @@ require 'gitlab/satellite/satellite' class Projects::MergeRequestsController < Projects::ApplicationController before_filter :module_enabled - before_filter :merge_request, only: [:edit, :update, :show, :diffs, :automerge, :automerge_check, :ci_status] + before_filter :merge_request, only: [:edit, :update, :show, :diffs, :automerge, :automerge_check, :ci_status, :accept_without_merge, :reject, :mark_fixed] before_filter :closes_issues, only: [:edit, :update, :show, :diffs] before_filter :validates_merge_request, only: [:show, :diffs] before_filter :define_show_vars, only: [:show, :diffs] @@ -26,8 +26,16 @@ class Projects::MergeRequestsController < Projects::ApplicationController @sort = params[:sort].humanize assignee_id, milestone_id = params[:assignee_id], params[:milestone_id] + assigned_group_id, created_group_id = params[:assigned_group_id], params[:created_group_id] @assignee = @project.team.find(assignee_id) if assignee_id.present? && !assignee_id.to_i.zero? @milestone = @project.milestones.find(milestone_id) if milestone_id.present? && !milestone_id.to_i.zero? + @user_groups = current_user.users_groups + @assigned_group = @user_groups.where(group_id: assigned_group_id).first.group if assigned_group_id.present? + @created_group = @user_groups.where(group_id: created_group_id).first.group if created_group_id.present? + # Merge request states, which will be available in the corresponding filter. + # We want to display all states, which displayed in the list MRs can have (i.e. everything what can be merged). + @available_states = MergeRequest::VALID_STATES_FOR_MERGE + @mr_state = params[:mr_state] end def show @@ -137,6 +145,31 @@ class Projects::MergeRequestsController < Projects::ApplicationController end end + def change_review_status(appropriate_current_statuses, change_status_method, review_action) + return access_denied! unless allowed_to_review? + + if appropriate_current_statuses.include?(@merge_request.state) + @merge_request.send(change_status_method) + @status = true + else + @status = false + end + @review_action = review_action + render "_review" + end + + def accept_without_merge + change_review_status(MergeRequest::VALID_STATES_FOR_ACCEPT, :accept, 'accept (without merge)') + end + + def reject + change_review_status(MergeRequest::VALID_STATES_FOR_REJECT, :reject, 'reject') + end + + def mark_fixed + change_review_status(MergeRequest::VALID_STATES_FOR_MARK_FIXED, :mark_fixed, 'mark as fixed') + end + def branch_from #This is always source @source_project = @merge_request.nil? ? @project : @merge_request.source_project @@ -152,6 +185,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController @target_project = selected_target_project @target_branches = @target_project.repository.branch_names @target_branches + + respond_to do |format| + format.js + end end def ci_status @@ -214,17 +251,35 @@ class Projects::MergeRequestsController < Projects::ApplicationController @commits = @merge_request.commits @allowed_to_merge = allowed_to_merge? - @show_merge_controls = @merge_request.opened? && @commits.any? && @allowed_to_merge + @show_merge_controls = MergeRequest::VALID_STATES_FOR_MERGE.include?(@merge_request.state) && @merge_request.opened? && @commits.any? && @allowed_to_merge + + @allowed_to_review = allowed_to_review? + @show_accept_button = MergeRequest::VALID_STATES_FOR_ACCEPT.include?(@merge_request.state) && @allowed_to_review + @show_reject_button = MergeRequest::VALID_STATES_FOR_REJECT.include?(@merge_request.state) && @allowed_to_review + @show_mark_fixed_button = MergeRequest::VALID_STATES_FOR_MARK_FIXED.include?(@merge_request.state) && @allowed_to_review + + @target_type = :merge_request + @target_id = @merge_request.id + + @protected_source_branch = project.protected_branch?(@merge_request.source_branch) end def allowed_to_merge? - action = if project.protected_branch?(@merge_request.target_branch) + action = if @merge_request.assignee == current_user + :push_code + elsif project.protected_branch?(@merge_request.target_branch) :push_code_to_protected_branches else :push_code end - can?(current_user, action, @project) + can_push_code = can?(current_user, action, @project) + can_merge = can?(current_user, :merge_merge_request, @project) + return can_push_code && can_merge + end + + def allowed_to_review? + can?(current_user, :review_merge_request, @project) end def invalid_mr diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index 663369e45840443129ebab1d294be060043fe2a1..f8ec8e10fd1b5037a9c90cda82e1e5a343f34444 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -105,8 +105,80 @@ module CommitsHelper branches.sort.map { |branch| link_to(branch, project_tree_path(project, branch)) }.join(", ").html_safe end - def get_old_file(project, commit, diff) - project.repository.blob_at(commit.parent_id, diff.old_path) if commit.parent_id + def parallel_diff_lines(project, commit, diff, file) + old_file = project.repository.blob_at(commit.parent_id, diff.old_path) if commit.parent_id + deleted_lines = {} + added_lines = {} + each_diff_line(diff, 0) do |line, type, line_code, line_new, line_old| + if type == "old" + deleted_lines[line_old] = { line_code: line_code, type: type, line: line } + elsif type == "new" + added_lines[line_new] = { line_code: line_code, type: type, line: line } + end + end + max_length = old_file ? old_file.sloc + added_lines.length : file.sloc + + offset1 = 0 + offset2 = 0 + old_lines = [] + new_lines = [] + + max_length.times do |line_index| + line_index1 = line_index - offset1 + line_index2 = line_index - offset2 + deleted_line = deleted_lines[line_index1 + 1] + added_line = added_lines[line_index2 + 1] + old_line = old_file.lines[line_index1] if old_file + new_line = file.lines[line_index2] + + if deleted_line && added_line + elsif deleted_line + new_line = nil + offset2 += 1 + elsif added_line + old_line = nil + offset1 += 1 + end + + old_lines[line_index] = DiffLine.new + new_lines[line_index] = DiffLine.new + + # old + if line_index == 0 && diff.new_file + old_lines[line_index].type = :file_created + old_lines[line_index].content = 'File was created' + elsif deleted_line + old_lines[line_index].type = :deleted + old_lines[line_index].content = old_line + old_lines[line_index].num = line_index1 + 1 + old_lines[line_index].code = deleted_line[:line_code] + elsif old_line + old_lines[line_index].type = :no_change + old_lines[line_index].content = old_line + old_lines[line_index].num = line_index1 + 1 + else + old_lines[line_index].type = :added + end + + # new + if line_index == 0 && diff.deleted_file + new_lines[line_index].type = :file_deleted + new_lines[line_index].content = "File was deleted" + elsif added_line + new_lines[line_index].type = :added + new_lines[line_index].num = line_index2 + 1 + new_lines[line_index].content = new_line + new_lines[line_index].code = added_line[:line_code] + elsif new_line + new_lines[line_index].type = :no_change + new_lines[line_index].num = line_index2 + 1 + new_lines[line_index].content = new_line + else + new_lines[line_index].type = :deleted + end + end + + return old_lines, new_lines end protected diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index 5e3f82fe9ceffdc29bc6fd96569a692f8d95cbdc..5ce8f814ee78d9f0eeb1c71fe8dc2b70a026c943 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -19,7 +19,7 @@ module MergeRequestsHelper target_project_id: target_project.id, source_branch: event.branch_name, target_branch: target_project.repository.root_ref, - title: event.branch_name.titleize + title: event.branch_name } end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index a6a507360bdf060f7850310dab7ff2469b112aa1..32796a969029747c8bbd7a0a9054bb8bbeea2a0b 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -66,7 +66,11 @@ module ProjectsHelper def project_filter_path(options={}) exist_opts = { + assignee_id: params[:assignee_id], + assigned_group_id: params[:assigned_group_id], + created_group_id: params[:created_group_id], state: params[:state], + mr_state: params[:mr_state], scope: params[:scope], label_name: params[:label_name], milestone_id: params[:milestone_id], diff --git a/app/mailers/emails/merge_requests.rb b/app/mailers/emails/merge_requests.rb index 25b8bf755e2d6660709fd70d11f1e586f803e338..e839d509ddad4cab26865ba7cbe59d396ab1342a 100644 --- a/app/mailers/emails/merge_requests.rb +++ b/app/mailers/emails/merge_requests.rb @@ -2,28 +2,36 @@ module Emails module MergeRequests def new_merge_request_email(recipient_id, merge_request_id) @merge_request = MergeRequest.find(merge_request_id) + @author = User.find(@merge_request.author_id) @project = @merge_request.project - mail(to: recipient(recipient_id), subject: subject("New merge request ##{@merge_request.iid}", @merge_request.title)) + mail(to: recipient(recipient_id), subject: subject("#{@author.name} created merge request !#{@merge_request.iid}", @merge_request.title)) end - def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id) + def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id, assigned_by) @merge_request = MergeRequest.find(merge_request_id) @previous_assignee = User.find_by_id(previous_assignee_id) if previous_assignee_id @project = @merge_request.project - mail(to: recipient(recipient_id), subject: subject("Changed merge request ##{@merge_request.iid}", @merge_request.title)) + mail(to: recipient(recipient_id), subject: subject("#{assigned_by.name} reassigned !#{@merge_request.iid} | #{@merge_request.title} to #{@merge_request.assignee.name}")) end def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id) @merge_request = MergeRequest.find(merge_request_id) @updated_by = User.find updated_by_user_id @project = @merge_request.project - mail(to: recipient(recipient_id), subject: subject("Closed merge request ##{@merge_request.iid}", @merge_request.title)) + mail(to: recipient(recipient_id), subject: subject("#{@updated_by.name} closed merge request !#{@merge_request.iid}", @merge_request.title)) end - def merged_merge_request_email(recipient_id, merge_request_id) + def reviewed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id, action) + @merge_request = MergeRequest.find(merge_request_id) + @updated_by = User.find updated_by_user_id + @action = action + mail(to: recipient(recipient_id), subject: subject("#{@updated_by.name} #{action} !#{@merge_request.iid}", @merge_request.title)) + end + + def merged_merge_request_email(recipient_id, merge_request_id, merged_by) @merge_request = MergeRequest.find(merge_request_id) @project = @merge_request.project - mail(to: recipient(recipient_id), subject: subject("Accepted merge request ##{@merge_request.iid}", @merge_request.title)) + mail(to: recipient(recipient_id), subject: subject("#{merged_by.name} merged !#{@merge_request.iid}", @merge_request.title)) end end diff --git a/app/mailers/emails/notes.rb b/app/mailers/emails/notes.rb index e967cf6dc739cb7f4257caf3bd456edb44bcaeaa..6630672bd2473a32f231ea08e9e8b4ac28583c1c 100644 --- a/app/mailers/emails/notes.rb +++ b/app/mailers/emails/notes.rb @@ -18,7 +18,7 @@ module Emails @note = Note.find(note_id) @merge_request = @note.noteable @project = @note.project - mail(to: recipient(recipient_id), subject: subject("Note for merge request ##{@merge_request.iid}")) + mail(to: recipient(recipient_id), subject: subject("#{@note.author_name} commented on ##{@merge_request.iid}", @merge_request.title)) end def note_wall_email(recipient_id, note_id) diff --git a/app/models/ability.rb b/app/models/ability.rb index cf925141f2d193b4cb4a1080f8f2d33fd8cdc843..65e5b00950f55026093785c68a346b3690b6e38c 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -61,6 +61,9 @@ class Ability if team.masters.include?(user) rules += project_master_rules + elsif team.developers_with_merge.include?(user) + rules += project_dev_with_merge_rules + elsif team.developers.include?(user) rules += project_dev_rules @@ -125,7 +128,15 @@ class Ability project_report_rules + [ :write_merge_request, :write_wiki, - :push_code + :push_code, + :modify_merge_request, + :review_merge_request + ] + end + + def project_dev_with_merge_rules + project_dev_rules + [ + :merge_merge_request ] end @@ -144,7 +155,7 @@ class Ability :push_code_to_protected_branches, :modify_issue, :modify_project_snippet, - :modify_merge_request, + :merge_merge_request, :admin_issue, :admin_milestone, :admin_project_snippet, diff --git a/app/models/diff_line.rb b/app/models/diff_line.rb new file mode 100644 index 0000000000000000000000000000000000000000..ad37945874a69fd21511b0646f1344273e9a6b17 --- /dev/null +++ b/app/models/diff_line.rb @@ -0,0 +1,3 @@ +class DiffLine + attr_accessor :type, :content, :num, :code +end diff --git a/app/models/event.rb b/app/models/event.rb index ddb863c1be24ab1ca0777f90ffd4e33b336f4662..40af22fda036378461a3ca214b5f2074005e51b9 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -29,6 +29,9 @@ class Event < ActiveRecord::Base MERGED = 7 JOINED = 8 # User joined project LEFT = 9 # User left project + ACCEPTED = 101 # User accepted merge request + REJECTED = 102 # User rejected merge request + FIXED = 103 # User fixed merge request delegate :name, :email, to: :author, prefix: true, allow_nil: true delegate :title, to: :issue, prefix: true, allow_nil: true @@ -141,6 +144,18 @@ class Event < ActiveRecord::Base action == LEFT end + def accepted? + action == ACCEPTED + end + + def rejected? + action == REJECTED + end + + def fixed? + action == FIXED + end + def membership_changed? joined? || left? end @@ -162,6 +177,12 @@ class Event < ActiveRecord::Base 'joined' elsif left? 'left' + elsif accepted? + 'accepted (without merge)' + elsif rejected? + 'rejected' + elsif fixed? + 'fixed' else "opened" end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index da7aebd944f48f1de664ae3191d2da7494c67b44..5fce34cc5bdb1743686ed422345bb6a57d7a9dfd 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -41,17 +41,30 @@ class MergeRequest < ActiveRecord::Base state_machine :state, initial: :opened do event :close do - transition [:reopened, :opened] => :closed + transition [:reopened, :opened, :accepted, :rejected, :fixed] => :closed end event :merge do - transition [:reopened, :opened] => :merged + transition [:reopened, :opened, :accepted, :rejected, :fixed] => :merged + end + + event :accept do + transition [:reopened, :opened, :fixed, :rejected] => :accepted + end + + event :reject do + transition [:reopened, :opened, :fixed, :accepted] => :rejected + end + + event :mark_fixed do + transition [:rejected] => :fixed end event :reopen do transition closed: :reopened end + # New (not reviewed merge request) state :opened state :reopened @@ -59,6 +72,15 @@ class MergeRequest < ActiveRecord::Base state :closed state :merged + + # Reviewed and without issues which should be fixed, but not merged yet + state :accepted + + # Reviewed and containing some issues + state :rejected + + # Fixed after review + state :fixed end state_machine :merge_status, initial: :unchecked do @@ -92,7 +114,8 @@ class MergeRequest < ActiveRecord::Base scope :of_group, ->(group) { where("source_project_id in (:group_project_ids) OR target_project_id in (:group_project_ids)", group_project_ids: group.project_ids) } scope :of_user_team, ->(team) { where("(source_project_id in (:team_project_ids) OR target_project_id in (:team_project_ids) AND assignee_id in (:team_member_ids))", team_project_ids: team.project_ids, team_member_ids: team.member_ids) } - scope :opened, -> { with_state(:opened) } + # Hack: we want the review states ('accepted', 'rejected', 'fixed') to behave in the same way as the 'opened' one + scope :opened, -> { with_states(:opened, :accepted, :rejected, :fixed) } scope :closed, -> { with_state(:closed) } scope :merged, -> { with_state(:merged) } scope :by_branch, ->(branch_name) { where("(source_branch LIKE :branch) OR (target_branch LIKE :branch)", branch: branch_name) } @@ -104,6 +127,16 @@ class MergeRequest < ActiveRecord::Base # both merged and closed mr's scope :closed, -> { with_states(:closed, :merged) } + VALID_STATES_FOR_MERGE = ["opened", "reopened", "accepted", "rejected", "fixed", "closed", "merged"] + VALID_STATES_FOR_ACCEPT = ["opened", "reopened", "rejected", "fixed"] + VALID_STATES_FOR_REJECT = ["opened", "reopened", "accepted", "fixed"] + VALID_STATES_FOR_MARK_FIXED = ["rejected"] + + # Hack: we want the review states ('accepted', 'rejected', 'fixed') to behave in the same way as the 'opened' one + def opened? + return state == "opened" || accepted? || rejected? || fixed? + end + def validate_branches if target_project==source_project && target_branch == source_branch errors.add :branch_conflict, "You can not use same project/branch for source and target" @@ -328,6 +361,10 @@ class MergeRequest < ActiveRecord::Base message end + def project + target_project + end + private def dump_commits(commits) diff --git a/app/models/project_team.rb b/app/models/project_team.rb index 5630f280aea13841b6ecb6ca9c20a22152ba4038..956944a86e62c0c7eb671c0a04dede4107754d25 100644 --- a/app/models/project_team.rb +++ b/app/models/project_team.rb @@ -84,6 +84,10 @@ class ProjectTeam @developers ||= fetch_members(:developers) end + def developers_with_merge + @developers_with_merge ||= fetch_members(:developers_with_merge) + end + def masters @masters ||= fetch_members(:masters) end diff --git a/app/models/users_group.rb b/app/models/users_group.rb index 181bf3222832b98d26f84938d95b335fa9c1e8bd..1a14100e43a916f68a89accd65f48bbca7dff0b1 100644 --- a/app/models/users_group.rb +++ b/app/models/users_group.rb @@ -27,6 +27,7 @@ class UsersGroup < ActiveRecord::Base scope :guests, -> { where(group_access: GUEST) } scope :reporters, -> { where(group_access: REPORTER) } scope :developers, -> { where(group_access: DEVELOPER) } + scope :developers_with_merge, -> { where(group_access: DEVELOPER_WITH_MERGE) } scope :masters, -> { where(group_access: MASTER) } scope :owners, -> { where(group_access: OWNER) } diff --git a/app/models/users_project.rb b/app/models/users_project.rb index 6f147859a5cf538184b7dba91e7cc31944073136..8ac2eaab612c776732771866b1401bf56b9f630d 100644 --- a/app/models/users_project.rb +++ b/app/models/users_project.rb @@ -31,6 +31,7 @@ class UsersProject < ActiveRecord::Base scope :guests, -> { where(project_access: GUEST) } scope :reporters, -> { where(project_access: REPORTER) } scope :developers, -> { where(project_access: DEVELOPER) } + scope :developers_with_merge, -> { where(project_access: DEVELOPER_WITH_MERGE) } scope :masters, -> { where(project_access: MASTER) } scope :in_project, ->(project) { where(project_id: project.id) } diff --git a/app/observers/merge_request_observer.rb b/app/observers/merge_request_observer.rb index 0ac555fce7c640b57da32f3dbd90608cbf060cdc..2131c4da82580eda11c661485f3c3488772e3be7 100644 --- a/app/observers/merge_request_observer.rb +++ b/app/observers/merge_request_observer.rb @@ -19,7 +19,7 @@ class MergeRequestObserver < ActivityObserver end def after_merge(merge_request, transition) - notification.merge_mr(merge_request) + notification.merge_mr(merge_request, current_user) # Since MR can be merged via sidekiq # to prevent event duplication do this check return true if merge_request.merge_event @@ -35,6 +35,21 @@ class MergeRequestObserver < ActivityObserver execute_hooks(merge_request) end + def after_accept(merge_request, transition) + notification.review_mr(merge_request, current_user, 'accepted (without merge)') + create_event(merge_request, Event::ACCEPTED) + end + + def after_reject(merge_request, transition) + notification.review_mr(merge_request, current_user, 'rejected') + create_event(merge_request, Event::REJECTED) + end + + def after_mark_fixed(merge_request, transition) + notification.review_mr(merge_request, current_user, 'fixed') + create_event(merge_request, Event::FIXED) + end + def after_reopen(merge_request, transition) create_event(merge_request, Event::REOPENED) create_note(merge_request) diff --git a/app/services/filtering_service.rb b/app/services/filtering_service.rb index b339065890bc7c01f0863478afc59581655c8cd1..7ee61d7db94c611faeb31299eea8013a76987686 100644 --- a/app/services/filtering_service.rb +++ b/app/services/filtering_service.rb @@ -31,6 +31,9 @@ class FilteringService items = by_search(items) items = by_milestone(items) items = by_assignee(items) + items = by_assigned_group_id(items) + items = by_created_group_id(items) + items = by_mr_state(items) items = by_label(items) items = sort(items) end @@ -113,6 +116,30 @@ class FilteringService items end + def by_assigned_group_id(items) + if params[:assigned_group_id].present? + items = items.joins(assignee: :users_groups).where(users_groups: {group_id: params[:assigned_group_id]}) + end + + items + end + + def by_created_group_id(items) + if params[:created_group_id].present? + items = items.joins(author: :users_groups).where(users_groups: {group_id: params[:created_group_id]}) + end + + items + end + + def by_mr_state(items) + if params[:mr_state].present? + items = items.where(state: params[:mr_state]) + end + + items + end + def by_label(items) if params[:label_name].present? items = items.tagged_with(params[:label_name]) diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index ebbb556b63cff322419d4d218bb9a90981eceba4..e0e7f13f6fff94050c3d39f0a52164aa7e356ffe 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -73,18 +73,28 @@ class NotificationService close_resource_email(merge_request, merge_request.target_project, current_user, 'closed_merge_request_email') end + # When we change a merge request's review state we should send next emails: + # + # * merge_request author if his notification level is not Disabled + # * merge_request assignee if his notification level is not Disabled + # * project team members with notification level higher then Participating + # + def review_mr(merge_request, current_user, action) + review_mr_email(merge_request, merge_request.target_project, current_user, action) + end + # When we merge a merge request we should send next emails: # # * merge_request author if their notification level is not Disabled # * merge_request assignee if their notification level is not Disabled # * project team members with notification level higher then Participating # - def merge_mr(merge_request) + def merge_mr(merge_request, current_user) recipients = reject_muted_users([merge_request.author, merge_request.assignee], merge_request.target_project) recipients = recipients.concat(project_watchers(merge_request.target_project)).uniq recipients.each do |recipient| - mailer.merged_merge_request_email(recipient.id, merge_request.id) + mailer.merged_merge_request_email(recipient.id, merge_request.id, current_user) end end @@ -227,6 +237,16 @@ class NotificationService end end + def review_mr_email(target, project, current_user, action) + recipients = reject_muted_users([target.author, target.assignee], project) + recipients = recipients.concat(project_watchers(project)).uniq + recipients.delete(current_user) + + recipients.each do |recipient| + mailer.send('reviewed_merge_request_email', recipient.id, target.id, current_user.id, action) + end + end + def close_resource_email(target, project, current_user, method) recipients = reject_muted_users([target.author, target.assignee], project) recipients = recipients.concat(project_watchers(project)).uniq @@ -250,7 +270,7 @@ class NotificationService recipients.delete(current_user) recipients.each do |recipient| - mailer.send(method, recipient.id, target.id, target.assignee_id_was) + mailer.send(method, recipient.id, target.id, target.assignee_id_was, current_user) end end diff --git a/app/views/help/permissions.html.haml b/app/views/help/permissions.html.haml index 15e3bf3a135c755af28a4b9999fde3abe7a8a811..11a6521c1eee46e9f467c0fd8c2bc2d9aeea1348 100644 --- a/app/views/help/permissions.html.haml +++ b/app/views/help/permissions.html.haml @@ -13,6 +13,7 @@ %th Guest %th Reporter %th Developer + %th Extended Developer %th Master %th Owner %tbody @@ -172,6 +173,7 @@ %th Guest %th Reporter %th Developer + %th Extended Developer %th Master %th Owner %tbody diff --git a/app/views/notify/reviewed_merge_request_email.html.haml b/app/views/notify/reviewed_merge_request_email.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..681da7c49abf9a368745ea4637f271b5678bd04e --- /dev/null +++ b/app/views/notify/reviewed_merge_request_email.html.haml @@ -0,0 +1,9 @@ +%p + = "Merge Request #{@merge_request.iid} was #{@action} by #{@updated_by.name}" +%p + = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.target_project, @merge_request) +%p + != merge_path_description(@merge_request, '→') +%p + Assignee: #{@merge_request.author_name} → #{@merge_request.assignee_name} + diff --git a/app/views/notify/reviewed_merge_request_email.text.haml b/app/views/notify/reviewed_merge_request_email.text.haml new file mode 100644 index 0000000000000000000000000000000000000000..b3d1134a84c0caab20f70a951c6c5f5704b885a5 --- /dev/null +++ b/app/views/notify/reviewed_merge_request_email.text.haml @@ -0,0 +1,8 @@ += "Merge Request #{@merge_request.iid} was #{@action} by #{@updated_by.name}" + +Merge Request Url: #{project_merge_request_url(@merge_request.target_project, @merge_request)} + += merge_path_description(@merge_request, 'to') + +Author: #{@merge_request.author_name} +Assignee: #{@merge_request.assignee_name} diff --git a/app/views/projects/commits/_parallel_view.html.haml b/app/views/projects/commits/_parallel_view.html.haml index 3234e9da0ac88fa923ee22fb156ea49cdc5e56db..5b60ab80ba45dd8111f49b50225ebde0ce0c28f0 100644 --- a/app/views/projects/commits/_parallel_view.html.haml +++ b/app/views/projects/commits/_parallel_view.html.haml @@ -1,75 +1,55 @@ / Side-by-side diff view -- old_file = get_old_file(project, @commit, diff) -- deleted_lines = {} -- added_lines = {} -- each_diff_line(diff, index) do |line, type, line_code, line_new, line_old, raw_line| - - if type == "old" - - deleted_lines[line_old] = { line_code: line_code, type: type, line: line } - - elsif type == "new" - - added_lines[line_new] = { line_code: line_code, type: type, line: line } - -- max_length = old_file.sloc + added_lines.length if old_file -- max_length ||= file.sloc -- offset1 = 0 -- offset2 = 0 +- old_lines, new_lines = parallel_diff_lines(project, @commit, diff, file) +- num_lines = old_lines.length %div.text-file-parallel - %table{ style: "table-layout: fixed;" } - - max_length.times do |line_index| - - line_index1 = line_index - offset1 - - line_index2 = line_index - offset2 - - deleted_line = deleted_lines[line_index1 + 1] - - added_line = added_lines[line_index2 + 1] - - old_line = old_file.lines[line_index1] if old_file - - new_line = file.lines[line_index2] + %div.diff-side.diff-side-left + %table + - old_lines.each do |line| + + %tr.line_holder.parallel + - if line.type == :file_created + %td.line_content.parallel= "File was created" + - elsif line.type == :deleted + %td.line_content{class: "parallel noteable_line old #{line.code}", "line_code" => line.code }= line.content + - else line.type == :no_change + %td.line_content.parallel= line.content + + %div.diff-middle + %table + - num_lines.times do |index| + %tr + - if old_lines[index].type == :deleted + %td.old_line.old= old_lines[index].num + - else + %td.old_line= old_lines[index].num + + %td.diff_line="" - - if deleted_line && added_line - - elsif deleted_line - - new_line = nil - - offset2 += 1 - - elsif added_line - - old_line = nil - - offset1 += 1 + - if new_lines[index].type == :added + %td.new_line.new= new_lines[index].num + - else + %td.new_line= new_lines[index].num - %tr.line_holder.parallel - - if line_index == 0 && diff.new_file - %td.line_content.parallel= "File was created" - %td.old_line= "" - - elsif deleted_line - %td.line_content{class: "parallel noteable_line old #{deleted_line[:line_code]}", "line_code" => deleted_line[:line_code] }= old_line - %td.old_line.old - = line_index1 + 1 - - if @comments_allowed - =# render "projects/notes/diff_note_link", line_code: deleted_line[:line_code] - - elsif old_line - %td.line_content.parallel= old_line - %td.old_line= line_index1 + 1 - - else - %td.line_content.parallel= "" - %td.old_line= "" + %div.diff-side.diff-side-right + %table + - new_lines.each do |line| - %td.diff_line= "" + %tr.line_holder.parallel + - if line.type == :file_deleted + %td.line_content.parallel= "File was deleted" + - elsif line.type == :added + %td.line_content{class: "parallel noteable_line new #{line.code}", "line_code" => line.code }= line.content + - else line.type == :no_change + %td.line_content.parallel= line.content - - if diff.deleted_file && line_index == 0 - %td.new_line= "" - %td.line_content.parallel= "File was deleted" - - elsif added_line - %td.new_line.new - = line_index2 + 1 - - if @comments_allowed - =# render "projects/notes/diff_note_link", line_code: added_line[:line_code] - %td.line_content{class: "parallel noteable_line new #{added_line[:line_code]}", "line_code" => added_line[:line_code] }= new_line - - elsif new_line - %td.new_line= line_index2 + 1 - %td.line_content.parallel= new_line - - else - %td.new_line= "" - %td.line_content.parallel= "" +:javascript + $('.diff-side-right').on('scroll', function(){ + $('.diff-side-left, .diff-middle').scrollTop($(this).scrollTop()); + $('.diff-side-left').scrollLeft($(this).scrollLeft()); + }); - - if @reply_allowed - - comments1 = [] - - comments2 = [] - - comments1 = @line_notes.select { |n| n.line_code == deleted_line[:line_code] }.sort_by(&:created_at) if deleted_line - - comments2 = @line_notes.select { |n| n.line_code == added_line[:line_code] }.sort_by(&:created_at) if added_line - - unless comments1.empty? && comments2.empty? - = render "projects/notes/diff_notes_with_reply_parallel", notes1: comments1, notes2: comments2, line1: deleted_line, line2: added_line \ No newline at end of file + $('.diff-side-left').on('scroll', function(){ + $('.diff-side-right, .diff-middle').scrollTop($(this).scrollTop()); // might never be relevant + $('.diff-side-right').scrollLeft($(this).scrollLeft()); + }); diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index ff763bca3070609fb4b2ab089b341b9da504a375..9863f1a12633e90e2f84f73869a6472a8384e2fc 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -2,6 +2,7 @@ .merge-request-title %span.light= "##{merge_request.iid}" = link_to_gfm truncate(merge_request.title, length: 80), project_merge_request_path(merge_request.target_project, merge_request), class: "row_title" + %span.light= "(#{merge_request.state})" - if merge_request.merged? %small.pull-right %i.icon-ok diff --git a/app/views/projects/merge_requests/_review.js.haml b/app/views/projects/merge_requests/_review.js.haml new file mode 100644 index 0000000000000000000000000000000000000000..30c05d44ed37da99e7c4849e098a14e5fc1c271e --- /dev/null +++ b/app/views/projects/merge_requests/_review.js.haml @@ -0,0 +1,7 @@ +-if @status + :plain + location.reload(true); +-else + :plain + alert("Failed to #{@review_action} the merge request!"); + diff --git a/app/views/projects/merge_requests/branch_from.js.haml b/app/views/projects/merge_requests/branch_from.js.haml index ec4d7f2121be7ac2211f6fbe3773e055e765ea3c..7079eb50f8eaaf839bacf67a5f579e096427229e 100644 --- a/app/views/projects/merge_requests/branch_from.js.haml +++ b/app/views/projects/merge_requests/branch_from.js.haml @@ -3,5 +3,5 @@ var mrTitle = $('#merge_request_title'); if(mrTitle.val().length == 0) { - mrTitle.val("#{params[:ref].titleize}"); + mrTitle.val("#{params[:ref]}"); } diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml index a525a49015f7269b19c47b2cba21bae6f4fde9df..60582ec51b5cdd7078ec7f6e537e3b81dcdc1fca 100644 --- a/app/views/projects/merge_requests/index.html.haml +++ b/app/views/projects/merge_requests/index.html.haml @@ -37,6 +37,44 @@ = image_tag avatar_icon(user.email), class: "avatar s16", alt: '' = user.name + .dropdown.inline.prepend-left-10 + %a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"} + %i.icon-group + %span.light assigned to group: + - if @assigned_group.present? + %strong= @assigned_group.name + - else + Any + %b.caret + %ul.dropdown-menu + %li + = link_to project_filter_path(assigned_group_id: nil) do + Any + - @user_groups.each do |user_group| + - group = user_group.group + %li + = link_to project_filter_path(assigned_group_id: group.id) do + = group.name + + .dropdown.inline.prepend-left-10 + %a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"} + %i.icon-group + %span.light created by group: + - if @created_group.present? + %strong= @created_group.name + - else + Any + %b.caret + %ul.dropdown-menu + %li + = link_to project_filter_path(created_group_id: nil) do + Any + - @user_groups.each do |user_group| + - group = user_group.group + %li + = link_to project_filter_path(created_group_id: group.id) do + = group.name + .dropdown.inline.prepend-left-10 %a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"} %i.icon-time @@ -60,8 +98,24 @@ %strong= milestone.title %small.light= milestone.expires_at - .pull-right - = render 'shared/sort_dropdown' + .dropdown.inline.prepend-left-10 + %a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"} + %span.light state: + - if @mr_state.present? + %strong= @mr_state.capitalize + - else + Any + %b.caret + %ul.dropdown-menu + %li + = link_to project_filter_path(mr_state: nil) do + Any + - @available_states.each do |mr_state| + %li + = link_to project_filter_path(mr_state: mr_state) do + = mr_state.capitalize + + = render 'shared/sort_dropdown' %ul.well-list.mr-list = render @merge_requests diff --git a/app/views/projects/merge_requests/show/_mr_accept.html.haml b/app/views/projects/merge_requests/show/_mr_accept.html.haml index 352d843f49fb0082ec434cec5d27d908e4a606d7..859bb891e875325bc962b3300914934e08b14376 100644 --- a/app/views/projects/merge_requests/show/_mr_accept.html.haml +++ b/app/views/projects/merge_requests/show/_mr_accept.html.haml @@ -31,11 +31,13 @@ .accept-group .pull-left = f.submit "Accept Merge Request", class: "btn btn-create accept_merge_request" - - unless @merge_request.disallow_source_branch_removal? - .remove_branch_holder.pull-left - = label_tag :should_remove_source_branch, class: "checkbox" do - = check_box_tag :should_remove_source_branch - Remove source-branch + - unless @protected_source_branch + - unless @merge_request.disallow_source_branch_removal? + .remove_branch_holder.pull-left + = label_tag :should_remove_source_branch, class: "checkbox" do + = check_box_tag :should_remove_source_branch + Remove source-branch + .clearfix .automerge_widget.no_satellite.hide @@ -67,3 +69,18 @@ %i.icon-refresh.icon-spin Merge is in progress. Please wait. Page will be automatically reloaded. + +- if @allowed_to_review + - if @show_accept_button + %span.merge-request-review-button + = form_for [:accept_without_merge, @project, @merge_request], remote: true, method: :get do |f| + = f.submit "Accept Without Merge", class: "btn btn-success accept_without_merge" + - if @show_reject_button + %span.merge-request-review-button + = form_for [:reject, @project, @merge_request], remote: true, method: :get do |f| + = f.submit "Reject", class: "btn btn-danger reject" + - if @show_mark_fixed_button + %span.merge-request-review-button + = form_for [:mark_fixed, @project, @merge_request], remote: true, method: :get do |f| + = f.submit "Mark as fixed", class: "btn btn-success mark_fixed" + .clearfix diff --git a/app/views/projects/merge_requests/show/_mr_title.html.haml b/app/views/projects/merge_requests/show/_mr_title.html.haml index 08a3fdf869a6b742991034cb58143efb6d718082..2a04b87241f7e27c9a76fb394964185193d4ab1f 100644 --- a/app/views/projects/merge_requests/show/_mr_title.html.haml +++ b/app/views/projects/merge_requests/show/_mr_title.html.haml @@ -1,5 +1,5 @@ %h3.page-title - = "Merge Request ##{@merge_request.iid}" + = "Merge Request ##{@merge_request.iid} (#{@merge_request.state})" %small created #{time_ago_with_tooltip(@merge_request.created_at)} diff --git a/app/views/projects/merge_requests/update_branches.js.haml b/app/views/projects/merge_requests/update_branches.js.haml index 6a21551e811cfae63db3ed8b536f0d28b7b426de..ca21b3bc0deb365c4db2f3ecc72576c80cd5e4e4 100644 --- a/app/views/projects/merge_requests/update_branches.js.haml +++ b/app/views/projects/merge_requests/update_branches.js.haml @@ -1,5 +1,9 @@ :plain $(".target_branch").html("#{escape_javascript(options_for_select(@target_branches))}"); - $(".target_branch").trigger("select2:updated"); + + $('select.target_branch').select2({ + width: 'resolve', + dropdownAutoWidth: true + }); + $(".mr_target_commit").html(""); - $(".target_branch").trigger("change"); diff --git a/config/routes.rb b/config/routes.rb index 315c339016b6c08b8f00496613a70d108ec68751..3fabb05b4cfde10a56471b24e0fbfcc155512ed5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -267,6 +267,9 @@ Gitlab::Application.routes.draw do get :automerge get :automerge_check get :ci_status + get :accept_without_merge + get :reject + get :mark_fixed end collection do diff --git a/db/migrate/20131022131722_add_accepted_state_to_merge_request.rb b/db/migrate/20131022131722_add_accepted_state_to_merge_request.rb new file mode 100644 index 0000000000000000000000000000000000000000..401ea7fc61b73f74aa5464ca00f8b75081155910 --- /dev/null +++ b/db/migrate/20131022131722_add_accepted_state_to_merge_request.rb @@ -0,0 +1,7 @@ +class AddAcceptedStateToMergeRequest < ActiveRecord::Migration + # We don't need the forward ("up") data migration + def down + # Return all 'accepted' MR's back to the 'opened' state + MergeRequest.where(state: :accepted).update_all(state: :opened) + end +end diff --git a/db/migrate/20131022131759_add_accepted_event.rb b/db/migrate/20131022131759_add_accepted_event.rb new file mode 100644 index 0000000000000000000000000000000000000000..2e463d0535dc989d42dcb831b80f0aa87c6262c4 --- /dev/null +++ b/db/migrate/20131022131759_add_accepted_event.rb @@ -0,0 +1,8 @@ +class AddAcceptedEvent < ActiveRecord::Migration + # We don't need the forward ("up") data migration + def down + # Remove all 'ACCEPTED' events + # 101 == Event::ACCEPTED, but I'm not sure whether that constant is supposed to be available when db:rollback is executed + Event.where(action: 101).delete_all + end +end diff --git a/db/migrate/20131029195936_add_rejected_and_fixed_states_to_merge_request.rb b/db/migrate/20131029195936_add_rejected_and_fixed_states_to_merge_request.rb new file mode 100644 index 0000000000000000000000000000000000000000..80bff7d763765983f25d8e0766a1c7f352bb9860 --- /dev/null +++ b/db/migrate/20131029195936_add_rejected_and_fixed_states_to_merge_request.rb @@ -0,0 +1,7 @@ +class AddRejectedAndFixedStatesToMergeRequest < ActiveRecord::Migration + # We don't need the forward ("up") data migration + def down + # Return all rejected and fixed MR's back to the 'opened' state + MergeRequest.where(state: [:rejected, :fixed]).update_all(state: :opened) + end +end diff --git a/db/migrate/20131029200011_add_rejected_and_fixed_events.rb b/db/migrate/20131029200011_add_rejected_and_fixed_events.rb new file mode 100644 index 0000000000000000000000000000000000000000..512e3e3f95c17029e2428a9f5e2f976b640ff4fe --- /dev/null +++ b/db/migrate/20131029200011_add_rejected_and_fixed_events.rb @@ -0,0 +1,9 @@ +class AddRejectedAndFixedEvents < ActiveRecord::Migration + # We don't need the forward ("up") data migration + def down + # Remove all 'REJECTED' and 'FIXED' events + # 102 == Event::REJECTED + # 103 == Event::FIXED + Event.where(action: [102, 103]).delete_all + end +end diff --git a/db/migrate/20131101150042_update_reviewed_merge_requests.rb b/db/migrate/20131101150042_update_reviewed_merge_requests.rb new file mode 100644 index 0000000000000000000000000000000000000000..1bf00666621c740a2715e7594899b8e70167df7b --- /dev/null +++ b/db/migrate/20131101150042_update_reviewed_merge_requests.rb @@ -0,0 +1,8 @@ +class UpdateReviewedMergeRequests < ActiveRecord::Migration + def up + # Update all merge requests in the review states (:accepted, :rejected, :fixed) + # This is normally done in Project.update_merge_requests(oldrev, newrev, ref, user) via a push hook, + # but only opened MRs were updated before, so we need to update the reviewed ones now + MergeRequest.where(state: [:accepted, :rejected, :fixed]).each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked } + end +end diff --git a/lib/gitlab/access.rb b/lib/gitlab/access.rb index 87f9cfab608ee17c4d659b0385b338654df6b74e..f22d33a39c984c6b07f467753d031168ae80cf97 100644 --- a/lib/gitlab/access.rb +++ b/lib/gitlab/access.rb @@ -5,11 +5,12 @@ # module Gitlab module Access - GUEST = 10 - REPORTER = 20 - DEVELOPER = 30 - MASTER = 40 - OWNER = 50 + GUEST = 10 + REPORTER = 20 + DEVELOPER = 30 + DEVELOPER_WITH_MERGE = 31 + MASTER = 40 + OWNER = 50 class << self def values @@ -21,6 +22,7 @@ module Gitlab "Guest" => GUEST, "Reporter" => REPORTER, "Developer" => DEVELOPER, + "Developer with merge" => DEVELOPER_WITH_MERGE, "Master" => MASTER, } end @@ -36,6 +38,7 @@ module Gitlab guest: GUEST, reporter: REPORTER, developer: DEVELOPER, + developer_with_merge: DEVELOPER_WITH_MERGE, master: MASTER, } end diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab index d1d959e152e101d321c763e734b1997cf595c4b4..cc39bc07cc7295d3e4813103c4d850405fed653b 100644 --- a/lib/support/nginx/gitlab +++ b/lib/support/nginx/gitlab @@ -1,6 +1,6 @@ # GITLAB # Maintainer: @randx -# App Version: 5.0 +# App Version: 6.1 upstream gitlab { server unix:/home/git/gitlab/tmp/sockets/gitlab.socket; diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index b1e5348681686452639f0bb1e9a3d9947631872d..7253af094a2cf6b32ee77b4bb597fd11ac21b92d 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -192,9 +192,10 @@ describe Notify do end describe 'that are reassigned' do + let(:current_user) { create(:user, email: "current@email.com") } before(:each) { merge_request.stub(:assignee_id_was).and_return(previous_assignee.id) } - subject { Notify.reassigned_merge_request_email(recipient.id, merge_request.id, previous_assignee.id) } + subject { Notify.reassigned_merge_request_email(recipient.id, merge_request.id, previous_assignee.id, current_user) } it_behaves_like 'a multiple recipients email'