Commit ceb1ebc9 authored by Robert Speicher's avatar Robert Speicher

Merge branch 'dm-fix-editing-files-on-forks' into 'master'

Refactor changing files in web UI

Closes #28711

See merge request !9323
parents bfb635a4 d170133b
...@@ -35,7 +35,7 @@ export default class BlobFileDropzone { ...@@ -35,7 +35,7 @@ export default class BlobFileDropzone {
this.removeFile(file); this.removeFile(file);
}); });
this.on('sending', function (file, xhr, formData) { this.on('sending', function (file, xhr, formData) {
formData.append('target_branch', form.find('input[name="target_branch"]').val()); formData.append('branch_name', form.find('input[name="branch_name"]').val());
formData.append('create_merge_request', form.find('.js-create-merge-request').val()); formData.append('create_merge_request', form.find('.js-create-merge-request').val());
formData.append('commit_message', form.find('.js-commit-message').val()); formData.append('commit_message', form.find('.js-commit-message').val());
}); });
......
module CreatesCommit module CreatesCommit
extend ActiveSupport::Concern extend ActiveSupport::Concern
def set_start_branch_to_branch_name
branch_exists = @repository.find_branch(@branch_name)
@start_branch = @branch_name if branch_exists
end
def create_commit(service, success_path:, failure_path:, failure_view: nil, success_notice: nil) def create_commit(service, success_path:, failure_path:, failure_view: nil, success_notice: nil)
set_commit_variables if can?(current_user, :push_code, @project)
@project_to_commit_into = @project
@branch_name ||= @ref
else
@project_to_commit_into = current_user.fork_of(@project)
@branch_name ||= @project_to_commit_into.repository.next_branch('patch')
end
@start_branch ||= @ref || @branch_name
commit_params = @commit_params.merge( commit_params = @commit_params.merge(
start_project: @mr_target_project, start_project: @project,
start_branch: @mr_target_branch, start_branch: @start_branch,
target_branch: @mr_source_branch branch_name: @branch_name
) )
result = service.new( result = service.new(@project_to_commit_into, current_user, commit_params).execute
@mr_source_project, current_user, commit_params).execute
if result[:status] == :success if result[:status] == :success
update_flash_notice(success_notice) update_flash_notice(success_notice)
...@@ -72,30 +84,30 @@ module CreatesCommit ...@@ -72,30 +84,30 @@ module CreatesCommit
def new_merge_request_path def new_merge_request_path
new_namespace_project_merge_request_path( new_namespace_project_merge_request_path(
@mr_source_project.namespace, @project_to_commit_into.namespace,
@mr_source_project, @project_to_commit_into,
merge_request: { merge_request: {
source_project_id: @mr_source_project.id, source_project_id: @project_to_commit_into.id,
target_project_id: @mr_target_project.id, target_project_id: @project.id,
source_branch: @mr_source_branch, source_branch: @branch_name,
target_branch: @mr_target_branch target_branch: @start_branch
} }
) )
end end
def existing_merge_request_path def existing_merge_request_path
namespace_project_merge_request_path(@mr_target_project.namespace, @mr_target_project, @merge_request) namespace_project_merge_request_path(@project.namespace, @project, @merge_request)
end end
def merge_request_exists? def merge_request_exists?
return @merge_request if defined?(@merge_request) return @merge_request if defined?(@merge_request)
@merge_request = MergeRequestsFinder.new(current_user, project_id: @mr_target_project.id).execute.opened. @merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened.
find_by(source_branch: @mr_source_branch, target_branch: @mr_target_branch, source_project_id: @mr_source_project) find_by(source_project_id: @project_to_commit_into, source_branch: @branch_name, target_branch: @start_branch)
end end
def different_project? def different_project?
@mr_source_project != @mr_target_project @project_to_commit_into != @project
end end
def create_merge_request? def create_merge_request?
...@@ -103,22 +115,6 @@ module CreatesCommit ...@@ -103,22 +115,6 @@ module CreatesCommit
# as the target branch in the same project, # as the target branch in the same project,
# we don't want to create a merge request. # we don't want to create a merge request.
params[:create_merge_request].present? && params[:create_merge_request].present? &&
(different_project? || @mr_target_branch != @mr_source_branch) (different_project? || @start_branch != @branch_name)
end
def set_commit_variables
if can?(current_user, :push_code, @project)
@mr_source_project = @project
@target_branch ||= @ref
else
@mr_source_project = current_user.fork_of(@project)
@target_branch ||= @mr_source_project.repository.next_branch('patch')
end
# Merge request to this project
@mr_target_project = @project
@mr_target_branch ||= @ref || @target_branch
@mr_source_branch = @target_branch
end end
end end
...@@ -89,9 +89,4 @@ class Projects::ApplicationController < ApplicationController ...@@ -89,9 +89,4 @@ class Projects::ApplicationController < ApplicationController
def builds_enabled def builds_enabled
return render_404 unless @project.feature_available?(:builds, current_user) return render_404 unless @project.feature_available?(:builds, current_user)
end end
def update_ref
branch_exists = @repository.find_branch(@target_branch)
@ref = @target_branch if branch_exists
end
end end
...@@ -25,10 +25,10 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -25,10 +25,10 @@ class Projects::BlobController < Projects::ApplicationController
end end
def create def create
update_ref set_start_branch_to_branch_name
create_commit(Files::CreateService, success_notice: "The file has been successfully created.", 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)) }, success_path: -> { namespace_project_blob_path(@project.namespace, @project, File.join(@branch_name, @file_path)) },
failure_view: :new, failure_view: :new,
failure_path: namespace_project_new_blob_path(@project.namespace, @project, @ref)) failure_path: namespace_project_new_blob_path(@project.namespace, @project, @ref))
end end
...@@ -69,10 +69,10 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -69,10 +69,10 @@ class Projects::BlobController < Projects::ApplicationController
end end
def destroy def destroy
create_commit(Files::DestroyService, success_notice: "The file has been successfully deleted.", create_commit(Files::DeleteService, success_notice: "The file has been successfully deleted.",
success_path: -> { namespace_project_tree_path(@project.namespace, @project, @target_branch) }, success_path: -> { namespace_project_tree_path(@project.namespace, @project, @branch_name) },
failure_view: :show, failure_view: :show,
failure_path: namespace_project_blob_path(@project.namespace, @project, @id)) failure_path: namespace_project_blob_path(@project.namespace, @project, @id))
end end
def diff def diff
...@@ -127,16 +127,16 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -127,16 +127,16 @@ class Projects::BlobController < Projects::ApplicationController
def after_edit_path def after_edit_path
from_merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).execute.find_by(iid: params[:from_merge_request_iid]) from_merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).execute.find_by(iid: params[:from_merge_request_iid])
if from_merge_request && @target_branch == @ref if from_merge_request && @branch_name == @ref
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) +
"##{hexdigest(@path)}" "##{hexdigest(@path)}"
else else
namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @path)) namespace_project_blob_path(@project.namespace, @project, File.join(@branch_name, @path))
end end
end end
def editor_variables def editor_variables
@target_branch = params[:target_branch] @branch_name = params[:branch_name]
@file_path = @file_path =
if action_name.to_s == 'create' if action_name.to_s == 'create'
......
...@@ -56,9 +56,7 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -56,9 +56,7 @@ class Projects::CommitController < Projects::ApplicationController
return render_404 if @start_branch.blank? return render_404 if @start_branch.blank?
@target_branch = create_new_branch? ? @commit.revert_branch_name : @start_branch @branch_name = create_new_branch? ? @commit.revert_branch_name : @start_branch
@mr_target_branch = @start_branch
create_commit(Commits::RevertService, success_notice: "The #{@commit.change_type_title(current_user)} has been successfully reverted.", create_commit(Commits::RevertService, success_notice: "The #{@commit.change_type_title(current_user)} has been successfully reverted.",
success_path: -> { successful_change_path }, failure_path: failed_change_path) success_path: -> { successful_change_path }, failure_path: failed_change_path)
...@@ -69,9 +67,7 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -69,9 +67,7 @@ class Projects::CommitController < Projects::ApplicationController
return render_404 if @start_branch.blank? return render_404 if @start_branch.blank?
@target_branch = create_new_branch? ? @commit.cherry_pick_branch_name : @start_branch @branch_name = create_new_branch? ? @commit.cherry_pick_branch_name : @start_branch
@mr_target_branch = @start_branch
create_commit(Commits::CherryPickService, success_notice: "The #{@commit.change_type_title(current_user)} has been successfully cherry-picked.", create_commit(Commits::CherryPickService, success_notice: "The #{@commit.change_type_title(current_user)} has been successfully cherry-picked.",
success_path: -> { successful_change_path }, failure_path: failed_change_path) success_path: -> { successful_change_path }, failure_path: failed_change_path)
...@@ -84,7 +80,7 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -84,7 +80,7 @@ class Projects::CommitController < Projects::ApplicationController
end end
def successful_change_path def successful_change_path
referenced_merge_request_url || namespace_project_commits_url(@project.namespace, @project, @target_branch) referenced_merge_request_url || namespace_project_commits_url(@project.namespace, @project, @branch_name)
end end
def failed_change_path def failed_change_path
......
...@@ -34,16 +34,16 @@ class Projects::TreeController < Projects::ApplicationController ...@@ -34,16 +34,16 @@ class Projects::TreeController < Projects::ApplicationController
def create_dir def create_dir
return render_404 unless @commit_params.values.all? return render_404 unless @commit_params.values.all?
update_ref set_start_branch_to_branch_name
create_commit(Files::CreateDirService, success_notice: "The directory has been successfully created.", 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)), success_path: namespace_project_tree_path(@project.namespace, @project, File.join(@branch_name, @dir_name)),
failure_path: namespace_project_tree_path(@project.namespace, @project, @ref)) failure_path: namespace_project_tree_path(@project.namespace, @project, @ref))
end end
private private
def assign_dir_vars def assign_dir_vars
@target_branch = params[:target_branch] @branch_name = params[:branch_name]
@dir_name = File.join(@path, params[:dir_name]) @dir_name = File.join(@path, params[:dir_name])
@commit_params = { @commit_params = {
......
...@@ -272,14 +272,14 @@ module ProjectsHelper ...@@ -272,14 +272,14 @@ module ProjectsHelper
end end
end end
def add_special_file_path(project, file_name:, commit_message: nil, target_branch: nil, context: nil) def add_special_file_path(project, file_name:, commit_message: nil, branch_name: nil, context: nil)
namespace_project_new_blob_path( namespace_project_new_blob_path(
project.namespace, project.namespace,
project, project,
project.default_branch || 'master', project.default_branch || 'master',
file_name: file_name, file_name: file_name,
commit_message: commit_message || "Add #{file_name.downcase}", commit_message: commit_message || "Add #{file_name.downcase}",
target_branch: target_branch, branch_name: branch_name,
context: context context: context
) )
end end
......
...@@ -35,7 +35,7 @@ module TreeHelper ...@@ -35,7 +35,7 @@ module TreeHelper
end end
def on_top_of_branch?(project = @project, ref = @ref) def on_top_of_branch?(project = @project, ref = @ref)
project.repository.branch_names.include?(ref) project.repository.branch_exists?(ref)
end end
def can_edit_tree?(project = nil, ref = nil) def can_edit_tree?(project = nil, ref = nil)
......
module Commits module Commits
class ChangeService < ::BaseService class ChangeService < Commits::CreateService
ValidationError = Class.new(StandardError) def initialize(*args)
ChangeError = Class.new(StandardError) super
def execute
@start_project = params[:start_project] || @project
@start_branch = params[:start_branch]
@target_branch = params[:target_branch]
@commit = params[:commit] @commit = params[:commit]
check_push_permissions
commit
rescue Repository::CommitError, Gitlab::Git::Repository::InvalidBlobName, GitHooksService::PreReceiveError,
ValidationError, ChangeError => ex
error(ex.message)
end end
private private
def commit
raise NotImplementedError
end
def commit_change(action) def commit_change(action)
raise NotImplementedError unless repository.respond_to?(action) raise NotImplementedError unless repository.respond_to?(action)
validate_target_branch if different_branch?
repository.public_send( repository.public_send(
action, action,
current_user, current_user,
@commit, @commit,
@target_branch, @branch_name,
start_project: @start_project, start_project: @start_project,
start_branch_name: @start_branch) start_branch_name: @start_branch)
success
rescue Repository::CreateTreeError rescue Repository::CreateTreeError
error_msg = "Sorry, we cannot #{action.to_s.dasherize} this #{@commit.change_type_title(current_user)} automatically. error_msg = "Sorry, we cannot #{action.to_s.dasherize} this #{@commit.change_type_title(current_user)} automatically.
A #{action.to_s.dasherize} may have already been performed with this #{@commit.change_type_title(current_user)}, or a more recent commit may have updated some of its content." This #{@commit.change_type_title(current_user)} may already have been #{action.to_s.dasherize}ed, or a more recent commit may have updated some of its content."
raise ChangeError, error_msg raise ChangeError, error_msg
end end
def check_push_permissions
allowed = ::Gitlab::UserAccess.new(current_user, project: project).can_push_to_branch?(@target_branch)
unless allowed
raise ValidationError.new('You are not allowed to push into this branch')
end
true
end
def validate_target_branch
result = ValidateNewBranchService.new(@project, current_user)
.execute(@target_branch)
if result[:status] == :error
raise ChangeError, "There was an error creating the source branch: #{result[:message]}"
end
end
def different_branch?
@start_branch != @target_branch || @start_project != @project
end
end end
end end
module Commits module Commits
class CherryPickService < ChangeService class CherryPickService < ChangeService
def commit def create_commit!
commit_change(:cherry_pick) commit_change(:cherry_pick)
end end
end end
......
module Commits
class CreateService < ::BaseService
ValidationError = Class.new(StandardError)
ChangeError = Class.new(StandardError)
def initialize(*args)
super
@start_project = params[:start_project] || @project
@start_branch = params[:start_branch]
@branch_name = params[:branch_name]
end
def execute
validate!
new_commit = create_commit!
success(result: new_commit)
rescue ValidationError, ChangeError, Gitlab::Git::Index::IndexError, Repository::CommitError, GitHooksService::PreReceiveError => ex
error(ex.message)
end
private
def create_commit!
raise NotImplementedError
end
def raise_error(message)
raise ValidationError, message
end
def different_branch?
@start_branch != @branch_name || @start_project != @project
end
def validate!
validate_permissions!
validate_on_branch!
validate_branch_existance!
validate_new_branch_name! if different_branch?
end
def validate_permissions!
allowed = ::Gitlab::UserAccess.new(current_user, project: project).can_push_to_branch?(@branch_name)
unless allowed
raise_error("You are not allowed to push into this branch")
end
end
def validate_on_branch!
if !@start_project.empty_repo? && !@start_project.repository.branch_exists?(@start_branch)
raise_error('You can only create or edit files when you are on a branch')
end
end
def validate_branch_existance!
if !project.empty_repo? && different_branch? && repository.branch_exists?(@branch_name)
raise_error("A branch called '#{@branch_name}' already exists. Switch to that branch in order to make changes")
end
end
def validate_new_branch_name!
result = ValidateNewBranchService.new(project, current_user).execute(@branch_name)
if result[:status] == :error
raise_error("Something went wrong when we tried to create '#{@branch_name}' for you: #{result[:message]}")
end
end
end
end
module Commits module Commits
class RevertService < ChangeService class RevertService < ChangeService
def commit def create_commit!
commit_change(:revert) commit_change(:revert)
end end
end end
......
module Files module Files
class BaseService < ::BaseService class BaseService < Commits::CreateService
ValidationError = Class.new(StandardError) def initialize(*args)
super
def execute
@start_project = params[:start_project] || @project
@start_branch = params[:start_branch]
@target_branch = params[:target_branch]
@author_email = params[:author_email]
@author_name = params[:author_name]
@commit_message = params[:commit_message] @commit_message = params[:commit_message]
@file_path = params[:file_path]
@previous_path = params[:previous_path]
@file_content = if params[:file_content_encoding] == 'base64'
Base64.decode64(params[:file_content])
else
params[:file_content]
end
@last_commit_sha = params[:last_commit_sha]
@author_email = params[:author_email]
@author_name = params[:author_name]
# Validate parameters
validate
# Create new branch if it different from start_branch
validate_target_branch if different_branch?
result = commit
if result
success(result: result)
else
error('Something went wrong. Your changes were not committed')
end
rescue Repository::CommitError, Gitlab::Git::Repository::InvalidBlobName, GitHooksService::PreReceiveError, ValidationError => ex
error(ex.message)
end
private
def different_branch?
@start_branch != @target_branch || @start_project != @project
end
def file_has_changed?
return false unless @last_commit_sha && last_commit
@last_commit_sha != last_commit.sha
end
def raise_error(message)
raise ValidationError.new(message)
end
def validate
allowed = ::Gitlab::UserAccess.new(current_user, project: project).can_push_to_branch?(@target_branch)
unless allowed
raise_error("You are not allowed to push into this branch")
end
if !@start_project.empty_repo? && !@start_project.repository.branch_exists?(@start_branch)
raise ValidationError, 'You can only create or edit files when you are on a branch'
end
if !project.empty_repo? && different_branch? && repository.branch_exists?(@branch_name)
raise ValidationError, "A branch called #{@branch_name} already exists. Switch to that branch in order to make changes"
end
end
def validate_target_branch @file_path = params[:file_path]
result = ValidateNewBranchService.new(project, current_user). @previous_path = params[:previous_path]
execute(@target_branch)
if result[:status] == :error @file_content = params[:file_content]
raise_error("Something went wrong when we tried to create #{@target_branch} for you: #{result[:message]}") @file_content = Base64.decode64(@file_content) if params[:file_content_encoding] == 'base64'
end
end end
end end
end end
module Files module Files
class CreateDirService < Files::BaseService class CreateDirService < Files::BaseService
def commit def create_commit!
repository.create_dir( repository.create_dir(
current_user, current_user,
@file_path, @file_path,
message: @commit_message, message: @commit_message,
branch_name: @target_branch, branch_name: @branch_name,
author_email: @author_email, author_email: @author_email,
author_name: @author_name, author_name: @author_name,
start_project: @start_project, start_project: @start_project,
start_branch_name: @start_branch) start_branch_name: @start_branch)
end end
def validate
super
unless @file_path =~ Gitlab::Regex.file_path_regex
raise_error(
'Your changes could not be committed, because the file path ' +
Gitlab::Regex.file_path_regex_message
)
end
end
end end
end end
module Files module Files
class CreateService < Files::BaseService class CreateService < Files::BaseService
def commit def create_commit!
repository.create_file( repository.create_file(
current_user, current_user,
@file_path, @file_path,
@file_content, @file_content,
message: @commit_message, message: @commit_message,
branch_name: @target_branch, branch_name: @branch_name,
author_email: @author_email, author_email: @author_email,
author_name: @author_name, author_name: @author_name,
start_project: @start_project, start_project: @start_project,
start_branch_name: @start_branch) start_branch_name: @start_branch)
end end
def validate
super
if @file_content.nil?
raise_error("You must provide content.")
end
if @file_path =~ Gitlab::Regex.directory_traversal_regex
raise_error(
'Your changes could not be committed, because the file name ' +
Gitlab::Regex.directory_traversal_regex_message
)
end
unless @file_path =~ Gitlab::Regex.file_path_regex
raise_error(
'Your changes could not be committed, because the file name ' +
Gitlab::Regex.file_path_regex_message
)
end
unless project.empty_repo?
@file_path.slice!(0) if @file_path.start_with?('/')
blob = repository.blob_at_branch(@start_branch, @file_path)
if blob
raise_error('Your changes could not be committed because a file with the same name already exists')
end
end
end
end end
end end
module Files module Files
class DestroyService < Files::BaseService class DeleteService < Files::BaseService
def commit def create_commit!
repository.delete_file( repository.delete_file(
current_user, current_user,
@file_path, @file_path,
message: @commit_message, message: @commit_message,
branch_name: @target_branch, branch_name: @branch_name,
author_email: @author_email, author_email: @author_email,
author_name: @author_name, author_name: @author_name,
start_project: @start_project, start_project: @start_project,
......
module Files module Files
class MultiService < Files::BaseService class MultiService < Files::BaseService
FileChangedError = Class.new(StandardError) def create_commit!
ACTIONS = %w[create update delete move].freeze
def commit
repository.multi_action( repository.multi_action(
user: current_user, user: current_user,
message: @commit_message, message: @commit_message,
branch_name: @target_branch, branch_name: @branch_name,
actions: params[:actions], actions: params[:actions],
author_email: @author_email, author_email: @author_email,
author_name: @author_name, author_name: @author_name,
...@@ -19,122 +15,17 @@ module Files ...@@ -19,122 +15,17 @@ module Files
private private
def validate def validate!
super super
params[:actions].each_with_index do |action, index| params[:actions].each do |action|
if ACTIONS.include?(action[:action].to_s) validate_action!(action)
action[:action] = action[:action].to_sym
else
raise_error("Unknown action type `#{action[:action]}`.")
end
unless action[:file_path].present?
raise_error("You must specify a file_path.")
end
action[:file_path].slice!(0) if action[:file_path] && action[:file_path].start_with?('/')
action[:previous_path].slice!(0) if action[:previous_path] && action[:previous_path].start_with?('/')
regex_check(action[:file_path])
regex_check(action[:previous_path]) if action[:previous_path]
if project.empty_repo? && action[:action] != :create
raise_error("No files to #{action[:action]}.")
end
validate_file_exists(action)
case action[:action]
when :create
validate_create(action)
when :update
validate_update(action)
when :delete
validate_delete(action)
when :move
validate_move(action, index)
end
end
end
def validate_file_exists(action)
return if action[:action] == :create
file_path = action[:file_path]
file_path = action[:previous_path] if action[:action] == :move
blob = repository.blob_at_branch(params[:branch], file_path)
unless blob
raise_error("File to be #{action[:action]}d `#{file_path}` does not exist.")
end end
end end
def last_commit def validate_action!(action)
Gitlab::Git::Commit.last_for_path(repository, @start_branch, @file_path) unless Gitlab::Git::Index::ACTIONS.include?(action[:action].to_s)
end raise_error("Unknown action '#{action[:action]}'")
def regex_check(file)
if file =~ Gitlab::Regex.directory_traversal_regex
raise_error(
'Your changes could not be committed, because the file name, `' +
file +
'` ' +
Gitlab::Regex.directory_traversal_regex_message
)
end
unless file =~ Gitlab::Regex.file_path_regex
raise_error(
'Your changes could not be committed, because the file name, `' +
file +
'` ' +
Gitlab::Regex.file_path_regex_message
)
end
end
def validate_create(action)
return if project.empty_repo?
if repository.blob_at_branch(params[:branch], action[:file_path])
raise_error("Your changes could not be committed because a file with the name `#{action[:file_path]}` already exists.")
end
if action[:content].nil?
raise_error("You must provide content.")
end
end
def validate_update(action)
if action[:content].nil?
raise_error("You must provide content.")
end
if file_has_changed?
raise FileChangedError.new("You are attempting to update a file `#{action[:file_path]}` that has changed since you started editing it.")
end
end
def validate_delete(action)
end
def validate_move(action, index)
if action[:previous_path].nil?
raise_error("You must supply the original file path when moving file `#{action[:file_path]}`.")
end
blob = repository.blob_at_branch(params[:branch], action[:file_path])
if blob
raise_error("Move destination `#{action[:file_path]}` already exists.")
end
if action[:content].nil?
blob = repository.blob_at_branch(params[:branch], action[:previous_path])
blob.load_all_data!(repository) if blob.truncated?
params[:actions][index][:content] = blob.data
end end
end end
end end
......
...@@ -2,10 +2,16 @@ module Files ...@@ -2,10 +2,16 @@ module Files
class UpdateService < Files::BaseService class UpdateService < Files::BaseService
FileChangedError = Class.new(StandardError) FileChangedError = Class.new(StandardError)
def commit def initialize(*args)
super
@last_commit_sha = params[:last_commit_sha]
end
def create_commit!
repository.update_file(current_user, @file_path, @file_content, repository.update_file(current_user, @file_path, @file_content,
message: @commit_message, message: @commit_message,
branch_name: @target_branch, branch_name: @branch_name,
previous_path: @previous_path, previous_path: @previous_path,
author_email: @author_email, author_email: @author_email,
author_name: @author_name, author_name: @author_name,
...@@ -15,21 +21,23 @@ module Files ...@@ -15,21 +21,23 @@ module Files
private private
def validate def file_has_changed?
super return false unless @last_commit_sha && last_commit
if @file_content.nil?
raise_error("You must provide content.")
end
if file_has_changed? @last_commit_sha != last_commit.sha
raise FileChangedError.new("You are attempting to update a file that has changed since you started editing it.")
end
end end
def last_commit def last_commit
@last_commit ||= Gitlab::Git::Commit. @last_commit ||= Gitlab::Git::Commit.
last_for_path(@start_project.repository, @start_branch, @file_path) last_for_path(@start_project.repository, @start_branch, @file_path)
end end
def validate!
super
if file_has_changed?
raise FileChangedError, "You are attempting to update a file that has changed since you started editing it."
end
end
end end
end end
...@@ -8,10 +8,7 @@ class ValidateNewBranchService < BaseService ...@@ -8,10 +8,7 @@ class ValidateNewBranchService < BaseService
return error('Branch name is invalid') return error('Branch name is invalid')
end end
repository = project.repository if project.repository.branch_exists?(branch_name)
existing_branch = repository.find_branch(branch_name)
if existing_branch
return error('Branch already exists') return error('Branch already exists')
end end
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
- if @conflict - if @conflict
.alert.alert-danger .alert.alert-danger
Someone edited the file the same time you did. Please check out Someone edited the file the same time you did. Please check out
= link_to "the file", namespace_project_blob_path(@project.namespace, @project, tree_join(@target_branch, @file_path)), target: "_blank", rel: 'noopener noreferrer' = link_to "the file", namespace_project_blob_path(@project.namespace, @project, tree_join(@branch_name, @file_path)), target: "_blank", rel: 'noopener noreferrer'
and make sure your changes will not unintentionally remove theirs. and make sure your changes will not unintentionally remove theirs.
.editor-title-row .editor-title-row
%h3.page-title.blob-edit-page-title %h3.page-title.blob-edit-page-title
......
...@@ -70,7 +70,7 @@ ...@@ -70,7 +70,7 @@
= link_to 'Set up Koding', add_koding_stack_path(@project) = link_to 'Set up Koding', add_koding_stack_path(@project)
- if @repository.gitlab_ci_yml.blank? && @project.deployment_service.present? - if @repository.gitlab_ci_yml.blank? && @project.deployment_service.present?
%li.missing %li.missing
= link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml', commit_message: 'Set up auto deploy', target_branch: 'auto-deploy', context: 'autodeploy') do = link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml', commit_message: 'Set up auto deploy', branch_name: 'auto-deploy', context: 'autodeploy') do
Set up auto deploy Set up auto deploy
- if @repository.commit - if @repository.commit
......
- dropdown_toggle_text = @target_branch || tree_edit_branch - dropdown_toggle_text = @branch_name || tree_edit_branch
= hidden_field_tag 'target_branch', dropdown_toggle_text = hidden_field_tag 'branch_name', dropdown_toggle_text
.dropdown .dropdown
= dropdown_toggle dropdown_toggle_text, { toggle: 'dropdown', selected: dropdown_toggle_text, field_name: 'target_branch', form_id: '.js-edit-blob-form', refs_url: namespace_project_branches_path(@project.namespace, @project) }, { toggle_class: 'js-project-branches-dropdown js-target-branch' } = dropdown_toggle dropdown_toggle_text, { toggle: 'dropdown', selected: dropdown_toggle_text, field_name: 'branch_name', form_id: '.js-edit-blob-form', refs_url: namespace_project_branches_path(@project.namespace, @project) }, { toggle_class: 'js-project-branches-dropdown js-target-branch' }
.dropdown-menu.dropdown-menu-selectable.dropdown-menu-paging.dropdown-menu-branches .dropdown-menu.dropdown-menu-selectable.dropdown-menu-paging.dropdown-menu-branches
= render partial: 'shared/projects/blob/branch_page_default' = render partial: 'shared/projects/blob/branch_page_default'
= render partial: 'shared/projects/blob/branch_page_create' = render partial: 'shared/projects/blob/branch_page_create'
= render 'shared/commit_message_container', placeholder: placeholder = render 'shared/commit_message_container', placeholder: placeholder
- if @project.empty_repo? - if @project.empty_repo?
= hidden_field_tag 'target_branch', @ref = hidden_field_tag 'branch_name', @ref
- else - else
- if can?(current_user, :push_code, @project) - if can?(current_user, :push_code, @project)
.form-group.branch .form-group.branch
= label_tag 'target_branch', 'Target branch', class: 'control-label' = label_tag 'branch_name', 'Target branch', class: 'control-label'
.col-sm-10 .col-sm-10
= render 'shared/branch_switcher' = render 'shared/branch_switcher'
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
= check_box_tag 'create_merge_request', 1, true, class: 'js-create-merge-request', id: "create_merge_request-#{nonce}" = 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 Start a <strong>new merge request</strong> with these changes
- else - else
= hidden_field_tag 'target_branch', @target_branch || tree_edit_branch = hidden_field_tag 'branch_name', @branch_name || tree_edit_branch
= hidden_field_tag 'create_merge_request', 1 = hidden_field_tag 'create_merge_request', 1
= hidden_field_tag 'original_branch', @ref, class: 'js-original-branch' = hidden_field_tag 'original_branch', @ref, class: 'js-original-branch'
...@@ -25,7 +25,5 @@ Feature: Revert Merge Requests ...@@ -25,7 +25,5 @@ Feature: Revert Merge Requests
@javascript @javascript
Scenario: I revert a merge request in a new merge request Scenario: I revert a merge request in a new merge request
Given I click on the revert button Given I click on the revert button
And I am on the Merge Request detail page
And I click on the revert button
And I revert the changes in a new merge request And I revert the changes in a new merge request
Then I should see the new merge request notice Then I should see the new merge request notice
...@@ -135,7 +135,7 @@ Feature: Project Source Browse Files ...@@ -135,7 +135,7 @@ Feature: Project Source Browse Files
And I fill the commit message And I fill the commit message
And I click on "Commit changes" And I click on "Commit changes"
Then I am on the new file page Then I am on the new file page
And I see a commit error message And I see "Path can contain only..."
@javascript @javascript
Scenario: I can create file with a directory name Scenario: I can create file with a directory name
......
...@@ -284,7 +284,11 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps ...@@ -284,7 +284,11 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end end
step 'I see "Unable to create directory"' do step 'I see "Unable to create directory"' do
expect(page).to have_content('Directory already exists') expect(page).to have_content('A directory with this name already exists')
end
step 'I see "Path can contain only..."' do
expect(page).to have_content('Path can contain only')
end end
step 'I see a commit error message' do step 'I see a commit error message' do
......
...@@ -62,7 +62,7 @@ module API ...@@ -62,7 +62,7 @@ module API
post ":id/repository/commits" do post ":id/repository/commits" do
authorize! :push_code, user_project authorize! :push_code, user_project
attrs = declared_params.merge(start_branch: declared_params[:branch], target_branch: declared_params[:branch]) attrs = declared_params.merge(start_branch: declared_params[:branch], branch_name: declared_params[:branch])
result = ::Files::MultiService.new(user_project, current_user, attrs).execute result = ::Files::MultiService.new(user_project, current_user, attrs).execute
...@@ -140,7 +140,7 @@ module API ...@@ -140,7 +140,7 @@ module API
commit_params = { commit_params = {
commit: commit, commit: commit,
start_branch: params[:branch], start_branch: params[:branch],
target_branch: params[:branch] branch_name: params[:branch]
} }
result = ::Commits::CherryPickService.new(user_project, current_user, commit_params).execute result = ::Commits::CherryPickService.new(user_project, current_user, commit_params).execute
......
...@@ -5,7 +5,7 @@ module API ...@@ -5,7 +5,7 @@ module API
{ {
file_path: attrs[:file_path], file_path: attrs[:file_path],
start_branch: attrs[:branch], start_branch: attrs[:branch],
target_branch: attrs[:branch], branch_name: attrs[:branch],
commit_message: attrs[:commit_message], commit_message: attrs[:commit_message],
file_content: attrs[:content], file_content: attrs[:content],
file_content_encoding: attrs[:encoding], file_content_encoding: attrs[:encoding],
...@@ -130,7 +130,7 @@ module API ...@@ -130,7 +130,7 @@ module API
authorize! :push_code, user_project authorize! :push_code, user_project
file_params = declared_params(include_missing: false) file_params = declared_params(include_missing: false)
result = ::Files::DestroyService.new(user_project, current_user, commit_params(file_params)).execute result = ::Files::DeleteService.new(user_project, current_user, commit_params(file_params)).execute
if result[:status] != :success if result[:status] != :success
render_api_error!(result[:message], 400) render_api_error!(result[:message], 400)
......
...@@ -53,7 +53,7 @@ module API ...@@ -53,7 +53,7 @@ module API
attrs = declared_params.dup attrs = declared_params.dup
branch = attrs.delete(:branch_name) branch = attrs.delete(:branch_name)
attrs.merge!(branch: branch, start_branch: branch, target_branch: branch) attrs.merge!(start_branch: branch, branch_name: branch)
result = ::Files::MultiService.new(user_project, current_user, attrs).execute result = ::Files::MultiService.new(user_project, current_user, attrs).execute
...@@ -131,7 +131,7 @@ module API ...@@ -131,7 +131,7 @@ module API
commit_params = { commit_params = {
commit: commit, commit: commit,
start_branch: params[:branch], start_branch: params[:branch],
target_branch: params[:branch] branch_name: params[:branch]
} }
result = ::Commits::CherryPickService.new(user_project, current_user, commit_params).execute result = ::Commits::CherryPickService.new(user_project, current_user, commit_params).execute
......
...@@ -6,7 +6,7 @@ module API ...@@ -6,7 +6,7 @@ module API
{ {
file_path: attrs[:file_path], file_path: attrs[:file_path],
start_branch: attrs[:branch], start_branch: attrs[:branch],
target_branch: attrs[:branch], branch_name: attrs[:branch],
commit_message: attrs[:commit_message], commit_message: attrs[:commit_message],
file_content: attrs[:content], file_content: attrs[:content],
file_content_encoding: attrs[:encoding], file_content_encoding: attrs[:encoding],
...@@ -123,7 +123,7 @@ module API ...@@ -123,7 +123,7 @@ module API
file_params = declared_params(include_missing: false) file_params = declared_params(include_missing: false)
file_params[:branch] = file_params.delete(:branch_name) file_params[:branch] = file_params.delete(:branch_name)
result = ::Files::DestroyService.new(user_project, current_user, commit_params(file_params)).execute result = ::Files::DeleteService.new(user_project, current_user, commit_params(file_params)).execute
if result[:status] == :success if result[:status] == :success
status(200) status(200)
......
module Gitlab module Gitlab
module Git module Git
class Index class Index
IndexError = Class.new(StandardError)
DEFAULT_MODE = 0o100644 DEFAULT_MODE = 0o100644
ACTIONS = %w(create create_dir update move delete).freeze
attr_reader :repository, :raw_index attr_reader :repository, :raw_index
def initialize(repository) def initialize(repository)
...@@ -23,9 +27,8 @@ module Gitlab ...@@ -23,9 +27,8 @@ module Gitlab
def create(options) def create(options)
options = normalize_options(options) options = normalize_options(options)
file_entry = get(options[:file_path]) if get(options[:file_path])
if file_entry raise IndexError, "A file with this name already exists"
raise Gitlab::Git::Repository::InvalidBlobName.new("Filename already exists")
end end
add_blob(options) add_blob(options)
...@@ -34,13 +37,12 @@ module Gitlab ...@@ -34,13 +37,12 @@ module Gitlab
def create_dir(options) def create_dir(options)
options = normalize_options(options) options = normalize_options(options)
file_entry = get(options[:file_path]) if get(options[:file_path])
if file_entry raise IndexError, "A file with this name already exists"
raise Gitlab::Git::Repository::InvalidBlobName.new("Directory already exists as a file")
end end
if dir_exists?(options[:file_path]) if dir_exists?(options[:file_path])
raise Gitlab::Git::Repository::InvalidBlobName.new("Directory already exists") raise IndexError, "A directory with this name already exists"
end end
options = options.dup options = options.dup
...@@ -55,7 +57,7 @@ module Gitlab ...@@ -55,7 +57,7 @@ module Gitlab
file_entry = get(options[:file_path]) file_entry = get(options[:file_path])
unless file_entry unless file_entry
raise Gitlab::Git::Repository::InvalidBlobName.new("File doesn't exist") raise IndexError, "A file with this name doesn't exist"
end end
add_blob(options, mode: file_entry[:mode]) add_blob(options, mode: file_entry[:mode])
...@@ -66,7 +68,11 @@ module Gitlab ...@@ -66,7 +68,11 @@ module Gitlab
file_entry = get(options[:previous_path]) file_entry = get(options[:previous_path])
unless file_entry unless file_entry
raise Gitlab::Git::Repository::InvalidBlobName.new("File doesn't exist") raise IndexError, "A file with this name doesn't exist"
end
if get(options[:file_path])
raise IndexError, "A file with this name already exists"
end end
raw_index.remove(options[:previous_path]) raw_index.remove(options[:previous_path])
...@@ -77,9 +83,8 @@ module Gitlab ...@@ -77,9 +83,8 @@ module Gitlab
def delete(options) def delete(options)
options = normalize_options(options) options = normalize_options(options)
file_entry = get(options[:file_path]) unless get(options[:file_path])
unless file_entry raise IndexError, "A file with this name doesn't exist"
raise Gitlab::Git::Repository::InvalidBlobName.new("File doesn't exist")
end end
raw_index.remove(options[:file_path]) raw_index.remove(options[:file_path])
...@@ -95,10 +100,20 @@ module Gitlab ...@@ -95,10 +100,20 @@ module Gitlab
end end
def normalize_path(path) def normalize_path(path)
unless path
raise IndexError, "You must provide a file path"
end
pathname = Gitlab::Git::PathHelper.normalize_path(path.dup) pathname = Gitlab::Git::PathHelper.normalize_path(path.dup)
if pathname.each_filename.include?('..') pathname.each_filename do |segment|
raise Gitlab::Git::Repository::InvalidBlobName.new('Invalid path') if segment == '..'
raise IndexError, 'Path cannot include directory traversal'
end
unless segment =~ Gitlab::Regex.file_name_regex
raise IndexError, "Path #{Gitlab::Regex.file_name_regex_message}"
end
end end
pathname.to_s pathname.to_s
...@@ -106,6 +121,10 @@ module Gitlab ...@@ -106,6 +121,10 @@ module Gitlab
def add_blob(options, mode: nil) def add_blob(options, mode: nil)
content = options[:content] content = options[:content]
unless content
raise IndexError, "You must provide content"
end
content = Base64.decode64(content) if options[:encoding] == 'base64' content = Base64.decode64(content) if options[:encoding] == 'base64'
detect = CharlockHolmes::EncodingDetector.new.detect(content) detect = CharlockHolmes::EncodingDetector.new.detect(content)
...@@ -119,7 +138,7 @@ module Gitlab ...@@ -119,7 +138,7 @@ module Gitlab
raw_index.add(path: options[:file_path], oid: oid, mode: mode || DEFAULT_MODE) raw_index.add(path: options[:file_path], oid: oid, mode: mode || DEFAULT_MODE)
rescue Rugged::IndexError => e rescue Rugged::IndexError => e
raise Gitlab::Git::Repository::InvalidBlobName.new(e.message) raise IndexError, e.message
end end
end end
end end
......
...@@ -73,22 +73,6 @@ module Gitlab ...@@ -73,22 +73,6 @@ module Gitlab
"can contain only letters, digits, '_', '-', '@', '+' and '.'." "can contain only letters, digits, '_', '-', '@', '+' and '.'."
end end
def file_path_regex
@file_path_regex ||= /\A[[[:alnum:]]_\-\.\/\@]*\z/.freeze
end
def file_path_regex_message
"can contain only letters, digits, '_', '-', '@' and '.'. Separate directories with a '/'."
end
def directory_traversal_regex
@directory_traversal_regex ||= /\.{2}/.freeze
end
def directory_traversal_regex_message
"cannot include directory traversal."
end
def archive_formats_regex def archive_formats_regex
# |zip|tar| tar.gz | tar.bz2 | # |zip|tar| tar.gz | tar.bz2 |
@archive_formats_regex ||= /(zip|tar|tar\.gz|tgz|gz|tar\.bz2|tbz|tbz2|tb2|bz2)/.freeze @archive_formats_regex ||= /(zip|tar|tar\.gz|tgz|gz|tar\.bz2|tbz|tbz2|tb2|bz2)/.freeze
......
...@@ -106,7 +106,7 @@ describe Projects::BlobController do ...@@ -106,7 +106,7 @@ describe Projects::BlobController do
namespace_id: project.namespace, namespace_id: project.namespace,
project_id: project, project_id: project,
id: 'master/CHANGELOG', id: 'master/CHANGELOG',
target_branch: 'master', branch_name: 'master',
content: 'Added changes', content: 'Added changes',
commit_message: 'Update CHANGELOG' commit_message: 'Update CHANGELOG'
} }
...@@ -178,7 +178,7 @@ describe Projects::BlobController do ...@@ -178,7 +178,7 @@ describe Projects::BlobController do
context 'when editing on the original repository' do context 'when editing on the original repository' do
it "redirects to forked project new merge request" do it "redirects to forked project new merge request" do
default_params[:target_branch] = "fork-test-1" default_params[:branch_name] = "fork-test-1"
default_params[:create_merge_request] = 1 default_params[:create_merge_request] = 1
put :update, default_params put :update, default_params
......
...@@ -97,29 +97,29 @@ describe Projects::TreeController do ...@@ -97,29 +97,29 @@ describe Projects::TreeController do
project_id: project, project_id: project,
id: 'master', id: 'master',
dir_name: path, dir_name: path,
target_branch: target_branch, branch_name: branch_name,
commit_message: 'Test commit message') commit_message: 'Test commit message')
end end
context 'successful creation' do context 'successful creation' do
let(:path) { 'files/new_dir'} let(:path) { 'files/new_dir'}
let(:target_branch) { 'master-test'} let(:branch_name) { 'master-test'}
it 'redirects to the new directory' do it 'redirects to the new directory' do
expect(subject). expect(subject).
to redirect_to("/#{project.path_with_namespace}/tree/#{target_branch}/#{path}") to redirect_to("/#{project.path_with_namespace}/tree/#{branch_name}/#{path}")
expect(flash[:notice]).to eq('The directory has been successfully created.') expect(flash[:notice]).to eq('The directory has been successfully created.')
end end
end end
context 'unsuccessful creation' do context 'unsuccessful creation' do
let(:path) { 'README.md' } let(:path) { 'README.md' }
let(:target_branch) { 'master'} let(:branch_name) { 'master'}
it 'does not allow overwriting of existing files' do it 'does not allow overwriting of existing files' do
expect(subject). expect(subject).
to redirect_to("/#{project.path_with_namespace}/tree/master") to redirect_to("/#{project.path_with_namespace}/tree/master")
expect(flash[:alert]).to eq('Directory already exists as a file') expect(flash[:alert]).to eq('A file with this name already exists')
end end
end end
end end
......
...@@ -77,7 +77,7 @@ feature 'New blob creation', feature: true, js: true do ...@@ -77,7 +77,7 @@ feature 'New blob creation', feature: true, js: true do
project, project,
user, user,
start_branch: 'master', start_branch: 'master',
target_branch: 'master', branch_name: 'master',
commit_message: 'Create file', commit_message: 'Create file',
file_path: 'feature.rb', file_path: 'feature.rb',
file_content: content file_content: content
...@@ -87,7 +87,7 @@ feature 'New blob creation', feature: true, js: true do ...@@ -87,7 +87,7 @@ feature 'New blob creation', feature: true, js: true do
end end
scenario 'shows error message' do scenario 'shows error message' do
expect(page).to have_content('Your changes could not be committed because a file with the same name already exists') expect(page).to have_content('A file with this name already exists')
expect(page).to have_content('New file') expect(page).to have_content('New file')
expect(page).to have_content('NextFeature') expect(page).to have_content('NextFeature')
end end
......
...@@ -29,16 +29,16 @@ feature 'User wants to create a file', feature: true do ...@@ -29,16 +29,16 @@ feature 'User wants to create a file', feature: true do
scenario 'directory name contains Chinese characters' do scenario 'directory name contains Chinese characters' do
submit_new_file(file_name: '中文/测试.md') submit_new_file(file_name: '中文/测试.md')
expect(page).to have_content 'The file has been successfully created.' expect(page).to have_content 'The file has been successfully created'
end end
scenario 'file name contains invalid characters' do scenario 'file name contains invalid characters' do
submit_new_file(file_name: '\\') submit_new_file(file_name: '\\')
expect(page).to have_content 'Your changes could not be committed, because the file name can contain only' expect(page).to have_content 'Path can contain only'
end end
scenario 'file name contains directory traversal' do scenario 'file name contains directory traversal' do
submit_new_file(file_name: '../README.md') submit_new_file(file_name: '../README.md')
expect(page).to have_content 'Your changes could not be committed, because the file name cannot include directory traversal.' expect(page).to have_content 'Path cannot include directory traversal'
end end
end end
...@@ -8,7 +8,7 @@ feature 'User wants to edit a file', feature: true do ...@@ -8,7 +8,7 @@ feature 'User wants to edit a file', feature: true do
let(:commit_params) do let(:commit_params) do
{ {
start_branch: project.default_branch, start_branch: project.default_branch,
target_branch: project.default_branch, branch_name: project.default_branch,
commit_message: "Committing First Update", commit_message: "Committing First Update",
file_path: ".gitignore", file_path: ".gitignore",
file_content: "First Update", file_content: "First Update",
......
...@@ -25,7 +25,7 @@ describe 'View on environment', js: true do ...@@ -25,7 +25,7 @@ describe 'View on environment', js: true do
project, project,
user, user,
start_branch: branch_name, start_branch: branch_name,
target_branch: branch_name, branch_name: branch_name,
commit_message: "Add .gitlab/route-map.yml", commit_message: "Add .gitlab/route-map.yml",
file_path: '.gitlab/route-map.yml', file_path: '.gitlab/route-map.yml',
file_content: route_map file_content: route_map
...@@ -36,7 +36,7 @@ describe 'View on environment', js: true do ...@@ -36,7 +36,7 @@ describe 'View on environment', js: true do
project, project,
user, user,
start_branch: branch_name, start_branch: branch_name,
target_branch: branch_name, branch_name: branch_name,
commit_message: "Update feature", commit_message: "Update feature",
file_path: file_path, file_path: file_path,
file_content: "# Noop" file_content: "# Noop"
......
...@@ -100,7 +100,7 @@ describe Gitlab::Diff::PositionTracer, lib: true do ...@@ -100,7 +100,7 @@ describe Gitlab::Diff::PositionTracer, lib: true do
project, project,
current_user, current_user,
start_branch: branch_name, start_branch: branch_name,
target_branch: branch_name, branch_name: branch_name,
commit_message: "Create file", commit_message: "Create file",
file_path: file_name, file_path: file_name,
file_content: content file_content: content
...@@ -113,7 +113,7 @@ describe Gitlab::Diff::PositionTracer, lib: true do ...@@ -113,7 +113,7 @@ describe Gitlab::Diff::PositionTracer, lib: true do
project, project,
current_user, current_user,
start_branch: branch_name, start_branch: branch_name,
target_branch: branch_name, branch_name: branch_name,
commit_message: "Update file", commit_message: "Update file",
file_path: file_name, file_path: file_name,
file_content: content file_content: content
...@@ -122,11 +122,11 @@ describe Gitlab::Diff::PositionTracer, lib: true do ...@@ -122,11 +122,11 @@ describe Gitlab::Diff::PositionTracer, lib: true do
end end
def delete_file(branch_name, file_name) def delete_file(branch_name, file_name)
Files::DestroyService.new( Files::DeleteService.new(
project, project,
current_user, current_user,
start_branch: branch_name, start_branch: branch_name,
target_branch: branch_name, branch_name: branch_name,
commit_message: "Delete file", commit_message: "Delete file",
file_path: file_name file_path: file_name
).execute ).execute
......
...@@ -33,7 +33,7 @@ describe Gitlab::Git::Index, seed_helper: true do ...@@ -33,7 +33,7 @@ describe Gitlab::Git::Index, seed_helper: true do
end end
it 'raises an error' do it 'raises an error' do
expect { index.create(options) }.to raise_error('Filename already exists') expect { index.create(options) }.to raise_error('A file with this name already exists')
end end
end end
...@@ -89,7 +89,7 @@ describe Gitlab::Git::Index, seed_helper: true do ...@@ -89,7 +89,7 @@ describe Gitlab::Git::Index, seed_helper: true do
end end
it 'raises an error' do it 'raises an error' do
expect { index.create_dir(options) }.to raise_error('Directory already exists as a file') expect { index.create_dir(options) }.to raise_error('A file with this name already exists')
end end
end end
...@@ -99,7 +99,7 @@ describe Gitlab::Git::Index, seed_helper: true do ...@@ -99,7 +99,7 @@ describe Gitlab::Git::Index, seed_helper: true do
end end
it 'raises an error' do it 'raises an error' do
expect { index.create_dir(options) }.to raise_error('Directory already exists') expect { index.create_dir(options) }.to raise_error('A directory with this name already exists')
end end
end end
end end
...@@ -118,7 +118,7 @@ describe Gitlab::Git::Index, seed_helper: true do ...@@ -118,7 +118,7 @@ describe Gitlab::Git::Index, seed_helper: true do
end end
it 'raises an error' do it 'raises an error' do
expect { index.update(options) }.to raise_error("File doesn't exist") expect { index.update(options) }.to raise_error("A file with this name doesn't exist")
end end
end end
...@@ -156,7 +156,15 @@ describe Gitlab::Git::Index, seed_helper: true do ...@@ -156,7 +156,15 @@ describe Gitlab::Git::Index, seed_helper: true do
it 'raises an error' do it 'raises an error' do
options[:previous_path] = 'documents/story.txt' options[:previous_path] = 'documents/story.txt'
expect { index.move(options) }.to raise_error("File doesn't exist") expect { index.move(options) }.to raise_error("A file with this name doesn't exist")
end
end
context 'when a file at the new path already exists' do
it 'raises an error' do
options[:file_path] = 'CHANGELOG'
expect { index.move(options) }.to raise_error("A file with this name already exists")
end end
end end
...@@ -203,7 +211,7 @@ describe Gitlab::Git::Index, seed_helper: true do ...@@ -203,7 +211,7 @@ describe Gitlab::Git::Index, seed_helper: true do
end end
it 'raises an error' do it 'raises an error' do
expect { index.delete(options) }.to raise_error("File doesn't exist") expect { index.delete(options) }.to raise_error("A file with this name doesn't exist")
end end
end end
......
...@@ -211,7 +211,7 @@ describe Gitlab::GitAccess, lib: true do ...@@ -211,7 +211,7 @@ describe Gitlab::GitAccess, lib: true do
target_branch = project.repository.lookup('feature') target_branch = project.repository.lookup('feature')
source_branch = project.repository.create_file( source_branch = project.repository.create_file(
user, user,
'John Doe', 'filename',
'This is the file content', 'This is the file content',
message: 'This is a good commit message', message: 'This is a good commit message',
branch_name: unprotected_branch) branch_name: unprotected_branch)
......
...@@ -32,12 +32,6 @@ describe Gitlab::Regex, lib: true do ...@@ -32,12 +32,6 @@ describe Gitlab::Regex, lib: true do
it { is_expected.to match('foo@bar') } it { is_expected.to match('foo@bar') }
end end
describe '.file_path_regex' do
subject { described_class.file_path_regex }
it { is_expected.to match('foo@/bar') }
end
describe '.environment_slug_regex' do describe '.environment_slug_regex' do
subject { described_class.environment_slug_regex } subject { described_class.environment_slug_regex }
......
...@@ -599,8 +599,7 @@ describe API::Commits, api: true do ...@@ -599,8 +599,7 @@ describe API::Commits, api: true do
post api("/projects/#{project.id}/repository/commits/#{master_pickable_commit.id}/cherry_pick", user), branch: 'markdown' post api("/projects/#{project.id}/repository/commits/#{master_pickable_commit.id}/cherry_pick", user), branch: 'markdown'
expect(response).to have_http_status(400) expect(response).to have_http_status(400)
expect(json_response['message']).to eq('Sorry, we cannot cherry-pick this commit automatically. expect(json_response['message']).to include('Sorry, we cannot cherry-pick this commit automatically.')
A cherry-pick may have already been performed with this commit, or a more recent commit may have updated some of its content.')
end end
it 'returns 400 if you are not allowed to push to the target branch' do it 'returns 400 if you are not allowed to push to the target branch' do
......
...@@ -205,7 +205,7 @@ describe API::Files, api: true do ...@@ -205,7 +205,7 @@ describe API::Files, api: true do
it "returns a 400 if editor fails to create file" do it "returns a 400 if editor fails to create file" do
allow_any_instance_of(Repository).to receive(:create_file). allow_any_instance_of(Repository).to receive(:create_file).
and_return(false) and_raise(Repository::CommitError, 'Cannot create file')
post api(route("any%2Etxt"), user), valid_params post api(route("any%2Etxt"), user), valid_params
...@@ -299,8 +299,8 @@ describe API::Files, api: true do ...@@ -299,8 +299,8 @@ describe API::Files, api: true do
expect(response).to have_http_status(400) expect(response).to have_http_status(400)
end end
it "returns a 400 if fails to create file" do it "returns a 400 if fails to delete file" do
allow_any_instance_of(Repository).to receive(:delete_file).and_return(false) allow_any_instance_of(Repository).to receive(:delete_file).and_raise(Repository::CommitError, 'Cannot delete file')
delete api(route(file_path), user), valid_params delete api(route(file_path), user), valid_params
......
...@@ -485,8 +485,7 @@ describe API::V3::Commits, api: true do ...@@ -485,8 +485,7 @@ describe API::V3::Commits, api: true do
post v3_api("/projects/#{project.id}/repository/commits/#{master_pickable_commit.id}/cherry_pick", user), branch: 'markdown' post v3_api("/projects/#{project.id}/repository/commits/#{master_pickable_commit.id}/cherry_pick", user), branch: 'markdown'
expect(response).to have_http_status(400) expect(response).to have_http_status(400)
expect(json_response['message']).to eq('Sorry, we cannot cherry-pick this commit automatically. expect(json_response['message']).to include('Sorry, we cannot cherry-pick this commit automatically.')
A cherry-pick may have already been performed with this commit, or a more recent commit may have updated some of its content.')
end end
it 'returns 400 if you are not allowed to push to the target branch' do it 'returns 400 if you are not allowed to push to the target branch' do
......
...@@ -129,7 +129,7 @@ describe API::V3::Files, api: true do ...@@ -129,7 +129,7 @@ describe API::V3::Files, api: true do
it "returns a 400 if editor fails to create file" do it "returns a 400 if editor fails to create file" do
allow_any_instance_of(Repository).to receive(:create_file). allow_any_instance_of(Repository).to receive(:create_file).
and_return(false) and_raise(Repository::CommitError, 'Cannot create file')
post v3_api("/projects/#{project.id}/repository/files", user), valid_params post v3_api("/projects/#{project.id}/repository/files", user), valid_params
...@@ -229,8 +229,8 @@ describe API::V3::Files, api: true do ...@@ -229,8 +229,8 @@ describe API::V3::Files, api: true do
expect(response).to have_http_status(400) expect(response).to have_http_status(400)
end end
it "returns a 400 if fails to create file" do it "returns a 400 if fails to delete file" do
allow_any_instance_of(Repository).to receive(:delete_file).and_return(false) allow_any_instance_of(Repository).to receive(:delete_file).and_raise(Repository::CommitError, 'Cannot delete file')
delete v3_api("/projects/#{project.id}/repository/files", user), valid_params delete v3_api("/projects/#{project.id}/repository/files", user), valid_params
......
...@@ -7,7 +7,7 @@ describe Files::UpdateService do ...@@ -7,7 +7,7 @@ describe Files::UpdateService do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:file_path) { 'files/ruby/popen.rb' } let(:file_path) { 'files/ruby/popen.rb' }
let(:new_contents) { 'New Content' } let(:new_contents) { 'New Content' }
let(:target_branch) { project.default_branch } let(:branch_name) { project.default_branch }
let(:last_commit_sha) { nil } let(:last_commit_sha) { nil }
let(:commit_params) do let(:commit_params) do
...@@ -19,7 +19,7 @@ describe Files::UpdateService do ...@@ -19,7 +19,7 @@ describe Files::UpdateService do
last_commit_sha: last_commit_sha, last_commit_sha: last_commit_sha,
start_project: project, start_project: project,
start_branch: project.default_branch, start_branch: project.default_branch,
target_branch: target_branch branch_name: branch_name
} }
end end
...@@ -73,7 +73,7 @@ describe Files::UpdateService do ...@@ -73,7 +73,7 @@ describe Files::UpdateService do
end end
context 'when target branch is different than source branch' do context 'when target branch is different than source branch' do
let(:target_branch) { "#{project.default_branch}-new" } let(:branch_name) { "#{project.default_branch}-new" }
it 'fires hooks only once' do it 'fires hooks only once' do
expect(GitHooksService).to receive(:new).once.and_call_original expect(GitHooksService).to receive(:new).once.and_call_original
......
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