Backport Gitlab::Git::Checksum to CE
This commit is contained in:
parent
59a158955e
commit
563d38e81e
2 changed files with 120 additions and 0 deletions
82
lib/gitlab/git/checksum.rb
Normal file
82
lib/gitlab/git/checksum.rb
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
module Gitlab
|
||||||
|
module Git
|
||||||
|
class Checksum
|
||||||
|
include Gitlab::Git::Popen
|
||||||
|
|
||||||
|
EMPTY_REPOSITORY_CHECKSUM = '0000000000000000000000000000000000000000'.freeze
|
||||||
|
|
||||||
|
Failure = Class.new(StandardError)
|
||||||
|
|
||||||
|
attr_reader :path, :relative_path, :storage, :storage_path
|
||||||
|
|
||||||
|
def initialize(storage, relative_path)
|
||||||
|
@storage = storage
|
||||||
|
@storage_path = Gitlab.config.repositories.storages[storage].legacy_disk_path
|
||||||
|
@relative_path = "#{relative_path}.git"
|
||||||
|
@path = File.join(storage_path, @relative_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
def calculate
|
||||||
|
unless repository_exists?
|
||||||
|
failure!(Gitlab::Git::Repository::NoRepository, 'No repository for such path')
|
||||||
|
end
|
||||||
|
|
||||||
|
calculate_checksum_by_shelling_out
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def repository_exists?
|
||||||
|
raw_repository.exists?
|
||||||
|
end
|
||||||
|
|
||||||
|
def calculate_checksum_by_shelling_out
|
||||||
|
args = %W(--git-dir=#{path} show-ref --heads --tags)
|
||||||
|
output, status = run_git(args)
|
||||||
|
|
||||||
|
if status&.zero?
|
||||||
|
refs = output.split("\n")
|
||||||
|
|
||||||
|
result = refs.inject(nil) do |checksum, ref|
|
||||||
|
value = Digest::SHA1.hexdigest(ref).hex
|
||||||
|
|
||||||
|
if checksum.nil?
|
||||||
|
value
|
||||||
|
else
|
||||||
|
checksum ^ value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
result.to_s(16)
|
||||||
|
else
|
||||||
|
# Empty repositories return with a non-zero status and an empty output.
|
||||||
|
if output&.empty?
|
||||||
|
EMPTY_REPOSITORY_CHECKSUM
|
||||||
|
else
|
||||||
|
failure!(Gitlab::Git::Checksum::Failure, output)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def failure!(klass, message)
|
||||||
|
Gitlab::GitLogger.error("'git show-ref --heads --tags' in #{path}: #{message}")
|
||||||
|
|
||||||
|
raise klass.new("Could not calculate the checksum for #{path}: #{message}")
|
||||||
|
end
|
||||||
|
|
||||||
|
def circuit_breaker
|
||||||
|
@circuit_breaker ||= Gitlab::Git::Storage::CircuitBreaker.for_storage(storage)
|
||||||
|
end
|
||||||
|
|
||||||
|
def raw_repository
|
||||||
|
Gitlab::Git::Repository.new(storage, relative_path, nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
def run_git(args)
|
||||||
|
circuit_breaker.perform do
|
||||||
|
popen([Gitlab.config.git.bin_path, *args], path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
38
spec/lib/gitlab/git/checksum_spec.rb
Normal file
38
spec/lib/gitlab/git/checksum_spec.rb
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Gitlab::Git::Checksum, seed_helper: true do
|
||||||
|
let(:storage) { 'default' }
|
||||||
|
|
||||||
|
it 'raises Gitlab::Git::Repository::NoRepository when there is no repo' do
|
||||||
|
checksum = described_class.new(storage, 'nonexistent-repo')
|
||||||
|
|
||||||
|
expect { checksum.calculate }.to raise_error Gitlab::Git::Repository::NoRepository
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'pretends that checksum is 000000... when the repo is empty' do
|
||||||
|
FileUtils.rm_rf(File.join(SEED_STORAGE_PATH, 'empty-repo.git'))
|
||||||
|
|
||||||
|
system(git_env, *%W(#{Gitlab.config.git.bin_path} init --bare empty-repo.git),
|
||||||
|
chdir: SEED_STORAGE_PATH,
|
||||||
|
out: '/dev/null',
|
||||||
|
err: '/dev/null')
|
||||||
|
|
||||||
|
checksum = described_class.new(storage, 'empty-repo')
|
||||||
|
|
||||||
|
expect(checksum.calculate).to eq '0000000000000000000000000000000000000000'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'raises Gitlab::Git::Repository::Failure when shelling out to git return non-zero status' do
|
||||||
|
checksum = described_class.new(storage, 'gitlab-git-test')
|
||||||
|
|
||||||
|
allow(checksum).to receive(:popen).and_return(['output', nil])
|
||||||
|
|
||||||
|
expect { checksum.calculate }.to raise_error Gitlab::Git::Checksum::Failure
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'calculates the checksum when there is a repo' do
|
||||||
|
checksum = described_class.new(storage, 'gitlab-git-test')
|
||||||
|
|
||||||
|
expect(checksum.calculate).to eq '54f21be4c32c02f6788d72207fa03ad3bce725e4'
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue