<commit_before># frozen_string_literal: true

class GitPushService < BaseService
  attr_accessor :push_data, :push_commits
  include Gitlab::Access
  include Gitlab::Utils::StrongMemoize

  # The N most recent commits to process in a single push payload.
  PROCESS_COMMIT_LIMIT = 100

  # This method will be called after each git update
  # and only if the provided user and project are present in GitLab.
  #
  # All callbacks for post receive action should be placed here.
  #
  # Next, this method:
  #  1. Creates the push event
  #  2. Updates merge requests
  #  3. Recognizes cross-references from commit messages
  #  4. Executes the project's webhooks
  #  5. Executes the project's services
  #  6. Checks if the project's main language has changed
  #
  def execute
    project.repository.after_create if project.empty_repo?
    project.repository.after_push_commit(branch_name)

    if push_remove_branch?
      project.repository.after_remove_branch
      @push_commits = []
    elsif push_to_new_branch?
      project.repository.after_create_branch

      # Re-find the pushed commits.
      if default_branch?
        # Initial push to the default branch. Take the full history of that branch as "newly pushed".
        process_default_branch
      else
        # Use the pushed commits that aren't reachable by the default branch
        # as a heuristic. This may include more commits than are actually pushed, but
        # that shouldn't matter because we check for existing cross-references later.
        @push_commits = project.repository.commits_between(project.default_branch, params[:newrev])

        # don't process commits for the initial push to the default branch
        process_commit_messages
      end
    elsif push_to_existing_branch?
      # Collect data for this git push
      @push_commits = project.repository.commits_between(params[:oldrev], params[:newrev])

      process_commit_messages

      # Update the bare repositories info/attributes file using the contents of the default branches
      # .gitattributes file
      update_gitattributes if default_branch?
    end

    execute_related_hooks
    perform_housekeeping

    update_remote_mirrors
    update_caches

    update_signatures
  end

  def update_gitattributes
    project.repository.copy_gitattributes(params[:ref])
  end

  def update_caches
    if default_branch?
      if push_to_new_branch?
        # If this is the initial push into the default branch, the file type caches
        # will already be reset as a result of `Project#change_head`.
        types = []
      else
        paths = Set.new

        last_pushed_commits.each do |commit|
          commit.raw_deltas.each do |diff|
            paths << diff.new_path
          end
        end

        types = Gitlab::FileDetector.types_in_paths(paths.to_a)
      end

      DetectRepositoryLanguagesWorker.perform_async(@project.id, current_user.id)
    else
      types = []
    end

    ProjectCacheWorker.perform_async(project.id, types, [:commit_count, :repository_size])
  end

  # rubocop: disable CodeReuse/ActiveRecord
  def update_signatures
    commit_shas = last_pushed_commits.map(&:sha)

    return if commit_shas.empty?

    shas_with_cached_signatures = GpgSignature.where(commit_sha: commit_shas).pluck(:commit_sha)
    commit_shas -= shas_with_cached_signatures

    return if commit_shas.empty?

    commit_shas = Gitlab::Git::Commit.shas_with_signatures(project.repository, commit_shas)

    CreateGpgSignatureWorker.perform_async(commit_shas, project.id)
  end
  # rubocop: enable CodeReuse/ActiveRecord

  # Schedules processing of commit messages.
  def process_commit_messages
    default = default_branch?

    last_pushed_commits.each do |commit|
      if commit.matches_cross_reference_regex?
        ProcessCommitWorker
          .perform_async(project.id, current_user.id, commit.to_hash, default)
      end
    end
  end

  protected
<commit_msg>Move some logic to a helper method in GitPushService<commit_after># frozen_string_literal: true

class GitPushService < BaseService
  attr_accessor :push_data, :push_commits
  include Gitlab::Access
  include Gitlab::Utils::StrongMemoize

  # The N most recent commits to process in a single push payload.
  PROCESS_COMMIT_LIMIT = 100

  # This method will be called after each git update
  # and only if the provided user and project are present in GitLab.
  #
  # All callbacks for post receive action should be placed here.
  #
  # Next, this method:
  #  1. Creates the push event
  #  2. Updates merge requests
  #  3. Recognizes cross-references from commit messages
  #  4. Executes the project's webhooks
  #  5. Executes the project's services
  #  6. Checks if the project's main language has changed
  #
  def execute
    update_commits
    execute_related_hooks
    perform_housekeeping

    update_remote_mirrors
    update_caches

    update_signatures
  end

  protected

  def update_commits
    project.repository.after_create if project.empty_repo?
    project.repository.after_push_commit(branch_name)

    if push_remove_branch?
      project.repository.after_remove_branch
      @push_commits = []
    elsif push_to_new_branch?
      project.repository.after_create_branch

      # Re-find the pushed commits.
      if default_branch?
        # Initial push to the default branch. Take the full history of that branch as "newly pushed".
        process_default_branch
      else
        # Use the pushed commits that aren't reachable by the default branch
        # as a heuristic. This may include more commits than are actually pushed, but
        # that shouldn't matter because we check for existing cross-references later.
        @push_commits = project.repository.commits_between(project.default_branch, params[:newrev])

        # don't process commits for the initial push to the default branch
        process_commit_messages
      end
    elsif push_to_existing_branch?
      # Collect data for this git push
      @push_commits = project.repository.commits_between(params[:oldrev], params[:newrev])

      process_commit_messages

      # Update the bare repositories info/attributes file using the contents of the default branches
      # .gitattributes file
      update_gitattributes if default_branch?
    end
  end

  def update_gitattributes
    project.repository.copy_gitattributes(params[:ref])
  end

  def update_caches
    if default_branch?
      if push_to_new_branch?
        # If this is the initial push into the default branch, the file type caches
        # will already be reset as a result of `Project#change_head`.
        types = []
      else
        paths = Set.new

        last_pushed_commits.each do |commit|
          commit.raw_deltas.each do |diff|
            paths << diff.new_path
          end
        end

        types = Gitlab::FileDetector.types_in_paths(paths.to_a)
      end

      DetectRepositoryLanguagesWorker.perform_async(@project.id, current_user.id)
    else
      types = []
    end

    ProjectCacheWorker.perform_async(project.id, types, [:commit_count, :repository_size])
  end

  # rubocop: disable CodeReuse/ActiveRecord
  def update_signatures
    commit_shas = last_pushed_commits.map(&:sha)

    return if commit_shas.empty?

    shas_with_cached_signatures = GpgSignature.where(commit_sha: commit_shas).pluck(:commit_sha)
    commit_shas -= shas_with_cached_signatures

    return if commit_shas.empty?

    commit_shas = Gitlab::Git::Commit.shas_with_signatures(project.repository, commit_shas)

    CreateGpgSignatureWorker.perform_async(commit_shas, project.id)
  end
  # rubocop: enable CodeReuse/ActiveRecord

  # Schedules processing of commit messages.
  def process_commit_messages
    default = default_branch?

    last_pushed_commits.each do |commit|
      if commit.matches_cross_reference_regex?
        ProcessCommitWorker
          .perform_async(project.id, current_user.id, commit.to_hash, default)
      end
    end
  end

{'fraction_non_alphanumeric': 0.05840330868529891, 'fraction_numerical': 0.0022559217947111165, 'mean_word_length': 3.2334217506631298, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4129387', 'n_tokens_mistral': 2347, 'n_tokens_neox': 2262, 'n_words': 670}