summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/merge_requests.js18
-rw-r--r--app/controllers/merge_requests_controller.rb20
-rw-r--r--app/models/ability.rb1
-rw-r--r--app/models/merge_request.rb48
-rw-r--r--app/models/project/hooks_trait.rb2
-rw-r--r--app/views/merge_requests/automerge.js.haml7
-rw-r--r--app/views/merge_requests/show.html.haml37
-rw-r--r--config/routes.rb2
-rw-r--r--db/migrate/20120329170745_add_automerge_to_merge_request.rb5
-rw-r--r--db/schema.rb4
-rw-r--r--lib/gitlab_merge.rb48
-rw-r--r--lib/gitlabhq/gitolite.rb29
-rw-r--r--lib/tasks/gitlab_enable_automerge.rake9
13 files changed, 223 insertions, 7 deletions
diff --git a/app/assets/javascripts/merge_requests.js b/app/assets/javascripts/merge_requests.js
index e87071b5404..239ea85c070 100644
--- a/app/assets/javascripts/merge_requests.js
+++ b/app/assets/javascripts/merge_requests.js
@@ -1,9 +1,19 @@
var MergeRequest = {
diffs_loaded: false,
commits_loaded: false,
+ opts: false,
init:
- function() {
+ function(opts) {
+ this.opts = opts;
+
+ if($(".automerge_widget").length){
+ $.get(opts.url_to_automerge_check, function(data){
+ $(".automerge_widget").hide();
+ $(".automerge_widget." + data.state).show();
+ }, "json");
+ }
+
$(".nav-tabs a").live("click", function() {
$(".nav-tabs a").parent().removeClass("active");
$(this).parent().addClass("active");
@@ -44,5 +54,11 @@ var MergeRequest = {
function() {
$(".first_mr_commits").remove();
$(".all_mr_commits").removeClass("hide");
+ },
+
+ already_cannot_be_merged:
+ function(){
+ $(".automerge_widget").hide();
+ $(".automerge_widget.already_cannot_be_merged").show();
}
}
diff --git a/app/controllers/merge_requests_controller.rb b/app/controllers/merge_requests_controller.rb
index 500406c919c..7a803e2db65 100644
--- a/app/controllers/merge_requests_controller.rb
+++ b/app/controllers/merge_requests_controller.rb
@@ -2,7 +2,7 @@ class MergeRequestsController < ApplicationController
before_filter :authenticate_user!
before_filter :project
before_filter :module_enabled
- before_filter :merge_request, :only => [:edit, :update, :destroy, :show, :commits, :diffs]
+ before_filter :merge_request, :only => [:edit, :update, :destroy, :show, :commits, :diffs, :automerge, :automerge_check]
layout "project"
# Authorize
@@ -89,6 +89,7 @@ class MergeRequestsController < ApplicationController
respond_to do |format|
if @merge_request.update_attributes(params[:merge_request].merge(:author_id_of_changes => current_user.id))
@merge_request.reload_code
+ @merge_request.mark_as_unchecked
format.html { redirect_to [@project, @merge_request], notice: 'Merge request was successfully updated.' }
format.json { head :ok }
else
@@ -98,6 +99,23 @@ class MergeRequestsController < ApplicationController
end
end
+ def automerge_check
+ if @merge_request.unchecked?
+ @merge_request.check_if_can_be_merged
+ end
+ render :json => {:state => @merge_request.human_state}
+ end
+
+ def automerge
+ return access_denied! unless can?(current_user, :accept_mr, @project)
+ if @merge_request.open? && @merge_request.can_be_merged?
+ @merge_request.automerge!(current_user)
+ @status = true
+ else
+ @status = false
+ end
+ end
+
def destroy
@merge_request.destroy
diff --git a/app/models/ability.rb b/app/models/ability.rb
index 5792948fa51..d65695a2079 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -48,6 +48,7 @@ class Ability
:admin_team_member,
:admin_merge_request,
:admin_note,
+ :accept_mr,
:admin_wiki
] if project.master_access_for?(user) || project.owner == user
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index edf073d4e88..248bbdacad4 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -1,6 +1,10 @@
require File.join(Rails.root, "app/models/commit")
class MergeRequest < ActiveRecord::Base
+ UNCHECKED = 1
+ CAN_BE_MERGED = 2
+ CANNOT_BE_MERGED = 3
+
belongs_to :project
belongs_to :author, :class_name => "User"
belongs_to :assignee, :class_name => "User"
@@ -45,6 +49,15 @@ class MergeRequest < ActiveRecord::Base
where("source_branch like :branch or target_branch like :branch", :branch => branch_name)
end
+ def human_state
+ states = {
+ CAN_BE_MERGED => "can_be_merged",
+ CANNOT_BE_MERGED => "cannot_be_merged",
+ UNCHECKED => "unchecked"
+ }
+ states[self.state]
+ end
+
def validate_branches
if target_branch == source_branch
errors.add :base, "You can not use same branch for source and target branches"
@@ -56,6 +69,27 @@ class MergeRequest < ActiveRecord::Base
self.reloaded_diffs
end
+ def unchecked?
+ state == UNCHECKED
+ end
+
+ def mark_as_unchecked
+ self.update_attributes(:state => UNCHECKED)
+ end
+
+ def can_be_merged?
+ state == CAN_BE_MERGED
+ end
+
+ def check_if_can_be_merged
+ self.state = if GitlabMerge.new(self, self.author).can_be_merged?
+ CAN_BE_MERGED
+ else
+ CANNOT_BE_MERGED
+ end
+ self.save
+ end
+
def new?
today? && created_at == updated_at
end
@@ -118,6 +152,10 @@ class MergeRequest < ActiveRecord::Base
save
end
+ def mark_as_unmergable
+ self.update_attributes :state => CANNOT_BE_MERGED
+ end
+
def reloaded_commits
if open? && unmerged_commits.any?
self.st_commits = unmerged_commits
@@ -144,6 +182,16 @@ class MergeRequest < ActiveRecord::Base
:author_id => user_id
)
end
+
+ def automerge!(current_user)
+ if GitlabMerge.new(self, current_user).merge
+ self.merge!(current_user.id)
+ true
+ end
+ rescue
+ self.mark_as_unmergable
+ false
+ end
end
# == Schema Information
#
diff --git a/app/models/project/hooks_trait.rb b/app/models/project/hooks_trait.rb
index c3a4f6139a3..f642ac978f3 100644
--- a/app/models/project/hooks_trait.rb
+++ b/app/models/project/hooks_trait.rb
@@ -18,7 +18,7 @@ module Project::HooksTrait
# Update code for merge requests
mrs = self.merge_requests.opened.find_all_by_branch(branch_name).all
- mrs.each { |merge_request| merge_request.reload_code }
+ mrs.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked }
# Close merge requests
mrs = self.merge_requests.opened.where(:target_branch => branch_name).all
diff --git a/app/views/merge_requests/automerge.js.haml b/app/views/merge_requests/automerge.js.haml
new file mode 100644
index 00000000000..93e184455af
--- /dev/null
+++ b/app/views/merge_requests/automerge.js.haml
@@ -0,0 +1,7 @@
+-if @status
+ :plain
+ location.reload();
+-else
+ :plain
+ MergeRequest.already_cannot_be_merged()
+
diff --git a/app/views/merge_requests/show.html.haml b/app/views/merge_requests/show.html.haml
index 8402a66ed19..56345c394c8 100644
--- a/app/views/merge_requests/show.html.haml
+++ b/app/views/merge_requests/show.html.haml
@@ -8,7 +8,6 @@
%span.right
- if can?(current_user, :modify_merge_request, @merge_request)
- if @merge_request.open?
- = link_to "Show how to merge", "#", :class => "how_to_merge_link btn small padded", :title => "How To Merge"
= link_to 'Close', project_merge_request_path(@project, @merge_request, :merge_request => {:closed => true }, :status_only => true), :method => :put, :class => "btn small padded", :title => "Close merge request"
= link_to edit_project_merge_request_path(@project, @merge_request), :class => "btn small padded" do
Edit
@@ -53,6 +52,34 @@
Closed by #{@merge_request.closed_event.author_name}
%small #{time_ago_in_words(@merge_request.closed_event.created_at)} ago.
+
+- if @merge_request.open? && @commits.any? && can?(current_user, :accept_mr, @project)
+ .automerge_widget.can_be_merged{:style => "display:none"}
+ .ui-box.padded
+ %p
+ You can accept this request automatically. If you still want to do it manually - #{link_to "click here", "#", :class => "how_to_merge_link vlink", :title => "How To Merge"} for instructions
+ = link_to "Accept Merge Request", automerge_project_merge_request_path(@project, @merge_request), :class => "btn small info accept_merge_request", :remote => true
+ &nbsp;
+
+ .automerge_widget.cannot_be_merged{:style => "display:none"}
+ .alert-message
+ %p
+ %strong This request cant be merged with GitLab. You should do it manually &nbsp;
+ = link_to "Show how to merge", "#", :class => "how_to_merge_link btn small padded", :title => "How To Merge"
+
+ .automerge_widget.unchecked
+ .alert-message
+ %p
+ %strong Checking for ability to automatically mergeā€¦
+
+ .automerge_widget.already_cannot_be_merged{:style => "display:none"}
+ .alert-message
+ %p
+ %strong This merge request already can not be merged
+
+
+
+
= render "merge_requests/commits"
- unless @commits.empty?
@@ -72,7 +99,13 @@
:javascript
$(function(){
- MergeRequest.init();
+ MergeRequest.init({
+ url_to_automerge_check: "#{automerge_check_project_merge_request_path(@project, @merge_request)}",
+ });
+
+ $(".accept_merge_request").live("ajax:beforeSend", function() {
+ $(this).replaceWith('#{image_tag "ajax_loader.gif"}');
+ })
})
= render "notes/per_line_form"
diff --git a/config/routes.rb b/config/routes.rb
index a6e32bc1f5b..681bffc9506 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -100,6 +100,8 @@ Gitlab::Application.routes.draw do
resources :merge_requests do
member do
get :diffs
+ get :automerge
+ get :automerge_check
end
collection do
diff --git a/db/migrate/20120329170745_add_automerge_to_merge_request.rb b/db/migrate/20120329170745_add_automerge_to_merge_request.rb
new file mode 100644
index 00000000000..de7c68ee1cb
--- /dev/null
+++ b/db/migrate/20120329170745_add_automerge_to_merge_request.rb
@@ -0,0 +1,5 @@
+class AddAutomergeToMergeRequest < ActiveRecord::Migration
+ def change
+ add_column :merge_requests, :state, :integer, :null => false, :default => 1
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index b1419a4f6d8..fc37d0b0139 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,8 +11,7 @@
#
# It's strongly recommended to check this file into your version control system.
-ActiveRecord::Schema.define(:version => 20120413135904) do
-
+ActiveRecord::Schema.define(:version => 20120329170745) do
create_table "events", :force => true do |t|
t.string "target_type"
t.integer "target_id"
@@ -65,6 +64,7 @@ ActiveRecord::Schema.define(:version => 20120413135904) do
t.text "st_commits", :limit => 2147483647
t.text "st_diffs", :limit => 2147483647
t.boolean "merged", :default => false, :null => false
+ t.boolean "auto_merge", :default => true, :null => false
end
add_index "merge_requests", ["project_id"], :name => "index_merge_requests_on_project_id"
diff --git a/lib/gitlab_merge.rb b/lib/gitlab_merge.rb
new file mode 100644
index 00000000000..66a0e7ec635
--- /dev/null
+++ b/lib/gitlab_merge.rb
@@ -0,0 +1,48 @@
+class GitlabMerge
+ attr_accessor :project, :merge_path, :merge_request, :user
+
+ def initialize(merge_request, user)
+ self.user = user
+ self.merge_request = merge_request
+ self.project = merge_request.project
+ self.merge_path = File.join(Rails.root, "tmp", "merge_repo", project.path, merge_request.id.to_s)
+ FileUtils.rm_rf(merge_path)
+ FileUtils.mkdir_p merge_path
+ end
+
+ def can_be_merged?
+ pull do |repo, output|
+ !(output =~ /Automatic merge failed/)
+ end
+ end
+
+ def merge
+ pull do |repo, output|
+ if output =~ /Automatic merge failed/
+ false
+ else
+ repo.git.push({}, "origin", merge_request.target_branch)
+ true
+ end
+ end
+ end
+
+ def pull
+ File.open(File.join(Rails.root, "tmp", "merge_repo", "#{project.path}.lock"), "w+") do |f|
+ f.flock(File::LOCK_EX)
+
+ self.project.repo.git.clone({:branch => merge_request.target_branch}, project.url_to_repo, merge_path)
+ unless File.exist?(self.merge_path)
+ raise "Gitlab user do not have access to repo. You should run: rake gitlab_enable_automerge"
+ end
+ Dir.chdir(merge_path) do
+ merge_repo = Grit::Repo.new('.')
+ merge_repo.git.sh "git config user.name \"#{user.name}\""
+ merge_repo.git.sh "git config user.email \"#{user.email}\""
+ output = merge_repo.git.pull({}, "--no-ff", "origin", merge_request.source_branch)
+ yield(merge_repo, output)
+ end
+
+ end
+ end
+end
diff --git a/lib/gitlabhq/gitolite.rb b/lib/gitlabhq/gitolite.rb
index 701b9a8f8b4..fabeb7d0fca 100644
--- a/lib/gitlabhq/gitolite.rb
+++ b/lib/gitlabhq/gitolite.rb
@@ -123,5 +123,34 @@ module Gitlabhq
repo
end
+
+ def admin_all_repo
+ ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(@local_dir,'gitolite'))
+ conf = ga_repo.config
+ owner_name = ""
+
+ # Read gitolite-admin user
+ #
+ begin
+ repo = conf.get_repo("gitolite-admin")
+ owner_name = repo.permissions[0]["RW+"][""][0]
+ raise StandardError if owner_name.blank?
+ rescue => ex
+ puts "Cant determine gitolite-admin owner".red
+ raise StandardError
+ end
+
+ # @ALL repos premission for gitolite owner
+ repo_name = "@all"
+ repo = if conf.has_repo?(repo_name)
+ conf.get_repo(repo_name)
+ else
+ ::Gitolite::Config::Repo.new(repo_name)
+ end
+
+ repo.add_permission("RW+", "", owner_name)
+ conf.add_repo(repo, true)
+ ga_repo.save
+ end
end
end
diff --git a/lib/tasks/gitlab_enable_automerge.rake b/lib/tasks/gitlab_enable_automerge.rake
new file mode 100644
index 00000000000..6ff5003eac1
--- /dev/null
+++ b/lib/tasks/gitlab_enable_automerge.rake
@@ -0,0 +1,9 @@
+desc "Give gitlab user full access to every repo"
+task :gitlab_enable_automerge => :environment do
+
+ Gitlabhq::GitHost.system.new.configure do |git|
+ git.admin_all_repo
+ end
+
+ puts "Done!".green
+end