diff options
Diffstat (limited to 'app/services')
-rw-r--r-- | app/services/auth/container_registry_authentication_service.rb | 2 | ||||
-rw-r--r-- | app/services/commits/change_service.rb | 34 | ||||
-rw-r--r-- | app/services/compare_service.rb | 26 | ||||
-rw-r--r-- | app/services/create_branch_service.rb | 30 | ||||
-rw-r--r-- | app/services/create_snippet_service.rb | 9 | ||||
-rw-r--r-- | app/services/delete_tag_service.rb | 2 | ||||
-rw-r--r-- | app/services/files/base_service.rb | 25 | ||||
-rw-r--r-- | app/services/files/create_dir_service.rb | 10 | ||||
-rw-r--r-- | app/services/files/create_service.rb | 14 | ||||
-rw-r--r-- | app/services/files/delete_service.rb | 10 | ||||
-rw-r--r-- | app/services/files/multi_service.rb | 8 | ||||
-rw-r--r-- | app/services/files/update_service.rb | 10 | ||||
-rw-r--r-- | app/services/git_hooks_service.rb | 6 | ||||
-rw-r--r-- | app/services/git_operation_service.rb | 179 | ||||
-rw-r--r-- | app/services/merge_requests/build_service.rb | 5 | ||||
-rw-r--r-- | app/services/merge_requests/merge_service.rb | 14 | ||||
-rw-r--r-- | app/services/validate_new_branch_service.rb | 22 |
17 files changed, 325 insertions, 81 deletions
diff --git a/app/services/auth/container_registry_authentication_service.rb b/app/services/auth/container_registry_authentication_service.rb index c00c5aebf57..5cb7a86a5ee 100644 --- a/app/services/auth/container_registry_authentication_service.rb +++ b/app/services/auth/container_registry_authentication_service.rb @@ -61,7 +61,7 @@ module Auth end def process_repository_access(type, name, actions) - requested_project = Project.find_with_namespace(name) + requested_project = Project.find_by_full_path(name) return unless requested_project actions = actions.select do |action| diff --git a/app/services/commits/change_service.rb b/app/services/commits/change_service.rb index 4d410f66c55..25e22f14e60 100644 --- a/app/services/commits/change_service.rb +++ b/app/services/commits/change_service.rb @@ -4,7 +4,8 @@ module Commits class ChangeError < StandardError; end def execute - @source_project = params[:source_project] || @project + @start_project = params[:start_project] || @project + @start_branch = params[:start_branch] @target_branch = params[:target_branch] @commit = params[:commit] @create_merge_request = params[:create_merge_request].present? @@ -25,13 +26,28 @@ module Commits def commit_change(action) raise NotImplementedError unless repository.respond_to?(action) - into = @create_merge_request ? @commit.public_send("#{action}_branch_name") : @target_branch - tree_id = repository.public_send("check_#{action}_content", @commit, @target_branch) + if @create_merge_request + into = @commit.public_send("#{action}_branch_name") + tree_branch = @start_branch + else + into = tree_branch = @target_branch + end + + tree_id = repository.public_send( + "check_#{action}_content", @commit, tree_branch) if tree_id - create_target_branch(into) if @create_merge_request + validate_target_branch(into) if @create_merge_request + + repository.public_send( + action, + current_user, + @commit, + into, + tree_id, + start_project: @start_project, + start_branch_name: @start_branch) - repository.public_send(action, current_user, @commit, into, tree_id) success else error_msg = "Sorry, we cannot #{action.to_s.dasherize} this #{@commit.change_type_title(current_user)} automatically. @@ -50,12 +66,12 @@ module Commits true end - def create_target_branch(new_branch) + def validate_target_branch(new_branch) # Temporary branch exists and contains the change commit - return success if repository.find_branch(new_branch) + return if repository.find_branch(new_branch) - result = CreateBranchService.new(@project, current_user) - .execute(new_branch, @target_branch, source_project: @source_project) + result = ValidateNewBranchService.new(@project, current_user) + .execute(new_branch) if result[:status] == :error raise ChangeError, "There was an error creating the source branch: #{result[:message]}" diff --git a/app/services/compare_service.rb b/app/services/compare_service.rb index 5e8fafca98c..ab4c02a97a0 100644 --- a/app/services/compare_service.rb +++ b/app/services/compare_service.rb @@ -3,23 +3,27 @@ require 'securerandom' # Compare 2 branches for one repo or between repositories # and return Gitlab::Git::Compare object that responds to commits and diffs class CompareService - def execute(source_project, source_branch, target_project, target_branch, straight: false) - source_commit = source_project.commit(source_branch) - return unless source_commit + attr_reader :start_project, :start_branch_name - source_sha = source_commit.sha + def initialize(new_start_project, new_start_branch_name) + @start_project = new_start_project + @start_branch_name = new_start_branch_name + end + def execute(target_project, target_branch, straight: false) # If compare with other project we need to fetch ref first - unless target_project == source_project - random_string = SecureRandom.hex + target_project.repository.with_repo_branch_commit( + start_project.repository, + start_branch_name) do |commit| + break unless commit - target_project.repository.fetch_ref( - source_project.repository.path_to_repo, - "refs/heads/#{source_branch}", - "refs/tmp/#{random_string}/head" - ) + compare(commit.sha, target_project, target_branch, straight) end + end + + private + def compare(source_sha, target_project, target_branch, straight) raw_compare = Gitlab::Git::Compare.new( target_project.repository.raw_repository, target_branch, diff --git a/app/services/create_branch_service.rb b/app/services/create_branch_service.rb index e004a303496..77459d8779d 100644 --- a/app/services/create_branch_service.rb +++ b/app/services/create_branch_service.rb @@ -1,31 +1,11 @@ class CreateBranchService < BaseService - def execute(branch_name, ref, source_project: @project) - valid_branch = Gitlab::GitRefValidator.validate(branch_name) + def execute(branch_name, ref) + result = ValidateNewBranchService.new(project, current_user) + .execute(branch_name) - unless valid_branch - return error('Branch name is invalid') - end - - repository = project.repository - existing_branch = repository.find_branch(branch_name) - - if existing_branch - return error('Branch already exists') - end - - new_branch = if source_project != @project - repository.fetch_ref( - source_project.repository.path_to_repo, - "refs/heads/#{ref}", - "refs/heads/#{branch_name}" - ) - - repository.after_create_branch + return result if result[:status] == :error - repository.find_branch(branch_name) - else - repository.add_branch(current_user, branch_name, ref) - end + new_branch = repository.add_branch(current_user, branch_name, ref) if new_branch success(new_branch) diff --git a/app/services/create_snippet_service.rb b/app/services/create_snippet_service.rb index 95cc9baf406..14f5ba064ff 100644 --- a/app/services/create_snippet_service.rb +++ b/app/services/create_snippet_service.rb @@ -1,5 +1,8 @@ class CreateSnippetService < BaseService def execute + request = params.delete(:request) + api = params.delete(:api) + snippet = if project project.snippets.build(params) else @@ -12,8 +15,12 @@ class CreateSnippetService < BaseService end snippet.author = current_user + snippet.spam = SpamService.new(snippet, request).check(api) + + if snippet.save + UserAgentDetailService.new(snippet, request).create + end - snippet.save snippet end end diff --git a/app/services/delete_tag_service.rb b/app/services/delete_tag_service.rb index a44dee14a0f..9d4bffb93e9 100644 --- a/app/services/delete_tag_service.rb +++ b/app/services/delete_tag_service.rb @@ -7,7 +7,7 @@ class DeleteTagService < BaseService return error('No such tag', 404) end - if repository.rm_tag(tag_name) + if repository.rm_tag(current_user, tag_name) release = project.releases.find_by(tag: tag_name) release.destroy if release diff --git a/app/services/files/base_service.rb b/app/services/files/base_service.rb index 9bd4bd464f7..0a25f56d24c 100644 --- a/app/services/files/base_service.rb +++ b/app/services/files/base_service.rb @@ -3,9 +3,9 @@ module Files class ValidationError < StandardError; end def execute - @source_project = params[:source_project] || @project - @source_branch = params[:source_branch] - @target_branch = params[:target_branch] + @start_project = params[:start_project] || @project + @start_branch = params[:start_branch] + @target_branch = params[:target_branch] @commit_message = params[:commit_message] @file_path = params[:file_path] @@ -22,10 +22,8 @@ module Files # Validate parameters validate - # Create new branch if it different from source_branch - if different_branch? - create_target_branch - end + # Create new branch if it different from start_branch + validate_target_branch if different_branch? result = commit if result @@ -40,7 +38,7 @@ module Files private def different_branch? - @source_branch != @target_branch || @source_project != @project + @start_branch != @target_branch || @start_project != @project end def file_has_changed? @@ -61,22 +59,23 @@ module Files end unless project.empty_repo? - unless @source_project.repository.branch_names.include?(@source_branch) + unless @start_project.repository.branch_exists?(@start_branch) raise_error('You can only create or edit files when you are on a branch') end if different_branch? - if repository.branch_names.include?(@target_branch) + if repository.branch_exists?(@target_branch) raise_error('Branch with such name already exists. You need to switch to this branch in order to make changes') end end end end - def create_target_branch - result = CreateBranchService.new(project, current_user).execute(@target_branch, @source_branch, source_project: @source_project) + def validate_target_branch + result = ValidateNewBranchService.new(project, current_user). + execute(@target_branch) - unless result[:status] == :success + if result[:status] == :error raise_error("Something went wrong when we tried to create #{@target_branch} for you: #{result[:message]}") end end diff --git a/app/services/files/create_dir_service.rb b/app/services/files/create_dir_service.rb index e5b4d60e467..858de5f0538 100644 --- a/app/services/files/create_dir_service.rb +++ b/app/services/files/create_dir_service.rb @@ -1,7 +1,15 @@ module Files class CreateDirService < Files::BaseService def commit - repository.commit_dir(current_user, @file_path, @commit_message, @target_branch, author_email: @author_email, author_name: @author_name) + repository.commit_dir( + current_user, + @file_path, + message: @commit_message, + branch_name: @target_branch, + author_email: @author_email, + author_name: @author_name, + start_project: @start_project, + start_branch_name: @start_branch) end def validate diff --git a/app/services/files/create_service.rb b/app/services/files/create_service.rb index b23576b9a28..88dd7bbaedb 100644 --- a/app/services/files/create_service.rb +++ b/app/services/files/create_service.rb @@ -1,7 +1,17 @@ module Files class CreateService < Files::BaseService def commit - repository.commit_file(current_user, @file_path, @file_content, @commit_message, @target_branch, false, author_email: @author_email, author_name: @author_name) + repository.commit_file( + current_user, + @file_path, + @file_content, + message: @commit_message, + branch_name: @target_branch, + update: false, + author_email: @author_email, + author_name: @author_name, + start_project: @start_project, + start_branch_name: @start_branch) end def validate @@ -24,7 +34,7 @@ module Files unless project.empty_repo? @file_path.slice!(0) if @file_path.start_with?('/') - blob = repository.blob_at_branch(@source_branch, @file_path) + 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') diff --git a/app/services/files/delete_service.rb b/app/services/files/delete_service.rb index 4f7e7a5baaa..50f0ffcac9f 100644 --- a/app/services/files/delete_service.rb +++ b/app/services/files/delete_service.rb @@ -1,7 +1,15 @@ module Files class DeleteService < Files::BaseService def commit - repository.remove_file(current_user, @file_path, @commit_message, @target_branch, author_email: @author_email, author_name: @author_name) + repository.remove_file( + current_user, + @file_path, + message: @commit_message, + branch_name: @target_branch, + author_email: @author_email, + author_name: @author_name, + start_project: @start_project, + start_branch_name: @start_branch) end end end diff --git a/app/services/files/multi_service.rb b/app/services/files/multi_service.rb index 54446e90007..6ba868df04d 100644 --- a/app/services/files/multi_service.rb +++ b/app/services/files/multi_service.rb @@ -5,11 +5,13 @@ module Files def commit repository.multi_action( user: current_user, - branch: @target_branch, message: @commit_message, + branch_name: @target_branch, actions: params[:actions], author_email: @author_email, - author_name: @author_name + author_name: @author_name, + start_project: @start_project, + start_branch_name: @start_branch ) end @@ -61,7 +63,7 @@ module Files end def last_commit - Gitlab::Git::Commit.last_for_path(repository, @source_branch, @file_path) + Gitlab::Git::Commit.last_for_path(repository, @start_branch, @file_path) end def regex_check(file) diff --git a/app/services/files/update_service.rb b/app/services/files/update_service.rb index 47a18e3e132..a71fe61a4b6 100644 --- a/app/services/files/update_service.rb +++ b/app/services/files/update_service.rb @@ -4,11 +4,13 @@ module Files def commit repository.update_file(current_user, @file_path, @file_content, - branch: @target_branch, - previous_path: @previous_path, message: @commit_message, + branch_name: @target_branch, + previous_path: @previous_path, author_email: @author_email, - author_name: @author_name) + author_name: @author_name, + start_project: @start_project, + start_branch_name: @start_branch) end private @@ -23,7 +25,7 @@ module Files def last_commit @last_commit ||= Gitlab::Git::Commit. - last_for_path(@source_project.repository, @source_branch, @file_path) + last_for_path(@start_project.repository, @start_branch, @file_path) end end end diff --git a/app/services/git_hooks_service.rb b/app/services/git_hooks_service.rb index 6cd3908d43a..d222d1e63aa 100644 --- a/app/services/git_hooks_service.rb +++ b/app/services/git_hooks_service.rb @@ -18,9 +18,9 @@ class GitHooksService end end - yield self - - run_hook('post-receive') + yield(self).tap do + run_hook('post-receive') + end end private diff --git a/app/services/git_operation_service.rb b/app/services/git_operation_service.rb new file mode 100644 index 00000000000..27bcc047601 --- /dev/null +++ b/app/services/git_operation_service.rb @@ -0,0 +1,179 @@ +class GitOperationService + attr_reader :user, :repository + + def initialize(new_user, new_repository) + @user = new_user + @repository = new_repository + end + + def add_branch(branch_name, newrev) + ref = Gitlab::Git::BRANCH_REF_PREFIX + branch_name + oldrev = Gitlab::Git::BLANK_SHA + + update_ref_in_hooks(ref, newrev, oldrev) + end + + def rm_branch(branch) + ref = Gitlab::Git::BRANCH_REF_PREFIX + branch.name + oldrev = branch.target + newrev = Gitlab::Git::BLANK_SHA + + update_ref_in_hooks(ref, newrev, oldrev) + end + + def add_tag(tag_name, newrev, options = {}) + ref = Gitlab::Git::TAG_REF_PREFIX + tag_name + oldrev = Gitlab::Git::BLANK_SHA + + with_hooks(ref, newrev, oldrev) do |service| + # We want to pass the OID of the tag object to the hooks. For an + # annotated tag we don't know that OID until after the tag object + # (raw_tag) is created in the repository. That is why we have to + # update the value after creating the tag object. Only the + # "post-receive" hook will receive the correct value in this case. + raw_tag = repository.rugged.tags.create(tag_name, newrev, options) + service.newrev = raw_tag.target_id + end + end + + def rm_tag(tag) + ref = Gitlab::Git::TAG_REF_PREFIX + tag.name + oldrev = tag.target + newrev = Gitlab::Git::BLANK_SHA + + update_ref_in_hooks(ref, newrev, oldrev) do + repository.rugged.tags.delete(tag_name) + end + end + + # Whenever `start_branch_name` is passed, if `branch_name` doesn't exist, + # it would be created from `start_branch_name`. + # If `start_project` is passed, and the branch doesn't exist, + # it would try to find the commits from it instead of current repository. + def with_branch( + branch_name, + start_branch_name: nil, + start_project: repository.project, + &block) + + check_with_branch_arguments!( + branch_name, start_branch_name, start_project) + + update_branch_with_hooks(branch_name) do + repository.with_repo_branch_commit( + start_project.repository, + start_branch_name || branch_name, + &block) + end + end + + private + + def update_branch_with_hooks(branch_name) + update_autocrlf_option + + was_empty = repository.empty? + + # Make commit + newrev = yield + + unless newrev + raise Repository::CommitError.new('Failed to create commit') + end + + branch = repository.find_branch(branch_name) + oldrev = find_oldrev_from_branch(newrev, branch) + + ref = Gitlab::Git::BRANCH_REF_PREFIX + branch_name + update_ref_in_hooks(ref, newrev, oldrev) + + # If repo was empty expire cache + repository.after_create if was_empty + repository.after_create_branch if + was_empty || Gitlab::Git.blank_ref?(oldrev) + + newrev + end + + def find_oldrev_from_branch(newrev, branch) + return Gitlab::Git::BLANK_SHA unless branch + + oldrev = branch.target + + if oldrev == repository.rugged.merge_base(newrev, branch.target) + oldrev + else + raise Repository::CommitError.new('Branch diverged') + end + end + + def update_ref_in_hooks(ref, newrev, oldrev) + with_hooks(ref, newrev, oldrev) do + update_ref(ref, newrev, oldrev) + end + end + + def with_hooks(ref, newrev, oldrev) + GitHooksService.new.execute( + user, + repository.path_to_repo, + oldrev, + newrev, + ref) do |service| + + yield(service) + end + end + + def update_ref(ref, newrev, oldrev) + # We use 'git update-ref' because libgit2/rugged currently does not + # offer 'compare and swap' ref updates. Without compare-and-swap we can + # (and have!) accidentally reset the ref to an earlier state, clobbering + # commits. See also https://github.com/libgit2/libgit2/issues/1534. + command = %W[#{Gitlab.config.git.bin_path} update-ref --stdin -z] + _, status = Gitlab::Popen.popen( + command, + repository.path_to_repo) do |stdin| + stdin.write("update #{ref}\x00#{newrev}\x00#{oldrev}\x00") + end + + unless status.zero? + raise Repository::CommitError.new( + "Could not update branch #{Gitlab::Git.branch_name(ref)}." \ + " Please refresh and try again.") + end + end + + def update_autocrlf_option + if repository.raw_repository.autocrlf != :input + repository.raw_repository.autocrlf = :input + end + end + + def check_with_branch_arguments!( + branch_name, start_branch_name, start_project) + return if repository.branch_exists?(branch_name) + + if repository.project != start_project + unless start_branch_name + raise ArgumentError, + 'Should also pass :start_branch_name if' + + ' :start_project is different from current project' + end + + unless start_project.repository.branch_exists?(start_branch_name) + raise ArgumentError, + "Cannot find branch #{branch_name} nor" \ + " #{start_branch_name} from" \ + " #{start_project.path_with_namespace}" + end + elsif start_branch_name + unless repository.branch_exists?(start_branch_name) + raise ArgumentError, + "Cannot find branch #{branch_name} nor" \ + " #{start_branch_name} from" \ + " #{repository.project.path_with_namespace}" + end + end + end +end diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb index 1d6d2754559..f4d52e3ebbd 100644 --- a/app/services/merge_requests/build_service.rb +++ b/app/services/merge_requests/build_service.rb @@ -47,9 +47,10 @@ module MergeRequests end def compare_branches - compare = CompareService.new.execute( + compare = CompareService.new( source_project, - source_branch, + source_branch + ).execute( target_project, target_branch ) diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb index ab9056a3250..5ca6fec962d 100644 --- a/app/services/merge_requests/merge_service.rb +++ b/app/services/merge_requests/merge_service.rb @@ -6,13 +6,17 @@ module MergeRequests # Executed when you do merge via GitLab UI # class MergeService < MergeRequests::BaseService - attr_reader :merge_request + attr_reader :merge_request, :source def execute(merge_request) @merge_request = merge_request return log_merge_error('Merge request is not mergeable', true) unless @merge_request.mergeable? + @source = find_merge_source + + return log_merge_error('No source for merge', true) unless @source + merge_request.in_locked_state do if commit after_merge @@ -34,7 +38,7 @@ module MergeRequests committer: committer } - commit_id = repository.merge(current_user, merge_request, options) + commit_id = repository.merge(current_user, source, merge_request, options) if commit_id merge_request.update(merge_commit_sha: commit_id) @@ -73,9 +77,11 @@ module MergeRequests end def merge_request_info - project = merge_request.project + merge_request.to_reference(full: true) + end - "#{project.to_reference}#{merge_request.to_reference}" + def find_merge_source + merge_request.diff_head_sha end end end diff --git a/app/services/validate_new_branch_service.rb b/app/services/validate_new_branch_service.rb new file mode 100644 index 00000000000..2f61be184ce --- /dev/null +++ b/app/services/validate_new_branch_service.rb @@ -0,0 +1,22 @@ +require_relative 'base_service' + +class ValidateNewBranchService < BaseService + def execute(branch_name) + valid_branch = Gitlab::GitRefValidator.validate(branch_name) + + unless valid_branch + return error('Branch name is invalid') + end + + repository = project.repository + existing_branch = repository.find_branch(branch_name) + + if existing_branch + return error('Branch already exists') + end + + success + rescue GitHooksService::PreReceiveError => ex + error(ex.message) + end +end |