104 lines
3.7 KiB
Ruby
104 lines
3.7 KiB
Ruby
module Gitlab
|
|
module Git
|
|
module RepositoryMirroring
|
|
REFMAPS = {
|
|
# With `:all_refs`, the repository is equivalent to the result of `git clone --mirror`
|
|
all_refs: '+refs/*:refs/*',
|
|
heads: '+refs/heads/*:refs/heads/*',
|
|
tags: '+refs/tags/*:refs/tags/*'
|
|
}.freeze
|
|
|
|
RemoteError = Class.new(StandardError)
|
|
|
|
def set_remote_as_mirror(remote_name, refmap: :all_refs)
|
|
set_remote_refmap(remote_name, refmap)
|
|
|
|
rugged.config["remote.#{remote_name}.mirror"] = true
|
|
rugged.config["remote.#{remote_name}.prune"] = true
|
|
end
|
|
|
|
def set_remote_refmap(remote_name, refmap)
|
|
Array(refmap).each_with_index do |refspec, i|
|
|
refspec = REFMAPS[refspec] || refspec
|
|
|
|
# We need multiple `fetch` entries, but Rugged only allows replacing a config, not adding to it.
|
|
# To make sure we start from scratch, we set the first using rugged, and use `git` for any others
|
|
if i == 0
|
|
rugged.config["remote.#{remote_name}.fetch"] = refspec
|
|
else
|
|
run_git(%W[config --add remote.#{remote_name}.fetch #{refspec}])
|
|
end
|
|
end
|
|
end
|
|
|
|
# Like all_refs public `Gitlab::Git::Repository` methods, this method is part
|
|
# of `Repository`'s interface through `method_missing`.
|
|
# `Repository` has its own `fetch_as_mirror` which uses `gitlab-shell` and
|
|
# takes some extra attributes, so we qualify this method name to prevent confusion.
|
|
def fetch_as_mirror_without_shell(url)
|
|
remote_name = "tmp-#{SecureRandom.hex}"
|
|
add_remote(remote_name, url)
|
|
set_remote_as_mirror(remote_name)
|
|
fetch_remote_without_shell(remote_name)
|
|
ensure
|
|
remove_remote(remote_name) if remote_name
|
|
end
|
|
|
|
def remote_tags(remote)
|
|
# Each line has this format: "dc872e9fa6963f8f03da6c8f6f264d0845d6b092\trefs/tags/v1.10.0\n"
|
|
# We want to convert it to: [{ 'v1.10.0' => 'dc872e9fa6963f8f03da6c8f6f264d0845d6b092' }, ...]
|
|
list_remote_tags(remote).map do |line|
|
|
target, path = line.strip.split("\t")
|
|
|
|
# When the remote repo does not have tags.
|
|
if target.nil? || path.nil?
|
|
Rails.logger.info "Empty or invalid list of tags for remote: #{remote}. Output: #{output}"
|
|
return []
|
|
end
|
|
|
|
name = path.split('/', 3).last
|
|
# We're only interested in tag references
|
|
# See: http://stackoverflow.com/questions/15472107/when-listing-git-ls-remote-why-theres-after-the-tag-name
|
|
next if name =~ /\^\{\}\Z/
|
|
|
|
target_commit = Gitlab::Git::Commit.find(self, target)
|
|
Gitlab::Git::Tag.new(self, name, target, target_commit)
|
|
end.compact
|
|
end
|
|
|
|
def remote_branches(remote_name)
|
|
branches = []
|
|
|
|
rugged.references.each("refs/remotes/#{remote_name}/*").map do |ref|
|
|
name = ref.name.sub(/\Arefs\/remotes\/#{remote_name}\//, '')
|
|
|
|
begin
|
|
target_commit = Gitlab::Git::Commit.find(self, ref.target)
|
|
branches << Gitlab::Git::Branch.new(self, name, ref.target, target_commit)
|
|
rescue Rugged::ReferenceError
|
|
# Omit invalid branch
|
|
end
|
|
end
|
|
|
|
branches
|
|
end
|
|
|
|
private
|
|
|
|
def list_remote_tags(remote)
|
|
tag_list, exit_code, error = nil
|
|
cmd = %W(#{Gitlab.config.git.bin_path} --git-dir=#{path} ls-remote --tags #{remote})
|
|
|
|
Open3.popen3(*cmd) do |stdin, stdout, stderr, wait_thr|
|
|
tag_list = stdout.read
|
|
error = stderr.read
|
|
exit_code = wait_thr.value.exitstatus
|
|
end
|
|
|
|
raise RemoteError, error unless exit_code.zero?
|
|
|
|
tag_list.split("\n")
|
|
end
|
|
end
|
|
end
|
|
end
|