57 lines
		
	
	
		
			1.9 KiB
		
	
	
	
		
			Ruby
		
	
	
	
			
		
		
	
	
			57 lines
		
	
	
		
			1.9 KiB
		
	
	
	
		
			Ruby
		
	
	
	
# frozen_string_literal: true
 | 
						|
 | 
						|
module Gitlab
 | 
						|
  module Git
 | 
						|
    class CrossRepoComparer
 | 
						|
      attr_reader :source_repo, :target_repo
 | 
						|
 | 
						|
      def initialize(source_repo, target_repo)
 | 
						|
        @source_repo = source_repo
 | 
						|
        @target_repo = target_repo
 | 
						|
      end
 | 
						|
 | 
						|
      def compare(source_ref, target_ref, straight:)
 | 
						|
        ensuring_ref_in_source(target_ref) do |target_commit_id|
 | 
						|
          Gitlab::Git::Compare.new(
 | 
						|
            source_repo,
 | 
						|
            target_commit_id,
 | 
						|
            source_ref,
 | 
						|
            straight: straight
 | 
						|
          )
 | 
						|
        end
 | 
						|
      end
 | 
						|
 | 
						|
      private
 | 
						|
 | 
						|
      def ensuring_ref_in_source(ref, &blk)
 | 
						|
        return yield ref if source_repo == target_repo
 | 
						|
 | 
						|
        # If the commit doesn't exist in the target, there's nothing we can do
 | 
						|
        commit_id = target_repo.commit(ref)&.sha
 | 
						|
        return unless commit_id
 | 
						|
 | 
						|
        # The commit pointed to by ref may exist in the source even when they
 | 
						|
        # are different repositories. This is particularly true of close forks,
 | 
						|
        # but may also be the case if a temporary ref for this comparison has
 | 
						|
        # already been created in the past, and the result hasn't been GC'd yet.
 | 
						|
        return yield commit_id if source_repo.commit(commit_id)
 | 
						|
 | 
						|
        # Worst case: the commit is not in the source repo so we need to fetch
 | 
						|
        # it. Use a temporary ref and clean up afterwards
 | 
						|
        with_commit_in_source_tmp(commit_id, &blk)
 | 
						|
      end
 | 
						|
 | 
						|
      # Fetch the ref into source_repo from target_repo, using a temporary ref
 | 
						|
      # name that will be deleted once the method completes. This is a no-op if
 | 
						|
      # fetching the source branch fails
 | 
						|
      def with_commit_in_source_tmp(commit_id, &blk)
 | 
						|
        tmp_ref = "refs/tmp/#{SecureRandom.hex}"
 | 
						|
 | 
						|
        yield commit_id if source_repo.fetch_source_branch!(target_repo, commit_id, tmp_ref)
 | 
						|
      ensure
 | 
						|
        source_repo.delete_refs(tmp_ref) # best-effort
 | 
						|
      end
 | 
						|
    end
 | 
						|
  end
 | 
						|
end
 |