Commit 03e517e0776f65aefd659958841e37c5c3f82a64
Exists in
master
and in
4 other branches
Merge branch 'bug/mr_duplicate_iids' of /home/git/repositories/gitlab/gitlabhq
Showing
12 changed files
with
101 additions
and
26 deletions
Show diff stats
app/controllers/projects/merge_requests_controller.rb
| @@ -79,6 +79,21 @@ class Projects::MergeRequestsController < Projects::ApplicationController | @@ -79,6 +79,21 @@ class Projects::MergeRequestsController < Projects::ApplicationController | ||
| 79 | end | 79 | end |
| 80 | 80 | ||
| 81 | def update | 81 | def update |
| 82 | + # If we close MergeRequest we want to ignore validation | ||
| 83 | + # so we can close broken one (Ex. fork project removed) | ||
| 84 | + if params[:merge_request] == {"state_event"=>"close"} | ||
| 85 | + @merge_request.allow_broken = true | ||
| 86 | + | ||
| 87 | + if @merge_request.close | ||
| 88 | + opts = { notice: 'Merge request was successfully closed.' } | ||
| 89 | + else | ||
| 90 | + opts = { alert: 'Failed to close merge request.' } | ||
| 91 | + end | ||
| 92 | + | ||
| 93 | + redirect_to [@merge_request.target_project, @merge_request], opts | ||
| 94 | + return | ||
| 95 | + end | ||
| 96 | + | ||
| 82 | if @merge_request.update_attributes(params[:merge_request].merge(author_id_of_changes: current_user.id)) | 97 | if @merge_request.update_attributes(params[:merge_request].merge(author_id_of_changes: current_user.id)) |
| 83 | @merge_request.reload_code | 98 | @merge_request.reload_code |
| 84 | @merge_request.mark_as_unchecked | 99 | @merge_request.mark_as_unchecked |
| @@ -160,14 +175,17 @@ class Projects::MergeRequestsController < Projects::ApplicationController | @@ -160,14 +175,17 @@ class Projects::MergeRequestsController < Projects::ApplicationController | ||
| 160 | end | 175 | end |
| 161 | 176 | ||
| 162 | def validates_merge_request | 177 | def validates_merge_request |
| 178 | + # If source project was removed (Ex. mr from fork to origin) | ||
| 179 | + return invalid_mr unless @merge_request.source_project | ||
| 180 | + | ||
| 163 | # Show git not found page | 181 | # Show git not found page |
| 164 | # if there is no saved commits between source & target branch | 182 | # if there is no saved commits between source & target branch |
| 165 | if @merge_request.commits.blank? | 183 | if @merge_request.commits.blank? |
| 166 | - # and if source target doesn't exist | ||
| 167 | - return invalid_mr unless @merge_request.target_project.repository.branch_names.include?(@merge_request.target_branch) | 184 | + # and if target branch doesn't exist |
| 185 | + return invalid_mr unless @merge_request.target_branch_exists? | ||
| 168 | 186 | ||
| 169 | - # or if source branch doesn't exist | ||
| 170 | - return invalid_mr unless @merge_request.source_project.repository.branch_names.include?(@merge_request.source_branch) | 187 | + # or if source branch doesn't exist |
| 188 | + return invalid_mr unless @merge_request.source_branch_exists? | ||
| 171 | end | 189 | end |
| 172 | end | 190 | end |
| 173 | 191 |
app/helpers/merge_requests_helper.rb
| @@ -36,7 +36,7 @@ module MergeRequestsHelper | @@ -36,7 +36,7 @@ module MergeRequestsHelper | ||
| 36 | 36 | ||
| 37 | def merge_path_description(merge_request, separator) | 37 | def merge_path_description(merge_request, separator) |
| 38 | if merge_request.for_fork? | 38 | if merge_request.for_fork? |
| 39 | - "Project:Branches: #{@merge_request.source_project.path_with_namespace}:#{@merge_request.source_branch} #{separator} #{@merge_request.target_project.path_with_namespace}:#{@merge_request.target_branch}" | 39 | + "Project:Branches: #{@merge_request.source_project_path}:#{@merge_request.source_branch} #{separator} #{@merge_request.target_project.path_with_namespace}:#{@merge_request.target_branch}" |
| 40 | else | 40 | else |
| 41 | "Branches: #{@merge_request.source_branch} #{separator} #{@merge_request.target_branch}" | 41 | "Branches: #{@merge_request.source_branch} #{separator} #{@merge_request.target_branch}" |
| 42 | end | 42 | end |
app/models/merge_request.rb
| @@ -35,6 +35,10 @@ class MergeRequest < ActiveRecord::Base | @@ -35,6 +35,10 @@ class MergeRequest < ActiveRecord::Base | ||
| 35 | 35 | ||
| 36 | attr_accessor :should_remove_source_branch | 36 | attr_accessor :should_remove_source_branch |
| 37 | 37 | ||
| 38 | + # When this attribute is true some MR validation is ignored | ||
| 39 | + # It allows us to close or modify broken merge requests | ||
| 40 | + attr_accessor :allow_broken | ||
| 41 | + | ||
| 38 | state_machine :state, initial: :opened do | 42 | state_machine :state, initial: :opened do |
| 39 | event :close do | 43 | event :close do |
| 40 | transition [:reopened, :opened] => :closed | 44 | transition [:reopened, :opened] => :closed |
| @@ -80,7 +84,7 @@ class MergeRequest < ActiveRecord::Base | @@ -80,7 +84,7 @@ class MergeRequest < ActiveRecord::Base | ||
| 80 | serialize :st_commits | 84 | serialize :st_commits |
| 81 | serialize :st_diffs | 85 | serialize :st_diffs |
| 82 | 86 | ||
| 83 | - validates :source_project, presence: true | 87 | + validates :source_project, presence: true, unless: :allow_broken |
| 84 | validates :source_branch, presence: true | 88 | validates :source_branch, presence: true |
| 85 | validates :target_project, presence: true | 89 | validates :target_project, presence: true |
| 86 | validates :target_branch, presence: true | 90 | validates :target_branch, presence: true |
| @@ -262,7 +266,7 @@ class MergeRequest < ActiveRecord::Base | @@ -262,7 +266,7 @@ class MergeRequest < ActiveRecord::Base | ||
| 262 | # Return the set of issues that will be closed if this merge request is accepted. | 266 | # Return the set of issues that will be closed if this merge request is accepted. |
| 263 | def closes_issues | 267 | def closes_issues |
| 264 | if target_branch == project.default_branch | 268 | if target_branch == project.default_branch |
| 265 | - unmerged_commits.map { |c| c.closes_issues(project) }.flatten.uniq.sort_by(&:id) | 269 | + commits.map { |c| c.closes_issues(project) }.flatten.uniq.sort_by(&:id) |
| 266 | else | 270 | else |
| 267 | [] | 271 | [] |
| 268 | end | 272 | end |
| @@ -273,6 +277,34 @@ class MergeRequest < ActiveRecord::Base | @@ -273,6 +277,34 @@ class MergeRequest < ActiveRecord::Base | ||
| 273 | "merge request !#{iid}" | 277 | "merge request !#{iid}" |
| 274 | end | 278 | end |
| 275 | 279 | ||
| 280 | + def target_project_path | ||
| 281 | + if target_project | ||
| 282 | + target_project.path_with_namespace | ||
| 283 | + else | ||
| 284 | + "(removed)" | ||
| 285 | + end | ||
| 286 | + end | ||
| 287 | + | ||
| 288 | + def source_project_path | ||
| 289 | + if source_project | ||
| 290 | + source_project.path_with_namespace | ||
| 291 | + else | ||
| 292 | + "(removed)" | ||
| 293 | + end | ||
| 294 | + end | ||
| 295 | + | ||
| 296 | + def source_branch_exists? | ||
| 297 | + return false unless self.source_project | ||
| 298 | + | ||
| 299 | + self.source_project.repository.branch_names.include?(self.source_branch) | ||
| 300 | + end | ||
| 301 | + | ||
| 302 | + def target_branch_exists? | ||
| 303 | + return false unless self.target_project | ||
| 304 | + | ||
| 305 | + self.target_project.repository.branch_names.include?(self.target_branch) | ||
| 306 | + end | ||
| 307 | + | ||
| 276 | private | 308 | private |
| 277 | 309 | ||
| 278 | def dump_commits(commits) | 310 | def dump_commits(commits) |
app/models/project.rb
| @@ -55,11 +55,15 @@ class Project < ActiveRecord::Base | @@ -55,11 +55,15 @@ class Project < ActiveRecord::Base | ||
| 55 | has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id" | 55 | has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id" |
| 56 | has_one :forked_from_project, through: :forked_project_link | 56 | has_one :forked_from_project, through: :forked_project_link |
| 57 | 57 | ||
| 58 | - has_many :services, dependent: :destroy | ||
| 59 | - has_many :events, dependent: :destroy | 58 | + # Merge Requests for target project should be removed with it |
| 60 | has_many :merge_requests, dependent: :destroy, foreign_key: "target_project_id" | 59 | has_many :merge_requests, dependent: :destroy, foreign_key: "target_project_id" |
| 61 | - has_many :fork_merge_requests,dependent: :destroy, foreign_key: "source_project_id", class_name: MergeRequest | 60 | + |
| 61 | + # Merge requests from source project should be kept when source project was removed | ||
| 62 | + has_many :fork_merge_requests, foreign_key: "source_project_id", class_name: MergeRequest | ||
| 63 | + | ||
| 62 | has_many :issues, -> { order "state DESC, created_at DESC" }, dependent: :destroy | 64 | has_many :issues, -> { order "state DESC, created_at DESC" }, dependent: :destroy |
| 65 | + has_many :services, dependent: :destroy | ||
| 66 | + has_many :events, dependent: :destroy | ||
| 63 | has_many :milestones, dependent: :destroy | 67 | has_many :milestones, dependent: :destroy |
| 64 | has_many :notes, dependent: :destroy | 68 | has_many :notes, dependent: :destroy |
| 65 | has_many :snippets, dependent: :destroy, class_name: "ProjectSnippet" | 69 | has_many :snippets, dependent: :destroy, class_name: "ProjectSnippet" |
app/views/projects/merge_requests/_form.html.haml
| @@ -10,7 +10,7 @@ | @@ -10,7 +10,7 @@ | ||
| 10 | .span5 | 10 | .span5 |
| 11 | .clearfix | 11 | .clearfix |
| 12 | .pull-left | 12 | .pull-left |
| 13 | - = f.select(:source_project_id,[[@merge_request.source_project.path_with_namespace,@merge_request.source_project.id]] , {}, {class: 'source_project chosen span3'}) | 13 | + = f.select(:source_project_id,[[@merge_request.source_project_path,@merge_request.source_project.id]] , {}, {class: 'source_project chosen span3'}) |
| 14 | .pull-left | 14 | .pull-left |
| 15 | | 15 | |
| 16 | = f.select(:source_branch, @merge_request.source_project.repository.branch_names, { include_blank: "Select branch" }, {class: 'source_branch chosen span2'}) | 16 | = f.select(:source_branch, @merge_request.source_project.repository.branch_names, { include_blank: "Select branch" }, {class: 'source_branch chosen span2'}) |
app/views/projects/merge_requests/_merge_request.html.haml
| @@ -10,7 +10,7 @@ | @@ -10,7 +10,7 @@ | ||
| 10 | %span.pull-right | 10 | %span.pull-right |
| 11 | - if merge_request.for_fork? | 11 | - if merge_request.for_fork? |
| 12 | %span.light | 12 | %span.light |
| 13 | - = "#{merge_request.source_project.path_with_namespace}" | 13 | + = "#{merge_request.source_project_path}" |
| 14 | = "#{merge_request.source_branch}" | 14 | = "#{merge_request.source_branch}" |
| 15 | %i.icon-angle-right.light | 15 | %i.icon-angle-right.light |
| 16 | = "#{merge_request.target_branch}" | 16 | = "#{merge_request.target_branch}" |
app/views/projects/merge_requests/_show.html.haml
| @@ -2,7 +2,10 @@ | @@ -2,7 +2,10 @@ | ||
| 2 | = render "projects/merge_requests/show/mr_title" | 2 | = render "projects/merge_requests/show/mr_title" |
| 3 | = render "projects/merge_requests/show/how_to_merge" | 3 | = render "projects/merge_requests/show/how_to_merge" |
| 4 | = render "projects/merge_requests/show/mr_box" | 4 | = render "projects/merge_requests/show/mr_box" |
| 5 | - = render "projects/merge_requests/show/mr_accept" | 5 | + - if @merge_request.source_branch_exists? && @merge_request.target_branch_exists? |
| 6 | + = render "projects/merge_requests/show/mr_accept" | ||
| 7 | + - else | ||
| 8 | + = render "projects/merge_requests/show/no_accept" | ||
| 6 | - if @merge_request.source_project.gitlab_ci? | 9 | - if @merge_request.source_project.gitlab_ci? |
| 7 | = render "projects/merge_requests/show/mr_ci" | 10 | = render "projects/merge_requests/show/mr_ci" |
| 8 | = render "projects/merge_requests/show/commits" | 11 | = render "projects/merge_requests/show/commits" |
app/views/projects/merge_requests/invalid.html.haml
| @@ -3,15 +3,21 @@ | @@ -3,15 +3,21 @@ | ||
| 3 | = render "projects/merge_requests/show/mr_box" | 3 | = render "projects/merge_requests/show/mr_box" |
| 4 | 4 | ||
| 5 | .alert.alert-error | 5 | .alert.alert-error |
| 6 | - %h5 | ||
| 7 | - %i.icon-exclamation-sign | ||
| 8 | - We cannot find | ||
| 9 | - %span.label-branch= @merge_request.source_branch | ||
| 10 | - or | ||
| 11 | - %span.label-branch= @merge_request.target_branch | ||
| 12 | - branches in the repository. | ||
| 13 | - %p | ||
| 14 | - Maybe it was removed or never pushed. | ||
| 15 | %p | 6 | %p |
| 7 | + We cannot render this merge request properly because | ||
| 8 | + - if @merge_request.for_fork? && !@merge_request.source_project | ||
| 9 | + fork project was removed | ||
| 10 | + - elsif !@merge_request.source_branch_exists? | ||
| 11 | + %span.label.label-inverse= @merge_request.source_branch | ||
| 12 | + does not exist in | ||
| 13 | + %span.label.label-info= @merge_request.source_project_path | ||
| 14 | + - elsif !@merge_request.target_branch_exists? | ||
| 15 | + %span.label.label-inverse= @merge_request.target_branch | ||
| 16 | + does not exist in | ||
| 17 | + %span.label.label-info= @merge_request.target_project_path | ||
| 18 | + - else | ||
| 19 | + of internal error | ||
| 20 | + | ||
| 21 | + %strong | ||
| 16 | Please close Merge Request or change branches with existing one | 22 | Please close Merge Request or change branches with existing one |
| 17 | 23 |
app/views/projects/merge_requests/show/_how_to_merge.html.haml
| @@ -10,7 +10,7 @@ | @@ -10,7 +10,7 @@ | ||
| 10 | %strong Step 1. | 10 | %strong Step 1. |
| 11 | Checkout target branch and get recent objects from GitLab | 11 | Checkout target branch and get recent objects from GitLab |
| 12 | Assuming remote for #{@merge_request.target_project.path_with_namespace} is called #{target_remote} | 12 | Assuming remote for #{@merge_request.target_project.path_with_namespace} is called #{target_remote} |
| 13 | - remote for #{@merge_request.source_project.path_with_namespace} is called #{source_remote} | 13 | + remote for #{@merge_request.source_project_path} is called #{source_remote} |
| 14 | %pre.dark | 14 | %pre.dark |
| 15 | :preserve | 15 | :preserve |
| 16 | git checkout #{target_remote} #{@merge_request.target_branch} | 16 | git checkout #{target_remote} #{@merge_request.target_branch} |
app/views/projects/merge_requests/show/_mr_title.html.haml
| @@ -3,7 +3,7 @@ | @@ -3,7 +3,7 @@ | ||
| 3 | | 3 | |
| 4 | -if @merge_request.for_fork? | 4 | -if @merge_request.for_fork? |
| 5 | %span.label-branch | 5 | %span.label-branch |
| 6 | - %span.label-project= truncate(@merge_request.source_project.path_with_namespace, length: 25) | 6 | + %span.label-project= truncate(@merge_request.source_project_path, length: 25) |
| 7 | #{@merge_request.source_branch} | 7 | #{@merge_request.source_branch} |
| 8 | → | 8 | → |
| 9 | %span.label-branch= @merge_request.target_branch | 9 | %span.label-branch= @merge_request.target_branch |
| @@ -24,7 +24,7 @@ | @@ -24,7 +24,7 @@ | ||
| 24 | %li= link_to "Email Patches", project_merge_request_path(@project, @merge_request, format: :patch) | 24 | %li= link_to "Email Patches", project_merge_request_path(@project, @merge_request, format: :patch) |
| 25 | %li= link_to "Plain Diff", project_merge_request_path(@project, @merge_request, format: :diff) | 25 | %li= link_to "Plain Diff", project_merge_request_path(@project, @merge_request, format: :diff) |
| 26 | 26 | ||
| 27 | - = link_to 'Close', project_merge_request_path(@project, @merge_request, merge_request: {state_event: :close }), method: :put, class: "btn grouped btn-close", title: "Close merge request" | 27 | + = link_to 'Close', project_merge_request_path(@project, @merge_request, merge_request: { state_event: :close }), method: :put, class: "btn grouped btn-close", title: "Close merge request" |
| 28 | 28 | ||
| 29 | = link_to edit_project_merge_request_path(@project, @merge_request), class: "btn grouped", id:"edit_merge_request" do | 29 | = link_to edit_project_merge_request_path(@project, @merge_request), class: "btn grouped", id:"edit_merge_request" do |
| 30 | %i.icon-edit | 30 | %i.icon-edit |
app/views/projects/merge_requests/show/_no_accept.html.haml
0 → 100644
| @@ -0,0 +1,12 @@ | @@ -0,0 +1,12 @@ | ||
| 1 | +.alert.alert-error | ||
| 2 | + %p | ||
| 3 | + This merge request can not be accepted because branch | ||
| 4 | + - unless @merge_request.source_branch_exists? | ||
| 5 | + %span.label.label-inverse= @merge_request.source_branch | ||
| 6 | + does not exist in | ||
| 7 | + %span.label.label-info= @merge_request.source_project_path | ||
| 8 | + - else | ||
| 9 | + %span.label.label-inverse= @merge_request.target_branch | ||
| 10 | + does not exist in | ||
| 11 | + %span.label.label-info= @merge_request.target_project_path | ||
| 12 | + %strong Please close this merge request or change branches with existing one |
spec/models/merge_request_spec.rb
| @@ -112,7 +112,7 @@ describe MergeRequest do | @@ -112,7 +112,7 @@ describe MergeRequest do | ||
| 112 | let(:commit2) { mock('commit2', closes_issues: [issue1]) } | 112 | let(:commit2) { mock('commit2', closes_issues: [issue1]) } |
| 113 | 113 | ||
| 114 | before do | 114 | before do |
| 115 | - subject.stub(unmerged_commits: [commit0, commit1, commit2]) | 115 | + subject.stub(commits: [commit0, commit1, commit2]) |
| 116 | end | 116 | end |
| 117 | 117 | ||
| 118 | it 'accesses the set of issues that will be closed on acceptance' do | 118 | it 'accesses the set of issues that will be closed on acceptance' do |