Migrate Repository#can_be_merged? to Gitaly
This commit is contained in:
parent
639cfdc221
commit
b4b267b739
7 changed files with 150 additions and 89 deletions
|
@ -831,13 +831,12 @@ class Repository
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_be_merged?(source_sha, target_branch)
|
def can_be_merged?(source_sha, target_branch)
|
||||||
our_commit = rugged.branches[target_branch].target
|
raw_repository.gitaly_migrate(:can_be_merged) do |is_enabled|
|
||||||
their_commit = rugged.lookup(source_sha)
|
if is_enabled
|
||||||
|
gitaly_can_be_merged?(source_sha, find_branch(target_branch).target)
|
||||||
if our_commit && their_commit
|
else
|
||||||
!rugged.merge_commits(our_commit, their_commit).conflicts?
|
rugged_can_be_merged?(source_sha, target_branch)
|
||||||
else
|
end
|
||||||
false
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1128,6 +1127,14 @@ class Repository
|
||||||
Gitlab::Git::Repository.new(project.repository_storage, disk_path + '.git', Gitlab::GlRepository.gl_repository(project, is_wiki))
|
Gitlab::Git::Repository.new(project.repository_storage, disk_path + '.git', Gitlab::GlRepository.gl_repository(project, is_wiki))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def gitaly_can_be_merged?(their_commit, our_commit)
|
||||||
|
!raw_repository.gitaly_conflicts_client(our_commit, their_commit).conflicts?
|
||||||
|
end
|
||||||
|
|
||||||
|
def rugged_can_be_merged?(their_commit, our_commit)
|
||||||
|
!rugged.merge_commits(our_commit, their_commit).conflicts?
|
||||||
|
end
|
||||||
|
|
||||||
def find_commits_by_message_by_shelling_out(query, ref, path, limit, offset)
|
def find_commits_by_message_by_shelling_out(query, ref, path, limit, offset)
|
||||||
ref ||= root_ref
|
ref ||= root_ref
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ module Gitlab
|
||||||
@conflicts ||= begin
|
@conflicts ||= begin
|
||||||
@target_repository.gitaly_migrate(:conflicts_list_conflict_files) do |is_enabled|
|
@target_repository.gitaly_migrate(:conflicts_list_conflict_files) do |is_enabled|
|
||||||
if is_enabled
|
if is_enabled
|
||||||
gitaly_conflicts_client(@target_repository).list_conflict_files
|
gitaly_conflicts_client(@target_repository).list_conflict_files.to_a
|
||||||
else
|
else
|
||||||
rugged_list_conflict_files
|
rugged_list_conflict_files
|
||||||
end
|
end
|
||||||
|
|
47
lib/gitlab/gitaly_client/conflict_files_stitcher.rb
Normal file
47
lib/gitlab/gitaly_client/conflict_files_stitcher.rb
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
module Gitlab
|
||||||
|
module GitalyClient
|
||||||
|
class ConflictFilesStitcher
|
||||||
|
include Enumerable
|
||||||
|
|
||||||
|
def initialize(rpc_response)
|
||||||
|
@rpc_response = rpc_response
|
||||||
|
end
|
||||||
|
|
||||||
|
def each
|
||||||
|
current_file = nil
|
||||||
|
|
||||||
|
@rpc_response.each do |msg|
|
||||||
|
msg.files.each do |gitaly_file|
|
||||||
|
if gitaly_file.header
|
||||||
|
yield current_file if current_file
|
||||||
|
|
||||||
|
current_file = file_from_gitaly_header(gitaly_file.header)
|
||||||
|
else
|
||||||
|
current_file.content << gitaly_file.content
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
yield current_file if current_file
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def file_from_gitaly_header(header)
|
||||||
|
Gitlab::Git::Conflict::File.new(
|
||||||
|
Gitlab::GitalyClient::Util.git_repository(header.repository),
|
||||||
|
header.commit_oid,
|
||||||
|
conflict_from_gitaly_file_header(header),
|
||||||
|
''
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def conflict_from_gitaly_file_header(header)
|
||||||
|
{
|
||||||
|
ours: { path: header.our_path, mode: header.our_mode },
|
||||||
|
theirs: { path: header.their_path }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -20,7 +20,11 @@ module Gitlab
|
||||||
)
|
)
|
||||||
response = GitalyClient.call(@repository.storage, :conflicts_service, :list_conflict_files, request)
|
response = GitalyClient.call(@repository.storage, :conflicts_service, :list_conflict_files, request)
|
||||||
|
|
||||||
files_from_response(response).to_a
|
GitalyClient::ConflictFilesStitcher.new(response)
|
||||||
|
end
|
||||||
|
|
||||||
|
def conflicts?
|
||||||
|
list_conflict_files.any?
|
||||||
end
|
end
|
||||||
|
|
||||||
def resolve_conflicts(target_repository, resolution, source_branch, target_branch)
|
def resolve_conflicts(target_repository, resolution, source_branch, target_branch)
|
||||||
|
@ -58,38 +62,6 @@ module Gitlab
|
||||||
user: Gitlab::Git::User.from_gitlab(resolution.user).to_gitaly
|
user: Gitlab::Git::User.from_gitlab(resolution.user).to_gitaly
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def files_from_response(response)
|
|
||||||
files = []
|
|
||||||
|
|
||||||
response.each do |msg|
|
|
||||||
msg.files.each do |gitaly_file|
|
|
||||||
if gitaly_file.header
|
|
||||||
files << file_from_gitaly_header(gitaly_file.header)
|
|
||||||
else
|
|
||||||
files.last.content << gitaly_file.content
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
files
|
|
||||||
end
|
|
||||||
|
|
||||||
def file_from_gitaly_header(header)
|
|
||||||
Gitlab::Git::Conflict::File.new(
|
|
||||||
Gitlab::GitalyClient::Util.git_repository(header.repository),
|
|
||||||
header.commit_oid,
|
|
||||||
conflict_from_gitaly_file_header(header),
|
|
||||||
''
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def conflict_from_gitaly_file_header(header)
|
|
||||||
{
|
|
||||||
ours: { path: header.our_path, mode: header.our_mode },
|
|
||||||
theirs: { path: header.their_path }
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Gitlab::GitalyClient::ConflictFilesStitcher do
|
||||||
|
describe 'enumeration' do
|
||||||
|
it 'combines segregated ConflictFile messages together' do
|
||||||
|
target_project = create(:project, :repository)
|
||||||
|
target_repository = target_project.repository.raw
|
||||||
|
target_gitaly_repository = target_repository.gitaly_repository
|
||||||
|
|
||||||
|
our_path_1 = 'our/path/1'
|
||||||
|
their_path_1 = 'their/path/1'
|
||||||
|
our_mode_1 = 0744
|
||||||
|
commit_oid_1 = 'f00'
|
||||||
|
content_1 = 'content of the first file'
|
||||||
|
|
||||||
|
our_path_2 = 'our/path/2'
|
||||||
|
their_path_2 = 'their/path/2'
|
||||||
|
our_mode_2 = 0600
|
||||||
|
commit_oid_2 = 'ba7'
|
||||||
|
content_2 = 'content of the second file'
|
||||||
|
|
||||||
|
header_1 = double(repository: target_gitaly_repository, commit_oid: commit_oid_1,
|
||||||
|
our_path: our_path_1, their_path: their_path_1, our_mode: our_mode_1)
|
||||||
|
header_2 = double(repository: target_gitaly_repository, commit_oid: commit_oid_2,
|
||||||
|
our_path: our_path_2, their_path: their_path_2, our_mode: our_mode_2)
|
||||||
|
|
||||||
|
messages = [
|
||||||
|
double(files: [double(header: header_1), double(header: nil, content: content_1[0..5])]),
|
||||||
|
double(files: [double(header: nil, content: content_1[6..-1])]),
|
||||||
|
double(files: [double(header: header_2)]),
|
||||||
|
double(files: [double(header: nil, content: content_2[0..5]), double(header: nil, content: content_2[6..10])]),
|
||||||
|
double(files: [double(header: nil, content: content_2[11..-1])])
|
||||||
|
]
|
||||||
|
|
||||||
|
conflict_files = described_class.new(messages).to_a
|
||||||
|
|
||||||
|
expect(conflict_files.size).to be(2)
|
||||||
|
|
||||||
|
expect(conflict_files[0].content).to eq(content_1)
|
||||||
|
expect(conflict_files[0].their_path).to eq(their_path_1)
|
||||||
|
expect(conflict_files[0].our_path).to eq(our_path_1)
|
||||||
|
expect(conflict_files[0].our_mode).to be(our_mode_1)
|
||||||
|
expect(conflict_files[0].repository).to eq(target_repository)
|
||||||
|
expect(conflict_files[0].commit_oid).to eq(commit_oid_1)
|
||||||
|
|
||||||
|
expect(conflict_files[1].content).to eq(content_2)
|
||||||
|
expect(conflict_files[1].their_path).to eq(their_path_2)
|
||||||
|
expect(conflict_files[1].our_path).to eq(our_path_2)
|
||||||
|
expect(conflict_files[1].our_mode).to be(our_mode_2)
|
||||||
|
expect(conflict_files[1].repository).to eq(target_repository)
|
||||||
|
expect(conflict_files[1].commit_oid).to eq(commit_oid_2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -19,41 +19,12 @@ describe Gitlab::GitalyClient::ConflictsService do
|
||||||
their_commit_oid: their_commit_oid
|
their_commit_oid: their_commit_oid
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
let(:our_path) { 'our/path' }
|
|
||||||
let(:their_path) { 'their/path' }
|
|
||||||
let(:our_mode) { 0744 }
|
|
||||||
let(:header) do
|
|
||||||
double(repository: target_gitaly_repository, commit_oid: our_commit_oid,
|
|
||||||
our_path: our_path, our_mode: 0744, their_path: their_path)
|
|
||||||
end
|
|
||||||
let(:response) do
|
|
||||||
[
|
|
||||||
double(files: [double(header: header), double(content: 'foo', header: nil)]),
|
|
||||||
double(files: [double(content: 'bar', header: nil)])
|
|
||||||
]
|
|
||||||
end
|
|
||||||
let(:file) { subject[0] }
|
|
||||||
|
|
||||||
subject { client.list_conflict_files }
|
|
||||||
|
|
||||||
it 'sends an RPC request' do
|
it 'sends an RPC request' do
|
||||||
expect_any_instance_of(Gitaly::ConflictsService::Stub).to receive(:list_conflict_files)
|
expect_any_instance_of(Gitaly::ConflictsService::Stub).to receive(:list_conflict_files)
|
||||||
.with(request, kind_of(Hash)).and_return([])
|
.with(request, kind_of(Hash)).and_return([].to_enum)
|
||||||
|
|
||||||
subject
|
client.list_conflict_files
|
||||||
end
|
|
||||||
|
|
||||||
it 'forms a Gitlab::Git::ConflictFile collection from the response' do
|
|
||||||
allow_any_instance_of(Gitaly::ConflictsService::Stub).to receive(:list_conflict_files)
|
|
||||||
.with(request, kind_of(Hash)).and_return(response)
|
|
||||||
|
|
||||||
expect(subject.size).to be(1)
|
|
||||||
expect(file.content).to eq('foobar')
|
|
||||||
expect(file.their_path).to eq(their_path)
|
|
||||||
expect(file.our_path).to eq(our_path)
|
|
||||||
expect(file.our_mode).to be(our_mode)
|
|
||||||
expect(file.repository).to eq(target_repository)
|
|
||||||
expect(file.commit_oid).to eq(our_commit_oid)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -358,28 +358,38 @@ describe Repository do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#can_be_merged?' do
|
describe '#can_be_merged?' do
|
||||||
context 'mergeable branches' do
|
shared_examples 'can be merged' do
|
||||||
subject { repository.can_be_merged?('0b4bc9a49b562e85de7cc9e834518ea6828729b9', 'master') }
|
context 'mergeable branches' do
|
||||||
|
subject { repository.can_be_merged?('0b4bc9a49b562e85de7cc9e834518ea6828729b9', 'master') }
|
||||||
|
|
||||||
it { is_expected.to be_truthy }
|
it { is_expected.to be_truthy }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'non-mergeable branches' do
|
||||||
|
subject { repository.can_be_merged?('bb5206fee213d983da88c47f9cf4cc6caf9c66dc', 'feature') }
|
||||||
|
|
||||||
|
it { is_expected.to be_falsey }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'non merged branch' do
|
||||||
|
subject { repository.merged_to_root_ref?('fix') }
|
||||||
|
|
||||||
|
it { is_expected.to be_falsey }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'non existent branch' do
|
||||||
|
subject { repository.merged_to_root_ref?('non_existent_branch') }
|
||||||
|
|
||||||
|
it { is_expected.to be_nil }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'non-mergeable branches' do
|
context 'when Gitaly can_be_merged feature is enabled' do
|
||||||
subject { repository.can_be_merged?('bb5206fee213d983da88c47f9cf4cc6caf9c66dc', 'feature') }
|
it_behaves_like 'can be merged'
|
||||||
|
|
||||||
it { is_expected.to be_falsey }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'non merged branch' do
|
context 'when Gitaly can_be_merged feature is disabled', :disable_gitaly do
|
||||||
subject { repository.merged_to_root_ref?('fix') }
|
it_behaves_like 'can be merged'
|
||||||
|
|
||||||
it { is_expected.to be_falsey }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'non existent branch' do
|
|
||||||
subject { repository.merged_to_root_ref?('non_existent_branch') }
|
|
||||||
|
|
||||||
it { is_expected.to be_nil }
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue