Merge branch 'feature/migrate-cherry-pick-to-gitaly' into 'master'
Migrate Gitlab::Git::Repository#cherry_pick to Gitaly Closes gitaly#737 See merge request gitlab-org/gitlab-ce!15517
This commit is contained in:
commit
f3a3bd50ea
|
@ -1 +1 @@
|
||||||
0.55.0
|
0.56.0
|
||||||
|
|
2
Gemfile
2
Gemfile
|
@ -400,7 +400,7 @@ group :ed25519 do
|
||||||
end
|
end
|
||||||
|
|
||||||
# Gitaly GRPC client
|
# Gitaly GRPC client
|
||||||
gem 'gitaly-proto', '~> 0.54.0', require: 'gitaly'
|
gem 'gitaly-proto', '~> 0.58.0', require: 'gitaly'
|
||||||
|
|
||||||
gem 'toml-rb', '~> 0.3.15', require: false
|
gem 'toml-rb', '~> 0.3.15', require: false
|
||||||
|
|
||||||
|
|
|
@ -276,7 +276,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.54.0)
|
gitaly-proto (0.58.0)
|
||||||
google-protobuf (~> 3.1)
|
google-protobuf (~> 3.1)
|
||||||
grpc (~> 1.0)
|
grpc (~> 1.0)
|
||||||
github-linguist (4.7.6)
|
github-linguist (4.7.6)
|
||||||
|
@ -1037,7 +1037,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.54.0)
|
gitaly-proto (~> 0.58.0)
|
||||||
github-linguist (~> 4.7.0)
|
github-linguist (~> 4.7.0)
|
||||||
gitlab-flowdock-git-hook (~> 1.0.1)
|
gitlab-flowdock-git-hook (~> 1.0.1)
|
||||||
gitlab-markup (~> 1.6.2)
|
gitlab-markup (~> 1.6.2)
|
||||||
|
|
|
@ -418,6 +418,20 @@ module Gitlab
|
||||||
parent_ids.size > 1
|
parent_ids.size > 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def to_gitaly_commit
|
||||||
|
return raw_commit if raw_commit.is_a?(Gitaly::GitCommit)
|
||||||
|
|
||||||
|
message_split = raw_commit.message.split("\n", 2)
|
||||||
|
Gitaly::GitCommit.new(
|
||||||
|
id: raw_commit.oid,
|
||||||
|
subject: message_split[0] ? message_split[0].chomp.b : "",
|
||||||
|
body: raw_commit.message.b,
|
||||||
|
parent_ids: raw_commit.parent_ids,
|
||||||
|
author: gitaly_commit_author_from_rugged(raw_commit.author),
|
||||||
|
committer: gitaly_commit_author_from_rugged(raw_commit.committer)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def init_from_hash(hash)
|
def init_from_hash(hash)
|
||||||
|
@ -463,6 +477,14 @@ module Gitlab
|
||||||
def serialize_keys
|
def serialize_keys
|
||||||
SERIALIZE_KEYS
|
SERIALIZE_KEYS
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def gitaly_commit_author_from_rugged(author_or_committer)
|
||||||
|
Gitaly::CommitAuthor.new(
|
||||||
|
name: author_or_committer[:name].b,
|
||||||
|
email: author_or_committer[:email].b,
|
||||||
|
date: Google::Protobuf::Timestamp.new(seconds: author_or_committer[:time].to_i)
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -809,44 +809,24 @@ module Gitlab
|
||||||
end
|
end
|
||||||
|
|
||||||
def cherry_pick(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:)
|
def cherry_pick(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:)
|
||||||
OperationService.new(user, self).with_branch(
|
gitaly_migrate(:cherry_pick) do |is_enabled|
|
||||||
branch_name,
|
args = {
|
||||||
start_branch_name: start_branch_name,
|
user: user,
|
||||||
start_repository: start_repository
|
commit: commit,
|
||||||
) do |start_commit|
|
branch_name: branch_name,
|
||||||
|
message: message,
|
||||||
|
start_branch_name: start_branch_name,
|
||||||
|
start_repository: start_repository
|
||||||
|
}
|
||||||
|
|
||||||
Gitlab::Git.check_namespace!(commit, start_repository)
|
if is_enabled
|
||||||
|
gitaly_operations_client.user_cherry_pick(args)
|
||||||
cherry_pick_tree_id = check_cherry_pick_content(commit, start_commit.sha)
|
else
|
||||||
raise CreateTreeError unless cherry_pick_tree_id
|
rugged_cherry_pick(args)
|
||||||
|
end
|
||||||
committer = user_to_committer(user)
|
|
||||||
|
|
||||||
create_commit(message: message,
|
|
||||||
author: {
|
|
||||||
email: commit.author_email,
|
|
||||||
name: commit.author_name,
|
|
||||||
time: commit.authored_date
|
|
||||||
},
|
|
||||||
committer: committer,
|
|
||||||
tree: cherry_pick_tree_id,
|
|
||||||
parents: [start_commit.sha])
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_cherry_pick_content(target_commit, source_sha)
|
|
||||||
args = [target_commit.sha, source_sha]
|
|
||||||
args << 1 if target_commit.merge_commit?
|
|
||||||
|
|
||||||
cherry_pick_index = rugged.cherrypick_commit(*args)
|
|
||||||
return false if cherry_pick_index.conflicts?
|
|
||||||
|
|
||||||
tree_id = cherry_pick_index.write_tree(rugged)
|
|
||||||
return false unless diff_exists?(source_sha, tree_id)
|
|
||||||
|
|
||||||
tree_id
|
|
||||||
end
|
|
||||||
|
|
||||||
def diff_exists?(sha1, sha2)
|
def diff_exists?(sha1, sha2)
|
||||||
rugged.diff(sha1, sha2).size > 0
|
rugged.diff(sha1, sha2).size > 0
|
||||||
end
|
end
|
||||||
|
@ -1673,6 +1653,45 @@ module Gitlab
|
||||||
raise InvalidRef, ex
|
raise InvalidRef, ex
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def rugged_cherry_pick(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:)
|
||||||
|
OperationService.new(user, self).with_branch(
|
||||||
|
branch_name,
|
||||||
|
start_branch_name: start_branch_name,
|
||||||
|
start_repository: start_repository
|
||||||
|
) do |start_commit|
|
||||||
|
|
||||||
|
Gitlab::Git.check_namespace!(commit, start_repository)
|
||||||
|
|
||||||
|
cherry_pick_tree_id = check_cherry_pick_content(commit, start_commit.sha)
|
||||||
|
raise CreateTreeError unless cherry_pick_tree_id
|
||||||
|
|
||||||
|
committer = user_to_committer(user)
|
||||||
|
|
||||||
|
create_commit(message: message,
|
||||||
|
author: {
|
||||||
|
email: commit.author_email,
|
||||||
|
name: commit.author_name,
|
||||||
|
time: commit.authored_date
|
||||||
|
},
|
||||||
|
committer: committer,
|
||||||
|
tree: cherry_pick_tree_id,
|
||||||
|
parents: [start_commit.sha])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_cherry_pick_content(target_commit, source_sha)
|
||||||
|
args = [target_commit.sha, source_sha]
|
||||||
|
args << 1 if target_commit.merge_commit?
|
||||||
|
|
||||||
|
cherry_pick_index = rugged.cherrypick_commit(*args)
|
||||||
|
return false if cherry_pick_index.conflicts?
|
||||||
|
|
||||||
|
tree_id = cherry_pick_index.write_tree(rugged)
|
||||||
|
return false unless diff_exists?(source_sha, tree_id)
|
||||||
|
|
||||||
|
tree_id
|
||||||
|
end
|
||||||
|
|
||||||
def local_fetch_ref(source_path, source_ref:, target_ref:)
|
def local_fetch_ref(source_path, source_ref:, target_ref:)
|
||||||
args = %W(fetch --no-tags -f #{source_path} #{source_ref}:#{target_ref})
|
args = %W(fetch --no-tags -f #{source_path} #{source_ref}:#{target_ref})
|
||||||
run_git(args)
|
run_git(args)
|
||||||
|
|
|
@ -122,6 +122,36 @@ module Gitlab
|
||||||
).branch_update
|
).branch_update
|
||||||
Gitlab::Git::OperationService::BranchUpdate.from_gitaly(branch_update)
|
Gitlab::Git::OperationService::BranchUpdate.from_gitaly(branch_update)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def user_cherry_pick(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:)
|
||||||
|
request = Gitaly::UserCherryPickRequest.new(
|
||||||
|
repository: @gitaly_repo,
|
||||||
|
user: Gitlab::Git::User.from_gitlab(user).to_gitaly,
|
||||||
|
commit: commit.to_gitaly_commit,
|
||||||
|
branch_name: GitalyClient.encode(branch_name),
|
||||||
|
message: GitalyClient.encode(message),
|
||||||
|
start_branch_name: GitalyClient.encode(start_branch_name.to_s),
|
||||||
|
start_repository: start_repository.gitaly_repository
|
||||||
|
)
|
||||||
|
|
||||||
|
response = GitalyClient.call(
|
||||||
|
@repository.storage,
|
||||||
|
:operation_service,
|
||||||
|
:user_cherry_pick,
|
||||||
|
request,
|
||||||
|
remote_storage: start_repository.storage
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.pre_receive_error.presence
|
||||||
|
raise Gitlab::Git::HooksService::PreReceiveError, response.pre_receive_error
|
||||||
|
elsif response.commit_error.presence
|
||||||
|
raise Gitlab::Git::CommitError, response.commit_error
|
||||||
|
elsif response.create_tree_error.presence
|
||||||
|
raise Gitlab::Git::Repository::CreateTreeError, response.create_tree_error
|
||||||
|
else
|
||||||
|
Gitlab::Git::OperationService::BranchUpdate.from_gitaly(response.branch_update)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1408,41 +1408,51 @@ describe Repository do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#cherry_pick' do
|
describe '#cherry_pick' do
|
||||||
let(:conflict_commit) { repository.commit('c642fe9b8b9f28f9225d7ea953fe14e74748d53b') }
|
shared_examples 'cherry-picking a commit' do
|
||||||
let(:pickable_commit) { repository.commit('7d3b0f7cff5f37573aea97cebfd5692ea1689924') }
|
let(:conflict_commit) { repository.commit('c642fe9b8b9f28f9225d7ea953fe14e74748d53b') }
|
||||||
let(:pickable_merge) { repository.commit('e56497bb5f03a90a51293fc6d516788730953899') }
|
let(:pickable_commit) { repository.commit('7d3b0f7cff5f37573aea97cebfd5692ea1689924') }
|
||||||
let(:message) { 'cherry-pick message' }
|
let(:pickable_merge) { repository.commit('e56497bb5f03a90a51293fc6d516788730953899') }
|
||||||
|
let(:message) { 'cherry-pick message' }
|
||||||
|
|
||||||
context 'when there is a conflict' do
|
context 'when there is a conflict' do
|
||||||
it 'raises an error' do
|
it 'raises an error' do
|
||||||
expect { repository.cherry_pick(user, conflict_commit, 'master', message) }.to raise_error(Gitlab::Git::Repository::CreateTreeError)
|
expect { repository.cherry_pick(user, conflict_commit, 'master', message) }.to raise_error(Gitlab::Git::Repository::CreateTreeError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when commit was already cherry-picked' do
|
||||||
|
it 'raises an error' do
|
||||||
|
repository.cherry_pick(user, pickable_commit, 'master', message)
|
||||||
|
|
||||||
|
expect { repository.cherry_pick(user, pickable_commit, 'master', message) }.to raise_error(Gitlab::Git::Repository::CreateTreeError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when commit can be cherry-picked' do
|
||||||
|
it 'cherry-picks the changes' do
|
||||||
|
expect(repository.cherry_pick(user, pickable_commit, 'master', message)).to be_truthy
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'cherry-picking a merge commit' do
|
||||||
|
it 'cherry-picks the changes' do
|
||||||
|
expect(repository.blob_at_branch('improve/awesome', 'foo/bar/.gitkeep')).to be_nil
|
||||||
|
|
||||||
|
cherry_pick_commit_sha = repository.cherry_pick(user, pickable_merge, 'improve/awesome', message)
|
||||||
|
cherry_pick_commit_message = project.commit(cherry_pick_commit_sha).message
|
||||||
|
|
||||||
|
expect(repository.blob_at_branch('improve/awesome', 'foo/bar/.gitkeep')).not_to be_nil
|
||||||
|
expect(cherry_pick_commit_message).to eq(message)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when commit was already cherry-picked' do
|
context 'when Gitaly cherry_pick feature is enabled' do
|
||||||
it 'raises an error' do
|
it_behaves_like 'cherry-picking a commit'
|
||||||
repository.cherry_pick(user, pickable_commit, 'master', message)
|
|
||||||
|
|
||||||
expect { repository.cherry_pick(user, pickable_commit, 'master', message) }.to raise_error(Gitlab::Git::Repository::CreateTreeError)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when commit can be cherry-picked' do
|
context 'when Gitaly cherry_pick feature is disabled', :disable_gitaly do
|
||||||
it 'cherry-picks the changes' do
|
it_behaves_like 'cherry-picking a commit'
|
||||||
expect(repository.cherry_pick(user, pickable_commit, 'master', message)).to be_truthy
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'cherry-picking a merge commit' do
|
|
||||||
it 'cherry-picks the changes' do
|
|
||||||
expect(repository.blob_at_branch('improve/awesome', 'foo/bar/.gitkeep')).to be_nil
|
|
||||||
|
|
||||||
cherry_pick_commit_sha = repository.cherry_pick(user, pickable_merge, 'improve/awesome', message)
|
|
||||||
cherry_pick_commit_message = project.commit(cherry_pick_commit_sha).message
|
|
||||||
|
|
||||||
expect(repository.blob_at_branch('improve/awesome', 'foo/bar/.gitkeep')).not_to be_nil
|
|
||||||
expect(cherry_pick_commit_message).to eq(message)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue