Commit cd770946 authored by Ahmad Sherif's avatar Ahmad Sherif

Add support for :all option to {count,find}_commits

parent cbefd38f
...@@ -411,7 +411,7 @@ group :ed25519 do ...@@ -411,7 +411,7 @@ group :ed25519 do
end end
# Gitaly GRPC client # Gitaly GRPC client
gem 'gitaly-proto', '~> 0.87.0', require: 'gitaly' gem 'gitaly-proto', '~> 0.88.0', require: 'gitaly'
# Locked until https://github.com/google/protobuf/issues/4210 is closed # Locked until https://github.com/google/protobuf/issues/4210 is closed
gem 'google-protobuf', '= 3.5.1' gem 'google-protobuf', '= 3.5.1'
......
...@@ -285,7 +285,7 @@ GEM ...@@ -285,7 +285,7 @@ GEM
po_to_json (>= 1.0.0) po_to_json (>= 1.0.0)
rails (>= 3.2.0) rails (>= 3.2.0)
gherkin-ruby (0.3.2) gherkin-ruby (0.3.2)
gitaly-proto (0.87.0) gitaly-proto (0.88.0)
google-protobuf (~> 3.1) google-protobuf (~> 3.1)
grpc (~> 1.0) grpc (~> 1.0)
github-linguist (5.3.3) github-linguist (5.3.3)
...@@ -1057,7 +1057,7 @@ DEPENDENCIES ...@@ -1057,7 +1057,7 @@ DEPENDENCIES
gettext (~> 3.2.2) gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.2.0) gettext_i18n_rails_js (~> 1.2.0)
gitaly-proto (~> 0.87.0) gitaly-proto (~> 0.88.0)
github-linguist (~> 5.3.3) github-linguist (~> 5.3.3)
gitlab-flowdock-git-hook (~> 1.0.1) gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-markup (~> 1.6.2) gitlab-markup (~> 1.6.2)
......
...@@ -479,9 +479,8 @@ module Gitlab ...@@ -479,9 +479,8 @@ module Gitlab
raise ArgumentError.new("invalid Repository#log limit: #{limit.inspect}") raise ArgumentError.new("invalid Repository#log limit: #{limit.inspect}")
end end
# TODO support options[:all] in Gitaly https://gitlab.com/gitlab-org/gitaly/issues/1049
gitaly_migrate(:find_commits) do |is_enabled| gitaly_migrate(:find_commits) do |is_enabled|
if is_enabled && !options[:all] if is_enabled
gitaly_commit_client.find_commits(options) gitaly_commit_client.find_commits(options)
else else
raw_log(options).map { |c| Commit.decorate(self, c) } raw_log(options).map { |c| Commit.decorate(self, c) }
...@@ -508,9 +507,8 @@ module Gitlab ...@@ -508,9 +507,8 @@ module Gitlab
def count_commits(options) def count_commits(options)
count_commits_options = process_count_commits_options(options) count_commits_options = process_count_commits_options(options)
# TODO add support for options[:all] in Gitaly https://gitlab.com/gitlab-org/gitaly/issues/1050
gitaly_migrate(:count_commits) do |is_enabled| gitaly_migrate(:count_commits) do |is_enabled|
if is_enabled && !options[:all] if is_enabled
count_commits_by_gitaly(count_commits_options) count_commits_by_gitaly(count_commits_options)
else else
count_commits_by_shelling_out(count_commits_options) count_commits_by_shelling_out(count_commits_options)
......
...@@ -134,7 +134,8 @@ module Gitlab ...@@ -134,7 +134,8 @@ module Gitlab
def commit_count(ref, options = {}) def commit_count(ref, options = {})
request = Gitaly::CountCommitsRequest.new( request = Gitaly::CountCommitsRequest.new(
repository: @gitaly_repo, repository: @gitaly_repo,
revision: encode_binary(ref) revision: encode_binary(ref),
all: !!options[:all]
) )
request.after = Google::Protobuf::Timestamp.new(seconds: options[:after].to_i) if options[:after].present? request.after = Google::Protobuf::Timestamp.new(seconds: options[:after].to_i) if options[:after].present?
request.before = Google::Protobuf::Timestamp.new(seconds: options[:before].to_i) if options[:before].present? request.before = Google::Protobuf::Timestamp.new(seconds: options[:before].to_i) if options[:before].present?
...@@ -269,6 +270,7 @@ module Gitlab ...@@ -269,6 +270,7 @@ module Gitlab
offset: options[:offset], offset: options[:offset],
follow: options[:follow], follow: options[:follow],
skip_merges: options[:skip_merges], skip_merges: options[:skip_merges],
all: !!options[:all],
disable_walk: true # This option is deprecated. The 'walk' implementation is being removed. disable_walk: true # This option is deprecated. The 'walk' implementation is being removed.
) )
request.after = GitalyClient.timestamp(options[:after]) if options[:after] request.after = GitalyClient.timestamp(options[:after]) if options[:after]
......
...@@ -751,255 +751,263 @@ describe Gitlab::Git::Repository, seed_helper: true do ...@@ -751,255 +751,263 @@ describe Gitlab::Git::Repository, seed_helper: true do
end end
describe "#log" do describe "#log" do
let(:commit_with_old_name) do shared_examples 'repository log' do
Gitlab::Git::Commit.decorate(repository, @commit_with_old_name_id) let(:commit_with_old_name) do
end Gitlab::Git::Commit.decorate(repository, @commit_with_old_name_id)
let(:commit_with_new_name) do end
Gitlab::Git::Commit.decorate(repository, @commit_with_new_name_id) let(:commit_with_new_name) do
end Gitlab::Git::Commit.decorate(repository, @commit_with_new_name_id)
let(:rename_commit) do end
Gitlab::Git::Commit.decorate(repository, @rename_commit_id) let(:rename_commit) do
end Gitlab::Git::Commit.decorate(repository, @rename_commit_id)
end
before(:context) do
# Add new commits so that there's a renamed file in the commit history
repo = Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged
@commit_with_old_name_id = new_commit_edit_old_file(repo)
@rename_commit_id = new_commit_move_file(repo)
@commit_with_new_name_id = new_commit_edit_new_file(repo)
end
after(:context) do
# Erase our commits so other tests get the original repo
repo = Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged
repo.references.update("refs/heads/master", SeedRepo::LastCommit::ID)
end
context "where 'follow' == true" do
let(:options) { { ref: "master", follow: true } }
context "and 'path' is a directory" do before(:context) do
it "does not follow renames" do # Add new commits so that there's a renamed file in the commit history
log_commits = repository.log(options.merge(path: "encoding")) repo = Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged
@commit_with_old_name_id = new_commit_edit_old_file(repo)
@rename_commit_id = new_commit_move_file(repo)
@commit_with_new_name_id = new_commit_edit_new_file(repo)
end
aggregate_failures do after(:context) do
expect(log_commits).to include(commit_with_new_name) # Erase our commits so other tests get the original repo
expect(log_commits).to include(rename_commit) repo = Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged
expect(log_commits).not_to include(commit_with_old_name) repo.references.update("refs/heads/master", SeedRepo::LastCommit::ID)
end
end
end end
context "and 'path' is a file that matches the new filename" do context "where 'follow' == true" do
context 'without offset' do let(:options) { { ref: "master", follow: true } }
it "follows renames" do
log_commits = repository.log(options.merge(path: "encoding/CHANGELOG")) context "and 'path' is a directory" do
it "does not follow renames" do
log_commits = repository.log(options.merge(path: "encoding"))
aggregate_failures do aggregate_failures do
expect(log_commits).to include(commit_with_new_name) expect(log_commits).to include(commit_with_new_name)
expect(log_commits).to include(rename_commit) expect(log_commits).to include(rename_commit)
expect(log_commits).to include(commit_with_old_name) expect(log_commits).not_to include(commit_with_old_name)
end end
end end
end end
context 'with offset=1' do context "and 'path' is a file that matches the new filename" do
it "follows renames and skip the latest commit" do context 'without offset' do
log_commits = repository.log(options.merge(path: "encoding/CHANGELOG", offset: 1)) it "follows renames" do
log_commits = repository.log(options.merge(path: "encoding/CHANGELOG"))
aggregate_failures do aggregate_failures do
expect(log_commits).not_to include(commit_with_new_name) expect(log_commits).to include(commit_with_new_name)
expect(log_commits).to include(rename_commit) expect(log_commits).to include(rename_commit)
expect(log_commits).to include(commit_with_old_name) expect(log_commits).to include(commit_with_old_name)
end
end end
end end
end
context 'with offset=1', 'and limit=1' do context 'with offset=1' do
it "follows renames, skip the latest commit and return only one commit" do it "follows renames and skip the latest commit" do
log_commits = repository.log(options.merge(path: "encoding/CHANGELOG", offset: 1, limit: 1)) log_commits = repository.log(options.merge(path: "encoding/CHANGELOG", offset: 1))
expect(log_commits).to contain_exactly(rename_commit) aggregate_failures do
expect(log_commits).not_to include(commit_with_new_name)
expect(log_commits).to include(rename_commit)
expect(log_commits).to include(commit_with_old_name)
end
end
end end
end
context 'with offset=1', 'and limit=2' do context 'with offset=1', 'and limit=1' do
it "follows renames, skip the latest commit and return only two commits" do it "follows renames, skip the latest commit and return only one commit" do
log_commits = repository.log(options.merge(path: "encoding/CHANGELOG", offset: 1, limit: 2)) log_commits = repository.log(options.merge(path: "encoding/CHANGELOG", offset: 1, limit: 1))
aggregate_failures do expect(log_commits).to contain_exactly(rename_commit)
expect(log_commits).to contain_exactly(rename_commit, commit_with_old_name)
end end
end end
end
context 'with offset=2' do context 'with offset=1', 'and limit=2' do
it "follows renames and skip the latest commit" do it "follows renames, skip the latest commit and return only two commits" do
log_commits = repository.log(options.merge(path: "encoding/CHANGELOG", offset: 2)) log_commits = repository.log(options.merge(path: "encoding/CHANGELOG", offset: 1, limit: 2))
aggregate_failures do aggregate_failures do
expect(log_commits).not_to include(commit_with_new_name) expect(log_commits).to contain_exactly(rename_commit, commit_with_old_name)
expect(log_commits).not_to include(rename_commit) end
expect(log_commits).to include(commit_with_old_name)
end end
end end
end
context 'with offset=2', 'and limit=1' do context 'with offset=2' do
it "follows renames, skip the two latest commit and return only one commit" do it "follows renames and skip the latest commit" do
log_commits = repository.log(options.merge(path: "encoding/CHANGELOG", offset: 2, limit: 1)) log_commits = repository.log(options.merge(path: "encoding/CHANGELOG", offset: 2))
expect(log_commits).to contain_exactly(commit_with_old_name) aggregate_failures do
expect(log_commits).not_to include(commit_with_new_name)
expect(log_commits).not_to include(rename_commit)
expect(log_commits).to include(commit_with_old_name)
end
end
end
context 'with offset=2', 'and limit=1' do
it "follows renames, skip the two latest commit and return only one commit" do
log_commits = repository.log(options.merge(path: "encoding/CHANGELOG", offset: 2, limit: 1))
expect(log_commits).to contain_exactly(commit_with_old_name)
end
end
context 'with offset=2', 'and limit=2' do
it "follows renames, skip the two latest commit and return only one commit" do
log_commits = repository.log(options.merge(path: "encoding/CHANGELOG", offset: 2, limit: 2))
aggregate_failures do
expect(log_commits).not_to include(commit_with_new_name)
expect(log_commits).not_to include(rename_commit)
expect(log_commits).to include(commit_with_old_name)
end
end
end end
end end
context 'with offset=2', 'and limit=2' do context "and 'path' is a file that matches the old filename" do
it "follows renames, skip the two latest commit and return only one commit" do it "does not follow renames" do
log_commits = repository.log(options.merge(path: "encoding/CHANGELOG", offset: 2, limit: 2)) log_commits = repository.log(options.merge(path: "CHANGELOG"))
aggregate_failures do aggregate_failures do
expect(log_commits).not_to include(commit_with_new_name) expect(log_commits).not_to include(commit_with_new_name)
expect(log_commits).not_to include(rename_commit) expect(log_commits).to include(rename_commit)
expect(log_commits).to include(commit_with_old_name) expect(log_commits).to include(commit_with_old_name)
end end
end end
end end
end
context "and 'path' is a file that matches the old filename" do context "unknown ref" do
it "does not follow renames" do it "returns an empty array" do
log_commits = repository.log(options.merge(path: "CHANGELOG")) log_commits = repository.log(options.merge(ref: 'unknown'))
aggregate_failures do expect(log_commits).to eq([])
expect(log_commits).not_to include(commit_with_new_name)
expect(log_commits).to include(rename_commit)
expect(log_commits).to include(commit_with_old_name)
end end
end end
end end
context "unknown ref" do context "where 'follow' == false" do
it "returns an empty array" do options = { follow: false }
log_commits = repository.log(options.merge(ref: 'unknown'))
expect(log_commits).to eq([])
end
end
end
context "where 'follow' == false" do context "and 'path' is a directory" do
options = { follow: false } let(:log_commits) do
repository.log(options.merge(path: "encoding"))
end
context "and 'path' is a directory" do it "does not follow renames" do
let(:log_commits) do expect(log_commits).to include(commit_with_new_name)
repository.log(options.merge(path: "encoding")) expect(log_commits).to include(rename_commit)
expect(log_commits).not_to include(commit_with_old_name)
end
end end
it "does not follow renames" do context "and 'path' is a file that matches the new filename" do
expect(log_commits).to include(commit_with_new_name) let(:log_commits) do
expect(log_commits).to include(rename_commit) repository.log(options.merge(path: "encoding/CHANGELOG"))
expect(log_commits).not_to include(commit_with_old_name) end
end
end
context "and 'path' is a file that matches the new filename" do it "does not follow renames" do
let(:log_commits) do expect(log_commits).to include(commit_with_new_name)
repository.log(options.merge(path: "encoding/CHANGELOG")) expect(log_commits).to include(rename_commit)
expect(log_commits).not_to include(commit_with_old_name)
end
end end
it "does not follow renames" do context "and 'path' is a file that matches the old filename" do
expect(log_commits).to include(commit_with_new_name) let(:log_commits) do
expect(log_commits).to include(rename_commit) repository.log(options.merge(path: "CHANGELOG"))
expect(log_commits).not_to include(commit_with_old_name) end
end
end
context "and 'path' is a file that matches the old filename" do it "does not follow renames" do
let(:log_commits) do expect(log_commits).to include(commit_with_old_name)
repository.log(options.merge(path: "CHANGELOG")) expect(log_commits).to include(rename_commit)
expect(log_commits).not_to include(commit_with_new_name)
end
end end
it "does not follow renames" do context "and 'path' includes a directory that used to be a file" do
expect(log_commits).to include(commit_with_old_name) let(:log_commits) do
expect(log_commits).to include(rename_commit) repository.log(options.merge(ref: "refs/heads/fix-blob-path", path: "files/testdir/file.txt"))
expect(log_commits).not_to include(commit_with_new_name) end
it "returns a list of commits" do
expect(log_commits.size).to eq(1)
end
end end
end end
context "and 'path' includes a directory that used to be a file" do context "where provides 'after' timestamp" do
let(:log_commits) do options = { after: Time.iso8601('2014-03-03T20:15:01+00:00') }
repository.log(options.merge(ref: "refs/heads/fix-blob-path", path: "files/testdir/file.txt"))
end
it "returns a list of commits" do it "should returns commits on or after that timestamp" do
expect(log_commits.size).to eq(1) commits = repository.log(options)
expect(commits.size).to be > 0
expect(commits).to satisfy do |commits|
commits.all? { |commit| commit.committed_date >= options[:after] }
end
end end
end end
end
context "where provides 'after' timestamp" do context "where provides 'before' timestamp" do
options = { after: Time.iso8601('2014-03-03T20:15:01+00:00') } options = { before: Time.iso8601('2014-03-03T20:15:01+00:00') }
it "should returns commits on or after that timestamp" do it "should returns commits on or before that timestamp" do
commits = repository.log(options) commits = repository.log(options)
expect(commits.size).to be > 0 expect(commits.size).to be > 0
expect(commits).to satisfy do |commits| expect(commits).to satisfy do |commits|
commits.all? { |commit| commit.committed_date >= options[:after] } commits.all? { |commit| commit.committed_date <= options[:before] }
end
end end
end end
end
context "where provides 'before' timestamp" do context 'when multiple paths are provided' do
options = { before: Time.iso8601('2014-03-03T20:15:01+00:00') } let(:options) { { ref: 'master', path: ['PROCESS.md', 'README.md'] } }
it "should returns commits on or before that timestamp" do def commit_files(commit)
commits = repository.log(options) commit.rugged_diff_from_parent.deltas.flat_map do |delta|
[delta.old_file[:path], delta.new_file[:path]].uniq.compact
expect(commits.size).to be > 0 end
expect(commits).to satisfy do |commits|
commits.all? { |commit| commit.committed_date <= options[:before] }
end end
end
end
context 'when multiple paths are provided' do it 'only returns commits matching at least one path' do
let(:options) { { ref: 'master', path: ['PROCESS.md', 'README.md'] } } commits = repository.log(options)
def commit_files(commit) expect(commits.size).to be > 0
commit.rugged_diff_from_parent.deltas.flat_map do |delta| expect(commits).to satisfy do |commits|
[delta.old_file[:path], delta.new_file[:path]].uniq.compact commits.none? { |commit| (commit_files(commit) & options[:path]).empty? }
end
end end
end end
it 'only returns commits matching at least one path' do context 'limit validation' do
commits = repository.log(options) where(:limit) do
[0, nil, '', 'foo']
end
expect(commits.size).to be > 0 with_them do
expect(commits).to satisfy do |commits| it { expect { repository.log(limit: limit) }.to raise_error(ArgumentError) }
commits.none? { |commit| (commit_files(commit) & options[:path]).empty? }
end end
end end
end
context 'limit validation' do context 'with all' do
where(:limit) do it 'returns a list of commits' do
[0, nil, '', 'foo'] commits = repository.log({ all: true, limit: 50 })
end
with_them do expect(commits.size).to eq(37)
it { expect { repository.log(limit: limit) }.to raise_error(ArgumentError) } end
end end
end end
context 'with all' do context 'when Gitaly find_commits feature is enabled' do
let(:options) { { all: true, limit: 50 } } it_behaves_like 'repository log'
end
it 'returns a list of commits' do
commits = repository.log(options)
expect(commits.size).to eq(37) context 'when Gitaly find_commits feature is disabled', :disable_gitaly do
end it_behaves_like 'repository log'
end end
end end
...@@ -1136,14 +1144,6 @@ describe Gitlab::Git::Repository, seed_helper: true do ...@@ -1136,14 +1144,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
expect(repository.count_commits(options)).to eq(10) expect(repository.count_commits(options)).to eq(10)
end end
end end
end
context 'when Gitaly count_commits feature is enabled' do
it_behaves_like 'extended commit counting'
end
context 'when Gitaly count_commits feature is disabled', :skip_gitaly_mock do
it_behaves_like 'extended commit counting'
context "with all" do context "with all" do
it "returns the number of commits in the whole repository" do it "returns the number of commits in the whole repository" do
...@@ -1155,10 +1155,18 @@ describe Gitlab::Git::Repository, seed_helper: true do ...@@ -1155,10 +1155,18 @@ describe Gitlab::Git::Repository, seed_helper: true do
context 'without all or ref being specified' do context 'without all or ref being specified' do
it "raises an ArgumentError" do it "raises an ArgumentError" do
expect { repository.count_commits({}) }.to raise_error(ArgumentError, "Please specify a valid ref or set the 'all' attribute to true") expect { repository.count_commits({}) }.to raise_error(ArgumentError)
end end
end end
end end
context 'when Gitaly count_commits feature is enabled' do
it_behaves_like 'extended commit counting'
end
context 'when Gitaly count_commits feature is disabled', :disable_gitaly do
it_behaves_like 'extended commit counting'
end
end end
describe '#autocrlf' do describe '#autocrlf' 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