Commit 3d51a6d4 authored by Douwe Maan's avatar Douwe Maan

Merge branch 'master' into reply-by-email

parents 8ec5fb13 8819007c
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
v 8.0.0 (unreleased)
- Remove satellites
- Better performance for web editor (switched from satellites to rugged)
- Faster merge
- Ability to fetch merge requests from refs/merge-requests/:id
- Allow displaying of archived projects in the admin interface (Artem Sidorenko)
- Allow configuration of import sources for new projects (Artem Sidorenko)
v 7.14.0 (unreleased) v 7.14.0 (unreleased)
- Update default robots.txt rules to disallow crawling of irrelevant pages (Ben Bodenmiller) - Update default robots.txt rules to disallow crawling of irrelevant pages (Ben Bodenmiller)
- Fix redirection after sign in when using auto_sign_in_with_provider - Fix redirection after sign in when using auto_sign_in_with_provider
......
7.14.0.pre 8.0.0.pre
...@@ -19,7 +19,7 @@ class @MergeRequestWidget ...@@ -19,7 +19,7 @@ class @MergeRequestWidget
when 'merged' when 'merged'
location.reload() location.reload()
else else
setTimeout(merge_request_widget.mergeInProgress, 3000) setTimeout(merge_request_widget.mergeInProgress, 2000)
dataType: 'json' dataType: 'json'
getMergeStatus: -> getMergeStatus: ->
......
...@@ -373,3 +373,23 @@ table { ...@@ -373,3 +373,23 @@ table {
border-color: #EEE !important; border-color: #EEE !important;
} }
} }
.center-top-menu {
border-bottom: 1px solid #EEE;
list-style: none;
text-align: center;
padding-bottom: 15px;
margin-bottom: 15px;
li {
display: inline-block;
a {
padding: 10px;
}
&.active a {
color: #666;
}
}
}
...@@ -6,3 +6,11 @@ ...@@ -6,3 +6,11 @@
font-size: 30px; font-size: 30px;
} }
} }
.explore-trending-block {
.lead {
line-height: 32px;
font-size: 18px;
margin-top: 10px;
}
}
...@@ -29,6 +29,15 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -29,6 +29,15 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
end end
end end
import_sources = params[:application_setting][:import_sources]
if import_sources.nil?
params[:application_setting][:import_sources] = []
else
import_sources.map! do |source|
source.to_str
end
end
params.require(:application_setting).permit( params.require(:application_setting).permit(
:default_projects_limit, :default_projects_limit,
:default_branch_protection, :default_branch_protection,
...@@ -47,6 +56,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -47,6 +56,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:version_check_enabled, :version_check_enabled,
:user_oauth_applications, :user_oauth_applications,
restricted_visibility_levels: [], restricted_visibility_levels: [],
import_sources: []
) )
end end
end end
...@@ -9,6 +9,7 @@ class Admin::ProjectsController < Admin::ApplicationController ...@@ -9,6 +9,7 @@ class Admin::ProjectsController < Admin::ApplicationController
@projects = @projects.where("visibility_level IN (?)", params[:visibility_levels]) if params[:visibility_levels].present? @projects = @projects.where("visibility_level IN (?)", params[:visibility_levels]) if params[:visibility_levels].present?
@projects = @projects.with_push if params[:with_push].present? @projects = @projects.with_push if params[:with_push].present?
@projects = @projects.abandoned if params[:abandoned].present? @projects = @projects.abandoned if params[:abandoned].present?
@projects = @projects.non_archived unless params[:with_archived].present?
@projects = @projects.search(params[:name]) if params[:name].present? @projects = @projects.search(params[:name]) if params[:name].present?
@projects = @projects.sort(@sort = params[:sort]) @projects = @projects.sort(@sort = params[:sort])
@projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]).per(PER_PAGE) @projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]).per(PER_PAGE)
......
...@@ -20,7 +20,7 @@ class ApplicationController < ActionController::Base ...@@ -20,7 +20,7 @@ class ApplicationController < ActionController::Base
protect_from_forgery with: :exception protect_from_forgery with: :exception
helper_method :abilities, :can?, :current_application_settings helper_method :abilities, :can?, :current_application_settings
helper_method :github_import_enabled?, :gitlab_import_enabled?, :bitbucket_import_enabled? helper_method :import_sources_enabled?, :github_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :gitorious_import_enabled?, :google_code_import_enabled?, :git_import_enabled?
rescue_from Encoding::CompatibilityError do |exception| rescue_from Encoding::CompatibilityError do |exception|
log_exception(exception) log_exception(exception)
...@@ -298,15 +298,43 @@ class ApplicationController < ActionController::Base ...@@ -298,15 +298,43 @@ class ApplicationController < ActionController::Base
@issuable_finder.execute @issuable_finder.execute
end end
def import_sources_enabled?
!current_application_settings.import_sources.empty?
end
def github_import_enabled? def github_import_enabled?
current_application_settings.import_sources.include?('github')
end
def github_import_configured?
Gitlab::OAuth::Provider.enabled?(:github) Gitlab::OAuth::Provider.enabled?(:github)
end end
def gitlab_import_enabled? def gitlab_import_enabled?
request.host != 'gitlab.com' && current_application_settings.import_sources.include?('gitlab')
end
def gitlab_import_configured?
Gitlab::OAuth::Provider.enabled?(:gitlab) Gitlab::OAuth::Provider.enabled?(:gitlab)
end end
def bitbucket_import_enabled? def bitbucket_import_enabled?
current_application_settings.import_sources.include?('bitbucket')
end
def bitbucket_import_configured?
Gitlab::OAuth::Provider.enabled?(:bitbucket) && Gitlab::BitbucketImport.public_key.present? Gitlab::OAuth::Provider.enabled?(:bitbucket) && Gitlab::BitbucketImport.public_key.present?
end end
def gitorious_import_enabled?
current_application_settings.import_sources.include?('gitorious')
end
def google_code_import_enabled?
current_application_settings.import_sources.include?('google_code')
end
def git_import_enabled?
current_application_settings.import_sources.include?('git')
end
end end
...@@ -7,6 +7,7 @@ class Explore::ProjectsController < Explore::ApplicationController ...@@ -7,6 +7,7 @@ class Explore::ProjectsController < Explore::ApplicationController
@tags = @projects.tags_on(:tags) @tags = @projects.tags_on(:tags)
@projects = @projects.tagged_with(params[:tag]) if params[:tag].present? @projects = @projects.tagged_with(params[:tag]) if params[:tag].present?
@projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present? @projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present?
@projects = @projects.non_archived
@projects = @projects.search(params[:search]) if params[:search].present? @projects = @projects.search(params[:search]) if params[:search].present?
@projects = @projects.sort(@sort = params[:sort]) @projects = @projects.sort(@sort = params[:sort])
@projects = @projects.includes(:namespace).page(params[:page]).per(PER_PAGE) @projects = @projects.includes(:namespace).page(params[:page]).per(PER_PAGE)
...@@ -14,6 +15,7 @@ class Explore::ProjectsController < Explore::ApplicationController ...@@ -14,6 +15,7 @@ class Explore::ProjectsController < Explore::ApplicationController
def trending def trending
@trending_projects = TrendingProjectsFinder.new.execute(current_user) @trending_projects = TrendingProjectsFinder.new.execute(current_user)
@trending_projects = @trending_projects.non_archived
@trending_projects = @trending_projects.page(params[:page]).per(PER_PAGE) @trending_projects = @trending_projects.page(params[:page]).per(PER_PAGE)
end end
......
...@@ -10,7 +10,8 @@ class HelpController < ApplicationController ...@@ -10,7 +10,8 @@ class HelpController < ApplicationController
respond_to do |format| respond_to do |format|
format.any(:markdown, :md, :html) do format.any(:markdown, :md, :html) do
path = Rails.root.join('doc', @category, "#{@file}.md") # Note: We are purposefully NOT using `Rails.root.join`
path = File.join(Rails.root, 'doc', @category, "#{@file}.md")
if File.exist?(path) if File.exist?(path)
@markdown = File.read(path) @markdown = File.read(path)
...@@ -24,7 +25,8 @@ class HelpController < ApplicationController ...@@ -24,7 +25,8 @@ class HelpController < ApplicationController
# Allow access to images in the doc folder # Allow access to images in the doc folder
format.any(:png, :gif, :jpeg) do format.any(:png, :gif, :jpeg) do
path = Rails.root.join('doc', @category, "#{@file}.#{params[:format]}") # Note: We are purposefully NOT using `Rails.root.join`
path = File.join(Rails.root, 'doc', @category, "#{@file}.#{params[:format]}")
if File.exist?(path) if File.exist?(path)
send_file(path, disposition: 'inline') send_file(path, disposition: 'inline')
......
class Import::GitoriousController < Import::BaseController class Import::GitoriousController < Import::BaseController
before_action :verify_gitorious_import_enabled
def new def new
redirect_to client.authorize_url(callback_import_gitorious_url) redirect_to client.authorize_url(callback_import_gitorious_url)
...@@ -40,4 +41,8 @@ class Import::GitoriousController < Import::BaseController ...@@ -40,4 +41,8 @@ class Import::GitoriousController < Import::BaseController
@client ||= Gitlab::GitoriousImport::Client.new(session[:gitorious_repos]) @client ||= Gitlab::GitoriousImport::Client.new(session[:gitorious_repos])
end end
def verify_gitorious_import_enabled
not_found! unless gitorious_import_enabled?
end
end end
class Import::GoogleCodeController < Import::BaseController class Import::GoogleCodeController < Import::BaseController
before_action :verify_google_code_import_enabled
before_action :user_map, only: [:new_user_map, :create_user_map] before_action :user_map, only: [:new_user_map, :create_user_map]
def new def new
...@@ -104,6 +105,10 @@ class Import::GoogleCodeController < Import::BaseController ...@@ -104,6 +105,10 @@ class Import::GoogleCodeController < Import::BaseController
@client ||= Gitlab::GoogleCodeImport::Client.new(session[:google_code_dump]) @client ||= Gitlab::GoogleCodeImport::Client.new(session[:google_code_dump])
end end
def verify_google_code_import_enabled
not_found! unless google_code_import_enabled?
end
def user_map def user_map
@user_map ||= begin @user_map ||= begin
user_map = client.user_map user_map = client.user_map
......
...@@ -13,27 +13,20 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -13,27 +13,20 @@ class Projects::BlobController < Projects::ApplicationController
before_action :commit, except: [:new, :create] before_action :commit, except: [:new, :create]
before_action :blob, except: [:new, :create] before_action :blob, except: [:new, :create]
before_action :from_merge_request, only: [:edit, :update] before_action :from_merge_request, only: [:edit, :update]
before_action :after_edit_path, only: [:edit, :update]
before_action :require_branch_head, only: [:edit, :update] before_action :require_branch_head, only: [:edit, :update]
before_action :editor_variables, except: [:show, :preview, :diff]
before_action :after_edit_path, only: [:edit, :update]
def new def new
commit unless @repository.empty? commit unless @repository.empty?
end end
def create def create
file_path = File.join(@path, File.basename(params[:file_name])) result = Files::CreateService.new(@project, current_user, @commit_params).execute
result = Files::CreateService.new(
@project,
current_user,
params.merge(new_branch: sanitized_new_branch_name),
@ref,
file_path
).execute
if result[:status] == :success if result[:status] == :success
flash[:notice] = "Your changes have been successfully committed" flash[:notice] = "Your changes have been successfully committed"
ref = sanitized_new_branch_name.presence || @ref redirect_to namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path))
redirect_to namespace_project_blob_path(@project.namespace, @project, File.join(ref, file_path))
else else
flash[:alert] = result[:message] flash[:alert] = result[:message]
render :new render :new
...@@ -48,22 +41,10 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -48,22 +41,10 @@ class Projects::BlobController < Projects::ApplicationController
end end
def update def update
result = Files::UpdateService. result = Files::UpdateService.new(@project, current_user, @commit_params).execute
new(
@project,
current_user,
params.merge(new_branch: sanitized_new_branch_name),
@ref,
@path
).execute
if result[:status] == :success if result[:status] == :success
flash[:notice] = "Your changes have been successfully committed" flash[:notice] = "Your changes have been successfully committed"
if from_merge_request
from_merge_request.reload_code
end
redirect_to after_edit_path redirect_to after_edit_path
else else
flash[:alert] = result[:message] flash[:alert] = result[:message]
...@@ -80,12 +61,11 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -80,12 +61,11 @@ class Projects::BlobController < Projects::ApplicationController
end end
def destroy def destroy
result = Files::DeleteService.new(@project, current_user, params, @ref, @path).execute result = Files::DeleteService.new(@project, current_user, @commit_params).execute
if result[:status] == :success if result[:status] == :success
flash[:notice] = "Your changes have been successfully committed" flash[:notice] = "Your changes have been successfully committed"
redirect_to namespace_project_tree_path(@project.namespace, @project, redirect_to namespace_project_tree_path(@project.namespace, @project, @target_branch)
@ref)
else else
flash[:alert] = result[:message] flash[:alert] = result[:message]
render :show render :show
...@@ -135,7 +115,6 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -135,7 +115,6 @@ class Projects::BlobController < Projects::ApplicationController
@id = params[:id] @id = params[:id]
@ref, @path = extract_ref(@id) @ref, @path = extract_ref(@id)
rescue InvalidPathError rescue InvalidPathError
not_found! not_found!
end end
...@@ -145,8 +124,8 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -145,8 +124,8 @@ class Projects::BlobController < Projects::ApplicationController
if from_merge_request if from_merge_request
diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) + diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) +
"#file-path-#{hexdigest(@path)}" "#file-path-#{hexdigest(@path)}"
elsif sanitized_new_branch_name.present? elsif @target_branch.present?
namespace_project_blob_path(@project.namespace, @project, File.join(sanitized_new_branch_name, @path)) namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @path))
else else
namespace_project_blob_path(@project.namespace, @project, @id) namespace_project_blob_path(@project.namespace, @project, @id)
end end
...@@ -160,4 +139,25 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -160,4 +139,25 @@ class Projects::BlobController < Projects::ApplicationController
def sanitized_new_branch_name def sanitized_new_branch_name
@new_branch ||= sanitize(strip_tags(params[:new_branch])) @new_branch ||= sanitize(strip_tags(params[:new_branch]))
end end
def editor_variables
@current_branch = @ref
@target_branch = (sanitized_new_branch_name || @ref)
@file_path =
if action_name.to_s == 'create'
File.join(@path, File.basename(params[:file_name]))
else
@path
end
@commit_params = {
file_path: @file_path,
current_branch: @current_branch,
target_branch: @target_branch,
commit_message: params[:commit_message],
file_content: params[:content],
file_content_encoding: params[:encoding]
}
end
end end
...@@ -13,13 +13,8 @@ class Projects::CompareController < Projects::ApplicationController ...@@ -13,13 +13,8 @@ class Projects::CompareController < Projects::ApplicationController
base_ref = Addressable::URI.unescape(params[:from]) base_ref = Addressable::URI.unescape(params[:from])
@ref = head_ref = Addressable::URI.unescape(params[:to]) @ref = head_ref = Addressable::URI.unescape(params[:to])
compare_result = CompareService.new.execute( compare_result = CompareService.new.
current_user, execute(@project, head_ref, @project, base_ref)
@project,
head_ref,
@project,
base_ref
)
@commits = compare_result.commits @commits = compare_result.commits
@diffs = compare_result.diffs @diffs = compare_result.diffs
......
require 'gitlab/satellite/satellite'
class Projects::MergeRequestsController < Projects::ApplicationController class Projects::MergeRequestsController < Projects::ApplicationController
before_action :module_enabled before_action :module_enabled
before_action :merge_request, only: [ before_action :merge_request, only: [
:edit, :update, :show, :diffs, :commits, :automerge, :automerge_check, :edit, :update, :show, :diffs, :commits, :merge, :merge_check,
:ci_status, :toggle_subscription :ci_status, :toggle_subscription
] ]
before_action :closes_issues, only: [:edit, :update, :show, :diffs, :commits] before_action :closes_issues, only: [:edit, :update, :show, :diffs, :commits]
...@@ -137,7 +135,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -137,7 +135,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end end
end end
def automerge_check def merge_check
if @merge_request.unchecked? if @merge_request.unchecked?
@merge_request.check_if_can_be_merged @merge_request.check_if_can_be_merged
end end
...@@ -147,11 +145,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -147,11 +145,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
render partial: "projects/merge_requests/widget/show.html.haml", layout: false render partial: "projects/merge_requests/widget/show.html.haml", layout: false
end end
def automerge def merge
return access_denied! unless @merge_request.can_be_merged_by?(current_user) return access_denied! unless @merge_request.can_be_merged_by?(current_user)
if @merge_request.automergeable? if @merge_request.mergeable?
AutoMergeWorker.perform_async(@merge_request.id, current_user.id, params) MergeWorker.perform_async(@merge_request.id, current_user.id, params)
@status = true @status = true
else else
@status = false @status = false
......
...@@ -39,4 +39,21 @@ module ApplicationSettingsHelper ...@@ -39,4 +39,21 @@ module ApplicationSettingsHelper
end end
end end
end end
# Return a group of checkboxes that use Bootstrap's button plugin for a
# toggle button effect.
def import_sources_checkboxes(help_block_id)
Gitlab::ImportSources.options.map do |name, source|
checked = current_application_settings.import_sources.include?(source)
css_class = 'btn'
css_class += ' active' if checked
checkbox_name = 'application_setting[import_sources][]'
label_tag(checkbox_name, class: css_class) do
check_box_tag(checkbox_name, source, checked,
autocomplete: 'off',
'aria-describedby' => help_block_id) + name
end
end
end
end end
...@@ -10,7 +10,7 @@ module ExploreHelper ...@@ -10,7 +10,7 @@ module ExploreHelper
options = exist_opts.merge(options) options = exist_opts.merge(options)
path = request.path path = explore_projects_path
path << "?#{options.to_param}" path << "?#{options.to_param}"
path path
end end
......
...@@ -67,6 +67,14 @@ module TabHelper ...@@ -67,6 +67,14 @@ module TabHelper
path.any? do |single_path| path.any? do |single_path|
current_path?(single_path) current_path?(single_path)
end end
elsif page = options.delete(:page)
unless page.respond_to?(:each)
page = [page]
end
page.any? do |single_page|
current_page?(single_page)
end
else else
c = options.delete(:controller) c = options.delete(:controller)
a = options.delete(:action) a = options.delete(:action)
......
...@@ -22,10 +22,12 @@ ...@@ -22,10 +22,12 @@
# user_oauth_applications :boolean default(TRUE) # user_oauth_applications :boolean default(TRUE)
# after_sign_out_path :string(255) # after_sign_out_path :string(255)
# session_expire_delay :integer default(10080), not null # session_expire_delay :integer default(10080), not null
# import_sources :text
# #
class ApplicationSetting < ActiveRecord::Base class ApplicationSetting < ActiveRecord::Base
serialize :restricted_visibility_levels serialize :restricted_visibility_levels
serialize :import_sources
serialize :restricted_signup_domains, Array serialize :restricted_signup_domains, Array
attr_accessor :restricted_signup_domains_raw attr_accessor :restricted_signup_domains_raw
...@@ -52,6 +54,16 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -52,6 +54,16 @@ class ApplicationSetting < ActiveRecord::Base
end end
end end
validates_each :import_sources do |record, attr, value|
unless value.nil?
value.each do |source|
unless Gitlab::ImportSources.options.has_value?(source)
record.errors.add(attr, "'#{source}' is not a import source")
end
end
end
end
def self.current def self.current
ApplicationSetting.last ApplicationSetting.last
end end
...@@ -70,7 +82,8 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -70,7 +82,8 @@ class ApplicationSetting < ActiveRecord::Base
session_expire_delay: Settings.gitlab['session_expire_delay'], session_expire_delay: Settings.gitlab['session_expire_delay'],
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'], default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_snippet_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'] restricted_signup_domains: Settings.gitlab['restricted_signup_domains'],
import_sources: ['github','bitbucket','gitlab','gitorious','google_code','git']
) )
end end
......
...@@ -41,8 +41,6 @@ class MergeRequest < ActiveRecord::Base ...@@ -41,8 +41,6 @@ class MergeRequest < ActiveRecord::Base
delegate :commits, :diffs, :last_commit, :last_commit_short_sha, to: :merge_request_diff, prefix: nil delegate :commits, :diffs, :last_commit, :last_commit_short_sha, to: :merge_request_diff, prefix: nil
attr_accessor :should_remove_source_branch
# When this attribute is true some MR validation is ignored # When this attribute is true some MR validation is ignored
# It allows us to close or modify broken merge requests # It allows us to close or modify broken merge requests
attr_accessor :allow_broken attr_accessor :allow_broken
...@@ -57,7 +55,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -57,7 +55,7 @@ class MergeRequest < ActiveRecord::Base
transition [:reopened, :opened] => :closed transition [:reopened, :opened] => :closed
end end
event :merge do event :mark_as_merged do
transition [:reopened, :opened, :locked] => :merged transition [:reopened, :opened, :locked] => :merged
end end
...@@ -205,7 +203,10 @@ class MergeRequest < ActiveRecord::Base ...@@ -205,7 +203,10 @@ class MergeRequest < ActiveRecord::Base
end end
def check_if_can_be_merged def check_if_can_be_merged
if Gitlab::Satellite::MergeAction.new(self.author, self).can_be_merged? can_be_merged =
project.repository.can_be_merged?(source_sha, target_branch)
if can_be_merged
mark_as_mergeable mark_as_mergeable
else else
mark_as_unmergeable mark_as_unmergeable
...@@ -220,18 +221,6 @@ class MergeRequest < ActiveRecord::Base ...@@ -220,18 +221,6 @@ class MergeRequest < ActiveRecord::Base
self.target_project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::CLOSED).last self.target_project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::CLOSED).last
end end
def automerge!(current_user, commit_message = nil)
return unless automergeable?
MergeRequests::AutoMergeService.
new(target_project, current_user).
execute(self, commit_message)
end
def remove_source_branch?
self.should_remove_source_branch && !self.source_project.root_ref?(self.source_branch) && !self.for_fork?
end
def open? def open?
opened? || reopened? opened? || reopened?
end end
...@@ -240,11 +229,11 @@ class MergeRequest < ActiveRecord::Base ...@@ -240,11 +229,11 @@ class MergeRequest < ActiveRecord::Base
title =~ /\A\[?WIP\]?:? /i title =~ /\A\[?WIP\]?:? /i
end end
def automergeable? def mergeable?
open? && !work_in_progress? && can_be_merged? open? && !work_in_progress? && can_be_merged?
end end
def automerge_status def gitlab_merge_status
if work_in_progress? if work_in_progress?
"work_in_progress" "work_in_progress"
else else
...@@ -271,14 +260,14 @@ class MergeRequest < ActiveRecord::Base ...@@ -271,14 +260,14 @@ class MergeRequest < ActiveRecord::Base
# #
# see "git diff" # see "git diff"
def to_diff(current_user) def to_diff(current_user)
Gitlab::Satellite::MergeAction.new(current_user, self).diff_in_satellite target_project.repository.diff_text(target_branch, source_sha)
end end
# Returns the commit as a series of email patches. # Returns the commit as a series of email patches.
# #
# see "git format-patch" # see "git format-patch"
def to_patch(current_user) def to_patch(current_user)
Gitlab::Satellite::MergeAction.new(current_user, self).format_patch target_project.repository.format_patch(target_branch, source_sha)
end end
def hook_attrs def hook_attrs
...@@ -429,4 +418,30 @@ class MergeRequest < ActiveRecord::Base ...@@ -429,4 +418,30 @@ class MergeRequest < ActiveRecord::Base
"Open" "Open"
end end
end end
def target_sha
@target_sha ||= target_project.
repository.commit(target_branch).sha
end
def source_sha
commits.first.sha
end
def fetch_ref
target_project.repository.fetch_ref(
source_project.repository.path_to_repo,
"refs/heads/#{source_branch}",
"refs/merge-requests/#{iid}/head"
)
end
def in_locked_state
begin
lock_mr
yield
ensure
unlock_mr if locked?
end
end
end end
...@@ -16,9 +16,8 @@ require Rails.root.join("app/models/commit") ...@@ -16,9 +16,8 @@ require Rails.root.join("app/models/commit")
class MergeRequestDiff < ActiveRecord::Base class MergeRequestDiff < ActiveRecord::Base
include Sortable include Sortable
# Prevent store of diff # Prevent store of diff if commits amount more then 500
# if commits amount more then 200 COMMITS_SAFE_SIZE = 500
COMMITS_SAFE_SIZE = 200
attr_reader :commits, :diffs attr_reader :commits, :diffs
...@@ -124,12 +123,12 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -124,12 +123,12 @@ class MergeRequestDiff < ActiveRecord::Base
if new_diffs.any? if new_diffs.any?
if new_diffs.size > Commit::DIFF_HARD_LIMIT_FILES if new_diffs.size > Commit::DIFF_HARD_LIMIT_FILES
self.state = :overflow_diff_files_limit self.state = :overflow_diff_files_limit
new_diffs = [] new_diffs = new_diffs.first[Commit::DIFF_HARD_LIMIT_LINES]
end end
if new_diffs.sum { |diff| diff.diff.lines.count } > Commit::DIFF_HARD_LIMIT_LINES if new_diffs.sum { |diff| diff.diff.lines.count } > Commit::DIFF_HARD_LIMIT_LINES
self.state = :overflow_diff_lines_limit self.state = :overflow_diff_lines_limit
new_diffs = [] new_diffs = new_diffs.first[Commit::DIFF_HARD_LIMIT_LINES]
end end
end end
...@@ -160,12 +159,21 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -160,12 +159,21 @@ class MergeRequestDiff < ActiveRecord::Base
private private
def compare_result def compare_result
@compare_result ||= CompareService.new.execute( @compare_result ||=
merge_request.author, begin
merge_request.source_project, # Update ref for merge request
merge_request.source_branch, merge_request.fetch_ref
merge_request.target_project,
# Get latest sha of branch from source project
source_sha = merge_request.source_project.commit(source_branch).sha
Gitlab::CompareResult.new(
Gitlab::Git::Compare.new(
merge_request.target_project.repository.raw_repository,
merge_request.target_branch, merge_request.target_branch,
source_sha,
)
) )
end end
end
end end
...@@ -118,12 +118,11 @@ class Namespace < ActiveRecord::Base ...@@ -118,12 +118,11 @@ class Namespace < ActiveRecord::Base
gitlab_shell.add_namespace(path_was) gitlab_shell.add_namespace(path_was)
if gitlab_shell.mv_namespace(path_was, path) if gitlab_shell.mv_namespace(path_was, path)
# If repositories moved successfully we need to remove old satellites # If repositories moved successfully we need to
# and send update instructions to users. # send update instructions to users.
# However we cannot allow rollback since we moved namespace dir # However we cannot allow rollback since we moved namespace dir
# So we basically we mute exceptions in next actions # So we basically we mute exceptions in next actions
begin begin
gitlab_shell.rm_satellites(path_was)
send_update_instructions send_update_instructions
rescue rescue
# Returning false does not rollback after_* transaction but gives # Returning false does not rollback after_* transaction but gives
......
...@@ -215,7 +215,7 @@ class Project < ActiveRecord::Base ...@@ -215,7 +215,7 @@ class Project < ActiveRecord::Base
end end
def search(query) def search(query)
joins(:namespace).where('projects.archived = ?', false). joins(:namespace).
where('LOWER(projects.name) LIKE :query OR where('LOWER(projects.name) LIKE :query OR
LOWER(projects.path) LIKE :query OR LOWER(projects.path) LIKE :query OR
LOWER(namespaces.name) LIKE :query OR LOWER(namespaces.name) LIKE :query OR
...@@ -520,14 +520,6 @@ class Project < ActiveRecord::Base ...@@ -520,14 +520,6 @@ class Project < ActiveRecord::Base
!repository.exists? || repository.empty? !repository.exists? || repository.empty?
end end
def ensure_satellite_exists
self.satellite.create unless self.satellite.exists?
end
def satellite
@satellite ||= Gitlab::Satellite::Satellite.new(self)
end
def repo def repo
repository.raw repository.raw
end end
...@@ -597,14 +589,11 @@ class Project < ActiveRecord::Base ...@@ -597,14 +589,11 @@ class Project < ActiveRecord::Base
new_path_with_namespace = File.join(namespace_dir, path) new_path_with_namespace = File.join(namespace_dir, path)
if gitlab_shell.mv_repository(old_path_with_namespace, new_path_with_namespace) if gitlab_shell.mv_repository(old_path_with_namespace, new_path_with_namespace)
# If repository moved successfully we need to remove old satellite # If repository moved successfully we need to send update instructions to users.
# and send update instructions to users.
# However we cannot allow rollback since we moved repository # However we cannot allow rollback since we moved repository
# So we basically we mute exceptions in next actions # So we basically we mute exceptions in next actions
begin begin
gitlab_shell.mv_repository("#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki") gitlab_shell.mv_repository("#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki")
gitlab_shell.rm_satellites(old_path_with_namespace)
ensure_satellite_exists
send_move_instructions send_move_instructions
reset_events_cache reset_events_cache
rescue rescue
...@@ -702,7 +691,6 @@ class Project < ActiveRecord::Base ...@@ -702,7 +691,6 @@ class Project < ActiveRecord::Base
def create_repository def create_repository
if forked? if forked?
if gitlab_shell.fork_repository(forked_from_project.path_with_namespace, self.namespace.path) if gitlab_shell.fork_repository(forked_from_project.path_with_namespace, self.namespace.path)
ensure_satellite_exists
true true
else else
errors.add(:base, 'Failed to fork repository via gitlab-shell') errors.add(:base, 'Failed to fork repository via gitlab-shell')
......
...@@ -74,6 +74,8 @@ class GitlabCiService < CiService ...@@ -74,6 +74,8 @@ class GitlabCiService < CiService
else else
:error :error
end end
rescue Errno::ECONNREFUSED
:error
end end
def fork_registration(new_project, private_token) def fork_registration(new_project, private_token)
...@@ -103,6 +105,8 @@ class GitlabCiService < CiService ...@@ -103,6 +105,8 @@ class GitlabCiService < CiService
if response.code == 200 and response["coverage"] if response.code == 200 and response["coverage"]
response["coverage"] response["coverage"]
end end
rescue Errno::ECONNREFUSED
nil
end end
def build_page(sha, ref) def build_page(sha, ref)
......
require 'securerandom'
class Repository class Repository
class PreReceiveError < StandardError; end
class CommitError < StandardError; end
include Gitlab::ShellAdapter include Gitlab::ShellAdapter
attr_accessor :raw_repository, :path_with_namespace, :project attr_accessor :raw_repository, :path_with_namespace, :project
...@@ -368,6 +373,89 @@ class Repository ...@@ -368,6 +373,89 @@ class Repository
@root_ref ||= raw_repository.root_ref @root_ref ||= raw_repository.root_ref
end end
def commit_file(user, path, content, message, branch)
commit_with_hooks(user, branch) do |ref|
path[0] = '' if path[0] == '/'
committer = user_to_comitter(user)
options = {}
options[:committer] = committer
options[:author] = committer
options[:commit] = {
message: message,
branch: ref,
}
options[:file] = {
content: content,
path: path
}
Gitlab::Git::Blob.commit(raw_repository, options)
end
end
def remove_file(user, path, message, branch)
commit_with_hooks(user, branch) do |ref|
path[0] = '' if path[0] == '/'
committer = user_to_comitter(user)
options = {}
options[:committer] = committer
options[:author] = committer
options[:commit] = {
message: message,
branch: ref
}
options[:file] = {
path: path
}
Gitlab::Git::Blob.remove(raw_repository, options)
end
end
def user_to_comitter(user)
{
email: user.email,
name: user.name,
time: Time.now
}
end
def can_be_merged?(source_sha, target_branch)
our_commit = rugged.branches[target_branch].target
their_commit = rugged.lookup(source_sha)
if our_commit && their_commit
!rugged.merge_commits(our_commit, their_commit).conflicts?
else
false
end
end
def merge(user, source_sha, target_branch, options = {})
our_commit = rugged.branches[target_branch].target
their_commit = rugged.lookup(source_sha)
raise "Invalid merge target" if our_commit.nil?
raise "Invalid merge source" if their_commit.nil?
merge_index = rugged.merge_commits(our_commit, their_commit)
return false if merge_index.conflicts?
commit_with_hooks(user, target_branch) do |ref|
actual_options = options.merge(
parents: [our_commit, their_commit],
tree: merge_index.write_tree(rugged),
update_ref: ref
)
Rugged::Commit.create(rugged, actual_options)
end
end
def merged_to_root_ref?(branch_name) def merged_to_root_ref?(branch_name)
branch_commit = commit(branch_name) branch_commit = commit(branch_name)
root_ref_commit = commit(root_ref) root_ref_commit = commit(root_ref)
...@@ -412,6 +500,64 @@ class Repository ...@@ -412,6 +500,64 @@ class Repository
) )
end end
def fetch_ref(source_path, source_ref, target_ref)
args = %W(git fetch #{source_path} #{source_ref}:#{target_ref})
Gitlab::Popen.popen(args, path_to_repo)
end
def commit_with_hooks(current_user, branch)
oldrev = Gitlab::Git::BLANK_SHA
ref = Gitlab::Git::BRANCH_REF_PREFIX + branch
gl_id = Gitlab::ShellEnv.gl_id(current_user)
was_empty = empty?
# Create temporary ref
random_string = SecureRandom.hex
tmp_ref = "refs/tmp/#{random_string}/head"
unless was_empty
oldrev = find_branch(branch).target
rugged.references.create(tmp_ref, oldrev)
end
# Make commit in tmp ref
newrev = yield(tmp_ref)
unless newrev
raise CommitError.new('Failed to create commit')
end
# Run GitLab pre-receive hook
pre_receive_hook = Gitlab::Git::Hook.new('pre-receive', path_to_repo)
status = pre_receive_hook.trigger(gl_id, oldrev, newrev, ref)
if status
if was_empty
# Create branch
rugged.references.create(ref, newrev)
else
# Update head
current_head = find_branch(branch).target
# Make sure target branch was not changed during pre-receive hook
if current_head == oldrev
rugged.references.update(ref, newrev)
else
raise CommitError.new('Commit was rejected because branch received new push')
end
end
# Run GitLab post receive hook
post_receive_hook = Gitlab::Git::Hook.new('post-receive', path_to_repo)
status = post_receive_hook.trigger(gl_id, oldrev, newrev, ref)
else
# Remove tmp ref and return error to user
rugged.references.delete(tmp_ref)
raise PreReceiveError.new('Commit was rejected by pre-receive hook')
end
end
private private
def cache def cache
......
...@@ -31,6 +31,10 @@ class BaseService ...@@ -31,6 +31,10 @@ class BaseService
SystemHooksService.new SystemHooksService.new
end end
def repository
project.repository
end
# Add an error to the specified model for restricted visibility levels # Add an error to the specified model for restricted visibility levels
def deny_visibility_level(model, denied_visibility_level = nil) def deny_visibility_level(model, denied_visibility_level = nil)
denied_visibility_level ||= model.visibility_level denied_visibility_level ||= model.visibility_level
......
require 'securerandom'
# Compare 2 branches for one repo or between repositories # Compare 2 branches for one repo or between repositories
# and return Gitlab::CompareResult object that responds to commits and diffs # and return Gitlab::CompareResult object that responds to commits and diffs
class CompareService class CompareService
def execute(current_user, source_project, source_branch, target_project, target_branch) def execute(source_project, source_branch, target_project, target_branch)
# Try to compare branches to get commits list and diffs source_sha = source_project.commit(source_branch).sha
#
# Note: Use satellite only when need to compare between two repos # If compare with other project we need to fetch ref first
# because satellites are slower than operations on bare repo unless target_project == source_project
if target_project == source_project random_string = SecureRandom.hex
target_project.repository.fetch_ref(
source_project.repository.path_to_repo,
"refs/heads/#{source_branch}",
"refs/tmp/#{random_string}/head"
)
end
Gitlab::CompareResult.new( Gitlab::CompareResult.new(
Gitlab::Git::Compare.new( Gitlab::Git::Compare.new(
target_project.repository.raw_repository, target_project.repository.raw_repository,
target_branch, target_branch,
source_branch, source_sha,
) )
) )
else
Gitlab::Satellite::CompareAction.new(
current_user,
target_project,
target_branch,
source_project,
source_branch
).result
end
end end
end end
module Files module Files
class BaseService < ::BaseService class BaseService < ::BaseService
attr_reader :ref, :path class ValidationError < StandardError; end
def initialize(project, user, params, ref, path = nil) def execute
@project, @current_user, @params = project, user, params.dup @current_branch = params[:current_branch]
@ref = ref @target_branch = params[:target_branch]
@path = path @commit_message = params[:commit_message]
@file_path = params[:file_path]
@file_content = if params[:file_content_encoding] == 'base64'
Base64.decode64(params[:file_content])
else
params[:file_content]
end
# Validate parameters
validate
# Create new branch if it different from current_branch
if @target_branch != @current_branch
create_target_branch
end
if sha = commit
success
else
error("Something went wrong. Your changes were not committed")
end
rescue Repository::CommitError, Repository::PreReceiveError, ValidationError => ex
error(ex.message)
end end
private private
def repository def current_branch
project.repository @current_branch ||= params[:current_branch]
end
def target_branch
@target_branch ||= params[:target_branch]
end
def raise_error(message)
raise ValidationError.new(message)
end
def validate
allowed = ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(@target_branch)
unless allowed
raise_error("You are not allowed to push into this branch")
end
unless project.empty_repo?
unless repository.branch_names.include?(@current_branch)
raise_error("You can only create files if you are on top of a branch")
end
if @current_branch != @target_branch
if repository.branch_names.include?(@target_branch)
raise_error("Branch with such name already exists. You need to switch to this branch in order to make changes")
end
end
end
end
def create_target_branch
result = CreateBranchService.new(project, current_user).execute(@target_branch, @current_branch)
unless result[:status] == :success
raise_error("Something went wrong when we tried to create #{@target_branch} for you")
end
end end
end end
end end
require_relative "base_service" require_relative "base_service"
module Files module Files
class CreateService < BaseService class CreateService < Files::BaseService
def execute def commit
allowed = Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref) repository.commit_file(current_user, @file_path, @file_content, @commit_message, @target_branch)
unless allowed
return error("You are not allowed to create file in this branch")
end end
file_name = File.basename(path) def validate
file_path = path super
file_name = File.basename(@file_path)
unless file_name =~ Gitlab::Regex.file_name_regex unless file_name =~ Gitlab::Regex.file_name_regex
return error( raise_error(
'Your changes could not be committed, because the file name ' + 'Your changes could not be committed, because the file name ' +
Gitlab::Regex.file_name_regex_message Gitlab::Regex.file_name_regex_message
) )
end end
if project.empty_repo? unless project.empty_repo?
# everything is ok because repo does not have a commits yet blob = repository.blob_at_branch(@current_branch, @file_path)
else
unless repository.branch_names.include?(ref)
return error("You can only create files if you are on top of a branch")
end
blob = repository.blob_at_branch(ref, file_path)
if blob if blob
return error("Your changes could not be committed, because file with such name exists") raise_error("Your changes could not be committed, because file with such name exists")
end
end end
new_file_action = Gitlab::Satellite::NewFileAction.new(current_user, project, ref, file_path)
created_successfully = new_file_action.commit!(
params[:content],
params[:commit_message],
params[:encoding],
params[:new_branch]
)
if created_successfully
success
else
error("Your changes could not be committed, because the file has been changed")
end end
end end
end end
......
require_relative "base_service" require_relative "base_service"
module Files module Files
class DeleteService < BaseService class DeleteService < Files::BaseService
def execute def commit
allowed = ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref) repository.remove_file(current_user, @file_path, @commit_message, @target_branch)
unless allowed
return error("You are not allowed to push into this branch")
end
unless repository.branch_names.include?(ref)
return error("You can only create files if you are on top of a branch")
end
blob = repository.blob_at_branch(ref, path)
unless blob
return error("You can only edit text files")
end
delete_file_action = Gitlab::Satellite::DeleteFileAction.new(current_user, project, ref, path)
deleted_successfully = delete_file_action.commit!(
nil,
params[:commit_message]
)
if deleted_successfully
success
else
error("Your changes could not be committed, because the file has been changed")
end
end end
end end
end end
require_relative "base_service" require_relative "base_service"
module Files module Files
class UpdateService < BaseService class UpdateService < Files::BaseService
def execute def commit
allowed = ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref) repository.commit_file(current_user, @file_path, @file_content, @commit_message, @target_branch)
unless allowed
return error("You are not allowed to push into this branch")
end
unless repository.branch_names.include?(ref)
return error("You can only create files if you are on top of a branch")
end
blob = repository.blob_at_branch(ref, path)
unless blob
return error("You can only edit text files")
end
edit_file_action = Gitlab::Satellite::EditFileAction.new(current_user, project, ref, path)
edit_file_action.commit!(
params[:content],
params[:commit_message],
params[:encoding],
params[:new_branch]
)
success
rescue Gitlab::Satellite::CheckoutFailed => ex
error("Your changes could not be committed because ref '#{ref}' could not be checked out", 400)
rescue Gitlab::Satellite::CommitFailed => ex
error("Your changes could not be committed. Maybe there was nothing to commit?", 409)
rescue Gitlab::Satellite::PushFailed => ex
error("Your changes could not be committed. Maybe the file was changed by another process?", 409)
end end
end end
end end
...@@ -10,16 +10,14 @@ class GitPushService ...@@ -10,16 +10,14 @@ class GitPushService
# #
# Next, this method: # Next, this method:
# 1. Creates the push event # 1. Creates the push event
# 2. Ensures that the project satellite exists # 2. Updates merge requests
# 3. Updates merge requests # 3. Recognizes cross-references from commit messages
# 4. Recognizes cross-references from commit messages # 4. Executes the project's web hooks
# 5. Executes the project's web hooks # 5. Executes the project's services
# 6. Executes the project's services
# #
def execute(project, user, oldrev, newrev, ref) def execute(project, user, oldrev, newrev, ref)
@project, @user = project, user @project, @user = project, user
project.ensure_satellite_exists
project.repository.expire_cache project.repository.expire_cache
if push_remove_branch?(ref, newrev) if push_remove_branch?(ref, newrev)
...@@ -133,7 +131,8 @@ class GitPushService ...@@ -133,7 +131,8 @@ class GitPushService
end end
def is_default_branch?(ref) def is_default_branch?(ref)
Gitlab::Git.branch_ref?(ref) && Gitlab::Git.ref_name(ref) == project.default_branch Gitlab::Git.branch_ref?(ref) &&
(Gitlab::Git.ref_name(ref) == project.default_branch || project.default_branch.nil?)
end end
def commit_user(commit) def commit_user(commit)
......
module MergeRequests
# AutoMergeService class
#
# Do git merge in satellite and in case of success
# mark merge request as merged and execute all hooks and notifications
# Called when you do merge via GitLab UI
class AutoMergeService < BaseMergeService
def execute(merge_request, commit_message)
merge_request.lock_mr
if Gitlab::Satellite::MergeAction.new(current_user, merge_request).merge!(commit_message)
merge_request.merge
create_merge_event(merge_request, current_user)
create_note(merge_request)
notification_service.merge_mr(merge_request, current_user)
execute_hooks(merge_request, 'merge')
true
else
merge_request.unlock_mr
false
end
rescue
merge_request.unlock_mr if merge_request.locked?
merge_request.mark_as_unmergeable
false
end
end
end
module MergeRequests
class BaseMergeService < MergeRequests::BaseService
private
def create_merge_event(merge_request, current_user)
EventCreateService.new.merge_mr(merge_request, current_user)
end
end
end
...@@ -12,12 +12,16 @@ module MergeRequests ...@@ -12,12 +12,16 @@ module MergeRequests
merge_request.target_project ||= (project.forked_from_project || project) merge_request.target_project ||= (project.forked_from_project || project)
merge_request.target_branch ||= merge_request.target_project.default_branch merge_request.target_branch ||= merge_request.target_project.default_branch
unless merge_request.target_branch && merge_request.source_branch if merge_request.target_branch.blank? || merge_request.source_branch.blank?
return build_failed(merge_request, nil) message =
if params[:source_branch] || params[:target_branch]
"You must select source and target branch"
end
return build_failed(merge_request, message)
end end
compare_result = CompareService.new.execute( compare_result = CompareService.new.execute(
current_user,
merge_request.source_project, merge_request.source_project,
merge_request.source_branch, merge_request.source_branch,
merge_request.target_project, merge_request.target_project,
...@@ -40,7 +44,6 @@ module MergeRequests ...@@ -40,7 +44,6 @@ module MergeRequests
merge_request.compare_diffs = diffs merge_request.compare_diffs = diffs
elsif diffs == false elsif diffs == false
# satellite timeout return false
merge_request.can_be_created = false merge_request.can_be_created = false
merge_request.compare_failed = true merge_request.compare_failed = true
end end
...@@ -59,9 +62,6 @@ module MergeRequests ...@@ -59,9 +62,6 @@ module MergeRequests
end end
merge_request merge_request
rescue Gitlab::Satellite::BranchesWithoutParent
return build_failed(merge_request, "Selected branches have no common commit so they cannot be merged.")
end end
def build_failed(merge_request, message) def build_failed(merge_request, message)
......
module MergeRequests module MergeRequests
# MergeService class # MergeService class
# #
# Mark existing merge request as merged # Do git merge and in case of success
# and execute all hooks and notifications # mark merge request as merged and execute all hooks and notifications
# Called when you do merge via command line and push code # Executed when you do merge via GitLab UI
# to target branch #
class MergeService < BaseMergeService class MergeService < MergeRequests::BaseService
attr_reader :merge_request, :commit_message
def execute(merge_request, commit_message) def execute(merge_request, commit_message)
merge_request.merge @commit_message = commit_message
@merge_request = merge_request
unless @merge_request.mergeable?
return error('Merge request is not mergeable')
end
merge_request.in_locked_state do
if commit
after_merge
success
else
error('Can not merge changes')
end
end
end
private
create_merge_event(merge_request, current_user) def commit
create_note(merge_request) committer = repository.user_to_comitter(current_user)
notification_service.merge_mr(merge_request, current_user)
execute_hooks(merge_request, 'merge') options = {
message: commit_message,
author: committer,
committer: committer
}
repository.merge(current_user, merge_request.source_sha, merge_request.target_branch, options)
end
true def after_merge
rescue MergeRequests::PostMergeService.new(project, current_user).execute(merge_request)
false
end end
end end
end end
module MergeRequests
# PostMergeService class
#
# Mark existing merge request as merged
# and execute all hooks and notifications
#
class PostMergeService < MergeRequests::BaseService
def execute(merge_request)
merge_request.mark_as_merged
create_merge_event(merge_request, current_user)
create_note(merge_request)
notification_service.merge_mr(merge_request, current_user)
execute_hooks(merge_request, 'merge')
end
private
def create_merge_event(merge_request, current_user)
EventCreateService.new.merge_mr(merge_request, current_user)
end
end
end
...@@ -33,9 +33,9 @@ module MergeRequests ...@@ -33,9 +33,9 @@ module MergeRequests
merge_requests.uniq.select(&:source_project).each do |merge_request| merge_requests.uniq.select(&:source_project).each do |merge_request|
MergeRequests::MergeService. MergeRequests::PostMergeService.
new(merge_request.target_project, @current_user). new(merge_request.target_project, @current_user).
execute(merge_request, nil) execute(merge_request)
end end
end end
......
...@@ -27,7 +27,6 @@ module Projects ...@@ -27,7 +27,6 @@ module Projects
end end
end end
project.satellite.destroy
log_info("Project \"#{project.name}\" was removed") log_info("Project \"#{project.name}\" was removed")
system_hook_service.execute_hooks_for(project, :destroy) system_hook_service.execute_hooks_for(project, :destroy)
true true
......
...@@ -33,9 +33,6 @@ module Projects ...@@ -33,9 +33,6 @@ module Projects
raise TransferError.new("Project with same path in target namespace already exists") raise TransferError.new("Project with same path in target namespace already exists")
end end
# Remove old satellite
project.satellite.destroy
# Apply new namespace id # Apply new namespace id
project.namespace = new_namespace project.namespace = new_namespace
project.save! project.save!
...@@ -51,9 +48,6 @@ module Projects ...@@ -51,9 +48,6 @@ module Projects
# Move wiki repo also if present # Move wiki repo also if present
gitlab_shell.mv_repository("#{old_path}.wiki", "#{new_path}.wiki") gitlab_shell.mv_repository("#{old_path}.wiki", "#{new_path}.wiki")
# Create a new satellite (reload project from DB)
Project.find(project.id).ensure_satellite_exists
# clear project cached events # clear project cached events
project.reset_events_cache project.reset_events_cache
......
...@@ -27,6 +27,20 @@ ...@@ -27,6 +27,20 @@
- restricted_level_checkboxes('restricted-visibility-help').each do |level| - restricted_level_checkboxes('restricted-visibility-help').each do |level|
= level = level
%span.help-block#restricted-visibility-help Selected levels cannot be used by non-admin users for projects or snippets %span.help-block#restricted-visibility-help Selected levels cannot be used by non-admin users for projects or snippets
.form-group
= f.label :import_sources, class: 'control-label col-sm-2'
.col-sm-10
- data_attrs = { toggle: 'buttons' }
.btn-group{ data: data_attrs }
- import_sources_checkboxes('import-sources-help').each do |source|
= source
%span.help-block#import-sources-help
Enabled sources for code import during project creation. OmniAuth must be configured for GitHub
= link_to "(?)", help_page_path("integration", "github")
, Bitbucket
= link_to "(?)", help_page_path("integration", "bitbucket")
and GitLab.com
= link_to "(?)", help_page_path("integration", "gitlab")
.form-group .form-group
.col-sm-offset-2.col-sm-10 .col-sm-offset-2.col-sm-10
.checkbox .checkbox
......
...@@ -23,6 +23,10 @@ ...@@ -23,6 +23,10 @@
= label_tag :abandoned do = label_tag :abandoned do
= check_box_tag :abandoned, 1, params[:abandoned] = check_box_tag :abandoned, 1, params[:abandoned]
%span No activity over 6 month %span No activity over 6 month
.checkbox
= label_tag :with_archived do
= check_box_tag :with_archived, 1, params[:with_archived]
%span Show archived projects
%fieldset %fieldset
%strong Visibility level: %strong Visibility level:
...@@ -73,6 +77,8 @@ ...@@ -73,6 +77,8 @@
= visibility_level_icon(project.visibility_level) = visibility_level_icon(project.visibility_level)
= link_to project.name_with_namespace, [:admin, project.namespace.becomes(Namespace), project] = link_to project.name_with_namespace, [:admin, project.namespace.becomes(Namespace), project]
.pull-right .pull-right
- if project.archived
%span.label.label-warning archived
%span.label.label-gray %span.label.label-gray
= repository_size(project) = repository_size(project)
= link_to 'Edit', edit_namespace_project_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn btn-sm" = link_to 'Edit', edit_namespace_project_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn btn-sm"
......
%ul.center-top-menu
= nav_link(page: [dashboard_groups_path]) do
= link_to dashboard_groups_path, title: 'Your groups', data: {placement: 'right'} do
Your Groups
= nav_link(page: [explore_groups_path]) do
= link_to explore_groups_path, title: 'Explore groups', data: {toggle: 'tooltip', placement: 'bottom'} do
Explore Groups
%ul.center-top-menu
= nav_link(path: ['dashboard#show', 'root#show']) do
= link_to dashboard_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do
Your Projects
= nav_link(page: starred_dashboard_projects_path) do
= link_to starred_dashboard_projects_path, title: 'Starred Projects', data: {placement: 'right'} do
Starred Projects
= nav_link(page: [explore_root_path, trending_explore_projects_path, starred_explore_projects_path, explore_projects_path]) do
= link_to explore_root_path, title: 'Explore', data: {toggle: 'tooltip', placement: 'bottom'} do
Explore Projects
- page_title "Groups" - page_title "Groups"
%h3.page-title = render 'dashboard/groups_head'
Group Membership
.slead
Group members have access to all group projects.
- if current_user.can_create_group? - if current_user.can_create_group?
%span.pull-right.hidden-xs %span.pull-right.hidden-xs
= link_to new_group_path, class: "btn btn-new" do = link_to new_group_path, class: "btn btn-new btn-sm" do
%i.fa.fa-plus %i.fa.fa-plus
New Group New Group
%p.light
Group members have access to all group projects.
%hr
.panel.panel-default .panel.panel-default
.panel-heading .panel-heading
%strong Groups %strong Groups
......
- page_title "Starred Projects" - page_title "Starred Projects"
= render 'dashboard/projects_head'
- if @projects.any? - if @projects.any?
= render 'shared/show_aside' = render 'shared/show_aside'
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
- if current_user - if current_user
= auto_discovery_link_tag(:atom, dashboard_url(format: :atom, private_token: current_user.private_token), title: "All activity") = auto_discovery_link_tag(:atom, dashboard_url(format: :atom, private_token: current_user.private_token), title: "All activity")
= render 'dashboard/projects_head'
- if @projects.any? - if @projects.any?
= render 'shared/show_aside' = render 'shared/show_aside'
......
- page_title "Groups" - page_title "Groups"
.clearfix - if current_user
= render 'dashboard/groups_head'
.clearfix.append-bottom-10
.pull-left .pull-left
= form_tag explore_groups_path, method: :get, class: 'form-inline form-tiny' do |f| = form_tag explore_groups_path, method: :get, class: 'form-inline form-tiny' do |f|
= hidden_field_tag :sort, @sort = hidden_field_tag :sort, @sort
...@@ -28,15 +30,12 @@ ...@@ -28,15 +30,12 @@
= link_to explore_groups_path(sort: sort_value_oldest_updated) do = link_to explore_groups_path(sort: sort_value_oldest_updated) do
= sort_title_oldest_updated = sort_title_oldest_updated
%hr
%ul.bordered-list %ul.bordered-list
- @groups.each do |group| - @groups.each do |group|
%li %li
.clearfix .clearfix
%h4 %h4
= link_to group_path(id: group.path) do = link_to group_path(id: group.path) do
%i.fa.fa-users
= group.name = group.name
.clearfix .clearfix
%p %p
......
.dropdown.inline
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
%span.light sort:
- if @sort.present?
= sort_options_hash[@sort]
- elsif current_page?(trending_explore_projects_path) || current_page?(explore_root_path)
Trending projects
- elsif current_page?(starred_explore_projects_path)
Most stars
- else
= sort_title_recently_created
%b.caret
%ul.dropdown-menu
%li
= link_to trending_explore_projects_path do
Trending projects
= link_to starred_explore_projects_path do
Most stars
= link_to explore_projects_filter_path(sort: sort_value_recently_created) do
= sort_title_recently_created
= link_to explore_projects_filter_path(sort: sort_value_oldest_created) do
= sort_title_oldest_created
= link_to explore_projects_filter_path(sort: sort_value_recently_updated) do
= sort_title_recently_updated
= link_to explore_projects_filter_path(sort: sort_value_oldest_updated) do
= sort_title_oldest_updated
...@@ -46,22 +46,4 @@ ...@@ -46,22 +46,4 @@
= link_to explore_projects_filter_path(tag: tag.name) do = link_to explore_projects_filter_path(tag: tag.name) do
%i.fa.fa-tag %i.fa.fa-tag
= tag.name = tag.name
= render 'explore/projects/dropdown'
.dropdown.inline
%button.dropdown-toggle.btn{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
%li
= link_to explore_projects_filter_path(sort: sort_value_recently_created) do
= sort_title_recently_created
= link_to explore_projects_filter_path(sort: sort_value_oldest_created) do
= sort_title_oldest_created
= link_to explore_projects_filter_path(sort: sort_value_recently_updated) do
= sort_title_recently_updated
= link_to explore_projects_filter_path(sort: sort_value_oldest_updated) do
= sort_title_oldest_updated
- page_title "Projects" - page_title "Projects"
- if current_user
= render 'dashboard/projects_head'
.clearfix .clearfix
= render 'filter' = render 'filter'
%br
%hr
.public-projects .public-projects
%ul.bordered-list.top-list %ul.bordered-list.top-list
= render @projects = render @projects
......
- page_title "Starred Projects" - page_title "Starred Projects"
- if current_user
= render 'dashboard/projects_head'
.explore-trending-block .explore-trending-block
%p.lead .lead
%i.fa.fa-star %i.fa.fa-star
See most starred projects See most starred projects
%hr .pull-right
= render 'explore/projects/dropdown'
.public-projects .public-projects
%ul.bordered-list %ul.bordered-list
= render @starred_projects = render @starred_projects
......
- page_title "Trending Projects" - page_title "Trending Projects"
- if current_user
= render 'dashboard/projects_head'
.explore-title .explore-title
%h3 %h3
Explore GitLab Explore GitLab
...@@ -6,10 +8,11 @@ ...@@ -6,10 +8,11 @@
Discover projects and groups. Share your projects with others Discover projects and groups. Share your projects with others
%hr %hr
.explore-trending-block .explore-trending-block
%p.lead .lead
%i.fa.fa-comments-o %i.fa.fa-comments-o
See most discussed projects for last month See most discussed projects for last month
%hr .pull-right
= render 'explore/projects/dropdown'
.public-projects .public-projects
%ul.bordered-list %ul.bordered-list
= render @trending_projects = render @trending_projects
...@@ -12,11 +12,14 @@ ...@@ -12,11 +12,14 @@
- @projects.each do |project| - @projects.each do |project|
%li %li
.list-item-name .list-item-name
%span{ class: visibility_level_color(project.visibility_level) }
= visibility_level_icon(project.visibility_level) = visibility_level_icon(project.visibility_level)
%strong= link_to project.name_with_namespace, project %strong= link_to project.name_with_namespace, project
.pull-right
- if project.archived
%span.label.label-warning archived
%span.label.label-gray %span.label.label-gray
= repository_size(project) = repository_size(project)
.pull-right
= link_to 'Members', namespace_project_project_members_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn btn-sm" = link_to 'Members', namespace_project_project_members_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn btn-sm"
= link_to 'Edit', edit_namespace_project_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn btn-sm" = link_to 'Edit', edit_namespace_project_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn btn-sm"
= link_to 'Remove', project, data: { confirm: remove_project_message(project)}, method: :delete, class: "btn btn-sm btn-remove" = link_to 'Remove', project, data: { confirm: remove_project_message(project)}, method: :delete, class: "btn btn-sm btn-remove"
......
- page_title "Explore" - page_title "Explore"
- header_title "Explore GitLab", explore_root_path - if current_user
- sidebar "explore" - header_title "Dashboard", root_path
- else
- header_title "Explore GitLab", explore_root_path
- sidebar "dashboard"
= render template: "layouts/application" = render template: "layouts/application"
...@@ -17,13 +17,13 @@ ...@@ -17,13 +17,13 @@
%li.visible-sm.visible-xs %li.visible-sm.visible-xs
= link_to search_path, title: 'Search', data: {toggle: 'tooltip', placement: 'bottom'} do = link_to search_path, title: 'Search', data: {toggle: 'tooltip', placement: 'bottom'} do
= icon('search') = icon('search')
%li.hidden-xs -#%li.hidden-xs
= link_to help_path, title: 'Help', data: {toggle: 'tooltip', placement: 'bottom'} do = link_to help_path, title: 'Help', data: {toggle: 'tooltip', placement: 'bottom'} do
= icon('question-circle fw') = icon('question-circle fw')
%li -#%li
= link_to explore_root_path, title: 'Explore', data: {toggle: 'tooltip', placement: 'bottom'} do = link_to explore_root_path, title: 'Explore', data: {toggle: 'tooltip', placement: 'bottom'} do
= icon('globe fw') = icon('globe fw')
%li -#%li
= link_to user_snippets_path(current_user), title: 'Your snippets', data: {toggle: 'tooltip', placement: 'bottom'} do = link_to user_snippets_path(current_user), title: 'Your snippets', data: {toggle: 'tooltip', placement: 'bottom'} do
= icon('clipboard fw') = icon('clipboard fw')
- if current_user.is_admin? - if current_user.is_admin?
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
%li.hidden-xs %li.hidden-xs
= link_to new_project_path, title: 'New project', data: {toggle: 'tooltip', placement: 'bottom'} do = link_to new_project_path, title: 'New project', data: {toggle: 'tooltip', placement: 'bottom'} do
= icon('plus fw') = icon('plus fw')
%li -#%li
= link_to profile_path, title: 'Profile settings', data: {toggle: 'tooltip', placement: 'bottom'} do = link_to profile_path, title: 'Profile settings', data: {toggle: 'tooltip', placement: 'bottom'} do
= icon('cog fw') = icon('cog fw')
%li %li
......
%ul.nav.nav-sidebar %ul.nav.nav-sidebar
= nav_link(path: ['dashboard#show', 'root#show'], html_options: {class: 'home'}) do = nav_link(path: ['dashboard#show', 'root#show', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do
= link_to dashboard_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do = link_to (current_user ? root_path : explore_root_path), title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do
= icon('dashboard fw') = icon('dashboard fw')
%span %span
Your Projects Projects
= nav_link(path: 'projects#starred') do
= link_to starred_dashboard_projects_path, title: 'Starred Projects', data: {placement: 'right'} do
= icon('star fw')
%span
Starred Projects
= nav_link(controller: :groups) do = nav_link(controller: :groups) do
= link_to dashboard_groups_path, title: 'Groups', data: {placement: 'right'} do = link_to (current_user ? dashboard_groups_path : explore_groups_path), title: 'Groups', data: {placement: 'right'} do
= icon('group fw') = icon('group fw')
%span %span
Groups Groups
- if current_user
= nav_link(controller: :milestones) do = nav_link(controller: :milestones) do
= link_to dashboard_milestones_path, title: 'Milestones', data: {placement: 'right'} do = link_to dashboard_milestones_path, title: 'Milestones', data: {placement: 'right'} do
= icon('clock-o fw') = icon('clock-o fw')
...@@ -31,6 +27,17 @@ ...@@ -31,6 +27,17 @@
%span %span
Merge Requests Merge Requests
%span.count= current_user.assigned_merge_requests.opened.count %span.count= current_user.assigned_merge_requests.opened.count
= nav_link(controller: :snippets) do
= link_to (current_user ? user_snippets_path(current_user) : snippets_path), title: 'Your snippets', data: {placement: 'right'} do
= icon('dashboard fw')
%span
Snippets
- if current_user
= nav_link(controller: :profile) do
= link_to profile_path, title: 'Profile settings', data: {toggle: 'tooltip', placement: 'bottom'} do
= icon('user fw')
%span
Profile
= nav_link(controller: :help) do = nav_link(controller: :help) do
= link_to help_path, title: 'Help', data: {placement: 'right'} do = link_to help_path, title: 'Help', data: {placement: 'right'} do
= icon('question-circle fw') = icon('question-circle fw')
......
%ul.nav.nav-sidebar
= nav_link(path: 'projects#trending') do
= link_to explore_root_path, title: 'Trending Projects', data: {placement: 'right'} do
= icon('comments fw')
%span Trending Projects
= nav_link(path: 'projects#starred') do
= link_to starred_explore_projects_path, title: 'Most-starred Projects', data: {placement: 'right'} do
= icon('star fw')
%span Most-starred Projects
= nav_link(path: 'projects#index') do
= link_to explore_projects_path, title: 'All Projects', data: {placement: 'right'} do
= icon('bookmark fw')
%span All Projects
= nav_link(controller: :groups) do
= link_to explore_groups_path, title: 'All Groups', data: {placement: 'right'} do
= icon('group fw')
%span All Groups
%ul.nav.nav-sidebar
- if current_user
= nav_link(path: user_snippets_path(current_user), html_options: {class: 'home'}) do
= link_to user_snippets_path(current_user), title: 'Your snippets', data: {placement: 'right'} do
= icon('dashboard fw')
%span
Your Snippets
= nav_link(path: snippets_path) do
= link_to snippets_path, title: 'Discover snippets', data: {placement: 'right'} do
= icon('globe fw')
%span
Discover Snippets
- page_title 'Snippets' - page_title 'Snippets'
- header_title 'Snippets', snippets_path - if current_user
- sidebar "snippets" - header_title "Dashboard", root_path
- else
- header_title 'Snippets', snippets_path
- sidebar "dashboard"
= render template: "layouts/application" = render template: "layouts/application"
%p.slead %p.slead
Should you ever lose your phone, each of these recovery codes can be used one Should you ever lose your phone, each of these recovery codes can be used one
time each to regain access to your account. Please save them in a safe place. time each to regain access to your account. Please save them in a safe place, or you
%b will
lose access to your account.
.codes.well .codes.well
%ul %ul
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
.modal-body .modal-body
To enable importing projects from Bitbucket, To enable importing projects from Bitbucket,
- if current_user.admin? - if current_user.admin?
you need to as administrator you need to configure
- else - else
your GitLab administrator needs to ask your GitLab administrator to configure
== #{link_to 'setup OAuth integration', 'https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/integration/bitbucket.md'}. == #{link_to 'OAuth integration', help_page_path("integration", "bitbucket")}.
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
.modal-body .modal-body
To enable importing projects from GitHub, To enable importing projects from GitHub,
- if current_user.admin? - if current_user.admin?
you need to as administrator you need to configure
- else - else
your GitLab administrator needs to ask your Gitlab administrator to configure
== #{link_to 'setup OAuth integration', 'https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/integration/github.md'}. == #{link_to 'OAuth integration', help_page_path("integration", "github")}.
\ No newline at end of file
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
.modal-body .modal-body
To enable importing projects from GitLab.com, To enable importing projects from GitLab.com,
- if current_user.admin? - if current_user.admin?
you need to as administrator you need to configure
- else - else
your GitLab administrator needs to ask your GitLab administrator to configure
== #{link_to 'setup OAuth integration', 'https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/integration/gitlab.md'}. == #{link_to 'OAuth integration', help_page_path("integration", "gitlab")}.
\ No newline at end of file
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
= render 'shared/commit_message_container', params: params, = render 'shared/commit_message_container', params: params,
placeholder: 'Add new file' placeholder: 'Add new file'
- unless @project.empty_repo?
.form-group.branch .form-group.branch
= label_tag 'branch', class: 'control-label' do = label_tag 'branch', class: 'control-label' do
Branch Branch
......
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
- if @merge_request.compare_failed - if @merge_request.compare_failed
.alert.alert-danger .alert.alert-danger
%h4 Compare failed %h4 Compare failed
%p We can't compare selected branches. It may be because of huge diff or satellite timeout. Please try again or select different branches. %p We can't compare selected branches. It may be because of huge diff. Please try again or select different branches.
- else - else
.light-well .light-well
.center .center
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
= icon('history') = icon('history')
Commits Commits
%span.badge= @commits.size %span.badge= @commits.size
%li.diffs-tab %li.diffs-tab.active
= link_to url_for(params), data: {target: '#diffs', action: 'diffs', toggle: 'tab'} do = link_to url_for(params), data: {target: '#diffs', action: 'diffs', toggle: 'tab'} do
= icon('list-alt') = icon('list-alt')
Changes Changes
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
.tab-content .tab-content
#commits.commits.tab-pane #commits.commits.tab-pane
= render "projects/commits/commits", project: @project = render "projects/commits/commits", project: @project
#diffs.diffs.tab-pane #diffs.diffs.tab-pane.active
- if @diffs.present? - if @diffs.present?
= render "projects/diffs/diffs", diffs: @diffs, project: @project = render "projects/diffs/diffs", diffs: @diffs, project: @project
- elsif @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE - elsif @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE
......
= render "projects/commits/commits", project: @merge_request.source_project = render "projects/commits/commits", project: @merge_request.project
- if @merge_request_diff.collected? - if @merge_request_diff.collected?
= render "projects/diffs/diffs", diffs: @merge_request.diffs, project: @merge_request.source_project = render "projects/diffs/diffs", diffs: @merge_request.diffs, project: @merge_request.project
- elsif @merge_request_diff.empty? - elsif @merge_request_diff.empty?
.nothing-here-block Nothing to merge from #{@merge_request.source_branch} into #{@merge_request.target_branch} .nothing-here-block Nothing to merge from #{@merge_request.source_branch} into #{@merge_request.target_branch}
- else - else
......
...@@ -3,8 +3,6 @@ ...@@ -3,8 +3,6 @@
.mr-widget-body .mr-widget-body
- if @project.archived? - if @project.archived?
= render 'projects/merge_requests/widget/open/archived' = render 'projects/merge_requests/widget/open/archived'
- elsif !@project.satellite.exists?
= render 'projects/merge_requests/widget/open/no_satellite'
- elsif @merge_request.commits.blank? - elsif @merge_request.commits.blank?
= render 'projects/merge_requests/widget/open/nothing' = render 'projects/merge_requests/widget/open/nothing'
- elsif @merge_request.branch_missing? - elsif @merge_request.branch_missing?
......
...@@ -11,10 +11,10 @@ ...@@ -11,10 +11,10 @@
var merge_request_widget; var merge_request_widget;
merge_request_widget = new MergeRequestWidget({ merge_request_widget = new MergeRequestWidget({
url_to_automerge_check: "#{automerge_check_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}", url_to_automerge_check: "#{merge_check_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}",
check_enable: #{@merge_request.unchecked? ? "true" : "false"}, check_enable: #{@merge_request.unchecked? ? "true" : "false"},
url_to_ci_check: "#{ci_status_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}", url_to_ci_check: "#{ci_status_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}",
ci_enable: #{@project.ci_service ? "true" : "false"}, ci_enable: #{@project.ci_service ? "true" : "false"},
current_status: "#{@merge_request.automerge_status}", current_status: "#{@merge_request.gitlab_merge_status}",
}); });
= form_for [:automerge, @project.namespace.becomes(Namespace), @project, @merge_request], remote: true, method: :post, html: { class: 'accept-mr-form js-requires-input' } do |f| = form_for [:merge, @project.namespace.becomes(Namespace), @project, @merge_request], remote: true, method: :post, html: { class: 'accept-mr-form js-requires-input' } do |f|
= hidden_field_tag :authenticity_token, form_authenticity_token = hidden_field_tag :authenticity_token, form_authenticity_token
.accept-merge-holder.clearfix.js-toggle-container .accept-merge-holder.clearfix.js-toggle-container
.accept-action .accept-action
......
%p
%span
%strong This repository does not have a satellite. Please ask an administrator to fix this issue!
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
.col-sm-10 .col-sm-10
= f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user), {}, {class: 'select2', tabindex: 2} = f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user), {}, {class: 'select2', tabindex: 2}
- if import_sources_enabled?
%hr %hr
.project-import.js-toggle-container .project-import.js-toggle-container
...@@ -29,46 +30,50 @@ ...@@ -29,46 +30,50 @@
%label.control-label Import project from %label.control-label Import project from
.col-sm-10 .col-sm-10
- if github_import_enabled? - if github_import_enabled?
= link_to status_import_github_path, class: 'btn' do - if github_import_configured?
= link_to status_import_github_path, class: 'btn import_github' do
%i.fa.fa-github %i.fa.fa-github
GitHub GitHub
- else - else
= link_to '#', class: 'how_to_import_link light btn' do = link_to '#', class: 'how_to_import_link light btn import_github' do
%i.fa.fa-github %i.fa.fa-github
GitHub GitHub
= render 'github_import_modal' = render 'github_import_modal'
- if bitbucket_import_enabled? - if bitbucket_import_enabled?
= link_to status_import_bitbucket_path, class: 'btn', "data-no-turbolink" => "true" do - if bitbucket_import_configured?
= link_to status_import_bitbucket_path, class: 'btn import_bitbucket', "data-no-turbolink" => "true" do
%i.fa.fa-bitbucket %i.fa.fa-bitbucket
Bitbucket Bitbucket
- else - else
= link_to '#', class: 'how_to_import_link light btn' do = link_to status_import_bitbucket_path, class: 'how_to_import_link light btn import_bitbucket', "data-no-turbolink" => "true" do
%i.fa.fa-bitbucket %i.fa.fa-bitbucket
Bitbucket Bitbucket
= render 'bitbucket_import_modal' = render 'bitbucket_import_modal'
- unless request.host == 'gitlab.com'
- if gitlab_import_enabled? - if gitlab_import_enabled?
= link_to status_import_gitlab_path, class: 'btn' do - if gitlab_import_configured?
= link_to status_import_gitlab_path, class: 'btn import_gitlab' do
%i.fa.fa-heart %i.fa.fa-heart
GitLab.com GitLab.com
- else - else
= link_to '#', class: 'how_to_import_link light btn' do = link_to status_import_gitlab_path, class: 'how_to_import_link light btn import_gitlab' do
%i.fa.fa-heart %i.fa.fa-heart
GitLab.com GitLab.com
= render 'gitlab_import_modal' = render 'gitlab_import_modal'
= link_to new_import_gitorious_path, class: 'btn' do - if gitorious_import_enabled?
= link_to new_import_gitorious_path, class: 'btn import_gitorious' do
%i.icon-gitorious.icon-gitorious-small %i.icon-gitorious.icon-gitorious-small
Gitorious.org Gitorious.org
= link_to new_import_google_code_path, class: 'btn' do - if google_code_import_enabled?
= link_to new_import_google_code_path, class: 'btn import_google_code' do
%i.fa.fa-google %i.fa.fa-google
Google Code Google Code
= link_to "#", class: 'btn js-toggle-button' do - if git_import_enabled?
= link_to "#", class: 'btn js-toggle-button import_git' do
%i.fa.fa-git %i.fa.fa-git
%span Any repo by URL %span Any repo by URL
......
%ul.center-top-menu
= nav_link(page: user_snippets_path(current_user), html_options: {class: 'home'}) do
= link_to user_snippets_path(current_user), title: 'Your snippets', data: {placement: 'right'} do
Your Snippets
= nav_link(page: snippets_path) do
= link_to snippets_path, title: 'Explore snippets', data: {placement: 'right'} do
Explore Snippets
- page_title "Your Snippets" - page_title "Your Snippets"
%h3.page-title = render 'head'
Your Snippets
.pull-right
= link_to new_snippet_path, class: "btn btn-new btn-grouped", title: "New Snippet" do
Add new snippet
%p.light .slead
Share code pastes with others out of git repository Share code pastes with others out of git repository
.pull-right
= link_to new_snippet_path, class: "btn btn-new btn-sm", title: "New Snippet" do
Add new snippet
%ul.nav.nav-tabs %ul.nav.nav-tabs
= nav_tab :scope, nil do = nav_tab :scope, nil do
= link_to user_snippets_path(@user) do = link_to user_snippets_path(@user) do
......
- page_title "Public Snippets" - page_title "Public Snippets"
%h3.page-title - if current_user
Public snippets = render 'head'
.pull-right .slead
- if current_user
= link_to new_snippet_path, class: "btn btn-new btn-grouped", title: "New Snippet" do
Add new snippet
= link_to user_snippets_path(current_user), class: "btn btn-grouped" do
Your snippets
%p.light
Public snippets created by you and other users are listed here Public snippets created by you and other users are listed here
%hr
= render 'snippets' = render 'snippets'
- page_title @snippet.title, "Snippets" - page_title @snippet.title, "Snippets"
%h3.page-title %h4.page-title
= @snippet.title = @snippet.title
- if @snippet.private? - if @snippet.private?
...@@ -8,17 +8,14 @@ ...@@ -8,17 +8,14 @@
private private
.pull-right .pull-right
= link_to new_snippet_path, class: "btn btn-new", title: "New Snippet" do = link_to new_snippet_path, class: "btn btn-new btn-sm", title: "New Snippet" do
Add new snippet Add new snippet
%hr
.append-bottom-20 .append-bottom-10.prepend-top-10
.pull-right .pull-right
= "##{@snippet.id}"
%span.light %span.light
by created by
= link_to user_snippets_path(@snippet.author) do = link_to user_snippets_path(@snippet.author) do
= image_tag avatar_icon(@snippet.author_email), class: "avatar avatar-inline s16", alt: ''
= @snippet.author_name = @snippet.author_name
.back-link .back-link
...@@ -27,7 +24,7 @@ ...@@ -27,7 +24,7 @@
&larr; your snippets &larr; your snippets
- else - else
= link_to snippets_path do = link_to snippets_path do
&larr; discover snippets &larr; explore snippets
.file-holder .file-holder
.file-title .file-title
......
- page_title "Snippets", @user.name - page_title "Snippets", @user.name
%h3.page-title
= image_tag avatar_icon(@user.email), class: "avatar s24"
= @user.name
%span
\/
Snippets
- if current_user
= link_to new_snippet_path, class: "btn btn-sm add_new pull-right", title: "New Snippet" do
Add new snippet
%hr %ol.breadcrumb
%li
= link_to snippets_path do
Snippets
%li
= @user.name
.pull-right.hidden-xs
= link_to user_path(@user) do
#{@user.name} profile page
= render 'snippets' = render 'snippets'
class AutoMergeWorker class MergeWorker
include Sidekiq::Worker include Sidekiq::Worker
sidekiq_options queue: :default sidekiq_options queue: :default
...@@ -7,7 +7,13 @@ class AutoMergeWorker ...@@ -7,7 +7,13 @@ class AutoMergeWorker
params = params.with_indifferent_access params = params.with_indifferent_access
current_user = User.find(current_user_id) current_user = User.find(current_user_id)
merge_request = MergeRequest.find(merge_request_id) merge_request = MergeRequest.find(merge_request_id)
merge_request.should_remove_source_branch = params[:should_remove_source_branch]
merge_request.automerge!(current_user, params[:commit_message]) result = MergeRequests::MergeService.new(merge_request.target_project, current_user).
execute(merge_request, params[:commit_message])
if result[:status] == :success && params[:should_remove_source_branch].present?
DeleteBranchService.new(merge_request.source_project, current_user).
execute(merge_request.source_branch)
end
end end
end end
...@@ -27,7 +27,6 @@ class RepositoryImportWorker ...@@ -27,7 +27,6 @@ class RepositoryImportWorker
project.import_finish project.import_finish
project.save project.save
project.satellite.create unless project.satellite.exists?
ProjectCacheWorker.perform_async(project.id) ProjectCacheWorker.perform_async(project.id)
Gitlab::BitbucketImport::KeyDeleter.new(project).execute if project.import_type == 'bitbucket' Gitlab::BitbucketImport::KeyDeleter.new(project).execute if project.import_type == 'bitbucket'
end end
......
...@@ -148,6 +148,7 @@ Settings.gitlab.default_projects_features['snippets'] = false if Settings. ...@@ -148,6 +148,7 @@ Settings.gitlab.default_projects_features['snippets'] = false if Settings.
Settings.gitlab.default_projects_features['visibility_level'] = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE) Settings.gitlab.default_projects_features['visibility_level'] = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE)
Settings.gitlab['repository_downloads_path'] = File.absolute_path(Settings.gitlab['repository_downloads_path'] || 'tmp/repositories', Rails.root) Settings.gitlab['repository_downloads_path'] = File.absolute_path(Settings.gitlab['repository_downloads_path'] || 'tmp/repositories', Rails.root)
Settings.gitlab['restricted_signup_domains'] ||= [] Settings.gitlab['restricted_signup_domains'] ||= []
Settings.gitlab['import_sources'] ||= ['github','bitbucket','gitlab','gitorious','google_code','git']
# #
# Reply by email # Reply by email
......
...@@ -463,8 +463,8 @@ Gitlab::Application.routes.draw do ...@@ -463,8 +463,8 @@ Gitlab::Application.routes.draw do
member do member do
get :diffs get :diffs
get :commits get :commits
post :automerge post :merge
get :automerge_check get :merge_check
get :ci_status get :ci_status
post :toggle_subscription post :toggle_subscription
end end
......
require 'yaml'
class AddSettingsImportSources < ActiveRecord::Migration
def change
unless column_exists?(:application_settings, :import_sources)
add_column :application_settings, :import_sources, :text
import_sources = YAML::dump(Settings.gitlab['import_sources'])
execute("update application_settings set import_sources = '#{import_sources}'")
end
end
end
...@@ -44,6 +44,7 @@ ActiveRecord::Schema.define(version: 20150818213832) do ...@@ -44,6 +44,7 @@ ActiveRecord::Schema.define(version: 20150818213832) do
t.boolean "user_oauth_applications", default: true t.boolean "user_oauth_applications", default: true
t.string "after_sign_out_path" t.string "after_sign_out_path"
t.integer "session_expire_delay", default: 10080, null: false t.integer "session_expire_delay", default: 10080, null: false
t.text "import_sources"
end end
create_table "audit_events", force: true do |t| create_table "audit_events", force: true do |t|
......
...@@ -56,9 +56,9 @@ To serve repositories over SSH there's an add-on application called gitlab-shell ...@@ -56,9 +56,9 @@ To serve repositories over SSH there's an add-on application called gitlab-shell
A typical install of GitLab will be on GNU/Linux. It uses Nginx or Apache as a web front end to proxypass the Unicorn web server. By default, communication between Unicorn and the front end is via a Unix domain socket but forwarding requests via TCP is also supported. The web front end accesses `/home/git/gitlab/public` bypassing the Unicorn server to serve static pages, uploads (e.g. avatar images or attachments), and precompiled assets. GitLab serves web pages and a [GitLab API](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/api) using the Unicorn web server. It uses Sidekiq as a job queue which, in turn, uses redis as a non-persistent database backend for job information, meta data, and incoming jobs. A typical install of GitLab will be on GNU/Linux. It uses Nginx or Apache as a web front end to proxypass the Unicorn web server. By default, communication between Unicorn and the front end is via a Unix domain socket but forwarding requests via TCP is also supported. The web front end accesses `/home/git/gitlab/public` bypassing the Unicorn server to serve static pages, uploads (e.g. avatar images or attachments), and precompiled assets. GitLab serves web pages and a [GitLab API](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/api) using the Unicorn web server. It uses Sidekiq as a job queue which, in turn, uses redis as a non-persistent database backend for job information, meta data, and incoming jobs.
The GitLab web app uses MySQL or PostgreSQL for persistent database information (e.g. users, permissions, issues, other meta data). GitLab stores the bare git repositories it serves in `/home/git/repositories` by default. It also keeps default branch and hook information with the bare repository. `/home/git/gitlab-satellites` keeps checked out repositories when performing actions such as a merge request, editing files in the web interface, etc. The GitLab web app uses MySQL or PostgreSQL for persistent database information (e.g. users, permissions, issues, other meta data). GitLab stores the bare git repositories it serves in `/home/git/repositories` by default. It also keeps default branch and hook information with the bare repository.
The satellite repository is used by the web interface for editing repositories and the wiki which is also a git repository. When serving repositories over HTTP/HTTPS GitLab utilizes the GitLab API to resolve authorization and access as well as serving git objects. When serving repositories over HTTP/HTTPS GitLab utilizes the GitLab API to resolve authorization and access as well as serving git objects.
The add-on component gitlab-shell serves repositories over SSH. It manages the SSH keys within `/home/git/.ssh/authorized_keys` which should not be manually edited. gitlab-shell accesses the bare repositories directly to serve git objects and communicates with redis to submit jobs to Sidekiq for GitLab to process. gitlab-shell queries the GitLab API to determine authorization and access. The add-on component gitlab-shell serves repositories over SSH. It manages the SSH keys within `/home/git/.ssh/authorized_keys` which should not be manually edited. gitlab-shell accesses the bare repositories directly to serve git objects and communicates with redis to submit jobs to Sidekiq for GitLab to process. gitlab-shell queries the GitLab API to determine authorization and access.
...@@ -129,7 +129,7 @@ Note: `/home/git/` is shorthand for `/home/git`. ...@@ -129,7 +129,7 @@ Note: `/home/git/` is shorthand for `/home/git`.
gitlabhq (includes Unicorn and Sidekiq logs) gitlabhq (includes Unicorn and Sidekiq logs)
- `/home/git/gitlab/log/` contains `application.log`, `production.log`, `sidekiq.log`, `unicorn.stdout.log`, `githost.log`, `satellites.log`, and `unicorn.stderr.log` normally. - `/home/git/gitlab/log/` contains `application.log`, `production.log`, `sidekiq.log`, `unicorn.stdout.log`, `githost.log` and `unicorn.stderr.log` normally.
gitlab-shell gitlab-shell
......
...@@ -216,10 +216,6 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da ...@@ -216,10 +216,6 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
sudo chmod -R u+rwX,go-w log/ sudo chmod -R u+rwX,go-w log/
sudo chmod -R u+rwX tmp/ sudo chmod -R u+rwX tmp/
# Create directory for satellites
sudo -u git -H mkdir /home/git/gitlab-satellites
sudo chmod u+rwx,g=rx,o-rwx /home/git/gitlab-satellites
# Make sure GitLab can write to the tmp/pids/ and tmp/sockets/ directories # Make sure GitLab can write to the tmp/pids/ and tmp/sockets/ directories
sudo chmod -R u+rwX tmp/pids/ sudo chmod -R u+rwX tmp/pids/
sudo chmod -R u+rwX tmp/sockets/ sudo chmod -R u+rwX tmp/sockets/
......
...@@ -40,7 +40,7 @@ We love [JRuby](http://jruby.org/) and [Rubinius](http://rubini.us/) but GitLab ...@@ -40,7 +40,7 @@ We love [JRuby](http://jruby.org/) and [Rubinius](http://rubini.us/) but GitLab
### Storage ### Storage
The necessary hard drive space largely depends on the size of the repos you want to store in GitLab but as a *rule of thumb* you should have at least twice as much free space as all your repos combined take up. You need twice the storage because [GitLab satellites](structure.md) contain an extra copy of each repo. The necessary hard drive space largely depends on the size of the repos you want to store in GitLab but as a *rule of thumb* you should have at least as much free space as all your repos combined take up.
If you want to be flexible about growing your hard drive space in the future consider mounting it using LVM so you can add more hard drives when you need them. If you want to be flexible about growing your hard drive space in the future consider mounting it using LVM so you can add more hard drives when you need them.
......
...@@ -6,16 +6,14 @@ This is the directory structure you will end up with following the instructions ...@@ -6,16 +6,14 @@ This is the directory structure you will end up with following the instructions
| |-- git | |-- git
| |-- .ssh | |-- .ssh
| |-- gitlab | |-- gitlab
| |-- gitlab-satellites
| |-- gitlab-shell | |-- gitlab-shell
| |-- repositories | |-- repositories
* `/home/git/.ssh` - contains openssh settings. Specifically the `authorized_keys` file managed by gitlab-shell. * `/home/git/.ssh` - contains openssh settings. Specifically the `authorized_keys` file managed by gitlab-shell.
* `/home/git/gitlab` - GitLab core software. * `/home/git/gitlab` - GitLab core software.
* `/home/git/gitlab-satellites` - checked out repositories for merge requests and file editing from web UI. This can be treated as a temporary files directory.
* `/home/git/gitlab-shell` - Core add-on component of GitLab. Maintains SSH cloning and other functionality. * `/home/git/gitlab-shell` - Core add-on component of GitLab. Maintains SSH cloning and other functionality.
* `/home/git/repositories` - bare repositories for all projects organized by namespace. This is where the git repositories which are pushed/pulled are maintained for all projects. **This area is critical data for projects. [Keep a backup](../raketasks/backup_restore.md)** * `/home/git/repositories` - bare repositories for all projects organized by namespace. This is where the git repositories which are pushed/pulled are maintained for all projects. **This area is critical data for projects. [Keep a backup](../raketasks/backup_restore.md)**
*Note: the default locations for gitlab-satellites and repositories can be configured in `config/gitlab.yml` of GitLab and `config.yml` of gitlab-shell.* *Note: the default locations for repositories can be configured in `config/gitlab.yml` of GitLab and `config.yml` of gitlab-shell.*
To see a more in-depth overview see the [GitLab architecture doc](../development/architecture.md). To see a more in-depth overview see the [GitLab architecture doc](../development/architecture.md).
...@@ -51,16 +51,6 @@ December 03, 2014 13:20 -> ERROR -> Command failed [1]: /usr/bin/git --git-dir=/ ...@@ -51,16 +51,6 @@ December 03, 2014 13:20 -> ERROR -> Command failed [1]: /usr/bin/git --git-dir=/
error: failed to push some refs to '/Users/vsizov/gitlab-development-kit/repositories/gitlabhq/gitlab_git.git' error: failed to push some refs to '/Users/vsizov/gitlab-development-kit/repositories/gitlabhq/gitlab_git.git'
``` ```
#### satellites.log
This file lives in `/var/log/gitlab/gitlab-rails/satellites.log` for omnibus package or in `/home/git/gitlab/log/satellites.log` for installations from the source.
In some cases GitLab should perform write actions to git repository, for example when it is needed to merge the merge request or edit a file with online editor. If something went wrong you can look into this file to find out what exactly happened.
```
October 07, 2014 11:36: Failed to create satellite for Chesley Weimann III / project1817
October 07, 2014 11:36: PID: 1872: git clone /Users/vsizov/gitlab-development-kit/gitlab/tmp/tests/repositories/conrad6841/gitlabhq.git /Users/vsizov/gitlab-development-kit/gitlab/tmp/tests/gitlab-satellites/conrad6841/gitlabhq
October 07, 2014 11:36: PID: 1872: -> fatal: repository '/Users/vsizov/gitlab-development-kit/gitlab/tmp/tests/repositories/conrad6841/gitlabhq.git' does not exist
```
#### sidekiq.log #### sidekiq.log
This file lives in `/var/log/gitlab/gitlab-rails/sidekiq.log` for omnibus package or in `/home/git/gitlab/log/sidekiq.log` for installations from the source. This file lives in `/var/log/gitlab/gitlab-rails/sidekiq.log` for omnibus package or in `/home/git/gitlab/log/sidekiq.log` for installations from the source.
......
...@@ -8,6 +8,10 @@ your phone. ...@@ -8,6 +8,10 @@ your phone.
By enabling 2FA, the only way someone other than you can log into your account By enabling 2FA, the only way someone other than you can log into your account
is to know your username and password *and* have access to your phone. is to know your username and password *and* have access to your phone.
#### Note
When you enable 2FA, don't forget to back up your recovery codes. For your safety, if you
lose your codes for GitLab.com, we can't disable or recover them.
## Enabling 2FA ## Enabling 2FA
**In GitLab:** **In GitLab:**
......
...@@ -105,24 +105,11 @@ Log directory writable? ... yes ...@@ -105,24 +105,11 @@ Log directory writable? ... yes
Tmp directory writable? ... yes Tmp directory writable? ... yes
Init script exists? ... yes Init script exists? ... yes
Init script up-to-date? ... yes Init script up-to-date? ... yes
Projects have satellites? ... yes
Redis version >= 2.0.0? ... yes Redis version >= 2.0.0? ... yes
Checking GitLab ... Finished Checking GitLab ... Finished
``` ```
## (Re-)Create satellite repositories
This will create satellite repositories for all your projects.
If necessary, remove the `repo_satellites` directory and rerun the commands below.
```
sudo -u git -H mkdir -p /home/git/gitlab-satellites
sudo -u git -H bundle exec rake gitlab:satellites:create RAILS_ENV=production
sudo chmod u+rwx,g=rx,o-rwx /home/git/gitlab-satellites
```
## Rebuild authorized_keys file ## Rebuild authorized_keys file
In some case it is necessary to rebuild the `authorized_keys` file. In some case it is necessary to rebuild the `authorized_keys` file.
......
...@@ -156,6 +156,7 @@ Tweet about the RC release: ...@@ -156,6 +156,7 @@ Tweet about the RC release:
1. Also check the CI changelog 1. Also check the CI changelog
1. Add a proposed tweet text to the blog post WIP MR description. 1. Add a proposed tweet text to the blog post WIP MR description.
1. Create a WIP MR for the blog post 1. Create a WIP MR for the blog post
1. Make sure merge request title starts with `WIP` so it can not be accidently merged until ready.
1. Ask Dmitriy (or a team member with OS X) to add screenshots to the WIP MR. 1. Ask Dmitriy (or a team member with OS X) to add screenshots to the WIP MR.
1. Decide with core team who will be the MVP user. 1. Decide with core team who will be the MVP user.
1. Create WIP MR for adding MVP to MVP page on website 1. Create WIP MR for adding MVP to MVP page on website
......
...@@ -13,4 +13,5 @@ ...@@ -13,4 +13,5 @@
- [Project users](add-user/add-user.md) - [Project users](add-user/add-user.md)
- [Protected branches](protected_branches.md) - [Protected branches](protected_branches.md)
- [Web Editor](web_editor.md) - [Web Editor](web_editor.md)
- [Merge Requests](merge_requests.md)
- ["Work In Progress" Merge Requests](wip_merge_requests.md) - ["Work In Progress" Merge Requests](wip_merge_requests.md)
# Merge Requests
Merge requests allow you to exchange changes you made to source code
## Checkout merge requests locally
Locate the section for your GitLab remote in the `.git/config` file. It looks like this:
```
[remote "origin"]
url = https://gitlab.com/gitlab-org/gitlab-ce.git
fetch = +refs/heads/*:refs/remotes/origin/*
```
Now add the line `fetch = +refs/merge-requests/*/head:refs/remotes/origin/merge-requests/*` to this section.
It should looks like this:
```
[remote "origin"]
url = https://gitlab.com/gitlab-org/gitlab-ce.git
fetch = +refs/heads/*:refs/remotes/origin/*
fetch = +refs/merge-requests/*/head:refs/remotes/origin/merge-requests/*
```
Now you can fetch all the merge requests requests:
```
$ git fetch origin
From https://gitlab.com/gitlab-org/gitlab-ce.git
* [new ref] refs/merge-requests/1/head -> origin/merge-requests/1
* [new ref] refs/merge-requests/2/head -> origin/merge-requests/2
...
```
To check out a particular merge request:
```
$ git checkout origin/merge-requests/1
```
...@@ -4,9 +4,18 @@ Feature: Admin Projects ...@@ -4,9 +4,18 @@ Feature: Admin Projects
Given I sign in as an admin Given I sign in as an admin
And there are projects in system And there are projects in system
Scenario: Projects list Scenario: I should see non-archived projects in the list
Given archived project "Archive"
When I visit admin projects page When I visit admin projects page
Then I should see all non-archived projects
And I should not see project "Archive"
Scenario: I should see all projects in the list
Given archived project "Archive"
When I visit admin projects page
And I check "Show archived projects"
Then I should see all projects Then I should see all projects
And I should see "archived" label
Scenario: Projects show Scenario: Projects show
When I visit admin projects page When I visit admin projects page
......
...@@ -4,10 +4,27 @@ Background: ...@@ -4,10 +4,27 @@ Background:
Given I sign in as a user Given I sign in as a user
And I own project "Shop" And I own project "Shop"
And I visit dashboard page And I visit dashboard page
And I click "New project" link
@javascript @javascript
Scenario: I should see New projects page Scenario: I should see New projects page
Given I click "New project" link
Then I see "New project" page Then I see "New project" page
Then I see all possible import optios
@javascript
Scenario: I should see instructions on how to import from Git URL
Given I see "New project" page
When I click on "Any repo by URL"
Then I see instructions on how to import from Git URL
@javascript
Scenario: I should see instructions on how to import from GitHub
Given I see "New project" page
When I click on "Import project from GitHub" When I click on "Import project from GitHub"
Then I see instructions on how to import from GitHub Then I see instructions on how to import from GitHub
@javascript
Scenario: I should see Google Code import page
Given I see "New project" page
When I click on "Google Code"
Then I redirected to Google Code import page
...@@ -6,10 +6,12 @@ Feature: Explore Projects ...@@ -6,10 +6,12 @@ Feature: Explore Projects
And private project "Enterprise" And private project "Enterprise"
Scenario: I visit public area Scenario: I visit public area
Given archived project "Archive"
When I visit the public projects area When I visit the public projects area
Then I should see project "Community" Then I should see project "Community"
And I should not see project "Internal" And I should not see project "Internal"
And I should not see project "Enterprise" And I should not see project "Enterprise"
And I should not see project "Archive"
Scenario: I visit public project page Scenario: I visit public project page
When I visit project "Community" page When I visit project "Community" page
...@@ -37,11 +39,13 @@ Feature: Explore Projects ...@@ -37,11 +39,13 @@ Feature: Explore Projects
And I should see empty public project details with ssh clone info And I should see empty public project details with ssh clone info
Scenario: I visit public area as user Scenario: I visit public area as user
Given I sign in as a user Given archived project "Archive"
And I sign in as a user
When I visit the public projects area When I visit the public projects area
Then I should see project "Community" Then I should see project "Community"
And I should see project "Internal" And I should see project "Internal"
And I should not see project "Enterprise" And I should not see project "Enterprise"
And I should not see project "Archive"
Scenario: I visit internal project page as user Scenario: I visit internal project page as user
Given I sign in as a user Given I sign in as a user
...@@ -102,15 +106,20 @@ Feature: Explore Projects ...@@ -102,15 +106,20 @@ Feature: Explore Projects
Then I should see list of merge requests for "Internal" project Then I should see list of merge requests for "Internal" project
Scenario: Trending page Scenario: Trending page
Given I sign in as a user Given archived project "Archive"
And project "Archive" has comments
And I sign in as a user
And project "Community" has comments And project "Community" has comments
When I visit the explore trending projects When I visit the explore trending projects
Then I should see project "Community" Then I should see project "Community"
And I should not see project "Internal" And I should not see project "Internal"
And I should not see project "Enterprise" And I should not see project "Enterprise"
And I should not see project "Archive"
Scenario: Most starred page Scenario: Most starred page
Given I sign in as a user Given archived project "Archive"
And I sign in as a user
When I visit the explore starred projects When I visit the explore starred projects
Then I should see project "Community" Then I should see project "Community"
And I should see project "Internal" And I should see project "Internal"
And I should see project "Archive"
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment