Commit a2aad053 authored by Rémy Coutable's avatar Rémy Coutable

Merge branch 'gitaly-multi-action-prep' into 'master'

Move git operations for multi_action into Gitlab::Git

Closes gitaly#872

See merge request gitlab-org/gitlab-ce!16215
parents 89f174da 0b07be59
...@@ -783,34 +783,30 @@ class Repository ...@@ -783,34 +783,30 @@ class Repository
end end
def create_dir(user, path, **options) def create_dir(user, path, **options)
options[:user] = user
options[:actions] = [{ action: :create_dir, file_path: path }] options[:actions] = [{ action: :create_dir, file_path: path }]
multi_action(**options) multi_action(user, **options)
end end
def create_file(user, path, content, **options) def create_file(user, path, content, **options)
options[:user] = user
options[:actions] = [{ action: :create, file_path: path, content: content }] options[:actions] = [{ action: :create, file_path: path, content: content }]
multi_action(**options) multi_action(user, **options)
end end
def update_file(user, path, content, **options) def update_file(user, path, content, **options)
previous_path = options.delete(:previous_path) previous_path = options.delete(:previous_path)
action = previous_path && previous_path != path ? :move : :update action = previous_path && previous_path != path ? :move : :update
options[:user] = user
options[:actions] = [{ action: action, file_path: path, previous_path: previous_path, content: content }] options[:actions] = [{ action: action, file_path: path, previous_path: previous_path, content: content }]
multi_action(**options) multi_action(user, **options)
end end
def delete_file(user, path, **options) def delete_file(user, path, **options)
options[:user] = user
options[:actions] = [{ action: :delete, file_path: path }] options[:actions] = [{ action: :delete, file_path: path }]
multi_action(**options) multi_action(user, **options)
end end
def with_cache_hooks def with_cache_hooks
...@@ -824,59 +820,14 @@ class Repository ...@@ -824,59 +820,14 @@ class Repository
result.newrev result.newrev
end end
def with_branch(user, *args) def multi_action(user, **options)
with_cache_hooks do start_project = options.delete(:start_project)
Gitlab::Git::OperationService.new(user, raw_repository).with_branch(*args) do |start_commit|
yield start_commit
end
end
end
# rubocop:disable Metrics/ParameterLists
def multi_action(
user:, branch_name:, message:, actions:,
author_email: nil, author_name: nil,
start_branch_name: nil, start_project: project)
with_branch(
user,
branch_name,
start_branch_name: start_branch_name,
start_repository: start_project.repository.raw_repository) do |start_commit|
index = Gitlab::Git::Index.new(raw_repository)
if start_commit
index.read_tree(start_commit.rugged_commit.tree)
parents = [start_commit.sha]
else
parents = []
end
actions.each do |options| if start_project
index.public_send(options.delete(:action), options) # rubocop:disable GitlabSecurity/PublicSend options[:start_repository] = start_project.repository.raw_repository
end
options = {
tree: index.write_tree,
message: message,
parents: parents
}
options.merge!(get_committer_and_author(user, email: author_email, name: author_name))
create_commit(options)
end end
end
# rubocop:enable Metrics/ParameterLists
def get_committer_and_author(user, email: nil, name: nil) with_cache_hooks { raw.multi_action(user, **options) }
committer = user_to_committer(user)
author = Gitlab::Git.committer_hash(email: email, name: name) || committer
{
author: author,
committer: committer
}
end end
def can_be_merged?(source_sha, target_branch) def can_be_merged?(source_sha, target_branch)
......
...@@ -4,7 +4,7 @@ module Files ...@@ -4,7 +4,7 @@ module Files
def create_commit! def create_commit!
repository.multi_action( repository.multi_action(
user: current_user, current_user,
message: @commit_message, message: @commit_message,
branch_name: @branch_name, branch_name: @branch_name,
actions: params[:actions], actions: params[:actions],
...@@ -13,6 +13,8 @@ module Files ...@@ -13,6 +13,8 @@ module Files
start_project: @start_project, start_project: @start_project,
start_branch_name: @start_branch start_branch_name: @start_branch
) )
rescue ArgumentError => e
raise_error(e)
end end
private private
...@@ -20,16 +22,7 @@ module Files ...@@ -20,16 +22,7 @@ module Files
def validate! def validate!
super super
params[:actions].each do |action| params[:actions].each { |action| validate_file_status!(action) }
validate_action!(action)
validate_file_status!(action)
end
end
def validate_action!(action)
unless Gitlab::Git::Index::ACTIONS.include?(action[:action].to_s)
raise_error("Unknown action '#{action[:action]}'")
end
end end
def validate_file_status!(action) def validate_file_status!(action)
......
...@@ -10,6 +10,7 @@ module Gitlab ...@@ -10,6 +10,7 @@ module Gitlab
DEFAULT_MODE = 0o100644 DEFAULT_MODE = 0o100644
ACTIONS = %w(create create_dir update move delete).freeze ACTIONS = %w(create create_dir update move delete).freeze
ACTION_OPTIONS = %i(file_path previous_path content encoding).freeze
attr_reader :repository, :raw_index attr_reader :repository, :raw_index
...@@ -20,6 +21,11 @@ module Gitlab ...@@ -20,6 +21,11 @@ module Gitlab
delegate :read_tree, :get, to: :raw_index delegate :read_tree, :get, to: :raw_index
def apply(action, options)
validate_action!(action)
public_send(action, options.slice(*ACTION_OPTIONS)) # rubocop:disable GitlabSecurity/PublicSend
end
def write_tree def write_tree
raw_index.write_tree(repository.rugged) raw_index.write_tree(repository.rugged)
end end
...@@ -140,6 +146,12 @@ module Gitlab ...@@ -140,6 +146,12 @@ module Gitlab
rescue Rugged::IndexError => e rescue Rugged::IndexError => e
raise IndexError, e.message raise IndexError, e.message
end end
def validate_action!(action)
unless ACTIONS.include?(action.to_s)
raise ArgumentError, "Unknown action '#{action}'"
end
end
end end
end end
end end
...@@ -1300,6 +1300,42 @@ module Gitlab ...@@ -1300,6 +1300,42 @@ module Gitlab
success || gitlab_projects_error success || gitlab_projects_error
end end
# rubocop:disable Metrics/ParameterLists
def multi_action(
user, branch_name:, message:, actions:,
author_email: nil, author_name: nil,
start_branch_name: nil, start_repository: self)
OperationService.new(user, self).with_branch(
branch_name,
start_branch_name: start_branch_name,
start_repository: start_repository
) do |start_commit|
index = Gitlab::Git::Index.new(self)
parents = []
if start_commit
index.read_tree(start_commit.rugged_commit.tree)
parents = [start_commit.sha]
end
actions.each { |opts| index.apply(opts.delete(:action), opts) }
committer = user_to_committer(user)
author = Gitlab::Git.committer_hash(email: author_email, name: author_name) || committer
options = {
tree: index.write_tree,
message: message,
parents: parents,
author: author,
committer: committer
}
create_commit(options)
end
end
# rubocop:enable Metrics/ParameterLists
def gitaly_repository def gitaly_repository
Gitlab::GitalyClient::Util.repository(@storage, @relative_path, @gl_repository) Gitlab::GitalyClient::Util.repository(@storage, @relative_path, @gl_repository)
end end
......
...@@ -582,38 +582,6 @@ describe Repository do ...@@ -582,38 +582,6 @@ describe Repository do
end end
end end
describe '#get_committer_and_author' do
it 'returns the committer and author data' do
options = repository.get_committer_and_author(user)
expect(options[:committer][:email]).to eq(user.email)
expect(options[:author][:email]).to eq(user.email)
end
context 'when the email/name are given' do
it 'returns an object containing the email/name' do
options = repository.get_committer_and_author(user, email: author_email, name: author_name)
expect(options[:author][:email]).to eq(author_email)
expect(options[:author][:name]).to eq(author_name)
end
end
context 'when the email is given but the name is not' do
it 'returns the committer as the author' do
options = repository.get_committer_and_author(user, email: author_email)
expect(options[:author][:email]).to eq(user.email)
expect(options[:author][:name]).to eq(user.name)
end
end
context 'when the name is given but the email is not' do
it 'returns nil' do
options = repository.get_committer_and_author(user, name: author_name)
expect(options[:author][:email]).to eq(user.email)
expect(options[:author][:name]).to eq(user.name)
end
end
end
describe "search_files_by_content" do describe "search_files_by_content" do
let(:results) { repository.search_files_by_content('feature', 'master') } let(:results) { repository.search_files_by_content('feature', 'master') }
subject { results } subject { results }
...@@ -1112,16 +1080,16 @@ describe Repository do ...@@ -1112,16 +1080,16 @@ describe Repository do
allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, '']) allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, ''])
end end
it 'expires branch cache' do subject do
expect(repository).not_to receive(:expire_exists_cache) Gitlab::Git::OperationService.new(git_user, repository.raw_repository).with_branch('new-feature') do
expect(repository).not_to receive(:expire_root_ref_cache)
expect(repository).not_to receive(:expire_emptiness_caches)
expect(repository).to receive(:expire_branches_cache)
repository.with_branch(user, 'new-feature') do
new_rev new_rev
end end
end end
it 'returns branch_created as true' do
expect(subject).not_to be_repo_created
expect(subject).to be_branch_created
end
end end
context 'when repository is empty' do context 'when repository is empty' do
......
...@@ -41,7 +41,7 @@ describe Files::MultiService do ...@@ -41,7 +41,7 @@ describe Files::MultiService do
describe '#execute' do describe '#execute' do
context 'with a valid action' do context 'with a valid action' do
it 'returns a hash with the :success status ' do it 'returns a hash with the :success status' do
results = subject.execute results = subject.execute
expect(results[:status]).to eq(:success) expect(results[:status]).to eq(:success)
...@@ -51,7 +51,7 @@ describe Files::MultiService do ...@@ -51,7 +51,7 @@ describe Files::MultiService do
context 'with an invalid action' do context 'with an invalid action' do
let(:action) { 'rename' } let(:action) { 'rename' }
it 'returns a hash with the :error status ' do it 'returns a hash with the :error status' do
results = subject.execute results = subject.execute
expect(results[:status]).to eq(:error) expect(results[:status]).to eq(:error)
......
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