summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG3
-rw-r--r--app/assets/javascripts/issue.js.coffee1
-rw-r--r--app/assets/javascripts/notes.js.coffee3
-rw-r--r--app/assets/javascripts/projects_list.js.coffee4
-rw-r--r--app/assets/stylesheets/framework/typography.scss2
-rw-r--r--app/assets/stylesheets/pages/projects.scss50
-rw-r--r--app/controllers/application_controller.rb23
-rw-r--r--app/controllers/projects/forks_controller.rb11
-rw-r--r--app/helpers/blob_helper.rb6
-rw-r--r--app/helpers/icons_helper.rb2
-rw-r--r--app/helpers/projects_helper.rb2
-rw-r--r--app/models/group.rb2
-rw-r--r--app/views/admin/groups/index.html.haml2
-rw-r--r--app/views/admin/projects/index.html.haml2
-rw-r--r--app/views/admin/users/index.html.haml2
-rw-r--r--app/views/explore/groups/index.html.haml2
-rw-r--r--app/views/explore/projects/_dropdown.html.haml3
-rw-r--r--app/views/layouts/nav/_project.html.haml7
-rw-r--r--app/views/projects/branches/index.html.haml2
-rw-r--r--app/views/projects/buttons/_dropdown.html.haml2
-rw-r--r--app/views/projects/forks/index.html.haml58
-rw-r--r--app/views/projects/forks/new.html.haml2
-rw-r--r--app/views/projects/tree/_tree_header.html.haml6
-rw-r--r--app/views/shared/_sort_dropdown.html.haml2
-rw-r--r--app/views/shared/projects/_list.html.haml6
-rw-r--r--app/views/shared/projects/_project.html.haml21
-rw-r--r--config/routes.rb2
-rw-r--r--doc/api/merge_requests.md74
-rw-r--r--features/dashboard/dashboard.feature30
-rw-r--r--features/project/fork.feature15
-rw-r--r--features/project/issues/issues.feature22
-rw-r--r--features/project/merge_requests.feature22
-rw-r--r--features/steps/dashboard/dashboard.rb1
-rw-r--r--features/steps/project/fork.rb25
-rw-r--r--features/steps/project/forked_merge_requests.rb4
-rw-r--r--features/steps/shared/issuable.rb13
-rw-r--r--lib/api/merge_requests.rb351
-rw-r--r--lib/gitlab/current_settings.rb31
-rw-r--r--spec/requests/api/merge_requests_spec.rb62
-rw-r--r--spec/routing/project_routing_spec.rb4
40 files changed, 569 insertions, 313 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 14f2f14becd..145d4c03731 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
Please view this file on the master branch, on stable branches it's out of date.
v 8.5.0 (unreleased)
+ - Ensure rake tasks that don't need a DB connection can be run without one
- Add "visibility" flag to GET /projects api endpoint
- Ignore binary files in code search to prevent Error 500 (Stan Hu)
- Upgrade gitlab_git to 7.2.23 to fix commit message mentions in first branch push
@@ -14,6 +15,8 @@ v 8.5.0 (unreleased)
- Track project import failure
- Fix visibility level text in admin area (Zeger-Jan van de Weg)
- Update the ExternalIssue regex pattern (Blake Hitchcock)
+ - Deprecate API "merge_request/:merge_request_id/comments". Use "merge_requests/:merge_request_id/notes" instead
+ - Deprecate API "merge_request/:merge_request_id/...". Use "merge_requests/:merge_request_id/..." instead
v 8.4.2
- Bump required gitlab-workhorse version to bring in a fix for missing
diff --git a/app/assets/javascripts/issue.js.coffee b/app/assets/javascripts/issue.js.coffee
index cbc70cd846c..d663e34871c 100644
--- a/app/assets/javascripts/issue.js.coffee
+++ b/app/assets/javascripts/issue.js.coffee
@@ -50,6 +50,7 @@ class @Issue
new Flash(issueFailMessage, 'alert')
success: (data, textStatus, jqXHR) ->
if data.saved
+ $(document).trigger('issuable:change');
if isClose
$('a.btn-close').addClass('hidden')
$('a.btn-reopen').removeClass('hidden')
diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee
index 53d72be66e3..3347ab65c90 100644
--- a/app/assets/javascripts/notes.js.coffee
+++ b/app/assets/javascripts/notes.js.coffee
@@ -64,6 +64,9 @@ class @Notes
# fetch notes when tab becomes visible
$(document).on "visibilitychange", @visibilityChange
+ # when issue status changes, we need to refresh data
+ $(document).on "issuable:change", @refresh
+
cleanBinding: ->
$(document).off "ajax:success", ".js-main-target-form"
$(document).off "ajax:success", ".js-discussion-note-form"
diff --git a/app/assets/javascripts/projects_list.js.coffee b/app/assets/javascripts/projects_list.js.coffee
index f2887af190b..b71509dbc5a 100644
--- a/app/assets/javascripts/projects_list.js.coffee
+++ b/app/assets/javascripts/projects_list.js.coffee
@@ -9,11 +9,13 @@ class @ProjectsList
$(".projects-list-filter").keyup ->
terms = $(this).val()
uiBox = $('div.projects-list-holder')
+ filterSelector = $(this).data('filter-selector') || 'span.filter-title'
+
if terms == "" || terms == undefined
uiBox.find("ul.projects-list li").show()
else
uiBox.find("ul.projects-list li").each (index) ->
- name = $(this).find("span.filter-title").text()
+ name = $(this).find(filterSelector).text()
if name.toLowerCase().search(terms.toLowerCase()) == -1
$(this).hide()
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index 4866a17005d..8d8f41287da 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -115,7 +115,7 @@
ul, ol {
padding: 0;
- margin: 6px 0 6px 18px !important;
+ margin: 6px 0 6px 28px !important;
}
li {
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 003a4c22f20..e4ea47cc4a2 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -564,3 +564,53 @@ pre.light-well {
color: #E62958;
margin-top: 2px;
}
+
+/*
+ * Forks list rendered on Project's forks page
+ */
+
+.forks-top-block {
+ padding: 16px 0;
+}
+
+.projects-search-form {
+ .dropdown-toggle.btn {
+ margin-top: -3px;
+ }
+
+ &.fork-search-form {
+ margin: 0;
+ margin-top: -$gl-padding;
+ padding-bottom: 0;
+
+ input {
+ /* Small devices (tablets, 768px and up) */
+ @media (min-width: $screen-sm-min) { width: 180px; }
+
+ /* Medium devices (desktops, 992px and up) */
+ @media (min-width: $screen-md-min) { width: 350px; }
+
+ /* Large devices (large desktops, 1200px and up) */
+ @media (min-width: $screen-lg-min) { width: 400px; }
+ }
+
+ .sort-forks {
+ width: 160px;
+ }
+
+ .fork-link {
+ float: right;
+ margin-left: $gl-padding;
+ }
+ }
+}
+
+.private-forks-notice .private-fork-icon {
+ i:nth-child(1) {
+ color: #2AA056;
+ }
+
+ i:nth-child(2) {
+ color: #FFFFFF;
+ }
+}
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 2d735b90597..824175c8a6c 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -298,7 +298,8 @@ class ApplicationController < ActionController::Base
end
def set_filters_params
- params[:sort] ||= 'id_desc'
+ set_default_sort
+
params[:scope] = 'all' if params[:scope].blank?
params[:state] = 'opened' if params[:state].blank?
@@ -405,4 +406,24 @@ class ApplicationController < ActionController::Base
current_user.nil? && root_path == request.path
end
+
+ private
+
+ def set_default_sort
+ key = if is_a_listing_page_for?('issues') || is_a_listing_page_for?('merge_requests')
+ 'issuable_sort'
+ end
+
+ cookies[key] = params[:sort] if key && params[:sort].present?
+ params[:sort] = cookies[key] if key
+ params[:sort] ||= 'id_desc'
+ end
+
+ def is_a_listing_page_for?(page_type)
+ controller_name, action_name = params.values_at(:controller, :action)
+
+ (controller_name == "projects/#{page_type}" && action_name == 'index') ||
+ (controller_name == 'groups' && action_name == page_type) ||
+ (controller_name == 'dashboard' && action_name == page_type)
+ end
end
diff --git a/app/controllers/projects/forks_controller.rb b/app/controllers/projects/forks_controller.rb
index 750181f0c19..e61e01c4a59 100644
--- a/app/controllers/projects/forks_controller.rb
+++ b/app/controllers/projects/forks_controller.rb
@@ -3,6 +3,15 @@ class Projects::ForksController < Projects::ApplicationController
before_action :require_non_empty_project
before_action :authorize_download_code!
+ def index
+ @sort = params[:sort] || 'id_desc'
+ @all_forks = project.forks.includes(:creator).order_by(@sort)
+
+ @public_forks, @protected_forks = @all_forks.partition do |project|
+ can?(current_user, :read_project, project)
+ end
+ end
+
def new
@namespaces = current_user.manageable_namespaces
@namespaces.delete(@project.namespace)
@@ -10,7 +19,7 @@ class Projects::ForksController < Projects::ApplicationController
def create
namespace = Namespace.find(params[:namespace_key])
-
+
@forked_project = namespace.projects.find_by(path: project.path)
@forked_project = nil unless @forked_project && @forked_project.forked_from_project == project
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 8b689b29a41..694c03206bd 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -36,8 +36,7 @@ module BlobHelper
notice: edit_in_new_fork_notice,
notice_now: edit_in_new_fork_notice_now
}
- fork_path = namespace_project_fork_path(project.namespace, project, namespace_key: current_user.namespace.id,
- continue: continue_params)
+ fork_path = namespace_project_forks_path(project.namespace, project, namespace_key: current_user.namespace.id, continue: continue_params)
link_to "Edit", fork_path, class: 'btn', method: :post
end
@@ -62,8 +61,7 @@ module BlobHelper
notice: edit_in_new_fork_notice + " Try to #{action} this file again.",
notice_now: edit_in_new_fork_notice_now
}
- fork_path = namespace_project_fork_path(project.namespace, project, namespace_key: current_user.namespace.id,
- continue: continue_params)
+ fork_path = namespace_project_forks_path(project.namespace, project, namespace_key: current_user.namespace.id, continue: continue_params)
link_to label, fork_path, class: "btn btn-#{btn_class}", method: :post
end
diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb
index 5724d3aabec..84c6d0883b0 100644
--- a/app/helpers/icons_helper.rb
+++ b/app/helpers/icons_helper.rb
@@ -7,7 +7,7 @@ module IconsHelper
# font-awesome-rails gem, but should we ever use a different icon pack in the
# future we won't have to change hundreds of method calls.
def icon(names, options = {})
- fa_icon(names, options)
+ options.include?(:base) ? fa_stacked_icon(names, options) : fa_icon(names, options)
end
def spinner(text = nil, visible = false)
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index f343d81a92a..8c8b355028c 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -116,7 +116,7 @@ module ProjectsHelper
private
def get_project_nav_tabs(project, current_user)
- nav_tabs = [:home]
+ nav_tabs = [:home, :forks]
if !project.empty_repo? && can?(current_user, :download_code, project)
nav_tabs << [:files, :commits, :network, :graphs]
diff --git a/app/models/group.rb b/app/models/group.rb
index 5a31b46920c..76042b3e3fd 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -19,7 +19,7 @@ require 'file_size_validator'
class Group < Namespace
include Gitlab::ConfigHelper
include Referable
-
+
has_many :group_members, dependent: :destroy, as: :source, class_name: 'GroupMember'
alias_method :members, :group_members
has_many :users, through: :group_members
diff --git a/app/views/admin/groups/index.html.haml b/app/views/admin/groups/index.html.haml
index 3940210e19b..118d3cfea07 100644
--- a/app/views/admin/groups/index.html.haml
+++ b/app/views/admin/groups/index.html.haml
@@ -17,7 +17,7 @@
.pull-right
.dropdown.inline
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
- %span.light sort:
+ %span.light
- if @sort.present?
= sort_options_hash[@sort]
- else
diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml
index b96ad6e2208..d39c0f44031 100644
--- a/app/views/admin/projects/index.html.haml
+++ b/app/views/admin/projects/index.html.haml
@@ -50,7 +50,7 @@
.controls
.dropdown.inline
%button.dropdown-toggle.btn.btn-sm{type: 'button', 'data-toggle' => 'dropdown'}
- %span.light sort:
+ %span.light
- if @sort.present?
= sort_options_hash[@sort]
- else
diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml
index b050a4d01c3..b6b1168bd37 100644
--- a/app/views/admin/users/index.html.haml
+++ b/app/views/admin/users/index.html.haml
@@ -32,7 +32,7 @@
.pull-right
.dropdown.inline
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
- %span.light sort:
+ %span.light
- if @sort.present?
= sort_options_hash[@sort]
- else
diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml
index fcb07b04083..8ffca96bb4e 100644
--- a/app/views/explore/groups/index.html.haml
+++ b/app/views/explore/groups/index.html.haml
@@ -18,7 +18,7 @@
.pull-right
.dropdown.inline
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
- %span.light sort:
+ %span.light
- if @sort.present?
= sort_options_hash[@sort]
- else
diff --git a/app/views/explore/projects/_dropdown.html.haml b/app/views/explore/projects/_dropdown.html.haml
index b23a3c1e5c1..a988d4c8154 100644
--- a/app/views/explore/projects/_dropdown.html.haml
+++ b/app/views/explore/projects/_dropdown.html.haml
@@ -1,6 +1,6 @@
.dropdown.inline
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
- %span.light sort:
+ %span.light
- if @sort.present?
= sort_options_hash[@sort]
- elsif current_page?(trending_explore_projects_path) || current_page?(explore_root_path)
@@ -24,4 +24,3 @@
= sort_title_recently_updated
= link_to explore_projects_filter_path(sort: sort_value_oldest_updated) do
= sort_title_oldest_updated
-
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index 270ccfd387f..319974e12c5 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -98,6 +98,13 @@
%span
Wiki
+ - if project_nav_tab? :forks
+ = nav_link(controller: :forks, action: :index) do
+ = link_to namespace_project_forks_path(@project.namespace, @project), title: 'Forks' do
+ = icon('code-fork fw')
+ %span
+ Forks
+
- if project_nav_tab? :snippets
= nav_link(controller: :snippets) do
= link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets' do
diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml
index 204def60794..7afea5a5049 100644
--- a/app/views/projects/branches/index.html.haml
+++ b/app/views/projects/branches/index.html.haml
@@ -10,7 +10,7 @@
&nbsp;
.dropdown.inline
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
- %span.light sort:
+ %span.light
- if @sort.present?
= @sort.humanize
- else
diff --git a/app/views/projects/buttons/_dropdown.html.haml b/app/views/projects/buttons/_dropdown.html.haml
index 511863d774e..e7c85edff96 100644
--- a/app/views/projects/buttons/_dropdown.html.haml
+++ b/app/views/projects/buttons/_dropdown.html.haml
@@ -46,7 +46,7 @@
- continue_params = { to: namespace_project_new_blob_path(@project.namespace, @project, @project.default_branch || 'master'),
notice: edit_in_new_fork_notice,
notice_now: edit_in_new_fork_notice_now }
- - fork_path = namespace_project_fork_path(@project.namespace, @project, namespace_key: current_user.namespace.id,
+ - fork_path = namespace_project_forks_path(@project.namespace, @project, namespace_key: current_user.namespace.id,
continue: continue_params)
= link_to fork_path, method: :post do
= icon('file fw')
diff --git a/app/views/projects/forks/index.html.haml b/app/views/projects/forks/index.html.haml
new file mode 100644
index 00000000000..a362185210a
--- /dev/null
+++ b/app/views/projects/forks/index.html.haml
@@ -0,0 +1,58 @@
+.gray-content-block.top-block.clearfix.white.forks-top-block
+ .pull-left
+ - public_count = @public_forks.size
+ - protected_count = @protected_forks.size
+ - full_count_title = "#{public_count} public and #{protected_count} private"
+ == #{pluralize(@all_forks.size, 'fork')}: #{full_count_title}
+
+ .pull-right
+ .projects-search-form.fork-search-form
+ = search_field_tag :filter_projects, nil, placeholder: 'Search forks', class: 'projects-list-filter form-control',
+ spellcheck: false, data: { 'filter-selector' => 'span.namespace-name' }
+
+ .dropdown.inline.prepend-left-10
+ %button.dropdown-toggle.btn.sort-forks{type: 'button', 'data-toggle' => 'dropdown'}
+ %span.light sort:
+ - if @sort.present?
+ = sort_options_hash[@sort]
+ - else
+ = sort_title_recently_created
+ %b.caret
+ %ul.dropdown-menu.dropdown-menu-align-right
+ %li
+ - excluded_filters = [:state, :scope, :label_name, :milestone_id, :assignee_id, :author_id]
+ = link_to page_filter_path(sort: sort_value_recently_created, without: excluded_filters) do
+ = sort_title_recently_created
+ = link_to page_filter_path(sort: sort_value_oldest_created, without: excluded_filters) do
+ = sort_title_oldest_created
+ = link_to page_filter_path(sort: sort_value_recently_updated, without: excluded_filters) do
+ = sort_title_recently_updated
+ = link_to page_filter_path(sort: sort_value_oldest_updated, without: excluded_filters) do
+ = sort_title_oldest_updated
+
+ .fork-link.inline
+ - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
+ = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'pull-right btn btn-new' do
+ = icon('code-fork fw')
+ Fork
+ - else
+ = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'pull-right btn btn-new' do
+ = icon('code-fork fw')
+ Fork
+
+
+.projects-list-holder
+ - if @public_forks.blank?
+ %ul.content-list
+ %li
+ .nothing-here-block No forks to show
+ - else
+ = render 'shared/projects/list', projects: @public_forks, use_creator_avatar: true,
+ forks: true, show_last_commit_as_description: true
+
+ - if protected_count > 0
+ %ul.projects-list.private-forks-notice
+ %li.project-row
+ = icon('lock fw', base: 'circle', class: 'fa-lg private-fork-icon')
+ %strong= pluralize(protected_count, 'private fork')
+ %span you have no access to.
diff --git a/app/views/projects/forks/new.html.haml b/app/views/projects/forks/new.html.haml
index 8a2c027a455..edabc2d3b44 100644
--- a/app/views/projects/forks/new.html.haml
+++ b/app/views/projects/forks/new.html.haml
@@ -22,7 +22,7 @@
- else
.fork-thumbnail
- = link_to namespace_project_fork_path(@project.namespace, @project, namespace_key: namespace.id), title: "Fork here", method: "POST", class: 'has_tooltip' do
+ = link_to namespace_project_forks_path(@project.namespace, @project, namespace_key: namespace.id), title: "Fork here", method: "POST", class: 'has_tooltip' do
= image_tag namespace_icon(namespace, 100)
.caption
%strong
diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml
index 3343288ad2b..3eb626e6dca 100644
--- a/app/views/projects/tree/_tree_header.html.haml
+++ b/app/views/projects/tree/_tree_header.html.haml
@@ -40,7 +40,7 @@
- continue_params = { to: namespace_project_new_blob_path(@project.namespace, @project, @id),
notice: edit_in_new_fork_notice,
notice_now: edit_in_new_fork_notice_now }
- - fork_path = namespace_project_fork_path(@project.namespace, @project, namespace_key: current_user.namespace.id,
+ - fork_path = namespace_project_forks_path(@project.namespace, @project, namespace_key: current_user.namespace.id,
continue: continue_params)
= link_to fork_path, method: :post do
= icon('pencil fw')
@@ -49,7 +49,7 @@
- continue_params = { to: request.fullpath,
notice: edit_in_new_fork_notice + " Try to upload a file again.",
notice_now: edit_in_new_fork_notice_now }
- - fork_path = namespace_project_fork_path(@project.namespace, @project, namespace_key: current_user.namespace.id,
+ - fork_path = namespace_project_forks_path(@project.namespace, @project, namespace_key: current_user.namespace.id,
continue: continue_params)
= link_to fork_path, method: :post do
= icon('file fw')
@@ -58,7 +58,7 @@
- continue_params = { to: request.fullpath,
notice: edit_in_new_fork_notice + " Try to create a new directory again.",
notice_now: edit_in_new_fork_notice_now }
- - fork_path = namespace_project_fork_path(@project.namespace, @project, namespace_key: current_user.namespace.id,
+ - fork_path = namespace_project_forks_path(@project.namespace, @project, namespace_key: current_user.namespace.id,
continue: continue_params)
= link_to fork_path, method: :post do
= icon('folder fw')
diff --git a/app/views/shared/_sort_dropdown.html.haml b/app/views/shared/_sort_dropdown.html.haml
index af3d35de325..f09ab25276d 100644
--- a/app/views/shared/_sort_dropdown.html.haml
+++ b/app/views/shared/_sort_dropdown.html.haml
@@ -1,6 +1,6 @@
.dropdown.inline.prepend-left-10
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
- %span.light sort:
+ %span.light
- if @sort.present?
= sort_options_hash[@sort]
- else
diff --git a/app/views/shared/projects/_list.html.haml b/app/views/shared/projects/_list.html.haml
index e5ffe1e29ae..b3f45373f6b 100644
--- a/app/views/shared/projects/_list.html.haml
+++ b/app/views/shared/projects/_list.html.haml
@@ -1,14 +1,18 @@
- projects_limit = 20 unless local_assigns[:projects_limit]
- avatar = true unless local_assigns[:avatar] == false
+- use_creator_avatar = false unless local_assigns[:use_creator_avatar] == true
- stars = true unless local_assigns[:stars] == false
+- forks = false unless local_assigns[:forks] == true
- ci = false unless local_assigns[:ci] == true
- skip_namespace = false unless local_assigns[:skip_namespace] == true
+- show_last_commit_as_description = false unless local_assigns[:show_last_commit_as_description] == true
%ul.projects-list
- projects.each_with_index do |project, i|
- css_class = (i >= projects_limit) ? 'hide' : nil
= render "shared/projects/project", project: project, skip_namespace: skip_namespace,
- avatar: avatar, stars: stars, css_class: css_class, ci: ci
+ avatar: avatar, stars: stars, css_class: css_class, ci: ci, use_creator_avatar: use_creator_avatar,
+ forks: forks, show_last_commit_as_description: show_last_commit_as_description
- if projects.size > projects_limit
%li.bottom.center
diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml
index 5db8056b77c..2aeeed63c95 100644
--- a/app/views/shared/projects/_project.html.haml
+++ b/app/views/shared/projects/_project.html.haml
@@ -1,9 +1,11 @@
- avatar = true unless local_assigns[:avatar] == false
- stars = true unless local_assigns[:stars] == false
+- forks = false unless local_assigns[:forks] == true
- ci = false unless local_assigns[:ci] == true
- skip_namespace = false unless local_assigns[:skip_namespace] == true
- css_class = '' unless local_assigns[:css_class]
-- css_class += " no-description" unless project.description.present?
+- show_last_commit_as_description = false unless local_assigns[:show_last_commit_as_description] == true
+- css_class += " no-description" if project.description.blank? && !show_last_commit_as_description
- ci_commit = project.ci_commit(project.commit.sha) if ci && !project.empty_repo? && project.commit
- cache_key = [project.namespace, project, controller.controller_name, controller.action_name, current_application_settings, 'v2.2']
- cache_key.push(ci_commit.status) if ci_commit
@@ -13,7 +15,10 @@
= link_to project_path(project), class: dom_class(project) do
- if avatar
.dash-project-avatar
- = project_icon(project, alt: '', class: 'avatar project-avatar s46')
+ - if use_creator_avatar
+ = image_tag avatar_icon(project.creator.email, 46), class: "avatar s46", alt:''
+ - else
+ = project_icon(project, alt: '', class: 'avatar project-avatar s46')
%span.project-full-name
%span.namespace-name
- if project.namespace && !skip_namespace
@@ -26,10 +31,18 @@
- if ci_commit
= render_ci_status(ci_commit)
&nbsp;
+ - if forks
+ %span
+ = icon('code-fork')
+ = project.forks_count
- if stars
%span
- %i.fa.fa-star
+ = icon('star')
= project.star_count
- - if project.description.present?
+ - if show_last_commit_as_description
+ .project-description
+ = link_to_gfm project.commit.title, namespace_project_commit_path(project.namespace, project, project.commit),
+ class: "commit-row-message"
+ - elsif project.description.present?
.project-description
= markdown(project.description, pipeline: :description)
diff --git a/config/routes.rb b/config/routes.rb
index 75418db8d25..fdfdb449085 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -554,7 +554,7 @@ Rails.application.routes.draw do
end
end
- resource :fork, only: [:new, :create]
+ resources :forks, only: [:index, :new, :create]
resource :import, only: [:new, :create, :show]
resources :refs, only: [] do
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index 8bc0a67067a..85ed31320b9 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -60,7 +60,7 @@ Parameters:
Shows information about a single merge request.
```
-GET /projects/:id/merge_request/:merge_request_id
+GET /projects/:id/merge_requests/:merge_request_id
```
Parameters:
@@ -105,7 +105,7 @@ Parameters:
Get a list of merge request commits.
```
-GET /projects/:id/merge_request/:merge_request_id/commits
+GET /projects/:id/merge_requests/:merge_request_id/commits
```
Parameters:
@@ -142,7 +142,7 @@ Parameters:
Shows information about the merge request including its files and changes.
```
-GET /projects/:id/merge_request/:merge_request_id/changes
+GET /projects/:id/merge_requests/:merge_request_id/changes
```
Parameters:
@@ -264,7 +264,7 @@ If an error occurs, an error number and a message explaining the reason is retur
Updates an existing merge request. You can change the target branch, title, or even close the MR.
```
-PUT /projects/:id/merge_request/:merge_request_id
+PUT /projects/:id/merge_requests/:merge_request_id
```
Parameters:
@@ -323,7 +323,7 @@ If merge request is already merged or closed - you get 405 and error message 'Me
If you don't have permissions to accept this merge request - you'll get a 401
```
-PUT /projects/:id/merge_request/:merge_request_id/merge
+PUT /projects/:id/merge_requests/:merge_request_id/merge
```
Parameters:
@@ -373,7 +373,7 @@ If the merge request is already merged or closed - you get 405 and error message
In case the merge request is not set to be merged when the build succeeds, you'll also get a 406 error.
```
-PUT /projects/:id/merge_request/:merge_request_id/cancel_merge_when_build_succeeds
+PUT /projects/:id/merge_requests/:merge_request_id/cancel_merge_when_build_succeeds
```
Parameters:
@@ -409,66 +409,6 @@ Parameters:
}
```
-## Post comment to MR
-
-Adds a comment to a merge request.
-
-```
-POST /projects/:id/merge_request/:merge_request_id/comments
-```
-
-Parameters:
-
-- `id` (required) - The ID of a project
-- `merge_request_id` (required) - ID of merge request
-- `note` (required) - Text of comment
-
-```json
-{
- "note": "text1"
-}
-```
-
-## Get the comments on a MR
-
-Gets all the comments associated with a merge request.
-
-```
-GET /projects/:id/merge_request/:merge_request_id/comments
-```
-
-Parameters:
-
-- `id` (required) - The ID of a project
-- `merge_request_id` (required) - ID of merge request
-
-```json
-[
- {
- "note": "this is the 1st comment on the 2merge merge request",
- "author": {
- "id": 11,
- "username": "admin",
- "email": "admin@example.com",
- "name": "Administrator",
- "state": "active",
- "created_at": "2014-03-06T08:17:35.000Z"
- }
- },
- {
- "note": "Status changed to closed",
- "author": {
- "id": 11,
- "username": "admin",
- "email": "admin@example.com",
- "name": "Administrator",
- "state": "active",
- "created_at": "2014-03-06T08:17:35.000Z"
- }
- }
-]
-```
-
## Comments on merge requets
-Comments are done via the notes resource.
+Comments are done via the [notes](notes.md) resource.
diff --git a/features/dashboard/dashboard.feature b/features/dashboard/dashboard.feature
index b667b587c5b..c3b3577c449 100644
--- a/features/dashboard/dashboard.feature
+++ b/features/dashboard/dashboard.feature
@@ -41,3 +41,33 @@ Feature: Dashboard
And user with name "John Doe" left project "Shop"
When I visit dashboard activity page
Then I should see "John Doe left project Shop" event
+
+ @javascript
+ Scenario: Sorting Issues
+ Given I visit dashboard issues page
+ And I sort the list by "Oldest updated"
+ And I visit dashboard activity page
+ And I visit dashboard issues page
+ Then The list should be sorted by "Oldest updated"
+
+ @javascript
+ Scenario: Visiting Project's issues after sorting
+ Given I visit dashboard issues page
+ And I sort the list by "Oldest updated"
+ And I visit project "Shop" issues page
+ Then The list should be sorted by "Oldest updated"
+
+ @javascript
+ Scenario: Sorting Merge Requests
+ Given I visit dashboard merge requests page
+ And I sort the list by "Oldest updated"
+ And I visit dashboard activity page
+ And I visit dashboard merge requests page
+ Then The list should be sorted by "Oldest updated"
+
+ @javascript
+ Scenario: Visiting Project's merge requests after sorting
+ Given I visit dashboard merge requests page
+ And I sort the list by "Oldest updated"
+ And I visit project "Shop" merge requests page
+ Then The list should be sorted by "Oldest updated"
diff --git a/features/project/fork.feature b/features/project/fork.feature
index 37cd53ee977..12695204e47 100644
--- a/features/project/fork.feature
+++ b/features/project/fork.feature
@@ -25,3 +25,18 @@ Feature: Project Fork
Then I should see "New merge request"
And I click link "New merge request"
Then I should see the new merge request page for my namespace
+
+ Scenario: Viewing forks of a Project
+ Given I click link "Fork"
+ When I fork to my namespace
+ And I visit the forks page of the "Shop" project
+ Then I should see my fork on the list
+
+ Scenario: Viewing private forks of a Project
+ Given There is an existent fork of the "Shop" project
+ And I click link "Fork"
+ When I fork to my namespace
+ And I visit the forks page of the "Shop" project
+ Then I should see my fork on the list
+ And I should not see the other fork listed
+ And I should see a private fork notice
diff --git a/features/project/issues/issues.feature b/features/project/issues/issues.feature
index 1502b0952cd..0b3d03aa2a5 100644
--- a/features/project/issues/issues.feature
+++ b/features/project/issues/issues.feature
@@ -60,6 +60,28 @@ Feature: Project Issues
Then I should see "Release 0.4" at the top
@javascript
+ Scenario: Visiting Issues after being sorted the list
+ Given I visit project "Shop" issues page
+ And I sort the list by "Oldest updated"
+ And I visit my project's home page
+ And I visit project "Shop" issues page
+ Then The list should be sorted by "Oldest updated"
+
+ @javascript
+ Scenario: Visiting Merge Requests after being sorted the list
+ Given I visit project "Shop" issues page
+ And I sort the list by "Oldest updated"
+ And I visit project "Shop" merge requests page
+ Then The list should be sorted by "Oldest updated"
+
+ @javascript
+ Scenario: Visiting Merge Requests from a differente Project after sorting
+ Given I visit project "Shop" merge requests page
+ And I sort the list by "Oldest updated"
+ And I visit dashboard merge requests page
+ Then The list should be sorted by "Oldest updated"
+
+ @javascript
Scenario: I search issue
Given I fill in issue search with "Re"
Then I should see "Release 0.4" in issues
diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature
index 4f780aa680f..ca1ee6b3c2b 100644
--- a/features/project/merge_requests.feature
+++ b/features/project/merge_requests.feature
@@ -85,6 +85,28 @@ Feature: Project Merge Requests
Then I should see "Bug NS-04" at the top
@javascript
+ Scenario: Visiting Merge Requests after being sorted the list
+ Given I visit project "Shop" merge requests page
+ And I sort the list by "Oldest updated"
+ And I visit my project's home page
+ And I visit project "Shop" merge requests page
+ Then The list should be sorted by "Oldest updated"
+
+ @javascript
+ Scenario: Visiting Issues after being sorted the list
+ Given I visit project "Shop" merge requests page
+ And I sort the list by "Oldest updated"
+ And I visit project "Shop" issues page
+ Then The list should be sorted by "Oldest updated"
+
+ @javascript
+ Scenario: Visiting Merge Requests from a differente Project after sorting
+ Given I visit project "Shop" merge requests page
+ And I sort the list by "Oldest updated"
+ And I visit dashboard merge requests page
+ Then The list should be sorted by "Oldest updated"
+
+ @javascript
Scenario: Visiting Merge Requests after commenting on diffs
Given project "Shop" have "Bug NS-05" open merge request with diffs inside
And I visit merge request page "Bug NS-05"
diff --git a/features/steps/dashboard/dashboard.rb b/features/steps/dashboard/dashboard.rb
index 63f0ec2b6e8..5062e348844 100644
--- a/features/steps/dashboard/dashboard.rb
+++ b/features/steps/dashboard/dashboard.rb
@@ -2,6 +2,7 @@ class Spinach::Features::Dashboard < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
include SharedProject
+ include SharedIssuable
step 'I should see "New Project" link' do
expect(page).to have_link "New project"
diff --git a/features/steps/project/fork.rb b/features/steps/project/fork.rb
index e98bd51ca89..5810276ced3 100644
--- a/features/steps/project/fork.rb
+++ b/features/steps/project/fork.rb
@@ -49,4 +49,29 @@ class Spinach::Features::ProjectFork < Spinach::FeatureSteps
step 'I should see the new merge request page for my namespace' do
current_path.should have_content(/#{current_user.namespace.name}/i)
end
+
+ step 'I visit the forks page of the "Shop" project' do
+ @project = Project.where(name: 'Shop').last
+ visit namespace_project_forks_path(@project.namespace, @project)
+ end
+
+ step 'I should see my fork on the list' do
+ page.within('.projects-list-holder') do
+ project = @user.fork_of(@project)
+ expect(page).to have_content("#{project.namespace.human_name} / #{project.name}")
+ end
+ end
+
+ step 'There is an existent fork of the "Shop" project' do
+ user = create(:user, name: 'Mike')
+ @forked_project = Projects::ForkService.new(@project, user).execute
+ end
+
+ step 'I should not see the other fork listed' do
+ expect(page).not_to have_content("#{@forked_project.namespace.human_name} / #{@forked_project.name}")
+ end
+
+ step 'I should see a private fork notice' do
+ expect(page).to have_content("1 private fork")
+ end
end
diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb
index cbdce78dc0c..7e4425ff662 100644
--- a/features/steps/project/forked_merge_requests.rb
+++ b/features/steps/project/forked_merge_requests.rb
@@ -43,7 +43,9 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
expect(page).to have_css("h3.page-title", text: "New Merge Request")
- fill_in "merge_request_title", with: "Merge Request On Forked Project"
+ page.within 'form#new_merge_request' do
+ fill_in "merge_request_title", with: "Merge Request On Forked Project"
+ end
end
step 'I submit the merge request' do
diff --git a/features/steps/shared/issuable.rb b/features/steps/shared/issuable.rb
index 4c5f7488efb..25c2b476f43 100644
--- a/features/steps/shared/issuable.rb
+++ b/features/steps/shared/issuable.rb
@@ -106,6 +106,19 @@ module SharedIssuable
edit_issuable
end
+ step 'I sort the list by "Oldest updated"' do
+ find('button.dropdown-toggle.btn').click
+ page.within('ul.dropdown-menu.dropdown-menu-align-right li') do
+ click_link "Oldest updated"
+ end
+ end
+
+ step 'The list should be sorted by "Oldest updated"' do
+ page.within('div.dropdown.inline.prepend-left-10') do
+ expect(page.find('button.dropdown-toggle.btn')).to have_content('Oldest updated')
+ end
+ end
+
def create_issuable_for_project(project_name:, title:, type: :issue)
project = Project.find_by(name: project_name)
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 5c97fe1c88c..dd7f24f3279 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -59,55 +59,6 @@ module API
present paginate(merge_requests), with: Entities::MergeRequest
end
- # Show MR
- #
- # Parameters:
- # id (required) - The ID of a project
- # merge_request_id (required) - The ID of MR
- #
- # Example:
- # GET /projects/:id/merge_request/:merge_request_id
- #
- get ":id/merge_request/:merge_request_id" do
- merge_request = user_project.merge_requests.find(params[:merge_request_id])
-
- authorize! :read_merge_request, merge_request
-
- present merge_request, with: Entities::MergeRequest
- end
-
- # Show MR commits
- #
- # Parameters:
- # id (required) - The ID of a project
- # merge_request_id (required) - The ID of MR
- #
- # Example:
- # GET /projects/:id/merge_request/:merge_request_id/commits
- #
- get ':id/merge_request/:merge_request_id/commits' do
- merge_request = user_project.merge_requests.
- find(params[:merge_request_id])
- authorize! :read_merge_request, merge_request
- present merge_request.commits, with: Entities::RepoCommit
- end
-
- # Show MR changes
- #
- # Parameters:
- # id (required) - The ID of a project
- # merge_request_id (required) - The ID of MR
- #
- # Example:
- # GET /projects/:id/merge_request/:merge_request_id/changes
- #
- get ':id/merge_request/:merge_request_id/changes' do
- merge_request = user_project.merge_requests.
- find(params[:merge_request_id])
- authorize! :read_merge_request, merge_request
- present merge_request, with: Entities::MergeRequestChanges
- end
-
# Create MR
#
# Parameters:
@@ -148,146 +99,206 @@ module API
end
end
- # Update MR
+ # Routing "merge_request/:merge_request_id/..." is DEPRECATED and WILL BE REMOVED in version 9.0
+ # Use "merge_requests/:merge_request_id/..." instead.
#
- # Parameters:
- # id (required) - The ID of a project
- # merge_request_id (required) - ID of MR
- # target_branch - The target branch
- # assignee_id - Assignee user ID
- # title - Title of MR
- # state_event - Status of MR. (close|reopen|merge)
- # description - Description of MR
- # labels (optional) - Labels for a MR as a comma-separated list
- # Example:
- # PUT /projects/:id/merge_request/:merge_request_id
- #
- put ":id/merge_request/:merge_request_id" do
- attrs = attributes_for_keys [:target_branch, :assignee_id, :title, :state_event, :description]
- merge_request = user_project.merge_requests.find(params[:merge_request_id])
- authorize! :update_merge_request, merge_request
-
- # Ensure source_branch is not specified
- if params[:source_branch].present?
- render_api_error!('Source branch cannot be changed', 400)
- end
-
- # Validate label names in advance
- if (errors = validate_label_params(params)).any?
- render_api_error!({ labels: errors }, 400)
- end
-
- merge_request = ::MergeRequests::UpdateService.new(user_project, current_user, attrs).execute(merge_request)
-
- if merge_request.valid?
- # Find or create labels and attach to issue
- unless params[:labels].nil?
- merge_request.remove_labels
- merge_request.add_labels_by_names(params[:labels].split(","))
- end
+ [":id/merge_request/:merge_request_id", ":id/merge_requests/:merge_request_id"].each do |path|
+ # Show MR
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # merge_request_id (required) - The ID of MR
+ #
+ # Example:
+ # GET /projects/:id/merge_requests/:merge_request_id
+ #
+ get path do
+ merge_request = user_project.merge_requests.find(params[:merge_request_id])
+
+ authorize! :read_merge_request, merge_request
present merge_request, with: Entities::MergeRequest
- else
- handle_merge_request_errors! merge_request.errors
end
- end
-
- # Merge MR
- #
- # Parameters:
- # id (required) - The ID of a project
- # merge_request_id (required) - ID of MR
- # merge_commit_message (optional) - Custom merge commit message
- # should_remove_source_branch (optional) - When true, the source branch will be deleted if possible
- # merge_when_build_succeeds (optional) - When true, this MR will be merged when the build succeeds
- # Example:
- # PUT /projects/:id/merge_request/:merge_request_id/merge
- #
- put ":id/merge_request/:merge_request_id/merge" do
- merge_request = user_project.merge_requests.find(params[:merge_request_id])
-
- # Merge request can not be merged
- # because user dont have permissions to push into target branch
- unauthorized! unless merge_request.can_be_merged_by?(current_user)
- not_allowed! if !merge_request.open? || merge_request.work_in_progress?
- merge_request.check_if_can_be_merged
-
- render_api_error!('Branch cannot be merged', 406) unless merge_request.can_be_merged?
-
- merge_params = {
- commit_message: params[:merge_commit_message],
- should_remove_source_branch: params[:should_remove_source_branch]
- }
-
- if parse_boolean(params[:merge_when_build_succeeds]) && merge_request.ci_commit && merge_request.ci_commit.active?
- ::MergeRequests::MergeWhenBuildSucceedsService.new(merge_request.target_project, current_user, merge_params).
- execute(merge_request)
- else
- ::MergeRequests::MergeService.new(merge_request.target_project, current_user, merge_params).
- execute(merge_request)
+ # Show MR commits
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # merge_request_id (required) - The ID of MR
+ #
+ # Example:
+ # GET /projects/:id/merge_requests/:merge_request_id/commits
+ #
+ get "#{path}/commits" do
+ merge_request = user_project.merge_requests.
+ find(params[:merge_request_id])
+ authorize! :read_merge_request, merge_request
+ present merge_request.commits, with: Entities::RepoCommit
end
- present merge_request, with: Entities::MergeRequest
- end
+ # Show MR changes
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # merge_request_id (required) - The ID of MR
+ #
+ # Example:
+ # GET /projects/:id/merge_requests/:merge_request_id/changes
+ #
+ get "#{path}/changes" do
+ merge_request = user_project.merge_requests.
+ find(params[:merge_request_id])
+ authorize! :read_merge_request, merge_request
+ present merge_request, with: Entities::MergeRequestChanges
+ end
- # Cancel Merge if Merge When build succeeds is enabled
- # Parameters:
- # id (required) - The ID of a project
- # merge_request_id (required) - ID of MR
- #
- post ":id/merge_request/:merge_request_id/cancel_merge_when_build_succeeds" do
- merge_request = user_project.merge_requests.find(params[:merge_request_id])
+ # Update MR
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # merge_request_id (required) - ID of MR
+ # target_branch - The target branch
+ # assignee_id - Assignee user ID
+ # title - Title of MR
+ # state_event - Status of MR. (close|reopen|merge)
+ # description - Description of MR
+ # labels (optional) - Labels for a MR as a comma-separated list
+ # Example:
+ # PUT /projects/:id/merge_requests/:merge_request_id
+ #
+ put path do
+ attrs = attributes_for_keys [:target_branch, :assignee_id, :title, :state_event, :description]
+ merge_request = user_project.merge_requests.find(params[:merge_request_id])
+ authorize! :update_merge_request, merge_request
+
+ # Ensure source_branch is not specified
+ if params[:source_branch].present?
+ render_api_error!('Source branch cannot be changed', 400)
+ end
- unauthorized! unless merge_request.can_cancel_merge_when_build_succeeds?(current_user)
+ # Validate label names in advance
+ if (errors = validate_label_params(params)).any?
+ render_api_error!({ labels: errors }, 400)
+ end
- ::MergeRequest::MergeWhenBuildSucceedsService.new(merge_request.target_project, current_user).cancel(merge_request)
- end
+ merge_request = ::MergeRequests::UpdateService.new(user_project, current_user, attrs).execute(merge_request)
- # Get a merge request's comments
- #
- # Parameters:
- # id (required) - The ID of a project
- # merge_request_id (required) - ID of MR
- # Examples:
- # GET /projects/:id/merge_request/:merge_request_id/comments
- #
- get ":id/merge_request/:merge_request_id/comments" do
- merge_request = user_project.merge_requests.find(params[:merge_request_id])
+ if merge_request.valid?
+ # Find or create labels and attach to issue
+ unless params[:labels].nil?
+ merge_request.remove_labels
+ merge_request.add_labels_by_names(params[:labels].split(","))
+ end
- authorize! :read_merge_request, merge_request
+ present merge_request, with: Entities::MergeRequest
+ else
+ handle_merge_request_errors! merge_request.errors
+ end
+ end
- present paginate(merge_request.notes.fresh), with: Entities::MRNote
- end
+ # Merge MR
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # merge_request_id (required) - ID of MR
+ # merge_commit_message (optional) - Custom merge commit message
+ # should_remove_source_branch (optional) - When true, the source branch will be deleted if possible
+ # merge_when_build_succeeds (optional) - When true, this MR will be merged when the build succeeds
+ # Example:
+ # PUT /projects/:id/merge_requests/:merge_request_id/merge
+ #
+ put "#{path}/merge" do
+ merge_request = user_project.merge_requests.find(params[:merge_request_id])
+
+ # Merge request can not be merged
+ # because user dont have permissions to push into target branch
+ unauthorized! unless merge_request.can_be_merged_by?(current_user)
+ not_allowed! if !merge_request.open? || merge_request.work_in_progress?
+
+ merge_request.check_if_can_be_merged
+
+ render_api_error!('Branch cannot be merged', 406) unless merge_request.can_be_merged?
+
+ merge_params = {
+ commit_message: params[:merge_commit_message],
+ should_remove_source_branch: params[:should_remove_source_branch]
+ }
+
+ if parse_boolean(params[:merge_when_build_succeeds]) && merge_request.ci_commit && merge_request.ci_commit.active?
+ ::MergeRequests::MergeWhenBuildSucceedsService.new(merge_request.target_project, current_user, merge_params).
+ execute(merge_request)
+ else
+ ::MergeRequests::MergeService.new(merge_request.target_project, current_user, merge_params).
+ execute(merge_request)
+ end
- # Post comment to merge request
- #
- # Parameters:
- # id (required) - The ID of a project
- # merge_request_id (required) - ID of MR
- # note (required) - Text of comment
- # Examples:
- # POST /projects/:id/merge_request/:merge_request_id/comments
- #
- post ":id/merge_request/:merge_request_id/comments" do
- required_attributes! [:note]
+ present merge_request, with: Entities::MergeRequest
+ end
- merge_request = user_project.merge_requests.find(params[:merge_request_id])
+ # Cancel Merge if Merge When build succeeds is enabled
+ # Parameters:
+ # id (required) - The ID of a project
+ # merge_request_id (required) - ID of MR
+ #
+ post "#{path}/cancel_merge_when_build_succeeds" do
+ merge_request = user_project.merge_requests.find(params[:merge_request_id])
- authorize! :create_note, merge_request
+ unauthorized! unless merge_request.can_cancel_merge_when_build_succeeds?(current_user)
- opts = {
- note: params[:note],
- noteable_type: 'MergeRequest',
- noteable_id: merge_request.id
- }
+ ::MergeRequest::MergeWhenBuildSucceedsService.new(merge_request.target_project, current_user).cancel(merge_request)
+ end
- note = ::Notes::CreateService.new(user_project, current_user, opts).execute
+ # Duplicate. DEPRECATED and WILL BE REMOVED in 9.0.
+ # Use GET "/projects/:id/merge_requests/:merge_request_id/notes" instead
+ #
+ # Get a merge request's comments
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # merge_request_id (required) - ID of MR
+ # Examples:
+ # GET /projects/:id/merge_requests/:merge_request_id/comments
+ #
+ get "#{path}/comments" do
+ merge_request = user_project.merge_requests.find(params[:merge_request_id])
+
+ authorize! :read_merge_request, merge_request
+
+ present paginate(merge_request.notes.fresh), with: Entities::MRNote
+ end
- if note.save
- present note, with: Entities::MRNote
- else
- render_api_error!("Failed to save note #{note.errors.messages}", 400)
+ # Duplicate. DEPRECATED and WILL BE REMOVED in 9.0.
+ # Use POST "/projects/:id/merge_requests/:merge_request_id/notes" instead
+ #
+ # Post comment to merge request
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # merge_request_id (required) - ID of MR
+ # note (required) - Text of comment
+ # Examples:
+ # POST /projects/:id/merge_requests/:merge_request_id/comments
+ #
+ post "#{path}/comments" do
+ required_attributes! [:note]
+
+ merge_request = user_project.merge_requests.find(params[:merge_request_id])
+
+ authorize! :create_note, merge_request
+
+ opts = {
+ note: params[:note],
+ noteable_type: 'MergeRequest',
+ noteable_id: merge_request.id
+ }
+
+ note = ::Notes::CreateService.new(user_project, current_user, opts).execute
+
+ if note.save
+ present note, with: Entities::MRNote
+ else
+ render_api_error!("Failed to save note #{note.errors.messages}", 400)
+ end
end
end
end
diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb
index ea054255820..a6b2f14521c 100644
--- a/lib/gitlab/current_settings.rb
+++ b/lib/gitlab/current_settings.rb
@@ -4,11 +4,14 @@ module Gitlab
key = :current_application_settings
RequestStore.store[key] ||= begin
+ settings = nil
+
if connect_to_db?
- ApplicationSetting.current || ApplicationSetting.create_from_defaults
- else
- fake_application_settings
+ settings = ApplicationSetting.current
+ settings ||= ApplicationSetting.create_from_defaults unless ActiveRecord::Migrator.needs_migration?
end
+
+ settings || fake_application_settings
end
end
@@ -18,28 +21,32 @@ module Gitlab
default_branch_protection: Settings.gitlab['default_branch_protection'],
signup_enabled: Settings.gitlab['signup_enabled'],
signin_enabled: Settings.gitlab['signin_enabled'],
+ twitter_sharing_enabled: Settings.gitlab['twitter_sharing_enabled'],
gravatar_enabled: Settings.gravatar['enabled'],
sign_in_text: Settings.extra['sign_in_text'],
restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
max_attachment_size: Settings.gitlab['max_attachment_size'],
session_expire_delay: Settings.gitlab['session_expire_delay'],
- import_sources: Settings.gitlab['import_sources'],
+ default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
+ default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
+ restricted_signup_domains: Settings.gitlab['restricted_signup_domains'],
+ import_sources: ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git'],
shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
max_artifacts_size: Settings.artifacts['max_size'],
+ require_two_factor_authentication: false,
+ two_factor_grace_period: 48
)
end
private
def connect_to_db?
- use_db = if ENV['USE_DB'] == "false"
- false
- else
- true
- end
-
- use_db && ActiveRecord::Base.connection.active? &&
- ActiveRecord::Base.connection.table_exists?('application_settings')
+ # When the DBMS is not available, an exception (e.g. PG::ConnectionBad) is raised
+ active_db_connection = ActiveRecord::Base.connection.active? rescue false
+
+ ENV['USE_DB'] != 'false' &&
+ active_db_connection &&
+ ActiveRecord::Base.connection.table_exists?('application_settings')
rescue ActiveRecord::NoDatabaseError
false
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index e194eb93cf4..d7bfa17b0b1 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -109,9 +109,9 @@ describe API::API, api: true do
end
end
- describe "GET /projects/:id/merge_request/:merge_request_id" do
+ describe "GET /projects/:id/merge_requests/:merge_request_id" do
it "should return merge_request" do
- get api("/projects/#{project.id}/merge_request/#{merge_request.id}", user)
+ get api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user)
expect(response.status).to eq(200)
expect(json_response['title']).to eq(merge_request.title)
expect(json_response['iid']).to eq(merge_request.iid)
@@ -126,14 +126,14 @@ describe API::API, api: true do
end
it "should return a 404 error if merge_request_id not found" do
- get api("/projects/#{project.id}/merge_request/999", user)
+ get api("/projects/#{project.id}/merge_requests/999", user)
expect(response.status).to eq(404)
end
end
- describe 'GET /projects/:id/merge_request/:merge_request_id/commits' do
+ describe 'GET /projects/:id/merge_requests/:merge_request_id/commits' do
context 'valid merge request' do
- before { get api("/projects/#{project.id}/merge_request/#{merge_request.id}/commits", user) }
+ before { get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/commits", user) }
let(:commit) { merge_request.commits.first }
it { expect(response.status).to eq 200 }
@@ -143,20 +143,20 @@ describe API::API, api: true do
end
it 'returns a 404 when merge_request_id not found' do
- get api("/projects/#{project.id}/merge_request/999/commits", user)
+ get api("/projects/#{project.id}/merge_requests/999/commits", user)
expect(response.status).to eq(404)
end
end
- describe 'GET /projects/:id/merge_request/:merge_request_id/changes' do
+ describe 'GET /projects/:id/merge_requests/:merge_request_id/changes' do
it 'should return the change information of the merge_request' do
- get api("/projects/#{project.id}/merge_request/#{merge_request.id}/changes", user)
+ get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/changes", user)
expect(response.status).to eq 200
expect(json_response['changes'].size).to eq(merge_request.diffs.size)
end
it 'returns a 404 when merge_request_id not found' do
- get api("/projects/#{project.id}/merge_request/999/changes", user)
+ get api("/projects/#{project.id}/merge_requests/999/changes", user)
expect(response.status).to eq(404)
end
end
@@ -311,19 +311,19 @@ describe API::API, api: true do
end
end
- describe "PUT /projects/:id/merge_request/:merge_request_id to close MR" do
+ describe "PUT /projects/:id/merge_requests/:merge_request_id to close MR" do
it "should return merge_request" do
- put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), state_event: "close"
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), state_event: "close"
expect(response.status).to eq(200)
expect(json_response['state']).to eq('closed')
end
end
- describe "PUT /projects/:id/merge_request/:merge_request_id/merge" do
+ describe "PUT /projects/:id/merge_requests/:merge_request_id/merge" do
let(:ci_commit) { create(:ci_commit_without_jobs) }
it "should return merge_request in case of success" do
- put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user)
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user)
expect(response.status).to eq(200)
end
@@ -332,7 +332,7 @@ describe API::API, api: true do
allow_any_instance_of(MergeRequest).
to receive(:can_be_merged?).and_return(false)
- put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user)
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user)
expect(response.status).to eq(406)
expect(json_response['message']).to eq('Branch cannot be merged')
@@ -340,14 +340,14 @@ describe API::API, api: true do
it "should return 405 if merge_request is not open" do
merge_request.close
- put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user)
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user)
expect(response.status).to eq(405)
expect(json_response['message']).to eq('405 Method Not Allowed')
end
it "should return 405 if merge_request is a work in progress" do
merge_request.update_attribute(:title, "WIP: #{merge_request.title}")
- put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user)
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user)
expect(response.status).to eq(405)
expect(json_response['message']).to eq('405 Method Not Allowed')
end
@@ -355,7 +355,7 @@ describe API::API, api: true do
it "should return 401 if user has no permissions to merge" do
user2 = create(:user)
project.team << [user2, :reporter]
- put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user2)
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user2)
expect(response.status).to eq(401)
expect(json_response['message']).to eq('401 Unauthorized')
end
@@ -364,7 +364,7 @@ describe API::API, api: true do
allow_any_instance_of(MergeRequest).to receive(:ci_commit).and_return(ci_commit)
allow(ci_commit).to receive(:active?).and_return(true)
- put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user), merge_when_build_succeeds: true
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user), merge_when_build_succeeds: true
expect(response.status).to eq(200)
expect(json_response['title']).to eq('Test')
@@ -372,33 +372,33 @@ describe API::API, api: true do
end
end
- describe "PUT /projects/:id/merge_request/:merge_request_id" do
+ describe "PUT /projects/:id/merge_requests/:merge_request_id" do
it "should return merge_request" do
- put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), title: "New title"
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), title: "New title"
expect(response.status).to eq(200)
expect(json_response['title']).to eq('New title')
end
it "should return merge_request" do
- put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), description: "New description"
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), description: "New description"
expect(response.status).to eq(200)
expect(json_response['description']).to eq('New description')
end
it "should return 400 when source_branch is specified" do
- put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user),
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user),
source_branch: "master", target_branch: "master"
expect(response.status).to eq(400)
end
it "should return merge_request with renamed target_branch" do
- put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), target_branch: "wiki"
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), target_branch: "wiki"
expect(response.status).to eq(200)
expect(json_response['target_branch']).to eq('wiki')
end
it 'should return 400 on invalid label names' do
- put api("/projects/#{project.id}/merge_request/#{merge_request.id}",
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.id}",
user),
title: 'new issue',
labels: 'label, ?'
@@ -407,11 +407,11 @@ describe API::API, api: true do
end
end
- describe "POST /projects/:id/merge_request/:merge_request_id/comments" do
+ describe "POST /projects/:id/merge_requests/:merge_request_id/comments" do
it "should return comment" do
original_count = merge_request.notes.size
- post api("/projects/#{project.id}/merge_request/#{merge_request.id}/comments", user), note: "My comment"
+ post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/comments", user), note: "My comment"
expect(response.status).to eq(201)
expect(json_response['note']).to eq('My comment')
expect(json_response['author']['name']).to eq(user.name)
@@ -420,20 +420,20 @@ describe API::API, api: true do
end
it "should return 400 if note is missing" do
- post api("/projects/#{project.id}/merge_request/#{merge_request.id}/comments", user)
+ post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/comments", user)
expect(response.status).to eq(400)
end
it "should return 404 if note is attached to non existent merge request" do
- post api("/projects/#{project.id}/merge_request/404/comments", user),
+ post api("/projects/#{project.id}/merge_requests/404/comments", user),
note: 'My comment'
expect(response.status).to eq(404)
end
end
- describe "GET :id/merge_request/:merge_request_id/comments" do
+ describe "GET :id/merge_requests/:merge_request_id/comments" do
it "should return merge_request comments ordered by created_at" do
- get api("/projects/#{project.id}/merge_request/#{merge_request.id}/comments", user)
+ get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/comments", user)
expect(response.status).to eq(200)
expect(json_response).to be_an Array
expect(json_response.length).to eq(2)
@@ -443,7 +443,7 @@ describe API::API, api: true do
end
it "should return a 404 error if merge_request_id not found" do
- get api("/projects/#{project.id}/merge_request/999/comments", user)
+ get api("/projects/#{project.id}/merge_requests/999/comments", user)
expect(response.status).to eq(404)
end
end
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index 22ba25217f0..22937226fce 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -496,11 +496,11 @@ end
describe Projects::ForksController, 'routing' do
it 'to #new' do
- expect(get('/gitlab/gitlabhq/fork/new')).to route_to('projects/forks#new', namespace_id: 'gitlab', project_id: 'gitlabhq')
+ expect(get('/gitlab/gitlabhq/forks/new')).to route_to('projects/forks#new', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
it 'to #create' do
- expect(post('/gitlab/gitlabhq/fork')).to route_to('projects/forks#create', namespace_id: 'gitlab', project_id: 'gitlabhq')
+ expect(post('/gitlab/gitlabhq/forks')).to route_to('projects/forks#create', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
end