Merge branch 'edit-on-fork' into 'master'
Automatically fork a project when not allowed to edit a file. Fixes #3215. To do: - [ ] Add tests ----- ## "Edit" button on file in a project the user does NOT have write access to ![edit_file](/uploads/7602157420768aef483a6586bba2d164/edit_file.png) ## Clicking will automatically create a fork ![during_fork](/uploads/32f4f5dff9f24ea99522000b0bf881c5/during_fork.png) ## When the fork has been created, the user is returned to the edit page on the original project with a notice ![edit_notice](/uploads/94ed1319404370ff1e9c0d672fb41e03/edit_notice.png) ## The user cannot change the target branch and is informed that editing will start an MR ![edit_footer](/uploads/4da68d4795c7177e575b7c434d16eeae/edit_footer.png) ## Hitting "Commit changes" will commit and start an MR from my fork to the origin project ![Screen_Shot_2015-12-17_at_23.38.08](/uploads/d777a4db6f38a5a1be84031694465bc1/Screen_Shot_2015-12-17_at_23.38.08.png) ----- ## "Create file, "Upload file" and "New directory" buttons in a project the user does NOT have write access to ![new_directory](/uploads/72f556248f30d6652523bbb4be01b3e0/new_directory.png) ## Clicking any of these options will automatically create a fork ![during_fork](/uploads/32f4f5dff9f24ea99522000b0bf881c5/during_fork.png) ## When the fork has been created, the user is returned to the tree page on the original project with a notice ![new_directory_notice](/uploads/a1a3e11308ae0e8f0913fae6813a37ed/new_directory_notice.png) ## Clicking "New directory" again will show the modal. The user cannot change the target branch and is informed that editing will start an MR ![new_dir](/uploads/99ca8cbfb2f70603e352b3fdf67b6281/new_dir.png) ## Hitting "Create directory" will commit and start an MR from my fork to the origin project ![Screen_Shot_2015-12-17_at_23.39.19](/uploads/3713d0235abf831361b803a6198c5bc1/Screen_Shot_2015-12-17_at_23.39.19.png) cc @dzaporozhets @skyruler See merge request !2145
This commit is contained in:
commit
76b7e24f85
|
@ -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
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
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')
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
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
|
|
@ -1,28 +0,0 @@
|
|||
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
|
|
@ -1,7 +1,7 @@
|
|||
# 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
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
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
|
||||
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
# 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
|
||||
def edit_blob_link(project = @project, ref = @ref, path = @path, options = {})
|
||||
return unless current_user
|
||||
|
||||
return unless blob && blob.text? && blob_editable?(blob)
|
||||
blob = project.repository.blob_at(ref, path) rescue nil
|
||||
|
||||
return unless blob && blob_text_viewable?(blob)
|
||||
|
||||
text = 'Edit'
|
||||
after = options[:after] || ''
|
||||
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 blob_editable?(blob, project = @project, ref = @ref)
|
||||
!blob.lfs_pointer? && allowed_tree_edit?(project, ref)
|
||||
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 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 = ""
|
||||
|
|
|
@ -592,47 +592,54 @@ class Repository
|
|||
Gitlab::Popen.popen(args, path_to_repo)
|
||||
end
|
||||
|
||||
def with_tmp_ref(oldrev = nil)
|
||||
random_string = SecureRandom.hex
|
||||
tmp_ref = "refs/tmp/#{random_string}/head"
|
||||
|
||||
if oldrev && !Gitlab::Git.blank_ref?(oldrev)
|
||||
rugged.references.create(tmp_ref, oldrev)
|
||||
end
|
||||
|
||||
# Make commit in 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?
|
||||
|
||||
# Create temporary ref
|
||||
random_string = SecureRandom.hex
|
||||
tmp_ref = "refs/tmp/#{random_string}/head"
|
||||
|
||||
unless was_empty
|
||||
oldrev = find_branch(branch).target
|
||||
rugged.references.create(tmp_ref, oldrev)
|
||||
end
|
||||
|
||||
# Make commit in tmp ref
|
||||
newrev = yield(tmp_ref)
|
||||
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
|
||||
unless newrev
|
||||
raise CommitError.new('Failed to create commit')
|
||||
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
|
||||
|
||||
# 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
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
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 is invalid')
|
||||
|
@ -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")
|
||||
|
|
|
@ -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}"
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -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: ' ', from_merge_request_id: @merge_request.id)
|
||||
from_merge_request_id: @merge_request.id)
|
||||
|
||||
|
||||
= 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
|
||||
|
|
|
@ -1,16 +1,22 @@
|
|||
= 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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue