diff options
author | Dmitriy Zaporozhets <dzaporozhets@sphereconsultinginc.com> | 2011-11-28 09:39:43 +0200 |
---|---|---|
committer | Dmitriy Zaporozhets <dzaporozhets@sphereconsultinginc.com> | 2011-11-28 09:39:43 +0200 |
commit | 6d460aa2d6b3959593c168eed181516036525393 (patch) | |
tree | bd7599850ad234b9a39fab366aad41b858154784 | |
parent | 224420396877cca13843fda9c69a2f94ffbad0b8 (diff) | |
download | gitlab-ce-6d460aa2d6b3959593c168eed181516036525393.tar.gz |
merge request entity
-rw-r--r-- | app/assets/javascripts/merge_requests.js.coffee | 3 | ||||
-rw-r--r-- | app/assets/stylesheets/issues.css.scss | 2 | ||||
-rw-r--r-- | app/assets/stylesheets/merge_requests.css.scss | 10 | ||||
-rw-r--r-- | app/assets/stylesheets/projects.css.scss | 15 | ||||
-rw-r--r-- | app/controllers/merge_requests_controller.rb | 73 | ||||
-rw-r--r-- | app/helpers/merge_requests_helper.rb | 2 | ||||
-rw-r--r-- | app/models/ability.rb | 5 | ||||
-rw-r--r-- | app/models/merge_request.rb | 36 | ||||
-rw-r--r-- | app/models/project.rb | 1 | ||||
-rw-r--r-- | app/views/layouts/project.html.haml | 4 | ||||
-rw-r--r-- | app/views/merge_requests/_form.html.haml | 39 | ||||
-rw-r--r-- | app/views/merge_requests/edit.html.haml | 1 | ||||
-rw-r--r-- | app/views/merge_requests/index.html.haml | 25 | ||||
-rw-r--r-- | app/views/merge_requests/new.html.haml | 1 | ||||
-rw-r--r-- | app/views/merge_requests/show.html.haml | 59 | ||||
-rw-r--r-- | config/routes.rb | 1 | ||||
-rw-r--r-- | db/migrate/20111127155345_create_merge_requests.rb | 15 | ||||
-rw-r--r-- | db/schema.rb | 16 | ||||
-rw-r--r-- | spec/helpers/application_helper_spec.rb | 35 | ||||
-rw-r--r-- | spec/models/merge_request_spec.rb | 5 | ||||
-rw-r--r-- | spec/requests/merge_requests_spec.rb | 11 |
21 files changed, 322 insertions, 37 deletions
diff --git a/app/assets/javascripts/merge_requests.js.coffee b/app/assets/javascripts/merge_requests.js.coffee new file mode 100644 index 00000000000..761567942fc --- /dev/null +++ b/app/assets/javascripts/merge_requests.js.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ diff --git a/app/assets/stylesheets/issues.css.scss b/app/assets/stylesheets/issues.css.scss index a283a4566f7..a34be642717 100644 --- a/app/assets/stylesheets/issues.css.scss +++ b/app/assets/stylesheets/issues.css.scss @@ -58,6 +58,8 @@ padding: 0; } +body.project-page .merge-request-form-holder table.no-borders tr, +body.project-page .merge-request-form-holder table.no-borders td, body.project-page .issue-form-holder table.no-borders tr, body.project-page .issue-form-holder table.no-borders td, body.project-page .new_snippet table tr, diff --git a/app/assets/stylesheets/merge_requests.css.scss b/app/assets/stylesheets/merge_requests.css.scss new file mode 100644 index 00000000000..2b60605b255 --- /dev/null +++ b/app/assets/stylesheets/merge_requests.css.scss @@ -0,0 +1,10 @@ +// Place all the styles related to the MergeRequests controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ + + +.merge-request-form-holder { + select { + width:300px; + } +} diff --git a/app/assets/stylesheets/projects.css.scss b/app/assets/stylesheets/projects.css.scss index b6c14c80141..34c9c10b4e4 100644 --- a/app/assets/stylesheets/projects.css.scss +++ b/app/assets/stylesheets/projects.css.scss @@ -155,6 +155,8 @@ input.ssh_project_url { } /** FORM INPUTS **/ +.new_merge_request, +.edit_merge_request, .user_new, .new_key, .new_issue, @@ -384,6 +386,19 @@ body.dashboard.project-page .news-feed .project-updates a.project-update span.up body.dashboard.project-page .news-feed .project-updates a.project-update span.update-author strong{font-weight: bold; font-style: normal;} /* eo Dashboard Page */ + +/** Merge requests */ +body.project-page .merge-request-commits {margin-bottom: 20px; display: block; width: 100%;} +body.project-page .merge-request-commits .data{ padding: 0} +body.project-page .merge-request-commits a.commit {padding: 10px; border-bottom: 1px solid #eee; overflow: hidden; display: block;} +body.project-page .merge-request-commits a.commit:last-child{border-bottom: 0} +body.project-page .merge-request-commits a.commit img{float: left; margin-right: 10px;} +body.project-page .merge-request-commits a.commit span.update-title, .dashboard-page .news-feed .project-updates li a span.update-author{display: block;} +body.project-page .merge-request-commits a.commit span.update-title{margin-bottom: 10px} +body.project-page .merge-request-commits a.commit span.update-author{color: #999; font-weight: normal; font-style: italic;} +body.project-page .merge-request-commits a.commit span.update-author strong{font-weight: bold; font-style: normal;} + + body.project-page .team_member_new .span-6, .team_member_edit .span-6{ padding:10px 0; } body.projects-page input.text.git-url.project_list_url { width:165px; } diff --git a/app/controllers/merge_requests_controller.rb b/app/controllers/merge_requests_controller.rb new file mode 100644 index 00000000000..c163bbd3443 --- /dev/null +++ b/app/controllers/merge_requests_controller.rb @@ -0,0 +1,73 @@ +class MergeRequestsController < ApplicationController + before_filter :authenticate_user! + before_filter :project + before_filter :merge_request, :only => [:edit, :update, :destroy, :show] + layout "project" + + # Authorize + before_filter :add_project_abilities + before_filter :authorize_read_project! + before_filter :authorize_write_project!, :only => [:new, :create, :edit, :update] + + def index + @merge_requests = @project.merge_requests.all + end + + def show + unless @project.repo.heads.map(&:name).include?(@merge_request.target_branch) && + @project.repo.heads.map(&:name).include?(@merge_request.source_branch) + head(404)and return + end + + @commits = @project.repo.commits_between(@merge_request.target_branch, @merge_request.source_branch).map {|c| Commit.new(c)} + end + + def new + @merge_request = @project.merge_requests.new + end + + def edit + end + + def create + @merge_request = @project.merge_requests.new(params[:merge_request]) + @merge_request.author = current_user + + respond_to do |format| + if @merge_request.save + format.html { redirect_to [@project, @merge_request], notice: 'Merge request was successfully created.' } + format.json { render json: @merge_request, status: :created, location: @merge_request } + else + format.html { render action: "new" } + format.json { render json: @merge_request.errors, status: :unprocessable_entity } + end + end + end + + def update + respond_to do |format| + if @merge_request.update_attributes(params[:merge_request]) + format.html { redirect_to [@project, @merge_request], notice: 'Merge request was successfully updated.' } + format.json { head :ok } + else + format.html { render action: "edit" } + format.json { render json: @merge_request.errors, status: :unprocessable_entity } + end + end + end + + def destroy + @merge_request.destroy + + respond_to do |format| + format.html { redirect_to project_merge_requests_url(@project) } + format.json { head :ok } + end + end + + protected + + def merge_request + @merge_request ||= @project.merge_requests.find(params[:id]) + end +end diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb new file mode 100644 index 00000000000..fac9870fc8a --- /dev/null +++ b/app/helpers/merge_requests_helper.rb @@ -0,0 +1,2 @@ +module MergeRequestsHelper +end diff --git a/app/models/ability.rb b/app/models/ability.rb index 5ab70650d81..c41704f9a11 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -17,6 +17,7 @@ class Ability :read_issue, :read_snippet, :read_team_member, + :read_merge_request, :read_note ] if project.readers.include?(user) @@ -24,6 +25,7 @@ class Ability :write_project, :write_issue, :write_snippet, + :write_merge_request, :write_note ] if project.writers.include?(user) @@ -32,6 +34,7 @@ class Ability :admin_issue, :admin_snippet, :admin_team_member, + :admin_merge_request, :admin_note ] if project.admins.include?(user) @@ -39,7 +42,7 @@ class Ability end class << self - [:issue, :note, :snippet].each do |name| + [:issue, :note, :snippet, :merge_request].each do |name| define_method "#{name}_abilities" do |user, subject| if subject.author == user [ diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb new file mode 100644 index 00000000000..dc4c92a9c98 --- /dev/null +++ b/app/models/merge_request.rb @@ -0,0 +1,36 @@ +class MergeRequest < ActiveRecord::Base + belongs_to :project + belongs_to :author, :class_name => "User" + belongs_to :assignee, :class_name => "User" + has_many :notes, :as => :noteable + + attr_protected :author, :author_id, :project, :project_id + + validates_presence_of :project_id + validates_presence_of :assignee_id + validates_presence_of :author_id + validates_presence_of :source_branch + validates_presence_of :target_branch + + delegate :name, + :email, + :to => :author, + :prefix => true + + delegate :name, + :email, + :to => :assignee, + :prefix => true + + validates :title, + :presence => true, + :length => { :within => 0..255 } + + scope :opened, where(:closed => false) + scope :closed, where(:closed => true) + scope :assigned, lambda { |u| where(:assignee_id => u.id)} + + def new? + today? && created_at == updated_at + end +end diff --git a/app/models/project.rb b/app/models/project.rb index 7998979d0d8..ae63efb6b40 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -3,6 +3,7 @@ require "grit" class Project < ActiveRecord::Base belongs_to :owner, :class_name => "User" + has_many :merge_requests, :dependent => :destroy has_many :issues, :dependent => :destroy, :order => "position" has_many :users_projects, :dependent => :destroy has_many :users, :through => :users_projects diff --git a/app/views/layouts/project.html.haml b/app/views/layouts/project.html.haml index 0c121cc9fef..20553c94b51 100644 --- a/app/views/layouts/project.html.haml +++ b/app/views/layouts/project.html.haml @@ -39,6 +39,10 @@ Wall - if @project.common_notes.today.count > 0 %span{ :class => "number" }= @project.common_notes.today.count + = link_to project_merge_requests_path(@project), :class => (controller.controller_name == "merge_requests") ? "current" : nil do + Merge Requests + - if @project.merge_requests.opened.count > 0 + %span{ :class => "number" }= @project.merge_requests.opened.count = link_to project_snippets_path(@project), :class => (controller.controller_name == "snippets") ? "current" : nil do Snippets - if @project.snippets.non_expired.count > 0 diff --git a/app/views/merge_requests/_form.html.haml b/app/views/merge_requests/_form.html.haml new file mode 100644 index 00000000000..8c0ca202dba --- /dev/null +++ b/app/views/merge_requests/_form.html.haml @@ -0,0 +1,39 @@ +%div.merge-request-form-holder + .ui-box.width-100p + %h3 + = @merge_request.new_record? ? "New Merge Request" : "Edit Merge Request ##{@merge_request.id}" + = form_for [@project, @merge_request] do |f| + .data + %table.no-borders + -if @merge_request.errors.any? + %tr + %td Errors + %td + #error_explanation + - @merge_request.errors.full_messages.each do |msg| + %span= msg + %br + + %tr + %td= f.label :title + %td= f.text_field :title + %tr + %td= f.label :target_branch, "From" + %td= f.select(:target_branch, @project.heads.map(&:name), { :include_blank => "Select branch" }) + %tr + %td= f.label :source_branch, "To" + %td= f.select(:source_branch, @project.heads.map(&:name), { :include_blank => "Select branch" }) + %tr + %td= f.label :assignee_id, "Assign to" + %td= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { :include_blank => "Select user" }) + .buttons + = f.submit 'Save', :class => "grey-button" + .right= link_to 'Back', project_merge_requests_path(@project), :class => "grey-button" + +:javascript + $(function(){ + $('select#merge_request_assignee_id').chosen(); + $('select#merge_request_source_branch').chosen(); + $('select#merge_request_target_branch').chosen(); + }); + diff --git a/app/views/merge_requests/edit.html.haml b/app/views/merge_requests/edit.html.haml new file mode 100644 index 00000000000..bcc5832792f --- /dev/null +++ b/app/views/merge_requests/edit.html.haml @@ -0,0 +1 @@ += render 'form' diff --git a/app/views/merge_requests/index.html.haml b/app/views/merge_requests/index.html.haml new file mode 100644 index 00000000000..b7fcea7d1e0 --- /dev/null +++ b/app/views/merge_requests/index.html.haml @@ -0,0 +1,25 @@ +%table + %thead + %th Target branch + %th Source branch + %th Author + %th Assignee + %th Title + %th + %th + %th + + - @merge_requests.each do |merge_request| + %tr + %td= merge_request.target_branch + %td= merge_request.source_branch + %td= merge_request.author_id + %td= merge_request.assignee_id + %td= merge_request.title + %td= link_to 'Show', [@project, merge_request] + %td= link_to 'Edit', edit_project_merge_request_path(@project, merge_request) + %td= link_to 'Destroy', [@project, merge_request], :confirm => 'Are you sure?', :method => :delete + +%br + += link_to 'New Merge request', new_project_merge_request_path(@project) diff --git a/app/views/merge_requests/new.html.haml b/app/views/merge_requests/new.html.haml new file mode 100644 index 00000000000..bcc5832792f --- /dev/null +++ b/app/views/merge_requests/new.html.haml @@ -0,0 +1 @@ += render 'form' diff --git a/app/views/merge_requests/show.html.haml b/app/views/merge_requests/show.html.haml new file mode 100644 index 00000000000..ffd44d65c9d --- /dev/null +++ b/app/views/merge_requests/show.html.haml @@ -0,0 +1,59 @@ +.merge-request-show-holder.ui-box.width-100p + %h3 + = "Merge Request ##{@merge_request.id}:" + + = "'#{@merge_request.source_branch}'" + → + = "'#{@merge_request.target_branch}'" + .right + - if @merge_request.closed + %span.tag.high Resolved + - else + %span.tag.today Open + + .data + %p= @merge_request.title + + - if @merge_request.author == @merge_request.assignee + = image_tag gravatar_icon(@merge_request.assignee_email), :width => 20, :style => "padding:0 5px;" + = @merge_request.assignee_name + - else + = image_tag gravatar_icon(@merge_request.author_email), :width => 20, :style => "padding:0 5px;" + = @merge_request.author_name + → + = image_tag gravatar_icon(@merge_request.assignee_email), :width => 20, :style => "padding:0 5px;" + = @merge_request.assignee_name + .right + %cite.cgray= @merge_request.created_at.stamp("21 Aug 2011, 11:15pm") + .clear + + .buttons + - if can? current_user, :write_project, @project + - if @merge_request.closed + = link_to 'Reopen', project_merge_request_path(@project, @merge_request, :merge_request => {:closed => false }, :status_only => true), :method => :put, :class => "grey-button" + - else + = link_to 'Resolve', project_merge_request_path(@project, @merge_request, :merge_request => {:closed => true }, :status_only => true), :method => :put, :class => "grey-button" + .right + = link_to 'Edit', edit_project_merge_request_path(@project, @merge_request), :class => "grey-button positive" + +.clear +%br +%br + + +- if @commits.size > 0 + .merge-request-commits.ui-box.width-100p + - @commits.each do |commit| + %a{ :class => "commit", :href => project_commit_path(@project, :id => commit.id) } + - if commit.author_email + = image_tag gravatar_icon(commit.author_email), :class => "left", :width => 40, :style => "padding-right:5px;" + - else + = image_tag "no_avatar.png", :class => "left", :width => 40, :style => "padding-right:5px;" + %span.update-title + = commit.id.to_s + %span.update-author + %strong= commit.author_name + authored + = time_ago_in_words(commit.created_at) + ago + .clear diff --git a/config/routes.rb b/config/routes.rb index 840ef524cf0..1426d5f26ff 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -59,6 +59,7 @@ Gitlab::Application.routes.draw do end end + resources :merge_requests resources :snippets resources :commits resources :team_members diff --git a/db/migrate/20111127155345_create_merge_requests.rb b/db/migrate/20111127155345_create_merge_requests.rb new file mode 100644 index 00000000000..1555ae84041 --- /dev/null +++ b/db/migrate/20111127155345_create_merge_requests.rb @@ -0,0 +1,15 @@ +class CreateMergeRequests < ActiveRecord::Migration + def change + create_table :merge_requests do |t| + t.string :target_branch, :null => false + t.string :source_branch, :null => false + t.integer :project_id, :null => false + t.integer :author_id + t.integer :assignee_id + t.string :title + t.boolean :closed, :default => false, :null => false + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 502f52ad9cb..8ffd874c7b7 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20111124115339) do +ActiveRecord::Schema.define(:version => 20111127155345) do create_table "features", :force => true do |t| t.string "name" @@ -21,6 +21,8 @@ ActiveRecord::Schema.define(:version => 20111124115339) do t.integer "project_id" t.datetime "created_at" t.datetime "updated_at" + t.string "version" + t.integer "status", :default => 0, :null => false end create_table "issues", :force => true do |t| @@ -45,6 +47,18 @@ ActiveRecord::Schema.define(:version => 20111124115339) do t.string "identifier" end + create_table "merge_requests", :force => true do |t| + t.string "target_branch", :null => false + t.string "source_branch", :null => false + t.integer "project_id", :null => false + t.integer "author_id" + t.integer "assignee_id" + t.string "title" + t.boolean "closed", :default => false, :null => false + t.datetime "created_at" + t.datetime "updated_at" + end + create_table "notes", :force => true do |t| t.text "note" t.string "noteable_id" diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb deleted file mode 100644 index 3e174ca47ab..00000000000 --- a/spec/helpers/application_helper_spec.rb +++ /dev/null @@ -1,35 +0,0 @@ -require 'spec_helper' - -describe ApplicationHelper do - context ".gravatar_icon" do - context "over http" do - it "returns the correct URL to www.gravatar.com" do - expected = "http://www.gravatar.com/avatar/f7daa65b2aa96290bb47c4d68d11fe6a?s=40&d=identicon" - - # Pretend we're running over HTTP - helper.stub(:request) do - request = double('request') - request.stub(:ssl?) { false } - request - end - - helper.gravatar_icon("admin@local.host").should == expected - end - end - - context "over https" do - it "returns the correct URL to secure.gravatar.com" do - expected = "https://secure.gravatar.com/avatar/f7daa65b2aa96290bb47c4d68d11fe6a?s=40&d=identicon" - - # Pretend we're running over HTTPS - helper.stub(:request) do - request = double('request') - request.stub(:ssl?) { true } - request - end - - helper.gravatar_icon("admin@local.host").should == expected - end - end - end -end diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb new file mode 100644 index 00000000000..cb260fc5fb1 --- /dev/null +++ b/spec/models/merge_request_spec.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +describe MergeRequest do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/requests/merge_requests_spec.rb b/spec/requests/merge_requests_spec.rb new file mode 100644 index 00000000000..1252e8cd00d --- /dev/null +++ b/spec/requests/merge_requests_spec.rb @@ -0,0 +1,11 @@ +require 'spec_helper' + +describe "MergeRequests" do + describe "GET /merge_requests" do + it "works! (now write some real specs)" do + # Run the generator again with the --webrat flag if you want to use webrat methods/matchers + get merge_requests_path + response.status.should be(200) + end + end +end |