merge_requests_controller.rb 9.13 KB
Newer Older
1 2
require 'gitlab/satellite/satellite'

3
class Projects::MergeRequestsController < Projects::ApplicationController
4
  before_filter :module_enabled
5 6
  before_filter :merge_request, only: [:edit, :update, :show, :diffs, :automerge, :automerge_check, :ci_status]
  before_filter :closes_issues, only: [:edit, :update, :show, :diffs]
7
  before_filter :validates_merge_request, only: [:show, :diffs]
8
  before_filter :define_show_vars, only: [:show, :diffs]
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
9 10 11 12 13

  # Allow read any merge_request
  before_filter :authorize_read_merge_request!

  # Allow write(create) merge_request
14
  before_filter :authorize_write_merge_request!, only: [:new, :create]
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
15 16

  # Allow modify merge_request
17
  before_filter :authorize_modify_merge_request!, only: [:close, :edit, :update, :sort]
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
18

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
19
  def index
20 21 22 23
    params[:sort] ||= 'newest'
    params[:scope] = 'all' if params[:scope].blank?
    params[:state] = 'opened' if params[:state].blank?

24
    @merge_requests = MergeRequestsFinder.new.execute(current_user, params.merge(project_id: @project.id))
25 26 27
    @merge_requests = @merge_requests.page(params[:page]).per(20)

    @sort = params[:sort].humanize
28 29 30
    assignee_id, milestone_id = params[:assignee_id], params[:milestone_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?
31
    @assignees = User.where(id: @project.merge_requests.pluck(:assignee_id))
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
32 33 34
  end

  def show
35 36
    @note_counts = Note.where(commit_id: @merge_request.commits.map(&:id)).
        group(:commit_id).count
37 38
    respond_to do |format|
      format.html
39 40
      format.diff { render text: @merge_request.to_diff(current_user) }
      format.patch { render text: @merge_request.to_patch(current_user) }
41
    end
randx's avatar
randx committed
42 43
  end

44
  def diffs
45
    @commit = @merge_request.last_commit
46

47
    @comments_allowed = @reply_allowed = true
48 49
    @comments_target = {noteable_type: 'MergeRequest',
                        noteable_id: @merge_request.id}
50
    @line_notes = @merge_request.notes.where("line_code is not null")
51 52 53 54

    diff_line_count = Commit::diff_line_count(@merge_request.diffs)
    @suppress_diff = Commit::diff_suppress?(@merge_request.diffs, diff_line_count) && !params[:force_show_diff]
    @force_suppress_diff = Commit::diff_force_suppress?(@merge_request.diffs, diff_line_count)
55 56 57 58 59

    respond_to do |format|
      format.html
      format.json { render json: { html: view_to_html_string("projects/merge_requests/show/_diffs") } }
    end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
60 61 62
  end

  def new
63 64 65 66
    params[:merge_request] ||= ActionController::Parameters.new(
      source_project: @project
    )

67
    @merge_request = MergeRequest.new(merge_request_params)
Izaak Alpert's avatar
Izaak Alpert committed
68
    @merge_request.source_project = @project unless @merge_request.source_project
69 70 71
    @merge_request.target_project ||= (@project.forked_from_project || @project)
    @target_branches = @merge_request.target_project.nil? ? [] : @merge_request.target_project.repository.branch_names
    @merge_request.target_branch ||= @merge_request.target_project.default_branch
Izaak Alpert's avatar
Izaak Alpert committed
72
    @source_project = @merge_request.source_project
73 74 75 76 77 78 79 80 81 82

    if @merge_request.target_branch && @merge_request.source_branch
      compare_action = Gitlab::Satellite::CompareAction.new(
        current_user,
        @merge_request.target_project,
        @merge_request.target_branch,
        @merge_request.source_project,
        @merge_request.source_branch
      )

83
      @compare_failed = false
84
      @commits = compare_action.commits
85 86 87 88 89 90 91 92 93

      if @commits
        @commits.map! { |commit| Commit.new(commit) }
        @commit = @commits.first
      else
        # false value because failed to get commits from satellite
        @commits = []
        @compare_failed = true
      end
94

95 96 97
      @note_counts = Note.where(commit_id: @commits.map(&:id)).
          group(:commit_id).count

98 99 100 101
      @diffs = compare_action.diffs
      @merge_request.title = @merge_request.source_branch.titleize.humanize
      @target_project = @merge_request.target_project
      @target_repo = @target_project.repository
102 103 104

      diff_line_count = Commit::diff_line_count(@diffs)
      @suppress_diff = Commit::diff_suppress?(@diffs, diff_line_count)
105
      @force_suppress_diff = @suppress_diff
106
    end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
107 108 109
  end

  def edit
Izaak Alpert's avatar
Izaak Alpert committed
110 111
    @source_project = @merge_request.source_project
    @target_project = @merge_request.target_project
112
    @target_branches = @merge_request.target_project.repository.branch_names
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
113 114 115
  end

  def create
116
    @target_branches ||= []
117
    @merge_request = MergeRequests::CreateService.new(project, current_user, merge_request_params).execute
118 119

    if @merge_request.valid?
120
      redirect_to project_merge_request_path(@merge_request.target_project, @merge_request), notice: 'Merge request was successfully created.'
121
    else
Izaak Alpert's avatar
Izaak Alpert committed
122 123 124
      @source_project = @merge_request.source_project
      @target_project = @merge_request.target_project
      render action: "new"
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
125 126 127 128
    end
  end

  def update
129
    @merge_request = MergeRequests::UpdateService.new(project, current_user, merge_request_params).execute(@merge_request)
130

131
    if @merge_request.valid?
132 133 134 135 136 137
      respond_to do |format|
        format.js
        format.html do
          redirect_to [@merge_request.target_project, @merge_request], notice: 'Merge request was successfully updated.'
        end
      end
138
    else
139
      render "edit"
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
140 141 142
    end
  end

Valery Sizov's avatar
Valery Sizov committed
143
  def automerge_check
144
    if @merge_request.unchecked?
Valery Sizov's avatar
Valery Sizov committed
145 146
      @merge_request.check_if_can_be_merged
    end
147
    render json: {merge_status: @merge_request.merge_status_name}
Valery Sizov's avatar
Valery Sizov committed
148 149
  end

randx's avatar
randx committed
150
  def automerge
151 152
    return access_denied! unless allowed_to_merge?

153
    if @merge_request.open? && @merge_request.can_be_merged?
154
      @merge_request.should_remove_source_branch = params[:should_remove_source_branch]
155
      @merge_request.automerge!(current_user, params[:merge_commit_message])
156 157 158 159
      @status = true
    else
      @status = false
    end
randx's avatar
randx committed
160 161
  end

162
  def branch_from
163
    #This is always source
Izaak Alpert's avatar
Izaak Alpert committed
164
    @source_project = @merge_request.nil? ? @project : @merge_request.source_project
165
    @commit = @repository.commit(params[:ref]) if params[:ref].present?
166 167 168
  end

  def branch_to
169
    @target_project = selected_target_project
170
    @commit = @target_project.repository.commit(params[:ref]) if params[:ref].present?
171 172
  end

173 174
  def update_branches
    @target_project = selected_target_project
Izaak Alpert's avatar
Izaak Alpert committed
175
    @target_branches = @target_project.repository.branch_names
176
    @target_branches
177 178 179 180

    respond_to do |format|
      format.js
    end
181 182
  end

183
  def ci_status
184
    status = @merge_request.source_project.ci_service.commit_status(merge_request.last_commit.sha)
185
    response = {status: status}
186 187 188 189

    render json: response
  end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
190 191
  protected

192
  def selected_target_project
193 194 195 196 197
    if @project.id.to_s == params[:target_project_id] || @project.forked_project_link.nil?
      @project
    else
      @project.forked_project_link.forked_from_project
    end
198 199
  end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
200
  def merge_request
skv's avatar
skv committed
201
    @merge_request ||= @project.merge_requests.find_by!(iid: params[:id])
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
202
  end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
203

204 205 206 207
  def closes_issues
    @closes_issues ||= @merge_request.closes_issues
  end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
208
  def authorize_modify_merge_request!
209
    return render_404 unless can?(current_user, :modify_merge_request, @merge_request)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
210 211 212
  end

  def authorize_admin_merge_request!
213
    return render_404 unless can?(current_user, :admin_merge_request, @merge_request)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
214
  end
215 216 217 218

  def module_enabled
    return render_404 unless @project.merge_requests_enabled
  end
219 220

  def validates_merge_request
221 222 223
    # If source project was removed (Ex. mr from fork to origin)
    return invalid_mr unless @merge_request.source_project

224 225 226
    # Show git not found page
    # if there is no saved commits between source & target branch
    if @merge_request.commits.blank?
227 228
      # and if target branch doesn't exist
      return invalid_mr unless @merge_request.target_branch_exists?
229

230 231
      # or if source branch doesn't exist
      return invalid_mr unless @merge_request.source_branch_exists?
232
    end
233 234 235 236
  end

  def define_show_vars
    # Build a note object for comment form
237
    @note = @project.notes.new(noteable: @merge_request)
238 239
    @notes = @merge_request.mr_and_commit_notes.inc_author.fresh
    @discussions = Note.discussions_from_notes(@notes)
240
    @noteable = @merge_request
241

242
    # Get commits from repository
243 244
    # or from cache if already merged
    @commits = @merge_request.commits
245

246
    @merge_request_diff = @merge_request.merge_request_diff
247
    @allowed_to_merge = allowed_to_merge?
248
    @show_merge_controls = @merge_request.open? && @commits.any? && @allowed_to_merge
249
    @source_branch = @merge_request.source_project.repository.find_branch(@merge_request.source_branch).try(:name)
250 251 252
  end

  def allowed_to_merge?
253
    allowed_to_push_code?(project, @merge_request.target_branch)
254 255 256 257 258 259 260
  end

  def invalid_mr
    # Render special view for MR with removed source or target branch
    render 'invalid'
  end

261 262
  def allowed_to_push_code?(project, branch)
    action = if project.protected_branch?(branch)
263 264 265 266 267
               :push_code_to_protected_branches
             else
               :push_code
             end

268
    can?(current_user, action, project)
269
  end
270 271 272 273 274 275 276 277

  def merge_request_params
    params.require(:merge_request).permit(
      :title, :assignee_id, :source_project_id, :source_branch,
      :target_project_id, :target_branch, :milestone_id,
      :state_event, :description, :label_list
    )
  end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
278
end