68 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Ruby
		
	
	
	
			
		
		
	
	
			68 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Ruby
		
	
	
	
# frozen_string_literal: true
 | 
						|
 | 
						|
module Gitlab
 | 
						|
  module Import
 | 
						|
    module MergeRequestHelpers
 | 
						|
      include DatabaseHelpers
 | 
						|
 | 
						|
      # rubocop: disable CodeReuse/ActiveRecord
 | 
						|
      def create_merge_request_without_hooks(project, attributes, iid)
 | 
						|
        # This work must be wrapped in a transaction as otherwise we can leave
 | 
						|
        # behind incomplete data in the event of an error. This can then lead
 | 
						|
        # to duplicate key errors when jobs are retried.
 | 
						|
        MergeRequest.transaction do
 | 
						|
          # When creating merge requests there are a lot of hooks that may
 | 
						|
          # run, for many different reasons. Many of these hooks (e.g. the
 | 
						|
          # ones used for rendering Markdown) are completely unnecessary and
 | 
						|
          # may even lead to transaction timeouts.
 | 
						|
          #
 | 
						|
          # To ensure importing pull requests has a minimal impact and can
 | 
						|
          # complete in a reasonable time we bypass all the hooks by inserting
 | 
						|
          # the row and then retrieving it. We then only perform the
 | 
						|
          # additional work that is strictly necessary.
 | 
						|
          merge_request_id = insert_and_return_id(attributes, project.merge_requests)
 | 
						|
 | 
						|
          merge_request = project.merge_requests.reset.find(merge_request_id)
 | 
						|
 | 
						|
          [merge_request, false]
 | 
						|
        end
 | 
						|
      rescue ActiveRecord::InvalidForeignKey
 | 
						|
        # It's possible the project has been deleted since scheduling this
 | 
						|
        # job. In this case we'll just skip creating the merge request.
 | 
						|
        []
 | 
						|
      rescue ActiveRecord::RecordNotUnique
 | 
						|
        # It's possible we previously created the MR, but failed when updating
 | 
						|
        # the Git data. In this case we'll just continue working on the
 | 
						|
        # existing row.
 | 
						|
        [project.merge_requests.find_by(iid: iid), true]
 | 
						|
      end
 | 
						|
      # rubocop: enable CodeReuse/ActiveRecord
 | 
						|
 | 
						|
      def insert_or_replace_git_data(merge_request, source_branch_sha, target_branch_sha, already_exists = false)
 | 
						|
        # These fields are set so we can create the correct merge request
 | 
						|
        # diffs.
 | 
						|
        merge_request.source_branch_sha = source_branch_sha
 | 
						|
        merge_request.target_branch_sha = target_branch_sha
 | 
						|
 | 
						|
        merge_request.keep_around_commit
 | 
						|
 | 
						|
        # We force to recreate all diffs to replace all existing data
 | 
						|
        # We use `.all` as otherwise `dependent: :nullify` (the default)
 | 
						|
        # takes an effect
 | 
						|
        merge_request.merge_request_diffs.all.delete_all if already_exists
 | 
						|
 | 
						|
        # MR diffs normally use an "after_save" hook to pull data from Git.
 | 
						|
        # All of this happens in the transaction started by calling
 | 
						|
        # create/save/etc. This in turn can lead to these transactions being
 | 
						|
        # held open for much longer than necessary. To work around this we
 | 
						|
        # first save the diff, then populate it.
 | 
						|
        diff = merge_request.merge_request_diffs.build
 | 
						|
        diff.importing = true
 | 
						|
        diff.save
 | 
						|
        diff.save_git_content
 | 
						|
        diff.set_as_latest_diff
 | 
						|
      end
 | 
						|
    end
 | 
						|
  end
 | 
						|
end
 |