2012-10-25 18:19:01 -04:00
|
|
|
module Gitlab
|
|
|
|
module Satellite
|
2012-10-25 20:50:24 -04:00
|
|
|
# GitLab server-side merge
|
2012-10-25 18:19:01 -04:00
|
|
|
class MergeAction < Action
|
2012-10-25 18:24:02 -04:00
|
|
|
attr_accessor :merge_request
|
2012-10-25 18:19:01 -04:00
|
|
|
|
2012-10-25 18:26:47 -04:00
|
|
|
def initialize(user, merge_request)
|
2013-04-25 10:15:33 -04:00
|
|
|
super user, merge_request.target_project
|
2012-10-25 18:19:01 -04:00
|
|
|
@merge_request = merge_request
|
|
|
|
end
|
|
|
|
|
2012-10-25 20:50:24 -04:00
|
|
|
# Checks if a merge request can be executed without user interaction
|
2012-10-25 18:19:01 -04:00
|
|
|
def can_be_merged?
|
|
|
|
in_locked_and_timed_satellite do |merge_repo|
|
2013-04-25 10:15:33 -04:00
|
|
|
prepare_satellite!(merge_repo)
|
2012-10-25 18:19:01 -04:00
|
|
|
merge_in_satellite!(merge_repo)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# Merges the source branch into the target branch in the satellite and
|
2013-04-25 10:15:33 -04:00
|
|
|
# pushes it back to the repository.
|
|
|
|
# It also removes the source branch if requested in the merge request (and this is permitted by the merge request).
|
2012-10-25 18:19:01 -04:00
|
|
|
#
|
|
|
|
# Returns false if the merge produced conflicts
|
2013-04-25 10:15:33 -04:00
|
|
|
# Returns false if pushing from the satellite to the repository failed or was rejected
|
2012-10-25 18:19:01 -04:00
|
|
|
# Returns true otherwise
|
2014-01-13 09:49:35 -05:00
|
|
|
def merge!(merge_commit_message = nil)
|
2012-10-25 18:19:01 -04:00
|
|
|
in_locked_and_timed_satellite do |merge_repo|
|
2013-04-25 10:15:33 -04:00
|
|
|
prepare_satellite!(merge_repo)
|
2014-01-13 09:49:35 -05:00
|
|
|
if merge_in_satellite!(merge_repo, merge_commit_message)
|
2013-11-19 08:41:11 -05:00
|
|
|
# push merge back to bare repo
|
2012-10-25 18:19:01 -04:00
|
|
|
# will raise CommandFailed when push fails
|
2013-04-25 10:15:33 -04:00
|
|
|
merge_repo.git.push(default_options, :origin, merge_request.target_branch)
|
2012-10-25 18:19:01 -04:00
|
|
|
# remove source branch
|
2013-01-17 05:07:01 -05:00
|
|
|
if merge_request.should_remove_source_branch && !project.root_ref?(merge_request.source_branch)
|
2012-10-25 18:19:01 -04:00
|
|
|
# will raise CommandFailed when push fails
|
2013-04-25 10:15:33 -04:00
|
|
|
merge_repo.git.push(default_options, :origin, ":#{merge_request.source_branch}")
|
2012-10-25 18:19:01 -04:00
|
|
|
end
|
|
|
|
# merge, push and branch removal successful
|
|
|
|
true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
rescue Grit::Git::CommandFailed => ex
|
2013-06-06 17:22:36 -04:00
|
|
|
handle_exception(ex)
|
2012-10-25 18:19:01 -04:00
|
|
|
end
|
|
|
|
|
2013-04-25 10:15:33 -04:00
|
|
|
# Get a raw diff of the source to the target
|
|
|
|
def diff_in_satellite
|
|
|
|
in_locked_and_timed_satellite do |merge_repo|
|
|
|
|
prepare_satellite!(merge_repo)
|
|
|
|
update_satellite_source_and_target!(merge_repo)
|
2014-01-13 09:16:45 -05:00
|
|
|
diff = merge_repo.git.native(:diff, default_options, "origin/#{merge_request.target_branch}", "source/#{merge_request.source_branch}")
|
2013-04-25 10:15:33 -04:00
|
|
|
end
|
|
|
|
rescue Grit::Git::CommandFailed => ex
|
2013-06-06 17:22:36 -04:00
|
|
|
handle_exception(ex)
|
2013-04-25 10:15:33 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
# Only show what is new in the source branch compared to the target branch, not the other way around.
|
|
|
|
# The line below with merge_base is equivalent to diff with three dots (git diff branch1...branch2)
|
|
|
|
# From the git documentation: "git diff A...B" is equivalent to "git diff $(git-merge-base A B) B"
|
|
|
|
def diffs_between_satellite
|
|
|
|
in_locked_and_timed_satellite do |merge_repo|
|
|
|
|
prepare_satellite!(merge_repo)
|
|
|
|
update_satellite_source_and_target!(merge_repo)
|
|
|
|
if merge_request.for_fork?
|
|
|
|
common_commit = merge_repo.git.native(:merge_base, default_options, ["origin/#{merge_request.target_branch}", "source/#{merge_request.source_branch}"]).strip
|
2013-06-03 16:20:50 -04:00
|
|
|
#this method doesn't take default options
|
|
|
|
diffs = merge_repo.diff(common_commit, "source/#{merge_request.source_branch}")
|
2013-04-25 10:15:33 -04:00
|
|
|
else
|
2013-07-17 16:28:07 -04:00
|
|
|
raise "Attempt to determine diffs between for a non forked merge request in satellite MergeRequest.id:[#{merge_request.id}]"
|
2013-04-25 10:15:33 -04:00
|
|
|
end
|
2013-06-06 17:22:36 -04:00
|
|
|
diffs = diffs.map { |diff| Gitlab::Git::Diff.new(diff) }
|
2013-04-25 10:15:33 -04:00
|
|
|
return diffs
|
|
|
|
end
|
|
|
|
rescue Grit::Git::CommandFailed => ex
|
2013-06-06 17:22:36 -04:00
|
|
|
handle_exception(ex)
|
2013-04-25 10:15:33 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
# Get commit as an email patch
|
|
|
|
def format_patch
|
|
|
|
in_locked_and_timed_satellite do |merge_repo|
|
|
|
|
prepare_satellite!(merge_repo)
|
|
|
|
update_satellite_source_and_target!(merge_repo)
|
2014-01-13 09:16:45 -05:00
|
|
|
patch = merge_repo.git.format_patch(default_options({stdout: true}), "origin/#{merge_request.target_branch}..source/#{merge_request.source_branch}")
|
2013-04-25 10:15:33 -04:00
|
|
|
end
|
|
|
|
rescue Grit::Git::CommandFailed => ex
|
2013-06-06 17:22:36 -04:00
|
|
|
handle_exception(ex)
|
2013-04-25 10:15:33 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
# Retrieve an array of commits between the source and the target
|
|
|
|
def commits_between
|
|
|
|
in_locked_and_timed_satellite do |merge_repo|
|
|
|
|
prepare_satellite!(merge_repo)
|
|
|
|
update_satellite_source_and_target!(merge_repo)
|
|
|
|
if (merge_request.for_fork?)
|
|
|
|
commits = merge_repo.commits_between("origin/#{merge_request.target_branch}", "source/#{merge_request.source_branch}")
|
|
|
|
else
|
2013-07-17 16:28:07 -04:00
|
|
|
raise "Attempt to determine commits between for a non forked merge request in satellite MergeRequest.id:[#{merge_request.id}]"
|
2013-04-25 10:15:33 -04:00
|
|
|
end
|
2013-06-06 17:22:36 -04:00
|
|
|
commits = commits.map { |commit| Gitlab::Git::Commit.new(commit, nil) }
|
2013-04-25 10:15:33 -04:00
|
|
|
return commits
|
|
|
|
end
|
|
|
|
rescue Grit::Git::CommandFailed => ex
|
2013-06-06 17:22:36 -04:00
|
|
|
handle_exception(ex)
|
2013-04-25 10:15:33 -04:00
|
|
|
end
|
|
|
|
|
2012-10-25 18:19:01 -04:00
|
|
|
private
|
|
|
|
# Merges the source_branch into the target_branch in the satellite.
|
|
|
|
#
|
|
|
|
# Note: it will clear out the satellite before doing anything
|
|
|
|
#
|
|
|
|
# Returns false if the merge produced conflicts
|
|
|
|
# Returns true otherwise
|
2014-01-13 06:17:38 -05:00
|
|
|
def merge_in_satellite!(repo, message = nil)
|
2013-04-25 10:15:33 -04:00
|
|
|
update_satellite_source_and_target!(repo)
|
2012-10-25 18:19:01 -04:00
|
|
|
|
2014-01-13 09:49:35 -05:00
|
|
|
message ||= "Merge branch '#{merge_request.source_branch}' into '#{merge_request.target_branch}'"
|
2014-01-13 06:17:38 -05:00
|
|
|
|
2013-04-25 10:15:33 -04:00
|
|
|
# merge the source branch into the satellite
|
2012-10-25 18:19:01 -04:00
|
|
|
# will raise CommandFailed when merge fails
|
2014-02-24 20:53:57 -05:00
|
|
|
repo.git.merge(default_options({no_ff: true}), "-m#{message}", "source/#{merge_request.source_branch}")
|
2013-04-25 10:15:33 -04:00
|
|
|
rescue Grit::Git::CommandFailed => ex
|
2013-06-06 17:22:36 -04:00
|
|
|
handle_exception(ex)
|
2013-04-25 10:15:33 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
# Assumes a satellite exists that is a fresh clone of the projects repo, prepares satellite for merges, diffs etc
|
|
|
|
def update_satellite_source_and_target!(repo)
|
2014-01-13 06:17:38 -05:00
|
|
|
repo.remote_add('source', merge_request.source_project.repository.path_to_repo)
|
|
|
|
repo.remote_fetch('source')
|
|
|
|
repo.git.checkout(default_options({b: true}), merge_request.target_branch, "origin/#{merge_request.target_branch}")
|
2012-10-25 18:19:01 -04:00
|
|
|
rescue Grit::Git::CommandFailed => ex
|
2013-06-06 17:22:36 -04:00
|
|
|
handle_exception(ex)
|
2012-10-25 18:19:01 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|