Read about our upcoming Code of Conduct on this issue

Commit d170133b authored by Douwe Maan's avatar Douwe Maan Committed by Robert Speicher
Browse files

Refactor changing files in web UI

parent c1780ce7ec42
......@@ -35,7 +35,7 @@ export default class BlobFileDropzone {
this.removeFile(file);
});
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('commit_message', form.find('.js-commit-message').val());
});
......
module CreatesCommit
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)
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(
start_project: @mr_target_project,
start_branch: @mr_target_branch,
target_branch: @mr_source_branch
start_project: @project,
start_branch: @start_branch,
branch_name: @branch_name
)
result = service.new(
@mr_source_project, current_user, commit_params).execute
result = service.new(@project_to_commit_into, current_user, commit_params).execute
if result[:status] == :success
update_flash_notice(success_notice)
......@@ -72,30 +84,30 @@ def final_success_path(success_path)
def new_merge_request_path
new_namespace_project_merge_request_path(
@mr_source_project.namespace,
@mr_source_project,
@project_to_commit_into.namespace,
@project_to_commit_into,
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
source_project_id: @project_to_commit_into.id,
target_project_id: @project.id,
source_branch: @branch_name,
target_branch: @start_branch
}
)
end
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
def merge_request_exists?
return @merge_request if defined?(@merge_request)
@merge_request = MergeRequestsFinder.new(current_user, project_id: @mr_target_project.id).execute.opened.
find_by(source_branch: @mr_source_branch, target_branch: @mr_target_branch, source_project_id: @mr_source_project)
@merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened.
find_by(source_project_id: @project_to_commit_into, source_branch: @branch_name, target_branch: @start_branch)
end
def different_project?
@mr_source_project != @mr_target_project
@project_to_commit_into != @project
end
def create_merge_request?
......@@ -103,22 +115,6 @@ def create_merge_request?
# as the target branch in the same project,
# we don't want to create a merge request.
params[:create_merge_request].present? &&
(different_project? || @mr_target_branch != @mr_source_branch)
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
(different_project? || @start_branch != @branch_name)
end
end
......@@ -89,9 +89,4 @@ def apply_diff_view_cookie!
def builds_enabled
return render_404 unless @project.feature_available?(:builds, current_user)
end
def update_ref
branch_exists = @repository.find_branch(@target_branch)
@ref = @target_branch if branch_exists
end
end
......@@ -25,10 +25,10 @@ def new
end
def create
update_ref
set_start_branch_to_branch_name
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_path: namespace_project_new_blob_path(@project.namespace, @project, @ref))
end
......@@ -69,10 +69,10 @@ def preview
end
def destroy
create_commit(Files::DestroyService, 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))
create_commit(Files::DeleteService, success_notice: "The file has been successfully deleted.",
success_path: -> { namespace_project_tree_path(@project.namespace, @project, @branch_name) },
failure_view: :show,
failure_path: namespace_project_blob_path(@project.namespace, @project, @id))
end
def diff
......@@ -127,16 +127,16 @@ def assign_blob_vars
def after_edit_path
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) +
"##{hexdigest(@path)}"
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
def editor_variables
@target_branch = params[:target_branch]
@branch_name = params[:branch_name]
@file_path =
if action_name.to_s == 'create'
......
......@@ -56,9 +56,7 @@ def revert
return render_404 if @start_branch.blank?
@target_branch = create_new_branch? ? @commit.revert_branch_name : @start_branch
@mr_target_branch = @start_branch
@branch_name = create_new_branch? ? @commit.revert_branch_name : @start_branch
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)
......@@ -69,9 +67,7 @@ def cherry_pick
return render_404 if @start_branch.blank?
@target_branch = create_new_branch? ? @commit.cherry_pick_branch_name : @start_branch
@mr_target_branch = @start_branch
@branch_name = create_new_branch? ? @commit.cherry_pick_branch_name : @start_branch
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)
......@@ -84,7 +80,7 @@ def create_new_branch?
end
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
def failed_change_path
......
......@@ -34,16 +34,16 @@ def show
def create_dir
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.",
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))
end
private
def assign_dir_vars
@target_branch = params[:target_branch]
@branch_name = params[:branch_name]
@dir_name = File.join(@path, params[:dir_name])
@commit_params = {
......
......@@ -272,14 +272,14 @@ def project_last_activity(project)
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(
project.namespace,
project,
project.default_branch || 'master',
file_name: file_name,
commit_message: commit_message || "Add #{file_name.downcase}",
target_branch: target_branch,
branch_name: branch_name,
context: context
)
end
......
......@@ -35,7 +35,7 @@ def tree_join(*args)
end
def on_top_of_branch?(project = @project, ref = @ref)
project.repository.branch_names.include?(ref)
project.repository.branch_exists?(ref)
end
def can_edit_tree?(project = nil, ref = nil)
......
module Commits
class ChangeService < ::BaseService
ValidationError = Class.new(StandardError)
ChangeError = Class.new(StandardError)
class ChangeService < Commits::CreateService
def initialize(*args)
super
def execute
@start_project = params[:start_project] || @project
@start_branch = params[:start_branch]
@target_branch = params[:target_branch]
@commit = params[:commit]
check_push_permissions
commit
rescue Repository::CommitError, Gitlab::Git::Repository::InvalidBlobName, GitHooksService::PreReceiveError,
ValidationError, ChangeError => ex
error(ex.message)
end
private
def commit
raise NotImplementedError
end
def commit_change(action)
raise NotImplementedError unless repository.respond_to?(action)
validate_target_branch if different_branch?
repository.public_send(
action,
current_user,
@commit,
@target_branch,
@branch_name,
start_project: @start_project,
start_branch_name: @start_branch)
success
rescue Repository::CreateTreeError
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
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
module Commits
class CherryPickService < ChangeService
def commit
def create_commit!
commit_change(:cherry_pick)
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
class RevertService < ChangeService
def commit
def create_commit!
commit_change(:revert)
end
end
......
module Files
class BaseService < ::BaseService
ValidationError = Class.new(StandardError)
def execute
@start_project = params[:start_project] || @project
@start_branch = params[:start_branch]
@target_branch = params[:target_branch]
class BaseService < Commits::CreateService
def initialize(*args)
super
@author_email = params[:author_email]
@author_name = params[:author_name]
@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
result = ValidateNewBranchService.new(project, current_user).
execute(@target_branch)
@file_path = params[:file_path]
@previous_path = params[:previous_path]
if result[:status] == :error
raise_error("Something went wrong when we tried to create #{@target_branch} for you: #{result[:message]}")
end
@file_content = params[:file_content]
@file_content = Base64.decode64(@file_content) if params[:file_content_encoding] == 'base64'
end
end
end
module Files
class CreateDirService < Files::BaseService
def commit
def create_commit!
repository.create_dir(
current_user,
@file_path,
message: @commit_message,
branch_name: @target_branch,
branch_name: @branch_name,
author_email: @author_email,
author_name: @author_name,
start_project: @start_project,
start_branch_name: @start_branch)
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
module Files
class CreateService < Files::BaseService
def commit
def create_commit!
repository.create_file(
current_user,
@file_path,
@file_content,
message: @commit_message,
branch_name: @target_branch,
branch_name: @branch_name,
author_email: @author_email,
author_name: @author_name,
start_project: @start_project,
start_branch_name: @start_branch)
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
module Files
class DestroyService < Files::BaseService
def commit
class DeleteService < Files::BaseService
def create_commit!
repository.delete_file(
current_user,
@file_path,
message: @commit_message,
branch_name: @target_branch,
branch_name: @branch_name,
author_email: @author_email,
author_name: @author_name,
start_project: @start_project,
......