diff options
Diffstat (limited to 'spec/lib/gitlab/git/repository_spec.rb')
-rw-r--r-- | spec/lib/gitlab/git/repository_spec.rb | 337 |
1 files changed, 186 insertions, 151 deletions
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 8a9e78ba3c3..e72fb9c6fbc 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -3,6 +3,7 @@ require "spec_helper" describe Gitlab::Git::Repository, :seed_helper do include Gitlab::EncodingHelper + include RepoHelpers using RSpec::Parameterized::TableSyntax shared_examples 'wrapping gRPC errors' do |gitaly_client_class, gitaly_client_method| @@ -28,51 +29,6 @@ describe Gitlab::Git::Repository, :seed_helper do let(:storage_path) { TestEnv.repos_path } let(:user) { build(:user) } - describe '.create_hooks' do - let(:repo_path) { File.join(storage_path, 'hook-test.git') } - let(:hooks_dir) { File.join(repo_path, 'hooks') } - let(:target_hooks_dir) { Gitlab.config.gitlab_shell.hooks_path } - let(:existing_target) { File.join(repo_path, 'foobar') } - - before do - FileUtils.rm_rf(repo_path) - FileUtils.mkdir_p(repo_path) - end - - context 'hooks is a directory' do - let(:existing_file) { File.join(hooks_dir, 'my-file') } - - before do - FileUtils.mkdir_p(hooks_dir) - FileUtils.touch(existing_file) - described_class.create_hooks(repo_path, target_hooks_dir) - end - - it { expect(File.readlink(hooks_dir)).to eq(target_hooks_dir) } - it { expect(Dir[File.join(repo_path, "hooks.old.*/my-file")].count).to eq(1) } - end - - context 'hooks is a valid symlink' do - before do - FileUtils.mkdir_p existing_target - File.symlink(existing_target, hooks_dir) - described_class.create_hooks(repo_path, target_hooks_dir) - end - - it { expect(File.readlink(hooks_dir)).to eq(target_hooks_dir) } - end - - context 'hooks is a broken symlink' do - before do - FileUtils.rm_f(existing_target) - File.symlink(existing_target, hooks_dir) - described_class.create_hooks(repo_path, target_hooks_dir) - end - - it { expect(File.readlink(hooks_dir)).to eq(target_hooks_dir) } - end - end - describe "Respond to" do subject { repository } @@ -95,6 +51,12 @@ describe Gitlab::Git::Repository, :seed_helper do end end + describe '#create_repository' do + it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RepositoryService, :create_repository do + subject { repository.create_repository } + end + end + describe '#branch_names' do subject { repository.branch_names } @@ -152,13 +114,14 @@ describe Gitlab::Git::Repository, :seed_helper do let(:append_sha) { true } let(:ref) { 'master' } let(:format) { nil } + let(:path) { nil } let(:expected_extension) { 'tar.gz' } let(:expected_filename) { "#{expected_prefix}.#{expected_extension}" } let(:expected_path) { File.join(storage_path, cache_key, expected_filename) } let(:expected_prefix) { "gitlab-git-test-#{ref}-#{SeedRepo::LastCommit::ID}" } - subject(:metadata) { repository.archive_metadata(ref, storage_path, 'gitlab-git-test', format, append_sha: append_sha) } + subject(:metadata) { repository.archive_metadata(ref, storage_path, 'gitlab-git-test', format, append_sha: append_sha, path: path) } it 'sets CommitId to the commit SHA' do expect(metadata['CommitId']).to eq(SeedRepo::LastCommit::ID) @@ -176,6 +139,14 @@ describe Gitlab::Git::Repository, :seed_helper do expect(metadata['ArchivePath']).to eq(expected_path) end + context 'path is set' do + let(:path) { 'foo/bar' } + + it 'appends the path to the prefix' do + expect(metadata['ArchivePrefix']).to eq("#{expected_prefix}-foo-bar") + end + end + context 'append_sha varies archive path and filename' do where(:append_sha, :ref, :expected_prefix) do sha = SeedRepo::LastCommit::ID @@ -215,6 +186,18 @@ describe Gitlab::Git::Repository, :seed_helper do it { is_expected.to be < 2 } end + describe '#object_directory_size' do + before do + allow(repository.gitaly_repository_client) + .to receive(:get_object_directory_size) + .and_return(2) + end + + subject { repository.object_directory_size } + + it { is_expected.to eq 2048 } + end + describe '#empty?' do it { expect(repository).not_to be_empty } end @@ -441,20 +424,20 @@ describe Gitlab::Git::Repository, :seed_helper do ensure_seeds end - it "should create a new branch" do + it "creates a new branch" do expect(repository.create_branch('new_branch', 'master')).not_to be_nil end - it "should create a new branch with the right name" do + it "creates a new branch with the right name" do expect(repository.create_branch('another_branch', 'master').name).to eq('another_branch') end - it "should fail if we create an existing branch" do + it "fails if we create an existing branch" do repository.create_branch('duplicated_branch', 'master') expect {repository.create_branch('duplicated_branch', 'master')}.to raise_error("Branch duplicated_branch already exists") end - it "should fail if we create a branch from a non existing ref" do + it "fails if we create a branch from a non existing ref" do expect {repository.create_branch('branch_based_in_wrong_ref', 'master_2_the_revenge')}.to raise_error("Invalid reference master_2_the_revenge") end end @@ -513,7 +496,7 @@ describe Gitlab::Git::Repository, :seed_helper do describe "#refs_hash" do subject { repository.refs_hash } - it "should have as many entries as branches and tags" do + it "has as many entries as branches and tags" do expected_refs = SeedRepo::Repo::BRANCHES + SeedRepo::Repo::TAGS # We flatten in case a commit is pointed at by more than one branch and/or tag expect(subject.values.flatten.size).to eq(expected_refs.size) @@ -522,6 +505,13 @@ describe Gitlab::Git::Repository, :seed_helper do it 'has valid commit ids as keys' do expect(subject.keys).to all( match(Commit::COMMIT_SHA_PATTERN) ) end + + it 'does not error when dereferenced_target is nil' do + blob_id = repository.blob_at('master', 'README.md').id + repository_rugged.tags.create("refs/tags/blob-tag", blob_id) + + expect { subject }.not_to raise_error + end end describe '#fetch_repository_as_mirror' do @@ -604,11 +594,11 @@ describe Gitlab::Git::Repository, :seed_helper do end shared_examples 'search files by content' do - it 'should have 2 items' do + it 'has 2 items' do expect(search_results.size).to eq(2) end - it 'should have the correct matching line' do + it 'has the correct matching line' do expect(search_results).to contain_exactly("search-files-by-content-branch:encoding/CHANGELOG\u00001\u0000search-files-by-content change\n", "search-files-by-content-branch:anotherfile\u00001\u0000search-files-by-content change\n") end @@ -619,16 +609,6 @@ describe Gitlab::Git::Repository, :seed_helper do repository.search_files_by_content('search-files-by-content', 'search-files-by-content-branch') end end - - it_should_behave_like 'search files by content' do - let(:search_results) do - repository.gitaly_repository_client.search_files_by_content( - 'search-files-by-content-branch', - 'search-files-by-content', - chunked_response: false - ) - end - end end describe '#find_remote_root_ref' do @@ -851,7 +831,7 @@ describe Gitlab::Git::Repository, :seed_helper do context "where provides 'after' timestamp" do options = { after: Time.iso8601('2014-03-03T20:15:01+00:00') } - it "should returns commits on or after that timestamp" do + it "returns commits on or after that timestamp" do commits = repository.log(options) expect(commits.size).to be > 0 @@ -864,7 +844,7 @@ describe Gitlab::Git::Repository, :seed_helper do context "where provides 'before' timestamp" do options = { before: Time.iso8601('2014-03-03T20:15:01+00:00') } - it "should returns commits on or before that timestamp" do + it "returns commits on or before that timestamp" do commits = repository.log(options) expect(commits.size).to be > 0 @@ -1065,14 +1045,14 @@ describe Gitlab::Git::Repository, :seed_helper do end describe '#find_branch' do - it 'should return a Branch for master' do + it 'returns a Branch for master' do branch = repository.find_branch('master') expect(branch).to be_a_kind_of(Gitlab::Git::Branch) expect(branch.name).to eq('master') end - it 'should handle non-existent branch' do + it 'handles non-existent branch' do branch = repository.find_branch('this-is-garbage') expect(branch).to eq(nil) @@ -1698,12 +1678,48 @@ describe Gitlab::Git::Repository, :seed_helper do expect(repository.delete_config(*%w[does.not.exist test.foo1 test.foo2])).to be_nil + # Workaround for https://github.com/libgit2/rugged/issues/785: If + # Gitaly changes .gitconfig while Rugged has the file loaded + # Rugged::Repository#each_key will report stale values unless a + # lookup is done first. + expect(repository_rugged.config['test.foo1']).to be_nil config_keys = repository_rugged.config.each_key.to_a expect(config_keys).not_to include('test.foo1') expect(config_keys).not_to include('test.foo2') end end + describe '#merge_to_ref' do + let(:repository) { mutable_repository } + let(:branch_head) { '6d394385cf567f80a8fd85055db1ab4c5295806f' } + let(:left_sha) { 'cfe32cf61b73a0d5e9f13e774abde7ff789b1660' } + let(:right_branch) { 'test-master' } + let(:target_ref) { 'refs/merge-requests/999/merge' } + + before do + repository.create_branch(right_branch, branch_head) unless repository.branch_exists?(right_branch) + end + + def merge_to_ref + repository.merge_to_ref(user, left_sha, right_branch, target_ref, 'Merge message') + end + + it 'generates a commit in the target_ref' do + expect(repository.ref_exists?(target_ref)).to be(false) + + commit_sha = merge_to_ref + ref_head = repository.commit(target_ref) + + expect(commit_sha).to be_present + expect(repository.ref_exists?(target_ref)).to be(true) + expect(ref_head.id).to eq(commit_sha) + end + + it 'does not change the right branch HEAD' do + expect { merge_to_ref }.not_to change { repository.find_branch(right_branch).target } + end + end + describe '#merge' do let(:repository) { mutable_repository } let(:source_sha) { '913c66a37b4a45b9769037c55c2d238bd0942d2e' } @@ -1910,13 +1926,6 @@ describe Gitlab::Git::Repository, :seed_helper do expect { imported_repo.fsck }.not_to raise_exception end - it 'creates a symlink to the global hooks dir' do - imported_repo.create_from_bundle(valid_bundle_path) - hooks_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access { File.join(imported_repo.path, 'hooks') } - - expect(File.readlink(hooks_path)).to eq(Gitlab.config.gitlab_shell.hooks_path) - end - it 'raises an error if the bundle is an attempted malicious payload' do expect do imported_repo.create_from_bundle(malicious_bundle_path) @@ -1924,6 +1933,70 @@ describe Gitlab::Git::Repository, :seed_helper do end end + describe '#compare_source_branch' do + let(:repository) { Gitlab::Git::Repository.new('default', TEST_GITATTRIBUTES_REPO_PATH, '', 'group/project') } + + context 'within same repository' do + it 'does not create a temp ref' do + expect(repository).not_to receive(:fetch_source_branch!) + expect(repository).not_to receive(:delete_refs) + + compare = repository.compare_source_branch('master', repository, 'feature', straight: false) + expect(compare).to be_a(Gitlab::Git::Compare) + expect(compare.commits.count).to be > 0 + end + + it 'returns empty commits when source ref does not exist' do + compare = repository.compare_source_branch('master', repository, 'non-existent-branch', straight: false) + + expect(compare.commits).to be_empty + end + end + + context 'with different repositories' do + context 'when ref is known by source repo, but not by target' do + before do + mutable_repository.write_ref('another-branch', 'feature') + end + + it 'creates temp ref' do + expect(repository).not_to receive(:fetch_source_branch!) + expect(repository).not_to receive(:delete_refs) + + compare = repository.compare_source_branch('master', mutable_repository, 'another-branch', straight: false) + expect(compare).to be_a(Gitlab::Git::Compare) + expect(compare.commits.count).to be > 0 + end + end + + context 'when ref is known by source and target repos' do + before do + mutable_repository.write_ref('another-branch', 'feature') + repository.write_ref('another-branch', 'feature') + end + + it 'does not create a temp ref' do + expect(repository).not_to receive(:fetch_source_branch!) + expect(repository).not_to receive(:delete_refs) + + compare = repository.compare_source_branch('master', mutable_repository, 'another-branch', straight: false) + expect(compare).to be_a(Gitlab::Git::Compare) + expect(compare.commits.count).to be > 0 + end + end + + context 'when ref is unknown by source repo' do + it 'returns nil when source ref does not exist' do + expect(repository).to receive(:fetch_source_branch!).and_call_original + expect(repository).to receive(:delete_refs).and_call_original + + compare = repository.compare_source_branch('master', mutable_repository, 'non-existent-branch', straight: false) + expect(compare).to be_nil + end + end + end + end + describe '#checksum' do it 'calculates the checksum for non-empty repo' do expect(repository.checksum).to eq '51d0a9662681f93e1fee547a6b7ba2bcaf716059' @@ -2097,86 +2170,48 @@ describe Gitlab::Git::Repository, :seed_helper do repository_rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", source_branch.dereferenced_target.sha) end - # Build the options hash that's passed to Rugged::Commit#create - def commit_options(repo, index, target, ref, message) - options = {} - options[:tree] = index.write_tree(repo) - options[:author] = { - email: "test@example.com", - name: "Test Author", - time: Time.gm(2014, "mar", 3, 20, 15, 1) - } - options[:committer] = { - email: "test@example.com", - name: "Test Author", - time: Time.gm(2014, "mar", 3, 20, 15, 1) - } - options[:message] ||= message - options[:parents] = repo.empty? ? [] : [target].compact - options[:update_ref] = ref - - options - end - - # Writes a new commit to the repo and returns a Rugged::Commit. Replaces the - # contents of CHANGELOG with a single new line of text. - def new_commit_edit_old_file(repo) - oid = repo.write("I replaced the changelog with this text", :blob) - index = repo.index - index.read_tree(repo.head.target.tree) - index.add(path: "CHANGELOG", oid: oid, mode: 0100644) - - options = commit_options( - repo, - index, - repo.head.target, - "HEAD", - "Edit CHANGELOG in its original location" - ) - - sha = Rugged::Commit.create(repo, options) - repo.lookup(sha) - end - - # Writes a new commit to the repo and returns a Rugged::Commit. Replaces the - # contents of the specified file_path with new text. - def new_commit_edit_new_file(repo, file_path, commit_message, text, branch = repo.head) - oid = repo.write(text, :blob) - index = repo.index - index.read_tree(branch.target.tree) - index.add(path: file_path, oid: oid, mode: 0100644) - options = commit_options(repo, index, branch.target, branch.canonical_name, commit_message) - sha = Rugged::Commit.create(repo, options) - repo.lookup(sha) - end - - # Writes a new commit to the repo and returns a Rugged::Commit. Replaces the - # contents of encoding/CHANGELOG with new text. - def new_commit_edit_new_file_on_branch(repo, file_path, branch_name, commit_message, text) - branch = repo.branches[branch_name] - new_commit_edit_new_file(repo, file_path, commit_message, text, branch) - end - - # Writes a new commit to the repo and returns a Rugged::Commit. Moves the - # CHANGELOG file to the encoding/ directory. - def new_commit_move_file(repo) - blob_oid = repo.head.target.tree.detect { |i| i[:name] == "CHANGELOG" }[:oid] - file_content = repo.lookup(blob_oid).content - oid = repo.write(file_content, :blob) - index = repo.index - index.read_tree(repo.head.target.tree) - index.add(path: "encoding/CHANGELOG", oid: oid, mode: 0100644) - index.remove("CHANGELOG") - - options = commit_options(repo, index, repo.head.target, "HEAD", "Move CHANGELOG to encoding/") - - sha = Rugged::Commit.create(repo, options) - repo.lookup(sha) - end - def refs(dir) IO.popen(%W[git -C #{dir} for-each-ref], &:read).split("\n").map do |line| line.split("\t").last end end + + describe '#disconnect_alternates' do + let(:project) { create(:project, :repository) } + let(:pool_repository) { create(:pool_repository) } + let(:repository) { project.repository } + let(:repository_path) { File.join(TestEnv.repos_path, repository.relative_path) } + let(:object_pool) { pool_repository.object_pool } + let(:object_pool_path) { File.join(TestEnv.repos_path, object_pool.repository.relative_path) } + let(:object_pool_rugged) { Rugged::Repository.new(object_pool_path) } + + before do + object_pool.create + end + + it 'does not raise an error when disconnecting a non-linked repository' do + expect { repository.disconnect_alternates }.not_to raise_error + end + + it 'removes the alternates file' do + object_pool.link(repository) + + alternates_file = File.join(repository_path, "objects", "info", "alternates") + expect(File.exist?(alternates_file)).to be_truthy + + repository.disconnect_alternates + + expect(File.exist?(alternates_file)).to be_falsey + end + + it 'can still access objects in the object pool' do + object_pool.link(repository) + new_commit = new_commit_edit_old_file(object_pool_rugged) + expect(repository.commit(new_commit.oid).id).to eq(new_commit.oid) + + repository.disconnect_alternates + + expect(repository.commit(new_commit.oid).id).to eq(new_commit.oid) + end + end end |