Skip to content
Snippets Groups Projects
Commit 351f205c authored by Alejandro Rodríguez's avatar Alejandro Rodríguez
Browse files

Incorporate ConflictsService.ListConflictFiles Gitaly RPC

parent f1d5e509
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -23,7 +23,7 @@ module MergeRequests
# when there are no conflict files.
conflicts.files.each(&:lines)
@conflicts_can_be_resolved_in_ui = conflicts.files.length > 0
rescue Rugged::OdbError, Gitlab::Git::Conflict::Parser::UnresolvableError, Gitlab::Git::Conflict::Resolver::ConflictSideMissing
rescue Gitlab::Git::CommandError, Gitlab::Git::Conflict::Parser::UnresolvableError, Gitlab::Git::Conflict::Resolver::ConflictSideMissing
@conflicts_can_be_resolved_in_ui = false
end
end
Loading
Loading
Loading
Loading
@@ -13,12 +13,18 @@ module Gitlab
 
def conflicts
@conflicts ||= begin
target_index = @target_repository.rugged.merge_commits(@our_commit_oid, @their_commit_oid)
# We don't need to do `with_repo_branch_commit` here, because the target
# project always fetches source refs when creating merge request diffs.
conflict_files(@target_repository, target_index)
@target_repository.gitaly_migrate(:conflicts_list_conflict_files) do |is_enabled|
if is_enabled
@target_repository.gitaly_conflicts_client.list_conflict_files(@our_commit_oid, @their_commit_oid)
else
rugged_list_conflict_files
end
end
end
rescue GRPC::FailedPrecondition => e
raise Gitlab::Git::Conflict::Resolver::ConflictSideMissing.new(e.message)
rescue Rugged::OdbError, GRPC::BadStatus => e
raise Gitlab::Git::CommandError.new(e)
end
 
def resolve_conflicts(source_repository, user, files, source_branch:, target_branch:, commit_message:)
Loading
Loading
@@ -84,6 +90,14 @@ module Gitlab
index.add(path: our_path, oid: oid, mode: file.our_mode)
index.conflict_remove(our_path)
end
def rugged_list_conflict_files
target_index = @target_repository.rugged.merge_commits(@our_commit_oid, @their_commit_oid)
# We don't need to do `with_repo_branch_commit` here, because the target
# project always fetches source refs when creating merge request diffs.
conflict_files(@target_repository, target_index)
end
end
end
end
Loading
Loading
Loading
Loading
@@ -1294,6 +1294,10 @@ module Gitlab
@gitaly_remote_client ||= Gitlab::GitalyClient::RemoteService.new(self)
end
 
def gitaly_conflicts_client
@gitaly_conflicts_client ||= Gitlab::GitalyClient::ConflictsService.new(self)
end
def gitaly_migrate(method, status: Gitlab::GitalyClient::MigrationStatus::OPT_IN, &block)
Gitlab::GitalyClient.migrate(method, status: status, &block)
rescue GRPC::NotFound => e
Loading
Loading
module Gitlab
module GitalyClient
class ConflictsService
def initialize(repository)
@gitaly_repo = repository.gitaly_repository
@repository = repository
end
def list_conflict_files(our_commit_oid, their_commit_oid)
request = Gitaly::ListConflictFilesRequest.new(
repository: @gitaly_repo,
our_commit_oid: our_commit_oid,
their_commit_oid: their_commit_oid
)
response = GitalyClient.call(@repository.storage, :conflicts_service, :list_conflict_files, request)
files = []
header = nil
content = nil
response.each do |msg|
msg.files.each do |gitaly_file|
if gitaly_file.header
# Add previous file to the collection, except on first iteration
files << conflict_file_from_gitaly(header, content) if header
header = gitaly_file.header
content = ""
else
# Append content to curret file
content << gitaly_file.content
end
end
end
# Add leftover file, if any
files << conflict_file_from_gitaly(header, content) if header
files
end
private
def conflict_file_from_gitaly(header, content)
Gitlab::Git::Conflict::File.new(
Gitlab::GitalyClient::Util.git_repository(header.repository),
header.commit_oid,
conflict_from_gitaly_file_header(header),
content
)
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
Loading
Loading
@@ -18,6 +18,12 @@ module Gitlab
)
end
 
def git_repository(gitaly_repository)
Gitlab::Git::Repository.new(gitaly_repository.storage_name,
gitaly_repository.relative_path,
gitaly_repository.gl_repository)
end
def gitlab_tag_from_gitaly_tag(repository, gitaly_tag)
if gitaly_tag.target_commit.present?
commit = Gitlab::Git::Commit.decorate(repository, gitaly_tag.target_commit)
Loading
Loading
require 'spec_helper'
describe Gitlab::GitalyClient::ConflictsService do
let(:project) { create(:project, :repository) }
let(:target_project) { create(:project, :repository) }
let(:repository) { project.repository }
let(:gitaly_repositoy) { repository.gitaly_repository }
let(:target_repository) { target_project.repository }
let(:target_gitaly_repository) { target_repository.gitaly_repository }
describe '#list_conflict_files' do
let(:request) do
Gitaly::ListConflictFilesRequest.new(
repository: target_gitaly_repository, our_commit_oid: our_commit_oid,
their_commit_oid: their_commit_oid
)
end
let(:our_commit_oid) { 'f00' }
let(:their_commit_oid) { 'f44' }
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] }
let(:client) { described_class.new(target_repository) }
subject { client.list_conflict_files(our_commit_oid, their_commit_oid) }
it 'sends an RPC request' do
expect_any_instance_of(Gitaly::ConflictsService::Stub).to receive(:list_conflict_files)
.with(request, kind_of(Hash)).and_return([])
subject
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
Loading
Loading
@@ -32,14 +32,6 @@ describe MergeRequests::Conflicts::ListService do
expect(conflicts_service(merge_request).can_be_resolved_in_ui?).to be_falsey
end
 
it 'returns a falsey value when the MR has a missing ref after a force push' do
merge_request = create_merge_request('conflict-resolvable')
service = conflicts_service(merge_request)
allow_any_instance_of(Rugged::Repository).to receive(:merge_commits).and_raise(Rugged::OdbError)
expect(service.can_be_resolved_in_ui?).to be_falsey
end
it 'returns a falsey value when the MR does not support new diff notes' do
merge_request = create_merge_request('conflict-resolvable')
merge_request.merge_request_diff.update_attributes(start_commit_sha: nil)
Loading
Loading
@@ -76,5 +68,23 @@ describe MergeRequests::Conflicts::ListService do
 
expect(conflicts_service(merge_request).can_be_resolved_in_ui?).to be_truthy
end
it 'returns a falsey value when the MR has a missing ref after a force push' do
merge_request = create_merge_request('conflict-resolvable')
service = conflicts_service(merge_request)
allow_any_instance_of(Gitlab::GitalyClient::ConflictsService).to receive(:list_conflict_files).and_raise(GRPC::Unknown)
expect(service.can_be_resolved_in_ui?).to be_falsey
end
context 'with gitaly disabled', :skip_gitaly_mock do
it 'returns a falsey value when the MR has a missing ref after a force push' do
merge_request = create_merge_request('conflict-resolvable')
service = conflicts_service(merge_request)
allow_any_instance_of(Rugged::Repository).to receive(:merge_commits).and_raise(Rugged::OdbError)
expect(service.can_be_resolved_in_ui?).to be_falsey
end
end
end
end
Loading
Loading
@@ -111,15 +111,6 @@ describe MergeRequests::Conflicts::ResolveService do
described_class.new(merge_request_from_fork).execute(user, params)
end
 
it 'gets conflicts from the source project' do
# REFACTOR NOTE: We used to test that `project.repository.rugged` wasn't
# used in this case, but since the refactor, for simplification,
# we always use that repository for read only operations.
expect(forked_project.repository.rugged).to receive(:merge_commits).and_call_original
subject
end
it 'creates a commit with the message' do
subject
 
Loading
Loading
@@ -132,6 +123,17 @@ describe MergeRequests::Conflicts::ResolveService do
expect(merge_request_from_fork.source_branch_head.parents.map(&:id))
.to eq(['404fa3fc7c2c9b5dacff102f353bdf55b1be2813', target_head])
end
context 'when gitaly is disabled', :skip_gitaly_mock do
it 'gets conflicts from the source project' do
# REFACTOR NOTE: We used to test that `project.repository.rugged` wasn't
# used in this case, but since the refactor, for simplification,
# we always use that repository for read only operations.
expect(forked_project.repository.rugged).to receive(:merge_commits).and_call_original
subject
end
end
end
end
 
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment