Commit e60bf2f2 authored by Alejandro Rodríguez's avatar Alejandro Rodríguez

Incorporate OperationService.UserSquash Gitaly RPC

parent a00aed74
Pipeline #17016460 failed with stages
in 32 minutes and 47 seconds
......@@ -1222,33 +1222,13 @@ module Gitlab
end
def squash(user, squash_id, branch:, start_sha:, end_sha:, author:, message:)
squash_path = worktree_path(SQUASH_WORKTREE_PREFIX, squash_id)
env = git_env_for_user(user).merge(
'GIT_AUTHOR_NAME' => author.name,
'GIT_AUTHOR_EMAIL' => author.email
)
diff_range = "#{start_sha}...#{end_sha}"
diff_files = run_git!(
%W(diff --name-only --diff-filter=a --binary #{diff_range})
).chomp
with_worktree(squash_path, branch, sparse_checkout_files: diff_files, env: env) do
# Apply diff of the `diff_range` to the worktree
diff = run_git!(%W(diff --binary #{diff_range}))
run_git!(%w(apply --index), chdir: squash_path, env: env) do |stdin|
stdin.write(diff)
gitaly_migrate(:squash) do |is_enabled|
if is_enabled
gitaly_operation_client.user_squash(user, squash_id, branch,
start_sha, end_sha, author, message)
else
git_squash(user, squash_id, branch, start_sha, end_sha, author, message)
end
# Commit the `diff_range` diff
run_git!(%W(commit --no-verify --message #{message}), chdir: squash_path, env: env)
# Return the squash sha. May print a warning for ambiguous refs, but
# we can ignore that with `--quiet` and just take the SHA, if present.
# HEAD here always refers to the current HEAD commit, even if there is
# another ref called HEAD.
run_git!(
%w(rev-parse --quiet --verify HEAD), chdir: squash_path, env: env
).chomp
end
end
......@@ -2164,6 +2144,37 @@ module Gitlab
end
end
def git_squash(user, squash_id, branch, start_sha, end_sha, author, message)
squash_path = worktree_path(SQUASH_WORKTREE_PREFIX, squash_id)
env = git_env_for_user(user).merge(
'GIT_AUTHOR_NAME' => author.name,
'GIT_AUTHOR_EMAIL' => author.email
)
diff_range = "#{start_sha}...#{end_sha}"
diff_files = run_git!(
%W(diff --name-only --diff-filter=a --binary #{diff_range})
).chomp
with_worktree(squash_path, branch, sparse_checkout_files: diff_files, env: env) do
# Apply diff of the `diff_range` to the worktree
diff = run_git!(%W(diff --binary #{diff_range}))
run_git!(%w(apply --index), chdir: squash_path, env: env) do |stdin|
stdin.write(diff)
end
# Commit the `diff_range` diff
run_git!(%W(commit --no-verify --message #{message}), chdir: squash_path, env: env)
# Return the squash sha. May print a warning for ambiguous refs, but
# we can ignore that with `--quiet` and just take the SHA, if present.
# HEAD here always refers to the current HEAD commit, even if there is
# another ref called HEAD.
run_git!(
%w(rev-parse --quiet --verify HEAD), chdir: squash_path, env: env
).chomp
end
end
def local_fetch_ref(source_path, source_ref:, target_ref:)
args = %W(fetch --no-tags -f #{source_path} #{source_ref}:#{target_ref})
run_git(args)
......
......@@ -183,6 +183,32 @@ module Gitlab
end
end
def user_squash(user, squash_id, branch, start_sha, end_sha, author, message)
request = Gitaly::UserSquashRequest.new(
repository: @gitaly_repo,
user: Gitlab::Git::User.from_gitlab(user).to_gitaly,
squash_id: squash_id.to_s,
branch: encode_binary(branch),
start_sha: start_sha,
end_sha: end_sha,
author: Gitlab::Git::User.from_gitlab(author).to_gitaly,
commit_message: encode_binary(message)
)
response = GitalyClient.call(
@repository.storage,
:operation_service,
:user_squash,
request
)
if response.git_error.presence
raise Gitlab::Git::Repository::GitError, response.git_error
end
response.squash_sha
end
def user_commit_files(
user, branch_name, commit_message, actions, author_email, author_name,
start_branch_name, start_repository)
......
......@@ -2183,7 +2183,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
repository.squash(user, squash_id, opts)
end
context 'sparse checkout' do
context 'sparse checkout', :skip_gitaly_mock do
let(:expected_files) { %w(files files/js files/js/application.js) }
before do
......
......@@ -123,4 +123,53 @@ describe Gitlab::GitalyClient::OperationService do
expect(subject.branch_created).to be(false)
end
end
describe '#user_squash' do
let(:branch_name) { 'my-branch' }
let(:squash_id) { '1' }
let(:start_sha) { 'b83d6e391c22777fca1ed3012fce84f633d7fed0' }
let(:end_sha) { '54cec5282aa9f21856362fe321c800c236a61615' }
let(:commit_message) { 'Squash message' }
let(:request) do
Gitaly::UserSquashRequest.new(
repository: repository.gitaly_repository,
user: gitaly_user,
squash_id: squash_id.to_s,
branch: branch_name,
start_sha: start_sha,
end_sha: end_sha,
author: gitaly_user,
commit_message: commit_message
)
end
let(:squash_sha) { 'f00' }
let(:response) { Gitaly::UserSquashResponse.new(squash_sha: squash_sha) }
subject do
client.user_squash(user, squash_id, branch_name, start_sha, end_sha, user, commit_message)
end
it 'sends a user_squash message and returns the squash sha' do
expect_any_instance_of(Gitaly::OperationService::Stub)
.to receive(:user_squash).with(request, kind_of(Hash))
.and_return(response)
expect(subject).to eq(squash_sha)
end
context "when git_error is present" do
let(:response) do
Gitaly::UserSquashResponse.new(git_error: "something failed")
end
it "throws a PreReceive exception" do
expect_any_instance_of(Gitaly::OperationService::Stub)
.to receive(:user_squash).with(request, kind_of(Hash))
.and_return(response)
expect { subject }.to raise_error(
Gitlab::Git::Repository::GitError, "something failed")
end
end
end
end
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