diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index bfbadb3a2ac..53cc1a6f929 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -1.23.0 \ No newline at end of file +1.24.0 diff --git a/Gemfile b/Gemfile index 2d769284f91..2e465f8ced7 100644 --- a/Gemfile +++ b/Gemfile @@ -421,7 +421,7 @@ group :ed25519 do end # Gitaly GRPC client -gem 'gitaly-proto', '~> 1.12.0', require: 'gitaly' +gem 'gitaly-proto', '~> 1.13.0', require: 'gitaly' gem 'grpc', '~> 1.15.0' diff --git a/Gemfile.lock b/Gemfile.lock index e4791a98f2f..4d37075cdfa 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -279,7 +279,7 @@ GEM gettext_i18n_rails (>= 0.7.1) po_to_json (>= 1.0.0) rails (>= 3.2.0) - gitaly-proto (1.12.0) + gitaly-proto (1.13.0) grpc (~> 1.0) github-markup (1.7.0) gitlab-default_value_for (3.1.1) @@ -310,7 +310,7 @@ GEM representable (~> 3.0) retriable (>= 2.0, < 4.0) google-protobuf (3.6.1) - googleapis-common-protos-types (1.0.2) + googleapis-common-protos-types (1.0.3) google-protobuf (~> 3.0) googleauth (0.6.6) faraday (~> 0.12) @@ -1018,7 +1018,7 @@ DEPENDENCIES gettext (~> 3.2.2) gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails_js (~> 1.3) - gitaly-proto (~> 1.12.0) + gitaly-proto (~> 1.13.0) github-markup (~> 1.7.0) gitlab-default_value_for (~> 3.1.1) gitlab-markup (~> 1.6.5) diff --git a/app/services/commits/create_service.rb b/app/services/commits/create_service.rb index a3b87c20761..bb34a3d3352 100644 --- a/app/services/commits/create_service.rb +++ b/app/services/commits/create_service.rb @@ -11,6 +11,7 @@ module Commits @start_project = params[:start_project] || @project @start_branch = params[:start_branch] @branch_name = params[:branch_name] + @force = params[:force] || false end def execute @@ -42,6 +43,10 @@ module Commits @start_branch != @branch_name || @start_project != @project end + def force? + !!@force + end + def validate! validate_permissions! validate_on_branch! @@ -65,13 +70,13 @@ module Commits end def validate_branch_existence! - if !project.empty_repo? && different_branch? && repository.branch_exists?(@branch_name) + if !project.empty_repo? && different_branch? && repository.branch_exists?(@branch_name) && !force? 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) + result = ValidateNewBranchService.new(project, current_user).execute(@branch_name, force: force?) if result[:status] == :error raise_error("Something went wrong when we tried to create '#{@branch_name}' for you: #{result[:message]}") diff --git a/app/services/files/multi_service.rb b/app/services/files/multi_service.rb index 927634c2159..c1bc26c330a 100644 --- a/app/services/files/multi_service.rb +++ b/app/services/files/multi_service.rb @@ -46,7 +46,8 @@ module Files author_email: @author_email, author_name: @author_name, start_project: @start_project, - start_branch_name: @start_branch + start_branch_name: @start_branch, + force: force? ) rescue ArgumentError => e raise_error(e) diff --git a/app/services/validate_new_branch_service.rb b/app/services/validate_new_branch_service.rb index c19e2ec2043..3f4a59e5cee 100644 --- a/app/services/validate_new_branch_service.rb +++ b/app/services/validate_new_branch_service.rb @@ -3,14 +3,14 @@ require_relative 'base_service' class ValidateNewBranchService < BaseService - def execute(branch_name) + def execute(branch_name, force: false) valid_branch = Gitlab::GitRefValidator.validate(branch_name) unless valid_branch return error('Branch name is invalid') end - if project.repository.branch_exists?(branch_name) + if project.repository.branch_exists?(branch_name) && !force return error('Branch already exists') end diff --git a/changelogs/unreleased/45035-force-push-api.yml b/changelogs/unreleased/45035-force-push-api.yml new file mode 100644 index 00000000000..05f5a36ac38 --- /dev/null +++ b/changelogs/unreleased/45035-force-push-api.yml @@ -0,0 +1,5 @@ +--- +title: Accept force option to overwrite branch on commit via API +merge_request: 25286 +author: +type: added diff --git a/doc/api/commits.md b/doc/api/commits.md index 8d36ae7d559..442178aedff 100644 --- a/doc/api/commits.md +++ b/doc/api/commits.md @@ -79,6 +79,7 @@ POST /projects/:id/repository/commits | `author_email` | string | no | Specify the commit author's email address | | `author_name` | string | no | Specify the commit author's name | | `stats` | boolean | no | Include commit stats. Default is true | +| `force` | boolean | no | When `true` overwrites the target branch with a new commit based on the `start_branch` | | `actions[]` Attribute | Type | Required | Description | | --------------------- | ---- | -------- | ----------- | diff --git a/lib/api/commits.rb b/lib/api/commits.rb index d0a9debda5b..65eb9bfb87e 100644 --- a/lib/api/commits.rb +++ b/lib/api/commits.rb @@ -99,6 +99,7 @@ module API optional :author_email, type: String, desc: 'Author email for commit' optional :author_name, type: String, desc: 'Author name for commit' optional :stats, type: Boolean, default: true, desc: 'Include commit stats' + optional :force, type: Boolean, default: false, desc: 'When `true` overwrites the target branch with a new commit based on the `start_branch`' end post ':id/repository/commits' do authorize_push_to_branch!(params[:branch]) diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 2bfff8397e8..2f8e4e9e93d 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -853,17 +853,20 @@ module Gitlab true end + # rubocop:disable Metrics/ParameterLists def multi_action( user, branch_name:, message:, actions:, author_email: nil, author_name: nil, - start_branch_name: nil, start_repository: self) + start_branch_name: nil, start_repository: self, + force: false) wrapped_gitaly_errors do gitaly_operation_client.user_commit_files(user, branch_name, message, actions, author_email, author_name, - start_branch_name, start_repository) + start_branch_name, start_repository, force) end end + # rubocop:enable Metrics/ParameterLists def write_config(full_path:) return unless full_path.present? diff --git a/lib/gitlab/gitaly_client/operation_service.rb b/lib/gitlab/gitaly_client/operation_service.rb index d172c798da2..bc45ee38fb5 100644 --- a/lib/gitlab/gitaly_client/operation_service.rb +++ b/lib/gitlab/gitaly_client/operation_service.rb @@ -277,14 +277,14 @@ module Gitlab end end + # rubocop:disable Metrics/ParameterLists def user_commit_files( user, branch_name, commit_message, actions, author_email, author_name, - start_branch_name, start_repository) - + start_branch_name, start_repository, force = false) req_enum = Enumerator.new do |y| header = user_commit_files_request_header(user, branch_name, commit_message, actions, author_email, author_name, - start_branch_name, start_repository) + start_branch_name, start_repository, force) y.yield Gitaly::UserCommitFilesRequest.new(header: header) @@ -319,6 +319,7 @@ module Gitlab Gitlab::Git::OperationService::BranchUpdate.from_gitaly(response.branch_update) end + # rubocop:enable Metrics/ParameterLists def user_commit_patches(user, branch_name, patches) header = Gitaly::UserApplyPatchRequest::Header.new( @@ -382,9 +383,10 @@ module Gitlab Gitlab::Git::OperationService::BranchUpdate.from_gitaly(response.branch_update) end + # rubocop:disable Metrics/ParameterLists def user_commit_files_request_header( user, branch_name, commit_message, actions, author_email, author_name, - start_branch_name, start_repository) + start_branch_name, start_repository, force) Gitaly::UserCommitFilesRequestHeader.new( repository: @gitaly_repo, @@ -394,9 +396,11 @@ module Gitlab commit_author_name: encode_binary(author_name), commit_author_email: encode_binary(author_email), start_branch_name: encode_binary(start_branch_name), - start_repository: start_repository.gitaly_repository + start_repository: start_repository.gitaly_repository, + force: force ) end + # rubocop:enable Metrics/ParameterLists def user_commit_files_action_header(action) Gitaly::UserCommitFilesActionHeader.new( diff --git a/spec/services/files/multi_service_spec.rb b/spec/services/files/multi_service_spec.rb index 84c48d63c64..6842fa9f435 100644 --- a/spec/services/files/multi_service_spec.rb +++ b/spec/services/files/multi_service_spec.rb @@ -235,6 +235,22 @@ describe Files::MultiService do expect(blob).to be_present end end + + context 'when force is set to true and branch already exists' do + let(:commit_params) do + { + commit_message: commit_message, + branch_name: 'feature', + start_branch: 'master', + actions: actions, + force: true + } + end + + it 'is still a success' do + expect(subject.execute[:status]).to eq(:success) + end + end end def update_file(path)