Commit ab9380d7 authored by Sean McGivern's avatar Sean McGivern

Move common working copy operations to base class

parent c88f77c8
module MergeRequests module MergeRequests
# MergeService class class RebaseService < MergeRequests::WorkingCopyBaseService
#
# Do git merge and in case of success
# mark merge request as merged and execute all hooks and notifications
# Executed when you do merge via GitLab UI
#
class RebaseService < MergeRequests::BaseService
include Gitlab::Popen
attr_reader :merge_request
def execute(merge_request) def execute(merge_request)
@merge_request = merge_request @merge_request = merge_request
...@@ -22,92 +12,53 @@ module MergeRequests ...@@ -22,92 +12,53 @@ module MergeRequests
def rebase def rebase
if merge_request.rebase_in_progress? if merge_request.rebase_in_progress?
log('Rebase task canceled: Another rebase is already in progress') log_error('Rebase task canceled: Another rebase is already in progress')
return false return false
end end
# Clone run_git_command(
output, status = popen( %W(clone -b #{merge_request.source_branch} -- #{source_project.repository.path_to_repo} #{tree_path}),
%W(git clone -b #{merge_request.source_branch} -- #{source_project.repository.path_to_repo} #{tree_path}),
nil, nil,
git_env git_env,
'clone repository for rebase'
) )
unless status.zero? run_git_command(
log('Failed to clone repository for rebase:') %W(pull --rebase #{target_project.repository.path_to_repo} #{merge_request.target_branch}),
log(output)
return false
end
# Rebase
output, status = popen(
%W(git pull --rebase #{target_project.repository.path_to_repo} #{merge_request.target_branch}),
tree_path, tree_path,
git_env git_env,
'rebase branch'
) )
unless status.zero? rebase_sha = run_git_command(
log('Failed to rebase branch:') %W(rev-parse #{merge_request.source_branch}),
log(output)
return false
end
output, status = popen(
%W(git rev-parse #{merge_request.source_branch}),
tree_path, tree_path,
git_env git_env,
'get SHA of rebased branch'
) )
unless status.zero? merge_request.update_attributes(rebase_commit_sha: rebase_sha)
log('Failed to get SHA of rebased branch:')
log(output)
return false
end
merge_request.update_attributes(rebase_commit_sha: output.chomp) run_git_command(
# Push %W(push -f origin #{merge_request.source_branch}),
output, status = popen(
%W(git push -f origin #{merge_request.source_branch}),
tree_path, tree_path,
git_env git_env,
'push rebased branch'
) )
unless status.zero?
log('Failed to push rebased branch:')
log(output)
return false
end
true true
rescue => ex rescue GitCommandError
log('Failed to rebase branch:') false
log(ex.message) rescue => e
log_error('Failed to rebase branch:')
log_error(e)
false
ensure ensure
clean_dir clean_dir
end end
def source_project
@source_project ||= merge_request.source_project
end
def target_project
@target_project ||= merge_request.target_project
end
def tree_path def tree_path
@tree_path ||= merge_request.rebase_dir_path @tree_path ||= merge_request.rebase_dir_path
end end
def log(message)
Gitlab::GitLogger.error(message)
end
def clean_dir
FileUtils.rm_rf(tree_path) if File.exist?(tree_path)
end
def git_env
{ 'GL_ID' => Gitlab::GlId.gl_id(current_user), 'GL_PROTOCOL' => 'web' }
end
end end
end end
require 'securerandom' require 'securerandom'
module MergeRequests module MergeRequests
class SquashService < MergeRequests::BaseService class SquashService < MergeRequests::WorkingCopyBaseService
include Gitlab::Popen attr_reader :repository, :rugged
attr_reader :merge_request, :repository, :rugged
def execute(merge_request) def execute(merge_request)
@merge_request = merge_request @merge_request = merge_request
@repository = merge_request.target_project.repository @repository = target_project.repository
@rugged = repository.rugged @rugged = repository.rugged
squash || error('Failed to squash. Should be done manually') squash || error('Failed to squash. Should be done manually')
...@@ -25,96 +23,59 @@ module MergeRequests ...@@ -25,96 +23,59 @@ module MergeRequests
temp_branch = SecureRandom.uuid temp_branch = SecureRandom.uuid
if merge_request.squash_in_progress? if merge_request.squash_in_progress?
log('Squash task canceled: Another squash is already in progress') log_error('Squash task canceled: Another squash is already in progress')
return false return false
end end
# Clone run_git_command(
output, status = popen( %W(clone -b #{merge_request.target_branch} -- #{repository.path_to_repo} #{tree_path}),
%W(git clone -b #{merge_request.target_branch} -- #{repository.path_to_repo} #{tree_path}),
nil, nil,
git_env git_env,
'clone repository for squash'
) )
unless status.zero? run_git_command(%w(apply --cached), tree_path, git_env, 'apply patch') do |stdin|
log('Failed to clone repository for squash:')
log(output)
return false
end
# Squash
output, status = popen(%w(git apply --cached), tree_path, git_env) do |stdin|
stdin.puts(merge_request_to_patch) stdin.puts(merge_request_to_patch)
end end
unless status.zero? run_git_command(
log('Failed to apply patch:') %W(commit -C #{merge_request.diff_head_sha}),
log(output)
return false
end
output, status = popen(
%W(git commit -C #{merge_request.diff_head_sha}),
tree_path, tree_path,
git_env.merge('GIT_COMMITTER_NAME' => current_user.name, 'GIT_COMMITTER_EMAIL' => current_user.email) git_env.merge('GIT_COMMITTER_NAME' => current_user.name, 'GIT_COMMITTER_EMAIL' => current_user.email),
'commit squashed changes'
) )
unless status.zero? squash_sha = run_git_command(
log('Failed to commit squashed changes:') %w(rev-parse HEAD),
log(output) tree_path,
return false git_env,
end "get SHA of squashed branch #{temp_branch}"
)
output, status = popen(%w(git rev-parse HEAD), tree_path, git_env)
unless status.zero?
log("Failed to get SHA of squashed branch #{temp_branch}:")
log(output)
return false
end
target = output.chomp
# Push to temporary ref
output, status = popen(%W(git push -f origin HEAD:#{temp_branch}), tree_path, git_env)
unless status.zero? run_git_command(
log('Failed to push squashed branch:') %W(push -f origin HEAD:#{temp_branch}),
log(output) tree_path,
return false git_env,
end 'push squashed branch'
)
repository.rm_branch(current_user, temp_branch, skip_event: true) repository.rm_branch(current_user, temp_branch)
success(squash_oid: target) success(squash_sha: squash_sha)
rescue => ex rescue GitCommandError
log("Failed to squash merge request #{project.path_with_namespace}#{merge_request.to_reference}:") false
log(ex.message) rescue => e
log_error("Failed to squash merge request #{merge_request.to_reference(full: true)}:")
log_error(e.message)
false false
ensure ensure
clean_dir clean_dir
end end
def inspect
''
end
def tree_path def tree_path
@tree_path ||= merge_request.squash_dir_path @tree_path ||= merge_request.squash_dir_path
end end
def log(message)
Gitlab::GitLogger.error(message)
end
def clean_dir
FileUtils.rm_rf(tree_path) if File.exist?(tree_path)
end
def git_env
{ 'GL_ID' => Gitlab::GlId.gl_id(current_user), 'GL_PROTOCOL' => 'web' }
end
def merge_request_to_patch def merge_request_to_patch
@merge_request_to_patch ||= rugged.diff(merge_request.diff_base_sha, merge_request.diff_head_sha).patch @merge_request_to_patch ||= rugged.diff(merge_request.diff_base_sha, merge_request.diff_head_sha).patch
end end
......
module MergeRequests
class WorkingCopyBaseService < MergeRequests::BaseService
class GitCommandError < StandardError; end
include Gitlab::Popen
attr_reader :merge_request
def run_git_command(command, path, env, message = nil, &block)
git_command = [Gitlab.config.git.bin_path] + command
output, status = popen(git_command, path, env, &block)
unless status.zero?
log_error("Failed to #{message}:") if message
log_error(output)
raise GitCommandError
end
output.chomp
end
def source_project
@source_project ||= merge_request.source_project
end
def target_project
@target_project ||= merge_request.target_project
end
def log_error(message)
Gitlab::GitLogger.error(message)
end
def clean_dir
FileUtils.rm_rf(tree_path) if File.exist?(tree_path)
end
def git_env
{ 'GL_ID' => Gitlab::GlId.gl_id(current_user), 'GL_PROTOCOL' => 'web' }
end
# Don't try to print expensive instance variables.
def inspect
"#<#{self.class} #{merge_request.to_reference(full: true)}>"
end
end
end
...@@ -14,7 +14,7 @@ describe MergeRequests::SquashService do ...@@ -14,7 +14,7 @@ describe MergeRequests::SquashService do
context 'when the squash succeeds' do context 'when the squash succeeds' do
it 'returns the squashed commit SHA' do it 'returns the squashed commit SHA' do
expect(service.execute(merge_request)).to match(status: :success, expect(service.execute(merge_request)).to match(status: :success,
squash_oid: a_string_matching(/\h{40}/)) squash_sha: a_string_matching(/\h{40}/))
end end
it 'cleans up the temporary directory' do it 'cleans up the temporary directory' do
...@@ -25,7 +25,7 @@ describe MergeRequests::SquashService do ...@@ -25,7 +25,7 @@ describe MergeRequests::SquashService do
context 'the squashed commit' do context 'the squashed commit' do
let(:squashed_commit) do let(:squashed_commit) do
squash_oid = service.execute(merge_request)[:squash_oid] squash_oid = service.execute(merge_request)[:squash_sha]
project.repository.commit(squash_oid) project.repository.commit(squash_oid)
end end
...@@ -46,11 +46,11 @@ describe MergeRequests::SquashService do ...@@ -46,11 +46,11 @@ describe MergeRequests::SquashService do
end end
stages = { stages = {
'clone repository' => ['git', 'clone'], 'clone repository' => 'clone',
'apply patch' => ['git', 'apply'], 'apply patch' => 'apply',
'commit squashed changes' => ['git', 'commit'], 'commit squashed changes' => 'commit',
'get SHA of squashed branch' => ['git', 'rev-parse'], 'get SHA of squashed branch' => 'rev-parse',
'push squashed branch' => ['git', 'push'] 'push squashed branch' => 'push'
} }
stages.each do |stage, command| stages.each do |stage, command|
...@@ -58,16 +58,18 @@ describe MergeRequests::SquashService do ...@@ -58,16 +58,18 @@ describe MergeRequests::SquashService do
let(:error) { 'A test error' } let(:error) { 'A test error' }
before do before do
git_command = a_collection_starting_with([Gitlab.config.git.bin_path, command])
allow(service).to receive(:popen).and_return(['', 0]) allow(service).to receive(:popen).and_return(['', 0])
allow(service).to receive(:popen).with(a_collection_starting_with(command), anything, anything) do allow(service).to receive(:popen).with(git_command, anything, anything) do
[error, 1] [error, 1]
end end
end end
it 'logs the stage and output' do it 'logs the stage and output' do
expect(service).to receive(:log).with(a_string_including(stage)) expect(service).to receive(:log_error).with(a_string_including(stage))
expect(service).to receive(:log).with(error) expect(service).to receive(:log_error).with(error)
service.execute(merge_request) service.execute(merge_request)
end end
...@@ -93,8 +95,8 @@ describe MergeRequests::SquashService do ...@@ -93,8 +95,8 @@ describe MergeRequests::SquashService do
end end
it 'logs the MR reference and exception' do it 'logs the MR reference and exception' do
expect(service).to receive(:log).with(a_string_including("#{project.path_with_namespace}#{merge_request.to_reference}")) expect(service).to receive(:log_error).with(a_string_including("#{project.path_with_namespace}#{merge_request.to_reference}"))
expect(service).to receive(:log).with(error) expect(service).to receive(:log_error).with(error)
service.execute(merge_request) service.execute(merge_request)
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