gitlab-org--gitlab-foss/lib/gitlab/git/cross_repo_comparer.rb

56 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