Commit 66dacfc52a1a91783b68e503e0e1844e26f47a54
Exists in
master
and in
4 other branches
Merge branch 'karlhungus-mr-on-fork' of /home/git/repositories/gitlab/gitlabhq
Showing
107 changed files
with
1748 additions
and
515 deletions
Show diff stats
Too many changes.
To preserve performance only 100 of 107 files displayed.
Gemfile
Gemfile.lock
| ... | ... | @@ -72,9 +72,12 @@ GEM |
| 72 | 72 | charlock_holmes (0.6.9.4) |
| 73 | 73 | childprocess (0.3.9) |
| 74 | 74 | ffi (~> 1.0, >= 1.0.11) |
| 75 | - chosen-rails (0.9.8) | |
| 76 | - railties (~> 3.0) | |
| 77 | - thor (~> 0.14) | |
| 75 | + chosen-rails (1.0.0) | |
| 76 | + coffee-rails (>= 3.2) | |
| 77 | + compass-rails (>= 1.0) | |
| 78 | + railties (>= 3.0) | |
| 79 | + sass-rails (>= 3.2) | |
| 80 | + chunky_png (1.2.8) | |
| 78 | 81 | code_analyzer (0.3.2) |
| 79 | 82 | sexp_processor |
| 80 | 83 | coderay (1.0.9) |
| ... | ... | @@ -87,6 +90,12 @@ GEM |
| 87 | 90 | coffee-script-source (1.6.2) |
| 88 | 91 | colored (1.2) |
| 89 | 92 | colorize (0.5.8) |
| 93 | + compass (0.12.2) | |
| 94 | + chunky_png (~> 1.2) | |
| 95 | + fssm (>= 0.2.7) | |
| 96 | + sass (~> 3.1) | |
| 97 | + compass-rails (1.0.3) | |
| 98 | + compass (>= 0.12.2, < 0.14) | |
| 90 | 99 | connection_pool (1.1.0) |
| 91 | 100 | coveralls (0.6.7) |
| 92 | 101 | colorize |
| ... | ... | @@ -149,6 +158,7 @@ GEM |
| 149 | 158 | dotenv (>= 0.7) |
| 150 | 159 | thor (>= 0.13.6) |
| 151 | 160 | formatador (0.2.4) |
| 161 | + fssm (0.2.10) | |
| 152 | 162 | gemoji (1.2.1) |
| 153 | 163 | gherkin-ruby (0.3.0) |
| 154 | 164 | github-linguist (2.3.4) |
| ... | ... | @@ -548,7 +558,7 @@ DEPENDENCIES |
| 548 | 558 | bootstrap-sass |
| 549 | 559 | capybara |
| 550 | 560 | carrierwave |
| 551 | - chosen-rails (= 0.9.8) | |
| 561 | + chosen-rails (= 1.0.0) | |
| 552 | 562 | coffee-rails |
| 553 | 563 | colored |
| 554 | 564 | coveralls | ... | ... |
app/assets/stylesheets/gitlab_bootstrap/mixins.scss
| ... | ... | @@ -17,6 +17,10 @@ |
| 17 | 17 | border-radius: $radius; |
| 18 | 18 | } |
| 19 | 19 | |
| 20 | +@mixin border-radius-left($radius) { | |
| 21 | + @include border-radius($radius 0 0 $radius) | |
| 22 | +} | |
| 23 | + | |
| 20 | 24 | @mixin linear-gradient($from, $to) { |
| 21 | 25 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from($from), to($to)); |
| 22 | 26 | background-image: -webkit-linear-gradient($from, $to); | ... | ... |
app/assets/stylesheets/sections/merge_requests.scss
| ... | ... | @@ -84,14 +84,23 @@ |
| 84 | 84 | |
| 85 | 85 | .label-branch { |
| 86 | 86 | @include border-radius(4px); |
| 87 | - padding: 2px 4px; | |
| 87 | + padding: 3px 4px; | |
| 88 | 88 | border: none; |
| 89 | 89 | font-size: 14px; |
| 90 | 90 | background: #474D57; |
| 91 | 91 | color: #fff; |
| 92 | 92 | font-family: $monospace_font; |
| 93 | - text-shadow: 0 1px 1px #111; | |
| 94 | 93 | font-weight: normal; |
| 94 | + overflow: hidden; | |
| 95 | + | |
| 96 | + .label-project { | |
| 97 | + @include border-radius-left(4px); | |
| 98 | + padding: 3px 4px; | |
| 99 | + background: #29A; | |
| 100 | + position: relative; | |
| 101 | + left: -4px; | |
| 102 | + letter-spacing: -1px; | |
| 103 | + } | |
| 95 | 104 | } |
| 96 | 105 | |
| 97 | 106 | .mr-list { | ... | ... |
app/assets/stylesheets/selects.scss
| 1 | 1 | /* CHZN reset few styles */ |
| 2 | -.chzn-container-single .chzn-single { | |
| 2 | +.chosen-container-single .chosen-single { | |
| 3 | 3 | background: #FFF; |
| 4 | 4 | border: 1px solid #bbb; |
| 5 | 5 | box-shadow: none; |
| 6 | 6 | } |
| 7 | -.chzn-container-active .chzn-single { | |
| 7 | +.chosen-container-active .chosen-single { | |
| 8 | 8 | background: #fff; |
| 9 | 9 | } |
| 10 | 10 | |
| ... | ... | @@ -41,38 +41,38 @@ |
| 41 | 41 | width: 120px; |
| 42 | 42 | } |
| 43 | 43 | |
| 44 | -.project-refs-form .chzn-container { | |
| 44 | +.project-refs-form .chosen-container { | |
| 45 | 45 | position: relative; |
| 46 | 46 | top: 0; |
| 47 | 47 | left: 0; |
| 48 | 48 | margin-right: 10px; |
| 49 | 49 | |
| 50 | - .chzn-drop { | |
| 50 | + .chosen-drop { | |
| 51 | 51 | min-width: 400px; |
| 52 | - .chzn-results { | |
| 52 | + .chosen-results { | |
| 53 | 53 | max-height: 300px; |
| 54 | 54 | } |
| 55 | - .chzn-search input { | |
| 55 | + .chosen-search input { | |
| 56 | 56 | min-width: 365px; |
| 57 | 57 | } |
| 58 | 58 | } |
| 59 | 59 | } |
| 60 | 60 | |
| 61 | 61 | /** Fix for Search Dropdown Border **/ |
| 62 | -.chzn-container { | |
| 63 | - .chzn-search { | |
| 62 | +.chosen-container { | |
| 63 | + .chosen-search { | |
| 64 | 64 | input:focus { |
| 65 | 65 | @include box-shadow(none); |
| 66 | 66 | } |
| 67 | 67 | } |
| 68 | 68 | |
| 69 | - .chzn-drop { | |
| 69 | + .chosen-drop { | |
| 70 | 70 | margin: 7px 0; |
| 71 | 71 | min-width: 200px; |
| 72 | 72 | border: 1px solid #bbb; |
| 73 | 73 | @include border-radius(0); |
| 74 | 74 | |
| 75 | - .chzn-results { | |
| 75 | + .chosen-results { | |
| 76 | 76 | margin-top: 5px; |
| 77 | 77 | max-height: 300px; |
| 78 | 78 | |
| ... | ... | @@ -95,7 +95,7 @@ |
| 95 | 95 | } |
| 96 | 96 | } |
| 97 | 97 | |
| 98 | - .chzn-search { | |
| 98 | + .chosen-search { | |
| 99 | 99 | @include bg-gray-gradient; |
| 100 | 100 | input { |
| 101 | 101 | min-width: 165px; |
| ... | ... | @@ -103,18 +103,19 @@ |
| 103 | 103 | } |
| 104 | 104 | } |
| 105 | 105 | } |
| 106 | +} | |
| 106 | 107 | |
| 107 | - .chzn-single { | |
| 108 | - @include bg-light-gray-gradient; | |
| 108 | +.chosen-container .chosen-single, | |
| 109 | +.chosen-container.chosen-with-drop .chosen-single { | |
| 110 | + @include bg-light-gray-gradient; | |
| 109 | 111 | |
| 110 | - div { | |
| 111 | - background: transparent; | |
| 112 | - border-left: none; | |
| 113 | - } | |
| 112 | + div { | |
| 113 | + background: transparent; | |
| 114 | + border-left: none; | |
| 115 | + } | |
| 114 | 116 | |
| 115 | - span { | |
| 116 | - font-weight: normal; | |
| 117 | - } | |
| 117 | + span { | |
| 118 | + font-weight: normal; | |
| 118 | 119 | } |
| 119 | 120 | } |
| 120 | 121 | |
| ... | ... | @@ -140,3 +141,11 @@ |
| 140 | 141 | padding: 7px; |
| 141 | 142 | color: #666; |
| 142 | 143 | } |
| 144 | + | |
| 145 | +.chosen-container .chosen-single div b { | |
| 146 | + background-position-y: 0px !important; | |
| 147 | +} | |
| 148 | + | |
| 149 | +.chosen-container .chosen-drop .chosen-search input { | |
| 150 | + background-position-y: -24px !important; | |
| 151 | +} | ... | ... |
app/contexts/filter_context.rb
app/contexts/merge_requests_load_context.rb
| ... | ... | @@ -14,7 +14,7 @@ class MergeRequestsLoadContext < BaseContext |
| 14 | 14 | end |
| 15 | 15 | |
| 16 | 16 | merge_requests = merge_requests.page(params[:page]).per(20) |
| 17 | - merge_requests = merge_requests.includes(:author, :project).order("created_at desc") | |
| 17 | + merge_requests = merge_requests.includes(:author, :source_project, :target_project).order("created_at desc") | |
| 18 | 18 | |
| 19 | 19 | # Filter by specific assignee_id (or lack thereof)? |
| 20 | 20 | if params[:assignee_id].present? | ... | ... |
app/contexts/search_context.rb
| ... | ... | @@ -19,7 +19,7 @@ class SearchContext |
| 19 | 19 | if params[:search_code].present? |
| 20 | 20 | result[:blobs] = project.repository.search_files(query, params[:repository_ref]) unless project.empty_repo? |
| 21 | 21 | else |
| 22 | - result[:merge_requests] = MergeRequest.where(project_id: project_ids).search(query).limit(20) | |
| 22 | + result[:merge_requests] = MergeRequest.in_projects(project_ids).search(query).limit(20) | |
| 23 | 23 | result[:issues] = Issue.where(project_id: project_ids).search(query).limit(20) |
| 24 | 24 | result[:wiki_pages] = [] |
| 25 | 25 | end | ... | ... |
app/controllers/projects/merge_requests_controller.rb
| ... | ... | @@ -24,8 +24,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController |
| 24 | 24 | format.html |
| 25 | 25 | format.js |
| 26 | 26 | |
| 27 | - format.diff { render text: @merge_request.to_diff } | |
| 28 | - format.patch { render text: @merge_request.to_patch } | |
| 27 | + format.diff { render text: @merge_request.to_diff(current_user) } | |
| 28 | + format.patch { render text: @merge_request.to_patch(current_user) } | |
| 29 | 29 | end |
| 30 | 30 | end |
| 31 | 31 | |
| ... | ... | @@ -33,27 +33,37 @@ class Projects::MergeRequestsController < Projects::ApplicationController |
| 33 | 33 | @commit = @merge_request.last_commit |
| 34 | 34 | |
| 35 | 35 | @comments_allowed = @reply_allowed = true |
| 36 | - @comments_target = { noteable_type: 'MergeRequest', | |
| 37 | - noteable_id: @merge_request.id } | |
| 36 | + @comments_target = {noteable_type: 'MergeRequest', | |
| 37 | + noteable_id: @merge_request.id} | |
| 38 | 38 | @line_notes = @merge_request.notes.where("line_code is not null") |
| 39 | 39 | end |
| 40 | 40 | |
| 41 | 41 | def new |
| 42 | - @merge_request = @project.merge_requests.new(params[:merge_request]) | |
| 42 | + @merge_request = MergeRequest.new(params[:merge_request]) | |
| 43 | + @merge_request.source_project = @project unless @merge_request.source_project | |
| 44 | + @merge_request.target_project = @project unless @merge_request.target_project | |
| 45 | + @target_branches = @merge_request.target_project.nil? ? [] : @merge_request.target_project.repository.branch_names | |
| 46 | + @source_project = @merge_request.source_project | |
| 47 | + @merge_request | |
| 43 | 48 | end |
| 44 | 49 | |
| 45 | 50 | def edit |
| 51 | + @source_project = @merge_request.source_project | |
| 52 | + @target_project = @merge_request.target_project | |
| 53 | + @target_branches = @merge_request.target_project.repository.branch_names | |
| 46 | 54 | end |
| 47 | 55 | |
| 48 | 56 | def create |
| 49 | - @merge_request = @project.merge_requests.new(params[:merge_request]) | |
| 57 | + @merge_request = MergeRequest.new(params[:merge_request]) | |
| 50 | 58 | @merge_request.author = current_user |
| 51 | - | |
| 59 | + @target_branches ||= [] | |
| 52 | 60 | if @merge_request.save |
| 53 | 61 | @merge_request.reload_code |
| 54 | - redirect_to [@project, @merge_request], notice: 'Merge request was successfully created.' | |
| 62 | + redirect_to [@merge_request.target_project, @merge_request], notice: 'Merge request was successfully created.' | |
| 55 | 63 | else |
| 56 | - render "new" | |
| 64 | + @source_project = @merge_request.source_project | |
| 65 | + @target_project = @merge_request.target_project | |
| 66 | + render action: "new" | |
| 57 | 67 | end |
| 58 | 68 | end |
| 59 | 69 | |
| ... | ... | @@ -61,7 +71,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController |
| 61 | 71 | if @merge_request.update_attributes(params[:merge_request].merge(author_id_of_changes: current_user.id)) |
| 62 | 72 | @merge_request.reload_code |
| 63 | 73 | @merge_request.mark_as_unchecked |
| 64 | - redirect_to [@project, @merge_request], notice: 'Merge request was successfully updated.' | |
| 74 | + redirect_to [@merge_request.target_project, @merge_request], notice: 'Merge request was successfully updated.' | |
| 65 | 75 | else |
| 66 | 76 | render "edit" |
| 67 | 77 | end |
| ... | ... | @@ -89,22 +99,35 @@ class Projects::MergeRequestsController < Projects::ApplicationController |
| 89 | 99 | end |
| 90 | 100 | |
| 91 | 101 | def branch_from |
| 92 | - @commit = @repository.commit(params[:ref]) | |
| 102 | + #This is always source | |
| 103 | + @source_project = @merge_request.nil? ? @project : @merge_request.source_project | |
| 104 | + @commit = @repository.commit(params[:ref]) if params[:ref].present? | |
| 93 | 105 | end |
| 94 | 106 | |
| 95 | 107 | def branch_to |
| 96 | - @commit = @repository.commit(params[:ref]) | |
| 108 | + @target_project = selected_target_project | |
| 109 | + @commit = @target_project.repository.commit(params[:ref]) if params[:ref].present? | |
| 110 | + end | |
| 111 | + | |
| 112 | + def update_branches | |
| 113 | + @target_project = selected_target_project | |
| 114 | + @target_branches = @target_project.repository.branch_names | |
| 115 | + @target_branches | |
| 97 | 116 | end |
| 98 | 117 | |
| 99 | 118 | def ci_status |
| 100 | 119 | status = project.gitlab_ci_service.commit_status(merge_request.last_commit.sha) |
| 101 | - response = { status: status } | |
| 120 | + response = {status: status} | |
| 102 | 121 | |
| 103 | 122 | render json: response |
| 104 | 123 | end |
| 105 | 124 | |
| 106 | 125 | protected |
| 107 | 126 | |
| 127 | + def selected_target_project | |
| 128 | + ((@project.id.to_s == params[:target_project_id]) || @project.forked_project_link.nil?) ? @project : @project.forked_project_link.forked_from_project | |
| 129 | + end | |
| 130 | + | |
| 108 | 131 | def merge_request |
| 109 | 132 | @merge_request ||= @project.merge_requests.find(params[:id]) |
| 110 | 133 | end |
| ... | ... | @@ -123,11 +146,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController |
| 123 | 146 | |
| 124 | 147 | def validates_merge_request |
| 125 | 148 | # Show git not found page if target branch doesn't exist |
| 126 | - return invalid_mr unless @project.repository.branch_names.include?(@merge_request.target_branch) | |
| 149 | + return invalid_mr unless @merge_request.target_project.repository.branch_names.include?(@merge_request.target_branch) | |
| 127 | 150 | |
| 128 | 151 | # Show git not found page if source branch doesn't exist |
| 129 | 152 | # and there is no saved commits between source & target branch |
| 130 | - return invalid_mr if !@project.repository.branch_names.include?(@merge_request.source_branch) && @merge_request.commits.blank? | |
| 153 | + return invalid_mr if !@merge_request.source_project.repository.branch_names.include?(@merge_request.source_branch) && @merge_request.commits.blank? | |
| 131 | 154 | end |
| 132 | 155 | |
| 133 | 156 | def define_show_vars | ... | ... |
app/helpers/commits_helper.rb
| ... | ... | @@ -56,8 +56,8 @@ module CommitsHelper |
| 56 | 56 | end |
| 57 | 57 | end |
| 58 | 58 | |
| 59 | - def commit_to_html commit | |
| 60 | - escape_javascript(render 'projects/commits/commit', commit: commit) | |
| 59 | + def commit_to_html commit, project | |
| 60 | + escape_javascript(render 'projects/commits/commit', commit: commit, project: project) unless commit.nil? | |
| 61 | 61 | end |
| 62 | 62 | |
| 63 | 63 | def diff_line_content(line) | ... | ... |
app/helpers/merge_requests_helper.rb
| ... | ... | @@ -2,14 +2,27 @@ module MergeRequestsHelper |
| 2 | 2 | def new_mr_path_from_push_event(event) |
| 3 | 3 | new_project_merge_request_path( |
| 4 | 4 | event.project, |
| 5 | - merge_request: { | |
| 6 | - source_branch: event.branch_name, | |
| 7 | - target_branch: event.project.repository.root_ref, | |
| 8 | - title: event.branch_name.titleize | |
| 9 | - } | |
| 5 | + new_mr_from_push_event(event, event.project) | |
| 10 | 6 | ) |
| 11 | 7 | end |
| 12 | 8 | |
| 9 | + def new_mr_path_for_fork_from_push_event(event) | |
| 10 | + new_project_merge_request_path( | |
| 11 | + event.project, | |
| 12 | + new_mr_from_push_event(event, event.project.forked_from_project) | |
| 13 | + ) | |
| 14 | + end | |
| 15 | + | |
| 16 | + def new_mr_from_push_event(event, target_project) | |
| 17 | + return :merge_request => { | |
| 18 | + source_project_id: event.project.id, | |
| 19 | + target_project_id: target_project.id, | |
| 20 | + source_branch: event.branch_name, | |
| 21 | + target_branch: target_project.repository.root_ref, | |
| 22 | + title: event.branch_name.titleize | |
| 23 | + } | |
| 24 | + end | |
| 25 | + | |
| 13 | 26 | def mr_css_classes mr |
| 14 | 27 | classes = "merge-request" |
| 15 | 28 | classes << " closed" if mr.closed? |
| ... | ... | @@ -18,6 +31,14 @@ module MergeRequestsHelper |
| 18 | 31 | end |
| 19 | 32 | |
| 20 | 33 | def ci_build_details_path merge_request |
| 21 | - merge_request.project.gitlab_ci_service.build_page(merge_request.last_commit.sha) | |
| 34 | + merge_request.source_project.gitlab_ci_service.build_page(merge_request.last_commit.sha) | |
| 35 | + end | |
| 36 | + | |
| 37 | + def merge_path_description(merge_request, separator) | |
| 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}" | |
| 40 | + else | |
| 41 | + "Branches: #{@merge_request.source_branch} #{separator} #{@merge_request.target_branch}" | |
| 42 | + end | |
| 22 | 43 | end |
| 23 | 44 | end | ... | ... |
app/mailers/emails/merge_requests.rb
| ... | ... | @@ -2,28 +2,65 @@ module Emails |
| 2 | 2 | module MergeRequests |
| 3 | 3 | def new_merge_request_email(recipient_id, merge_request_id) |
| 4 | 4 | @merge_request = MergeRequest.find(merge_request_id) |
| 5 | - @project = @merge_request.project | |
| 6 | 5 | mail(to: recipient(recipient_id), subject: subject("new merge request !#{@merge_request.id}", @merge_request.title)) |
| 7 | 6 | end |
| 8 | 7 | |
| 9 | 8 | def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id) |
| 10 | 9 | @merge_request = MergeRequest.find(merge_request_id) |
| 11 | 10 | @previous_assignee = User.find_by_id(previous_assignee_id) if previous_assignee_id |
| 12 | - @project = @merge_request.project | |
| 13 | 11 | mail(to: recipient(recipient_id), subject: subject("changed merge request !#{@merge_request.id}", @merge_request.title)) |
| 14 | 12 | end |
| 15 | 13 | |
| 16 | 14 | def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id) |
| 17 | 15 | @merge_request = MergeRequest.find(merge_request_id) |
| 18 | - @project = @merge_request.project | |
| 19 | 16 | @updated_by = User.find updated_by_user_id |
| 20 | 17 | mail(to: recipient(recipient_id), subject: subject("Closed merge request !#{@merge_request.id}", @merge_request.title)) |
| 21 | 18 | end |
| 22 | 19 | |
| 23 | 20 | def merged_merge_request_email(recipient_id, merge_request_id) |
| 24 | 21 | @merge_request = MergeRequest.find(merge_request_id) |
| 25 | - @project = @merge_request.project | |
| 26 | 22 | mail(to: recipient(recipient_id), subject: subject("Accepted merge request !#{@merge_request.id}", @merge_request.title)) |
| 27 | 23 | end |
| 28 | 24 | end |
| 25 | + | |
| 26 | + # Over rides default behavour to show source/target | |
| 27 | + # Formats arguments into a String suitable for use as an email subject | |
| 28 | + # | |
| 29 | + # extra - Extra Strings to be inserted into the subject | |
| 30 | + # | |
| 31 | + # Examples | |
| 32 | + # | |
| 33 | + # >> subject('Lorem ipsum') | |
| 34 | + # => "GitLab Merge Request | Lorem ipsum" | |
| 35 | + # | |
| 36 | + # # Automatically inserts Project name: | |
| 37 | + # Forked MR | |
| 38 | + # => source project => <Project id: 1, name: "Ruby on Rails", path: "ruby_on_rails", ...> | |
| 39 | + # => target project => <Project id: 2, name: "My Ror", path: "ruby_on_rails", ...> | |
| 40 | + # => source branch => source | |
| 41 | + # => target branch => target | |
| 42 | + # >> subject('Lorem ipsum') | |
| 43 | + # => "GitLab Merge Request | Ruby on Rails:source >> My Ror:target | Lorem ipsum " | |
| 44 | + # | |
| 45 | + # Non Forked MR | |
| 46 | + # => source project => <Project id: 1, name: "Ruby on Rails", path: "ruby_on_rails", ...> | |
| 47 | + # => target project => <Project id: 1, name: "Ruby on Rails", path: "ruby_on_rails", ...> | |
| 48 | + # => source branch => source | |
| 49 | + # => target branch => target | |
| 50 | + # >> subject('Lorem ipsum') | |
| 51 | + # => "GitLab Merge Request | Ruby on Rails | source >> target | Lorem ipsum " | |
| 52 | + # # Accepts multiple arguments | |
| 53 | + # >> subject('Lorem ipsum', 'Dolor sit amet') | |
| 54 | + # => "GitLab Merge Request | Lorem ipsum | Dolor sit amet" | |
| 55 | + def subject(*extra) | |
| 56 | + subject = "GitLab Merge Request |" | |
| 57 | + if @merge_request.for_fork? | |
| 58 | + subject << "#{@merge_request.source_project.name_with_namespace}:#{merge_request.source_branch} >> #{@merge_request.target_project.name_with_namespace}:#{merge_request.target_branch}" | |
| 59 | + else | |
| 60 | + subject << "#{@merge_request.source_project.name_with_namespace} | #{merge_request.source_branch} >> #{merge_request.target_branch}" | |
| 61 | + end | |
| 62 | + subject << " | " + extra.join(' | ') if extra.present? | |
| 63 | + subject | |
| 64 | + end | |
| 65 | + | |
| 29 | 66 | end | ... | ... |
app/mailers/notify.rb
| ... | ... | @@ -6,6 +6,7 @@ class Notify < ActionMailer::Base |
| 6 | 6 | |
| 7 | 7 | add_template_helper ApplicationHelper |
| 8 | 8 | add_template_helper GitlabMarkdownHelper |
| 9 | + add_template_helper MergeRequestsHelper | |
| 9 | 10 | |
| 10 | 11 | default_url_options[:host] = Gitlab.config.gitlab.host |
| 11 | 12 | default_url_options[:protocol] = Gitlab.config.gitlab.protocol | ... | ... |
app/models/concerns/issuable.rb
| ... | ... | @@ -9,23 +9,19 @@ module Issuable |
| 9 | 9 | include Mentionable |
| 10 | 10 | |
| 11 | 11 | included do |
| 12 | - belongs_to :project | |
| 13 | 12 | belongs_to :author, class_name: "User" |
| 14 | 13 | belongs_to :assignee, class_name: "User" |
| 15 | 14 | belongs_to :milestone |
| 16 | 15 | has_many :notes, as: :noteable, dependent: :destroy |
| 17 | 16 | |
| 18 | - validates :project, presence: true | |
| 19 | 17 | validates :author, presence: true |
| 20 | 18 | validates :title, presence: true, length: { within: 0..255 } |
| 21 | 19 | |
| 22 | - scope :opened, -> { with_state(:opened) } | |
| 23 | - scope :closed, -> { with_state(:closed) } | |
| 24 | - scope :of_group, ->(group) { where(project_id: group.project_ids) } | |
| 25 | 20 | scope :assigned_to, ->(u) { where(assignee_id: u.id)} |
| 26 | 21 | scope :recent, -> { order("created_at DESC") } |
| 27 | 22 | scope :assigned, -> { where("assignee_id IS NOT NULL") } |
| 28 | 23 | scope :unassigned, -> { where("assignee_id IS NULL") } |
| 24 | + scope :of_projects, ->(ids) { where(project_id: ids) } | |
| 29 | 25 | |
| 30 | 26 | delegate :name, |
| 31 | 27 | :email, | ... | ... |
app/models/issue.rb
| ... | ... | @@ -17,8 +17,17 @@ |
| 17 | 17 | # |
| 18 | 18 | |
| 19 | 19 | class Issue < ActiveRecord::Base |
| 20 | + | |
| 20 | 21 | include Issuable |
| 21 | 22 | |
| 23 | + belongs_to :project | |
| 24 | + validates :project, presence: true | |
| 25 | + | |
| 26 | + scope :of_group, ->(group) { where(project_id: group.project_ids) } | |
| 27 | + scope :of_user_team, ->(team) { where(project_id: team.project_ids, assignee_id: team.member_ids) } | |
| 28 | + scope :opened, -> { with_state(:opened) } | |
| 29 | + scope :closed, -> { with_state(:closed) } | |
| 30 | + | |
| 22 | 31 | attr_accessible :title, :assignee_id, :position, :description, |
| 23 | 32 | :milestone_id, :label_list, :author_id_of_changes, |
| 24 | 33 | :state_event | ... | ... |
app/models/merge_request.rb
| ... | ... | @@ -2,30 +2,35 @@ |
| 2 | 2 | # |
| 3 | 3 | # Table name: merge_requests |
| 4 | 4 | # |
| 5 | -# id :integer not null, primary key | |
| 6 | -# target_branch :string(255) not null | |
| 7 | -# source_branch :string(255) not null | |
| 8 | -# project_id :integer not null | |
| 9 | -# author_id :integer | |
| 10 | -# assignee_id :integer | |
| 11 | -# title :string(255) | |
| 12 | -# created_at :datetime | |
| 13 | -# updated_at :datetime | |
| 14 | -# st_commits :text(2147483647) | |
| 15 | -# st_diffs :text(2147483647) | |
| 16 | -# milestone_id :integer | |
| 17 | -# state :string(255) | |
| 18 | -# merge_status :string(255) | |
| 5 | +# id :integer not null, primary key | |
| 6 | +# target_project_id :integer not null | |
| 7 | +# target_branch :string(255) not null | |
| 8 | +# source_project_id :integer not null | |
| 9 | +# source_branch :string(255) not null | |
| 10 | +# author_id :integer | |
| 11 | +# assignee_id :integer | |
| 12 | +# title :string(255) | |
| 13 | +# created_at :datetime | |
| 14 | +# updated_at :datetime | |
| 15 | +# st_commits :text(2147483647) | |
| 16 | +# st_diffs :text(2147483647) | |
| 17 | +# milestone_id :integer | |
| 18 | +# state :string(255) | |
| 19 | +# merge_status :string(255) | |
| 19 | 20 | # |
| 20 | 21 | |
| 21 | 22 | require Rails.root.join("app/models/commit") |
| 22 | 23 | require Rails.root.join("lib/static_model") |
| 23 | 24 | |
| 24 | 25 | class MergeRequest < ActiveRecord::Base |
| 26 | + | |
| 25 | 27 | include Issuable |
| 26 | 28 | |
| 27 | - attr_accessible :title, :assignee_id, :target_branch, :source_branch, :milestone_id, | |
| 28 | - :author_id_of_changes, :state_event | |
| 29 | + belongs_to :target_project, foreign_key: :target_project_id, class_name: "Project" | |
| 30 | + belongs_to :source_project, foreign_key: :source_project_id, class_name: "Project" | |
| 31 | + | |
| 32 | + attr_accessible :title, :assignee_id, :source_project_id, :source_branch, :target_project_id, :target_branch, :milestone_id, :author_id_of_changes, :state_event | |
| 33 | + | |
| 29 | 34 | |
| 30 | 35 | attr_accessor :should_remove_source_branch |
| 31 | 36 | |
| ... | ... | @@ -74,30 +79,37 @@ class MergeRequest < ActiveRecord::Base |
| 74 | 79 | serialize :st_commits |
| 75 | 80 | serialize :st_diffs |
| 76 | 81 | |
| 82 | + validates :source_project, presence: true | |
| 77 | 83 | validates :source_branch, presence: true |
| 84 | + validates :target_project, presence: true | |
| 78 | 85 | validates :target_branch, presence: true |
| 79 | - validate :validate_branches | |
| 86 | + validate :validate_branches | |
| 80 | 87 | |
| 88 | + 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) } | |
| 89 | + 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) } | |
| 90 | + scope :opened, -> { with_state(:opened) } | |
| 91 | + scope :closed, -> { with_state(:closed) } | |
| 81 | 92 | scope :merged, -> { with_state(:merged) } |
| 82 | - scope :by_branch, ->(branch_name) { where("source_branch LIKE :branch OR target_branch LIKE :branch", branch: branch_name) } | |
| 93 | + scope :by_branch, ->(branch_name) { where("(source_branch LIKE :branch) OR (target_branch LIKE :branch)", branch: branch_name) } | |
| 83 | 94 | scope :cared, ->(user) { where('assignee_id = :user OR author_id = :user', user: user.id) } |
| 84 | 95 | scope :by_milestone, ->(milestone) { where(milestone_id: milestone) } |
| 85 | - | |
| 96 | + scope :in_projects, ->(project_ids) { where("source_project_id in (:project_ids) OR target_project_id in (:project_ids)", project_ids: project_ids) } | |
| 97 | + scope :of_projects, ->(ids) { where(target_project_id: ids) } | |
| 86 | 98 | # Closed scope for merge request should return |
| 87 | 99 | # both merged and closed mr's |
| 88 | 100 | scope :closed, -> { with_states(:closed, :merged) } |
| 89 | 101 | |
| 90 | 102 | def validate_branches |
| 91 | - if target_branch == source_branch | |
| 92 | - errors.add :branch_conflict, "You can not use same branch for source and target branches" | |
| 103 | + if target_project==source_project && target_branch == source_branch | |
| 104 | + errors.add :branch_conflict, "You can not use same project/branch for source and target" | |
| 93 | 105 | end |
| 94 | 106 | |
| 95 | 107 | if opened? || reopened? |
| 96 | - similar_mrs = self.project.merge_requests.where(source_branch: source_branch, target_branch: target_branch).opened | |
| 108 | + similar_mrs = self.target_project.merge_requests.where(source_branch: source_branch, target_branch: target_branch, source_project_id: source_project.id).opened | |
| 97 | 109 | similar_mrs = similar_mrs.where('id not in (?)', self.id) if self.id |
| 98 | 110 | |
| 99 | 111 | if similar_mrs.any? |
| 100 | - errors.add :base, "There is already an open merge request for this branches" | |
| 112 | + errors.add :base, "Cannot Create: This merge request already exists: #{similar_mrs.pluck(:title)}" | |
| 101 | 113 | end |
| 102 | 114 | end |
| 103 | 115 | end |
| ... | ... | @@ -137,7 +149,14 @@ class MergeRequest < ActiveRecord::Base |
| 137 | 149 | end |
| 138 | 150 | |
| 139 | 151 | def unmerged_diffs |
| 140 | - Gitlab::Git::Diff.between(project.repository, source_branch, target_branch) | |
| 152 | + diffs = if for_fork? | |
| 153 | + Gitlab::Satellite::MergeAction.new(author, self).diffs_between_satellite | |
| 154 | + else | |
| 155 | + Gitlab::Git::Diff.between(target_project.repository, source_branch, target_branch) | |
| 156 | + end | |
| 157 | + | |
| 158 | + diffs ||= [] | |
| 159 | + diffs | |
| 141 | 160 | end |
| 142 | 161 | |
| 143 | 162 | def last_commit |
| ... | ... | @@ -145,11 +164,11 @@ class MergeRequest < ActiveRecord::Base |
| 145 | 164 | end |
| 146 | 165 | |
| 147 | 166 | def merge_event |
| 148 | - self.project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::MERGED).last | |
| 167 | + self.target_project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::MERGED).last | |
| 149 | 168 | end |
| 150 | 169 | |
| 151 | 170 | def closed_event |
| 152 | - self.project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::CLOSED).last | |
| 171 | + self.target_project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::CLOSED).last | |
| 153 | 172 | end |
| 154 | 173 | |
| 155 | 174 | def commits |
| ... | ... | @@ -165,15 +184,24 @@ class MergeRequest < ActiveRecord::Base |
| 165 | 184 | if opened? && unmerged_commits.any? |
| 166 | 185 | self.st_commits = dump_commits(unmerged_commits) |
| 167 | 186 | save |
| 187 | + | |
| 168 | 188 | end |
| 169 | 189 | commits |
| 170 | 190 | end |
| 171 | 191 | |
| 172 | 192 | def unmerged_commits |
| 173 | - self.project.repository. | |
| 174 | - commits_between(self.target_branch, self.source_branch). | |
| 193 | + if for_fork? | |
| 194 | + commits = Gitlab::Satellite::MergeAction.new(self.author, self).commits_between | |
| 195 | + else | |
| 196 | + commits = target_project.repository.commits_between(self.target_branch, self.source_branch) | |
| 197 | + end | |
| 198 | + | |
| 199 | + if commits.present? | |
| 200 | + commits = Commit.decorate(commits). | |
| 175 | 201 | sort_by(&:created_at). |
| 176 | 202 | reverse |
| 203 | + end | |
| 204 | + commits | |
| 177 | 205 | end |
| 178 | 206 | |
| 179 | 207 | def merge!(user_id) |
| ... | ... | @@ -199,21 +227,29 @@ class MergeRequest < ActiveRecord::Base |
| 199 | 227 | # Returns the raw diff for this merge request |
| 200 | 228 | # |
| 201 | 229 | # see "git diff" |
| 202 | - def to_diff | |
| 203 | - project.repo.git.native(:diff, {timeout: 30, raise: true}, "#{target_branch}...#{source_branch}") | |
| 230 | + def to_diff(current_user) | |
| 231 | + Gitlab::Satellite::MergeAction.new(current_user, self).diff_in_satellite | |
| 204 | 232 | end |
| 205 | 233 | |
| 206 | 234 | # Returns the commit as a series of email patches. |
| 207 | 235 | # |
| 208 | 236 | # see "git format-patch" |
| 209 | - def to_patch | |
| 210 | - project.repo.git.format_patch({timeout: 30, raise: true, stdout: true}, "#{target_branch}..#{source_branch}") | |
| 237 | + def to_patch(current_user) | |
| 238 | + Gitlab::Satellite::MergeAction.new(current_user, self).format_patch | |
| 211 | 239 | end |
| 212 | 240 | |
| 213 | 241 | def last_commit_short_sha |
| 214 | 242 | @last_commit_short_sha ||= last_commit.sha[0..10] |
| 215 | 243 | end |
| 216 | 244 | |
| 245 | + def for_fork? | |
| 246 | + target_project != source_project | |
| 247 | + end | |
| 248 | + | |
| 249 | + def disallow_source_branch_removal? | |
| 250 | + (source_project.root_ref? source_branch) || for_fork? | |
| 251 | + end | |
| 252 | + | |
| 217 | 253 | private |
| 218 | 254 | |
| 219 | 255 | def dump_commits(commits) | ... | ... |
app/models/note.rb
| ... | ... | @@ -42,8 +42,8 @@ class Note < ActiveRecord::Base |
| 42 | 42 | |
| 43 | 43 | # Scopes |
| 44 | 44 | scope :for_commit_id, ->(commit_id) { where(noteable_type: "Commit", commit_id: commit_id) } |
| 45 | - scope :inline, -> { where("line_code IS NOT NULL") } | |
| 46 | - scope :not_inline, -> { where(line_code: [nil, '']) } | |
| 45 | + scope :inline, ->{ where("line_code IS NOT NULL") } | |
| 46 | + scope :not_inline, ->{ where(line_code: [nil, '']) } | |
| 47 | 47 | |
| 48 | 48 | scope :common, ->{ where(noteable_type: ["", nil]) } |
| 49 | 49 | scope :fresh, ->{ order("created_at ASC, id ASC") } |
| ... | ... | @@ -53,10 +53,10 @@ class Note < ActiveRecord::Base |
| 53 | 53 | serialize :st_diff |
| 54 | 54 | before_create :set_diff, if: ->(n) { n.line_code.present? } |
| 55 | 55 | |
| 56 | - def self.create_status_change_note(noteable, author, status) | |
| 56 | + def self.create_status_change_note(noteable, project, author, status) | |
| 57 | 57 | create({ |
| 58 | 58 | noteable: noteable, |
| 59 | - project: noteable.project, | |
| 59 | + project: project, | |
| 60 | 60 | author: author, |
| 61 | 61 | note: "_Status changed to #{status}_" |
| 62 | 62 | }, without_protection: true) |
| ... | ... | @@ -65,7 +65,7 @@ class Note < ActiveRecord::Base |
| 65 | 65 | def commit_author |
| 66 | 66 | @commit_author ||= |
| 67 | 67 | project.users.find_by_email(noteable.author_email) || |
| 68 | - project.users.find_by_name(noteable.author_name) | |
| 68 | + project.users.find_by_name(noteable.author_name) | |
| 69 | 69 | rescue |
| 70 | 70 | nil |
| 71 | 71 | end | ... | ... |
app/models/project.rb
| ... | ... | @@ -53,7 +53,7 @@ class Project < ActiveRecord::Base |
| 53 | 53 | |
| 54 | 54 | has_many :services, dependent: :destroy |
| 55 | 55 | has_many :events, dependent: :destroy |
| 56 | - has_many :merge_requests, dependent: :destroy | |
| 56 | + has_many :merge_requests, dependent: :destroy, foreign_key: "target_project_id" | |
| 57 | 57 | has_many :issues, dependent: :destroy, order: "state DESC, created_at DESC" |
| 58 | 58 | has_many :milestones, dependent: :destroy |
| 59 | 59 | has_many :notes, dependent: :destroy | ... | ... |
app/observers/activity_observer.rb
| 1 | 1 | class ActivityObserver < BaseObserver |
| 2 | - observe :issue, :merge_request, :note, :milestone | |
| 2 | + observe :issue, :note, :milestone | |
| 3 | 3 | |
| 4 | 4 | def after_create(record) |
| 5 | 5 | event_author_id = record.author_id |
| ... | ... | @@ -13,47 +13,27 @@ class ActivityObserver < BaseObserver |
| 13 | 13 | end |
| 14 | 14 | |
| 15 | 15 | if event_author_id |
| 16 | - Event.create( | |
| 17 | - project: record.project, | |
| 18 | - target_id: record.id, | |
| 19 | - target_type: record.class.name, | |
| 20 | - action: Event.determine_action(record), | |
| 21 | - author_id: event_author_id | |
| 22 | - ) | |
| 16 | + create_event(record, Event.determine_action(record)) | |
| 23 | 17 | end |
| 24 | 18 | end |
| 25 | 19 | |
| 26 | 20 | def after_close(record, transition) |
| 27 | - Event.create( | |
| 28 | - project: record.project, | |
| 29 | - target_id: record.id, | |
| 30 | - target_type: record.class.name, | |
| 31 | - action: Event::CLOSED, | |
| 32 | - author_id: record.author_id_of_changes | |
| 33 | - ) | |
| 21 | + create_event(record, Event::CLOSED) | |
| 34 | 22 | end |
| 35 | 23 | |
| 36 | 24 | def after_reopen(record, transition) |
| 37 | - Event.create( | |
| 38 | - project: record.project, | |
| 39 | - target_id: record.id, | |
| 40 | - target_type: record.class.name, | |
| 41 | - action: Event::REOPENED, | |
| 42 | - author_id: record.author_id_of_changes | |
| 43 | - ) | |
| 25 | + create_event(record, Event::REOPENED) | |
| 44 | 26 | end |
| 45 | 27 | |
| 46 | - def after_merge(record, transition) | |
| 47 | - # Since MR can be merged via sidekiq | |
| 48 | - # to prevent event duplication do this check | |
| 49 | - return true if record.merge_event | |
| 28 | + protected | |
| 50 | 29 | |
| 30 | + def create_event(record, status) | |
| 51 | 31 | Event.create( |
| 52 | - project: record.project, | |
| 53 | - target_id: record.id, | |
| 54 | - target_type: record.class.name, | |
| 55 | - action: Event::MERGED, | |
| 56 | - author_id: record.author_id_of_changes | |
| 32 | + project: record.project, | |
| 33 | + target_id: record.id, | |
| 34 | + target_type: record.class.name, | |
| 35 | + action: status, | |
| 36 | + author_id: record.author_id | |
| 57 | 37 | ) |
| 58 | 38 | end |
| 59 | 39 | end | ... | ... |
app/observers/issue_observer.rb
| ... | ... | @@ -23,6 +23,6 @@ class IssueObserver < BaseObserver |
| 23 | 23 | |
| 24 | 24 | # Create issue note with service comment like 'Status changed to closed' |
| 25 | 25 | def create_note(issue) |
| 26 | - Note.create_status_change_note(issue, current_user, issue.state) | |
| 26 | + Note.create_status_change_note(issue, issue.project, current_user, issue.state) | |
| 27 | 27 | end |
| 28 | 28 | end | ... | ... |
app/observers/merge_request_observer.rb
| 1 | -class MergeRequestObserver < BaseObserver | |
| 1 | +class MergeRequestObserver < ActivityObserver | |
| 2 | + observe :merge_request | |
| 3 | + | |
| 2 | 4 | def after_create(merge_request) |
| 5 | + if merge_request.author_id | |
| 6 | + create_event(merge_request, Event.determine_action(merge_request)) | |
| 7 | + end | |
| 8 | + | |
| 3 | 9 | notification.new_merge_request(merge_request, current_user) |
| 4 | 10 | end |
| 5 | 11 | |
| 6 | 12 | def after_close(merge_request, transition) |
| 7 | - Note.create_status_change_note(merge_request, current_user, merge_request.state) | |
| 13 | + create_event(merge_request, Event::CLOSED) | |
| 14 | + Note.create_status_change_note(merge_request, merge_request.target_project, current_user, merge_request.state) | |
| 8 | 15 | |
| 9 | 16 | notification.close_mr(merge_request, current_user) |
| 10 | 17 | end |
| 11 | 18 | |
| 12 | 19 | def after_merge(merge_request, transition) |
| 13 | 20 | notification.merge_mr(merge_request) |
| 21 | + # Since MR can be merged via sidekiq | |
| 22 | + # to prevent event duplication do this check | |
| 23 | + return true if merge_request.merge_event | |
| 24 | + | |
| 25 | + Event.create( | |
| 26 | + project: merge_request.target_project, | |
| 27 | + target_id: merge_request.id, | |
| 28 | + target_type: merge_request.class.name, | |
| 29 | + action: Event::MERGED, | |
| 30 | + author_id: merge_request.author_id_of_changes | |
| 31 | + ) | |
| 14 | 32 | end |
| 15 | 33 | |
| 16 | 34 | def after_reopen(merge_request, transition) |
| 17 | - Note.create_status_change_note(merge_request, current_user, merge_request.state) | |
| 35 | + create_event(merge_request, Event::REOPENED) | |
| 36 | + Note.create_status_change_note(merge_request, merge_request.target_project, current_user, merge_request.state) | |
| 18 | 37 | end |
| 19 | 38 | |
| 20 | 39 | def after_update(merge_request) |
| 21 | 40 | notification.reassigned_merge_request(merge_request, current_user) if merge_request.is_being_reassigned? |
| 22 | 41 | end |
| 42 | + | |
| 43 | + def create_event(record, status) | |
| 44 | + Event.create( | |
| 45 | + project: record.target_project, | |
| 46 | + target_id: record.id, | |
| 47 | + target_type: record.class.name, | |
| 48 | + action: status, | |
| 49 | + author_id: record.author_id | |
| 50 | + ) | |
| 51 | + end | |
| 52 | + | |
| 23 | 53 | end | ... | ... |
app/services/notification_service.rb
| ... | ... | @@ -23,7 +23,7 @@ class NotificationService |
| 23 | 23 | # * project team members with notification level higher then Participating |
| 24 | 24 | # |
| 25 | 25 | def new_issue(issue, current_user) |
| 26 | - new_resource_email(issue, 'new_issue_email') | |
| 26 | + new_resource_email(issue, issue.project, 'new_issue_email') | |
| 27 | 27 | end |
| 28 | 28 | |
| 29 | 29 | # When we close an issue we should send next emails: |
| ... | ... | @@ -33,7 +33,7 @@ class NotificationService |
| 33 | 33 | # * project team members with notification level higher then Participating |
| 34 | 34 | # |
| 35 | 35 | def close_issue(issue, current_user) |
| 36 | - close_resource_email(issue, current_user, 'closed_issue_email') | |
| 36 | + close_resource_email(issue, issue.project, current_user, 'closed_issue_email') | |
| 37 | 37 | end |
| 38 | 38 | |
| 39 | 39 | # When we reassign an issue we should send next emails: |
| ... | ... | @@ -42,7 +42,7 @@ class NotificationService |
| 42 | 42 | # * issue new assignee if his notification level is not Disabled |
| 43 | 43 | # |
| 44 | 44 | def reassigned_issue(issue, current_user) |
| 45 | - reassign_resource_email(issue, current_user, 'reassigned_issue_email') | |
| 45 | + reassign_resource_email(issue, issue.project, current_user, 'reassigned_issue_email') | |
| 46 | 46 | end |
| 47 | 47 | |
| 48 | 48 | |
| ... | ... | @@ -51,7 +51,7 @@ class NotificationService |
| 51 | 51 | # * mr assignee if his notification level is not Disabled |
| 52 | 52 | # |
| 53 | 53 | def new_merge_request(merge_request, current_user) |
| 54 | - new_resource_email(merge_request, 'new_merge_request_email') | |
| 54 | + new_resource_email(merge_request, merge_request.target_project, 'new_merge_request_email') | |
| 55 | 55 | end |
| 56 | 56 | |
| 57 | 57 | # When we reassign a merge_request we should send next emails: |
| ... | ... | @@ -60,7 +60,7 @@ class NotificationService |
| 60 | 60 | # * merge_request assignee if his notification level is not Disabled |
| 61 | 61 | # |
| 62 | 62 | def reassigned_merge_request(merge_request, current_user) |
| 63 | - reassign_resource_email(merge_request, current_user, 'reassigned_merge_request_email') | |
| 63 | + reassign_resource_email(merge_request, merge_request.target_project, current_user, 'reassigned_merge_request_email') | |
| 64 | 64 | end |
| 65 | 65 | |
| 66 | 66 | # When we close a merge request we should send next emails: |
| ... | ... | @@ -70,7 +70,7 @@ class NotificationService |
| 70 | 70 | # * project team members with notification level higher then Participating |
| 71 | 71 | # |
| 72 | 72 | def close_mr(merge_request, current_user) |
| 73 | - close_resource_email(merge_request, current_user, 'closed_merge_request_email') | |
| 73 | + close_resource_email(merge_request, merge_request.target_project, current_user, 'closed_merge_request_email') | |
| 74 | 74 | end |
| 75 | 75 | |
| 76 | 76 | # When we merge a merge request we should send next emails: |
| ... | ... | @@ -80,8 +80,8 @@ class NotificationService |
| 80 | 80 | # * project team members with notification level higher then Participating |
| 81 | 81 | # |
| 82 | 82 | def merge_mr(merge_request) |
| 83 | - recipients = reject_muted_users([merge_request.author, merge_request.assignee], merge_request.project) | |
| 84 | - recipients = recipients.concat(project_watchers(merge_request.project)).uniq | |
| 83 | + recipients = reject_muted_users([merge_request.author, merge_request.assignee], merge_request.target_project) | |
| 84 | + recipients = recipients.concat(project_watchers(merge_request.target_project)).uniq | |
| 85 | 85 | |
| 86 | 86 | recipients.each do |recipient| |
| 87 | 87 | mailer.merged_merge_request_email(recipient.id, merge_request.id) |
| ... | ... | @@ -194,14 +194,14 @@ class NotificationService |
| 194 | 194 | end |
| 195 | 195 | end |
| 196 | 196 | |
| 197 | - def new_resource_email(target, method) | |
| 197 | + def new_resource_email(target, project, method) | |
| 198 | 198 | if target.respond_to?(:participants) |
| 199 | 199 | recipients = target.participants |
| 200 | 200 | else |
| 201 | 201 | recipients = [] |
| 202 | 202 | end |
| 203 | - recipients = reject_muted_users(recipients, target.project) | |
| 204 | - recipients = recipients.concat(project_watchers(target.project)).uniq | |
| 203 | + recipients = reject_muted_users(recipients, project) | |
| 204 | + recipients = recipients.concat(project_watchers(project)).uniq | |
| 205 | 205 | recipients.delete(target.author) |
| 206 | 206 | |
| 207 | 207 | recipients.each do |recipient| |
| ... | ... | @@ -209,9 +209,9 @@ class NotificationService |
| 209 | 209 | end |
| 210 | 210 | end |
| 211 | 211 | |
| 212 | - def close_resource_email(target, current_user, method) | |
| 213 | - recipients = reject_muted_users([target.author, target.assignee], target.project) | |
| 214 | - recipients = recipients.concat(project_watchers(target.project)).uniq | |
| 212 | + def close_resource_email(target, project, current_user, method) | |
| 213 | + recipients = reject_muted_users([target.author, target.assignee], project) | |
| 214 | + recipients = recipients.concat(project_watchers(project)).uniq | |
| 215 | 215 | recipients.delete(current_user) |
| 216 | 216 | |
| 217 | 217 | recipients.each do |recipient| |
| ... | ... | @@ -219,14 +219,14 @@ class NotificationService |
| 219 | 219 | end |
| 220 | 220 | end |
| 221 | 221 | |
| 222 | - def reassign_resource_email(target, current_user, method) | |
| 222 | + def reassign_resource_email(target, project, current_user, method) | |
| 223 | 223 | recipients = User.where(id: [target.assignee_id, target.assignee_id_was]) |
| 224 | 224 | |
| 225 | 225 | # Add watchers to email list |
| 226 | - recipients = recipients.concat(project_watchers(target.project)) | |
| 226 | + recipients = recipients.concat(project_watchers(project)) | |
| 227 | 227 | |
| 228 | 228 | # reject users with disabled notifications |
| 229 | - recipients = reject_muted_users(recipients, target.project) | |
| 229 | + recipients = reject_muted_users(recipients, project) | |
| 230 | 230 | |
| 231 | 231 | # Reject me from recipients if I reassign an item |
| 232 | 232 | recipients.delete(current_user) | ... | ... |
app/views/notify/closed_merge_request_email.html.haml
| 1 | 1 | %p |
| 2 | 2 | = "Merge Request #{@merge_request.id} was closed by #{@updated_by.name}" |
| 3 | 3 | %p |
| 4 | - = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.project, @merge_request) | |
| 4 | + = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.target_project, @merge_request) | |
| 5 | 5 | %p |
| 6 | - Branches: #{@merge_request.source_branch} → #{@merge_request.target_branch} | |
| 6 | + != merge_path_description(@merge_request, '→') | |
| 7 | 7 | %p |
| 8 | 8 | Assignee: #{@merge_request.author_name} → #{@merge_request.assignee_name} |
| 9 | 9 | ... | ... |
app/views/notify/closed_merge_request_email.text.haml
| 1 | 1 | = "Merge Request #{@merge_request.id} was closed by #{@updated_by.name}" |
| 2 | 2 | |
| 3 | -Merge Request url: #{project_merge_request_url(@merge_request.project, @merge_request)} | |
| 3 | +Merge Request url: #{project_merge_request_url(@merge_request.target_project, @merge_request)} | |
| 4 | 4 | |
| 5 | -Branches: #{@merge_request.source_branch} - #{@merge_request.target_branch} | |
| 5 | += merge_path_description(@merge_request, 'to') | |
| 6 | 6 | |
| 7 | 7 | Author: #{@merge_request.author_name} |
| 8 | 8 | Assignee: #{@merge_request.assignee_name} | ... | ... |
app/views/notify/merged_merge_request_email.html.haml
| 1 | 1 | %p |
| 2 | 2 | = "Merge Request #{@merge_request.id} was merged" |
| 3 | 3 | %p |
| 4 | - = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.project, @merge_request) | |
| 4 | + = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.target_project, @merge_request) | |
| 5 | 5 | %p |
| 6 | - Branches: #{@merge_request.source_branch} → #{@merge_request.target_branch} | |
| 6 | + != merge_path_description(@merge_request, '→') | |
| 7 | 7 | %p |
| 8 | 8 | Assignee: #{@merge_request.author_name} → #{@merge_request.assignee_name} |
| 9 | 9 | ... | ... |
app/views/notify/merged_merge_request_email.text.haml
| 1 | 1 | = "Merge Request #{@merge_request.id} was merged" |
| 2 | 2 | |
| 3 | -Merge Request Url: #{project_merge_request_url(@merge_request.project, @merge_request)} | |
| 3 | +Merge Request Url: #{project_merge_request_url(@merge_request.target_project, @merge_request)} | |
| 4 | 4 | |
| 5 | -Branches: #{@merge_request.source_branch} - #{@merge_request.target_branch} | |
| 5 | += merge_path_description(@merge_request, 'to') | |
| 6 | 6 | |
| 7 | 7 | Author: #{@merge_request.author_name} |
| 8 | 8 | Assignee: #{@merge_request.assignee_name} | ... | ... |
app/views/notify/new_merge_request_email.html.haml
| 1 | 1 | %p |
| 2 | 2 | = "New Merge Request !#{@merge_request.id}" |
| 3 | 3 | %p |
| 4 | - = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.project, @merge_request) | |
| 4 | + = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.target_project, @merge_request) | |
| 5 | 5 | %p |
| 6 | - Branches: #{@merge_request.source_branch} → #{@merge_request.target_branch} | |
| 6 | + != merge_path_description(@merge_request, '→') | |
| 7 | 7 | %p |
| 8 | 8 | Assignee: #{@merge_request.author_name} → #{@merge_request.assignee_name} |
| 9 | 9 | ... | ... |
app/views/notify/new_merge_request_email.text.erb
| 1 | 1 | New Merge Request <%= @merge_request.id %> |
| 2 | 2 | |
| 3 | -<%= url_for(project_merge_request_url(@merge_request.project, @merge_request)) %> | |
| 4 | - | |
| 3 | +<%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request)) %> | |
| 5 | 4 | |
| 6 | -Branches: <%= @merge_request.source_branch %> to <%= @merge_request.target_branch %> | |
| 5 | +<%= merge_path_description(@merge_request, 'to') %> | |
| 7 | 6 | Author: <%= @merge_request.author_name %> |
| 8 | 7 | Asignee: <%= @merge_request.assignee_name %> |
| 9 | 8 | ... | ... |
app/views/notify/note_merge_request_email.html.haml
| 1 | 1 | %p |
| 2 | 2 | - if @note.for_diff_line? |
| 3 | - = link_to "New comment on diff", diffs_project_merge_request_url(@merge_request.project, @merge_request, anchor: "note_#{@note.id}") | |
| 3 | + = link_to "New comment on diff", diffs_project_merge_request_url(@merge_request.target_project, @merge_request, anchor: "note_#{@note.id}") | |
| 4 | 4 | - else |
| 5 | - = link_to "New comment", project_merge_request_url(@merge_request.project, @merge_request, anchor: "note_#{@note.id}") | |
| 5 | + = link_to "New comment", project_merge_request_url(@merge_request.target_project, @merge_request, anchor: "note_#{@note.id}") | |
| 6 | 6 | for Merge Request ##{@merge_request.id} |
| 7 | 7 | %cite "#{truncate(@merge_request.title, length: 20)}" |
| 8 | 8 | = render 'note_message' | ... | ... |
app/views/notify/note_merge_request_email.text.erb
| 1 | 1 | New comment for Merge Request <%= @merge_request.id %> |
| 2 | 2 | |
| 3 | -<%= url_for(project_merge_request_url(@merge_request.project, @merge_request, anchor: "note_#{@note.id}")) %> | |
| 3 | +<%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request, anchor: "note_#{@note.id}")) %> | |
| 4 | 4 | |
| 5 | 5 | |
| 6 | 6 | <%= @note.author_name %> | ... | ... |
app/views/notify/reassigned_merge_request_email.html.haml
| 1 | 1 | %p |
| 2 | 2 | = "Reassigned Merge Request !#{@merge_request.id}" |
| 3 | - = link_to_gfm truncate(@merge_request.title, length: 30), project_merge_request_url(@merge_request.project, @merge_request) | |
| 3 | + = link_to_gfm truncate(@merge_request.title, length: 30), project_merge_request_url(@merge_request.target_project, @merge_request) | |
| 4 | 4 | %p |
| 5 | 5 | Assignee changed |
| 6 | 6 | - if @previous_assignee | ... | ... |
app/views/notify/reassigned_merge_request_email.text.erb
| 1 | 1 | Reassigned Merge Request <%= @merge_request.id %> |
| 2 | 2 | |
| 3 | -<%= url_for(project_merge_request_url(@merge_request.project, @merge_request)) %> | |
| 3 | +<%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request)) %> | |
| 4 | 4 | |
| 5 | 5 | |
| 6 | 6 | Assignee changed <%= "from #{@previous_assignee.name}" if @previous_assignee %> to <%= @merge_request.assignee_name %> | ... | ... |
app/views/projects/commit/show.html.haml
app/views/projects/commits/_commit.html.haml
| 1 | 1 | %li.commit |
| 2 | 2 | .browse_code_link_holder |
| 3 | 3 | %p |
| 4 | - %strong= link_to "Browse Code »", project_tree_path(@project, commit), class: "right" | |
| 4 | + %strong= link_to "Browse Code »", project_tree_path(project, commit), class: "right" | |
| 5 | 5 | %p |
| 6 | - = link_to commit.short_id(8), project_commit_path(@project, commit), class: "commit_short_id" | |
| 6 | + = link_to commit.short_id(8), project_commit_path(project, commit), class: "commit_short_id" | |
| 7 | 7 | = commit_author_link(commit, avatar: true, size: 24) |
| 8 | 8 | |
| 9 | - = link_to_gfm truncate(commit.title, length: 70), project_commit_path(@project, commit.id), class: "row_title" | |
| 9 | + = link_to_gfm truncate(commit.title, length: 70), project_commit_path(project, commit.id), class: "row_title" | |
| 10 | 10 | |
| 11 | 11 | %time.committed_ago{ datetime: commit.committed_date, title: commit.committed_date.stamp("Aug 21, 2011 9:23pm") } |
| 12 | 12 | = time_ago_in_words(commit.committed_date) |
| ... | ... | @@ -14,7 +14,7 @@ |
| 14 | 14 | |
| 15 | 15 | |
| 16 | 16 | %span.notes_count |
| 17 | - - notes = @project.notes.for_commit_id(commit.id) | |
| 17 | + - notes = project.notes.for_commit_id(commit.id) | |
| 18 | 18 | - if notes.any? |
| 19 | 19 | %span.badge.badge-info |
| 20 | 20 | %i.icon-comment | ... | ... |
app/views/projects/commits/_commits.html.haml
app/views/projects/commits/_diffs.html.haml
| ... | ... | @@ -5,7 +5,7 @@ |
| 5 | 5 | %p To prevent performance issue we rejected diff information. |
| 6 | 6 | %p |
| 7 | 7 | But if you still want to see diff |
| 8 | - = link_to "click this link", project_commit_path(@project, @commit, force_show_diff: true), class: "underlined_link" | |
| 8 | + = link_to "click this link", project_commit_path(project, @commit, force_show_diff: true), class: "underlined_link" | |
| 9 | 9 | |
| 10 | 10 | %p.commit-stat-summary |
| 11 | 11 | Showing |
| ... | ... | @@ -23,8 +23,8 @@ |
| 23 | 23 | - unless @suppress_diff |
| 24 | 24 | - diffs.each_with_index do |diff, i| |
| 25 | 25 | - next if diff.diff.empty? |
| 26 | - - file = Gitlab::Git::Blob.new(@repository, @commit.id, @ref, diff.new_path) | |
| 27 | - - file = Gitlab::Git::Blob.new(@repository, @commit.parent_id, @ref, diff.old_path) unless file.exists? | |
| 26 | + - file = Gitlab::Git::Blob.new(project.repository, @commit.id, @ref, diff.new_path) | |
| 27 | + - file = Gitlab::Git::Blob.new(project.repository, @commit.parent_id, @ref, diff.old_path) unless file.exists? | |
| 28 | 28 | - next unless file.exists? |
| 29 | 29 | .file{id: "diff-#{i}"} |
| 30 | 30 | .header |
| ... | ... | @@ -32,7 +32,7 @@ |
| 32 | 32 | %span= diff.old_path |
| 33 | 33 | |
| 34 | 34 | - if @commit.parent_ids.present? |
| 35 | - = link_to project_blob_path(@project, tree_join(@commit.parent_id, diff.new_path)), {:class => 'btn btn-tiny pull-right view-file'} do | |
| 35 | + = link_to project_blob_path(project, tree_join(@commit.parent_id, diff.new_path)), {:class => 'btn btn-tiny pull-right view-file'} do | |
| 36 | 36 | View file @ |
| 37 | 37 | %span.commit-short-id= @commit.short_id(6) |
| 38 | 38 | - else |
| ... | ... | @@ -40,7 +40,7 @@ |
| 40 | 40 | - if diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode |
| 41 | 41 | %span.file-mode= "#{diff.a_mode} → #{diff.b_mode}" |
| 42 | 42 | |
| 43 | - = link_to project_blob_path(@project, tree_join(@commit.id, diff.new_path)), {:class => 'btn btn-tiny pull-right view-file'} do | |
| 43 | + = link_to project_blob_path(project, tree_join(@commit.id, diff.new_path)), {:class => 'btn btn-tiny pull-right view-file'} do | |
| 44 | 44 | View file @ |
| 45 | 45 | %span.commit-short-id= @commit.short_id(6) |
| 46 | 46 | |
| ... | ... | @@ -50,7 +50,7 @@ |
| 50 | 50 | - if file.text? |
| 51 | 51 | = render "projects/commits/text_file", diff: diff, index: i |
| 52 | 52 | - elsif file.image? |
| 53 | - - old_file = Gitlab::Git::Blob.new(@repository, @commit.parent_id, @ref, diff.old_path) if @commit.parent_id | |
| 53 | + - old_file = Gitlab::Git::Blob.new(project.repository, @commit.parent_id, @ref, diff.old_path) if @commit.parent_id | |
| 54 | 54 | = render "projects/commits/image", diff: diff, old_file: old_file, file: file, index: i |
| 55 | 55 | - else |
| 56 | 56 | %p.nothing_here_message No preview for this file type | ... | ... |
app/views/projects/compare/show.html.haml
| ... | ... | @@ -15,8 +15,8 @@ |
| 15 | 15 | %div.ui-box |
| 16 | 16 | .title |
| 17 | 17 | Commits (#{@commits.count}) |
| 18 | - %ul.well-list= render Commit.decorate(@commits) | |
| 18 | + %ul.well-list= render Commit.decorate(@commits), project: @project | |
| 19 | 19 | |
| 20 | 20 | - unless @diffs.empty? |
| 21 | 21 | %h4 Diff |
| 22 | - = render "projects/commits/diffs", diffs: @diffs | |
| 22 | + = render "projects/commits/diffs", diffs: @diffs, project: @project | ... | ... |
app/views/projects/merge_requests/_form.html.haml
| ... | ... | @@ -12,17 +12,27 @@ |
| 12 | 12 | .row |
| 13 | 13 | .span5 |
| 14 | 14 | .light-well |
| 15 | - %h5.cgray From (Head Branch) | |
| 16 | - = f.select(:source_branch, @repository.branch_names, { include_blank: "Select branch" }, {class: 'chosen span4'}) | |
| 15 | + %h5.cgray From | |
| 16 | + .padded | |
| 17 | + = f.select(:source_project_id,[[@merge_request.source_project.path_with_namespace,@merge_request.source_project.id]] , {}, {class: 'source_project chosen span4'}) | |
| 18 | + .prepend-top-10 | |
| 19 | + %i.icon-code-fork | |
| 20 | + | |
| 21 | + = f.select(:source_branch, @merge_request.source_project.repository.branch_names, { include_blank: "Select branch" }, {class: 'source_branch chosen span3'}) | |
| 17 | 22 | .mr_source_commit.prepend-top-10 |
| 18 | - | |
| 19 | 23 | .span2 |
| 20 | 24 | %h1.merge-request-angle |
| 21 | 25 | %i.icon-angle-right |
| 22 | 26 | .span5 |
| 23 | 27 | .light-well |
| 24 | - %h5.cgray To (Base Branch) | |
| 25 | - = f.select(:target_branch, @repository.branch_names, { include_blank: "Select branch" }, {class: 'chosen span4'}) | |
| 28 | + %h5.cgray To | |
| 29 | + - projects = @project.forked_from_project.nil? ? [@project] : [ @project,@project.forked_from_project] | |
| 30 | + .padded | |
| 31 | + = f.select(:target_project_id, projects.map { |proj| [proj.path_with_namespace,proj.id] }, {include_blank: "Select Target Project" }, {class: 'target_project chosen span4'}) | |
| 32 | + .prepend-top-10 | |
| 33 | + %i.icon-code-fork | |
| 34 | + | |
| 35 | + = f.select(:target_branch, @target_branches, { include_blank: "Select branch" }, {class: 'target_branch chosen span3'}) | |
| 26 | 36 | .mr_target_commit.prepend-top-10 |
| 27 | 37 | |
| 28 | 38 | %hr |
| ... | ... | @@ -47,32 +57,34 @@ |
| 47 | 57 | Milestone |
| 48 | 58 | .controls= f.select(:milestone_id, @project.milestones.active.all.map {|p| [ p.title, p.id ] }, { include_blank: "Select milestone" }, {class: 'chosen'}) |
| 49 | 59 | |
| 50 | - | |
| 51 | 60 | .form-actions |
| 52 | 61 | - if @merge_request.new_record? |
| 53 | 62 | = f.submit 'Submit merge request', class: "btn btn-create" |
| 54 | 63 | -else |
| 55 | 64 | = f.submit 'Save changes', class: "btn btn-save" |
| 56 | 65 | - if @merge_request.new_record? |
| 57 | - = link_to project_merge_requests_path(@project), class: "btn btn-cancel" do | |
| 66 | + = link_to project_merge_requests_path(@source_project), class: "btn btn-cancel" do | |
| 58 | 67 | Cancel |
| 59 | 68 | - else |
| 60 | - = link_to project_merge_request_path(@project, @merge_request), class: "btn btn-cancel" do | |
| 69 | + = link_to project_merge_request_path(@target_project, @merge_request), class: "btn btn-cancel" do | |
| 61 | 70 | Cancel |
| 62 | 71 | |
| 63 | 72 | :javascript |
| 64 | 73 | disableButtonIfEmptyField("#merge_request_title", ".btn-save"); |
| 65 | 74 | |
| 66 | 75 | var source_branch = $("#merge_request_source_branch") |
| 67 | - , target_branch = $("#merge_request_target_branch"); | |
| 76 | + , target_branch = $("#merge_request_target_branch") | |
| 77 | + , target_project = $("#merge_request_target_project_id"); | |
| 68 | 78 | |
| 69 | - $.get("#{branch_from_project_merge_requests_path(@project)}", {ref: source_branch.val() }); | |
| 70 | - $.get("#{branch_to_project_merge_requests_path(@project)}", {ref: target_branch.val() }); | |
| 79 | + $.get("#{branch_from_project_merge_requests_path(@source_project)}", {ref: source_branch.val() }); | |
| 80 | + $.get("#{branch_to_project_merge_requests_path(@source_project)}", {target_project_id: target_project.val(),ref: target_branch.val() }); | |
| 71 | 81 | |
| 72 | - source_branch.live("change", function() { | |
| 73 | - $.get("#{branch_from_project_merge_requests_path(@project)}", {ref: $(this).val() }); | |
| 82 | + target_project.on("change", function() { | |
| 83 | + $.get("#{update_branches_project_merge_requests_path(@source_project)}", {target_project_id: $(this).val() }); | |
| 74 | 84 | }); |
| 75 | - | |
| 76 | - target_branch.live("change", function() { | |
| 77 | - $.get("#{branch_to_project_merge_requests_path(@project)}", {ref: $(this).val() }); | |
| 85 | + source_branch.on("change", function() { | |
| 86 | + $.get("#{branch_from_project_merge_requests_path(@source_project)}", {ref: $(this).val() }); | |
| 87 | + }); | |
| 88 | + target_branch.on("change", function() { | |
| 89 | + $.get("#{branch_to_project_merge_requests_path(@source_project)}", {target_project_id: target_project.val(),ref: $(this).val() }); | |
| 78 | 90 | }); | ... | ... |
app/views/projects/merge_requests/_merge_request.html.haml
| 1 | 1 | %li{ class: mr_css_classes(merge_request) } |
| 2 | 2 | .merge-request-title |
| 3 | 3 | %span.light= "##{merge_request.id}" |
| 4 | - = link_to_gfm truncate(merge_request.title, length: 80), project_merge_request_path(merge_request.project, merge_request), class: "row_title" | |
| 4 | + = link_to_gfm truncate(merge_request.title, length: 80), project_merge_request_path(merge_request.target_project, merge_request), class: "row_title" | |
| 5 | 5 | - if merge_request.merged? |
| 6 | 6 | %small.pull-right |
| 7 | 7 | %i.icon-ok |
| 8 | 8 | = "MERGED" |
| 9 | 9 | - else |
| 10 | 10 | %span.pull-right |
| 11 | - %i.icon-angle-right | |
| 12 | - = merge_request.target_branch | |
| 11 | + - if merge_request.for_fork? | |
| 12 | + %span.light | |
| 13 | + = "#{merge_request.source_project.path_with_namespace}" | |
| 14 | + = "#{merge_request.source_branch}" | |
| 15 | + %i.icon-angle-right.light | |
| 16 | + = "#{merge_request.target_branch}" | |
| 17 | + - else | |
| 18 | + = "#{merge_request.source_branch}" | |
| 19 | + %i.icon-angle-right.light | |
| 20 | + = "#{merge_request.target_branch}" | |
| 13 | 21 | .merge-request-info |
| 14 | 22 | - if merge_request.author |
| 15 | - authored by #{link_to_member(@project, merge_request.author)} | |
| 23 | + authored by #{link_to_member(merge_request.source_project, merge_request.author)} | |
| 16 | 24 | - if merge_request.votes_count > 0 |
| 17 | 25 | = render 'votes/votes_inline', votable: merge_request |
| 18 | 26 | - if merge_request.notes.any? | ... | ... |
app/views/projects/merge_requests/branch_from.js.haml
| 1 | 1 | :plain |
| 2 | - $(".mr_source_commit").html("#{commit_to_html(@commit)}"); | |
| 2 | + $(".mr_source_commit").html("#{commit_to_html(@commit, @source_project)}"); | |
| 3 | 3 | var mrTitle = $('#merge_request_title'); |
| 4 | 4 | |
| 5 | - if(mrTitle.is(":empty")) { | |
| 5 | + if(mrTitle.val().length == 0) { | |
| 6 | 6 | mrTitle.val("#{params[:ref].titleize}"); |
| 7 | 7 | } | ... | ... |
app/views/projects/merge_requests/branch_to.js.haml
app/views/projects/merge_requests/show/_commits.html.haml
| ... | ... | @@ -7,19 +7,19 @@ |
| 7 | 7 | - if @commits.count > 8 |
| 8 | 8 | %ul.first-commits.well-list |
| 9 | 9 | - @commits.first(8).each do |commit| |
| 10 | - = render "projects/commits/commit", commit: commit | |
| 10 | + = render "projects/commits/commit", commit: commit, project: @merge_request.source_project | |
| 11 | 11 | %li.bottom |
| 12 | 12 | 8 of #{@commits.count} commits displayed. |
| 13 | 13 | %strong |
| 14 | 14 | %a.show-all-commits Click here to show all |
| 15 | 15 | %ul.all-commits.hide.well-list |
| 16 | 16 | - @commits.each do |commit| |
| 17 | - = render "projects/commits/commit", commit: commit | |
| 17 | + = render "projects/commits/commit", commit: commit, project: @merge_request.source_project | |
| 18 | 18 | |
| 19 | 19 | - else |
| 20 | 20 | %ul.well-list |
| 21 | 21 | - @commits.each do |commit| |
| 22 | - = render "projects/commits/commit", commit: commit | |
| 22 | + = render "projects/commits/commit", commit: commit, project: @merge_request.source_project | |
| 23 | 23 | |
| 24 | 24 | - else |
| 25 | 25 | %h4.nothing_here_message | ... | ... |
app/views/projects/merge_requests/show/_diffs.html.haml
| 1 | 1 | - if @merge_request.valid_diffs? |
| 2 | - = render "projects/commits/diffs", diffs: @merge_request.diffs | |
| 2 | + = render "projects/commits/diffs", diffs: @merge_request.diffs, project: @merge_request.source_project | |
| 3 | 3 | - elsif @merge_request.broken_diffs? |
| 4 | 4 | %h4.nothing_here_message |
| 5 | 5 | Can't load diff. |
| 6 | 6 | You can |
| 7 | - = link_to "download it", project_merge_request_path(@project, @merge_request, format: :diff), class: "vlink" | |
| 7 | + = link_to "download it", project_merge_request_path(@merge_request.source_project, @merge_request), format: :diff, class: "vlink" | |
| 8 | 8 | instead. |
| 9 | 9 | - else |
| 10 | 10 | %h4.nothing_here_message Nothing to merge | ... | ... |
app/views/projects/merge_requests/show/_how_to_merge.html.haml
| ... | ... | @@ -3,17 +3,49 @@ |
| 3 | 3 | %a.close{href: "#", "data-dismiss" => "modal"} × |
| 4 | 4 | %h3 How to merge |
| 5 | 5 | .modal-body |
| 6 | - %p | |
| 7 | - %strong Step 1. | |
| 8 | - Checkout target branch and get recent objects from GitLab | |
| 9 | - %pre.dark | |
| 10 | - :preserve | |
| 11 | - git checkout #{@merge_request.target_branch} | |
| 12 | - git fetch origin | |
| 13 | - %p | |
| 14 | - %strong Step 2. | |
| 15 | - Merge source branch into target branch and push changes to GitLab | |
| 16 | - %pre.dark | |
| 17 | - :preserve | |
| 18 | - git merge origin/#{@merge_request.source_branch} | |
| 19 | - git push origin #{@merge_request.target_branch} | |
| 6 | + - if @merge_request.for_fork? | |
| 7 | + - source_remote = @merge_request.source_project.namespace.nil? ? "source" :@merge_request.source_project.namespace.path | |
| 8 | + - target_remote = @merge_request.target_project.namespace.nil? ? "target" :@merge_request.target_project.namespace.path | |
| 9 | + %p | |
| 10 | + %strong Step 1. | |
| 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} | |
| 13 | + remote for #{@merge_request.source_project.path_with_namespace} is called #{source_remote} | |
| 14 | + %pre.dark | |
| 15 | + :preserve | |
| 16 | + git checkout #{target_remote} #{@merge_request.target_branch} | |
| 17 | + git fetch #{source_remote} | |
| 18 | + %p | |
| 19 | + %strong Step 2. | |
| 20 | + Merge source branch into target branch and push changes to GitLab | |
| 21 | + %pre.dark | |
| 22 | + :preserve | |
| 23 | + git merge #{source_remote}/#{@merge_request.source_branch} | |
| 24 | + git push #{target_remote} #{@merge_request.target_branch} | |
| 25 | + - else | |
| 26 | + %p | |
| 27 | + %strong Step 1. | |
| 28 | + Checkout target branch and get recent objects from GitLab | |
| 29 | + %pre.dark | |
| 30 | + :preserve | |
| 31 | + git checkout #{@merge_request.target_branch} | |
| 32 | + git fetch origin | |
| 33 | + %p | |
| 34 | + %strong Step 2. | |
| 35 | + Merge source branch into target branch and push changes to GitLab | |
| 36 | + %pre.dark | |
| 37 | + :preserve | |
| 38 | + git merge origin/#{@merge_request.source_branch} | |
| 39 | + git push origin #{@merge_request.target_branch} | |
| 40 | + | |
| 41 | + | |
| 42 | +:javascript | |
| 43 | + $(function(){ | |
| 44 | + var modal = $('#modal_merge_info').modal({modal: true, show:false}); | |
| 45 | + $('.how_to_merge_link').bind("click", function(){ | |
| 46 | + modal.show(); | |
| 47 | + }); | |
| 48 | + $('.modal-header .close').bind("click", function(){ | |
| 49 | + modal.hide(); | |
| 50 | + }) | |
| 51 | + }) | ... | ... |
app/views/projects/merge_requests/show/_mr_accept.html.haml
| ... | ... | @@ -16,7 +16,7 @@ |
| 16 | 16 | for instructions |
| 17 | 17 | .accept_group |
| 18 | 18 | = f.submit "Accept Merge Request", class: "btn btn-create accept_merge_request" |
| 19 | - - unless @project.root_ref? @merge_request.source_branch | |
| 19 | + - unless @merge_request.disallow_source_branch_removal? | |
| 20 | 20 | .remove_branch_holder |
| 21 | 21 | = label_tag :should_remove_source_branch, class: "checkbox" do |
| 22 | 22 | = check_box_tag :should_remove_source_branch | ... | ... |
app/views/projects/merge_requests/show/_mr_title.html.haml
| 1 | 1 | %h3.page-title |
| 2 | 2 | = "Merge Request ##{@merge_request.id}:" |
| 3 | 3 | |
| 4 | - %span.label-branch= @merge_request.source_branch | |
| 5 | - → | |
| 6 | - %span.label-branch= @merge_request.target_branch | |
| 4 | + -if @merge_request.for_fork? | |
| 5 | + %span.label-branch | |
| 6 | + %span.label-project= truncate(@merge_request.source_project.path_with_namespace, length: 25) | |
| 7 | + #{@merge_request.source_branch} | |
| 8 | + → | |
| 9 | + %span.label-branch= @merge_request.target_branch | |
| 10 | + - else | |
| 11 | + %span.label-branch= @merge_request.source_branch | |
| 12 | + → | |
| 13 | + %span.label-branch= @merge_request.target_branch | |
| 7 | 14 | |
| 8 | 15 | %span.pull-right |
| 9 | 16 | - if can?(current_user, :modify_merge_request, @merge_request) |
| ... | ... | @@ -19,7 +26,7 @@ |
| 19 | 26 | |
| 20 | 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" |
| 21 | 28 | |
| 22 | - = link_to edit_project_merge_request_path(@project, @merge_request), class: "btn grouped" do | |
| 29 | + = link_to edit_project_merge_request_path(@project, @merge_request), class: "btn grouped", id:"edit_merge_request" do | |
| 23 | 30 | %i.icon-edit |
| 24 | 31 | Edit |
| 25 | 32 | ... | ... |
app/views/projects/merge_requests/update_branches.js.haml
0 → 100644
app/views/projects/notes/_discussion.html.haml
| ... | ... | @@ -23,7 +23,7 @@ |
| 23 | 23 | discussion on this merge request diff |
| 24 | 24 | - elsif note.for_commit? |
| 25 | 25 | started a discussion on commit |
| 26 | - #{link_to note.noteable.short_id, project_commit_path(@project, note.noteable)} | |
| 26 | + #{link_to note.noteable.short_id, project_commit_path(note.project, note.noteable)} | |
| 27 | 27 | = link_to_commit_diff_line_note(note) if note.for_diff_line? |
| 28 | 28 | - else |
| 29 | 29 | %cite.cgray started a discussion | ... | ... |
app/views/search/_result.html.haml
| ... | ... | @@ -22,11 +22,14 @@ |
| 22 | 22 | - @merge_requests.each do |merge_request| |
| 23 | 23 | %li |
| 24 | 24 | merge request: |
| 25 | - = link_to [merge_request.project, merge_request] do | |
| 25 | + = link_to [merge_request.target_project, merge_request] do | |
| 26 | 26 | %span ##{merge_request.id} |
| 27 | 27 | %strong.term |
| 28 | 28 | = truncate merge_request.title, length: 50 |
| 29 | - %span.light (#{merge_request.project.name_with_namespace}) | |
| 29 | + - if merge_request.for_fork? | |
| 30 | + %span.light (#{merge_request.source_project.name_with_namespace}:#{merge_request.source_branch} → #{merge_request.target_project.name_with_namespace}:#{merge_request.target_branch}) | |
| 31 | + - else | |
| 32 | + %span.light (#{merge_request.source_branch} → #{merge_request.target_branch}) | |
| 30 | 33 | - @issues.each do |issue| |
| 31 | 34 | %li |
| 32 | 35 | issue: | ... | ... |
app/views/shared/_merge_requests.html.haml
config/routes.rb
db/fixtures/development/10_merge_requests.rb
| ... | ... | @@ -23,7 +23,8 @@ Gitlab::Seeder.quiet do |
| 23 | 23 | id: i, |
| 24 | 24 | source_branch: branches.first, |
| 25 | 25 | target_branch: branches.last, |
| 26 | - project_id: project.id, | |
| 26 | + source_project_id: project.id, | |
| 27 | + target_project_id: project.id, | |
| 27 | 28 | author_id: user_id, |
| 28 | 29 | assignee_id: user_id, |
| 29 | 30 | milestone: project.milestones.sample, | ... | ... |
db/fixtures/test/001_repo.rb
| ... | ... | @@ -19,5 +19,18 @@ FileUtils.cd(REPO_PATH) do |
| 19 | 19 | # Remove the copy |
| 20 | 20 | FileUtils.rm(SEED_REPO) |
| 21 | 21 | end |
| 22 | +puts ' done.' | |
| 23 | +print "Creating seed satellite..." | |
| 24 | + | |
| 25 | +SATELLITE_PATH = Rails.root.join('tmp', 'satellite') | |
| 26 | +# Make directory | |
| 27 | +FileUtils.mkdir_p(SATELLITE_PATH) | |
| 28 | +# Clear any potential directory | |
| 29 | +FileUtils.rm_rf("#{SATELLITE_PATH}/gitlabhq") | |
| 30 | +# Chdir, clone from the seed | |
| 31 | +FileUtils.cd(SATELLITE_PATH) do | |
| 32 | + # Clone the satellite | |
| 22 | 33 | |
| 34 | + `git clone --quiet #{REPO_PATH}/gitlabhq #{SATELLITE_PATH}/gitlabhq` | |
| 35 | +end | |
| 23 | 36 | puts ' done.' | ... | ... |
| ... | ... | @@ -0,0 +1,12 @@ |
| 1 | +class AllowMergesForForks < ActiveRecord::Migration | |
| 2 | + def self.up | |
| 3 | + add_column :merge_requests, :target_project_id, :integer, :null => false | |
| 4 | + MergeRequest.update_all("target_project_id = project_id") | |
| 5 | + rename_column :merge_requests, :project_id, :source_project_id | |
| 6 | + end | |
| 7 | + | |
| 8 | + def self.down | |
| 9 | + remove_column :merge_requests, :target_project_id | |
| 10 | + rename_column :merge_requests, :source_project_id,:project_id | |
| 11 | + end | |
| 12 | +end | ... | ... |
db/schema.rb
| ... | ... | @@ -55,8 +55,8 @@ ActiveRecord::Schema.define(:version => 20130804151314) do |
| 55 | 55 | t.integer "assignee_id" |
| 56 | 56 | t.integer "author_id" |
| 57 | 57 | t.integer "project_id" |
| 58 | - t.datetime "created_at" | |
| 59 | - t.datetime "updated_at" | |
| 58 | + t.datetime "created_at", :null => false | |
| 59 | + t.datetime "updated_at", :null => false | |
| 60 | 60 | t.integer "position", :default => 0 |
| 61 | 61 | t.string "branch_name" |
| 62 | 62 | t.text "description" |
| ... | ... | @@ -73,8 +73,8 @@ ActiveRecord::Schema.define(:version => 20130804151314) do |
| 73 | 73 | |
| 74 | 74 | create_table "keys", :force => true do |t| |
| 75 | 75 | t.integer "user_id" |
| 76 | - t.datetime "created_at" | |
| 77 | - t.datetime "updated_at" | |
| 76 | + t.datetime "created_at", :null => false | |
| 77 | + t.datetime "updated_at", :null => false | |
| 78 | 78 | t.text "key" |
| 79 | 79 | t.string "title" |
| 80 | 80 | t.string "type" |
| ... | ... | @@ -84,27 +84,28 @@ ActiveRecord::Schema.define(:version => 20130804151314) do |
| 84 | 84 | add_index "keys", ["user_id"], :name => "index_keys_on_user_id" |
| 85 | 85 | |
| 86 | 86 | create_table "merge_requests", :force => true do |t| |
| 87 | - t.string "target_branch", :null => false | |
| 88 | - t.string "source_branch", :null => false | |
| 89 | - t.integer "project_id", :null => false | |
| 87 | + t.string "target_branch", :null => false | |
| 88 | + t.string "source_branch", :null => false | |
| 89 | + t.integer "source_project_id", :null => false | |
| 90 | 90 | t.integer "author_id" |
| 91 | 91 | t.integer "assignee_id" |
| 92 | 92 | t.string "title" |
| 93 | - t.datetime "created_at" | |
| 94 | - t.datetime "updated_at" | |
| 95 | - t.text "st_commits", :limit => 2147483647 | |
| 96 | - t.text "st_diffs", :limit => 2147483647 | |
| 93 | + t.datetime "created_at", :null => false | |
| 94 | + t.datetime "updated_at", :null => false | |
| 95 | + t.text "st_commits", :limit => 2147483647 | |
| 96 | + t.text "st_diffs", :limit => 2147483647 | |
| 97 | 97 | t.integer "milestone_id" |
| 98 | 98 | t.string "state" |
| 99 | 99 | t.string "merge_status" |
| 100 | + t.integer "target_project_id", :null => false | |
| 100 | 101 | end |
| 101 | 102 | |
| 102 | 103 | add_index "merge_requests", ["assignee_id"], :name => "index_merge_requests_on_assignee_id" |
| 103 | 104 | add_index "merge_requests", ["author_id"], :name => "index_merge_requests_on_author_id" |
| 104 | 105 | add_index "merge_requests", ["created_at"], :name => "index_merge_requests_on_created_at" |
| 105 | 106 | add_index "merge_requests", ["milestone_id"], :name => "index_merge_requests_on_milestone_id" |
| 106 | - add_index "merge_requests", ["project_id"], :name => "index_merge_requests_on_project_id" | |
| 107 | 107 | add_index "merge_requests", ["source_branch"], :name => "index_merge_requests_on_source_branch" |
| 108 | + add_index "merge_requests", ["source_project_id"], :name => "index_merge_requests_on_project_id" | |
| 108 | 109 | add_index "merge_requests", ["target_branch"], :name => "index_merge_requests_on_target_branch" |
| 109 | 110 | add_index "merge_requests", ["title"], :name => "index_merge_requests_on_title" |
| 110 | 111 | |
| ... | ... | @@ -140,8 +141,8 @@ ActiveRecord::Schema.define(:version => 20130804151314) do |
| 140 | 141 | t.text "note" |
| 141 | 142 | t.string "noteable_type" |
| 142 | 143 | t.integer "author_id" |
| 143 | - t.datetime "created_at" | |
| 144 | - t.datetime "updated_at" | |
| 144 | + t.datetime "created_at", :null => false | |
| 145 | + t.datetime "updated_at", :null => false | |
| 145 | 146 | t.integer "project_id" |
| 146 | 147 | t.string "attachment" |
| 147 | 148 | t.string "line_code" |
| ... | ... | @@ -162,8 +163,8 @@ ActiveRecord::Schema.define(:version => 20130804151314) do |
| 162 | 163 | t.string "name" |
| 163 | 164 | t.string "path" |
| 164 | 165 | t.text "description" |
| 165 | - t.datetime "created_at" | |
| 166 | - t.datetime "updated_at" | |
| 166 | + t.datetime "created_at", :null => false | |
| 167 | + t.datetime "updated_at", :null => false | |
| 167 | 168 | t.integer "creator_id" |
| 168 | 169 | t.string "default_branch" |
| 169 | 170 | t.boolean "issues_enabled", :default => true, :null => false |
| ... | ... | @@ -212,8 +213,8 @@ ActiveRecord::Schema.define(:version => 20130804151314) do |
| 212 | 213 | t.text "content", :limit => 2147483647 |
| 213 | 214 | t.integer "author_id", :null => false |
| 214 | 215 | t.integer "project_id" |
| 215 | - t.datetime "created_at" | |
| 216 | - t.datetime "updated_at" | |
| 216 | + t.datetime "created_at", :null => false | |
| 217 | + t.datetime "updated_at", :null => false | |
| 217 | 218 | t.string "file_name" |
| 218 | 219 | t.datetime "expires_at" |
| 219 | 220 | t.boolean "private", :default => true, :null => false |
| ... | ... | @@ -235,6 +236,9 @@ ActiveRecord::Schema.define(:version => 20130804151314) do |
| 235 | 236 | t.datetime "created_at" |
| 236 | 237 | end |
| 237 | 238 | |
| 239 | + add_index "taggings", ["tag_id"], :name => "index_taggings_on_tag_id" | |
| 240 | + add_index "taggings", ["taggable_id", "taggable_type", "context"], :name => "index_taggings_on_taggable_id_and_taggable_type_and_context" | |
| 241 | + | |
| 238 | 242 | create_table "tags", :force => true do |t| |
| 239 | 243 | t.string "name" |
| 240 | 244 | end |
| ... | ... | @@ -266,37 +270,37 @@ ActiveRecord::Schema.define(:version => 20130804151314) do |
| 266 | 270 | end |
| 267 | 271 | |
| 268 | 272 | create_table "users", :force => true do |t| |
| 269 | - t.string "email", :default => "", :null => false | |
| 270 | - t.string "encrypted_password", :limit => 128, :default => "", :null => false | |
| 273 | + t.string "email", :default => "", :null => false | |
| 274 | + t.string "encrypted_password", :default => "", :null => false | |
| 271 | 275 | t.string "reset_password_token" |
| 272 | 276 | t.datetime "reset_password_sent_at" |
| 273 | 277 | t.datetime "remember_created_at" |
| 274 | - t.integer "sign_in_count", :default => 0 | |
| 278 | + t.integer "sign_in_count", :default => 0 | |
| 275 | 279 | t.datetime "current_sign_in_at" |
| 276 | 280 | t.datetime "last_sign_in_at" |
| 277 | 281 | t.string "current_sign_in_ip" |
| 278 | 282 | t.string "last_sign_in_ip" |
| 279 | - t.datetime "created_at" | |
| 280 | - t.datetime "updated_at" | |
| 283 | + t.datetime "created_at", :null => false | |
| 284 | + t.datetime "updated_at", :null => false | |
| 281 | 285 | t.string "name" |
| 282 | - t.boolean "admin", :default => false, :null => false | |
| 283 | - t.integer "projects_limit", :default => 10 | |
| 284 | - t.string "skype", :default => "", :null => false | |
| 285 | - t.string "linkedin", :default => "", :null => false | |
| 286 | - t.string "twitter", :default => "", :null => false | |
| 286 | + t.boolean "admin", :default => false, :null => false | |
| 287 | + t.integer "projects_limit", :default => 10 | |
| 288 | + t.string "skype", :default => "", :null => false | |
| 289 | + t.string "linkedin", :default => "", :null => false | |
| 290 | + t.string "twitter", :default => "", :null => false | |
| 287 | 291 | t.string "authentication_token" |
| 288 | - t.integer "theme_id", :default => 1, :null => false | |
| 292 | + t.integer "theme_id", :default => 1, :null => false | |
| 289 | 293 | t.string "bio" |
| 290 | - t.integer "failed_attempts", :default => 0 | |
| 294 | + t.integer "failed_attempts", :default => 0 | |
| 291 | 295 | t.datetime "locked_at" |
| 292 | 296 | t.string "extern_uid" |
| 293 | 297 | t.string "provider" |
| 294 | 298 | t.string "username" |
| 295 | - t.boolean "can_create_group", :default => true, :null => false | |
| 296 | - t.boolean "can_create_team", :default => true, :null => false | |
| 299 | + t.boolean "can_create_group", :default => true, :null => false | |
| 300 | + t.boolean "can_create_team", :default => true, :null => false | |
| 297 | 301 | t.string "state" |
| 298 | - t.integer "color_scheme_id", :default => 1, :null => false | |
| 299 | - t.integer "notification_level", :default => 1, :null => false | |
| 302 | + t.integer "color_scheme_id", :default => 1, :null => false | |
| 303 | + t.integer "notification_level", :default => 1, :null => false | |
| 300 | 304 | t.datetime "password_expires_at" |
| 301 | 305 | t.integer "created_by_id" |
| 302 | 306 | end |
| ... | ... | @@ -304,6 +308,7 @@ ActiveRecord::Schema.define(:version => 20130804151314) do |
| 304 | 308 | add_index "users", ["admin"], :name => "index_users_on_admin" |
| 305 | 309 | add_index "users", ["authentication_token"], :name => "index_users_on_authentication_token", :unique => true |
| 306 | 310 | add_index "users", ["email"], :name => "index_users_on_email", :unique => true |
| 311 | + add_index "users", ["extern_uid", "provider"], :name => "index_users_on_extern_uid_and_provider", :unique => true | |
| 307 | 312 | add_index "users", ["name"], :name => "index_users_on_name" |
| 308 | 313 | add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true |
| 309 | 314 | add_index "users", ["username"], :name => "index_users_on_username" |
| ... | ... | @@ -322,8 +327,8 @@ ActiveRecord::Schema.define(:version => 20130804151314) do |
| 322 | 327 | create_table "users_projects", :force => true do |t| |
| 323 | 328 | t.integer "user_id", :null => false |
| 324 | 329 | t.integer "project_id", :null => false |
| 325 | - t.datetime "created_at" | |
| 326 | - t.datetime "updated_at" | |
| 330 | + t.datetime "created_at", :null => false | |
| 331 | + t.datetime "updated_at", :null => false | |
| 327 | 332 | t.integer "project_access", :default => 0, :null => false |
| 328 | 333 | t.integer "notification_level", :default => 3, :null => false |
| 329 | 334 | end |
| ... | ... | @@ -335,8 +340,8 @@ ActiveRecord::Schema.define(:version => 20130804151314) do |
| 335 | 340 | create_table "web_hooks", :force => true do |t| |
| 336 | 341 | t.string "url" |
| 337 | 342 | t.integer "project_id" |
| 338 | - t.datetime "created_at" | |
| 339 | - t.datetime "updated_at" | |
| 343 | + t.datetime "created_at", :null => false | |
| 344 | + t.datetime "updated_at", :null => false | |
| 340 | 345 | t.string "type", :default => "ProjectHook" |
| 341 | 346 | t.integer "service_id" |
| 342 | 347 | end | ... | ... |
features/dashboard/dashboard.feature
| ... | ... | @@ -0,0 +1,43 @@ |
| 1 | +Feature: Project Forked Merge Requests | |
| 2 | + Background: | |
| 3 | + Given I sign in as a user | |
| 4 | + And I am a member of project "Shop" | |
| 5 | + And I have a project forked off of "Shop" called "Forked Shop" | |
| 6 | + | |
| 7 | + # TODO: fix | |
| 8 | + #@javascript | |
| 9 | + #Scenario: I can visit the target projects commit for a forked merge request | |
| 10 | + #Given I visit project "Forked Shop" merge requests page | |
| 11 | + #And I click link "New Merge Request" | |
| 12 | + #And I fill out a "Merge Request On Forked Project" merge request | |
| 13 | + #And I follow the target commit link | |
| 14 | + #Then I should see the commit under the forked from project | |
| 15 | + | |
| 16 | + @javascript | |
| 17 | + Scenario: I submit new unassigned merge request to a forked project | |
| 18 | + Given I visit project "Forked Shop" merge requests page | |
| 19 | + And I click link "New Merge Request" | |
| 20 | + And I fill out a "Merge Request On Forked Project" merge request | |
| 21 | + And I submit the merge request | |
| 22 | + Then I should see merge request "Merge Request On Forked Project" | |
| 23 | + | |
| 24 | + @javascript | |
| 25 | + Scenario: I can edit a forked merge request | |
| 26 | + Given I visit project "Forked Shop" merge requests page | |
| 27 | + And I click link "New Merge Request" | |
| 28 | + And I fill out a "Merge Request On Forked Project" merge request | |
| 29 | + And I submit the merge request | |
| 30 | + And I should see merge request "Merge Request On Forked Project" | |
| 31 | + And I click link edit "Merge Request On Forked Project" | |
| 32 | + Then I see the edit page prefilled for "Merge Request On Forked Project" | |
| 33 | + And I update the merge request title | |
| 34 | + And I save the merge request | |
| 35 | + Then I should see the edited merge request | |
| 36 | + | |
| 37 | + @javascript | |
| 38 | + Scenario: I cannot submit an invalid merge request | |
| 39 | + Given I visit project "Forked Shop" merge requests page | |
| 40 | + And I click link "New Merge Request" | |
| 41 | + And I fill out an invalid "Merge Request On Forked Project" merge request | |
| 42 | + And I submit the merge request | |
| 43 | + Then I should see validation errors | ... | ... |
features/project/merge_requests.feature
| ... | ... | @@ -29,6 +29,7 @@ Feature: Project Merge Requests |
| 29 | 29 | And I click link "Close" |
| 30 | 30 | Then I should see closed merge request "Bug NS-04" |
| 31 | 31 | |
| 32 | + @javascript | |
| 32 | 33 | Scenario: I submit new unassigned merge request |
| 33 | 34 | Given I click link "New Merge Request" |
| 34 | 35 | And I submit new merge request "Wiki Feature" | ... | ... |
features/steps/dashboard/dashboard.rb
| ... | ... | @@ -22,6 +22,7 @@ class Dashboard < Spinach::FeatureSteps |
| 22 | 22 | |
| 23 | 23 | Then 'I see prefilled new Merge Request page' do |
| 24 | 24 | current_path.should == new_project_merge_request_path(@project) |
| 25 | + find("#merge_request_target_project_id").value.should == @project.id.to_s | |
| 25 | 26 | find("#merge_request_source_branch").value.should == "new_design" |
| 26 | 27 | find("#merge_request_target_branch").value.should == "master" |
| 27 | 28 | find("#merge_request_title").value.should == "New Design" | ... | ... |
features/steps/dashboard/dashboard_event_filters.rb
| ... | ... | @@ -61,7 +61,7 @@ class EventFilters < Spinach::FeatureSteps |
| 61 | 61 | end |
| 62 | 62 | |
| 63 | 63 | And 'this project has merge request event' do |
| 64 | - merge_request = create :merge_request, author: @user, project: @project | |
| 64 | + merge_request = create :merge_request, author: @user, source_project: @project, target_project: @project | |
| 65 | 65 | Event.create( |
| 66 | 66 | project: @project, |
| 67 | 67 | action: Event::MERGED, | ... | ... |
features/steps/dashboard/dashboard_merge_requests.rb
| ... | ... | @@ -6,18 +6,24 @@ class DashboardMergeRequests < Spinach::FeatureSteps |
| 6 | 6 | merge_requests = @user.merge_requests |
| 7 | 7 | merge_requests.each do |mr| |
| 8 | 8 | page.should have_content(mr.title[0..10]) |
| 9 | - page.should have_content(mr.project.name) | |
| 9 | + page.should have_content(mr.target_project.name) | |
| 10 | + page.should have_content(mr.source_project.name) | |
| 10 | 11 | end |
| 11 | 12 | end |
| 12 | 13 | |
| 13 | 14 | And 'I have authored merge requests' do |
| 14 | - project1 = create :project | |
| 15 | - project2 = create :project | |
| 15 | + project1_source = create :project | |
| 16 | + project1_target= create :project | |
| 17 | + project2_source = create :project | |
| 18 | + project2_target = create :project | |
| 16 | 19 | |
| 17 | - project1.team << [@user, :master] | |
| 18 | - project2.team << [@user, :master] | |
| 19 | 20 | |
| 20 | - merge_request1 = create :merge_request, author: @user, project: project1 | |
| 21 | - merge_request2 = create :merge_request, author: @user, project: project2 | |
| 21 | + project1_source.team << [@user, :master] | |
| 22 | + project1_target.team << [@user, :master] | |
| 23 | + project2_source.team << [@user, :master] | |
| 24 | + project2_target.team << [@user, :master] | |
| 25 | + | |
| 26 | + merge_request1 = create :merge_request, author: @user, source_project: project1_source, target_project: project1_target | |
| 27 | + merge_request2 = create :merge_request, author: @user, source_project: project2_source, target_project: project2_target | |
| 22 | 28 | end |
| 23 | 29 | end | ... | ... |
features/steps/group/group.rb
| ... | ... | @@ -60,7 +60,8 @@ class Groups < Spinach::FeatureSteps |
| 60 | 60 | |
| 61 | 61 | Given 'project from group has merge requests assigned to me' do |
| 62 | 62 | create :merge_request, |
| 63 | - project: project, | |
| 63 | + source_project: project, | |
| 64 | + target_project: project, | |
| 64 | 65 | assignee: current_user, |
| 65 | 66 | author: current_user |
| 66 | 67 | end | ... | ... |
features/steps/project/project_fork.rb
| ... | ... | @@ -4,6 +4,8 @@ class ForkProject < Spinach::FeatureSteps |
| 4 | 4 | include SharedProject |
| 5 | 5 | |
| 6 | 6 | step 'I click link "Fork"' do |
| 7 | + page.should have_content "Shop" | |
| 8 | + page.should have_content "Fork" | |
| 7 | 9 | Gitlab::Shell.any_instance.stub(:fork_repository).and_return(true) |
| 8 | 10 | click_link "Fork" |
| 9 | 11 | end |
| ... | ... | @@ -17,9 +19,13 @@ class ForkProject < Spinach::FeatureSteps |
| 17 | 19 | step 'I should see the forked project page' do |
| 18 | 20 | page.should have_content "Project was successfully forked." |
| 19 | 21 | current_path.should include current_user.namespace.path |
| 22 | + @forked_project = Project.find_by_namespace_id(current_user.namespace.path) | |
| 20 | 23 | end |
| 21 | 24 | |
| 22 | 25 | step 'I already have a project named "Shop" in my namespace' do |
| 26 | + current_user.namespace ||= create(:namespace) | |
| 27 | + current_user.namespace.should_not be_nil | |
| 28 | + current_user.namespace.path.should_not be_nil | |
| 23 | 29 | @my_project = create(:project_with_code, name: "Shop", namespace: current_user.namespace) |
| 24 | 30 | end |
| 25 | 31 | ... | ... |
| ... | ... | @@ -0,0 +1,184 @@ |
| 1 | +class ProjectForkedMergeRequests < Spinach::FeatureSteps | |
| 2 | + include SharedAuthentication | |
| 3 | + include SharedProject | |
| 4 | + include SharedNote | |
| 5 | + include SharedPaths | |
| 6 | + include ChosenHelper | |
| 7 | + | |
| 8 | + step 'I am a member of project "Shop"' do | |
| 9 | + @project = Project.find_by_name "Shop" | |
| 10 | + @project ||= create(:project_with_code, name: "Shop") | |
| 11 | + @project.team << [@user, :reporter] | |
| 12 | + end | |
| 13 | + | |
| 14 | + step 'I have a project forked off of "Shop" called "Forked Shop"' do | |
| 15 | + @forking_user = @user | |
| 16 | + forked_project_link = build(:forked_project_link) | |
| 17 | + @forked_project = Project.find_by_name "Forked Shop" | |
| 18 | + @forked_project ||= create(:source_project_with_code, name: "Forked Shop", forked_project_link: forked_project_link, creator_id: @forking_user.id , namespace: @forking_user.namespace) | |
| 19 | + | |
| 20 | + forked_project_link.forked_from_project = @project | |
| 21 | + forked_project_link.forked_to_project = @forked_project | |
| 22 | + @forked_project.team << [@forking_user , :master] | |
| 23 | + forked_project_link.save! | |
| 24 | + end | |
| 25 | + | |
| 26 | + step 'I click link "New Merge Request"' do | |
| 27 | + click_link "New Merge Request" | |
| 28 | + end | |
| 29 | + | |
| 30 | + step 'I should see merge request "Merge Request On Forked Project"' do | |
| 31 | + @project.merge_requests.size.should >= 1 | |
| 32 | + @merge_request = @project.merge_requests.last | |
| 33 | + current_path.should == project_merge_request_path(@project, @merge_request) | |
| 34 | + @merge_request.title.should == "Merge Request On Forked Project" | |
| 35 | + @merge_request.source_project.should == @forked_project | |
| 36 | + @merge_request.source_branch.should == "master" | |
| 37 | + @merge_request.target_branch.should == "stable" | |
| 38 | + page.should have_content @forked_project.path_with_namespace | |
| 39 | + page.should have_content @project.path_with_namespace | |
| 40 | + page.should have_content @merge_request.source_branch | |
| 41 | + page.should have_content @merge_request.target_branch | |
| 42 | + end | |
| 43 | + | |
| 44 | + step 'I fill out a "Merge Request On Forked Project" merge request' do | |
| 45 | + chosen @forked_project.id, from: "#merge_request_source_project_id" | |
| 46 | + chosen @project.id, from: "#merge_request_target_project_id" | |
| 47 | + | |
| 48 | + find(:select, "merge_request_source_project_id", {}).value.should == @forked_project.id.to_s | |
| 49 | + find(:select, "merge_request_target_project_id", {}).value.should == @project.id.to_s | |
| 50 | + | |
| 51 | + chosen "master", from: "#merge_request_source_branch" | |
| 52 | + chosen "stable", from: "#merge_request_target_branch" | |
| 53 | + | |
| 54 | + find(:select, "merge_request_source_branch", {}).value.should == 'master' | |
| 55 | + find(:select, "merge_request_target_branch", {}).value.should == 'stable' | |
| 56 | + | |
| 57 | + fill_in "merge_request_title", with: "Merge Request On Forked Project" | |
| 58 | + end | |
| 59 | + | |
| 60 | + step 'I submit the merge request' do | |
| 61 | + click_button "Submit merge request" | |
| 62 | + end | |
| 63 | + | |
| 64 | + step 'I follow the target commit link' do | |
| 65 | + commit = @project.repository.commit | |
| 66 | + click_link commit.short_id(8) | |
| 67 | + end | |
| 68 | + | |
| 69 | + step 'I should see the commit under the forked from project' do | |
| 70 | + commit = @project.repository.commit | |
| 71 | + page.should have_content(commit.message) | |
| 72 | + end | |
| 73 | + | |
| 74 | + step 'I click "Create Merge Request on fork" link' do | |
| 75 | + click_link "Create Merge Request on fork" | |
| 76 | + end | |
| 77 | + | |
| 78 | + step 'I see prefilled new Merge Request page for the forked project' do | |
| 79 | + current_path.should == new_project_merge_request_path(@forked_project) | |
| 80 | + find("#merge_request_source_project_id").value.should == @forked_project.id.to_s | |
| 81 | + find("#merge_request_target_project_id").value.should == @project.id.to_s | |
| 82 | + find("#merge_request_source_branch").value.should have_content "new_design" | |
| 83 | + find("#merge_request_target_branch").value.should have_content "master" | |
| 84 | + find("#merge_request_title").value.should == "New Design" | |
| 85 | + verify_commit_link(".mr_target_commit",@project) | |
| 86 | + verify_commit_link(".mr_source_commit",@forked_project) | |
| 87 | + end | |
| 88 | + | |
| 89 | + step 'I update the merge request title' do | |
| 90 | + fill_in "merge_request_title", with: "An Edited Forked Merge Request" | |
| 91 | + end | |
| 92 | + | |
| 93 | + step 'I save the merge request' do | |
| 94 | + click_button "Save changes" | |
| 95 | + end | |
| 96 | + | |
| 97 | + step 'I should see the edited merge request' do | |
| 98 | + page.should have_content "An Edited Forked Merge Request" | |
| 99 | + @project.merge_requests.size.should >= 1 | |
| 100 | + @merge_request = @project.merge_requests.last | |
| 101 | + current_path.should == project_merge_request_path(@project, @merge_request) | |
| 102 | + @merge_request.source_project.should == @forked_project | |
| 103 | + @merge_request.source_branch.should == "master" | |
| 104 | + @merge_request.target_branch.should == "stable" | |
| 105 | + page.should have_content @forked_project.path_with_namespace | |
| 106 | + page.should have_content @project.path_with_namespace | |
| 107 | + page.should have_content @merge_request.source_branch | |
| 108 | + page.should have_content @merge_request.target_branch | |
| 109 | + end | |
| 110 | + | |
| 111 | + step 'I should see last push widget' do | |
| 112 | + page.should have_content "You pushed to new_design" | |
| 113 | + page.should have_link "Create Merge Request" | |
| 114 | + end | |
| 115 | + | |
| 116 | + step 'project "Forked Shop" has push event' do | |
| 117 | + @forked_project = Project.find_by_name("Forked Shop") | |
| 118 | + | |
| 119 | + data = { | |
| 120 | + before: "0000000000000000000000000000000000000000", | |
| 121 | + after: "0220c11b9a3e6c69dc8fd35321254ca9a7b98f7e", | |
| 122 | + ref: "refs/heads/new_design", | |
| 123 | + user_id: @user.id, | |
| 124 | + user_name: @user.name, | |
| 125 | + repository: { | |
| 126 | + name: @forked_project.name, | |
| 127 | + url: "localhost/rubinius", | |
| 128 | + description: "", | |
| 129 | + homepage: "localhost/rubinius", | |
| 130 | + private: true | |
| 131 | + } | |
| 132 | + } | |
| 133 | + | |
| 134 | + @event = Event.create( | |
| 135 | + project: @forked_project, | |
| 136 | + action: Event::PUSHED, | |
| 137 | + data: data, | |
| 138 | + author_id: @user.id | |
| 139 | + ) | |
| 140 | + end | |
| 141 | + | |
| 142 | + | |
| 143 | + step 'I click link edit "Merge Request On Forked Project"' do | |
| 144 | + find("#edit_merge_request").click | |
| 145 | + end | |
| 146 | + | |
| 147 | + step 'I see the edit page prefilled for "Merge Request On Forked Project"' do | |
| 148 | + current_path.should == edit_project_merge_request_path(@project, @merge_request) | |
| 149 | + page.should have_content "Edit merge request ##{@merge_request.id}" | |
| 150 | + find("#merge_request_title").value.should == "Merge Request On Forked Project" | |
| 151 | + find("#merge_request_source_project_id").value.should == @forked_project.id.to_s | |
| 152 | + find("#merge_request_target_project_id").value.should == @project.id.to_s | |
| 153 | + find("#merge_request_source_branch").value.should have_content "master" | |
| 154 | + verify_commit_link(".mr_source_commit",@forked_project) | |
| 155 | + find("#merge_request_target_branch").value.should have_content "stable" | |
| 156 | + verify_commit_link(".mr_target_commit",@project) | |
| 157 | + end | |
| 158 | + | |
| 159 | + step 'I fill out an invalid "Merge Request On Forked Project" merge request' do | |
| 160 | + #If this isn't filled in the rest of the validations won't be triggered | |
| 161 | + fill_in "merge_request_title", with: "Merge Request On Forked Project" | |
| 162 | + find(:select, "merge_request_source_project_id", {}).value.should == @forked_project.id.to_s | |
| 163 | + find(:select, "merge_request_target_project_id", {}).value.should == @forked_project.id.to_s | |
| 164 | + find(:select, "merge_request_source_branch", {}).value.should == "" | |
| 165 | + find(:select, "merge_request_target_branch", {}).value.should == "" | |
| 166 | + end | |
| 167 | + | |
| 168 | + step 'I should see validation errors' do | |
| 169 | + page.should have_content "Source branch can't be blank" | |
| 170 | + page.should have_content "Target branch can't be blank" | |
| 171 | + page.should have_content "Branch conflict You can not use same project/branch for source and target" | |
| 172 | + end | |
| 173 | + | |
| 174 | + def project | |
| 175 | + @project ||= Project.find_by_name!("Shop") | |
| 176 | + end | |
| 177 | + | |
| 178 | + #Verify a link is generated against the correct project | |
| 179 | + def verify_commit_link(container_div, container_project) | |
| 180 | + #This should force a wait for the javascript to execute | |
| 181 | + find(:div,container_div).should have_css ".browse_code_link_holder" | |
| 182 | + find(:div,container_div).find(".commit_short_id")['href'].should have_content "#{container_project.path_with_namespace}/commit" | |
| 183 | + end | |
| 184 | +end | ... | ... |
features/steps/project/project_merge_requests.rb
| ... | ... | @@ -56,30 +56,41 @@ class ProjectMergeRequests < Spinach::FeatureSteps |
| 56 | 56 | end |
| 57 | 57 | |
| 58 | 58 | And 'I submit new merge request "Wiki Feature"' do |
| 59 | - fill_in "merge_request_title", with: "Wiki Feature" | |
| 60 | - select "bootstrap", from: "merge_request_source_branch" | |
| 61 | - select "master", from: "merge_request_target_branch" | |
| 59 | + #this must come first, so that the target branch is set by the time the "select" for "notes_refactoring" is executed | |
| 60 | + select project.path_with_namespace, :from => "merge_request_target_project_id" | |
| 61 | + fill_in "merge_request_title", :with => "Wiki Feature" | |
| 62 | + select "master", :from => "merge_request_source_branch" | |
| 63 | + find(:select, "merge_request_target_project_id", {}).value.should == project.id.to_s | |
| 64 | + find(:select, "merge_request_source_project_id", {}).value.should == project.id.to_s | |
| 65 | + | |
| 66 | + #using "notes_refactoring" because "Bug NS-04" uses master/stable, this will fail merge_request validation if the branches are the same | |
| 67 | + find(:select, "merge_request_target_branch", {}).find(:option, "notes_refactoring", {}).value.should == "notes_refactoring" | |
| 68 | + select "notes_refactoring", :from => "merge_request_target_branch" | |
| 69 | + | |
| 62 | 70 | click_button "Submit merge request" |
| 63 | 71 | end |
| 64 | 72 | |
| 65 | 73 | And 'project "Shop" have "Bug NS-04" open merge request' do |
| 66 | 74 | create(:merge_request, |
| 67 | 75 | title: "Bug NS-04", |
| 68 | - project: project, | |
| 76 | + source_project: project, | |
| 77 | + target_project: project, | |
| 69 | 78 | author: project.users.first) |
| 70 | 79 | end |
| 71 | 80 | |
| 72 | 81 | And 'project "Shop" have "Bug NS-05" open merge request with diffs inside' do |
| 73 | 82 | create(:merge_request_with_diffs, |
| 74 | 83 | title: "Bug NS-05", |
| 75 | - project: project, | |
| 84 | + source_project: project, | |
| 85 | + target_project: project, | |
| 76 | 86 | author: project.users.first) |
| 77 | 87 | end |
| 78 | 88 | |
| 79 | 89 | And 'project "Shop" have "Feature NS-03" closed merge request' do |
| 80 | 90 | create(:closed_merge_request, |
| 81 | 91 | title: "Feature NS-03", |
| 82 | - project: project, | |
| 92 | + source_project: project, | |
| 93 | + target_project: project, | |
| 83 | 94 | author: project.users.first) |
| 84 | 95 | end |
| 85 | 96 | ... | ... |
features/steps/project/project_network_graph.rb
| ... | ... | @@ -15,11 +15,11 @@ class ProjectNetworkGraph < Spinach::FeatureSteps |
| 15 | 15 | end |
| 16 | 16 | |
| 17 | 17 | And 'page should select "master" in select box' do |
| 18 | - page.should have_selector '#ref_chzn span', text: "master" | |
| 18 | + page.should have_selector '.chosen-single span', text: "master" | |
| 19 | 19 | end |
| 20 | 20 | |
| 21 | 21 | And 'page should select "v2.1.0" in select box' do |
| 22 | - page.should have_selector '#ref_chzn span', text: "v2.1.0" | |
| 22 | + page.should have_selector '.chosen-single span', text: "v2.1.0" | |
| 23 | 23 | end |
| 24 | 24 | |
| 25 | 25 | And 'page should have "master" on graph' do |
| ... | ... | @@ -61,11 +61,11 @@ class ProjectNetworkGraph < Spinach::FeatureSteps |
| 61 | 61 | end |
| 62 | 62 | |
| 63 | 63 | And 'page should select "stable" in select box' do |
| 64 | - page.should have_selector '#ref_chzn span', text: "stable" | |
| 64 | + page.should have_selector '.chosen-single span', text: "stable" | |
| 65 | 65 | end |
| 66 | 66 | |
| 67 | 67 | And 'page should select "v2.1.0" in select box' do |
| 68 | - page.should have_selector '#ref_chzn span', text: "v2.1.0" | |
| 68 | + page.should have_selector '.chosen-single span', text: "v2.1.0" | |
| 69 | 69 | end |
| 70 | 70 | |
| 71 | 71 | And 'page should have "stable" on graph' do | ... | ... |
features/steps/shared/paths.rb
| ... | ... | @@ -184,6 +184,10 @@ module SharedPaths |
| 184 | 184 | visit project_path(project) |
| 185 | 185 | end |
| 186 | 186 | |
| 187 | + step 'I visit project "Forked Shop" merge requests page' do | |
| 188 | + visit project_merge_requests_path(@forked_project) | |
| 189 | + end | |
| 190 | + | |
| 187 | 191 | step 'I visit edit project "Shop" page' do |
| 188 | 192 | visit edit_project_path(project) |
| 189 | 193 | end |
| ... | ... | @@ -239,18 +243,22 @@ module SharedPaths |
| 239 | 243 | |
| 240 | 244 | step 'I visit merge request page "Bug NS-04"' do |
| 241 | 245 | mr = MergeRequest.find_by_title("Bug NS-04") |
| 242 | - visit project_merge_request_path(mr.project, mr) | |
| 246 | + visit project_merge_request_path(mr.target_project, mr) | |
| 243 | 247 | end |
| 244 | 248 | |
| 245 | 249 | step 'I visit merge request page "Bug NS-05"' do |
| 246 | 250 | mr = MergeRequest.find_by_title("Bug NS-05") |
| 247 | - visit project_merge_request_path(mr.project, mr) | |
| 251 | + visit project_merge_request_path(mr.target_project, mr) | |
| 248 | 252 | end |
| 249 | 253 | |
| 250 | 254 | step 'I visit project "Shop" merge requests page' do |
| 251 | 255 | visit project_merge_requests_path(project) |
| 252 | 256 | end |
| 253 | 257 | |
| 258 | + step 'I visit forked project "Shop" merge requests page' do | |
| 259 | + visit project_merge_requests_path(project) | |
| 260 | + end | |
| 261 | + | |
| 254 | 262 | step 'I visit project "Shop" milestones page' do |
| 255 | 263 | visit project_milestones_path(project) |
| 256 | 264 | end | ... | ... |
features/support/env.rb
| ... | ... | @@ -14,7 +14,7 @@ require 'spinach/capybara' |
| 14 | 14 | require 'sidekiq/testing/inline' |
| 15 | 15 | |
| 16 | 16 | |
| 17 | -%w(valid_commit select2_helper test_env).each do |f| | |
| 17 | +%w(valid_commit select2_helper chosen_helper test_env).each do |f| | |
| 18 | 18 | require Rails.root.join('spec', 'support', f) |
| 19 | 19 | end |
| 20 | 20 | |
| ... | ... | @@ -35,8 +35,7 @@ Capybara.ignore_hidden_elements = false |
| 35 | 35 | DatabaseCleaner.strategy = :truncation |
| 36 | 36 | |
| 37 | 37 | Spinach.hooks.before_scenario do |
| 38 | - TestEnv.init(mailer: false) | |
| 39 | - | |
| 38 | + TestEnv.setup_stubs | |
| 40 | 39 | DatabaseCleaner.start |
| 41 | 40 | end |
| 42 | 41 | |
| ... | ... | @@ -45,6 +44,7 @@ Spinach.hooks.after_scenario do |
| 45 | 44 | end |
| 46 | 45 | |
| 47 | 46 | Spinach.hooks.before_run do |
| 47 | + TestEnv.init(mailer: false, init_repos: true, repos: false) | |
| 48 | 48 | RSpec::Mocks::setup self |
| 49 | 49 | |
| 50 | 50 | include FactoryGirl::Syntax::Methods | ... | ... |
lib/api/merge_requests.rb
| ... | ... | @@ -14,6 +14,14 @@ module API |
| 14 | 14 | end |
| 15 | 15 | not_found! |
| 16 | 16 | end |
| 17 | + | |
| 18 | + def not_fork?(target_project_id, user_project) | |
| 19 | + target_project_id.nil? || target_project_id == user_project.id.to_s | |
| 20 | + end | |
| 21 | + | |
| 22 | + def target_matches_fork(target_project_id,user_project) | |
| 23 | + user_project.forked? && user_project.forked_from_project.id.to_s == target_project_id | |
| 24 | + end | |
| 17 | 25 | end |
| 18 | 26 | |
| 19 | 27 | # List merge requests |
| ... | ... | @@ -51,9 +59,10 @@ module API |
| 51 | 59 | # |
| 52 | 60 | # Parameters: |
| 53 | 61 | # |
| 54 | - # id (required) - The ID of a project | |
| 62 | + # id (required) - The ID of a project - this will be the source of the merge request | |
| 55 | 63 | # source_branch (required) - The source branch |
| 56 | 64 | # target_branch (required) - The target branch |
| 65 | + # target_project - The target project of the merge request defaults to the :id of the project | |
| 57 | 66 | # assignee_id - Assignee user ID |
| 58 | 67 | # title (required) - Title of MR |
| 59 | 68 | # |
| ... | ... | @@ -63,10 +72,20 @@ module API |
| 63 | 72 | post ":id/merge_requests" do |
| 64 | 73 | authorize! :write_merge_request, user_project |
| 65 | 74 | required_attributes! [:source_branch, :target_branch, :title] |
| 66 | - | |
| 67 | - attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title] | |
| 75 | + attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :target_project_id] | |
| 68 | 76 | merge_request = user_project.merge_requests.new(attrs) |
| 69 | 77 | merge_request.author = current_user |
| 78 | + merge_request.source_project = user_project | |
| 79 | + target_project_id = attrs[:target_project_id] | |
| 80 | + if not_fork?(target_project_id, user_project) | |
| 81 | + merge_request.target_project = user_project | |
| 82 | + else | |
| 83 | + if target_matches_fork(target_project_id,user_project) | |
| 84 | + merge_request.target_project = Project.find_by_id(attrs[:target_project_id]) | |
| 85 | + else | |
| 86 | + render_api_error!('(Bad Request) Specified target project that is not the source project, or the source fork of the project.', 400) | |
| 87 | + end | |
| 88 | + end | |
| 70 | 89 | |
| 71 | 90 | if merge_request.save |
| 72 | 91 | merge_request.reload_code | ... | ... |
lib/gitlab/satellite/action.rb
| ... | ... | @@ -25,25 +25,31 @@ module Gitlab |
| 25 | 25 | end |
| 26 | 26 | end |
| 27 | 27 | rescue Errno::ENOMEM => ex |
| 28 | - Gitlab::GitLogger.error(ex.message) | |
| 29 | - return false | |
| 28 | + return handle_exception(ex) | |
| 30 | 29 | rescue Grit::Git::GitTimeout => ex |
| 31 | - Gitlab::GitLogger.error(ex.message) | |
| 32 | - return false | |
| 30 | + return handle_exception(ex) | |
| 33 | 31 | ensure |
| 34 | 32 | Gitlab::ShellEnv.reset_env |
| 35 | 33 | end |
| 36 | 34 | |
| 37 | - # * Clears the satellite | |
| 38 | - # * Updates the satellite from Gitolite | |
| 35 | + # * Recreates the satellite | |
| 39 | 36 | # * Sets up Git variables for the user |
| 40 | 37 | # |
| 41 | 38 | # Note: use this within #in_locked_and_timed_satellite |
| 42 | 39 | def prepare_satellite!(repo) |
| 43 | 40 | project.satellite.clear_and_update! |
| 44 | 41 | |
| 45 | - repo.git.config({}, "user.name", user.name) | |
| 46 | - repo.git.config({}, "user.email", user.email) | |
| 42 | + repo.config['user.name'] = user.name | |
| 43 | + repo.config['user.email'] = user.email | |
| 44 | + end | |
| 45 | + | |
| 46 | + def default_options(options = {}) | |
| 47 | + {raise: true, timeout: true}.merge(options) | |
| 48 | + end | |
| 49 | + | |
| 50 | + def handle_exception(exception) | |
| 51 | + Gitlab::GitLogger.error(exception.message) | |
| 52 | + false | |
| 47 | 53 | end |
| 48 | 54 | end |
| 49 | 55 | end | ... | ... |
lib/gitlab/satellite/merge_action.rb
| ... | ... | @@ -5,48 +5,120 @@ module Gitlab |
| 5 | 5 | attr_accessor :merge_request |
| 6 | 6 | |
| 7 | 7 | def initialize(user, merge_request) |
| 8 | - super user, merge_request.project | |
| 8 | + super user, merge_request.target_project | |
| 9 | 9 | @merge_request = merge_request |
| 10 | 10 | end |
| 11 | 11 | |
| 12 | 12 | # Checks if a merge request can be executed without user interaction |
| 13 | 13 | def can_be_merged? |
| 14 | 14 | in_locked_and_timed_satellite do |merge_repo| |
| 15 | + prepare_satellite!(merge_repo) | |
| 15 | 16 | merge_in_satellite!(merge_repo) |
| 16 | 17 | end |
| 17 | 18 | end |
| 18 | 19 | |
| 19 | 20 | # Merges the source branch into the target branch in the satellite and |
| 20 | - # pushes it back to Gitolite. | |
| 21 | - # It also removes the source branch if requested in the merge request. | |
| 21 | + # pushes it back to the repository. | |
| 22 | + # It also removes the source branch if requested in the merge request (and this is permitted by the merge request). | |
| 22 | 23 | # |
| 23 | 24 | # Returns false if the merge produced conflicts |
| 24 | - # Returns false if pushing from the satellite to Gitolite failed or was rejected | |
| 25 | + # Returns false if pushing from the satellite to the repository failed or was rejected | |
| 25 | 26 | # Returns true otherwise |
| 26 | 27 | def merge! |
| 27 | 28 | in_locked_and_timed_satellite do |merge_repo| |
| 29 | + prepare_satellite!(merge_repo) | |
| 28 | 30 | if merge_in_satellite!(merge_repo) |
| 29 | 31 | # push merge back to Gitolite |
| 30 | 32 | # will raise CommandFailed when push fails |
| 31 | - merge_repo.git.push({raise: true, timeout: true}, :origin, merge_request.target_branch) | |
| 32 | - | |
| 33 | + merge_repo.git.push(default_options, :origin, merge_request.target_branch) | |
| 33 | 34 | # remove source branch |
| 34 | 35 | if merge_request.should_remove_source_branch && !project.root_ref?(merge_request.source_branch) |
| 35 | 36 | # will raise CommandFailed when push fails |
| 36 | - merge_repo.git.push({raise: true, timeout: true}, :origin, ":#{merge_request.source_branch}") | |
| 37 | + merge_repo.git.push(default_options, :origin, ":#{merge_request.source_branch}") | |
| 37 | 38 | end |
| 38 | - | |
| 39 | 39 | # merge, push and branch removal successful |
| 40 | 40 | true |
| 41 | 41 | end |
| 42 | 42 | end |
| 43 | 43 | rescue Grit::Git::CommandFailed => ex |
| 44 | - Gitlab::GitLogger.error(ex.message) | |
| 45 | - false | |
| 44 | + handle_exception(ex) | |
| 46 | 45 | end |
| 47 | 46 | |
| 48 | - private | |
| 47 | + # Get a raw diff of the source to the target | |
| 48 | + def diff_in_satellite | |
| 49 | + in_locked_and_timed_satellite do |merge_repo| | |
| 50 | + prepare_satellite!(merge_repo) | |
| 51 | + update_satellite_source_and_target!(merge_repo) | |
| 52 | + | |
| 53 | + if merge_request.for_fork? | |
| 54 | + diff = merge_repo.git.native(:diff, default_options, "origin/#{merge_request.target_branch}", "source/#{merge_request.source_branch}") | |
| 55 | + else | |
| 56 | + diff = merge_repo.git.native(:diff, default_options, "#{merge_request.target_branch}", "#{merge_request.source_branch}") | |
| 57 | + end | |
| 58 | + | |
| 59 | + return diff | |
| 60 | + end | |
| 61 | + rescue Grit::Git::CommandFailed => ex | |
| 62 | + handle_exception(ex) | |
| 63 | + end | |
| 64 | + | |
| 65 | + # Only show what is new in the source branch compared to the target branch, not the other way around. | |
| 66 | + # The line below with merge_base is equivalent to diff with three dots (git diff branch1...branch2) | |
| 67 | + # From the git documentation: "git diff A...B" is equivalent to "git diff $(git-merge-base A B) B" | |
| 68 | + def diffs_between_satellite | |
| 69 | + in_locked_and_timed_satellite do |merge_repo| | |
| 70 | + prepare_satellite!(merge_repo) | |
| 71 | + update_satellite_source_and_target!(merge_repo) | |
| 72 | + if merge_request.for_fork? | |
| 73 | + common_commit = merge_repo.git.native(:merge_base, default_options, ["origin/#{merge_request.target_branch}", "source/#{merge_request.source_branch}"]).strip | |
| 74 | + #this method doesn't take default options | |
| 75 | + diffs = merge_repo.diff(common_commit, "source/#{merge_request.source_branch}") | |
| 76 | + else | |
| 77 | + raise "Attempt to determine diffs between for a non forked merge request in satellite MergeRequest.id:[#{merge_request.id}]" | |
| 78 | + end | |
| 79 | + diffs = diffs.map { |diff| Gitlab::Git::Diff.new(diff) } | |
| 80 | + return diffs | |
| 81 | + end | |
| 82 | + rescue Grit::Git::CommandFailed => ex | |
| 83 | + handle_exception(ex) | |
| 84 | + end | |
| 85 | + | |
| 86 | + # Get commit as an email patch | |
| 87 | + def format_patch | |
| 88 | + in_locked_and_timed_satellite do |merge_repo| | |
| 89 | + prepare_satellite!(merge_repo) | |
| 90 | + update_satellite_source_and_target!(merge_repo) | |
| 91 | + | |
| 92 | + if (merge_request.for_fork?) | |
| 93 | + patch = merge_repo.git.format_patch(default_options({stdout: true}), "origin/#{merge_request.target_branch}..source/#{merge_request.source_branch}") | |
| 94 | + else | |
| 95 | + patch = merge_repo.git.format_patch(default_options({stdout: true}), "#{merge_request.target_branch}..#{merge_request.source_branch}") | |
| 96 | + end | |
| 97 | + | |
| 98 | + return patch | |
| 99 | + end | |
| 100 | + rescue Grit::Git::CommandFailed => ex | |
| 101 | + handle_exception(ex) | |
| 102 | + end | |
| 49 | 103 | |
| 104 | + # Retrieve an array of commits between the source and the target | |
| 105 | + def commits_between | |
| 106 | + in_locked_and_timed_satellite do |merge_repo| | |
| 107 | + prepare_satellite!(merge_repo) | |
| 108 | + update_satellite_source_and_target!(merge_repo) | |
| 109 | + if (merge_request.for_fork?) | |
| 110 | + commits = merge_repo.commits_between("origin/#{merge_request.target_branch}", "source/#{merge_request.source_branch}") | |
| 111 | + else | |
| 112 | + raise "Attempt to determine commits between for a non forked merge request in satellite MergeRequest.id:[#{merge_request.id}]" | |
| 113 | + end | |
| 114 | + commits = commits.map { |commit| Gitlab::Git::Commit.new(commit, nil) } | |
| 115 | + return commits | |
| 116 | + end | |
| 117 | + rescue Grit::Git::CommandFailed => ex | |
| 118 | + handle_exception(ex) | |
| 119 | + end | |
| 120 | + | |
| 121 | + private | |
| 50 | 122 | # Merges the source_branch into the target_branch in the satellite. |
| 51 | 123 | # |
| 52 | 124 | # Note: it will clear out the satellite before doing anything |
| ... | ... | @@ -54,18 +126,35 @@ module Gitlab |
| 54 | 126 | # Returns false if the merge produced conflicts |
| 55 | 127 | # Returns true otherwise |
| 56 | 128 | def merge_in_satellite!(repo) |
| 57 | - prepare_satellite!(repo) | |
| 58 | - | |
| 59 | - # create target branch in satellite at the corresponding commit from Gitolite | |
| 60 | - repo.git.checkout({raise: true, timeout: true, b: true}, merge_request.target_branch, "origin/#{merge_request.target_branch}") | |
| 129 | + update_satellite_source_and_target!(repo) | |
| 61 | 130 | |
| 62 | - # merge the source branch from Gitolite into the satellite | |
| 131 | + # merge the source branch into the satellite | |
| 63 | 132 | # will raise CommandFailed when merge fails |
| 64 | - repo.git.pull({raise: true, timeout: true, no_ff: true}, :origin, merge_request.source_branch) | |
| 133 | + if merge_request.for_fork? | |
| 134 | + repo.git.pull(default_options({no_ff: true}), 'source', merge_request.source_branch) | |
| 135 | + else | |
| 136 | + repo.git.pull(default_options({no_ff: true}), 'origin', merge_request.source_branch) | |
| 137 | + end | |
| 65 | 138 | rescue Grit::Git::CommandFailed => ex |
| 66 | - Gitlab::GitLogger.error(ex.message) | |
| 67 | - false | |
| 139 | + handle_exception(ex) | |
| 68 | 140 | end |
| 141 | + | |
| 142 | + # Assumes a satellite exists that is a fresh clone of the projects repo, prepares satellite for merges, diffs etc | |
| 143 | + def update_satellite_source_and_target!(repo) | |
| 144 | + if merge_request.for_fork? | |
| 145 | + repo.remote_add('source', merge_request.source_project.repository.path_to_repo) | |
| 146 | + repo.remote_fetch('source') | |
| 147 | + repo.git.checkout(default_options({b: true}), merge_request.target_branch, "origin/#{merge_request.target_branch}") | |
| 148 | + else | |
| 149 | + # We can't trust the input here being branch names, we can't always check it out because it could be a relative ref i.e. HEAD~3 | |
| 150 | + # we could actually remove the if true, because it should never ever happen (as long as the satellite has been prepared) | |
| 151 | + repo.git.checkout(default_options, "#{merge_request.source_branch}") | |
| 152 | + repo.git.checkout(default_options, "#{merge_request.target_branch}") | |
| 153 | + end | |
| 154 | + rescue Grit::Git::CommandFailed => ex | |
| 155 | + handle_exception(ex) | |
| 156 | + end | |
| 157 | + | |
| 69 | 158 | end |
| 70 | 159 | end |
| 71 | 160 | end | ... | ... |
lib/gitlab/satellite/satellite.rb
| 1 | 1 | module Gitlab |
| 2 | - class SatelliteNotExistError < StandardError; end | |
| 2 | + class SatelliteNotExistError < StandardError; end | |
| 3 | 3 | |
| 4 | 4 | module Satellite |
| 5 | 5 | class Satellite |
| ... | ... | @@ -24,8 +24,11 @@ module Gitlab |
| 24 | 24 | def clear_and_update! |
| 25 | 25 | raise_no_satellite unless exists? |
| 26 | 26 | |
| 27 | + File.exists? path | |
| 28 | + @repo = nil | |
| 27 | 29 | clear_working_dir! |
| 28 | 30 | delete_heads! |
| 31 | + remove_remotes! | |
| 29 | 32 | update_from_source! |
| 30 | 33 | end |
| 31 | 34 | |
| ... | ... | @@ -55,10 +58,11 @@ module Gitlab |
| 55 | 58 | raise_no_satellite unless exists? |
| 56 | 59 | |
| 57 | 60 | File.open(lock_file, "w+") do |f| |
| 58 | - f.flock(File::LOCK_EX) | |
| 59 | - | |
| 60 | - Dir.chdir(path) do | |
| 61 | - return yield | |
| 61 | + begin | |
| 62 | + f.flock File::LOCK_EX | |
| 63 | + Dir.chdir(path) { return yield } | |
| 64 | + ensure | |
| 65 | + f.flock File::LOCK_UN | |
| 62 | 66 | end |
| 63 | 67 | end |
| 64 | 68 | end |
| ... | ... | @@ -100,20 +104,34 @@ module Gitlab |
| 100 | 104 | if heads.include? PARKING_BRANCH |
| 101 | 105 | repo.git.checkout({}, PARKING_BRANCH) |
| 102 | 106 | else |
| 103 | - repo.git.checkout({b: true}, PARKING_BRANCH) | |
| 107 | + repo.git.checkout(default_options({b: true}), PARKING_BRANCH) | |
| 104 | 108 | end |
| 105 | 109 | |
| 106 | 110 | # remove the parking branch from the list of heads ... |
| 107 | 111 | heads.delete(PARKING_BRANCH) |
| 108 | 112 | # ... and delete all others |
| 109 | - heads.each { |head| repo.git.branch({D: true}, head) } | |
| 113 | + heads.each { |head| repo.git.branch(default_options({D: true}), head) } | |
| 114 | + end | |
| 115 | + | |
| 116 | + # Deletes all remotes except origin | |
| 117 | + # | |
| 118 | + # This ensures we have no remote name clashes or issues updating branches when | |
| 119 | + # working with the satellite. | |
| 120 | + def remove_remotes! | |
| 121 | + remotes = repo.git.remote.split(' ') | |
| 122 | + remotes.delete('origin') | |
| 123 | + remotes.each { |name| repo.git.remote(default_options,'rm', name)} | |
| 110 | 124 | end |
| 111 | 125 | |
| 112 | 126 | # Updates the satellite from Gitolite |
| 113 | 127 | # |
| 114 | 128 | # Note: this will only update remote branches (i.e. origin/*) |
| 115 | 129 | def update_from_source! |
| 116 | - repo.git.fetch({timeout: true}, :origin) | |
| 130 | + repo.git.fetch(default_options, :origin) | |
| 131 | + end | |
| 132 | + | |
| 133 | + def default_options(options = {}) | |
| 134 | + {raise: true, timeout: true}.merge(options) | |
| 117 | 135 | end |
| 118 | 136 | |
| 119 | 137 | # Create directory for storing | ... | ... |
| ... | ... | @@ -0,0 +1,59 @@ |
| 1 | +require 'spec_helper' | |
| 2 | + | |
| 3 | +describe FilterContext do | |
| 4 | + | |
| 5 | + let(:user) { create :user } | |
| 6 | + let(:user2) { create :user } | |
| 7 | + let(:project1) { create(:project, creator_id: user.id) } | |
| 8 | + let(:project2) { create(:project, creator_id: user.id) } | |
| 9 | + let(:merge_request1) { create(:merge_request, author_id: user.id, source_project: project1, target_project: project2) } | |
| 10 | + let(:merge_request2) { create(:merge_request, author_id: user.id, source_project: project2, target_project: project1) } | |
| 11 | + let(:merge_request3) { create(:merge_request, author_id: user.id, source_project: project2, target_project: project2) } | |
| 12 | + let(:merge_request4) { create(:merge_request, author_id: user2.id, source_project: project2, target_project: project2, target_branch:"notes_refactoring") } | |
| 13 | + let(:issue1) { create(:issue, assignee_id: user.id, project: project1) } | |
| 14 | + let(:issue2) { create(:issue, assignee_id: user.id, project: project2) } | |
| 15 | + let(:issue3) { create(:issue, assignee_id: user2.id, project: project2) } | |
| 16 | + | |
| 17 | + describe 'merge requests' do | |
| 18 | + before :each do | |
| 19 | + merge_request1 | |
| 20 | + merge_request2 | |
| 21 | + merge_request3 | |
| 22 | + merge_request4 | |
| 23 | + end | |
| 24 | + | |
| 25 | + it 'should by default filter properly' do | |
| 26 | + merge_requests = user.cared_merge_requests | |
| 27 | + params ={} | |
| 28 | + merge_requests = FilterContext.new(merge_requests, params).execute | |
| 29 | + merge_requests.size.should == 3 | |
| 30 | + end | |
| 31 | + | |
| 32 | + it 'should apply blocks passed in on creation to the filters' do | |
| 33 | + merge_requests = user.cared_merge_requests | |
| 34 | + params = {:project_id => project1.id} | |
| 35 | + merge_requests = FilterContext.new(merge_requests, params).execute | |
| 36 | + merge_requests.size.should == 1 | |
| 37 | + end | |
| 38 | + end | |
| 39 | + | |
| 40 | + describe 'issues' do | |
| 41 | + before :each do | |
| 42 | + issue1 | |
| 43 | + issue2 | |
| 44 | + issue3 | |
| 45 | + end | |
| 46 | + it 'should by default filter projects properly' do | |
| 47 | + issues = user.assigned_issues | |
| 48 | + params = {} | |
| 49 | + issues = FilterContext.new(issues, params).execute | |
| 50 | + issues.size.should == 2 | |
| 51 | + end | |
| 52 | + it 'should apply blocks passed in on creation to the filters' do | |
| 53 | + issues = user.assigned_issues | |
| 54 | + params = {:project_id => project1.id} | |
| 55 | + issues = FilterContext.new(issues, params).execute | |
| 56 | + issues.size.should == 1 | |
| 57 | + end | |
| 58 | + end | |
| 59 | +end | ... | ... |
spec/controllers/commit_controller_spec.rb
spec/controllers/commits_controller_spec.rb
spec/controllers/merge_requests_controller_spec.rb
| ... | ... | @@ -3,7 +3,7 @@ require 'spec_helper' |
| 3 | 3 | describe Projects::MergeRequestsController do |
| 4 | 4 | let(:project) { create(:project_with_code) } |
| 5 | 5 | let(:user) { create(:user) } |
| 6 | - let(:merge_request) { create(:merge_request_with_diffs, project: project, target_branch: "bcf03b5d~3", source_branch: "bcf03b5d") } | |
| 6 | + let(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project, target_branch: "bcf03b5d~3", source_branch: "bcf03b5d") } | |
| 7 | 7 | |
| 8 | 8 | before do |
| 9 | 9 | sign_in(user) |
| ... | ... | @@ -28,7 +28,7 @@ describe Projects::MergeRequestsController do |
| 28 | 28 | it "should render it" do |
| 29 | 29 | get :show, project_id: project.code, id: merge_request.id, format: format |
| 30 | 30 | |
| 31 | - expect(response.body).to eq(merge_request.send(:"to_#{format}")) | |
| 31 | + expect(response.body).to eq((merge_request.send(:"to_#{format}",user)).to_s) | |
| 32 | 32 | end |
| 33 | 33 | |
| 34 | 34 | it "should not escape Html" do | ... | ... |
spec/factories.rb
| ... | ... | @@ -29,8 +29,19 @@ FactoryGirl.define do |
| 29 | 29 | sequence(:name) { |n| "project#{n}" } |
| 30 | 30 | path { name.downcase.gsub(/\s/, '_') } |
| 31 | 31 | creator |
| 32 | + | |
| 33 | + trait :source do | |
| 34 | + sequence(:name) { |n| "source project#{n}" } | |
| 35 | + end | |
| 36 | + trait :target do | |
| 37 | + sequence(:name) { |n| "target project#{n}" } | |
| 38 | + end | |
| 39 | + | |
| 40 | + factory :source_project, traits: [:source] | |
| 41 | + factory :target_project, traits: [:target] | |
| 32 | 42 | end |
| 33 | 43 | |
| 44 | + | |
| 34 | 45 | factory :redmine_project, parent: :project do |
| 35 | 46 | issues_tracker { "redmine" } |
| 36 | 47 | issues_tracker_id { "project_name_in_redmine" } |
| ... | ... | @@ -39,11 +50,20 @@ FactoryGirl.define do |
| 39 | 50 | factory :project_with_code, parent: :project do |
| 40 | 51 | path { 'gitlabhq' } |
| 41 | 52 | |
| 53 | + trait :source_path do | |
| 54 | + path { 'source_gitlabhq' } | |
| 55 | + end | |
| 56 | + | |
| 57 | + trait :target_path do | |
| 58 | + path { 'target_gitlabhq' } | |
| 59 | + end | |
| 60 | + | |
| 61 | + factory :source_project_with_code, traits: [:source, :source_path] | |
| 62 | + factory :target_project_with_code, traits: [:target, :target_path] | |
| 63 | + | |
| 42 | 64 | after :create do |project| |
| 43 | - repos_path = Rails.root.join('tmp', 'test-git-base-path') | |
| 44 | - seed_repo = Rails.root.join('tmp', 'repositories', 'gitlabhq') | |
| 45 | - target_repo = File.join(repos_path, project.path_with_namespace + '.git') | |
| 46 | - system("ln -s #{seed_repo} #{target_repo}") | |
| 65 | + TestEnv.clear_repo_dir(project.namespace, project.path) | |
| 66 | + TestEnv.create_repo(project.namespace, project.path) | |
| 47 | 67 | end |
| 48 | 68 | end |
| 49 | 69 | |
| ... | ... | @@ -86,7 +106,8 @@ FactoryGirl.define do |
| 86 | 106 | factory :merge_request do |
| 87 | 107 | title |
| 88 | 108 | author |
| 89 | - project factory: :project_with_code | |
| 109 | + source_project factory: :source_project_with_code | |
| 110 | + target_project factory: :target_project_with_code | |
| 90 | 111 | source_branch "master" |
| 91 | 112 | target_branch "stable" |
| 92 | 113 | |
| ... | ... | @@ -96,13 +117,13 @@ FactoryGirl.define do |
| 96 | 117 | source_branch "stable" # pretend bcf03b5d |
| 97 | 118 | st_commits do |
| 98 | 119 | [ |
| 99 | - project.repository.commit('bcf03b5d').to_hash, | |
| 100 | - project.repository.commit('bcf03b5d~1').to_hash, | |
| 101 | - project.repository.commit('bcf03b5d~2').to_hash | |
| 120 | + source_project.repository.commit('bcf03b5d').to_hash, | |
| 121 | + source_project.repository.commit('bcf03b5d~1').to_hash, | |
| 122 | + source_project.repository.commit('bcf03b5d~2').to_hash | |
| 102 | 123 | ] |
| 103 | 124 | end |
| 104 | 125 | st_diffs do |
| 105 | - project.repo.diff("bcf03b5d~3", "bcf03b5d") | |
| 126 | + source_project.repo.diff("bcf03b5d~3", "bcf03b5d") | |
| 106 | 127 | end |
| 107 | 128 | end |
| 108 | 129 | |
| ... | ... | @@ -133,7 +154,7 @@ FactoryGirl.define do |
| 133 | 154 | |
| 134 | 155 | trait :on_commit do |
| 135 | 156 | project factory: :project_with_code |
| 136 | - commit_id "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a" | |
| 157 | + commit_id "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a" | |
| 137 | 158 | noteable_type "Commit" |
| 138 | 159 | end |
| 139 | 160 | |
| ... | ... | @@ -143,12 +164,12 @@ FactoryGirl.define do |
| 143 | 164 | |
| 144 | 165 | trait :on_merge_request do |
| 145 | 166 | project factory: :project_with_code |
| 146 | - noteable_id 1 | |
| 167 | + noteable_id 1 | |
| 147 | 168 | noteable_type "MergeRequest" |
| 148 | 169 | end |
| 149 | 170 | |
| 150 | 171 | trait :on_issue do |
| 151 | - noteable_id 1 | |
| 172 | + noteable_id 1 | |
| 152 | 173 | noteable_type "Issue" |
| 153 | 174 | end |
| 154 | 175 | ... | ... |
spec/factories_spec.rb
spec/features/gitlab_flavored_markdown_spec.rb
| ... | ... | @@ -3,11 +3,11 @@ require 'spec_helper' |
| 3 | 3 | describe "GitLab Flavored Markdown" do |
| 4 | 4 | let(:project) { create(:project_with_code) } |
| 5 | 5 | let(:issue) { create(:issue, project: project) } |
| 6 | - let(:merge_request) { create(:merge_request, project: project) } | |
| 6 | + let(:merge_request) { create(:merge_request, source_project: project, target_project: project) } | |
| 7 | 7 | let(:fred) do |
| 8 | - u = create(:user, name: "fred") | |
| 9 | - project.team << [u, :master] | |
| 10 | - u | |
| 8 | + u = create(:user, name: "fred") | |
| 9 | + project.team << [u, :master] | |
| 10 | + u | |
| 11 | 11 | end |
| 12 | 12 | |
| 13 | 13 | before do |
| ... | ... | @@ -83,9 +83,7 @@ describe "GitLab Flavored Markdown" do |
| 83 | 83 | |
| 84 | 84 | describe "for merge requests" do |
| 85 | 85 | before do |
| 86 | - @merge_request = create(:merge_request, | |
| 87 | - project: project, | |
| 88 | - title: "fix ##{issue.id}") | |
| 86 | + @merge_request = create(:merge_request, source_project: project, target_project: project, title: "fix ##{issue.id}") | |
| 89 | 87 | end |
| 90 | 88 | |
| 91 | 89 | it "should render title in merge_requests#index" do | ... | ... |
spec/features/notes_on_merge_requests_spec.rb
| ... | ... | @@ -2,8 +2,8 @@ require 'spec_helper' |
| 2 | 2 | |
| 3 | 3 | describe "On a merge request", js: true do |
| 4 | 4 | let!(:project) { create(:project_with_code) } |
| 5 | - let!(:merge_request) { create(:merge_request, project: project) } | |
| 6 | - let!(:note) { create(:note_on_merge_request_with_attachment, project: project) } | |
| 5 | + let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) } | |
| 6 | + let!(:note) { create(:note_on_merge_request_with_attachment, project: project) } | |
| 7 | 7 | |
| 8 | 8 | before do |
| 9 | 9 | login_as :user |
| ... | ... | @@ -62,7 +62,7 @@ describe "On a merge request", js: true do |
| 62 | 62 | |
| 63 | 63 | it 'should be added and form reset' do |
| 64 | 64 | should have_content("This is awsome!") |
| 65 | - within(".js-main-target-form") { should have_no_field("note[note]", with: "This is awesome!") } | |
| 65 | + within(".js-main-target-form") { should have_no_field("note[note]", with: "This is awesome!") } | |
| 66 | 66 | within(".js-main-target-form") { should have_css(".js-note-preview", visible: false) } |
| 67 | 67 | within(".js-main-target-form") { should have_css(".js-note-text", visible: true) } |
| 68 | 68 | end |
| ... | ... | @@ -135,8 +135,8 @@ describe "On a merge request", js: true do |
| 135 | 135 | end |
| 136 | 136 | |
| 137 | 137 | describe "On a merge request diff", js: true, focus: true do |
| 138 | - let!(:project) { create(:project_with_code) } | |
| 139 | - let!(:merge_request) { create(:merge_request_with_diffs, project: project) } | |
| 138 | + let!(:project) { create(:source_project_with_code) } | |
| 139 | + let!(:merge_request) { create(:merge_request_with_diffs, source_project: project, target_project: project) } | |
| 140 | 140 | |
| 141 | 141 | before do |
| 142 | 142 | login_as :user |
| ... | ... | @@ -144,6 +144,7 @@ describe "On a merge request diff", js: true, focus: true do |
| 144 | 144 | visit diffs_project_merge_request_path(project, merge_request) |
| 145 | 145 | end |
| 146 | 146 | |
| 147 | + | |
| 147 | 148 | subject { page } |
| 148 | 149 | |
| 149 | 150 | describe "when adding a note" do |
| ... | ... | @@ -183,6 +184,9 @@ describe "On a merge request diff", js: true, focus: true do |
| 183 | 184 | end |
| 184 | 185 | |
| 185 | 186 | describe "with muliple note forms" do |
| 187 | + let!(:project) { create(:source_project_with_code) } | |
| 188 | + let!(:merge_request) { create(:merge_request_with_diffs, source_project: project, target_project: project) } | |
| 189 | + | |
| 186 | 190 | before do |
| 187 | 191 | find('a[data-line-code="4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185"]').click |
| 188 | 192 | find('a[data-line-code="342e16cbbd482ac2047dc679b2749d248cc1428f_18_17"]').click |
| ... | ... | @@ -205,13 +209,13 @@ describe "On a merge request diff", js: true, focus: true do |
| 205 | 209 | |
| 206 | 210 | # TODO: fix |
| 207 | 211 | #it 'should check if previews were rendered separately' do |
| 208 | - #within("tr[id='4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185'] + .js-temp-notes-holder") do | |
| 209 | - #should have_css(".js-note-preview", text: "One comment on line 185") | |
| 210 | - #end | |
| 212 | + #within("tr[id='4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185'] + .js-temp-notes-holder") do | |
| 213 | + #should have_css(".js-note-preview", text: "One comment on line 185") | |
| 214 | + #end | |
| 211 | 215 | |
| 212 | - #within("tr[id='342e16cbbd482ac2047dc679b2749d248cc1428f_18_17'] + .js-temp-notes-holder") do | |
| 213 | - #should have_css(".js-note-preview", text: "Another comment on line 17") | |
| 214 | - #end | |
| 216 | + #within("tr[id='342e16cbbd482ac2047dc679b2749d248cc1428f_18_17'] + .js-temp-notes-holder") do | |
| 217 | + #should have_css(".js-note-preview", text: "Another comment on line 17") | |
| 218 | + #end | |
| 215 | 219 | #end |
| 216 | 220 | end |
| 217 | 221 | |
| ... | ... | @@ -238,39 +242,38 @@ describe "On a merge request diff", js: true, focus: true do |
| 238 | 242 | |
| 239 | 243 | # TODO: fix |
| 240 | 244 | #it "should remove last note of a discussion" do |
| 241 | - #within("tr[id='342e16cbbd482ac2047dc679b2749d248cc1428f_18_17'] + .notes-holder") do | |
| 242 | - #find(".js-note-delete").click | |
| 243 | - #end | |
| 244 | - | |
| 245 | - #should_not have_css(".note_holder") | |
| 245 | + # within("tr[id='342e16cbbd482ac2047dc679b2749d248cc1428f_18_17'] + .notes-holder") do | |
| 246 | + # find(".js-note-delete").click | |
| 247 | + # end | |
| 248 | + # should_not have_css(".note_holder") | |
| 246 | 249 | #end |
| 247 | 250 | end |
| 248 | 251 | end |
| 249 | 252 | |
| 250 | 253 | # TODO: fix |
| 251 | 254 | #describe "when replying to a note" do |
| 252 | - #before do | |
| 253 | - ## create first note | |
| 254 | - #find('a[data-line-code="4735dfc552ad7bf15ca468adc3cad9d05b624490_184_184"]').click | |
| 255 | + #before do | |
| 256 | + ## create first note | |
| 257 | + # find('a[data-line-code="4735dfc552ad7bf15ca468adc3cad9d05b624490_184_184"]').click | |
| 255 | 258 | |
| 256 | - #within(".js-temp-notes-holder") do | |
| 257 | - #fill_in "note[note]", with: "One comment on line 184" | |
| 258 | - #click_button("Add Comment") | |
| 259 | - #end | |
| 259 | + # within(".js-temp-notes-holder") do | |
| 260 | + # fill_in "note[note]", with: "One comment on line 184" | |
| 261 | + # click_button("Add Comment") | |
| 262 | + #end | |
| 260 | 263 | |
| 261 | - #within(".js-temp-notes-holder") do | |
| 262 | - #find(".js-discussion-reply-button").click | |
| 263 | - #fill_in "note[note]", with: "An additional comment in reply" | |
| 264 | - #click_button("Add Comment") | |
| 265 | - #end | |
| 266 | - #end | |
| 267 | - | |
| 268 | - #it 'should be inserted and form removed from reply' do | |
| 269 | - #should have_content("An additional comment in reply") | |
| 270 | - #within(".notes_holder") { should have_css(".note", count: 2) } | |
| 271 | - #within(".notes_holder") { should have_no_css("form") } | |
| 272 | - #within(".notes_holder") { should have_link("Reply") } | |
| 273 | - #end | |
| 264 | + # within(".js-temp-notes-holder") do | |
| 265 | + # find(".js-discussion-reply-button").click | |
| 266 | + # fill_in "note[note]", with: "An additional comment in reply" | |
| 267 | + # click_button("Add Comment") | |
| 268 | + # end | |
| 269 | + #end | |
| 270 | + | |
| 271 | + #it 'should be inserted and form removed from reply' do | |
| 272 | + # should have_content("An additional comment in reply") | |
| 273 | + # within(".notes_holder") { should have_css(".note", count: 2) } | |
| 274 | + # within(".notes_holder") { should have_no_css("form") } | |
| 275 | + # within(".notes_holder") { should have_link("Reply") } | |
| 276 | + # end | |
| 274 | 277 | #end |
| 275 | 278 | end |
| 276 | 279 | ... | ... |
spec/features/profile_spec.rb
spec/features/projects_spec.rb
spec/features/security/project_access_spec.rb
| ... | ... | @@ -14,10 +14,10 @@ describe "Application access" do |
| 14 | 14 | end |
| 15 | 15 | |
| 16 | 16 | describe "Project" do |
| 17 | - let(:project) { create(:project_with_code) } | |
| 17 | + let(:project) { create(:project_with_code) } | |
| 18 | 18 | |
| 19 | - let(:master) { create(:user) } | |
| 20 | - let(:guest) { create(:user) } | |
| 19 | + let(:master) { create(:user) } | |
| 20 | + let(:guest) { create(:user) } | |
| 21 | 21 | let(:reporter) { create(:user) } |
| 22 | 22 | |
| 23 | 23 | before do |
| ... | ... | @@ -108,7 +108,7 @@ describe "Application access" do |
| 108 | 108 | describe "GET /project_code/blob" do |
| 109 | 109 | before do |
| 110 | 110 | commit = project.repository.commit |
| 111 | - path = commit.tree.contents.select { |i| i.is_a?(Grit::Blob)}.first.name | |
| 111 | + path = commit.tree.contents.select { |i| i.is_a?(Grit::Blob) }.first.name | |
| 112 | 112 | @blob_path = project_blob_path(project, File.join(commit.id, path)) |
| 113 | 113 | end |
| 114 | 114 | |
| ... | ... | @@ -232,13 +232,13 @@ describe "Application access" do |
| 232 | 232 | |
| 233 | 233 | |
| 234 | 234 | describe "PublicProject" do |
| 235 | - let(:project) { create(:project_with_code) } | |
| 235 | + let(:project) { create(:project_with_code) } | |
| 236 | 236 | |
| 237 | - let(:master) { create(:user) } | |
| 238 | - let(:guest) { create(:user) } | |
| 237 | + let(:master) { create(:user) } | |
| 238 | + let(:guest) { create(:user) } | |
| 239 | 239 | let(:reporter) { create(:user) } |
| 240 | 240 | |
| 241 | - let(:admin) { create(:user) } | |
| 241 | + let(:admin) { create(:user) } | |
| 242 | 242 | |
| 243 | 243 | before do |
| 244 | 244 | # public project |
| ... | ... | @@ -339,7 +339,7 @@ describe "Application access" do |
| 339 | 339 | describe "GET /project_code/blob" do |
| 340 | 340 | before do |
| 341 | 341 | commit = project.repository.commit |
| 342 | - path = commit.tree.contents.select { |i| i.is_a?(Grit::Blob)}.first.name | |
| 342 | + path = commit.tree.contents.select { |i| i.is_a?(Grit::Blob) }.first.name | |
| 343 | 343 | @blob_path = project_blob_path(project, File.join(commit.id, path)) |
| 344 | 344 | end |
| 345 | 345 | ... | ... |
spec/helpers/gitlab_markdown_helper_spec.rb
| ... | ... | @@ -9,7 +9,7 @@ describe GitlabMarkdownHelper do |
| 9 | 9 | let(:user) { create(:user, username: 'gfm') } |
| 10 | 10 | let(:commit) { project.repository.commit } |
| 11 | 11 | let(:issue) { create(:issue, project: project) } |
| 12 | - let(:merge_request) { create(:merge_request, project: project) } | |
| 12 | + let(:merge_request) { create(:merge_request, source_project: project, target_project: project) } | |
| 13 | 13 | let(:snippet) { create(:project_snippet, project: project) } |
| 14 | 14 | let(:member) { project.users_projects.where(user_id: user).first } |
| 15 | 15 | ... | ... |
| ... | ... | @@ -0,0 +1,116 @@ |
| 1 | +require 'spec_helper' | |
| 2 | + | |
| 3 | +describe 'Gitlab::Satellite::Action' do | |
| 4 | + let(:project) { create(:project_with_code) } | |
| 5 | + let(:user) { create(:user) } | |
| 6 | + | |
| 7 | + describe '#prepare_satellite!' do | |
| 8 | + | |
| 9 | + it 'create a repository with a parking branch and one remote: origin' do | |
| 10 | + repo = project.satellite.repo | |
| 11 | + | |
| 12 | + #now lets dirty it up | |
| 13 | + | |
| 14 | + starting_remote_count = repo.git.list_remotes.size | |
| 15 | + starting_remote_count.should >= 1 | |
| 16 | + #kind of hookey way to add a second remote | |
| 17 | + origin_uri = repo.git.remote({v: true}).split(" ")[1] | |
| 18 | + begin | |
| 19 | + repo.git.remote({raise: true}, 'add', 'another-remote', origin_uri) | |
| 20 | + repo.git.branch({raise: true}, 'a-new-branch') | |
| 21 | + | |
| 22 | + repo.heads.size.should > (starting_remote_count) | |
| 23 | + repo.git.remote().split(" ").size.should > (starting_remote_count) | |
| 24 | + rescue | |
| 25 | + end | |
| 26 | + | |
| 27 | + repo.git.config({}, "user.name", "#{user.name} -- foo") | |
| 28 | + repo.git.config({}, "user.email", "#{user.email} -- foo") | |
| 29 | + repo.config['user.name'].should =="#{user.name} -- foo" | |
| 30 | + repo.config['user.email'].should =="#{user.email} -- foo" | |
| 31 | + | |
| 32 | + | |
| 33 | + #These must happen in the context of the satellite directory... | |
| 34 | + satellite_action = Gitlab::Satellite::Action.new(user, project) | |
| 35 | + project.satellite.lock { | |
| 36 | + #Now clean it up, use send to get around prepare_satellite! being protected | |
| 37 | + satellite_action.send(:prepare_satellite!, repo) | |
| 38 | + } | |
| 39 | + | |
| 40 | + #verify it's clean | |
| 41 | + heads = repo.heads.map(&:name) | |
| 42 | + heads.size.should == 1 | |
| 43 | + heads.include?(Gitlab::Satellite::Satellite::PARKING_BRANCH).should == true | |
| 44 | + remotes = repo.git.remote().split(' ') | |
| 45 | + remotes.size.should == 1 | |
| 46 | + remotes.include?('origin').should == true | |
| 47 | + repo.config['user.name'].should ==user.name | |
| 48 | + repo.config['user.email'].should ==user.email | |
| 49 | + end | |
| 50 | + end | |
| 51 | + | |
| 52 | + describe '#in_locked_and_timed_satellite' do | |
| 53 | + | |
| 54 | + it 'should make use of a lockfile' do | |
| 55 | + repo = project.satellite.repo | |
| 56 | + called = false | |
| 57 | + | |
| 58 | + #set assumptions | |
| 59 | + File.rm(project.satellite.lock_file) unless !File.exists? project.satellite.lock_file | |
| 60 | + | |
| 61 | + File.exists?(project.satellite.lock_file).should be_false | |
| 62 | + | |
| 63 | + satellite_action = Gitlab::Satellite::Action.new(user, project) | |
| 64 | + satellite_action.send(:in_locked_and_timed_satellite) do |sat_repo| | |
| 65 | + repo.should == sat_repo | |
| 66 | + (File.exists? project.satellite.lock_file).should be_true | |
| 67 | + called = true | |
| 68 | + end | |
| 69 | + | |
| 70 | + called.should be_true | |
| 71 | + | |
| 72 | + end | |
| 73 | + | |
| 74 | + it 'should be able to use the satellite after locking' do | |
| 75 | + repo = project.satellite.repo | |
| 76 | + called = false | |
| 77 | + | |
| 78 | + # Set base assumptions | |
| 79 | + if File.exists? project.satellite.lock_file | |
| 80 | + FileLockStatusChecker.new(project.satellite.lock_file).flocked?.should be_false | |
| 81 | + end | |
| 82 | + | |
| 83 | + satellite_action = Gitlab::Satellite::Action.new(user, project) | |
| 84 | + satellite_action.send(:in_locked_and_timed_satellite) do |sat_repo| | |
| 85 | + called = true | |
| 86 | + repo.should == sat_repo | |
| 87 | + (File.exists? project.satellite.lock_file).should be_true | |
| 88 | + FileLockStatusChecker.new(project.satellite.lock_file).flocked?.should be_true | |
| 89 | + end | |
| 90 | + | |
| 91 | + called.should be_true | |
| 92 | + FileLockStatusChecker.new(project.satellite.lock_file).flocked?.should be_false | |
| 93 | + | |
| 94 | + end | |
| 95 | + | |
| 96 | + class FileLockStatusChecker < File | |
| 97 | + def flocked? &block | |
| 98 | + status = flock LOCK_EX|LOCK_NB | |
| 99 | + case status | |
| 100 | + when false | |
| 101 | + return true | |
| 102 | + when 0 | |
| 103 | + begin | |
| 104 | + block ? block.call : false | |
| 105 | + ensure | |
| 106 | + flock LOCK_UN | |
| 107 | + end | |
| 108 | + else | |
| 109 | + raise SystemCallError, status | |
| 110 | + end | |
| 111 | + end | |
| 112 | + end | |
| 113 | + | |
| 114 | + end | |
| 115 | +end | |
| 116 | + | ... | ... |
| ... | ... | @@ -0,0 +1,148 @@ |
| 1 | +require 'spec_helper' | |
| 2 | + | |
| 3 | +describe 'Gitlab::Satellite::MergeAction' do | |
| 4 | + before(:each) do | |
| 5 | +# TestEnv.init(mailer: false, init_repos: true, repos: true) | |
| 6 | + @master = ['master', 'bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a'] | |
| 7 | + @one_after_stable = ['stable', '6ea87c47f0f8a24ae031c3fff17bc913889ecd00'] #this commit sha is one after stable | |
| 8 | + @wiki_branch = ['wiki', '635d3e09b72232b6e92a38de6cc184147e5bcb41'] #this is the commit sha where the wiki branch goes off from master | |
| 9 | + @conflicting_metior = ['metior', '313d96e42b313a0af5ab50fa233bf43e27118b3f'] #this branch conflicts with the wiki branch | |
| 10 | + | |
| 11 | + #these commits are quite close together, itended to make string diffs/format patches small | |
| 12 | + @close_commit1 = ['2_3_notes_fix', '8470d70da67355c9c009e4401746b1d5410af2e3'] | |
| 13 | + @close_commit2 = ['scss_refactoring', 'f0f14c8eaba69ebddd766498a9d0b0e79becd633'] | |
| 14 | + end | |
| 15 | + | |
| 16 | + let(:project) { create(:project_with_code) } | |
| 17 | + let(:merge_request) { create(:merge_request, source_project: project, target_project: project) } | |
| 18 | + let(:merge_request_fork) { create(:merge_request) } | |
| 19 | + describe '#commits_between' do | |
| 20 | + def verify_commits(commits, first_commit_sha, last_commit_sha) | |
| 21 | + commits.each { |commit| commit.class.should == Gitlab::Git::Commit } | |
| 22 | + commits.first.id.should == first_commit_sha | |
| 23 | + commits.last.id.should == last_commit_sha | |
| 24 | + end | |
| 25 | + | |
| 26 | + context 'on fork' do | |
| 27 | + it 'should get proper commits between' do | |
| 28 | + merge_request_fork.target_branch = @one_after_stable[0] | |
| 29 | + merge_request_fork.source_branch = @master[0] | |
| 30 | + commits = Gitlab::Satellite::MergeAction.new(merge_request_fork.author, merge_request_fork).commits_between | |
| 31 | + verify_commits(commits, @one_after_stable[1], @master[1]) | |
| 32 | + | |
| 33 | + merge_request_fork.target_branch = @wiki_branch[0] | |
| 34 | + merge_request_fork.source_branch = @master[0] | |
| 35 | + commits = Gitlab::Satellite::MergeAction.new(merge_request_fork.author, merge_request_fork).commits_between | |
| 36 | + verify_commits(commits, @wiki_branch[1], @master[1]) | |
| 37 | + end | |
| 38 | + end | |
| 39 | + | |
| 40 | + context 'between branches' do | |
| 41 | + it 'should raise exception -- not expected to be used by non forks' do | |
| 42 | + merge_request.target_branch = @one_after_stable[0] | |
| 43 | + merge_request.source_branch = @master[0] | |
| 44 | + expect {Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).commits_between}.to raise_error | |
| 45 | + | |
| 46 | + merge_request.target_branch = @wiki_branch[0] | |
| 47 | + merge_request.source_branch = @master[0] | |
| 48 | + expect {Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).commits_between}.to raise_error | |
| 49 | + end | |
| 50 | + end | |
| 51 | + end | |
| 52 | + | |
| 53 | + describe '#format_patch' do | |
| 54 | + let(:target_commit) {['artiom-config-examples','9edbac5ac88ffa1ec9dad0097226b51e29ebc9ac']} | |
| 55 | + let(:source_commit) {['metior', '313d96e42b313a0af5ab50fa233bf43e27118b3f']} | |
| 56 | + | |
| 57 | + def verify_content(patch) | |
| 58 | + (patch.include? source_commit[1]).should be_true | |
| 59 | + (patch.include? '635d3e09b72232b6e92a38de6cc184147e5bcb41').should be_true | |
| 60 | + (patch.include? '2bb2dee057327c81978ed0aa99904bd7ff5e6105').should be_true | |
| 61 | + (patch.include? '2e83de1924ad3429b812d17498b009a8b924795d').should be_true | |
| 62 | + (patch.include? 'ee45a49c57a362305431cbf004e4590b713c910e').should be_true | |
| 63 | + (patch.include? 'a6870dd08f8f274d9a6b899f638c0c26fefaa690').should be_true | |
| 64 | + | |
| 65 | + (patch.include? 'e74fae147abc7d2ffbf93d363dbbe45b87751f6f').should be_false | |
| 66 | + (patch.include? '86f76b11c670425bbab465087f25172378d76147').should be_false | |
| 67 | + end | |
| 68 | + | |
| 69 | + context 'on fork' do | |
| 70 | + it 'should build a format patch' do | |
| 71 | + merge_request_fork.target_branch = target_commit[0] | |
| 72 | + merge_request_fork.source_branch = source_commit[0] | |
| 73 | + patch = Gitlab::Satellite::MergeAction.new(merge_request_fork.author, merge_request_fork).format_patch | |
| 74 | + verify_content(patch) | |
| 75 | + end | |
| 76 | + end | |
| 77 | + | |
| 78 | + context 'between branches' do | |
| 79 | + it 'should build a format patch' do | |
| 80 | + merge_request.target_branch = target_commit[0] | |
| 81 | + merge_request.source_branch = source_commit[0] | |
| 82 | + patch = Gitlab::Satellite::MergeAction.new(merge_request_fork.author, merge_request).format_patch | |
| 83 | + verify_content(patch) | |
| 84 | + end | |
| 85 | + end | |
| 86 | + end | |
| 87 | + | |
| 88 | + describe '#diffs_between_satellite tested against diff_in_satellite' do | |
| 89 | + | |
| 90 | + def is_a_matching_diff(diff, diffs) | |
| 91 | + diff_count = diff.scan('diff --git').size | |
| 92 | + diff_count.should >= 1 | |
| 93 | + diffs.size.should == diff_count | |
| 94 | + diffs.each do |a_diff| | |
| 95 | + a_diff.class.should == Gitlab::Git::Diff | |
| 96 | + (diff.include? a_diff.diff).should be_true | |
| 97 | + end | |
| 98 | + end | |
| 99 | + | |
| 100 | + context 'on fork' do | |
| 101 | + it 'should get proper diffs' do | |
| 102 | + merge_request_fork.target_branch = @close_commit1[0] | |
| 103 | + merge_request_fork.source_branch = @master[0] | |
| 104 | + diffs = Gitlab::Satellite::MergeAction.new(merge_request_fork.author, merge_request_fork).diffs_between_satellite | |
| 105 | + | |
| 106 | + merge_request_fork.target_branch = @close_commit1[0] | |
| 107 | + merge_request_fork.source_branch = @master[0] | |
| 108 | + diff = Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request_fork).diff_in_satellite | |
| 109 | + | |
| 110 | + is_a_matching_diff(diff, diffs) | |
| 111 | + end | |
| 112 | + end | |
| 113 | + | |
| 114 | + context 'between branches' do | |
| 115 | + it 'should get proper diffs' do | |
| 116 | + merge_request.target_branch = @close_commit1[0] | |
| 117 | + merge_request.source_branch = @master[0] | |
| 118 | + expect{Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).diffs_between_satellite}.to raise_error | |
| 119 | + end | |
| 120 | + end | |
| 121 | + end | |
| 122 | + | |
| 123 | + describe '#can_be_merged?' do | |
| 124 | + context 'on fork' do | |
| 125 | + it 'return true or false depending on if something is mergable' do | |
| 126 | + merge_request_fork.target_branch = @one_after_stable[0] | |
| 127 | + merge_request_fork.source_branch = @master[0] | |
| 128 | + Gitlab::Satellite::MergeAction.new(merge_request_fork.author, merge_request_fork).can_be_merged?.should be_true | |
| 129 | + | |
| 130 | + merge_request_fork.target_branch = @conflicting_metior[0] | |
| 131 | + merge_request_fork.source_branch = @wiki_branch[0] | |
| 132 | + Gitlab::Satellite::MergeAction.new(merge_request_fork.author, merge_request_fork).can_be_merged?.should be_false | |
| 133 | + end | |
| 134 | + end | |
| 135 | + | |
| 136 | + context 'between branches' do | |
| 137 | + it 'return true or false depending on if something is mergable' do | |
| 138 | + merge_request.target_branch = @one_after_stable[0] | |
| 139 | + merge_request.source_branch = @master[0] | |
| 140 | + Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).can_be_merged?.should be_true | |
| 141 | + | |
| 142 | + merge_request.target_branch = @conflicting_metior[0] | |
| 143 | + merge_request.source_branch = @wiki_branch[0] | |
| 144 | + Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).can_be_merged?.should be_false | |
| 145 | + end | |
| 146 | + end | |
| 147 | + end | |
| 148 | +end | |
| 0 | 149 | \ No newline at end of file | ... | ... |
spec/mailers/notify_spec.rb
| ... | ... | @@ -167,7 +167,7 @@ describe Notify do |
| 167 | 167 | end |
| 168 | 168 | |
| 169 | 169 | context 'for merge requests' do |
| 170 | - let(:merge_request) { create(:merge_request, assignee: assignee, project: project) } | |
| 170 | + let(:merge_request) { create(:merge_request, assignee: assignee, source_project: project, target_project: project) } | |
| 171 | 171 | |
| 172 | 172 | describe 'that are new' do |
| 173 | 173 | subject { Notify.new_merge_request_email(merge_request.assignee_id, merge_request.id) } |
| ... | ... | @@ -311,7 +311,7 @@ describe Notify do |
| 311 | 311 | end |
| 312 | 312 | |
| 313 | 313 | describe 'on a merge request' do |
| 314 | - let(:merge_request) { create(:merge_request, project: project) } | |
| 314 | + let(:merge_request) { create(:merge_request, source_project: project, target_project: project) } | |
| 315 | 315 | let(:note_on_merge_request_path) { project_merge_request_path(project, merge_request, anchor: "note_#{note.id}") } |
| 316 | 316 | before(:each) { note.stub(:noteable).and_return(merge_request) } |
| 317 | 317 | ... | ... |
spec/models/commit_spec.rb
spec/models/forked_project_link_spec.rb
| ... | ... | @@ -12,9 +12,9 @@ |
| 12 | 12 | require 'spec_helper' |
| 13 | 13 | |
| 14 | 14 | describe ForkedProjectLink, "add link on fork" do |
| 15 | - let(:project_from) {create(:project)} | |
| 16 | - let(:namespace) {create(:namespace)} | |
| 17 | - let(:user) {create(:user, namespace: namespace)} | |
| 15 | + let(:project_from) { create(:project) } | |
| 16 | + let(:namespace) { create(:namespace) } | |
| 17 | + let(:user) { create(:user, namespace: namespace) } | |
| 18 | 18 | |
| 19 | 19 | before do |
| 20 | 20 | @project_to = fork_project(project_from, user) |
| ... | ... | @@ -30,9 +30,9 @@ describe ForkedProjectLink, "add link on fork" do |
| 30 | 30 | end |
| 31 | 31 | |
| 32 | 32 | describe :forked_from_project do |
| 33 | - let(:forked_project_link) {build(:forked_project_link)} | |
| 34 | - let(:project_from) {create(:project)} | |
| 35 | - let(:project_to) {create(:project, forked_project_link: forked_project_link)} | |
| 33 | + let(:forked_project_link) { build(:forked_project_link) } | |
| 34 | + let(:project_from) { create(:project) } | |
| 35 | + let(:project_to) { create(:project, forked_project_link: forked_project_link) } | |
| 36 | 36 | |
| 37 | 37 | |
| 38 | 38 | before :each do | ... | ... |
spec/models/merge_request_spec.rb
| ... | ... | @@ -41,15 +41,12 @@ describe MergeRequest do |
| 41 | 41 | it { should include_module(Issuable) } |
| 42 | 42 | end |
| 43 | 43 | |
| 44 | - describe "#mr_and_commit_notes" do | |
| 45 | - | |
| 46 | - end | |
| 47 | 44 | |
| 48 | 45 | describe "#mr_and_commit_notes" do |
| 49 | 46 | let!(:merge_request) { create(:merge_request) } |
| 50 | 47 | |
| 51 | 48 | before do |
| 52 | - merge_request.stub(:commits) { [merge_request.project.repository.commit] } | |
| 49 | + merge_request.stub(:commits) { [merge_request.source_project.repository.commit] } | |
| 53 | 50 | create(:note, commit_id: merge_request.commits.first.id, noteable_type: 'Commit') |
| 54 | 51 | create(:note, noteable: merge_request) |
| 55 | 52 | end |
| ... | ... | @@ -71,4 +68,38 @@ describe MergeRequest do |
| 71 | 68 | subject.is_being_reassigned?.should be_false |
| 72 | 69 | end |
| 73 | 70 | end |
| 71 | + | |
| 72 | + describe '#for_fork?' do | |
| 73 | + it 'returns true if the merge request is for a fork' do | |
| 74 | + subject.source_project = create(:source_project) | |
| 75 | + subject.target_project = create(:target_project) | |
| 76 | + | |
| 77 | + subject.for_fork?.should be_true | |
| 78 | + end | |
| 79 | + it 'returns false if is not for a fork' do | |
| 80 | + subject.source_project = create(:source_project) | |
| 81 | + subject.target_project = subject.source_project | |
| 82 | + subject.for_fork?.should be_false | |
| 83 | + end | |
| 84 | + end | |
| 85 | + | |
| 86 | + describe '#allow_source_branch_removal?' do | |
| 87 | + it 'should not allow removal when mr is a fork' do | |
| 88 | + | |
| 89 | + subject.disallow_source_branch_removal?.should be_true | |
| 90 | + end | |
| 91 | + it 'should not allow removal when the mr is not a fork, but the source branch is the root reference' do | |
| 92 | + subject.target_project = subject.source_project | |
| 93 | + subject.source_branch = subject.source_project.repository.root_ref | |
| 94 | + subject.disallow_source_branch_removal?.should be_true | |
| 95 | + end | |
| 96 | + | |
| 97 | + it 'should not disallow removal when the mr is not a fork, and but source branch is not the root reference' do | |
| 98 | + subject.target_project = subject.source_project | |
| 99 | + subject.source_branch = "Something Different #{subject.source_project.repository.root_ref}" | |
| 100 | + subject.for_fork?.should be_false | |
| 101 | + subject.disallow_source_branch_removal?.should be_false | |
| 102 | + end | |
| 103 | + end | |
| 104 | + | |
| 74 | 105 | end | ... | ... |
spec/models/note_spec.rb
| ... | ... | @@ -144,12 +144,12 @@ describe Note do |
| 144 | 144 | end |
| 145 | 145 | |
| 146 | 146 | describe '#create_status_change_note' do |
| 147 | - let(:project) { create(:project) } | |
| 148 | - let(:thing) { create(:issue, project: project) } | |
| 149 | - let(:author) { create(:user) } | |
| 150 | - let(:status) { 'new_status' } | |
| 147 | + let(:project) { create(:project) } | |
| 148 | + let(:thing) { create(:issue, project: project) } | |
| 149 | + let(:author) { create(:user) } | |
| 150 | + let(:status) { 'new_status' } | |
| 151 | 151 | |
| 152 | - subject { Note.create_status_change_note(thing, author, status) } | |
| 152 | + subject { Note.create_status_change_note(thing, project, author, status) } | |
| 153 | 153 | |
| 154 | 154 | it 'creates and saves a Note' do |
| 155 | 155 | should be_a Note |
| ... | ... | @@ -157,9 +157,9 @@ describe Note do |
| 157 | 157 | end |
| 158 | 158 | |
| 159 | 159 | its(:noteable) { should == thing } |
| 160 | - its(:project) { should == thing.project } | |
| 161 | - its(:author) { should == author } | |
| 162 | - its(:note) { should =~ /Status changed to #{status}/ } | |
| 160 | + its(:project) { should == thing.project } | |
| 161 | + its(:author) { should == author } | |
| 162 | + its(:note) { should =~ /Status changed to #{status}/ } | |
| 163 | 163 | end |
| 164 | 164 | |
| 165 | 165 | describe :authorization do | ... | ... |
spec/models/project_spec.rb
| ... | ... | @@ -26,6 +26,9 @@ |
| 26 | 26 | require 'spec_helper' |
| 27 | 27 | |
| 28 | 28 | describe Project do |
| 29 | + before(:each) { enable_observers } | |
| 30 | + after(:each) { disable_observers } | |
| 31 | + | |
| 29 | 32 | describe "Associations" do |
| 30 | 33 | it { should belong_to(:group) } |
| 31 | 34 | it { should belong_to(:namespace) } |
| ... | ... | @@ -95,12 +98,11 @@ describe Project do |
| 95 | 98 | end |
| 96 | 99 | |
| 97 | 100 | describe "last_activity methods" do |
| 98 | - before { enable_observers } | |
| 99 | - let(:project) { create(:project) } | |
| 101 | + let(:project) { create(:project) } | |
| 100 | 102 | let(:last_event) { double(created_at: Time.now) } |
| 101 | 103 | |
| 102 | 104 | describe "last_activity" do |
| 103 | - it "should alias last_activity to last_event"do | |
| 105 | + it "should alias last_activity to last_event" do | |
| 104 | 106 | project.stub(last_event: last_event) |
| 105 | 107 | project.last_activity.should == last_event |
| 106 | 108 | end |
| ... | ... | @@ -122,7 +124,7 @@ describe Project do |
| 122 | 124 | let(:project) { create(:project_with_code) } |
| 123 | 125 | |
| 124 | 126 | before do |
| 125 | - @merge_request = create(:merge_request, project: project) | |
| 127 | + @merge_request = create(:merge_request, source_project: project, target_project: project) | |
| 126 | 128 | @key = create(:key, user_id: project.owner.id) |
| 127 | 129 | end |
| 128 | 130 | ... | ... |
spec/observers/activity_observer_spec.rb
| ... | ... | @@ -8,18 +8,6 @@ describe ActivityObserver do |
| 8 | 8 | it { @event.project.should == project } |
| 9 | 9 | end |
| 10 | 10 | |
| 11 | - describe "Merge Request created" do | |
| 12 | - before do | |
| 13 | - MergeRequest.observers.enable :activity_observer do | |
| 14 | - @merge_request = create(:merge_request, project: project) | |
| 15 | - @event = Event.last | |
| 16 | - end | |
| 17 | - end | |
| 18 | - | |
| 19 | - it_should_be_valid_event | |
| 20 | - it { @event.action.should == Event::CREATED } | |
| 21 | - it { @event.target.should == @merge_request } | |
| 22 | - end | |
| 23 | 11 | |
| 24 | 12 | describe "Issue created" do |
| 25 | 13 | before do | ... | ... |
spec/observers/issue_observer_spec.rb
| ... | ... | @@ -26,14 +26,13 @@ describe IssueObserver do |
| 26 | 26 | before { mock_issue.stub(state: 'closed') } |
| 27 | 27 | |
| 28 | 28 | it 'note is created if the issue is being closed' do |
| 29 | - Note.should_receive(:create_status_change_note).with(mock_issue, some_user, 'closed') | |
| 29 | + Note.should_receive(:create_status_change_note).with(mock_issue, mock_issue.project, some_user, 'closed') | |
| 30 | 30 | |
| 31 | 31 | subject.after_close(mock_issue, nil) |
| 32 | 32 | end |
| 33 | 33 | |
| 34 | 34 | it 'trigger notification to send emails' do |
| 35 | 35 | subject.notification.should_receive(:close_issue).with(mock_issue, some_user) |
| 36 | - | |
| 37 | 36 | subject.after_close(mock_issue, nil) |
| 38 | 37 | end |
| 39 | 38 | end |
| ... | ... | @@ -42,8 +41,7 @@ describe IssueObserver do |
| 42 | 41 | before { mock_issue.stub(state: 'reopened') } |
| 43 | 42 | |
| 44 | 43 | it 'note is created if the issue is being reopened' do |
| 45 | - Note.should_receive(:create_status_change_note).with(mock_issue, some_user, 'reopened') | |
| 46 | - | |
| 44 | + Note.should_receive(:create_status_change_note).with(mock_issue, mock_issue.project, some_user, 'reopened') | |
| 47 | 45 | subject.after_reopen(mock_issue, nil) |
| 48 | 46 | end |
| 49 | 47 | end | ... | ... |
spec/observers/merge_request_observer_spec.rb
| 1 | 1 | require 'spec_helper' |
| 2 | 2 | |
| 3 | 3 | describe MergeRequestObserver do |
| 4 | - let(:some_user) { create :user } | |
| 5 | - let(:assignee) { create :user } | |
| 6 | - let(:author) { create :user } | |
| 7 | - let(:mr_mock) { double(:merge_request, id: 42, assignee: assignee, author: author) } | |
| 8 | - let(:assigned_mr) { create(:merge_request, assignee: assignee, author: author) } | |
| 4 | + let(:some_user) { create :user } | |
| 5 | + let(:assignee) { create :user } | |
| 6 | + let(:author) { create :user } | |
| 7 | + let(:mr_mock) { double(:merge_request, id: 42, assignee: assignee, author: author) } | |
| 8 | + let(:assigned_mr) { create(:merge_request, assignee: assignee, author: author) } | |
| 9 | 9 | let(:unassigned_mr) { create(:merge_request, author: author) } |
| 10 | - let(:closed_assigned_mr) { create(:closed_merge_request, assignee: assignee, author: author) } | |
| 10 | + let(:closed_assigned_mr) { create(:closed_merge_request, assignee: assignee, author: author) } | |
| 11 | 11 | let(:closed_unassigned_mr) { create(:closed_merge_request, author: author) } |
| 12 | 12 | |
| 13 | 13 | before { subject.stub(:current_user).and_return(some_user) } |
| 14 | 14 | before { subject.stub(notification: mock('NotificationService').as_null_object) } |
| 15 | + before { mr_mock.stub(:author_id) } | |
| 16 | + before { mr_mock.stub(:target_project) } | |
| 15 | 17 | before(:each) { enable_observers } |
| 16 | - | |
| 18 | + after(:each) { disable_observers } | |
| 17 | 19 | |
| 18 | 20 | subject { MergeRequestObserver.instance } |
| 19 | 21 | |
| ... | ... | @@ -30,7 +32,7 @@ describe MergeRequestObserver do |
| 30 | 32 | end |
| 31 | 33 | |
| 32 | 34 | it 'is called when a merge request is changed' do |
| 33 | - changed = create(:merge_request, project: create(:project)) | |
| 35 | + changed = create(:merge_request, source_project: create(:project)) | |
| 34 | 36 | subject.should_receive(:after_update) |
| 35 | 37 | |
| 36 | 38 | MergeRequest.observers.enable :merge_request_observer do |
| ... | ... | @@ -59,13 +61,13 @@ describe MergeRequestObserver do |
| 59 | 61 | context '#after_close' do |
| 60 | 62 | context 'a status "closed"' do |
| 61 | 63 | it 'note is created if the merge request is being closed' do |
| 62 | - Note.should_receive(:create_status_change_note).with(assigned_mr, some_user, 'closed') | |
| 64 | + Note.should_receive(:create_status_change_note).with(assigned_mr, assigned_mr.target_project, some_user, 'closed') | |
| 63 | 65 | |
| 64 | 66 | assigned_mr.close |
| 65 | 67 | end |
| 66 | 68 | |
| 67 | 69 | it 'notification is delivered only to author if the merge request is being closed' do |
| 68 | - Note.should_receive(:create_status_change_note).with(unassigned_mr, some_user, 'closed') | |
| 70 | + Note.should_receive(:create_status_change_note).with(unassigned_mr, unassigned_mr.target_project, some_user, 'closed') | |
| 69 | 71 | |
| 70 | 72 | unassigned_mr.close |
| 71 | 73 | end |
| ... | ... | @@ -75,16 +77,41 @@ describe MergeRequestObserver do |
| 75 | 77 | context '#after_reopen' do |
| 76 | 78 | context 'a status "reopened"' do |
| 77 | 79 | it 'note is created if the merge request is being reopened' do |
| 78 | - Note.should_receive(:create_status_change_note).with(closed_assigned_mr, some_user, 'reopened') | |
| 80 | + Note.should_receive(:create_status_change_note).with(closed_assigned_mr, closed_assigned_mr.target_project, some_user, 'reopened') | |
| 79 | 81 | |
| 80 | 82 | closed_assigned_mr.reopen |
| 81 | 83 | end |
| 82 | 84 | |
| 83 | 85 | it 'notification is delivered only to author if the merge request is being reopened' do |
| 84 | - Note.should_receive(:create_status_change_note).with(closed_unassigned_mr, some_user, 'reopened') | |
| 86 | + Note.should_receive(:create_status_change_note).with(closed_unassigned_mr, closed_unassigned_mr.target_project, some_user, 'reopened') | |
| 85 | 87 | |
| 86 | 88 | closed_unassigned_mr.reopen |
| 87 | 89 | end |
| 88 | 90 | end |
| 89 | 91 | end |
| 92 | + | |
| 93 | + describe "Merge Request created" do | |
| 94 | + def self.it_should_be_valid_event | |
| 95 | + it { @event.should_not be_nil } | |
| 96 | + it { @event.should_not be_nil } | |
| 97 | + it { @event.project.should == project } | |
| 98 | + it { @event.project.should == project } | |
| 99 | + end | |
| 100 | + | |
| 101 | + let(:project) { create(:project) } | |
| 102 | + before do | |
| 103 | + TestEnv.enable_observers | |
| 104 | + @merge_request = create(:merge_request, source_project: project, target_project: project) | |
| 105 | + @event = Event.last | |
| 106 | + end | |
| 107 | + | |
| 108 | + after do | |
| 109 | + TestEnv.disable_observers | |
| 110 | + end | |
| 111 | + | |
| 112 | + it_should_be_valid_event | |
| 113 | + it { @event.action.should == Event::CREATED } | |
| 114 | + it { @event.target.should == @merge_request } | |
| 115 | + end | |
| 116 | + | |
| 90 | 117 | end | ... | ... |
spec/observers/user_observer_spec.rb
spec/observers/users_project_observer_spec.rb
spec/requests/api/merge_requests_spec.rb
| ... | ... | @@ -3,10 +3,12 @@ require "spec_helper" |
| 3 | 3 | describe API::API do |
| 4 | 4 | include ApiHelpers |
| 5 | 5 | |
| 6 | - let(:user) { create(:user ) } | |
| 7 | - let!(:project) { create(:project_with_code, creator_id: user.id) } | |
| 8 | - let!(:merge_request) { create(:merge_request, author: user, assignee: user, project: project, title: "Test") } | |
| 9 | - before { project.team << [user, :reporters] } | |
| 6 | + let(:user) { create(:user) } | |
| 7 | + let!(:project) {create(:project_with_code, creator_id: user.id) } | |
| 8 | + let!(:merge_request) { create(:merge_request, author: user, assignee: user, source_project: project, target_project: project, title: "Test") } | |
| 9 | + before { | |
| 10 | + project.team << [user, :reporters] | |
| 11 | + } | |
| 10 | 12 | |
| 11 | 13 | describe "GET /projects/:id/merge_requests" do |
| 12 | 14 | context "when unauthenticated" do |
| ... | ... | @@ -40,35 +42,104 @@ describe API::API do |
| 40 | 42 | end |
| 41 | 43 | |
| 42 | 44 | describe "POST /projects/:id/merge_requests" do |
| 43 | - it "should return merge_request" do | |
| 44 | - post api("/projects/#{project.id}/merge_requests", user), | |
| 45 | - title: 'Test merge_request', source_branch: "stable", target_branch: "master", author: user | |
| 46 | - response.status.should == 201 | |
| 47 | - json_response['title'].should == 'Test merge_request' | |
| 48 | - end | |
| 45 | + context 'between branches projects' do | |
| 46 | + it "should return merge_request" do | |
| 47 | + post api("/projects/#{project.id}/merge_requests", user), | |
| 48 | + title: 'Test merge_request', source_branch: "stable", target_branch: "master", author: user | |
| 49 | + response.status.should == 201 | |
| 50 | + json_response['title'].should == 'Test merge_request' | |
| 51 | + end | |
| 49 | 52 | |
| 50 | - it "should return 422 when source_branch equals target_branch" do | |
| 51 | - post api("/projects/#{project.id}/merge_requests", user), | |
| 52 | - title: "Test merge_request", source_branch: "master", target_branch: "master", author: user | |
| 53 | - response.status.should == 422 | |
| 54 | - end | |
| 53 | + it "should return 422 when source_branch equals target_branch" do | |
| 54 | + post api("/projects/#{project.id}/merge_requests", user), | |
| 55 | + title: "Test merge_request", source_branch: "master", target_branch: "master", author: user | |
| 56 | + response.status.should == 422 | |
| 57 | + end | |
| 55 | 58 | |
| 56 | - it "should return 400 when source_branch is missing" do | |
| 57 | - post api("/projects/#{project.id}/merge_requests", user), | |
| 58 | - title: "Test merge_request", target_branch: "master", author: user | |
| 59 | - response.status.should == 400 | |
| 60 | - end | |
| 59 | + it "should return 400 when source_branch is missing" do | |
| 60 | + post api("/projects/#{project.id}/merge_requests", user), | |
| 61 | + title: "Test merge_request", target_branch: "master", author: user | |
| 62 | + response.status.should == 400 | |
| 63 | + end | |
| 61 | 64 | |
| 62 | - it "should return 400 when target_branch is missing" do | |
| 63 | - post api("/projects/#{project.id}/merge_requests", user), | |
| 64 | - title: "Test merge_request", source_branch: "stable", author: user | |
| 65 | - response.status.should == 400 | |
| 65 | + it "should return 400 when target_branch is missing" do | |
| 66 | + post api("/projects/#{project.id}/merge_requests", user), | |
| 67 | + title: "Test merge_request", source_branch: "stable", author: user | |
| 68 | + response.status.should == 400 | |
| 69 | + end | |
| 70 | + | |
| 71 | + it "should return 400 when title is missing" do | |
| 72 | + post api("/projects/#{project.id}/merge_requests", user), | |
| 73 | + target_branch: 'master', source_branch: 'stable' | |
| 74 | + response.status.should == 400 | |
| 75 | + end | |
| 66 | 76 | end |
| 67 | 77 | |
| 68 | - it "should return 400 when title is missing" do | |
| 69 | - post api("/projects/#{project.id}/merge_requests", user), | |
| 70 | - target_branch: 'master', source_branch: 'stable' | |
| 71 | - response.status.should == 400 | |
| 78 | + context 'forked projects' do | |
| 79 | + let!(:user2) {create(:user)} | |
| 80 | + let!(:forked_project_link) { build(:forked_project_link) } | |
| 81 | + let!(:fork_project) { create(:source_project_with_code, forked_project_link: forked_project_link, namespace: user2.namespace, creator_id: user2.id) } | |
| 82 | + let!(:unrelated_project) { create(:target_project_with_code, namespace: user2.namespace, creator_id: user2.id) } | |
| 83 | + | |
| 84 | + before :each do |each| | |
| 85 | + fork_project.team << [user2, :reporters] | |
| 86 | + forked_project_link.forked_from_project = project | |
| 87 | + forked_project_link.forked_to_project = fork_project | |
| 88 | + forked_project_link.save! | |
| 89 | + end | |
| 90 | + | |
| 91 | + it "should return merge_request" do | |
| 92 | + post api("/projects/#{fork_project.id}/merge_requests", user2), | |
| 93 | + title: 'Test merge_request', source_branch: "stable", target_branch: "master", author: user2, target_project_id: project.id | |
| 94 | + response.status.should == 201 | |
| 95 | + json_response['title'].should == 'Test merge_request' | |
| 96 | + end | |
| 97 | + | |
| 98 | + it "should not return 422 when source_branch equals target_branch" do | |
| 99 | + project.id.should_not == fork_project.id | |
| 100 | + fork_project.forked?.should be_true | |
| 101 | + fork_project.forked_from_project.should == project | |
| 102 | + post api("/projects/#{fork_project.id}/merge_requests", user2), | |
| 103 | + title: 'Test merge_request', source_branch: "master", target_branch: "master", author: user2, target_project_id: project.id | |
| 104 | + response.status.should == 201 | |
| 105 | + json_response['title'].should == 'Test merge_request' | |
| 106 | + end | |
| 107 | + | |
| 108 | + it "should return 400 when source_branch is missing" do | |
| 109 | + post api("/projects/#{fork_project.id}/merge_requests", user2), | |
| 110 | + title: 'Test merge_request', target_branch: "master", author: user2, target_project_id: project.id | |
| 111 | + response.status.should == 400 | |
| 112 | + end | |
| 113 | + | |
| 114 | + it "should return 400 when target_branch is missing" do | |
| 115 | + post api("/projects/#{fork_project.id}/merge_requests", user2), | |
| 116 | + title: 'Test merge_request', target_branch: "master", author: user2, target_project_id: project.id | |
| 117 | + response.status.should == 400 | |
| 118 | + end | |
| 119 | + | |
| 120 | + it "should return 400 when title is missing" do | |
| 121 | + post api("/projects/#{fork_project.id}/merge_requests", user2), | |
| 122 | + target_branch: 'master', source_branch: 'stable', author: user2, target_project_id: project.id | |
| 123 | + response.status.should == 400 | |
| 124 | + end | |
| 125 | + | |
| 126 | + it "should return 400 when target_branch is specified and not a forked project" do | |
| 127 | + post api("/projects/#{project.id}/merge_requests", user), | |
| 128 | + title: 'Test merge_request', target_branch: 'master', source_branch: 'stable', author: user, target_project_id: fork_project.id | |
| 129 | + response.status.should == 400 | |
| 130 | + end | |
| 131 | + | |
| 132 | + it "should return 400 when target_branch is specified and for a different fork" do | |
| 133 | + post api("/projects/#{fork_project.id}/merge_requests", user2), | |
| 134 | + title: 'Test merge_request', target_branch: 'master', source_branch: 'stable', author: user2, target_project_id: unrelated_project.id | |
| 135 | + response.status.should == 400 | |
| 136 | + end | |
| 137 | + | |
| 138 | + it "should return 201 when target_branch is specified and for the same project" do | |
| 139 | + post api("/projects/#{fork_project.id}/merge_requests", user2), | |
| 140 | + title: 'Test merge_request', target_branch: 'master', source_branch: 'stable', author: user2, target_project_id: fork_project.id | |
| 141 | + response.status.should == 201 | |
| 142 | + end | |
| 72 | 143 | end |
| 73 | 144 | end |
| 74 | 145 | |
| ... | ... | @@ -97,14 +168,14 @@ describe API::API do |
| 97 | 168 | |
| 98 | 169 | it "should return 422 when source_branch and target_branch are renamed the same" do |
| 99 | 170 | put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), |
| 100 | - source_branch: "master", target_branch: "master" | |
| 171 | + source_branch: "master", target_branch: "master" | |
| 101 | 172 | response.status.should == 422 |
| 102 | 173 | end |
| 103 | 174 | |
| 104 | 175 | it "should return merge_request with renamed target_branch" do |
| 105 | - put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), target_branch: "test" | |
| 176 | + put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), target_branch: "wiki" | |
| 106 | 177 | response.status.should == 200 |
| 107 | - json_response['target_branch'].should == 'test' | |
| 178 | + json_response['target_branch'].should == 'wiki' | |
| 108 | 179 | end |
| 109 | 180 | end |
| 110 | 181 | ... | ... |
spec/requests/api/milestones_spec.rb
spec/requests/api/notes_spec.rb
| ... | ... | @@ -6,7 +6,7 @@ describe API::API do |
| 6 | 6 | let(:user) { create(:user) } |
| 7 | 7 | let!(:project) { create(:project, namespace: user.namespace ) } |
| 8 | 8 | let!(:issue) { create(:issue, project: project, author: user) } |
| 9 | - let!(:merge_request) { create(:merge_request, project: project, author: user) } | |
| 9 | + let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: user) } | |
| 10 | 10 | let!(:snippet) { create(:project_snippet, project: project, author: user) } |
| 11 | 11 | let!(:issue_note) { create(:note, noteable: issue, project: project, author: user) } |
| 12 | 12 | let!(:merge_request_note) { create(:note, noteable: merge_request, project: project, author: user) } | ... | ... |