Commit 03e517e0776f65aefd659958841e37c5c3f82a64

Authored by Dmitriy Zaporozhets
2 parents 7bb2a536 8c5aa5e3

Merge branch 'bug/mr_duplicate_iids' of /home/git/repositories/gitlab/gitlabhq

app/controllers/projects/merge_requests_controller.rb
... ... @@ -79,6 +79,21 @@ class Projects::MergeRequestsController < Projects::ApplicationController
79 79 end
80 80  
81 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 97 if @merge_request.update_attributes(params[:merge_request].merge(author_id_of_changes: current_user.id))
83 98 @merge_request.reload_code
84 99 @merge_request.mark_as_unchecked
... ... @@ -160,14 +175,17 @@ class Projects::MergeRequestsController < Projects::ApplicationController
160 175 end
161 176  
162 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 181 # Show git not found page
164 182 # if there is no saved commits between source & target branch
165 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 189 end
172 190 end
173 191  
... ...
app/helpers/merge_requests_helper.rb
... ... @@ -36,7 +36,7 @@ module MergeRequestsHelper
36 36  
37 37 def merge_path_description(merge_request, separator)
38 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 40 else
41 41 "Branches: #{@merge_request.source_branch} #{separator} #{@merge_request.target_branch}"
42 42 end
... ...
app/models/merge_request.rb
... ... @@ -35,6 +35,10 @@ class MergeRequest < ActiveRecord::Base
35 35  
36 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 42 state_machine :state, initial: :opened do
39 43 event :close do
40 44 transition [:reopened, :opened] => :closed
... ... @@ -80,7 +84,7 @@ class MergeRequest < ActiveRecord::Base
80 84 serialize :st_commits
81 85 serialize :st_diffs
82 86  
83   - validates :source_project, presence: true
  87 + validates :source_project, presence: true, unless: :allow_broken
84 88 validates :source_branch, presence: true
85 89 validates :target_project, presence: true
86 90 validates :target_branch, presence: true
... ... @@ -262,7 +266,7 @@ class MergeRequest < ActiveRecord::Base
262 266 # Return the set of issues that will be closed if this merge request is accepted.
263 267 def closes_issues
264 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 270 else
267 271 []
268 272 end
... ... @@ -273,6 +277,34 @@ class MergeRequest < ActiveRecord::Base
273 277 "merge request !#{iid}"
274 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 308 private
277 309  
278 310 def dump_commits(commits)
... ...
app/models/project.rb
... ... @@ -55,11 +55,15 @@ class Project < ActiveRecord::Base
55 55 has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
56 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 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 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 67 has_many :milestones, dependent: :destroy
64 68 has_many :notes, dependent: :destroy
65 69 has_many :snippets, dependent: :destroy, class_name: "ProjectSnippet"
... ...
app/views/projects/merge_requests/_form.html.haml
... ... @@ -10,7 +10,7 @@
10 10 .span5
11 11 .clearfix
12 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 14 .pull-left
15 15  
16 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 10 %span.pull-right
11 11 - if merge_request.for_fork?
12 12 %span.light
13   - = "#{merge_request.source_project.path_with_namespace}"
  13 + = "#{merge_request.source_project_path}"
14 14 = "#{merge_request.source_branch}"
15 15 %i.icon-angle-right.light
16 16 = "#{merge_request.target_branch}"
... ...
app/views/projects/merge_requests/_show.html.haml
... ... @@ -2,7 +2,10 @@
2 2 = render "projects/merge_requests/show/mr_title"
3 3 = render "projects/merge_requests/show/how_to_merge"
4 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 9 - if @merge_request.source_project.gitlab_ci?
7 10 = render "projects/merge_requests/show/mr_ci"
8 11 = render "projects/merge_requests/show/commits"
... ...
app/views/projects/merge_requests/invalid.html.haml
... ... @@ -3,15 +3,21 @@
3 3 = render "projects/merge_requests/show/mr_box"
4 4  
5 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 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 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 10 %strong Step 1.
11 11 Checkout target branch and get recent objects from GitLab
12 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 14 %pre.dark
15 15 :preserve
16 16 git checkout #{target_remote} #{@merge_request.target_branch}
... ...
app/views/projects/merge_requests/show/_mr_title.html.haml
... ... @@ -3,7 +3,7 @@
3 3  
4 4 -if @merge_request.for_fork?
5 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 7 #{@merge_request.source_branch}
8 8 →
9 9 %span.label-branch= @merge_request.target_branch
... ... @@ -24,7 +24,7 @@
24 24 %li= link_to "Email Patches", project_merge_request_path(@project, @merge_request, format: :patch)
25 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 29 = link_to edit_project_merge_request_path(@project, @merge_request), class: "btn grouped", id:"edit_merge_request" do
30 30 %i.icon-edit
... ...
app/views/projects/merge_requests/show/_no_accept.html.haml 0 → 100644
... ... @@ -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 112 let(:commit2) { mock('commit2', closes_issues: [issue1]) }
113 113  
114 114 before do
115   - subject.stub(unmerged_commits: [commit0, commit1, commit2])
  115 + subject.stub(commits: [commit0, commit1, commit2])
116 116 end
117 117  
118 118 it 'accesses the set of issues that will be closed on acceptance' do
... ...