Commit 5ec1ad8b2375bdce7a820df1be3dc67b18ad2bd0
Exists in
master
and in
4 other branches
Merge pull request #1743 from riyad/fix-merging-bugs
Fix merging bugs
Showing
2 changed files
with
74 additions
and
34 deletions
Show diff stats
app/models/merge_request.rb
| @@ -135,7 +135,8 @@ class MergeRequest < ActiveRecord::Base | @@ -135,7 +135,8 @@ class MergeRequest < ActiveRecord::Base | ||
| 135 | end | 135 | end |
| 136 | 136 | ||
| 137 | def mark_as_unmergable | 137 | def mark_as_unmergable |
| 138 | - self.update_attributes state: CANNOT_BE_MERGED | 138 | + self.state = CANNOT_BE_MERGED |
| 139 | + self.save | ||
| 139 | end | 140 | end |
| 140 | 141 | ||
| 141 | def reloaded_commits | 142 | def reloaded_commits |
| @@ -166,7 +167,7 @@ class MergeRequest < ActiveRecord::Base | @@ -166,7 +167,7 @@ class MergeRequest < ActiveRecord::Base | ||
| 166 | end | 167 | end |
| 167 | 168 | ||
| 168 | def automerge!(current_user) | 169 | def automerge!(current_user) |
| 169 | - if Gitlab::Merge.new(self, current_user).merge && self.unmerged_commits.empty? | 170 | + if Gitlab::Merge.new(self, current_user).merge! && self.unmerged_commits.empty? |
| 170 | self.merge!(current_user.id) | 171 | self.merge!(current_user.id) |
| 171 | true | 172 | true |
| 172 | end | 173 | end |
lib/gitlab/merge.rb
| 1 | module Gitlab | 1 | module Gitlab |
| 2 | class Merge | 2 | class Merge |
| 3 | - attr_accessor :project, :merge_request, :user | 3 | + attr_accessor :merge_request, :project, :user |
| 4 | 4 | ||
| 5 | def initialize(merge_request, user) | 5 | def initialize(merge_request, user) |
| 6 | - self.user = user | ||
| 7 | - self.merge_request = merge_request | ||
| 8 | - self.project = merge_request.project | 6 | + @merge_request = merge_request |
| 7 | + @project = merge_request.project | ||
| 8 | + @user = user | ||
| 9 | end | 9 | end |
| 10 | 10 | ||
| 11 | def can_be_merged? | 11 | def can_be_merged? |
| 12 | - result = false | ||
| 13 | - process do |repo, output| | ||
| 14 | - result = !(output =~ /CONFLICT/) | 12 | + in_locked_and_timed_satellite do |merge_repo| |
| 13 | + merge_in_satellite!(merge_repo) | ||
| 15 | end | 14 | end |
| 16 | - result | ||
| 17 | end | 15 | end |
| 18 | 16 | ||
| 19 | - def merge | ||
| 20 | - process do |repo, output| | ||
| 21 | - if output =~ /CONFLICT/ | ||
| 22 | - false | ||
| 23 | - else | ||
| 24 | - !!repo.git.push({}, "origin", merge_request.target_branch) | 17 | + # Merges the source branch into the target branch in the satellite and |
| 18 | + # pushes it back to Gitolite. | ||
| 19 | + # It also removes the source branch if requested in the merge request. | ||
| 20 | + # | ||
| 21 | + # Returns false if the merge produced conflicts | ||
| 22 | + # Returns false if pushing from the satallite to Gitolite failed or was rejected | ||
| 23 | + # Returns true otherwise | ||
| 24 | + def merge! | ||
| 25 | + in_locked_and_timed_satellite do |merge_repo| | ||
| 26 | + if merge_in_satellite!(merge_repo) | ||
| 27 | + # push merge back to Gitolite | ||
| 28 | + # will raise CommandFailed when push fails | ||
| 29 | + merge_repo.git.push({raise: true}, :origin, merge_request.target_branch) | ||
| 30 | + | ||
| 31 | + # remove source branch | ||
| 32 | + if merge_request.should_remove_source_branch && !project.root_ref?(merge_request.source_branch) | ||
| 33 | + # will raise CommandFailed when push fails | ||
| 34 | + merge_repo.git.push({raise: true}, :origin, ":#{merge_request.source_branch}") | ||
| 35 | + end | ||
| 36 | + | ||
| 37 | + # merge, push and branch removal successful | ||
| 38 | + true | ||
| 25 | end | 39 | end |
| 26 | end | 40 | end |
| 41 | + rescue Grit::Git::CommandFailed | ||
| 42 | + false | ||
| 27 | end | 43 | end |
| 28 | 44 | ||
| 29 | - def process | 45 | + private |
| 46 | + | ||
| 47 | + # * Sets a 30s timeout for Git | ||
| 48 | + # * Locks the satellite repo | ||
| 49 | + # * Yields the prepared satallite repo | ||
| 50 | + def in_locked_and_timed_satellite | ||
| 30 | Grit::Git.with_timeout(30.seconds) do | 51 | Grit::Git.with_timeout(30.seconds) do |
| 31 | lock_file = Rails.root.join("tmp", "#{project.path}.lock") | 52 | lock_file = Rails.root.join("tmp", "#{project.path}.lock") |
| 32 | 53 | ||
| @@ -37,29 +58,47 @@ module Gitlab | @@ -37,29 +58,47 @@ module Gitlab | ||
| 37 | raise "Satellite doesn't exist" | 58 | raise "Satellite doesn't exist" |
| 38 | end | 59 | end |
| 39 | 60 | ||
| 40 | - project.satellite.clear | ||
| 41 | - | ||
| 42 | Dir.chdir(project.satellite.path) do | 61 | Dir.chdir(project.satellite.path) do |
| 43 | - merge_repo = Grit::Repo.new('.') | ||
| 44 | - merge_repo.git.sh "git reset --hard" | ||
| 45 | - merge_repo.git.sh "git fetch origin" | ||
| 46 | - merge_repo.git.sh "git config user.name \"#{user.name}\"" | ||
| 47 | - merge_repo.git.sh "git config user.email \"#{user.email}\"" | ||
| 48 | - merge_repo.git.sh "git checkout -b #{merge_request.target_branch} origin/#{merge_request.target_branch}" | ||
| 49 | - output = merge_repo.git.pull({}, "--no-ff", "origin", merge_request.source_branch) | ||
| 50 | - | ||
| 51 | - #remove source-branch | ||
| 52 | - if merge_request.should_remove_source_branch && !project.root_ref?(merge_request.source_branch) | ||
| 53 | - merge_repo.git.sh "git push origin :#{merge_request.source_branch}" | ||
| 54 | - end | ||
| 55 | - | ||
| 56 | - yield(merge_repo, output) | 62 | + repo = Grit::Repo.new('.') |
| 63 | + | ||
| 64 | + return yield repo | ||
| 57 | end | 65 | end |
| 58 | end | 66 | end |
| 59 | end | 67 | end |
| 60 | - | ||
| 61 | rescue Grit::Git::GitTimeout | 68 | rescue Grit::Git::GitTimeout |
| 62 | return false | 69 | return false |
| 63 | end | 70 | end |
| 71 | + | ||
| 72 | + # Merges the source_branch into the target_branch in the satellite. | ||
| 73 | + # | ||
| 74 | + # Note: it will clear out the satellite before doing anything | ||
| 75 | + # | ||
| 76 | + # Returns false if the merge produced conflicts | ||
| 77 | + # Returns true otherwise | ||
| 78 | + def merge_in_satellite!(repo) | ||
| 79 | + prepare_satelite!(repo) | ||
| 80 | + | ||
| 81 | + # create target branch in satellite at the corresponding commit from Gitolite | ||
| 82 | + repo.git.checkout({b: true}, merge_request.target_branch, "origin/#{merge_request.target_branch}") | ||
| 83 | + | ||
| 84 | + # merge the source branch from Gitolite into the satellite | ||
| 85 | + # will raise CommandFailed when merge fails | ||
| 86 | + repo.git.pull({no_ff: true, raise: true}, :origin, merge_request.source_branch) | ||
| 87 | + rescue Grit::Git::CommandFailed | ||
| 88 | + false | ||
| 89 | + end | ||
| 90 | + | ||
| 91 | + # * Clears the satellite | ||
| 92 | + # * Updates the satellite from Gitolite | ||
| 93 | + # * Sets up Git variables for the user | ||
| 94 | + def prepare_satelite!(repo) | ||
| 95 | + project.satellite.clear | ||
| 96 | + | ||
| 97 | + repo.git.reset(hard: true) | ||
| 98 | + repo.git.fetch({}, :origin) | ||
| 99 | + | ||
| 100 | + repo.git.config({}, "user.name", user.name) | ||
| 101 | + repo.git.config({}, "user.email", user.email) | ||
| 102 | + end | ||
| 64 | end | 103 | end |
| 65 | end | 104 | end |