merge_requests_controller.rb 7.9 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 39
    respond_to do |format|
      format.html
40 41
      format.diff { render text: @merge_request.to_diff(current_user) }
      format.patch { render text: @merge_request.to_patch(current_user) }
42
    end
randx's avatar
randx committed
43 44
  end

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

54 55 56 57
    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
58 59 60
  end

  def new
61 62 63 64 65 66 67 68 69 70 71 72
    params[:merge_request] ||= ActionController::Parameters.new(source_project: @project)
    @merge_request = MergeRequests::BuildService.new(project, current_user, merge_request_params).execute

    @target_branches = if @merge_request.target_project
                         @merge_request.target_project.repository.branch_names
                       else
                         []
                       end

    @target_project = merge_request.target_project
    @source_project = merge_request.source_project
    @commits = @merge_request.compare_commits
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
73
    @commit = @merge_request.compare_commits.last
74 75 76
    @diffs = @merge_request.compare_diffs
    @note_counts = Note.where(commit_id: @commits.map(&:id)).
      group(:commit_id).count
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
77 78 79
  end

  def edit
Izaak Alpert's avatar
Izaak Alpert committed
80 81
    @source_project = @merge_request.source_project
    @target_project = @merge_request.target_project
82
    @target_branches = @merge_request.target_project.repository.branch_names
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
83 84 85
  end

  def create
86
    @target_branches ||= []
87
    @merge_request = MergeRequests::CreateService.new(project, current_user, merge_request_params).execute
88 89

    if @merge_request.valid?
90
      redirect_to project_merge_request_path(@merge_request.target_project, @merge_request), notice: 'Merge request was successfully created.'
91
    else
Izaak Alpert's avatar
Izaak Alpert committed
92 93 94
      @source_project = @merge_request.source_project
      @target_project = @merge_request.target_project
      render action: "new"
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
95 96 97 98
    end
  end

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

101
    if @merge_request.valid?
102 103 104 105 106 107
      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
108
    else
109
      render "edit"
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
110 111 112
    end
  end

Valery Sizov's avatar
Valery Sizov committed
113
  def automerge_check
114
    if @merge_request.unchecked?
Valery Sizov's avatar
Valery Sizov committed
115 116
      @merge_request.check_if_can_be_merged
    end
117
    render json: {merge_status: @merge_request.merge_status_name}
Valery Sizov's avatar
Valery Sizov committed
118 119
  end

randx's avatar
randx committed
120
  def automerge
121 122
    return access_denied! unless allowed_to_merge?

123
    if @merge_request.open? && @merge_request.can_be_merged?
124
      @merge_request.should_remove_source_branch = params[:should_remove_source_branch]
125
      @merge_request.automerge!(current_user, params[:commit_message])
126 127 128 129
      @status = true
    else
      @status = false
    end
randx's avatar
randx committed
130 131
  end

132
  def branch_from
133
    #This is always source
Izaak Alpert's avatar
Izaak Alpert committed
134
    @source_project = @merge_request.nil? ? @project : @merge_request.source_project
135
    @commit = @repository.commit(params[:ref]) if params[:ref].present?
136 137 138
  end

  def branch_to
139
    @target_project = selected_target_project
140
    @commit = @target_project.repository.commit(params[:ref]) if params[:ref].present?
141 142
  end

143 144
  def update_branches
    @target_project = selected_target_project
Izaak Alpert's avatar
Izaak Alpert committed
145
    @target_branches = @target_project.repository.branch_names
146 147 148 149

    respond_to do |format|
      format.js
    end
150 151
  end

152
  def ci_status
153
    status = @merge_request.source_project.ci_service.commit_status(merge_request.last_commit.sha)
154
    response = {status: status}
155 156 157 158

    render json: response
  end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
159 160
  protected

161
  def selected_target_project
162 163 164 165 166
    if @project.id.to_s == params[:target_project_id] || @project.forked_project_link.nil?
      @project
    else
      @project.forked_project_link.forked_from_project
    end
167 168
  end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
169
  def merge_request
skv's avatar
skv committed
170
    @merge_request ||= @project.merge_requests.find_by!(iid: params[:id])
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
171
  end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
172

173 174 175 176
  def closes_issues
    @closes_issues ||= @merge_request.closes_issues
  end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
177
  def authorize_modify_merge_request!
178
    return render_404 unless can?(current_user, :modify_merge_request, @merge_request)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
179 180 181
  end

  def authorize_admin_merge_request!
182
    return render_404 unless can?(current_user, :admin_merge_request, @merge_request)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
183
  end
184 185 186 187

  def module_enabled
    return render_404 unless @project.merge_requests_enabled
  end
188 189

  def validates_merge_request
190 191 192
    # If source project was removed (Ex. mr from fork to origin)
    return invalid_mr unless @merge_request.source_project

193 194 195
    # Show git not found page
    # if there is no saved commits between source & target branch
    if @merge_request.commits.blank?
196 197
      # and if target branch doesn't exist
      return invalid_mr unless @merge_request.target_branch_exists?
198

199 200
      # or if source branch doesn't exist
      return invalid_mr unless @merge_request.source_branch_exists?
201
    end
202 203 204 205
  end

  def define_show_vars
    # Build a note object for comment form
206
    @note = @project.notes.new(noteable: @merge_request)
207 208
    @notes = @merge_request.mr_and_commit_notes.inc_author.fresh
    @discussions = Note.discussions_from_notes(@notes)
209
    @noteable = @merge_request
210

211
    # Get commits from repository
212 213
    # or from cache if already merged
    @commits = @merge_request.commits
214

215
    @merge_request_diff = @merge_request.merge_request_diff
216
    @allowed_to_merge = allowed_to_merge?
217
    @show_merge_controls = @merge_request.open? && @commits.any? && @allowed_to_merge
218
    @source_branch = @merge_request.source_project.repository.find_branch(@merge_request.source_branch).try(:name)
219 220 221
  end

  def allowed_to_merge?
222
    allowed_to_push_code?(project, @merge_request.target_branch)
223 224 225 226 227 228 229
  end

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

230 231
  def allowed_to_push_code?(project, branch)
    action = if project.protected_branch?(branch)
232 233 234 235 236
               :push_code_to_protected_branches
             else
               :push_code
             end

237
    can?(current_user, action, project)
238
  end
239 240 241 242 243

  def merge_request_params
    params.require(:merge_request).permit(
      :title, :assignee_id, :source_project_id, :source_branch,
      :target_project_id, :target_branch, :milestone_id,
244
      :state_event, :description, label_ids: []
245 246
    )
  end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
247
end