Commit a898714c authored by Sean McGivern's avatar Sean McGivern

Merge branch 'zj-empty-repo' into 'master'

Remove Rugged::Repository#empty?

Closes gitaly#699

See merge request gitlab-org/gitlab-ce!15622
parents b756b0af 03ac8d5d
...@@ -227,7 +227,6 @@ class Project < ActiveRecord::Base ...@@ -227,7 +227,6 @@ class Project < ActiveRecord::Base
delegate :members, to: :team, prefix: true delegate :members, to: :team, prefix: true
delegate :add_user, :add_users, to: :team delegate :add_user, :add_users, to: :team
delegate :add_guest, :add_reporter, :add_developer, :add_master, to: :team delegate :add_guest, :add_reporter, :add_developer, :add_master, to: :team
delegate :empty_repo?, to: :repository
# Validations # Validations
validates :creator, presence: true, on: :create validates :creator, presence: true, on: :create
...@@ -499,6 +498,10 @@ class Project < ActiveRecord::Base ...@@ -499,6 +498,10 @@ class Project < ActiveRecord::Base
auto_devops&.enabled.nil? && !current_application_settings.auto_devops_enabled? auto_devops&.enabled.nil? && !current_application_settings.auto_devops_enabled?
end end
def empty_repo?
repository.empty?
end
def repository_storage_path def repository_storage_path
Gitlab.config.repositories.storages[repository_storage].try(:[], 'path') Gitlab.config.repositories.storages[repository_storage].try(:[], 'path')
end end
......
...@@ -37,7 +37,7 @@ class Repository ...@@ -37,7 +37,7 @@ class Repository
issue_template_names merge_request_template_names).freeze issue_template_names merge_request_template_names).freeze
# Methods that use cache_method but only memoize the value # Methods that use cache_method but only memoize the value
MEMOIZED_CACHED_METHODS = %i(license empty_repo?).freeze MEMOIZED_CACHED_METHODS = %i(license).freeze
# Certain method caches should be refreshed when certain types of files are # Certain method caches should be refreshed when certain types of files are
# changed. This Hash maps file types (as returned by Gitlab::FileDetector) to # changed. This Hash maps file types (as returned by Gitlab::FileDetector) to
...@@ -497,7 +497,11 @@ class Repository ...@@ -497,7 +497,11 @@ class Repository
end end
cache_method :exists? cache_method :exists?
delegate :empty?, to: :raw_repository def empty?
return true unless exists?
!has_visible_content?
end
cache_method :empty? cache_method :empty?
# The size of this repository in megabytes. # The size of this repository in megabytes.
...@@ -944,13 +948,8 @@ class Repository ...@@ -944,13 +948,8 @@ class Repository
end end
end end
def empty_repo?
!exists? || !has_visible_content?
end
cache_method :empty_repo?, memoize_only: true
def search_files_by_content(query, ref) def search_files_by_content(query, ref)
return [] if empty_repo? || query.blank? return [] if empty? || query.blank?
offset = 2 offset = 2
args = %W(grep -i -I -n --before-context #{offset} --after-context #{offset} -E -e #{Regexp.escape(query)} #{ref || root_ref}) args = %W(grep -i -I -n --before-context #{offset} --after-context #{offset} -E -e #{Regexp.escape(query)} #{ref || root_ref})
...@@ -959,7 +958,7 @@ class Repository ...@@ -959,7 +958,7 @@ class Repository
end end
def search_files_by_name(query, ref) def search_files_by_name(query, ref)
return [] if empty_repo? || query.blank? return [] if empty? || query.blank?
args = %W(ls-tree --full-tree -r #{ref || root_ref} --name-status | #{Regexp.escape(query)}) args = %W(ls-tree --full-tree -r #{ref || root_ref} --name-status | #{Regexp.escape(query)})
......
...@@ -193,12 +193,9 @@ module Backup ...@@ -193,12 +193,9 @@ module Backup
end end
def empty_repo?(project_or_wiki) def empty_repo?(project_or_wiki)
project_or_wiki.repository.expire_exists_cache # protect backups from stale cache # Protect against stale caches
project_or_wiki.repository.empty_repo? project_or_wiki.repository.expire_emptiness_caches
rescue => e project_or_wiki.repository.empty?
progress.puts "Ignoring repository error and continuing backing up project: #{display_repo_path(project_or_wiki)} - #{e.message}".color(:orange)
false
end end
def repository_storage_paths_args def repository_storage_paths_args
......
...@@ -83,7 +83,7 @@ module Gitlab ...@@ -83,7 +83,7 @@ module Gitlab
Gitlab::Git.check_namespace!(start_repository) Gitlab::Git.check_namespace!(start_repository)
start_repository = RemoteRepository.new(start_repository) unless start_repository.is_a?(RemoteRepository) start_repository = RemoteRepository.new(start_repository) unless start_repository.is_a?(RemoteRepository)
start_branch_name = nil if start_repository.empty_repo? start_branch_name = nil if start_repository.empty?
if start_branch_name && !start_repository.branch_exists?(start_branch_name) if start_branch_name && !start_repository.branch_exists?(start_branch_name)
raise ArgumentError, "Cannot find branch #{start_branch_name} in #{start_repository.relative_path}" raise ArgumentError, "Cannot find branch #{start_branch_name} in #{start_repository.relative_path}"
......
...@@ -24,10 +24,12 @@ module Gitlab ...@@ -24,10 +24,12 @@ module Gitlab
@path = repository.path @path = repository.path
end end
def empty_repo? def empty?
# We will override this implementation in gitaly-ruby because we cannot # We will override this implementation in gitaly-ruby because we cannot
# use '@repository' there. # use '@repository' there.
@repository.empty_repo? #
# Caches and memoization used on the Rails side
!@repository.exists? || @repository.empty?
end end
def commit_id(revision) def commit_id(revision)
......
...@@ -75,9 +75,6 @@ module Gitlab ...@@ -75,9 +75,6 @@ module Gitlab
@attributes = Gitlab::Git::Attributes.new(path) @attributes = Gitlab::Git::Attributes.new(path)
end end
delegate :empty?,
to: :rugged
def ==(other) def ==(other)
path == other.path path == other.path
end end
...@@ -206,6 +203,13 @@ module Gitlab ...@@ -206,6 +203,13 @@ module Gitlab
end end
end end
# Git repository can contains some hidden refs like:
# /refs/notes/*
# /refs/git-as-svn/*
# /refs/pulls/*
# This refs by default not visible in project page and not cloned to client side.
alias_method :has_visible_content?, :has_local_branches?
def has_local_branches_rugged? def has_local_branches_rugged?
rugged.branches.each(:local).any? do |ref| rugged.branches.each(:local).any? do |ref|
begin begin
...@@ -1004,7 +1008,7 @@ module Gitlab ...@@ -1004,7 +1008,7 @@ module Gitlab
Gitlab::Git.check_namespace!(start_repository) Gitlab::Git.check_namespace!(start_repository)
start_repository = RemoteRepository.new(start_repository) unless start_repository.is_a?(RemoteRepository) start_repository = RemoteRepository.new(start_repository) unless start_repository.is_a?(RemoteRepository)
return yield nil if start_repository.empty_repo? return yield nil if start_repository.empty?
if start_repository.same_repository?(self) if start_repository.same_repository?(self)
yield commit(start_branch_name) yield commit(start_branch_name)
...@@ -1120,24 +1124,8 @@ module Gitlab ...@@ -1120,24 +1124,8 @@ module Gitlab
Gitlab::Git::Commit.find(self, ref) Gitlab::Git::Commit.find(self, ref)
end end
# Refactoring aid; allows us to copy code from app/models/repository.rb def empty?
def empty_repo? !has_visible_content?
!exists? || !has_visible_content?
end
#
# Git repository can contains some hidden refs like:
# /refs/notes/*
# /refs/git-as-svn/*
# /refs/pulls/*
# This refs by default not visible in project page and not cloned to client side.
#
# This method return true if repository contains some content visible in project page.
#
def has_visible_content?
return @has_visible_content if defined?(@has_visible_content)
@has_visible_content = has_local_branches?
end end
# Like all public `Gitlab::Git::Repository` methods, this method is part # Like all public `Gitlab::Git::Repository` methods, this method is part
......
...@@ -27,7 +27,7 @@ module Gitlab ...@@ -27,7 +27,7 @@ module Gitlab
end end
SERVER_VERSION_FILE = 'GITALY_SERVER_VERSION'.freeze SERVER_VERSION_FILE = 'GITALY_SERVER_VERSION'.freeze
MAXIMUM_GITALY_CALLS = 30 MAXIMUM_GITALY_CALLS = 35
CLIENT_NAME = (Sidekiq.server? ? 'gitlab-sidekiq' : 'gitlab-web').freeze CLIENT_NAME = (Sidekiq.server? ? 'gitlab-sidekiq' : 'gitlab-web').freeze
MUTEX = Mutex.new MUTEX = Mutex.new
......
...@@ -18,32 +18,12 @@ describe Backup::Repository do ...@@ -18,32 +18,12 @@ describe Backup::Repository do
describe '#dump' do describe '#dump' do
describe 'repo failure' do describe 'repo failure' do
before do before do
allow_any_instance_of(Repository).to receive(:empty_repo?).and_raise(Rugged::OdbError)
allow(Gitlab::Popen).to receive(:popen).and_return(['normal output', 0]) allow(Gitlab::Popen).to receive(:popen).and_return(['normal output', 0])
end end
it 'does not raise error' do it 'does not raise error' do
expect { described_class.new.dump }.not_to raise_error expect { described_class.new.dump }.not_to raise_error
end end
it 'shows the appropriate error' do
described_class.new.dump
expect(progress).to have_received(:puts).with("Ignoring repository error and continuing backing up project: #{project.full_path} - Rugged::OdbError")
end
end
describe 'command failure' do
before do
allow_any_instance_of(Repository).to receive(:empty_repo?).and_return(false)
allow(Gitlab::Popen).to receive(:popen).and_return(['error', 1])
end
it 'shows the appropriate error' do
described_class.new.dump
expect(progress).to have_received(:puts).with("Ignoring error on #{project.full_path} - error")
end
end end
end end
...@@ -65,51 +45,23 @@ describe Backup::Repository do ...@@ -65,51 +45,23 @@ describe Backup::Repository do
context 'for a wiki' do context 'for a wiki' do
let(:wiki) { create(:project_wiki) } let(:wiki) { create(:project_wiki) }
context 'wiki repo has content' do it 'invalidates the emptiness cache' do
let!(:wiki_page) { create(:wiki_page, wiki: wiki) } expect(wiki.repository).to receive(:expire_emptiness_caches).once
before do wiki.empty?
wiki.repository.exists? # initial cache end
end
context '`repository.exists?` is incorrectly cached as false' do
before do
repo = wiki.repository
repo.send(:cache).expire(:exists?)
repo.send(:cache).fetch(:exists?) { false }
repo.send(:instance_variable_set, :@exists, false)
end
it 'returns false, regardless of bad cache value' do context 'wiki repo has content' do
expect(described_class.new.send(:empty_repo?, wiki)).to be_falsey let!(:wiki_page) { create(:wiki_page, wiki: wiki) }
end
end
context '`repository.exists?` is correctly cached as true' do it 'returns true, regardless of bad cache value' do
it 'returns false' do expect(described_class.new.send(:empty_repo?, wiki)).to be(false)
expect(described_class.new.send(:empty_repo?, wiki)).to be_falsey
end
end end
end end
context 'wiki repo does not have content' do context 'wiki repo does not have content' do
context '`repository.exists?` is incorrectly cached as true' do it 'returns true, regardless of bad cache value' do
before do expect(described_class.new.send(:empty_repo?, wiki)).to be_truthy
repo = wiki.repository
repo.send(:cache).expire(:exists?)
repo.send(:cache).fetch(:exists?) { true }
repo.send(:instance_variable_set, :@exists, true)
end
it 'returns true, regardless of bad cache value' do
expect(described_class.new.send(:empty_repo?, wiki)).to be_truthy
end
end
context '`repository.exists?` is correctly cached as false' do
it 'returns true' do
expect(described_class.new.send(:empty_repo?, wiki)).to be_truthy
end
end end
end end
end end
......
...@@ -4,7 +4,7 @@ describe Gitlab::Git::RemoteRepository, seed_helper: true do ...@@ -4,7 +4,7 @@ describe Gitlab::Git::RemoteRepository, seed_helper: true do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') } let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
subject { described_class.new(repository) } subject { described_class.new(repository) }
describe '#empty_repo?' do describe '#empty?' do
using RSpec::Parameterized::TableSyntax using RSpec::Parameterized::TableSyntax
where(:repository, :result) do where(:repository, :result) do
...@@ -13,7 +13,7 @@ describe Gitlab::Git::RemoteRepository, seed_helper: true do ...@@ -13,7 +13,7 @@ describe Gitlab::Git::RemoteRepository, seed_helper: true do
end end
with_them do with_them do
it { expect(subject.empty_repo?).to eq(result) } it { expect(subject.empty?).to eq(result) }
end end
end end
......
...@@ -257,7 +257,7 @@ describe Gitlab::Git::Repository, seed_helper: true do ...@@ -257,7 +257,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
end end
describe '#empty?' do describe '#empty?' do
it { expect(repository.empty?).to be_falsey } it { expect(repository).not_to be_empty }
end end
describe '#ref_names' do describe '#ref_names' do
......
...@@ -313,7 +313,6 @@ describe Project do ...@@ -313,7 +313,6 @@ describe Project do
it { is_expected.to delegate_method(method).to(:team) } it { is_expected.to delegate_method(method).to(:team) }
end end
it { is_expected.to delegate_method(:empty_repo?).to(:repository) }
it { is_expected.to delegate_method(:members).to(:team).with_prefix(true) } it { is_expected.to delegate_method(:members).to(:team).with_prefix(true) }
it { is_expected.to delegate_method(:name).to(:owner).with_prefix(true).with_arguments(allow_nil: true) } it { is_expected.to delegate_method(:name).to(:owner).with_prefix(true).with_arguments(allow_nil: true) }
end end
...@@ -656,6 +655,24 @@ describe Project do ...@@ -656,6 +655,24 @@ describe Project do
end end
end end
describe '#empty_repo?' do
context 'when the repo does not exist' do
let(:project) { build_stubbed(:project) }
it 'returns true' do
expect(project.empty_repo?).to be(true)
end
end
context 'when the repo exists' do
let(:project) { create(:project, :repository) }
let(:empty_project) { create(:project, :empty_repo) }
it { expect(empty_project.empty_repo?).to be(true) }
it { expect(project.empty_repo?).to be(false) }
end
end
describe '#external_issue_tracker' do describe '#external_issue_tracker' do
let(:project) { create(:project) } let(:project) { create(:project) }
let(:ext_project) { create(:redmine_project) } let(:ext_project) { create(:redmine_project) }
......
...@@ -583,7 +583,7 @@ describe Repository do ...@@ -583,7 +583,7 @@ describe Repository do
end end
it 'properly handles query when repo is empty' do it 'properly handles query when repo is empty' do
repository = create(:project).repository repository = create(:project, :empty_repo).repository
results = repository.search_files_by_content('test', 'master') results = repository.search_files_by_content('test', 'master')
expect(results).to match_array([]) expect(results).to match_array([])
...@@ -619,7 +619,7 @@ describe Repository do ...@@ -619,7 +619,7 @@ describe Repository do
end end
it 'properly handles query when repo is empty' do it 'properly handles query when repo is empty' do
repository = create(:project).repository repository = create(:project, :empty_repo).repository
results = repository.search_files_by_name('test', 'master') results = repository.search_files_by_name('test', 'master')
...@@ -1204,17 +1204,15 @@ describe Repository do ...@@ -1204,17 +1204,15 @@ describe Repository do
let(:empty_repository) { create(:project_empty_repo).repository } let(:empty_repository) { create(:project_empty_repo).repository }
it 'returns true for an empty repository' do it 'returns true for an empty repository' do
expect(empty_repository.empty?).to eq(true) expect(empty_repository).to be_empty
end end
it 'returns false for a non-empty repository' do it 'returns false for a non-empty repository' do
expect(repository.empty?).to eq(false) expect(repository).not_to be_empty
end end
it 'caches the output' do it 'caches the output' do
expect(repository.raw_repository).to receive(:empty?) expect(repository.raw_repository).to receive(:has_visible_content?).once
.once
.and_return(false)
repository.empty? repository.empty?
repository.empty? repository.empty?
......
...@@ -212,7 +212,7 @@ describe 'gitlab:app namespace rake task' do ...@@ -212,7 +212,7 @@ describe 'gitlab:app namespace rake task' do
# Avoid asking gitaly about the root ref (which will fail beacuse of the # Avoid asking gitaly about the root ref (which will fail beacuse of the
# mocked storages) # mocked storages)
allow_any_instance_of(Repository).to receive(:empty_repo?).and_return(false) allow_any_instance_of(Repository).to receive(:empty?).and_return(false)
end end
after do after do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment