Commit 5a3f23f395eef87bf3457e6474bac82333e71ec8

Authored by Dmitriy Zaporozhets
1 parent 27e36998

Persist Merge Request diff. Auto merge request close on push

app/controllers/merge_requests_controller.rb
@@ -41,13 +41,12 @@ class MergeRequestsController < ApplicationController @@ -41,13 +41,12 @@ class MergeRequestsController < ApplicationController
41 41
42 @note = @project.notes.new(:noteable => @merge_request) 42 @note = @project.notes.new(:noteable => @merge_request)
43 43
44 - @commits = @project.repo.  
45 - commits_between(@merge_request.target_branch, @merge_request.source_branch).  
46 - map {|c| Commit.new(c)}.  
47 - sort_by(&:created_at).  
48 - reverse 44 + # Get commits from repository
  45 + # or from cache if already merged
  46 + @commits = @merge_request.commits
49 47
50 - render_full_content 48 + # Close MR if nothing to merge
  49 + #@merge_request.mark_as_merged! if @merge_request.probably_merged?
51 50
52 respond_to do |format| 51 respond_to do |format|
53 format.html 52 format.html
@@ -76,6 +75,8 @@ class MergeRequestsController < ApplicationController @@ -76,6 +75,8 @@ class MergeRequestsController < ApplicationController
76 75
77 respond_to do |format| 76 respond_to do |format|
78 if @merge_request.save 77 if @merge_request.save
  78 + @merge_request.reloaded_commits
  79 + @merge_request.reloaded_diffs
79 format.html { redirect_to [@project, @merge_request], notice: 'Merge request was successfully created.' } 80 format.html { redirect_to [@project, @merge_request], notice: 'Merge request was successfully created.' }
80 format.json { render json: @merge_request, status: :created, location: @merge_request } 81 format.json { render json: @merge_request, status: :created, location: @merge_request }
81 else 82 else
app/models/event.rb
@@ -7,6 +7,7 @@ class Event < ActiveRecord::Base @@ -7,6 +7,7 @@ class Event < ActiveRecord::Base
7 Reopened = 4 7 Reopened = 4
8 Pushed = 5 8 Pushed = 5
9 Commented = 6 9 Commented = 6
  10 + Merged = 7
10 11
11 belongs_to :project 12 belongs_to :project
12 belongs_to :target, :polymorphic => true 13 belongs_to :target, :polymorphic => true
app/models/merge_request.rb
  1 +require File.join(Rails.root, "app/models/commit")
  2 +
1 class MergeRequest < ActiveRecord::Base 3 class MergeRequest < ActiveRecord::Base
2 belongs_to :project 4 belongs_to :project
3 belongs_to :author, :class_name => "User" 5 belongs_to :author, :class_name => "User"
4 belongs_to :assignee, :class_name => "User" 6 belongs_to :assignee, :class_name => "User"
5 has_many :notes, :as => :noteable, :dependent => :destroy 7 has_many :notes, :as => :noteable, :dependent => :destroy
6 8
  9 + serialize :st_commits
  10 + serialize :st_diffs
  11 +
7 attr_protected :author, :author_id, :project, :project_id 12 attr_protected :author, :author_id, :project, :project_id
8 attr_accessor :author_id_of_changes 13 attr_accessor :author_id_of_changes
9 14
@@ -32,7 +37,6 @@ class MergeRequest &lt; ActiveRecord::Base @@ -32,7 +37,6 @@ class MergeRequest &lt; ActiveRecord::Base
32 scope :closed, where(:closed => true) 37 scope :closed, where(:closed => true)
33 scope :assigned, lambda { |u| where(:assignee_id => u.id)} 38 scope :assigned, lambda { |u| where(:assignee_id => u.id)}
34 39
35 -  
36 def validate_branches 40 def validate_branches
37 if target_branch == source_branch 41 if target_branch == source_branch
38 errors.add :base, "You can not use same branch for source and target branches" 42 errors.add :base, "You can not use same branch for source and target branches"
@@ -44,18 +48,65 @@ class MergeRequest &lt; ActiveRecord::Base @@ -44,18 +48,65 @@ class MergeRequest &lt; ActiveRecord::Base
44 end 48 end
45 49
46 def diffs 50 def diffs
  51 + st_diffs || []
  52 + end
  53 +
  54 + def reloaded_diffs
  55 + if open? && unmerged_diffs.any?
  56 + self.st_diffs = unmerged_diffs
  57 + save
  58 + end
  59 + diffs
  60 + end
  61 +
  62 + def unmerged_diffs
47 commits = project.repo.commits_between(target_branch, source_branch).map {|c| Commit.new(c)} 63 commits = project.repo.commits_between(target_branch, source_branch).map {|c| Commit.new(c)}
48 diffs = project.repo.diff(commits.first.prev_commit.id, commits.last.id) rescue [] 64 diffs = project.repo.diff(commits.first.prev_commit.id, commits.last.id) rescue []
49 end 65 end
50 66
51 def last_commit 67 def last_commit
52 - project.commit(source_branch) 68 + commits.first
53 end 69 end
54 70
55 # Return the number of +1 comments (upvotes) 71 # Return the number of +1 comments (upvotes)
56 def upvotes 72 def upvotes
57 notes.select(&:upvote?).size 73 notes.select(&:upvote?).size
58 end 74 end
  75 +
  76 + def commits
  77 + st_commits || []
  78 + end
  79 +
  80 + def probably_merged?
  81 + unmerged_commits.empty? &&
  82 + commits.any? && open?
  83 + end
  84 +
  85 + def open?
  86 + !closed
  87 + end
  88 +
  89 + def mark_as_merged!
  90 + self.merged = true
  91 + self.closed = true
  92 + save
  93 + end
  94 +
  95 + def reloaded_commits
  96 + if open? && unmerged_commits.any?
  97 + self.st_commits = unmerged_commits
  98 + save
  99 + end
  100 + commits
  101 + end
  102 +
  103 + def unmerged_commits
  104 + self.project.repo.
  105 + commits_between(self.target_branch, self.source_branch).
  106 + map {|c| Commit.new(c)}.
  107 + sort_by(&:created_at).
  108 + reverse
  109 + end
59 end 110 end
60 # == Schema Information 111 # == Schema Information
61 # 112 #
app/models/project.rb
@@ -73,6 +73,40 @@ class Project &lt; ActiveRecord::Base @@ -73,6 +73,40 @@ class Project &lt; ActiveRecord::Base
73 ) 73 )
74 end 74 end
75 75
  76 + def update_merge_requests(oldrev, newrev, ref, author_key_id)
  77 + return true unless ref =~ /heads/
  78 + branch_name = ref.gsub("refs/heads/", "")
  79 +
  80 + key = Key.find_by_identifier(author_key_id)
  81 + user = key.user
  82 +
  83 + c_ids = self.commits_between(oldrev, newrev).map(&:id)
  84 +
  85 + # update commits & diffs for existing MR
  86 + mrs = self.merge_requests.opened.where(:source_branch => branch_name).all
  87 + mrs.each do |merge_request|
  88 + merge_request.reloaded_commits
  89 + merge_request.reloaded_diffs
  90 + end
  91 +
  92 + # Close merge requests
  93 + mrs = self.merge_requests.opened.where(:target_branch => branch_name).all
  94 + mrs.each do |merge_request|
  95 + next unless merge_request.last_commit
  96 + # Mark as merged & create event if merged
  97 + if c_ids.include?(merge_request.last_commit.id)
  98 + merge_request.mark_as_merged!
  99 + Event.create(
  100 + :project => self,
  101 + :action => Event::Merged,
  102 + :data => {:merge_request_id => merge_request.id},
  103 + :author_id => user.id
  104 + )
  105 + end
  106 + end
  107 + true
  108 + end
  109 +
76 def execute_web_hooks(oldrev, newrev, ref, author_key_id) 110 def execute_web_hooks(oldrev, newrev, ref, author_key_id)
77 ref_parts = ref.split('/') 111 ref_parts = ref.split('/')
78 112
app/views/merge_requests/_commits.html.haml
@@ -2,7 +2,9 @@ @@ -2,7 +2,9 @@
2 .ui-box 2 .ui-box
3 %h5 Commits 3 %h5 Commits
4 .merge-request-commits 4 .merge-request-commits
5 - %ul.unstyled= render @commits 5 + %ul.unstyled
  6 + - @commits.each do |commit|
  7 + = render "commits/commit", :commit => commit
6 8
7 - else 9 - else
8 %h5 10 %h5
app/views/merge_requests/show.html.haml
@@ -11,12 +11,10 @@ @@ -11,12 +11,10 @@
11 11
12 %span.right 12 %span.right
13 - if can?(current_user, :modify_merge_request, @merge_request) 13 - if can?(current_user, :modify_merge_request, @merge_request)
14 - - if @merge_request.closed  
15 - = link_to 'Reopen', project_merge_request_path(@project, @merge_request, :merge_request => {:closed => false }, :status_only => true), :method => :put, :class => "btn"  
16 - - else 14 + - if @merge_request.open?
17 = link_to 'Close', project_merge_request_path(@project, @merge_request, :merge_request => {:closed => true }, :status_only => true), :method => :put, :class => "btn", :title => "Close merge request" 15 = link_to 'Close', project_merge_request_path(@project, @merge_request, :merge_request => {:closed => true }, :status_only => true), :method => :put, :class => "btn", :title => "Close merge request"
18 - = link_to edit_project_merge_request_path(@project, @merge_request), :class => "btn" do  
19 - Edit 16 + = link_to edit_project_merge_request_path(@project, @merge_request), :class => "btn" do
  17 + Edit
20 18
21 - if @merge_request.upvotes > 0 19 - if @merge_request.upvotes > 0
22 = link_to "#notes", :class => "btn success" do 20 = link_to "#notes", :class => "btn success" do
app/workers/post_receive.rb
@@ -8,7 +8,13 @@ class PostReceive @@ -8,7 +8,13 @@ class PostReceive
8 # Ignore push from non-gitlab users 8 # Ignore push from non-gitlab users
9 return false unless Key.find_by_identifier(author_key_id) 9 return false unless Key.find_by_identifier(author_key_id)
10 10
  11 + # Create push event
11 project.observe_push(oldrev, newrev, ref, author_key_id) 12 project.observe_push(oldrev, newrev, ref, author_key_id)
  13 +
  14 + # Close merged MR
  15 + project.update_merge_requests(oldrev, newrev, ref, author_key_id)
  16 +
  17 + # Execute web hooks
12 project.execute_web_hooks(oldrev, newrev, ref, author_key_id) 18 project.execute_web_hooks(oldrev, newrev, ref, author_key_id)
13 end 19 end
14 end 20 end
db/migrate/20120315111711_add_commits_diff_store_to_merge_request.rb 0 → 100644
@@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
  1 +class AddCommitsDiffStoreToMergeRequest < ActiveRecord::Migration
  2 + def change
  3 + add_column :merge_requests, :st_commits, :text, :null => true
  4 + add_column :merge_requests, :st_diffs, :text, :null => true
  5 + end
  6 +end
db/migrate/20120315132931_add_merged_to_merge_request.rb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +class AddMergedToMergeRequest < ActiveRecord::Migration
  2 + def change
  3 + add_column :merge_requests, :merged, :true, :null => false, :default => false
  4 + end
  5 +end
@@ -11,7 +11,7 @@ @@ -11,7 +11,7 @@
11 # 11 #
12 # It's strongly recommended to check this file into your version control system. 12 # It's strongly recommended to check this file into your version control system.
13 13
14 -ActiveRecord::Schema.define(:version => 20120307095918) do 14 +ActiveRecord::Schema.define(:version => 20120315132931) do
15 15
16 create_table "events", :force => true do |t| 16 create_table "events", :force => true do |t|
17 t.string "target_type" 17 t.string "target_type"
@@ -50,19 +50,8 @@ ActiveRecord::Schema.define(:version =&gt; 20120307095918) do @@ -50,19 +50,8 @@ ActiveRecord::Schema.define(:version =&gt; 20120307095918) do
50 t.integer "project_id" 50 t.integer "project_id"
51 end 51 end
52 52
53 - create_table "merge_requests", :force => true do |t|  
54 - t.string "target_branch", :null => false  
55 - t.string "source_branch", :null => false  
56 - t.integer "project_id", :null => false  
57 - t.integer "author_id"  
58 - t.integer "assignee_id"  
59 - t.string "title"  
60 - t.boolean "closed", :default => false, :null => false  
61 - t.datetime "created_at", :null => false  
62 - t.datetime "updated_at", :null => false  
63 - end  
64 -  
65 - add_index "merge_requests", ["project_id"], :name => "index_merge_requests_on_project_id" 53 +# Could not dump table "merge_requests" because of following StandardError
  54 +# Unknown type 'true' for column 'merged'
66 55
67 create_table "notes", :force => true do |t| 56 create_table "notes", :force => true do |t|
68 t.text "note" 57 t.text "note"