summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitriy Zaporozhets <dzaporozhets@sphereconsultinginc.com>2011-11-28 09:39:43 +0200
committerDmitriy Zaporozhets <dzaporozhets@sphereconsultinginc.com>2011-11-28 09:39:43 +0200
commit6d460aa2d6b3959593c168eed181516036525393 (patch)
treebd7599850ad234b9a39fab366aad41b858154784
parent224420396877cca13843fda9c69a2f94ffbad0b8 (diff)
downloadgitlab-ce-6d460aa2d6b3959593c168eed181516036525393.tar.gz
merge request entity
-rw-r--r--app/assets/javascripts/merge_requests.js.coffee3
-rw-r--r--app/assets/stylesheets/issues.css.scss2
-rw-r--r--app/assets/stylesheets/merge_requests.css.scss10
-rw-r--r--app/assets/stylesheets/projects.css.scss15
-rw-r--r--app/controllers/merge_requests_controller.rb73
-rw-r--r--app/helpers/merge_requests_helper.rb2
-rw-r--r--app/models/ability.rb5
-rw-r--r--app/models/merge_request.rb36
-rw-r--r--app/models/project.rb1
-rw-r--r--app/views/layouts/project.html.haml4
-rw-r--r--app/views/merge_requests/_form.html.haml39
-rw-r--r--app/views/merge_requests/edit.html.haml1
-rw-r--r--app/views/merge_requests/index.html.haml25
-rw-r--r--app/views/merge_requests/new.html.haml1
-rw-r--r--app/views/merge_requests/show.html.haml59
-rw-r--r--config/routes.rb1
-rw-r--r--db/migrate/20111127155345_create_merge_requests.rb15
-rw-r--r--db/schema.rb16
-rw-r--r--spec/helpers/application_helper_spec.rb35
-rw-r--r--spec/models/merge_request_spec.rb5
-rw-r--r--spec/requests/merge_requests_spec.rb11
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}:"
+ &nbsp;
+ = "'#{@merge_request.source_branch}'"
+ &rarr;
+ = "'#{@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
+ &rarr;
+ = 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