Commit 44cf1cba authored by Valery Sizov's avatar Valery Sizov

Merge remote-tracking branch 'origin/master' into emoji-picker-search

parents 6d070547 d1043c43
......@@ -172,7 +172,7 @@ gem 'd3_rails', '~> 3.5.5'
gem "cal-heatmap-rails", "~> 0.0.1"
# underscore-rails
gem "underscore-rails", "~> 1.4.4"
gem "underscore-rails", "~> 1.8.0"
# Sanitize user input
gem "sanitize", '~> 2.0'
......
......@@ -769,7 +769,7 @@ GEM
uglifier (2.7.2)
execjs (>= 0.3.0)
json (>= 1.8.0)
underscore-rails (1.4.4)
underscore-rails (1.8.3)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.1)
......@@ -965,7 +965,7 @@ DEPENDENCIES
tinder (~> 1.10.0)
turbolinks (~> 2.5.0)
uglifier (~> 2.7.2)
underscore-rails (~> 1.4.4)
underscore-rails (~> 1.8.0)
unf (~> 0.1.4)
unicorn (~> 4.8.2)
unicorn-worker-killer (~> 0.4.2)
......
......@@ -35,7 +35,7 @@ class @BlobFileDropzone
return
this.on 'sending', (file, xhr, formData) ->
formData.append('new_branch', form.find('.js-new-branch').val())
formData.append('target_branch', form.find('.js-target-branch').val())
formData.append('create_merge_request', form.find('.js-create-merge-request').val())
formData.append('commit_message', form.find('.js-commit-message').val())
return
......
......@@ -18,7 +18,7 @@ class @MergeRequestWidget
if data.state == "merged"
urlSuffix = if deleteSourceBranch then '?delete_source=true' else ''
window.location.href = window.location.href + urlSuffix
window.location.href = window.location.pathname + urlSuffix
else if data.merge_error
$('.mr-widget-body').html("<h4>" + data.merge_error + "</h4>")
else
......
class @NewBranchForm
constructor: (form, availableRefs) ->
@branchNameError = form.find('.js-branch-name-error')
@name = form.find('.js-branch-name')
@ref = form.find('#ref')
@setupAvailableRefs(availableRefs)
@setupRestrictions()
@addBinding()
@init()
addBinding: ->
@name.on 'blur', @validate
init: ->
@name.trigger 'blur' if @name.val().length > 0
setupAvailableRefs: (availableRefs) ->
@ref.autocomplete
source: availableRefs,
minLength: 1
setupRestrictions: ->
startsWith = {
pattern: /^(\/|\.)/g,
prefix: "can't start with",
conjunction: "or"
}
endsWith = {
pattern: /(\/|\.|\.lock)$/g,
prefix: "can't end in",
conjunction: "or"
}
invalid = {
pattern: /(\s|~|\^|:|\?|\*|\[|\\|\.\.|@\{|\/{2,}){1}/g
prefix: "can't contain",
conjunction: ", "
}
single = {
pattern: /^@+$/g
prefix: "can't be",
conjunction: "or"
}
@restrictions = [startsWith, invalid, endsWith, single]
validate: =>
@branchNameError.empty()
unique = (values, value) ->
values.push(value) unless value in values
values
formatter = (values, restriction) ->
formatted = values.map (value) ->
switch
when /\s/.test value then 'spaces'
when /\/{2,}/g.test value then 'consecutive slashes'
else "'#{value}'"
"#{restriction.prefix} #{formatted.join(restriction.conjunction)}"
validator = (errors, restriction) =>
matched = @name.val().match(restriction.pattern)
if matched
errors.concat formatter(matched.reduce(unique, []), restriction)
else
errors
errors = @restrictions.reduce validator, []
if errors.length > 0
errorMessage = $("<span/>").text(errors.join(', '))
@branchNameError.append(errorMessage)
class @NewCommitForm
constructor: (form) ->
@newBranch = form.find('.js-new-branch')
@newBranch = form.find('.js-target-branch')
@originalBranch = form.find('.js-original-branch')
@createMergeRequest = form.find('.js-create-merge-request')
@createMergeRequestContainer = form.find('.js-create-merge-request-container')
......
......@@ -8,17 +8,17 @@ class @ProjectsList
$(".projects-list-filter").keyup ->
terms = $(this).val()
uiBox = $(this).closest('.projects-list-holder')
uiBox = $('div.projects-list-holder')
if terms == "" || terms == undefined
uiBox.find(".projects-list li").show()
uiBox.find("ul.projects-list li").show()
else
uiBox.find(".projects-list li").each (index) ->
name = $(this).find(".filter-title").text()
uiBox.find("ul.projects-list li").each (index) ->
name = $(this).find("span.filter-title").text()
if name.toLowerCase().search(terms.toLowerCase()) == -1
$(this).hide()
else
$(this).show()
uiBox.find(".projects-list li.bottom").hide()
uiBox.find("ul.projects-list li.bottom").hide()
......@@ -337,6 +337,8 @@ ul.nav.nav-projects-tabs {
.top-area {
border-bottom: 1px solid #EEE;
margin: 0 -16px;
padding: 0 $gl-padding;
ul.left-top-menu {
display: inline-block;
......
module CreatesCommit
extend ActiveSupport::Concern
def create_commit(service, success_path:, failure_path:, failure_view: nil, success_notice: nil)
set_commit_variables
commit_params = @commit_params.merge(
source_project: @project,
source_branch: @ref,
target_branch: @target_branch
)
result = service.new(@tree_edit_project, current_user, commit_params).execute
if result[:status] == :success
flash[:notice] = success_notice || "Your changes have been successfully committed."
if create_merge_request?
success_path = new_merge_request_path
target = different_project? ? "project" : "branch"
flash[:notice] << " You can now submit a merge request to get this change into the original #{target}."
end
respond_to do |format|
format.html { redirect_to success_path }
format.json { render json: { message: "success", filePath: success_path } }
end
else
flash[:alert] = result[:message]
respond_to do |format|
format.html do
if failure_view
render failure_view
else
redirect_to failure_path
end
end
format.json { render json: { message: "failed", filePath: failure_path } }
end
end
end
def authorize_edit_tree!
return if can?(current_user, :push_code, project)
return if current_user && current_user.already_forked?(project)
access_denied!
end
private
def new_merge_request_path
new_namespace_project_merge_request_path(
@mr_source_project.namespace,
@mr_source_project,
merge_request: {
source_project_id: @mr_source_project.id,
target_project_id: @mr_target_project.id,
source_branch: @mr_source_branch,
target_branch: @mr_target_branch
}
)
end
def different_project?
@mr_source_project != @mr_target_project
end
def different_branch?
@mr_source_branch != @mr_target_branch || different_project?
end
def create_merge_request?
params[:create_merge_request].present? && different_branch?
end
def set_commit_variables
@mr_source_branch = @target_branch
if can?(current_user, :push_code, @project)
# Edit file in this project
@tree_edit_project = @project
@mr_source_project = @project
if @project.forked?
# Merge request from this project to fork origin
@mr_target_project = @project.forked_from_project
@mr_target_branch = @mr_target_project.repository.root_ref
else
# Merge request to this project
@mr_target_project = @project
@mr_target_branch = @ref
end
else
# Edit file in fork
@tree_edit_project = current_user.fork_of(@project)
# Merge request from fork to this project
@mr_source_project = @tree_edit_project
@mr_target_project = @project
@mr_target_branch = @mr_target_project.repository.root_ref
end
end
end
module CreatesMergeRequestForCommit
extend ActiveSupport::Concern
def new_merge_request_path
if @project.forked?
target_project = @project.forked_from_project || @project
target_branch = target_project.repository.root_ref
else
target_project = @project
target_branch = @ref
end
new_namespace_project_merge_request_path(
@project.namespace,
@project,
merge_request: {
source_project_id: @project.id,
target_project_id: target_project.id,
source_branch: @new_branch,
target_branch: target_branch
}
)
end
def create_merge_request?
params[:create_merge_request] && @new_branch != @ref
end
end
# Controller for viewing a file's blame
class Projects::BlobController < Projects::ApplicationController
include ExtractsPath
include CreatesMergeRequestForCommit
include CreatesCommit
include ActionView::Helpers::SanitizeHelper
# Raised when given an invalid file path
......@@ -9,21 +9,21 @@ class Projects::BlobController < Projects::ApplicationController
before_action :require_non_empty_project, except: [:new, :create]
before_action :authorize_download_code!
before_action :authorize_push_code!, only: [:destroy, :create]
before_action :authorize_edit_tree!, only: [:new, :create, :edit, :update, :destroy]
before_action :assign_blob_vars
before_action :commit, except: [:new, :create]
before_action :blob, except: [:new, :create]
before_action :from_merge_request, 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
commit unless @repository.empty?
end
def create
create_commit(Files::CreateService, success_path: after_create_path,
create_commit(Files::CreateService, success_notice: "The file has been successfully created.",
success_path: namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)),
failure_view: :new,
failure_path: namespace_project_new_blob_path(@project.namespace, @project, @ref))
end
......@@ -36,6 +36,14 @@ class Projects::BlobController < Projects::ApplicationController
end
def update
after_edit_path =
if from_merge_request && @target_branch == @ref
diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) +
"#file-path-#{hexdigest(@path)}"
else
namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @path))
end
create_commit(Files::UpdateService, success_path: after_edit_path,
failure_view: :edit,
failure_path: namespace_project_blob_path(@project.namespace, @project, @id))
......@@ -50,15 +58,10 @@ class Projects::BlobController < Projects::ApplicationController
end
def destroy
result = Files::DeleteService.new(@project, current_user, @commit_params).execute
if result[:status] == :success
flash[:notice] = "Your changes have been successfully committed"
redirect_to after_destroy_path
else
flash[:alert] = result[:message]
render :show
end
create_commit(Files::DeleteService, success_notice: "The file has been successfully deleted.",
success_path: namespace_project_tree_path(@project.namespace, @project, @target_branch),
failure_view: :show,
failure_path: namespace_project_blob_path(@project.namespace, @project, @id))
end
def diff
......@@ -108,74 +111,13 @@ class Projects::BlobController < Projects::ApplicationController
render_404
end
def create_commit(service, success_path:, failure_view:, failure_path:)
result = service.new(@project, current_user, @commit_params).execute
if result[:status] == :success
flash[:notice] = "Your changes have been successfully committed"
respond_to do |format|
format.html { redirect_to success_path }
format.json { render json: { message: "success", filePath: success_path } }
end
else
flash[:alert] = result[:message]
respond_to do |format|
format.html { render failure_view }
format.json { render json: { message: "failed", filePath: failure_path } }
end
end
end
def after_create_path
@after_create_path ||=
if create_merge_request?
new_merge_request_path
else
namespace_project_blob_path(@project.namespace, @project, File.join(@new_branch, @file_path))
end
end
def after_edit_path
@after_edit_path ||=
if create_merge_request?
new_merge_request_path
elsif from_merge_request && @new_branch == @ref
diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) +
"#file-path-#{hexdigest(@path)}"
else
namespace_project_blob_path(@project.namespace, @project, File.join(@new_branch, @path))
end
end
def after_destroy_path
@after_destroy_path ||=
if create_merge_request?
new_merge_request_path
else
namespace_project_tree_path(@project.namespace, @project, @new_branch)
end
end
def from_merge_request
# If blob edit was initiated from merge request page
@from_merge_request ||= MergeRequest.find_by(id: params[:from_merge_request_id])
end
def sanitized_new_branch_name
sanitize(strip_tags(params[:new_branch]))
end
def editor_variables
@current_branch = @ref
@new_branch =
if params[:new_branch].present?
sanitized_new_branch_name
elsif ::Gitlab::GitAccess.new(current_user, @project).can_push_to_branch?(@ref)
@ref
else
@repository.next_patch_branch
end
@target_branch = params[:target_branch]
@file_path =
if action_name.to_s == 'create'
......@@ -194,8 +136,6 @@ class Projects::BlobController < Projects::ApplicationController
@commit_params = {
file_path: @file_path,
current_branch: @current_branch,
target_branch: @new_branch,
commit_message: params[:commit_message],
file_content: params[:content],
file_content_encoding: params[:encoding]
......
......@@ -10,19 +10,35 @@ class Projects::ForksController < Projects::ApplicationController
def create
namespace = Namespace.find(params[:namespace_key])
@forked_project = ::Projects::ForkService.new(project, current_user, namespace: namespace).execute
@forked_project = namespace.projects.find_by(path: project.path)
@forked_project = nil unless @forked_project && @forked_project.forked_from_project == project
@forked_project ||= ::Projects::ForkService.new(project, current_user, namespace: namespace).execute
if @forked_project.saved? && @forked_project.forked?
if @forked_project.import_in_progress?
redirect_to namespace_project_import_path(@forked_project.namespace, @forked_project)
redirect_to namespace_project_import_path(@forked_project.namespace, @forked_project, continue: continue_params)
else
redirect_to(
namespace_project_path(@forked_project.namespace, @forked_project),
notice: 'Project was successfully forked.'
)
if continue_params
redirect_to continue_params[:to], notice: continue_params[:notice]
else
redirect_to namespace_project_path(@forked_project.namespace, @forked_project), notice: "The project was successfully forked."
end
end
else
render :error
end
end
private
def continue_params
continue_params = params[:continue]
if continue_params
continue_params.permit(:to, :notice, :notice_now)
else
nil
end
end
end
class Projects::ImportsController < Projects::ApplicationController
# Authorize
before_action :authorize_admin_project!
before_action :require_no_repo
before_action :require_no_repo, except: :show
before_action :redirect_if_progress, except: :show
def new
......@@ -24,21 +24,36 @@ class Projects::ImportsController < Projects::ApplicationController
end
def show
unless @project.import_in_progress?
if @project.import_finished?
redirect_to(project_path(@project)) and return
if @project.repository_exists? || @project.import_finished?
if continue_params
redirect_to continue_params[:to], notice: continue_params[:notice]
else
redirect_to(new_namespace_project_import_path(@project.namespace,
@project)) and return
redirect_to project_path(@project), notice: "The project was successfully forked."
end
elsif @project.import_failed?
redirect_to new_namespace_project_import_path(@project.namespace, @project)
else
if continue_params && continue_params[:notice_now]
flash.now[:notice] = continue_params[:notice_now]
end
# Render
end
end
private
def continue_params
continue_params = params[:continue]
if continue_params
continue_params.permit(:to, :notice, :notice_now)
else
nil
end
end
def require_no_repo
if @project.repository_exists? && !@project.import_in_progress?
redirect_to(namespace_project_path(@project.namespace, @project)) and return
redirect_to(namespace_project_path(@project.namespace, @project))
end
end
......
# Controller for viewing a repository's file structure
class Projects::TreeController < Projects::ApplicationController
include ExtractsPath
include CreatesMergeRequestForCommit
include CreatesCommit
include ActionView::Helpers::SanitizeHelper
before_action :require_non_empty_project, except: [:new, :create]
before_action :assign_ref_vars
before_action :assign_dir_vars, only: [:create_dir]
before_action :authorize_download_code!
before_action :authorize_push_code!, only: [:create_dir]
before_action :authorize_edit_tree!, only: [:create_dir]
def show
return render_404 unless @repository.commit(@ref)
......@@ -34,44 +34,20 @@ class Projects::TreeController < Projects::ApplicationController
def create_dir
return render_404 unless @commit_params.values.all?
begin
result = Files::CreateDirService.new(@project, current_user, @commit_params).execute
message = result[:message]
rescue => e
message = e.to_s
end
if result && result[:status] == :success
flash[:notice] = "The directory has been successfully created"
respond_to do |format|
format.html { redirect_to after_create_dir_path }
end
else
flash[:alert] = message
respond_to do |format|
format.html { redirect_to namespace_project_blob_path(@project.namespace, @project, @new_branch) }
end
end
create_commit(Files::CreateDirService, success_notice: "The directory has been successfully created.",
success_path: namespace_project_tree_path(@project.namespace, @project, File.join(@target_branch, @dir_name)),
failure_path: namespace_project_tree_path(@project.namespace, @project, @ref))
end
private
def assign_dir_vars
@new_branch = params[:new_branch].present? ? sanitize(strip_tags(params[:new_branch])) : @ref
@target_branch = params[:target_branch]
@dir_name = File.join(@path, params[:dir_name])
@commit_params = {
file_path: @dir_name,
current_branch: @ref,
target_branch: @new_branch,
commit_message: params[:commit_message],
}
end
def after_create_dir_path
if create_merge_request?
new_merge_request_path
else
namespace_project_blob_path(@project.namespace, @project, File.join(@new_branch, @dir_name))
end
end
end
......@@ -22,32 +22,90 @@ module BlobHelper
%w(credits changelog news copying copyright license authors)
end
def edit_blob_link(project, ref, path, options = {})
blob =
begin
project.repository.blob_at(ref, path)
rescue
nil
end
return unless blob && blob.text? && blob_editable?(blob)
text = 'Edit'
after = options[:after] || ''
def edit_blob_link(project = @project, ref = @ref, path = @path, options = {})
return unless current_user
blob = project.repository.blob_at(ref, path) rescue nil
return unless blob && blob_text_viewable?(blob)
from_mr = options[:from_merge_request_id]
link_opts = {}
link_opts[:from_merge_request_id] = from_mr if from_mr
cls = 'btn btn-small'
link_to(text,
namespace_project_edit_blob_path(project.namespace, project,
tree_join(ref, path),
link_opts),
class: cls
) + after.html_safe
edit_path = namespace_project_edit_blob_path(project.namespace, project,
tree_join(ref, path),
link_opts)
if !on_top_of_branch?
button_tag "Edit", class: "btn btn-default disabled has_tooltip", title: "You can only edit files when you are on a branch", data: { container: 'body' }
elsif can_edit_blob?(blob)
link_to "Edit", edit_path, class: 'btn btn-small'
elsif can?(current_user, :fork_project, project)
continue_params = {
to: edit_path,
notice: edit_in_new_fork_notice,
notice_now: edit_in_new_fork_notice_now
}
fork_path = namespace_project_fork_path(project.namespace, project, namespace_key: current_user.namespace.id,
continue: continue_params)
link_to "Edit", fork_path, class: 'btn btn-small', method: :post
end
end
def modify_file_link(project = @project, ref = @ref, path = @path, label:, action:, btn_class:, modal_type:)
return unless current_user
blob = project.repository.blob_at(ref, path) rescue nil
return unless blob
if !on_top_of_branch?
button_tag label, class: "btn btn-#{btn_class} disabled has_tooltip", title: "You can only #{action} files when you are on a branch", data: { container: 'body' }
elsif blob.lfs_pointer?
button_tag label, class: "btn btn-#{btn_class} disabled has_tooltip", title: "It is not possible to #{action} files that are stored in LFS using the web interface", data: { container: 'body' }
elsif can_edit_blob?(blob)
button_tag label, class: "btn btn-#{btn_class}", 'data-target' => "#modal-#{modal_type}-blob", 'data-toggle' => 'modal'
elsif can?(current_user, :fork_project, project)
continue_params = {
to: request.fullpath,
notice: edit_in_new_fork_notice + " Try to #{action} this file again.",
notice_now: edit_in_new_fork_notice_now
}
fork_path = namespace_project_fork_path(project.namespace, project, namespace_key: current_user.namespace.id,
continue: continue_params)
link_to label, fork_path, class: "btn btn-#{btn_class}", method: :post
end
end
def replace_blob_link(project = @project, ref = @ref, path = @path)
modify_file_link(
project,
ref,
path,
label: "Replace",
action: "replace",
btn_class: "default",
modal_type: "upload"
)
end
def delete_blob_link(project = @project, ref = @ref, path = @path)
modify_file_link(
project,
ref,
path,
label: "Delete",
action: "delete",
btn_class: "remove",
modal_type: "remove"
)
end
def blob_editable?(blob, project = @project, ref = @ref)
!blob.lfs_pointer? && allowed_tree_edit?(project, ref)
def can_edit_blob?(blob, project = @project, ref = @ref)
!blob.lfs_pointer? && can_edit_tree?(project, ref)
end
def leave_edit_message
......@@ -70,7 +128,7 @@ module BlobHelper
icon("#{file_type_icon_class('file', mode, name)} fw")
end
def blob_viewable?(blob)
def blob_text_viewable?(blob)
blob && blob.text? && !blob.lfs_pointer?
end
......
......@@ -50,24 +50,49 @@ module TreeHelper
project.repository.branch_names.include?(ref)
end
def allowed_tree_edit?(project = nil, ref = nil)
def can_edit_tree?(project = nil, ref = nil)
project ||= @project
ref ||= @ref
return false unless on_top_of_branch?(project, ref)
can?(current_user, :push_code, project)
can?(current_user, :push_code, project) ||
(current_user && current_user.already_forked?(project))
end
def tree_edit_branch(project = @project, ref = @ref)
if allowed_tree_edit?(project, ref)
if can_push_branch?(project, ref)
ref
else
project.repository.next_patch_branch
end
return unless can_edit_tree?(project, ref)
if can_push_branch?(project, ref)
ref
else
project = tree_edit_project(project)
project.repository.next_patch_branch
end
end
def tree_edit_project(project = @project)
if can?(current_user, :push_code, project)
project
elsif current_user && current_user.already_forked?(project)
current_user.fork_of(project)
end
end
def edit_in_new_fork_notice_now
"You're not allowed to make changes to this project directly." +
" A fork of this project is being created that you can make changes in, so you can submit a merge request."
end
def edit_in_new_fork_notice
"You're not allowed to make changes to this project directly." +
" A fork of this project has been created that you can make changes in, so you can submit a merge request."
end
def commit_in_fork_help
"A new branch will be created in your fork and a new merge request will be started."
end
def tree_breadcrumbs(tree, max_links = 2)
if @path.present?
part_path = ""
......
......@@ -69,7 +69,6 @@ module VisibilityLevelHelper
def skip_level?(form_model, level)
form_model.is_a?(Project) &&
form_model.forked? &&
!Gitlab::VisibilityLevel.allowed_fork_levels(form_model.forked_from_project.visibility_level).include?(level)
!form_model.visibility_level_allowed?(level)
end
end
......@@ -64,6 +64,19 @@ class Project < ActiveRecord::Base
update_column(:last_activity_at, self.created_at)
end
# update visibility_levet of forks
after_update :update_forks_visibility_level
def update_forks_visibility_level
return unless visibility_level < visibility_level_was
forks.each do |forked_project|
if forked_project.visibility_level > visibility_level
forked_project.visibility_level = visibility_level
forked_project.save!
end
end
end
ActsAsTaggableOn.strict_case_match = true
acts_as_taggable_on :tags
......@@ -100,9 +113,12 @@ class Project < ActiveRecord::Base
has_one :gitlab_issue_tracker_service, dependent: :destroy
has_one :external_wiki_service, dependent: :destroy
has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
has_one :forked_from_project, through: :forked_project_link
has_many :forked_project_links, foreign_key: "forked_from_project_id"
has_many :forks, through: :forked_project_links, source: :forked_to_project
has_one :forked_from_project, through: :forked_project_link
# Merge Requests for target project should be removed with it
has_many :merge_requests, dependent: :destroy, foreign_key: 'target_project_id'
# Merge requests from source project should be kept when source project was removed
......@@ -768,7 +784,7 @@ class Project < ActiveRecord::Base
end
def forks_count
ForkedProjectLink.where(forked_from_project_id: self.id).count
forks.count
end
def find_label(name)
......@@ -862,4 +878,9 @@ class Project < ActiveRecord::Base
def open_issues_count
issues.opened.count
end
def visibility_level_allowed?(level)
return true unless forked?
Gitlab::VisibilityLevel.allowed_fork_levels(forked_from_project.visibility_level).include?(level.to_i)
end
end
......@@ -592,47 +592,54 @@ class Repository
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
was_empty = empty?
# Create temporary ref
def with_tmp_ref(oldrev = nil)
random_string = SecureRandom.hex
tmp_ref = "refs/tmp/#{random_string}/head"
unless was_empty
oldrev = find_branch(branch).target
if oldrev && !Gitlab::Git.blank_ref?(oldrev)
rugged.references.create(tmp_ref, oldrev)
end
# Make commit in tmp ref
newrev = yield(tmp_ref)
yield(tmp_ref)
ensure
rugged.references.delete(tmp_ref) rescue nil
end
def commit_with_hooks(current_user, branch)
oldrev = Gitlab::Git::BLANK_SHA
ref = Gitlab::Git::BRANCH_REF_PREFIX + branch
was_empty = empty?
unless newrev
raise CommitError.new('Failed to create commit')
unless was_empty
oldrev = find_branch(branch).target
end
GitHooksService.new.execute(current_user, path_to_repo, oldrev, newrev, ref) do
if was_empty
# Create branch
rugged.references.create(ref, newrev)
else
# Update head
current_head = find_branch(branch).target
with_tmp_ref(oldrev) do |tmp_ref|
# Make commit in tmp ref
newrev = yield(tmp_ref)
unless newrev
raise CommitError.new('Failed to create commit')
end
# Make sure target branch was not changed during pre-receive hook
if current_head == oldrev
rugged.references.update(ref, newrev)
GitHooksService.new.execute(current_user, path_to_repo, oldrev, newrev, ref) do
if was_empty
# Create branch
rugged.references.create(ref, newrev)
else
raise CommitError.new('Commit was rejected because branch received new push')
# 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
end
end
rescue GitHooksService::PreReceiveError
# Remove tmp ref and return error to user
rugged.references.delete(tmp_ref)
raise
end
private
......
......@@ -39,10 +39,7 @@ class BaseService
def deny_visibility_level(model, denied_visibility_level = nil)
denied_visibility_level ||= model.visibility_level
level_name = 'Unknown'
Gitlab::VisibilityLevel.options.each do |name, level|
level_name = name if level == denied_visibility_level
end
level_name = Gitlab::VisibilityLevel.level_name(denied_visibility_level)
model.errors.add(
:visibility_level,
......
require_relative 'base_service'
class CreateBranchService < BaseService
def execute(branch_name, ref)
def execute(branch_name, ref, source_project: @project)
valid_branch = Gitlab::GitRefValidator.validate(branch_name)
if valid_branch == false
return error('Branch name invalid')
return error('Branch name is invalid')
end
repository = project.repository
......@@ -13,7 +13,20 @@ class CreateBranchService < BaseService
return error('Branch already exists')
end
new_branch = repository.add_branch(current_user, branch_name, ref)
new_branch = nil
if source_project != @project
repository.with_tmp_ref do |tmp_ref|
repository.fetch_ref(
source_project.repository.path_to_repo,
"refs/heads/#{ref}",
tmp_ref
)
new_branch = repository.add_branch(current_user, branch_name, tmp_ref)
end
else
new_branch = repository.add_branch(current_user, branch_name, ref)
end
if new_branch
push_data = build_push_data(project, current_user, new_branch)
......
......@@ -3,8 +3,10 @@ module Files
class ValidationError < StandardError; end
def execute
@current_branch = params[:current_branch]
@source_project = params[:source_project] || @project
@source_branch = params[:source_branch]
@target_branch = params[:target_branch]
@commit_message = params[:commit_message]
@file_path = params[:file_path]
@file_content = if params[:file_content_encoding] == 'base64'
......@@ -16,8 +18,8 @@ module Files
# Validate parameters
validate
# Create new branch if it different from current_branch
if @target_branch != @current_branch
# Create new branch if it different from source_branch
if different_branch?
create_target_branch
end
......@@ -26,18 +28,14 @@ module Files
else
error("Something went wrong. Your changes were not committed")
end
rescue Repository::CommitError, GitHooksService::PreReceiveError, ValidationError => ex
rescue Repository::CommitError, Gitlab::Git::Repository::InvalidBlobName, GitHooksService::PreReceiveError, ValidationError => ex
error(ex.message)
end
private
def current_branch
@current_branch ||= params[:current_branch]
end
def target_branch
@target_branch ||= params[:target_branch]
def different_branch?
@source_branch != @target_branch || @source_project != @project
end
def raise_error(message)
......@@ -52,11 +50,11 @@ module Files
end
unless project.empty_repo?
unless repository.branch_names.include?(@current_branch)
unless @source_project.repository.branch_names.include?(@source_branch)
raise_error("You can only create or edit files when you are on a branch")
end
if @current_branch != @target_branch
if different_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
......@@ -65,10 +63,10 @@ module Files
end
def create_target_branch
result = CreateBranchService.new(project, current_user).execute(@target_branch, @current_branch)
result = CreateBranchService.new(project, current_user).execute(@target_branch, @source_branch, source_project: @source_project)
unless result[:status] == :success
raise_error("Something went wrong when we tried to create #{@target_branch} for you")
raise_error("Something went wrong when we tried to create #{@target_branch} for you: #{result[:message]}")
end
end
end
......
......@@ -26,7 +26,7 @@ module Files
unless project.empty_repo?
@file_path.slice!(0) if @file_path.start_with?('/')
blob = repository.blob_at_branch(@current_branch, @file_path)
blob = repository.blob_at_branch(@source_branch, @file_path)
if blob
raise_error("Your changes could not be committed because a file with the same name already exists")
......
......@@ -3,12 +3,16 @@ module Projects
def execute
# check that user is allowed to set specified visibility_level
new_visibility = params[:visibility_level]
if new_visibility && new_visibility.to_i != project.visibility_level
unless can?(current_user, :change_visibility_level, project) &&
Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility)
deny_visibility_level(project, new_visibility)
return project
if new_visibility
if new_visibility.to_i != project.visibility_level
unless can?(current_user, :change_visibility_level, project) &&
Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility)
deny_visibility_level(project, new_visibility)
return project
end
end
return false unless visibility_level_allowed?(new_visibility)
end
new_branch = params[:default_branch]
......@@ -23,5 +27,19 @@ module Projects
end
end
end
private
def visibility_level_allowed?(level)
return true if project.visibility_level_allowed?(level)
level_name = Gitlab::VisibilityLevel.level_name(level)
project.errors.add(
:visibility_level,
"#{level_name} could not be set as visibility level of this project - parent project settings are more restrictive"
)
false
end
end
end
......@@ -2,3 +2,7 @@
= button_tag 'Commit Changes', class: 'btn commit-btn js-commit-button btn-create'
= link_to 'Cancel', cancel_path,
class: 'btn btn-cancel', data: {confirm: leave_edit_message}
- unless can?(current_user, :push_code, @project)
.inline.prepend-left-10
= commit_in_fork_help
......@@ -2,7 +2,7 @@
= link_to 'Raw', namespace_project_raw_path(@project.namespace, @project, @id),
class: 'btn btn-sm', target: '_blank'
-# only show normal/blame view links for text files
- if blob_viewable?(@blob)
- if blob_text_viewable?(@blob)
- if current_page? namespace_project_blame_path(@project.namespace, @project, @id)
= link_to 'Normal View', namespace_project_blob_path(@project.namespace, @project, @id),
class: 'btn btn-sm'
......@@ -14,13 +14,8 @@
= link_to 'Permalink', namespace_project_blob_path(@project.namespace, @project,
tree_join(@commit.sha, @path)), class: 'btn btn-sm'
- if blob_editable?(@blob)
- if current_user
.btn-group{ role: "group" }
= edit_blob_link(@project, @ref, @path)
%button.btn.btn-default{ 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal' } Replace
%button.btn.btn-remove{ 'data-target' => '#modal-remove-blob', 'data-toggle' => 'modal' } Delete
- elsif !on_top_of_branch?
.btn-group{ role: "group" }
%button.btn.btn-default.disabled.has_tooltip{title: "You can only edit files when you are on a branch.", data: {container: 'body'}} Edit
%button.btn.btn-default.disabled.has_tooltip{title: "You can only replace files when you are on a branch.", data: {container: 'body'}} Replace
%button.btn.btn-remove.disabled.has_tooltip{title: "You can only delete files when you are on a branch.", data: {container: 'body'}} Delete
= edit_blob_link
= replace_blob_link
= delete_blob_link
......@@ -17,5 +17,9 @@
= submit_tag "Create directory", class: 'btn btn-create'
= link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
- unless can?(current_user, :push_code, @project)
.inline.prepend-left-10
= commit_in_fork_help
:javascript
new NewCommitForm($('.js-create-dir-form'))
......@@ -20,6 +20,11 @@
= button_tag button_title, class: 'btn btn-small btn-create btn-upload-file', id: 'submit-all'
= link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
- unless can?(current_user, :push_code, @project)
.inline.prepend-left-10
= commit_in_fork_help
:javascript
disableButtonIfEmptyField($('.js-upload-blob-form').find('.js-commit-message'), '.btn-upload-file');
new BlobFileDropzone($('.js-upload-blob-form'), '#{method}');
......
......@@ -20,7 +20,7 @@
= hidden_field_tag 'last_commit', @last_commit
= hidden_field_tag 'content', '', id: "file-content"
= hidden_field_tag 'from_merge_request_id', params[:from_merge_request_id]
= render 'projects/commit_button', ref: @ref, cancel_path: @after_edit_path
= render 'projects/commit_button', ref: @ref, cancel_path: namespace_project_blob_path(@project.namespace, @project, @id)
:javascript
blob = new EditBlob(gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}", "#{@blob.language.try(:ace_mode)}")
......
......@@ -6,7 +6,7 @@
%div#tree-holder.tree-holder
= render 'blob', blob: @blob
- if blob_editable?(@blob)
- if can_edit_blob?(@blob)
= render 'projects/blob/remove'
- title = "Replace #{@blob.name}"
......
......@@ -9,11 +9,12 @@
New Branch
%hr
= form_tag namespace_project_branches_path, method: :post, id: "new-branch-form", class: "form-horizontal js-requires-input" do
= form_tag namespace_project_branches_path, method: :post, id: "new-branch-form", class: "form-horizontal js-create-branch-form js-requires-input" do
.form-group
= label_tag :branch_name, nil, class: 'control-label'
.col-sm-10
= text_field_tag :branch_name, params[:branch_name], required: true, tabindex: 1, autofocus: true, class: 'form-control'
= text_field_tag :branch_name, params[:branch_name], required: true, tabindex: 1, autofocus: true, class: 'form-control js-branch-name'
.help-block.text-danger.js-branch-name-error
.form-group
= label_tag :ref, 'Create from', class: 'control-label'
.col-sm-10
......@@ -26,7 +27,4 @@
:javascript
var availableRefs = #{@project.repository.ref_names.to_json};
$("#ref").autocomplete({
source: availableRefs,
minLength: 1
});
new NewBranchForm($('.js-create-branch-form'), availableRefs)
......@@ -18,10 +18,11 @@
= link_to new_namespace_project_snippet_path(@project.namespace, @project) do
= icon('file-text-o fw')
New snippet
- if can?(current_user, :push_code, @project)
%li.divider
%li
= link_to namespace_project_new_blob_path(@project.namespace, @project, @project.default_branch || 'master'), title: 'New file' do
= link_to namespace_project_new_blob_path(@project.namespace, @project, @project.default_branch || 'master') do
= icon('file fw')
New file
%li
......@@ -32,3 +33,20 @@
= link_to new_namespace_project_tag_path(@project.namespace, @project) do
= icon('tags fw')
New tag
- elsif current_user && current_user.already_forked?(@project)
%li.divider
%li
= link_to namespace_project_new_blob_path(@project.namespace, @project, @project.default_branch || 'master') do
= icon('file fw')
New file
- elsif can?(current_user, :fork_project, @project)
%li.divider
%li
- continue_params = { to: namespace_project_new_blob_path(@project.namespace, @project, @project.default_branch || 'master'),
notice: edit_in_new_fork_notice,
notice_now: edit_in_new_fork_notice_now }
- fork_path = namespace_project_fork_path(@project.namespace, @project, namespace_key: current_user.namespace.id,
continue: continue_params)
= link_to fork_path, method: :post do
= icon('file fw')
New file
......@@ -24,7 +24,7 @@
= "#{diff_file.diff.a_mode}#{diff_file.diff.b_mode}"
.diff-controls
- if blob_viewable?(blob)
- if blob_text_viewable?(blob)
= link_to '#', class: 'js-toggle-diff-comments btn btn-sm active has_tooltip', title: "Toggle comments for this file" do
%i.fa.fa-comments
&nbsp;
......@@ -32,14 +32,15 @@
- if editable_diff?(diff_file)
= edit_blob_link(@merge_request.source_project,
@merge_request.source_branch, diff_file.new_path,
after: '&nbsp;', from_merge_request_id: @merge_request.id)
from_merge_request_id: @merge_request.id)
&nbsp;
= view_file_btn(diff_commit.id, diff_file, project)
.diff-content.diff-wrap-lines
-# Skipp all non non-supported blobs
- return unless blob.respond_to?('text?')
- if blob_viewable?(blob)
- if blob_text_viewable?(blob)
- if diff_view == 'parallel'
= render "projects/diffs/parallel_view", diff_file: diff_file, project: project, blob: blob, index: i
- else
......
......@@ -43,4 +43,3 @@
%i.fa.fa-spinner.fa-spin
Forking repository
%p Please wait a moment, this page will automatically refresh when ready.
......@@ -17,7 +17,7 @@
- if merge_request.open? && merge_request.broken?
%li
= link_to merge_request_path(merge_request), class: "has_tooltip", title: "Cannot be merged automatically", data: {container: 'body'} do
= link_to merge_request_path(merge_request), class: "has_tooltip", title: "Cannot be merged automatically", data: { container: 'body' } do
= icon('exclamation-triangle')
- if merge_request.assignee
......
......@@ -29,7 +29,7 @@
- if tree.readme
= render "projects/tree/readme", readme: tree.readme
- if allowed_tree_edit?
- if can_edit_tree?
= render 'projects/blob/upload', title: 'Upload New File', placeholder: 'Upload new file', button_title: 'Upload file', form_path: namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post
= render 'projects/blob/new_dir'
......
......@@ -11,34 +11,65 @@
= link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, path)
- else
= link_to title, '#'
- if allowed_tree_edit?
- if current_user
%li
%span.dropdown
%a.dropdown-toggle.btn.btn-sm.add-to-tree{href: '#', "data-toggle" => "dropdown"}
- if !on_top_of_branch?
%span.btn.btn-sm.add-to-tree.disabled.has_tooltip{title: "You can only add files when you are on a branch", data: { container: 'body' }}
= icon('plus')
%ul.dropdown-menu
%li
= link_to namespace_project_new_blob_path(@project.namespace, @project, @id), title: 'Create file', id: 'new-file-link' do
= icon('pencil fw')
New file
%li
= link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal'} do
= icon('file fw')
Upload file
%li
= link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal'} do
= icon('folder fw')
New directory
%li.divider
%li
= link_to new_namespace_project_branch_path(@project.namespace, @project) do
= icon('code-fork fw')
New branch
%li
= link_to new_namespace_project_tag_path(@project.namespace, @project) do
= icon('tags fw')
New tag
- elsif !on_top_of_branch?
%li
%span.btn.btn-sm.add-to-tree.disabled.has_tooltip{title: "You can only add files when you are on a branch.", data: {container: 'body'}}
= icon('plus')
- else
%span.dropdown
%a.dropdown-toggle.btn.btn-sm.add-to-tree{href: '#', "data-toggle" => "dropdown"}
= icon('plus')
%ul.dropdown-menu
- if can_edit_tree?
%li
= link_to namespace_project_new_blob_path(@project.namespace, @project, @id) do
= icon('pencil fw')
New file
%li
= link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal'} do
= icon('file fw')
Upload file
%li
= link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal'} do
= icon('folder fw')
New directory
- elsif can?(current_user, :fork_project, @project)
%li
- continue_params = { to: namespace_project_new_blob_path(@project.namespace, @project, @id),
notice: edit_in_new_fork_notice,
notice_now: edit_in_new_fork_notice_now }
- fork_path = namespace_project_fork_path(@project.namespace, @project, namespace_key: current_user.namespace.id,
continue: continue_params)
= link_to fork_path, method: :post do
= icon('pencil fw')
New file
%li
- continue_params = { to: request.fullpath,
notice: edit_in_new_fork_notice + " Try to upload a file again.",
notice_now: edit_in_new_fork_notice_now }
- fork_path = namespace_project_fork_path(@project.namespace, @project, namespace_key: current_user.namespace.id,
continue: continue_params)
= link_to fork_path, method: :post do
= icon('file fw')
Upload file
%li
- continue_params = { to: request.fullpath,
notice: edit_in_new_fork_notice + " Try to create a new directory again.",
notice_now: edit_in_new_fork_notice_now }
- fork_path = namespace_project_fork_path(@project.namespace, @project, namespace_key: current_user.namespace.id,
continue: continue_params)
= link_to fork_path, method: :post do
= icon('folder fw')
New directory
%li.divider
%li
= link_to new_namespace_project_branch_path(@project.namespace, @project) do
= icon('code-fork fw')
New branch
%li
= link_to new_namespace_project_tag_path(@project.namespace, @project) do
= icon('tags fw')
New tag
= render 'shared/commit_message_container', placeholder: placeholder
- unless @project.empty_repo?
.form-group.branch
= label_tag 'new_branch', 'Target branch', class: 'control-label'
.col-sm-10
= text_field_tag 'new_branch', @new_branch || tree_edit_branch, required: true, class: "form-control js-new-branch"
- if @project.empty_repo?
= hidden_field_tag 'target_branch', @ref
- else
- if can?(current_user, :push_code, @project)
.form-group.branch
= label_tag 'target_branch', 'Target branch', class: 'control-label'
.col-sm-10
= text_field_tag 'target_branch', @target_branch || tree_edit_branch, required: true, class: "form-control js-target-branch"
.js-create-merge-request-container
.checkbox
- nonce = SecureRandom.hex
= label_tag "create_merge_request-#{nonce}" do
= check_box_tag 'create_merge_request', 1, true, class: 'js-create-merge-request', id: "create_merge_request-#{nonce}"
Start a <strong>new merge request</strong> with these changes
.js-create-merge-request-container
.checkbox
- nonce = SecureRandom.hex
= label_tag "create_merge_request-#{nonce}" do
= check_box_tag 'create_merge_request', 1, true, class: 'js-create-merge-request', id: "create_merge_request-#{nonce}"
Start a <strong>new merge request</strong> with these changes
- else
= hidden_field_tag 'target_branch', @target_branch || tree_edit_branch
= hidden_field_tag 'create_merge_request', 1
= hidden_field_tag 'original_branch', @ref, class: 'js-original-branch'
......@@ -25,6 +25,7 @@ Feature: Project Commits Branches
And I click branch 'improve/awesome' delete link
Then I should not see branch 'improve/awesome'
@javascript
Scenario: I create a branch with invalid name
Given I visit project branches page
And I click new branch link
......
......@@ -12,6 +12,14 @@ Feature: Project Merge Requests Acceptance
Then I should see merge request merged
And I should not see the Remove Source Branch button
@javascript
Scenario: Accepting the Merge Request when URL has an anchor
Given I am on the Merge Request detail with note anchor page
When I click on "Remove source branch" option
And I click on Accept Merge Request
Then I should see merge request merged
And I should not see the Remove Source Branch button
@javascript
Scenario: Accepting the Merge Request without removing the source branch
Given I am on the Merge Request detail page
......
......@@ -24,6 +24,12 @@ Feature: Project Source Browse Files
Given I click on "New file" link in repo
Then I can see new file page
Scenario: I can create file when I don't have write access
Given I don't have write access
And I click on "New file" link in repo
Then I should see a notice about a new fork having been created
Then I can see new file page
@javascript
Scenario: I can create and commit file
Given I click on "New file" link in repo
......@@ -34,6 +40,17 @@ Feature: Project Source Browse Files
Then I am redirected to the new file
And I should see its new content
@javascript
Scenario: I can create and commit file when I don't have write access
Given I don't have write access
And I click on "New file" link in repo
And I edit code
And I fill the new file name
And I fill the commit message
And I click on "Commit Changes"
Then I am redirected to the fork's new merge request page
And I can see the new commit message
@javascript
Scenario: I can create and commit file with new lines at the end of file
Given I click on "New file" link in repo
......@@ -45,6 +62,17 @@ Feature: Project Source Browse Files
And I click button "Edit"
And I should see its content with new lines preserved at end of file
@javascript
Scenario: I can create and commit file and specify new branch
Given I click on "New file" link in repo
And I edit code
And I fill the new file name
And I fill the commit message
And I fill the new branch name
And I click on "Commit Changes"
Then I am redirected to the new merge request page
And I should see its new content
@javascript
Scenario: I can upload file and commit
Given I click on "Upload file" link in repo
......@@ -56,6 +84,19 @@ Feature: Project Source Browse Files
And I am redirected to the new merge request page
And I can see the new commit message
@javascript
Scenario: I can upload file and commit when I don't have write access
Given I don't have write access
And I click on "Upload file" link in repo
Then I should see a notice about a new fork having been created
When I click on "Upload file" link in repo
And I upload a new text file
And I fill the upload file commit message
And I click on "Upload file"
Then I can see the new text file
And I am redirected to the fork's new merge request page
And I can see the new commit message
@javascript
Scenario: I can replace file and commit
Given I click on ".gitignore" file in repo
......@@ -68,15 +109,19 @@ Feature: Project Source Browse Files
And I can see the replacement commit message
@javascript
Scenario: I can create and commit file and specify new branch
Given I click on "New file" link in repo
And I edit code
And I fill the new file name
And I fill the commit message
And I fill the new branch name
And I click on "Commit Changes"
Then I am redirected to the new merge request page
And I should see its new content
Scenario: I can replace file and commit when I don't have write access
Given I don't have write access
And I click on ".gitignore" file in repo
And I see the ".gitignore"
And I click on "Replace"
Then I should see a notice about a new fork having been created
When I click on "Replace"
And I replace it with a text file
And I fill the replace file commit message
And I click on "Replace file"
Then I can see the new text file
And I am redirected to the fork's new merge request page
And I can see the replacement commit message
@javascript
Scenario: I can create file in empty repo
......@@ -117,6 +162,14 @@ Feature: Project Source Browse Files
And I click button "Edit"
Then I can edit code
@javascript
Scenario: I can edit file when I don't have write access
Given I don't have write access
And I click on ".gitignore" file in repo
And I click button "Edit"
Then I should see a notice about a new fork having been created
And I can edit code
Scenario: If the file is binary the edit link is hidden
Given I visit a binary file in the repo
Then I cannot see the edit button
......@@ -131,6 +184,17 @@ Feature: Project Source Browse Files
Then I am redirected to the ".gitignore"
And I should see its new content
@javascript
Scenario: I can edit and commit file when I don't have write access
Given I don't have write access
And I click on ".gitignore" file in repo
And I click button "Edit"
And I edit code
And I fill the commit message
And I click on "Commit Changes"
Then I am redirected to the fork's new merge request page
And I can see the new commit message
@javascript
Scenario: I can edit and commit file to new branch
Given I click on ".gitignore" file in repo
......@@ -161,6 +225,17 @@ Feature: Project Source Browse Files
And I click on "Create directory"
Then I am redirected to the new merge request page
@javascript
Scenario: I can create directory in repo when I don't have write access
Given I don't have write access
When I click on "New directory" link in repo
Then I should see a notice about a new fork having been created
When I click on "New directory" link in repo
And I fill the new directory name
And I fill the commit message
And I click on "Create directory"
Then I am redirected to the fork's new merge request page
@javascript
Scenario: I attempt to create an existing directory
When I click on "New directory" link in repo
......@@ -188,6 +263,19 @@ Feature: Project Source Browse Files
Then I am redirected to the files URL
And I don't see the ".gitignore"
@javascript
Scenario: I can delete file and commit when I don't have write access
Given I don't have write access
And I click on ".gitignore" file in repo
And I see the ".gitignore"
And I click on "Delete"
Then I should see a notice about a new fork having been created
When I click on "Delete"
And I fill the commit message
And I click on "Delete file"
Then I am redirected to the fork's new merge request page
And I can see the new commit message
Scenario: I can browse directory with Browse Dir
Given I click on files directory
And I click on History link
......
......@@ -61,7 +61,8 @@ class Spinach::Features::ProjectCommitsBranches < Spinach::FeatureSteps
end
step 'I should see new an error that branch is invalid' do
expect(page).to have_content 'Branch name invalid'
expect(page).to have_content 'Branch name is invalid'
expect(page).to have_content "can't contain spaces"
end
step 'I should see new an error that ref is invalid' do
......
......@@ -6,6 +6,10 @@ class Spinach::Features::ProjectMergeRequestsAcceptance < Spinach::FeatureSteps
visit merge_request_path(@merge_request)
end
step 'I am on the Merge Request detail with note anchor page' do
visit merge_request_path(@merge_request, anchor: 'note_123')
end
step 'I click on "Remove source branch" option' do
check('Remove source branch')
end
......
......@@ -5,6 +5,12 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
include SharedPaths
include RepoHelpers
step "I don't have write access" do
@project = create(:project, name: "Other Project", path: "other-project")
@project.team << [@user, :reporter]
visit namespace_project_tree_path(@project.namespace, @project, root_ref)
end
step 'I should see files from repository' do
expect(page).to have_content "VERSION"
expect(page).to have_content ".gitignore"
......@@ -75,7 +81,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end
step 'I fill the new branch name' do
fill_in :new_branch, with: 'new_branch_name', visible: true
fill_in :target_branch, with: 'new_branch_name', visible: true
end
step 'I fill the new file name with an illegal name' do
......@@ -87,7 +93,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end
step 'I fill the commit message' do
fill_in :commit_message, with: 'Not yet a commit message.', visible: true
fill_in :commit_message, with: 'New commit message', visible: true
end
step 'I click link "Diff"' do
......@@ -103,7 +109,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end
step 'I click on "Delete"' do
click_button 'Delete'
click_on 'Delete'
end
step 'I click on "Delete file"' do
......@@ -111,7 +117,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end
step 'I click on "Replace"' do
click_button "Replace"
click_on "Replace"
end
step 'I click on "Replace file"' do
......@@ -124,7 +130,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
step 'I click on "New file" link in repo' do
find('.add-to-tree').click
click_link 'Create file'
click_link 'New file'
end
step 'I click on "Upload file" link in repo' do
......@@ -155,7 +161,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end
step 'I can see the new commit message' do
expect(page).to have_content "New upload commit message"
expect(page).to have_content "New commit message"
end
step 'I upload a new text file' do
......@@ -164,7 +170,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
step 'I fill the upload file commit message' do
page.within('#modal-upload-blob') do
fill_in :commit_message, with: 'New upload commit message'
fill_in :commit_message, with: 'New commit message'
end
end
......@@ -251,9 +257,14 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
expect(current_path).to eq(new_namespace_project_merge_request_path(@project.namespace, @project))
end
step "I am redirected to the fork's new merge request page" do
fork = @user.fork_of(@project)
expect(current_path).to eq(new_namespace_project_merge_request_path(fork.namespace, fork))
end
step 'I am redirected to the root directory' do
expect(current_path).to eq(
namespace_project_tree_path(@project.namespace, @project, 'master/'))
namespace_project_tree_path(@project.namespace, @project, 'master'))
end
step "I don't see the permalink link" do
......@@ -332,8 +343,12 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
expect(page).to have_content 'Permalink'
expect(page).not_to have_content 'Edit'
expect(page).not_to have_content 'Blame'
expect(page).not_to have_content 'Delete'
expect(page).not_to have_content 'Replace'
expect(page).to have_content 'Delete'
expect(page).to have_content 'Replace'
end
step 'I should see a notice about a new fork having been created' do
expect(page).to have_content "You're not allowed to make changes to this project directly. A fork of this project has been created that you can make changes in, so you can submit a merge request."
end
private
......
......@@ -7,7 +7,7 @@ module API
def commit_params(attrs)
{
file_path: attrs[:file_path],
current_branch: attrs[:branch_name],
source_branch: attrs[:branch_name],
target_branch: attrs[:branch_name],
commit_message: attrs[:commit_message],
file_content: attrs[:content],
......
......@@ -51,6 +51,15 @@ module Gitlab
def allowed_fork_levels(origin_level)
[PRIVATE, INTERNAL, PUBLIC].select{ |level| level <= origin_level }
end
def level_name(level)
level_name = 'Unknown'
options.each do |name, lvl|
level_name = name if lvl == level.to_i
end
level_name
end
end
def private?
......
......@@ -98,7 +98,7 @@ describe Projects::TreeController do
project_id: project.to_param,
id: 'master',
dir_name: path,
new_branch: target_branch,
target_branch: target_branch,
commit_message: 'Test commit message')
end
......@@ -108,8 +108,8 @@ describe Projects::TreeController do
it 'redirects to the new directory' do
expect(subject).
to redirect_to("/#{project.path_with_namespace}/blob/#{target_branch}/#{path}")
expect(flash[:notice]).to eq('The directory has been successfully created')
to redirect_to("/#{project.path_with_namespace}/tree/#{target_branch}/#{path}")
expect(flash[:notice]).to eq('The directory has been successfully created.')
end
end
......@@ -119,7 +119,7 @@ describe Projects::TreeController do
it 'does not allow overwriting of existing files' do
expect(subject).
to redirect_to("/#{project.path_with_namespace}/blob/master")
to redirect_to("/#{project.path_with_namespace}/tree/master")
expect(flash[:alert]).to eq('Directory already exists as a file')
end
end
......
%form.js-create-branch-form
%input.js-branch-name
.js-branch-name-error
%input{id: "ref"}
#= require jquery.ui.all
#= require new_branch_form
describe 'Branch', ->
describe 'create a new branch', ->
fixture.preload('new_branch.html')
fillNameWith = (value) ->
$('.js-branch-name').val(value).trigger('blur')
expectToHaveError = (error) ->
expect($('.js-branch-name-error span').text()).toEqual(error)
beforeEach ->
fixture.load('new_branch.html')
$('form').on 'submit', (e) -> e.preventDefault()
@form = new NewBranchForm($('.js-create-branch-form'), [])
it "can't start with a dot", ->
fillNameWith '.foo'
expectToHaveError "can't start with '.'"
it "can't start with a slash", ->
fillNameWith '/foo'
expectToHaveError "can't start with '/'"
it "can't have two consecutive dots", ->
fillNameWith 'foo..bar'
expectToHaveError "can't contain '..'"
it "can't have spaces anywhere", ->
fillNameWith ' foo'
expectToHaveError "can't contain spaces"
fillNameWith 'foo bar'
expectToHaveError "can't contain spaces"
fillNameWith 'foo '
expectToHaveError "can't contain spaces"
it "can't have ~ anywhere", ->
fillNameWith '~foo'
expectToHaveError "can't contain '~'"
fillNameWith 'foo~bar'
expectToHaveError "can't contain '~'"
fillNameWith 'foo~'
expectToHaveError "can't contain '~'"
it "can't have tilde anwhere", ->
fillNameWith '~foo'
expectToHaveError "can't contain '~'"
fillNameWith 'foo~bar'
expectToHaveError "can't contain '~'"
fillNameWith 'foo~'
expectToHaveError "can't contain '~'"
it "can't have caret anywhere", ->
fillNameWith '^foo'
expectToHaveError "can't contain '^'"
fillNameWith 'foo^bar'
expectToHaveError "can't contain '^'"
fillNameWith 'foo^'
expectToHaveError "can't contain '^'"
it "can't have : anywhere", ->
fillNameWith ':foo'
expectToHaveError "can't contain ':'"
fillNameWith 'foo:bar'
expectToHaveError "can't contain ':'"
fillNameWith ':foo'
expectToHaveError "can't contain ':'"
it "can't have question mark anywhere", ->
fillNameWith '?foo'
expectToHaveError "can't contain '?'"
fillNameWith 'foo?bar'
expectToHaveError "can't contain '?'"
fillNameWith 'foo?'
expectToHaveError "can't contain '?'"
it "can't have asterisk anywhere", ->
fillNameWith '*foo'
expectToHaveError "can't contain '*'"
fillNameWith 'foo*bar'
expectToHaveError "can't contain '*'"
fillNameWith 'foo*'
expectToHaveError "can't contain '*'"
it "can't have open bracket anywhere", ->
fillNameWith '[foo'
expectToHaveError "can't contain '['"
fillNameWith 'foo[bar'
expectToHaveError "can't contain '['"
fillNameWith 'foo['
expectToHaveError "can't contain '['"
it "can't have a backslash anywhere", ->
fillNameWith '\\foo'
expectToHaveError "can't contain '\\'"
fillNameWith 'foo\\bar'
expectToHaveError "can't contain '\\'"
fillNameWith 'foo\\'
expectToHaveError "can't contain '\\'"
it "can't contain a sequence @{ anywhere", ->
fillNameWith '@{foo'
expectToHaveError "can't contain '@{'"
fillNameWith 'foo@{bar'
expectToHaveError "can't contain '@{'"
fillNameWith 'foo@{'
expectToHaveError "can't contain '@{'"
it "can't have consecutive slashes", ->
fillNameWith 'foo//bar'
expectToHaveError "can't contain consecutive slashes"
it "can't end with a slash", ->
fillNameWith 'foo/'
expectToHaveError "can't end in '/'"
it "can't end with a dot", ->
fillNameWith 'foo.'
expectToHaveError "can't end in '.'"
it "can't end with .lock", ->
fillNameWith 'foo.lock'
expectToHaveError "can't end in '.lock'"
it "can't be the single character @", ->
fillNameWith '@'
expectToHaveError "can't be '@'"
it "concatenates all error messages", ->
fillNameWith '/foo bar?~.'
expectToHaveError "can't start with '/', can't contain spaces, '?', '~', can't end in '.'"
it "doesn't duplicate error messages", ->
fillNameWith '?foo?bar?zoo?'
expectToHaveError "can't contain '?'"
it "removes the error message when is a valid name", ->
fillNameWith 'foo?bar'
expect($('.js-branch-name-error span').length).toEqual(1)
fillNameWith 'foobar'
expect($('.js-branch-name-error span').length).toEqual(0)
it "can have dashes anywhere", ->
fillNameWith '-foo-bar-zoo-'
expect($('.js-branch-name-error span').length).toEqual(0)
it "can have underscores anywhere", ->
fillNameWith '_foo_bar_zoo_'
expect($('.js-branch-name-error span').length).toEqual(0)
it "can have numbers anywhere", ->
fillNameWith '1foo2bar3zoo4'
expect($('.js-branch-name-error span').length).toEqual(0)
it "can be only letters", ->
fillNameWith 'foo'
expect($('.js-branch-name-error span').length).toEqual(0)
......@@ -552,4 +552,28 @@ describe Project, models: true do
end
end
end
describe '#visibility_level_allowed?' do
let(:project) { create :project, visibility_level: Gitlab::VisibilityLevel::INTERNAL }
context 'when checking on non-forked project' do
it { expect(project.visibility_level_allowed?(Gitlab::VisibilityLevel::PRIVATE)).to be_truthy }
it { expect(project.visibility_level_allowed?(Gitlab::VisibilityLevel::INTERNAL)).to be_truthy }
it { expect(project.visibility_level_allowed?(Gitlab::VisibilityLevel::PUBLIC)).to be_truthy }
end
context 'when checking on forked project' do
let(:forked_project) { create :forked_project_with_submodules }
before do
forked_project.build_forked_project_link(forked_to_project_id: forked_project.id, forked_from_project_id: project.id)
forked_project.save
end
it { expect(forked_project.visibility_level_allowed?(Gitlab::VisibilityLevel::PRIVATE)).to be_truthy }
it { expect(forked_project.visibility_level_allowed?(Gitlab::VisibilityLevel::INTERNAL)).to be_truthy }
it { expect(forked_project.visibility_level_allowed?(Gitlab::VisibilityLevel::PUBLIC)).to be_falsey }
end
end
end
......@@ -118,7 +118,7 @@ describe API::API, api: true do
branch_name: 'new design',
ref: branch_sha
expect(response.status).to eq(400)
expect(json_response['message']).to eq('Branch name invalid')
expect(json_response['message']).to eq('Branch name is invalid')
end
it 'should return 400 if branch already exists' do
......
......@@ -100,6 +100,45 @@ describe Projects::UpdateService, services: true do
end
end
describe :visibility_level do
let(:user) { create :user, admin: true }
let(:project) { create :project, visibility_level: Gitlab::VisibilityLevel::INTERNAL }
let(:forked_project) { create :forked_project_with_submodules, visibility_level: Gitlab::VisibilityLevel::INTERNAL }
let(:opts) { {} }
before do
forked_project.build_forked_project_link(forked_to_project_id: forked_project.id, forked_from_project_id: project.id)
forked_project.save
@created_internal = project.internal?
@fork_created_internal = forked_project.internal?
end
context 'should update forks visibility level when parent set to more restrictive' do
before do
opts.merge!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
update_project(project, user, opts).inspect
end
it { expect(@created_internal).to be_truthy }
it { expect(@fork_created_internal).to be_truthy }
it { expect(project.private?).to be_truthy }
it { expect(project.forks.first.private?).to be_truthy }
end
context 'should not update forks visibility level when parent set to less restrictive' do
before do
opts.merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
update_project(project, user, opts).inspect
end
it { expect(@created_internal).to be_truthy }
it { expect(@fork_created_internal).to be_truthy }
it { expect(project.public?).to be_truthy }
it { expect(project.forks.first.internal?).to be_truthy }
end
end
def update_project(project, user, opts)
Projects::UpdateService.new(project, user, opts).execute
end
......
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