Commit 0d715bcd authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'mr-on-fork' of https://github.com/karlhungus/gitlabhq into karlhungus-mr-on-fork

Conflicts:
	app/views/projects/commit/show.html.haml
	app/views/projects/compare/show.html.haml
	app/views/projects/merge_requests/branch_from.js.haml
parents 4f07a6a9 4d373005
...@@ -400,6 +400,18 @@ img.emoji { ...@@ -400,6 +400,18 @@ img.emoji {
margin-bottom: 10px; margin-bottom: 10px;
} }
.label-project {
@include border-radius(4px);
padding: 2px 4px;
border: none;
font-size: 14px;
background: #474D57;
color: #fff;
font-family: $monospace_font;
text-shadow: 0 1px 1px #111;
font-weight: normal;
}
.group-name { .group-name {
font-size: 14px; font-size: 14px;
line-height: 24px; line-height: 24px;
......
...@@ -12,7 +12,7 @@ class FilterContext ...@@ -12,7 +12,7 @@ class FilterContext
def apply_filter items def apply_filter items
if params[:project_id] if params[:project_id]
items = items.where(project_id: params[:project_id]) items = items.by_project(params[:project_id])
end end
if params[:search].present? if params[:search].present?
......
...@@ -14,7 +14,7 @@ class MergeRequestsLoadContext < BaseContext ...@@ -14,7 +14,7 @@ class MergeRequestsLoadContext < BaseContext
end end
merge_requests = merge_requests.page(params[:page]).per(20) merge_requests = merge_requests.page(params[:page]).per(20)
merge_requests = merge_requests.includes(:author, :project).order("created_at desc") merge_requests = merge_requests.includes(:author, :source_project, :target_project).order("created_at desc")
# Filter by specific assignee_id (or lack thereof)? # Filter by specific assignee_id (or lack thereof)?
if params[:assignee_id].present? if params[:assignee_id].present?
......
...@@ -19,7 +19,7 @@ class SearchContext ...@@ -19,7 +19,7 @@ class SearchContext
if params[:search_code].present? if params[:search_code].present?
result[:blobs] = project.repository.search_files(query, params[:repository_ref]) unless project.empty_repo? result[:blobs] = project.repository.search_files(query, params[:repository_ref]) unless project.empty_repo?
else else
result[:merge_requests] = MergeRequest.where(project_id: project_ids).search(query).limit(10) result[:merge_requests] = MergeRequest.in_projects(project_ids).search(query).limit(10)
result[:issues] = Issue.where(project_id: project_ids).search(query).limit(10) result[:issues] = Issue.where(project_id: project_ids).search(query).limit(10)
result[:wiki_pages] = [] result[:wiki_pages] = []
end end
......
...@@ -24,8 +24,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -24,8 +24,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
format.html format.html
format.js format.js
format.diff { render text: @merge_request.to_diff } format.diff { render text: @merge_request.to_diff(current_user) }
format.patch { render text: @merge_request.to_patch } format.patch { render text: @merge_request.to_patch(current_user) }
end end
end end
...@@ -33,27 +33,37 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -33,27 +33,37 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@commit = @merge_request.last_commit @commit = @merge_request.last_commit
@comments_allowed = @reply_allowed = true @comments_allowed = @reply_allowed = true
@comments_target = { noteable_type: 'MergeRequest', @comments_target = {noteable_type: 'MergeRequest',
noteable_id: @merge_request.id } noteable_id: @merge_request.id}
@line_notes = @merge_request.notes.where("line_code is not null") @line_notes = @merge_request.notes.where("line_code is not null")
end end
def new def new
@merge_request = @project.merge_requests.new(params[:merge_request]) @merge_request = MergeRequest.new(params[:merge_request])
@merge_request.source_project = @project unless @merge_request.source_project
@merge_request.target_project = @project unless @merge_request.target_project
@target_branches = @merge_request.target_project.nil? ? [] : @merge_request.target_project.repository.branch_names
@source_project = @merge_request.source_project
@merge_request
end end
def edit def edit
@source_project = @merge_request.source_project
@target_project = @merge_request.target_project
@target_branches = @merge_request.target_project.repository.branch_names
end end
def create def create
@merge_request = @project.merge_requests.new(params[:merge_request]) @merge_request = MergeRequest.new(params[:merge_request])
@merge_request.author = current_user @merge_request.author = current_user
@target_branches ||= []
if @merge_request.save if @merge_request.save
@merge_request.reload_code @merge_request.reload_code
redirect_to [@project, @merge_request], notice: 'Merge request was successfully created.' redirect_to [@merge_request.target_project, @merge_request], notice: 'Merge request was successfully created.'
else else
render "new" @source_project = @merge_request.source_project
@target_project = @merge_request.target_project
render action: "new"
end end
end end
...@@ -61,7 +71,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -61,7 +71,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
if @merge_request.update_attributes(params[:merge_request].merge(author_id_of_changes: current_user.id)) if @merge_request.update_attributes(params[:merge_request].merge(author_id_of_changes: current_user.id))
@merge_request.reload_code @merge_request.reload_code
@merge_request.mark_as_unchecked @merge_request.mark_as_unchecked
redirect_to [@project, @merge_request], notice: 'Merge request was successfully updated.' redirect_to [@merge_request.target_project, @merge_request], notice: 'Merge request was successfully updated.'
else else
render "edit" render "edit"
end end
...@@ -89,22 +99,35 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -89,22 +99,35 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end end
def branch_from def branch_from
#This is always source
@source_project = @merge_request.nil? ? @project : @merge_request.source_project
@commit = @repository.commit(params[:ref]) @commit = @repository.commit(params[:ref])
end end
def branch_to def branch_to
@commit = @repository.commit(params[:ref]) @target_project = selected_target_project
@commit = @target_project.repository.commit(params[:ref])
end
def update_branches
@target_project = selected_target_project
@target_branches = @target_project.repository.branch_names
@target_branches
end end
def ci_status def ci_status
status = project.gitlab_ci_service.commit_status(merge_request.last_commit.sha) status = project.gitlab_ci_service.commit_status(merge_request.last_commit.sha)
response = { status: status } response = {status: status}
render json: response render json: response
end end
protected protected
def selected_target_project
((@project.id.to_s == params[:target_project_id]) || @project.forked_project_link.nil?) ? @project : @project.forked_project_link.forked_from_project
end
def merge_request def merge_request
@merge_request ||= @project.merge_requests.find(params[:id]) @merge_request ||= @project.merge_requests.find(params[:id])
end end
...@@ -123,11 +146,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -123,11 +146,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def validates_merge_request def validates_merge_request
# Show git not found page if target branch doesn't exist # Show git not found page if target branch doesn't exist
return invalid_mr unless @project.repository.branch_names.include?(@merge_request.target_branch) return invalid_mr unless @merge_request.target_project.repository.branch_names.include?(@merge_request.target_branch)
# Show git not found page if source branch doesn't exist # Show git not found page if source branch doesn't exist
# and there is no saved commits between source & target branch # and there is no saved commits between source & target branch
return invalid_mr if !@project.repository.branch_names.include?(@merge_request.source_branch) && @merge_request.commits.blank? return invalid_mr if !@merge_request.source_project.repository.branch_names.include?(@merge_request.source_branch) && @merge_request.commits.blank?
end end
def define_show_vars def define_show_vars
......
...@@ -108,8 +108,8 @@ module CommitsHelper ...@@ -108,8 +108,8 @@ module CommitsHelper
end end
end end
def commit_to_html commit def commit_to_html commit, project
escape_javascript(render 'projects/commits/commit', commit: commit) escape_javascript(render 'projects/commits/commit', commit: commit, project: project) unless commit.nil?
end end
def diff_line_content(line) def diff_line_content(line)
......
...@@ -2,12 +2,25 @@ module MergeRequestsHelper ...@@ -2,12 +2,25 @@ module MergeRequestsHelper
def new_mr_path_from_push_event(event) def new_mr_path_from_push_event(event)
new_project_merge_request_path( new_project_merge_request_path(
event.project, event.project,
merge_request: { new_mr_from_push_event(event, event.project)
)
end
def new_mr_path_for_fork_from_push_event(event)
new_project_merge_request_path(
event.project,
new_mr_from_push_event(event, event.project.forked_from_project)
)
end
def new_mr_from_push_event(event, target_project)
return :merge_request => {
source_project_id: event.project.id,
target_project_id: target_project.id,
source_branch: event.branch_name, source_branch: event.branch_name,
target_branch: event.project.repository.root_ref, target_branch: target_project.repository.root_ref,
title: event.branch_name.titleize title: event.branch_name.titleize
} }
)
end end
def mr_css_classes mr def mr_css_classes mr
...@@ -18,6 +31,14 @@ module MergeRequestsHelper ...@@ -18,6 +31,14 @@ module MergeRequestsHelper
end end
def ci_build_details_path merge_request def ci_build_details_path merge_request
merge_request.project.gitlab_ci_service.build_page(merge_request.last_commit.sha) merge_request.source_project.gitlab_ci_service.build_page(merge_request.last_commit.sha)
end
def merge_path_description(merge_request, separator)
if merge_request.for_fork?
"Project:Branches: #{@merge_request.source_project.path_with_namespace}:#{@merge_request.source_branch} #{separator} #{@merge_request.target_project.path_with_namespace}:#{@merge_request.target_branch}"
else
"Branches: #{@merge_request.source_branch} #{separator} #{@merge_request.target_branch}"
end
end end
end end
...@@ -2,28 +2,65 @@ module Emails ...@@ -2,28 +2,65 @@ module Emails
module MergeRequests module MergeRequests
def new_merge_request_email(recipient_id, merge_request_id) def new_merge_request_email(recipient_id, merge_request_id)
@merge_request = MergeRequest.find(merge_request_id) @merge_request = MergeRequest.find(merge_request_id)
@project = @merge_request.project
mail(to: recipient(recipient_id), subject: subject("new merge request !#{@merge_request.id}", @merge_request.title)) mail(to: recipient(recipient_id), subject: subject("new merge request !#{@merge_request.id}", @merge_request.title))
end end
def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id) def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id)
@merge_request = MergeRequest.find(merge_request_id) @merge_request = MergeRequest.find(merge_request_id)
@previous_assignee = User.find_by_id(previous_assignee_id) if previous_assignee_id @previous_assignee = User.find_by_id(previous_assignee_id) if previous_assignee_id
@project = @merge_request.project
mail(to: recipient(recipient_id), subject: subject("changed merge request !#{@merge_request.id}", @merge_request.title)) mail(to: recipient(recipient_id), subject: subject("changed merge request !#{@merge_request.id}", @merge_request.title))
end end
def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id) def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id)
@merge_request = MergeRequest.find(merge_request_id) @merge_request = MergeRequest.find(merge_request_id)
@project = @merge_request.project
@updated_by = User.find updated_by_user_id @updated_by = User.find updated_by_user_id
mail(to: recipient(recipient_id), subject: subject("Closed merge request !#{@merge_request.id}", @merge_request.title)) mail(to: recipient(recipient_id), subject: subject("Closed merge request !#{@merge_request.id}", @merge_request.title))
end end
def merged_merge_request_email(recipient_id, merge_request_id) def merged_merge_request_email(recipient_id, merge_request_id)
@merge_request = MergeRequest.find(merge_request_id) @merge_request = MergeRequest.find(merge_request_id)
@project = @merge_request.project
mail(to: recipient(recipient_id), subject: subject("Accepted merge request !#{@merge_request.id}", @merge_request.title)) mail(to: recipient(recipient_id), subject: subject("Accepted merge request !#{@merge_request.id}", @merge_request.title))
end end
end end
# Over rides default behavour to show source/target
# Formats arguments into a String suitable for use as an email subject
#
# extra - Extra Strings to be inserted into the subject
#
# Examples
#
# >> subject('Lorem ipsum')
# => "GitLab Merge Request | Lorem ipsum"
#
# # Automatically inserts Project name:
# Forked MR
# => source project => <Project id: 1, name: "Ruby on Rails", path: "ruby_on_rails", ...>
# => target project => <Project id: 2, name: "My Ror", path: "ruby_on_rails", ...>
# => source branch => source
# => target branch => target
# >> subject('Lorem ipsum')
# => "GitLab Merge Request | Ruby on Rails:source >> My Ror:target | Lorem ipsum "
#
# Non Forked MR
# => source project => <Project id: 1, name: "Ruby on Rails", path: "ruby_on_rails", ...>
# => target project => <Project id: 1, name: "Ruby on Rails", path: "ruby_on_rails", ...>
# => source branch => source
# => target branch => target
# >> subject('Lorem ipsum')
# => "GitLab Merge Request | Ruby on Rails | source >> target | Lorem ipsum "
# # Accepts multiple arguments
# >> subject('Lorem ipsum', 'Dolor sit amet')
# => "GitLab Merge Request | Lorem ipsum | Dolor sit amet"
def subject(*extra)
subject = "GitLab Merge Request |"
if @merge_request.for_fork?
subject << "#{@merge_request.source_project.name_with_namespace}:#{merge_request.source_branch} >> #{@merge_request.target_project.name_with_namespace}:#{merge_request.target_branch}"
else
subject << "#{@merge_request.source_project.name_with_namespace} | #{merge_request.source_branch} >> #{merge_request.target_branch}"
end
subject << " | " + extra.join(' | ') if extra.present?
subject
end
end end
...@@ -6,6 +6,7 @@ class Notify < ActionMailer::Base ...@@ -6,6 +6,7 @@ class Notify < ActionMailer::Base
add_template_helper ApplicationHelper add_template_helper ApplicationHelper
add_template_helper GitlabMarkdownHelper add_template_helper GitlabMarkdownHelper
add_template_helper MergeRequestsHelper
default_url_options[:host] = Gitlab.config.gitlab.host default_url_options[:host] = Gitlab.config.gitlab.host
default_url_options[:protocol] = Gitlab.config.gitlab.protocol default_url_options[:protocol] = Gitlab.config.gitlab.protocol
......
...@@ -9,19 +9,14 @@ module Issuable ...@@ -9,19 +9,14 @@ module Issuable
include Mentionable include Mentionable
included do included do
belongs_to :project
belongs_to :author, class_name: "User" belongs_to :author, class_name: "User"
belongs_to :assignee, class_name: "User" belongs_to :assignee, class_name: "User"
belongs_to :milestone belongs_to :milestone
has_many :notes, as: :noteable, dependent: :destroy has_many :notes, as: :noteable, dependent: :destroy
validates :project, presence: true
validates :author, presence: true validates :author, presence: true
validates :title, presence: true, length: { within: 0..255 } validates :title, presence: true, length: { within: 0..255 }
scope :opened, -> { with_state(:opened) }
scope :closed, -> { with_state(:closed) }
scope :of_group, ->(group) { where(project_id: group.project_ids) }
scope :assigned_to, ->(u) { where(assignee_id: u.id)} scope :assigned_to, ->(u) { where(assignee_id: u.id)}
scope :recent, -> { order("created_at DESC") } scope :recent, -> { order("created_at DESC") }
scope :assigned, -> { where("assignee_id IS NOT NULL") } scope :assigned, -> { where("assignee_id IS NOT NULL") }
......
...@@ -17,8 +17,18 @@ ...@@ -17,8 +17,18 @@
# #
class Issue < ActiveRecord::Base class Issue < ActiveRecord::Base
include Issuable include Issuable
belongs_to :project
validates :project, presence: true
scope :of_group, ->(group) { where(project_id: group.project_ids) }
scope :of_user_team, ->(team) { where(project_id: team.project_ids, assignee_id: team.member_ids) }
scope :opened, -> { with_state(:opened) }
scope :closed, -> { with_state(:closed) }
scope :by_project, ->(project_id) {where(project_id:project_id)}
attr_accessible :title, :assignee_id, :position, :description, attr_accessible :title, :assignee_id, :position, :description,
:milestone_id, :label_list, :author_id_of_changes, :milestone_id, :label_list, :author_id_of_changes,
:state_event :state_event
......
...@@ -3,9 +3,10 @@ ...@@ -3,9 +3,10 @@
# Table name: merge_requests # Table name: merge_requests
# #
# id :integer not null, primary key # id :integer not null, primary key
# target_project_id :integer not null
# target_branch :string(255) not null # target_branch :string(255) not null
# source_project_id :integer not null
# source_branch :string(255) not null # source_branch :string(255) not null
# project_id :integer not null
# author_id :integer # author_id :integer
# assignee_id :integer # assignee_id :integer
# title :string(255) # title :string(255)
...@@ -22,10 +23,14 @@ require Rails.root.join("app/models/commit") ...@@ -22,10 +23,14 @@ require Rails.root.join("app/models/commit")
require Rails.root.join("lib/static_model") require Rails.root.join("lib/static_model")
class MergeRequest < ActiveRecord::Base class MergeRequest < ActiveRecord::Base
include Issuable include Issuable
attr_accessible :title, :assignee_id, :target_branch, :source_branch, :milestone_id, belongs_to :target_project, foreign_key: :target_project_id, class_name: "Project"
:author_id_of_changes, :state_event belongs_to :source_project, foreign_key: :source_project_id, class_name: "Project"
attr_accessible :title, :assignee_id, :source_project_id, :source_branch, :target_project_id, :target_branch, :milestone_id, :author_id_of_changes, :state_event
attr_accessor :should_remove_source_branch attr_accessor :should_remove_source_branch
...@@ -74,30 +79,37 @@ class MergeRequest < ActiveRecord::Base ...@@ -74,30 +79,37 @@ class MergeRequest < ActiveRecord::Base
serialize :st_commits serialize :st_commits
serialize :st_diffs serialize :st_diffs
validates :source_project, presence: true
validates :source_branch, presence: true validates :source_branch, presence: true
validates :target_project, presence: true
validates :target_branch, presence: true validates :target_branch, presence: true
validate :validate_branches validate :validate_branches
scope :of_group, ->(group) { where("source_project_id in (:group_project_ids) OR target_project_id in (:group_project_ids)", group_project_ids: group.project_ids) }
scope :of_user_team, ->(team) { where("(source_project_id in (:team_project_ids) OR target_project_id in (:team_project_ids) AND assignee_id in (:team_member_ids))", team_project_ids: team.project_ids, team_member_ids: team.member_ids) }
scope :opened, -> { with_state(:opened) }
scope :closed, -> { with_state(:closed) }
scope :merged, -> { with_state(:merged) } scope :merged, -> { with_state(:merged) }
scope :by_branch, ->(branch_name) { where("source_branch LIKE :branch OR target_branch LIKE :branch", branch: branch_name) } scope :by_branch, ->(branch_name) { where("(source_branch LIKE :branch) OR (target_branch LIKE :branch)", branch: branch_name) }
scope :cared, ->(user) { where('assignee_id = :user OR author_id = :user', user: user.id) } scope :cared, ->(user) { where('assignee_id = :user OR author_id = :user', user: user.id) }
scope :by_milestone, ->(milestone) { where(milestone_id: milestone) } scope :by_milestone, ->(milestone) { where(milestone_id: milestone) }
scope :by_project, ->(project_id) { where("source_project_id = :project_id OR target_project_id = :project_id", project_id: project_id) }
scope :in_projects, ->(project_ids) { where("source_project_id in (:project_ids) OR target_project_id in (:project_ids)", project_ids: project_ids) }
# Closed scope for merge request should return # Closed scope for merge request should return
# both merged and closed mr's # both merged and closed mr's
scope :closed, -> { with_states(:closed, :merged) } scope :closed, -> { with_states(:closed, :merged) }
def validate_branches def validate_branches
if target_branch == source_branch if target_project==source_project && target_branch == source_branch
errors.add :branch_conflict, "You can not use same branch for source and target branches" errors.add :branch_conflict, "You can not use same project/branch for source and target"
end end
if opened? || reopened? if opened? || reopened?
similar_mrs = self.project.merge_requests.where(source_branch: source_branch, target_branch: target_branch).opened similar_mrs = self.target_project.merge_requests.where(source_branch: source_branch, target_branch: target_branch, source_project_id: source_project.id).opened
similar_mrs = similar_mrs.where('id not in (?)', self.id) if self.id similar_mrs = similar_mrs.where('id not in (?)', self.id) if self.id
if similar_mrs.any? if similar_mrs.any?
errors.add :base, "There is already an open merge request for this branches" errors.add :base, "Cannot Create: This merge request already exists: #{similar_mrs.pluck(:title)}"
end end
end end
end end
...@@ -137,7 +149,13 @@ class MergeRequest < ActiveRecord::Base ...@@ -137,7 +149,13 @@ class MergeRequest < ActiveRecord::Base
end end
def unmerged_diffs def unmerged_diffs
project.repository.diffs_between(source_branch, target_branch) if for_fork?
diffs = Gitlab::Satellite::MergeAction.new(author, self).diffs_between_satellite
else
diffs = target_project.repository.diffs_between(source_branch, target_branch)
end
diffs ||= []
diffs
end end
def last_commit def last_commit
...@@ -145,11 +163,11 @@ class MergeRequest < ActiveRecord::Base ...@@ -145,11 +163,11 @@ class MergeRequest < ActiveRecord::Base
end end
def merge_event def merge_event
self.project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::MERGED).last self.target_project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::MERGED).last
end end
def closed_event def closed_event
self.project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::CLOSED).last self.target_project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::CLOSED).last
end end
def commits def commits
...@@ -165,16 +183,24 @@ class MergeRequest < ActiveRecord::Base ...@@ -165,16 +183,24 @@ class MergeRequest < ActiveRecord::Base
if opened? && unmerged_commits.any? if opened? && unmerged_commits.any?
self.st_commits = dump_commits(unmerged_commits) self.st_commits = dump_commits(unmerged_commits)
save save
end end
commits commits
end end
def unmerged_commits def unmerged_commits
self.project.repository. if for_fork?
commits_between(self.target_branch, self.source_branch). commits = Gitlab::Satellite::MergeAction.new(self.author, self).commits_between
else
commits = target_project.repository.commits_between(self.target_branch, self.source_branch)
end
if commits.present?
commits = Commit.decorate(commits).
sort_by(&:created_at). sort_by(&:created_at).
reverse reverse
end end
commits
end
def merge!(user_id) def merge!(user_id)
self.author_id_of_changes = user_id self.author_id_of_changes = user_id
...@@ -199,21 +225,29 @@ class MergeRequest < ActiveRecord::Base ...@@ -199,21 +225,29 @@ class MergeRequest < ActiveRecord::Base
# Returns the raw diff for this merge request # Returns the raw diff for this merge request
# #
# see "git diff" # see "git diff"
def to_diff def to_diff(current_user)
project.repo.git.native(:diff, {timeout: 30, raise: true}, "#{target_branch}...#{source_branch}") Gitlab::Satellite::MergeAction.new(current_user, self).diff_in_satellite
end end
# Returns the commit as a series of email patches. # Returns the commit as a series of email patches.
# #
# see "git format-patch" # see "git format-patch"
def to_patch def to_patch(current_user)
project.repo.git.format_patch({timeout: 30, raise: true, stdout: true}, "#{target_branch}..#{source_branch}") Gitlab::Satellite::MergeAction.new(current_user, self).format_patch
end end
def last_commit_short_sha def last_commit_short_sha
@last_commit_short_sha ||= last_commit.sha[0..10] @last_commit_short_sha ||= last_commit.sha[0..10]
end end
def for_fork?
target_project != source_project
end
def disallow_source_branch_removal?
(source_project.root_ref? source_branch) || for_fork?
end
private private
def dump_commits(commits) def dump_commits(commits)
......
...@@ -42,18 +42,18 @@ class Note < ActiveRecord::Base ...@@ -42,18 +42,18 @@ class Note < ActiveRecord::Base
# Scopes # Scopes
scope :for_commit_id, ->(commit_id) { where(noteable_type: "Commit", commit_id: commit_id) } scope :for_commit_id, ->(commit_id) { where(noteable_type: "Commit", commit_id: commit_id) }
scope :inline, -> { where("line_code IS NOT NULL") } scope :inline, ->{ where("line_code IS NOT NULL") }
scope :not_inline, -> { where(line_code: [nil, '']) } scope :not_inline, ->{ where(line_code: [nil, '']) }
scope :common, ->{ where(noteable_type: ["", nil]) } scope :common, ->{ where(noteable_type: ["", nil]) }
scope :fresh, ->{ order("created_at ASC, id ASC") } scope :fresh, ->{ order("created_at ASC, id ASC") }
scope :inc_author_project, ->{ includes(:project, :author) } scope :inc_author_project, ->{ includes(:project, :author) }
scope :inc_author, ->{ includes(:author) } scope :inc_author, ->{ includes(:author) }
def self.create_status_change_note(noteable, author, status) def self.create_status_change_note(noteable, project, author, status)
create({ create({
noteable: noteable, noteable: noteable,
project: noteable.project, project: project,
author: author, author: author,
note: "_Status changed to #{status}_" note: "_Status changed to #{status}_"
}, without_protection: true) }, without_protection: true)
......
...@@ -53,7 +53,7 @@ class Project < ActiveRecord::Base ...@@ -53,7 +53,7 @@ class Project < ActiveRecord::Base
has_many :services, dependent: :destroy has_many :services, dependent: :destroy
has_many :events, dependent: :destroy has_many :events, dependent: :destroy
has_many :merge_requests, dependent: :destroy has_many :merge_requests, dependent: :destroy, foreign_key: "target_project_id"
has_many :issues, dependent: :destroy, order: "state DESC, created_at DESC" has_many :issues, dependent: :destroy, order: "state DESC, created_at DESC"
has_many :milestones, dependent: :destroy has_many :milestones, dependent: :destroy
has_many :notes, dependent: :destroy has_many :notes, dependent: :destroy
......
class ActivityObserver < BaseObserver class ActivityObserver < BaseObserver
observe :issue, :merge_request, :note, :milestone observe :issue, :note, :milestone
def after_create(record) def after_create(record)
event_author_id = record.author_id event_author_id = record.author_id
...@@ -13,47 +13,27 @@ class ActivityObserver < BaseObserver ...@@ -13,47 +13,27 @@ class ActivityObserver < BaseObserver
end end
if event_author_id if event_author_id
Event.create( create_event(record, Event.determine_action(record))
project: record.project,
target_id: record.id,
target_type: record.class.name,
action: Event.determine_action(record),
author_id: event_author_id
)
end end
end end
def after_close(record, transition) def after_close(record, transition)
Event.create( create_event(record, Event::CLOSED)
project: record.project,
target_id: record.id,
target_type: record.class.name,
action: Event::CLOSED,
author_id: record.author_id_of_changes
)
end end
def after_reopen(record, transition) def after_reopen(record, transition)
Event.create( create_event(record, Event::REOPENED)
project: record.project,
target_id: record.id,
target_type: record.class.name,
action: Event::REOPENED,
author_id: record.author_id_of_changes
)
end end
def after_merge(record, transition) protected
# Since MR can be merged via sidekiq
# to prevent event duplication do this check
return true if record.merge_event
def create_event(record, status)
Event.create( Event.create(
project: record.project, project: record.project,
target_id: record.id, target_id: record.id,
target_type: record.class.name, target_type: record.class.name,
action: Event::MERGED, action: status,
author_id: record.author_id_of_changes author_id: record.author_id
) )
end end
end end
...@@ -23,6 +23,6 @@ class IssueObserver < BaseObserver ...@@ -23,6 +23,6 @@ class IssueObserver < BaseObserver
# Create issue note with service comment like 'Status changed to closed' # Create issue note with service comment like 'Status changed to closed'
def create_note(issue) def create_note(issue)
Note.create_status_change_note(issue, current_user, issue.state) Note.create_status_change_note(issue, issue.project, current_user, issue.state)
end end
end end
class MergeRequestObserver < BaseObserver class MergeRequestObserver < ActivityObserver
observe :merge_request
def after_create(merge_request) def after_create(merge_request)
if merge_request.author_id
create_event(merge_request, Event.determine_action(merge_request))
end
notification.new_merge_request(merge_request, current_user) notification.new_merge_request(merge_request, current_user)
end end
def after_close(merge_request, transition) def after_close(merge_request, transition)
Note.create_status_change_note(merge_request, current_user, merge_request.state) create_event(merge_request, Event::CLOSED)
Note.create_status_change_note(merge_request, merge_request.target_project, current_user, merge_request.state)
notification.close_mr(merge_request, current_user) notification.close_mr(merge_request, current_user)
end end
def after_merge(merge_request, transition) def after_merge(merge_request, transition)
notification.merge_mr(merge_request) notification.merge_mr(merge_request)
# Since MR can be merged via sidekiq
# to prevent event duplication do this check
return true if merge_request.merge_event
Event.create(
project: merge_request.target_project,
target_id: merge_request.id,
target_type: merge_request.class.name,
action: Event::MERGED,
author_id: merge_request.author_id_of_changes
)
end end
def after_reopen(merge_request, transition) def after_reopen(merge_request, transition)
Note.create_status_change_note(merge_request, current_user, merge_request.state) create_event(merge_request, Event::REOPENED)
Note.create_status_change_note(merge_request, merge_request.target_project, current_user, merge_request.state)
end end
def after_update(merge_request) def after_update(merge_request)
notification.reassigned_merge_request(merge_request, current_user) if merge_request.is_being_reassigned? notification.reassigned_merge_request(merge_request, current_user) if merge_request.is_being_reassigned?
end end
def create_event(record, status)
Event.create(
project: record.target_project,
target_id: record.id,
target_type: record.class.name,
action: status,
author_id: record.author_id
)
end
end end
...@@ -23,7 +23,7 @@ class NotificationService ...@@ -23,7 +23,7 @@ class NotificationService
# * project team members with notification level higher then Participating # * project team members with notification level higher then Participating
# #
def new_issue(issue, current_user) def new_issue(issue, current_user)
new_resource_email(issue, 'new_issue_email') new_resource_email(issue, issue.project, 'new_issue_email')
end end
# When we close an issue we should send next emails: # When we close an issue we should send next emails:
...@@ -33,7 +33,7 @@ class NotificationService ...@@ -33,7 +33,7 @@ class NotificationService
# * project team members with notification level higher then Participating # * project team members with notification level higher then Participating
# #
def close_issue(issue, current_user) def close_issue(issue, current_user)
close_resource_email(issue, current_user, 'closed_issue_email') close_resource_email(issue, issue.project, current_user, 'closed_issue_email')
end end
# When we reassign an issue we should send next emails: # When we reassign an issue we should send next emails:
...@@ -42,7 +42,7 @@ class NotificationService ...@@ -42,7 +42,7 @@ class NotificationService
# * issue new assignee if his notification level is not Disabled # * issue new assignee if his notification level is not Disabled
# #
def reassigned_issue(issue, current_user) def reassigned_issue(issue, current_user)
reassign_resource_email(issue, current_user, 'reassigned_issue_email') reassign_resource_email(issue, issue.project, current_user, 'reassigned_issue_email')
end end
...@@ -51,7 +51,7 @@ class NotificationService ...@@ -51,7 +51,7 @@ class NotificationService
# * mr assignee if his notification level is not Disabled # * mr assignee if his notification level is not Disabled
# #
def new_merge_request(merge_request, current_user) def new_merge_request(merge_request, current_user)
new_resource_email(merge_request, 'new_merge_request_email') new_resource_email(merge_request, merge_request.target_project, 'new_merge_request_email')
end end
# When we reassign a merge_request we should send next emails: # When we reassign a merge_request we should send next emails:
...@@ -60,7 +60,7 @@ class NotificationService ...@@ -60,7 +60,7 @@ class NotificationService
# * merge_request assignee if his notification level is not Disabled # * merge_request assignee if his notification level is not Disabled
# #
def reassigned_merge_request(merge_request, current_user) def reassigned_merge_request(merge_request, current_user)
reassign_resource_email(merge_request, current_user, 'reassigned_merge_request_email') reassign_resource_email(merge_request, merge_request.target_project, current_user, 'reassigned_merge_request_email')
end end
# When we close a merge request we should send next emails: # When we close a merge request we should send next emails:
...@@ -70,7 +70,7 @@ class NotificationService ...@@ -70,7 +70,7 @@ class NotificationService
# * project team members with notification level higher then Participating # * project team members with notification level higher then Participating
# #
def close_mr(merge_request, current_user) def close_mr(merge_request, current_user)
close_resource_email(merge_request, current_user, 'closed_merge_request_email') close_resource_email(merge_request, merge_request.target_project, current_user, 'closed_merge_request_email')
end end
# When we merge a merge request we should send next emails: # When we merge a merge request we should send next emails:
...@@ -80,8 +80,8 @@ class NotificationService ...@@ -80,8 +80,8 @@ class NotificationService
# * project team members with notification level higher then Participating # * project team members with notification level higher then Participating
# #
def merge_mr(merge_request) def merge_mr(merge_request)
recipients = reject_muted_users([merge_request.author, merge_request.assignee], merge_request.project) recipients = reject_muted_users([merge_request.author, merge_request.assignee], merge_request.target_project)
recipients = recipients.concat(project_watchers(merge_request.project)).uniq recipients = recipients.concat(project_watchers(merge_request.target_project)).uniq
recipients.each do |recipient| recipients.each do |recipient|
mailer.merged_merge_request_email(recipient.id, merge_request.id) mailer.merged_merge_request_email(recipient.id, merge_request.id)
...@@ -191,14 +191,14 @@ class NotificationService ...@@ -191,14 +191,14 @@ class NotificationService
end end
end end
def new_resource_email(target, method) def new_resource_email(target, project, method)
if target.respond_to?(:participants) if target.respond_to?(:participants)
recipients = target.participants recipients = target.participants
else else
recipients = [] recipients = []
end end
recipients = reject_muted_users(recipients, target.project) recipients = reject_muted_users(recipients, project)
recipients = recipients.concat(project_watchers(target.project)).uniq recipients = recipients.concat(project_watchers(project)).uniq
recipients.delete(target.author) recipients.delete(target.author)
recipients.each do |recipient| recipients.each do |recipient|
...@@ -206,9 +206,9 @@ class NotificationService ...@@ -206,9 +206,9 @@ class NotificationService
end end
end end
def close_resource_email(target, current_user, method) def close_resource_email(target, project, current_user, method)
recipients = reject_muted_users([target.author, target.assignee], target.project) recipients = reject_muted_users([target.author, target.assignee], project)
recipients = recipients.concat(project_watchers(target.project)).uniq recipients = recipients.concat(project_watchers(project)).uniq
recipients.delete(current_user) recipients.delete(current_user)
recipients.each do |recipient| recipients.each do |recipient|
...@@ -216,14 +216,14 @@ class NotificationService ...@@ -216,14 +216,14 @@ class NotificationService
end end
end end
def reassign_resource_email(target, current_user, method) def reassign_resource_email(target, project, current_user, method)
recipients = User.where(id: [target.assignee_id, target.assignee_id_was]) recipients = User.where(id: [target.assignee_id, target.assignee_id_was])
# Add watchers to email list # Add watchers to email list
recipients = recipients.concat(project_watchers(target.project)) recipients = recipients.concat(project_watchers(project))
# reject users with disabled notifications # reject users with disabled notifications
recipients = reject_muted_users(recipients, target.project) recipients = reject_muted_users(recipients, project)
# Reject me from recipients if I reassign an item # Reject me from recipients if I reassign an item
recipients.delete(current_user) recipients.delete(current_user)
......
...@@ -9,6 +9,9 @@ ...@@ -9,6 +9,9 @@
= time_ago_in_words(event.created_at) = time_ago_in_words(event.created_at)
ago. ago.
.pull-right .pull-right
= link_to new_mr_path_from_push_event(event), title: "New Merge Request", class: "btn btn-create btn-small" do = link_to new_mr_path_from_push_event(event), title: "New Merge Request", class: "btn btn-new-mr" do
Create Merge Request Create Merge Request
- if !event.project.nil? && event.project.forked?
= link_to new_mr_path_for_fork_from_push_event(event), title: "New Merge Request", class: "btn btn-create btn-small" do
Create Merge Request on fork
%hr %hr
%p %p
= "Merge Request #{@merge_request.id} was closed by #{@updated_by.name}" = "Merge Request #{@merge_request.id} was closed by #{@updated_by.name}"
%p %p
= link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.project, @merge_request) = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.target_project, @merge_request)
%p %p
Branches: #{@merge_request.source_branch} &rarr; #{@merge_request.target_branch} != merge_path_description(@merge_request, '&rarr;')
%p %p
Assignee: #{@merge_request.author_name} &rarr; #{@merge_request.assignee_name} Assignee: #{@merge_request.author_name} &rarr; #{@merge_request.assignee_name}
= "Merge Request #{@merge_request.id} was closed by #{@updated_by.name}" = "Merge Request #{@merge_request.id} was closed by #{@updated_by.name}"
Merge Request url: #{project_merge_request_url(@merge_request.project, @merge_request)} Merge Request url: #{project_merge_request_url(@merge_request.target_project, @merge_request)}
Branches: #{@merge_request.source_branch} - #{@merge_request.target_branch} = merge_path_description(@merge_request, 'to')
Author: #{@merge_request.author_name} Author: #{@merge_request.author_name}
Assignee: #{@merge_request.assignee_name} Assignee: #{@merge_request.assignee_name}
%p %p
= "Merge Request #{@merge_request.id} was merged" = "Merge Request #{@merge_request.id} was merged"
%p %p
= link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.project, @merge_request) = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.target_project, @merge_request)
%p %p
Branches: #{@merge_request.source_branch} &rarr; #{@merge_request.target_branch} != merge_path_description(@merge_request, '&rarr;')
%p %p
Assignee: #{@merge_request.author_name} &rarr; #{@merge_request.assignee_name} Assignee: #{@merge_request.author_name} &rarr; #{@merge_request.assignee_name}
= "Merge Request #{@merge_request.id} was merged" = "Merge Request #{@merge_request.id} was merged"
Merge Request Url: #{project_merge_request_url(@merge_request.project, @merge_request)} Merge Request Url: #{project_merge_request_url(@merge_request.target_project, @merge_request)}
Branches: #{@merge_request.source_branch} - #{@merge_request.target_branch} = merge_path_description(@merge_request, 'to')
Author: #{@merge_request.author_name} Author: #{@merge_request.author_name}
Assignee: #{@merge_request.assignee_name} Assignee: #{@merge_request.assignee_name}
%p %p
= "New Merge Request !#{@merge_request.id}" = "New Merge Request !#{@merge_request.id}"
%p %p
= link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.project, @merge_request) = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.target_project, @merge_request)
%p %p
Branches: #{@merge_request.source_branch} &rarr; #{@merge_request.target_branch} != merge_path_description(@merge_request, '&rarr;')
%p %p
Assignee: #{@merge_request.author_name} &rarr; #{@merge_request.assignee_name} Assignee: #{@merge_request.author_name} &rarr; #{@merge_request.assignee_name}
New Merge Request <%= @merge_request.id %> New Merge Request <%= @merge_request.id %>
<%= url_for(project_merge_request_url(@merge_request.project, @merge_request)) %> <%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request)) %>
<%= merge_path_description(@merge_request, 'to') %>
Branches: <%= @merge_request.source_branch %> to <%= @merge_request.target_branch %>
Author: <%= @merge_request.author_name %> Author: <%= @merge_request.author_name %>
Asignee: <%= @merge_request.assignee_name %> Asignee: <%= @merge_request.assignee_name %>
%p %p
- if @note.for_diff_line? - if @note.for_diff_line?
= link_to "New comment on diff", diffs_project_merge_request_url(@merge_request.project, @merge_request, anchor: "note_#{@note.id}") = link_to "New comment on diff", diffs_project_merge_request_url(@merge_request.target_project, @merge_request, anchor: "note_#{@note.id}")
- else - else
= link_to "New comment", project_merge_request_url(@merge_request.project, @merge_request, anchor: "note_#{@note.id}") = link_to "New comment", project_merge_request_url(@merge_request.target_project, @merge_request, anchor: "note_#{@note.id}")
for Merge Request ##{@merge_request.id} for Merge Request ##{@merge_request.id}
%cite "#{truncate(@merge_request.title, length: 20)}" %cite "#{truncate(@merge_request.title, length: 20)}"
= render 'note_message' = render 'note_message'
New comment for Merge Request <%= @merge_request.id %> New comment for Merge Request <%= @merge_request.id %>
<%= url_for(project_merge_request_url(@merge_request.project, @merge_request, anchor: "note_#{@note.id}")) %> <%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request, anchor: "note_#{@note.id}")) %>
<%= @note.author_name %> <%= @note.author_name %>
......
%p %p
= "Reassigned Merge Request !#{@merge_request.id}" = "Reassigned Merge Request !#{@merge_request.id}"
= link_to_gfm truncate(@merge_request.title, length: 30), project_merge_request_url(@merge_request.project, @merge_request) = link_to_gfm truncate(@merge_request.title, length: 30), project_merge_request_url(@merge_request.target_project, @merge_request)
%p %p
Assignee changed Assignee changed
- if @previous_assignee - if @previous_assignee
......
Reassigned Merge Request <%= @merge_request.id %> Reassigned Merge Request <%= @merge_request.id %>
<%= url_for(project_merge_request_url(@merge_request.project, @merge_request)) %> <%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request)) %>
Assignee changed <%= "from #{@previous_assignee.name}" if @previous_assignee %> to <%= @merge_request.assignee_name %> Assignee changed <%= "from #{@previous_assignee.name}" if @previous_assignee %> to <%= @merge_request.assignee_name %>
......
= render "commit_box" = render "commit_box"
= render "projects/commits/diffs", diffs: @commit.diffs = render "projects/commits/diffs", diffs: @commit.diffs, project: @project
= render "projects/notes/notes_with_form" = render "projects/notes/notes_with_form"
%li.commit %li.commit
.browse_code_link_holder .browse_code_link_holder
%p %p
%strong= link_to "Browse Code »", project_tree_path(@project, commit), class: "right" %strong= link_to "Browse Code »", project_tree_path(project, commit), class: "right"
%p %p
= link_to commit.short_id(8), project_commit_path(@project, commit), class: "commit_short_id" = link_to commit.short_id(8), project_commit_path(project, commit), class: "commit_short_id"
= commit_author_link(commit, avatar: true, size: 24) = commit_author_link(commit, avatar: true, size: 24)
&nbsp; &nbsp;
= link_to_gfm truncate(commit.title, length: 70), project_commit_path(@project, commit.id), class: "row_title" = link_to_gfm truncate(commit.title, length: 70), project_commit_path(project, commit.id), class: "row_title"
%time.committed_ago{ datetime: commit.committed_date, title: commit.committed_date.stamp("Aug 21, 2011 9:23pm") } %time.committed_ago{ datetime: commit.committed_date, title: commit.committed_date.stamp("Aug 21, 2011 9:23pm") }
= time_ago_in_words(commit.committed_date) = time_ago_in_words(commit.committed_date)
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
&nbsp; &nbsp;
%span.notes_count %span.notes_count
- notes = @project.notes.for_commit_id(commit.id) - notes = project.notes.for_commit_id(commit.id)
- if notes.any? - if notes.any?
%span.badge.badge-info %span.badge.badge-info
%i.icon-comment %i.icon-comment
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
.title .title
%i.icon-calendar %i.icon-calendar
%span= day.stamp("28 Aug, 2010") %span= day.stamp("28 Aug, 2010")
.pull-right .pull-right
%small= pluralize(commits.count, 'commit') %small= pluralize(commits.count, 'commit')
%ul.well-list= render commits %ul.well-list= render commits, project: @project
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
%p To prevent performance issue we rejected diff information. %p To prevent performance issue we rejected diff information.
%p %p
But if you still want to see diff But if you still want to see diff
= link_to "click this link", project_commit_path(@project, @commit, force_show_diff: true), class: "underlined_link" = link_to "click this link", project_commit_path(project, @commit, force_show_diff: true), class: "underlined_link"
%p.commit-stat-summary %p.commit-stat-summary
Showing Showing
...@@ -23,8 +23,8 @@ ...@@ -23,8 +23,8 @@
- unless @suppress_diff - unless @suppress_diff
- diffs.each_with_index do |diff, i| - diffs.each_with_index do |diff, i|
- next if diff.diff.empty? - next if diff.diff.empty?
- file = Gitlab::Git::Blob.new(@repository, @commit.id, @ref, diff.new_path) - file = Gitlab::Git::Blob.new(project.repository, @commit.id, @ref, diff.new_path)
- file = Gitlab::Git::Blob.new(@repository, @commit.parent_id, @ref, diff.old_path) unless file.exists? - file = Gitlab::Git::Blob.new(project.repository, @commit.parent_id, @ref, diff.old_path) unless file.exists?
- next unless file.exists? - next unless file.exists?
.file{id: "diff-#{i}"} .file{id: "diff-#{i}"}
.header .header
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
%span= diff.old_path %span= diff.old_path
- if @commit.parent_ids.present? - if @commit.parent_ids.present?
= link_to project_blob_path(@project, tree_join(@commit.parent_id, diff.new_path)), {:class => 'btn btn-tiny pull-right view-file'} do = link_to project_blob_path(project, tree_join(@commit.parent_id, diff.new_path)), {:class => 'btn btn-tiny pull-right view-file'} do
View file @ View file @
%span.commit-short-id= @commit.short_id(6) %span.commit-short-id= @commit.short_id(6)
- else - else
...@@ -40,7 +40,7 @@ ...@@ -40,7 +40,7 @@
- if diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode - if diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode
%span.file-mode= "#{diff.a_mode}#{diff.b_mode}" %span.file-mode= "#{diff.a_mode}#{diff.b_mode}"
= link_to project_blob_path(@project, tree_join(@commit.id, diff.new_path)), {:class => 'btn btn-tiny pull-right view-file'} do = link_to project_blob_path(project, tree_join(@commit.id, diff.new_path)), {:class => 'btn btn-tiny pull-right view-file'} do
View file @ View file @
%span.commit-short-id= @commit.short_id(6) %span.commit-short-id= @commit.short_id(6)
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
- if file.text? - if file.text?
= render "projects/commits/text_file", diff: diff, index: i = render "projects/commits/text_file", diff: diff, index: i
- elsif file.image? - elsif file.image?
- old_file = Gitlab::Git::Blob.new(@repository, @commit.parent_id, @ref, diff.old_path) if @commit.parent_id - old_file = Gitlab::Git::Blob.new(project.repository, @commit.parent_id, @ref, diff.old_path) if @commit.parent_id
= render "projects/commits/image", diff: diff, old_file: old_file, file: file, index: i = render "projects/commits/image", diff: diff, old_file: old_file, file: file, index: i
- else - else
%p.nothing_here_message No preview for this file type %p.nothing_here_message No preview for this file type
...@@ -15,8 +15,8 @@ ...@@ -15,8 +15,8 @@
%div.ui-box %div.ui-box
.title .title
Commits (#{@commits.count}) Commits (#{@commits.count})
%ul.well-list= render Commit.decorate(@commits) %ul.well-list= render Commit.decorate(@commits), project: @project
- unless @diffs.empty? - unless @diffs.empty?
%h4 Diff %h4 Diff
= render "projects/commits/diffs", diffs: @diffs = render "projects/commits/diffs", diffs: @diffs, project: @project
...@@ -12,17 +12,19 @@ ...@@ -12,17 +12,19 @@
.row .row
.span5 .span5
.light-well .light-well
%h5.cgray From (Head Branch) %h5.cgray From
= f.select(:source_branch, @repository.branch_names, { include_blank: "Select branch" }, {class: 'chosen span4'}) .padded= f.select(:source_project_id,[[@merge_request.source_project.path_with_namespace,@merge_request.source_project.id]] , {}, {class: 'source_project chosen span4'})
.padded= f.select(:source_branch, @merge_request.source_project.repository.branch_names, { include_blank: "Select branch" }, {class: 'source_branch chosen span4'})
.mr_source_commit.prepend-top-10 .mr_source_commit.prepend-top-10
.span2 .span2
%h1.merge-request-angle %h1.merge-request-angle
%i.icon-angle-right %i.icon-angle-right
.span5 .span5
.light-well .light-well
%h5.cgray To (Base Branch) %h5.cgray To
= f.select(:target_branch, @repository.branch_names, { include_blank: "Select branch" }, {class: 'chosen span4'}) - projects = @project.forked_from_project.nil? ? [@project] : [ @project,@project.forked_from_project]
.padded= f.select(:target_project_id, projects.map { |proj| [proj.path_with_namespace,proj.id] }, {include_blank: "Select Target Project" }, {class: 'target_project chosen span4'})
.padded= f.select(:target_branch, @target_branches, { include_blank: "Select branch" }, {class: 'target_branch chosen span4'})
.mr_target_commit.prepend-top-10 .mr_target_commit.prepend-top-10
%hr %hr
...@@ -47,32 +49,34 @@ ...@@ -47,32 +49,34 @@
Milestone Milestone
.input= f.select(:milestone_id, @project.milestones.active.all.map {|p| [ p.title, p.id ] }, { include_blank: "Select milestone" }, {class: 'chosen'}) .input= f.select(:milestone_id, @project.milestones.active.all.map {|p| [ p.title, p.id ] }, { include_blank: "Select milestone" }, {class: 'chosen'})
.form-actions .form-actions
- if @merge_request.new_record? - if @merge_request.new_record?
= f.submit 'Submit merge request', class: "btn btn-create" = f.submit 'Submit merge request', class: "btn btn-create"
-else -else
= f.submit 'Save changes', class: "btn btn-save" = f.submit 'Save changes', class: "btn btn-save"
- if @merge_request.new_record? - if @merge_request.new_record?
= link_to project_merge_requests_path(@project), class: "btn btn-cancel" do = link_to project_merge_requests_path(@source_project), class: "btn btn-cancel" do
Cancel Cancel
- else - else
= link_to project_merge_request_path(@project, @merge_request), class: "btn btn-cancel" do = link_to project_merge_request_path(@target_project, @merge_request), class: "btn btn-cancel" do
Cancel Cancel
:javascript :javascript
disableButtonIfEmptyField("#merge_request_title", ".btn-save"); disableButtonIfEmptyField("#merge_request_title", ".btn-save");
var source_branch = $("#merge_request_source_branch") var source_branch = $("#merge_request_source_branch")
, target_branch = $("#merge_request_target_branch"); , target_branch = $("#merge_request_target_branch")
, target_project = $("#merge_request_target_project_id");
$.get("#{branch_from_project_merge_requests_path(@project)}", {ref: source_branch.val() }); $.get("#{branch_from_project_merge_requests_path(@source_project)}", {ref: source_branch.val() });
$.get("#{branch_to_project_merge_requests_path(@project)}", {ref: target_branch.val() }); $.get("#{branch_to_project_merge_requests_path(@source_project)}", {target_project_id: target_project.val(),ref: target_branch.val() });
source_branch.live("change", function() { target_project.on("change", function() {
$.get("#{branch_from_project_merge_requests_path(@project)}", {ref: $(this).val() }); $.get("#{update_branches_project_merge_requests_path(@source_project)}", {target_project_id: $(this).val() });
}); });
source_branch.on("change", function() {
target_branch.live("change", function() { $.get("#{branch_from_project_merge_requests_path(@source_project)}", {ref: $(this).val() });
$.get("#{branch_to_project_merge_requests_path(@project)}", {ref: $(this).val() }); });
target_branch.on("change", function() {
$.get("#{branch_to_project_merge_requests_path(@source_project)}", {target_project_id: target_project.val(),ref: $(this).val() });
}); });
%li{ class: mr_css_classes(merge_request) } %li{ class: mr_css_classes(merge_request) }
.merge-request-title .merge-request-title
%span.light= "##{merge_request.id}" %span.light= "##{merge_request.id}"
= link_to_gfm truncate(merge_request.title, length: 80), project_merge_request_path(merge_request.project, merge_request), class: "row_title" = link_to_gfm truncate(merge_request.title, length: 80), project_merge_request_path(merge_request.target_project, merge_request), class: "row_title"
- if merge_request.merged? - if merge_request.merged?
%small.pull-right %small.pull-right
%i.icon-ok %i.icon-ok
= "MERGED" = "MERGED"
- else - else
%span.pull-right %span.pull-right
- if merge_request.for_fork?
= "#{merge_request.source_project.path_with_namespace}/#{merge_request.source_branch}"
%i.icon-angle-right %i.icon-angle-right
= merge_request.target_branch = "#{merge_request.target_project.path_with_namespace}/#{merge_request.target_branch}"
- else
= "#{merge_request.source_branch}"
%i.icon-angle-right
= "#{merge_request.target_branch}"
.merge-request-info .merge-request-info
- if merge_request.author - if merge_request.author
authored by #{link_to_member(@project, merge_request.author)} authored by #{link_to_member(merge_request.source_project, merge_request.author)}
- if merge_request.votes_count > 0 - if merge_request.votes_count > 0
= render 'votes/votes_inline', votable: merge_request = render 'votes/votes_inline', votable: merge_request
- if merge_request.notes.any? - if merge_request.notes.any?
......
:plain :plain
$(".mr_source_commit").html("#{commit_to_html(@commit)}"); $(".mr_source_commit").html("#{commit_to_html(@commit, @source_project)}");
var mrTitle = $('#merge_request_title'); var mrTitle = $('#merge_request_title');
if(mrTitle.is(":empty")) { if(mrTitle.is(":empty")) {
......
:plain :plain
$(".mr_target_commit").html("#{commit_to_html(@commit)}"); $(".mr_target_commit").html("#{commit_to_html(@commit, @target_project)}");
...@@ -7,19 +7,19 @@ ...@@ -7,19 +7,19 @@
- if @commits.count > 8 - if @commits.count > 8
%ul.first-commits.well-list %ul.first-commits.well-list
- @commits.first(8).each do |commit| - @commits.first(8).each do |commit|
= render "projects/commits/commit", commit: commit = render "projects/commits/commit", commit: commit, project: @merge_request.source_project
%li.bottom %li.bottom
8 of #{@commits.count} commits displayed. 8 of #{@commits.count} commits displayed.
%strong %strong
%a.show-all-commits Click here to show all %a.show-all-commits Click here to show all
%ul.all-commits.hide.well-list %ul.all-commits.hide.well-list
- @commits.each do |commit| - @commits.each do |commit|
= render "projects/commits/commit", commit: commit = render "projects/commits/commit", commit: commit, project: @merge_request.source_project
- else - else
%ul.well-list %ul.well-list
- @commits.each do |commit| - @commits.each do |commit|
= render "projects/commits/commit", commit: commit = render "projects/commits/commit", commit: commit, project: @merge_request.source_project
- else - else
%h4.nothing_here_message %h4.nothing_here_message
......
- if @merge_request.valid_diffs? - if @merge_request.valid_diffs?
= render "projects/commits/diffs", diffs: @merge_request.diffs = render "projects/commits/diffs", diffs: @merge_request.diffs, project: @merge_request.source_project
- elsif @merge_request.broken_diffs? - elsif @merge_request.broken_diffs?
%h4.nothing_here_message %h4.nothing_here_message
Can't load diff. Can't load diff.
You can You can
= link_to "download it", project_merge_request_path(@project, @merge_request, format: :diff), class: "vlink" = link_to "download it", project_merge_request_path(@merge_request.source_project, @merge_request), format: :diff, class: "vlink"
instead. instead.
- else - else
%h4.nothing_here_message Nothing to merge %h4.nothing_here_message Nothing to merge
...@@ -3,6 +3,26 @@ ...@@ -3,6 +3,26 @@
%a.close{href: "#"} × %a.close{href: "#"} ×
%h3 How To Merge %h3 How To Merge
.modal-body .modal-body
- if @merge_request.for_fork?
- source_remote = @merge_request.source_project.namespace.nil? ? "source" :@merge_request.source_project.namespace.path
- target_remote = @merge_request.target_project.namespace.nil? ? "target" :@merge_request.target_project.namespace.path
%p
%strong Step 1.
Checkout target branch and get recent objects from GitLab
Assuming remote for #{@merge_request.target_project.path_with_namespace} is called #{target_remote}
remote for #{@merge_request.source_project.path_with_namespace} is called #{source_remote}
%pre.dark
:preserve
git checkout #{target_remote} #{@merge_request.target_branch}
git fetch #{source_remote}
%p
%strong Step 2.
Merge source branch into target branch and push changes to GitLab
%pre.dark
:preserve
git merge #{source_remote}/#{@merge_request.source_branch}
git push #{target_remote} #{@merge_request.target_branch}
- else
%p %p
%strong Step 1. %strong Step 1.
Checkout target branch and get recent objects from GitLab Checkout target branch and get recent objects from GitLab
...@@ -17,3 +37,15 @@ ...@@ -17,3 +37,15 @@
:preserve :preserve
git merge origin/#{@merge_request.source_branch} git merge origin/#{@merge_request.source_branch}
git push origin #{@merge_request.target_branch} git push origin #{@merge_request.target_branch}
:javascript
$(function(){
var modal = $('#modal_merge_info').modal({modal: true, show:false});
$('.how_to_merge_link').bind("click", function(){
modal.show();
});
$('.modal-header .close').bind("click", function(){
modal.hide();
})
})
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
for instructions for instructions
.accept_group .accept_group
= f.submit "Accept Merge Request", class: "btn btn-create accept_merge_request" = f.submit "Accept Merge Request", class: "btn btn-create accept_merge_request"
- unless @project.root_ref? @merge_request.source_branch - unless @merge_request.disallow_source_branch_removal?
.remove_branch_holder .remove_branch_holder
= label_tag :should_remove_source_branch, class: "checkbox" do = label_tag :should_remove_source_branch, class: "checkbox" do
= check_box_tag :should_remove_source_branch = check_box_tag :should_remove_source_branch
......
%h3.page-title %h3.page-title
= "Merge Request ##{@merge_request.id}:" = "Merge Request ##{@merge_request.id}:"
&nbsp; &nbsp;
-if @merge_request.for_fork?
%span.label-project= @merge_request.source_project.path_with_namespace
%span.label-branch= @merge_request.source_branch
&rarr;
%span.label-project= @merge_request.target_project.path_with_namespace
%span.label-branch= @merge_request.target_branch
- else
%span.label-branch= @merge_request.source_branch %span.label-branch= @merge_request.source_branch
&rarr; &rarr;
%span.label-branch= @merge_request.target_branch %span.label-branch= @merge_request.target_branch
...@@ -19,7 +26,7 @@ ...@@ -19,7 +26,7 @@
= link_to 'Close', project_merge_request_path(@project, @merge_request, merge_request: {state_event: :close }), method: :put, class: "btn grouped btn-close", title: "Close merge request" = link_to 'Close', project_merge_request_path(@project, @merge_request, merge_request: {state_event: :close }), method: :put, class: "btn grouped btn-close", title: "Close merge request"
= link_to edit_project_merge_request_path(@project, @merge_request), class: "btn grouped" do = link_to edit_project_merge_request_path(@project, @merge_request), class: "btn grouped", id:"edit_merge_request" do
%i.icon-edit %i.icon-edit
Edit Edit
......
:plain
$(".target_branch").html("#{escape_javascript(options_for_select(@target_branches))}");
$(".target_branch").trigger("liszt:updated");
$(".mr_target_commit").html("");
$(".target_branch").trigger("change");
\ No newline at end of file
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
discussion on this merge request diff discussion on this merge request diff
- elsif note.for_commit? - elsif note.for_commit?
started a discussion on commit started a discussion on commit
#{link_to note.noteable.short_id, project_commit_path(@project, note.noteable)} #{link_to note.noteable.short_id, project_commit_path(note.project, note.noteable)}
= link_to_commit_diff_line_note(note) if note.for_diff_line? = link_to_commit_diff_line_note(note) if note.for_diff_line?
- else - else
%cite.cgray started a discussion %cite.cgray started a discussion
......
...@@ -22,11 +22,14 @@ ...@@ -22,11 +22,14 @@
- @merge_requests.each do |merge_request| - @merge_requests.each do |merge_request|
%li %li
merge request: merge request:
= link_to [merge_request.project, merge_request] do = link_to [merge_request.target_project, merge_request] do
%span ##{merge_request.id} %span ##{merge_request.id}
%strong.term %strong.term
= truncate merge_request.title, length: 50 = truncate merge_request.title, length: 50
%span.light (#{merge_request.project.name_with_namespace}) - if merge_request.for_fork?
%span.light (#{merge_request.source_project.name_with_namespace}:#{merge_request.source_branch} &rarr; #{merge_request.target_project.name_with_namespace}:#{merge_request.target_branch})
- else
%span.light (#{merge_request.source_branch} &rarr; #{merge_request.target_branch})
- @issues.each do |issue| - @issues.each do |issue|
%li %li
issue: issue:
......
- if @merge_requests.any? - if @merge_requests.any?
- @merge_requests.group_by(&:project).each do |group| - @merge_requests.group_by(&:target_project).each do |group|
.ui-box .ui-box
- project = group[0] - project = group[0]
.title .title
......
...@@ -257,6 +257,7 @@ Gitlab::Application.routes.draw do ...@@ -257,6 +257,7 @@ Gitlab::Application.routes.draw do
collection do collection do
get :branch_from get :branch_from
get :branch_to get :branch_to
get :update_branches
end end
end end
......
...@@ -23,7 +23,8 @@ Gitlab::Seeder.quiet do ...@@ -23,7 +23,8 @@ Gitlab::Seeder.quiet do
id: i, id: i,
source_branch: branches.first, source_branch: branches.first,
target_branch: branches.last, target_branch: branches.last,
project_id: project.id, source_project_id: project.id,
target_project_id: project.id,
author_id: user_id, author_id: user_id,
assignee_id: user_id, assignee_id: user_id,
milestone: project.milestones.sample, milestone: project.milestones.sample,
......
...@@ -19,5 +19,18 @@ FileUtils.cd(REPO_PATH) do ...@@ -19,5 +19,18 @@ FileUtils.cd(REPO_PATH) do
# Remove the copy # Remove the copy
FileUtils.rm(SEED_REPO) FileUtils.rm(SEED_REPO)
end end
puts ' done.'
print "Creating seed satellite..."
SATELLITE_PATH = Rails.root.join('tmp', 'satellite')
# Make directory
FileUtils.mkdir_p(SATELLITE_PATH)
# Clear any potential directory
FileUtils.rm_rf("#{SATELLITE_PATH}/gitlabhq")
# Chdir, clone from the seed
FileUtils.cd(SATELLITE_PATH) do
# Clone the satellite
`git clone --quiet #{REPO_PATH}/gitlabhq #{SATELLITE_PATH}/gitlabhq`
end
puts ' done.' puts ' done.'
class AllowMergesForForks < ActiveRecord::Migration
def self.up
add_column :merge_requests, :target_project_id, :integer, :null => false
MergeRequest.update_all("target_project_id = project_id")
rename_column :merge_requests, :project_id, :source_project_id
end
def self.down
remove_column :merge_requests, :target_project_id
rename_column :merge_requests, :source_project_id,:project_id
end
end
...@@ -86,7 +86,7 @@ ActiveRecord::Schema.define(:version => 20130624162710) do ...@@ -86,7 +86,7 @@ ActiveRecord::Schema.define(:version => 20130624162710) do
create_table "merge_requests", :force => true do |t| create_table "merge_requests", :force => true do |t|
t.string "target_branch", :null => false t.string "target_branch", :null => false
t.string "source_branch", :null => false t.string "source_branch", :null => false
t.integer "project_id", :null => false t.integer "source_project_id", :null => false
t.integer "author_id" t.integer "author_id"
t.integer "assignee_id" t.integer "assignee_id"
t.string "title" t.string "title"
...@@ -97,14 +97,15 @@ ActiveRecord::Schema.define(:version => 20130624162710) do ...@@ -97,14 +97,15 @@ ActiveRecord::Schema.define(:version => 20130624162710) do
t.integer "milestone_id" t.integer "milestone_id"
t.string "state" t.string "state"
t.string "merge_status" t.string "merge_status"
t.integer "target_project_id", :null => false
end end
add_index "merge_requests", ["assignee_id"], :name => "index_merge_requests_on_assignee_id" add_index "merge_requests", ["assignee_id"], :name => "index_merge_requests_on_assignee_id"
add_index "merge_requests", ["author_id"], :name => "index_merge_requests_on_author_id" add_index "merge_requests", ["author_id"], :name => "index_merge_requests_on_author_id"
add_index "merge_requests", ["created_at"], :name => "index_merge_requests_on_created_at" add_index "merge_requests", ["created_at"], :name => "index_merge_requests_on_created_at"
add_index "merge_requests", ["milestone_id"], :name => "index_merge_requests_on_milestone_id" add_index "merge_requests", ["milestone_id"], :name => "index_merge_requests_on_milestone_id"
add_index "merge_requests", ["project_id"], :name => "index_merge_requests_on_project_id"
add_index "merge_requests", ["source_branch"], :name => "index_merge_requests_on_source_branch" add_index "merge_requests", ["source_branch"], :name => "index_merge_requests_on_source_branch"
add_index "merge_requests", ["source_project_id"], :name => "index_merge_requests_on_project_id"
add_index "merge_requests", ["target_branch"], :name => "index_merge_requests_on_target_branch" add_index "merge_requests", ["target_branch"], :name => "index_merge_requests_on_target_branch"
add_index "merge_requests", ["title"], :name => "index_merge_requests_on_title" add_index "merge_requests", ["title"], :name => "index_merge_requests_on_title"
......
...@@ -16,6 +16,7 @@ Feature: Dashboard ...@@ -16,6 +16,7 @@ Feature: Dashboard
And I visit dashboard page And I visit dashboard page
Then I should see groups list Then I should see groups list
@javascript
Scenario: I should see last push widget Scenario: I should see last push widget
Then I should see last push widget Then I should see last push widget
And I click "Create Merge Request" link And I click "Create Merge Request" link
......
Feature: Project Forked Merge Requests
Background:
Given I sign in as a user
And I am a member of project "Shop"
And I have a project forked off of "Shop" called "Forked Shop"
@javascript
Scenario: I can visit the target projects commit for a forked merge request
Given I visit project "Forked Shop" merge requests page
And I click link "New Merge Request"
And I fill out a "Merge Request On Forked Project" merge request
And I follow the target commit link
Then I should see the commit under the forked from project
@javascript
Scenario: I submit new unassigned merge request to a forked project
Given I visit project "Forked Shop" merge requests page
And I click link "New Merge Request"
And I fill out a "Merge Request On Forked Project" merge request
And I submit the merge request
Then I should see merge request "Merge Request On Forked Project"
@javascript
Scenario: I should see a push widget for forked merge requests
Given project "Forked Shop" has push event
And I visit dashboard page
Then I should see last push widget
And I click "Create Merge Request on fork" link
Then I see prefilled new Merge Request page for the forked project
@javascript
Scenario: I can edit a forked merge request
Given I visit project "Forked Shop" merge requests page
And I click link "New Merge Request"
And I fill out a "Merge Request On Forked Project" merge request
And I submit the merge request
And I should see merge request "Merge Request On Forked Project"
And I click link edit "Merge Request On Forked Project"
Then I see the edit page prefilled for "Merge Request On Forked Project"
And I update the merge request title
And I save the merge request
Then I should see the edited merge request
@javascript
Scenario: I cannot submit an invalid merge request
Given I visit project "Forked Shop" merge requests page
And I click link "New Merge Request"
And I fill out an invalid "Merge Request On Forked Project" merge request
And I submit the merge request
Then I should see validation errors
\ No newline at end of file
...@@ -29,6 +29,7 @@ Feature: Project Merge Requests ...@@ -29,6 +29,7 @@ Feature: Project Merge Requests
And I click link "Close" And I click link "Close"
Then I should see closed merge request "Bug NS-04" Then I should see closed merge request "Bug NS-04"
@javascript
Scenario: I submit new unassigned merge request Scenario: I submit new unassigned merge request
Given I click link "New Merge Request" Given I click link "New Merge Request"
And I submit new merge request "Wiki Feature" And I submit new merge request "Wiki Feature"
......
...@@ -22,6 +22,7 @@ class Dashboard < Spinach::FeatureSteps ...@@ -22,6 +22,7 @@ class Dashboard < Spinach::FeatureSteps
Then 'I see prefilled new Merge Request page' do Then 'I see prefilled new Merge Request page' do
current_path.should == new_project_merge_request_path(@project) current_path.should == new_project_merge_request_path(@project)
find("#merge_request_target_project_id").value.should == @project.id.to_s
find("#merge_request_source_branch").value.should == "new_design" find("#merge_request_source_branch").value.should == "new_design"
find("#merge_request_target_branch").value.should == "master" find("#merge_request_target_branch").value.should == "master"
find("#merge_request_title").value.should == "New Design" find("#merge_request_title").value.should == "New Design"
......
...@@ -61,7 +61,7 @@ class EventFilters < Spinach::FeatureSteps ...@@ -61,7 +61,7 @@ class EventFilters < Spinach::FeatureSteps
end end
And 'this project has merge request event' do And 'this project has merge request event' do
merge_request = create :merge_request, author: @user, project: @project merge_request = create :merge_request, author: @user, source_project: @project, target_project: @project
Event.create( Event.create(
project: @project, project: @project,
action: Event::MERGED, action: Event::MERGED,
......
...@@ -6,18 +6,24 @@ class DashboardMergeRequests < Spinach::FeatureSteps ...@@ -6,18 +6,24 @@ class DashboardMergeRequests < Spinach::FeatureSteps
merge_requests = @user.merge_requests merge_requests = @user.merge_requests
merge_requests.each do |mr| merge_requests.each do |mr|
page.should have_content(mr.title[0..10]) page.should have_content(mr.title[0..10])
page.should have_content(mr.project.name) page.should have_content(mr.target_project.name)
page.should have_content(mr.source_project.name)
end end
end end
And 'I have authored merge requests' do And 'I have authored merge requests' do
project1 = create :project project1_source = create :project
project2 = create :project project1_target= create :project
project2_source = create :project
project2_target = create :project
project1.team << [@user, :master]
project2.team << [@user, :master]
merge_request1 = create :merge_request, author: @user, project: project1 project1_source.team << [@user, :master]
merge_request2 = create :merge_request, author: @user, project: project2 project1_target.team << [@user, :master]
project2_source.team << [@user, :master]
project2_target.team << [@user, :master]
merge_request1 = create :merge_request, author: @user, source_project: project1_source, target_project: project1_target
merge_request2 = create :merge_request, author: @user, source_project: project2_source, target_project: project2_target
end end
end end
...@@ -60,7 +60,8 @@ class Groups < Spinach::FeatureSteps ...@@ -60,7 +60,8 @@ class Groups < Spinach::FeatureSteps
Given 'project from group has merge requests assigned to me' do Given 'project from group has merge requests assigned to me' do
create :merge_request, create :merge_request,
project: project, source_project: project,
target_project: project,
assignee: current_user, assignee: current_user,
author: current_user author: current_user
end end
......
...@@ -4,6 +4,8 @@ class ForkProject < Spinach::FeatureSteps ...@@ -4,6 +4,8 @@ class ForkProject < Spinach::FeatureSteps
include SharedProject include SharedProject
step 'I click link "Fork"' do step 'I click link "Fork"' do
page.should have_content "Shop"
page.should have_content "Fork"
Gitlab::Shell.any_instance.stub(:fork_repository).and_return(true) Gitlab::Shell.any_instance.stub(:fork_repository).and_return(true)
click_link "Fork" click_link "Fork"
end end
...@@ -17,9 +19,13 @@ class ForkProject < Spinach::FeatureSteps ...@@ -17,9 +19,13 @@ class ForkProject < Spinach::FeatureSteps
step 'I should see the forked project page' do step 'I should see the forked project page' do
page.should have_content "Project was successfully forked." page.should have_content "Project was successfully forked."
current_path.should include current_user.namespace.path current_path.should include current_user.namespace.path
@forked_project = Project.find_by_namespace_id(current_user.namespace.path)
end end
step 'I already have a project named "Shop" in my namespace' do step 'I already have a project named "Shop" in my namespace' do
current_user.namespace ||= create(:namespace)
current_user.namespace.should_not be_nil
current_user.namespace.path.should_not be_nil
@my_project = create(:project_with_code, name: "Shop", namespace: current_user.namespace) @my_project = create(:project_with_code, name: "Shop", namespace: current_user.namespace)
end end
......
class ProjectForkedMergeRequests < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedNote
include SharedPaths
Given 'I am a member of project "Shop"' do
@project = Project.find_by_name "Shop"
@project ||= create(:project_with_code, name: "Shop")
@project.team << [@user, :reporter]
end
And 'I have a project forked off of "Shop" called "Forked Shop"' do
@forking_user = @user
forked_project_link = build(:forked_project_link)
@forked_project = Project.find_by_name "Forked Shop"
@forked_project ||= create(:source_project_with_code, name: "Forked Shop", forked_project_link: forked_project_link, creator_id: @forking_user.id , namespace: @forking_user.namespace)
forked_project_link.forked_from_project = @project
forked_project_link.forked_to_project = @forked_project
@forked_project.team << [@forking_user , :master]
forked_project_link.save!
end
Given 'I click link "New Merge Request"' do
click_link "New Merge Request"
end
Then 'I should see merge request "Merge Request On Forked Project"' do
page.should have_content "Merge Request On Forked Project"
@project.merge_requests.size.should >= 1
@merge_request = @project.merge_requests.last
current_path.should == project_merge_request_path(@project, @merge_request)
@merge_request.title.should == "Merge Request On Forked Project"
@merge_request.source_project.should == @forked_project
@merge_request.source_branch.should == "master"
@merge_request.target_branch.should == "stable"
page.should have_content @forked_project.path_with_namespace
page.should have_content @project.path_with_namespace
page.should have_content @merge_request.source_branch
page.should have_content @merge_request.target_branch
end
And 'I fill out a "Merge Request On Forked Project" merge request' do
#The ordering here is a bit whacky on purpose:
#Select the target right away, to give update_branches time to run and clean up the target_branches
find(:select, "merge_request_target_project_id", {}).value.should == @forked_project.id.to_s
select @project.path_with_namespace, from: "merge_request_target_project_id"
fill_in "merge_request_title", with: "Merge Request On Forked Project"
find(:select, "merge_request_source_project_id", {}).value.should == @forked_project.id.to_s
find(:select, "merge_request_target_project_id", {}).value.should == @project.id.to_s
#Ensure the option exists in the select
find(:select, "merge_request_source_branch", {}).should have_content "master"
select "master", from: "merge_request_source_branch"
#Ensure the option is selected
find(:select, "merge_request_source_branch", {}).value.should have_content "master"
verify_commit_link(".mr_source_commit",@forked_project)
#This could fail if the javascript hasn't run yet, there is a timing issue here -- this is why we do the select at the top
#Ensure the option exists in the select
find(:select, "merge_request_target_branch", {}).should have_content "stable"
#We must give apparently lots of time for update branches to finish
(find(:select, "merge_request_target_branch", {}).find(:option, "stable",{}).select_option).should be_true
#Ensure the option is selected
find(:select, "merge_request_target_branch", {}).value.should have_content "stable"
verify_commit_link(".mr_target_commit",@project)
end
And 'I submit the merge request' do
click_button "Submit merge request"
end
And 'I follow the target commit link' do
commit = @project.repository.commit
click_link commit.short_id(8)
end
Then 'I should see the commit under the forked from project' do
commit = @project.repository.commit
page.should have_content(commit.message)
end
And 'I click "Create Merge Request on fork" link' do
click_link "Create Merge Request on fork"
end
Then 'I see prefilled new Merge Request page for the forked project' do
current_path.should == new_project_merge_request_path(@forked_project)
find("#merge_request_source_project_id").value.should == @forked_project.id.to_s
find("#merge_request_target_project_id").value.should == @project.id.to_s
find("#merge_request_source_branch").value.should have_content "new_design"
find("#merge_request_target_branch").value.should have_content "master"
find("#merge_request_title").value.should == "New Design"
verify_commit_link(".mr_target_commit",@project)
verify_commit_link(".mr_source_commit",@forked_project)
end
And 'I update the merge request title' do
fill_in "merge_request_title", with: "An Edited Forked Merge Request"
end
And 'I save the merge request' do
click_button "Save changes"
end
Then 'I should see the edited merge request' do
page.should have_content "An Edited Forked Merge Request"
@project.merge_requests.size.should >= 1
@merge_request = @project.merge_requests.last
current_path.should == project_merge_request_path(@project, @merge_request)
@merge_request.source_project.should == @forked_project
@merge_request.source_branch.should == "master"
@merge_request.target_branch.should == "stable"
page.should have_content @forked_project.path_with_namespace
page.should have_content @project.path_with_namespace
page.should have_content @merge_request.source_branch
page.should have_content @merge_request.target_branch
end
Then 'I should see last push widget' do
page.should have_content "You pushed to new_design"
page.should have_link "Create Merge Request"
end
Given 'project "Forked Shop" has push event' do
@forked_project = Project.find_by_name("Forked Shop")
data = {
before: "0000000000000000000000000000000000000000",
after: "0220c11b9a3e6c69dc8fd35321254ca9a7b98f7e",
ref: "refs/heads/new_design",
user_id: @user.id,
user_name: @user.name,
repository: {
name: @forked_project.name,
url: "localhost/rubinius",
description: "",
homepage: "localhost/rubinius",
private: true
}
}
@event = Event.create(
project: @forked_project,
action: Event::PUSHED,
data: data,
author_id: @user.id
)
end
Then 'I click link edit "Merge Request On Forked Project"' do
find("#edit_merge_request").click
end
Then 'I see the edit page prefilled for "Merge Request On Forked Project"' do
current_path.should == edit_project_merge_request_path(@project, @merge_request)
page.should have_content "Edit merge request #{@merge_request.id}"
find("#merge_request_title").value.should == "Merge Request On Forked Project"
find("#merge_request_source_project_id").value.should == @forked_project.id.to_s
find("#merge_request_target_project_id").value.should == @project.id.to_s
find("#merge_request_source_branch").value.should have_content "master"
verify_commit_link(".mr_source_commit",@forked_project)
find("#merge_request_target_branch").value.should have_content "stable"
verify_commit_link(".mr_target_commit",@project)
end
And 'I fill out an invalid "Merge Request On Forked Project" merge request' do
#If this isn't filled in the rest of the validations won't be triggered
fill_in "merge_request_title", with: "Merge Request On Forked Project"
find(:select, "merge_request_source_project_id", {}).value.should == @forked_project.id.to_s
find(:select, "merge_request_target_project_id", {}).value.should == @forked_project.id.to_s
find(:select, "merge_request_source_branch", {}).value.should == ""
find(:select, "merge_request_target_branch", {}).value.should == ""
end
Then 'I should see validation errors' do
page.should have_content "Source branch can't be blank"
page.should have_content "Target branch can't be blank"
page.should have_content "Branch conflict You can not use same project/branch for source and target"
end
def project
@project ||= Project.find_by_name!("Shop")
end
#Verify a link is generated against the correct project
def verify_commit_link(container_div, container_project)
#This should force a wait for the javascript to execute
find(:div,container_div).should have_css ".browse_code_link_holder"
find(:div,container_div).find(".commit_short_id")['href'].should have_content "#{container_project.path_with_namespace}/commit"
end
end
...@@ -56,30 +56,41 @@ class ProjectMergeRequests < Spinach::FeatureSteps ...@@ -56,30 +56,41 @@ class ProjectMergeRequests < Spinach::FeatureSteps
end end
And 'I submit new merge request "Wiki Feature"' do And 'I submit new merge request "Wiki Feature"' do
fill_in "merge_request_title", with: "Wiki Feature" #this must come first, so that the target branch is set by the time the "select" for "notes_refactoring" is executed
select "bootstrap", from: "merge_request_source_branch" select project.path_with_namespace, :from => "merge_request_target_project_id"
select "master", from: "merge_request_target_branch" fill_in "merge_request_title", :with => "Wiki Feature"
select "master", :from => "merge_request_source_branch"
find(:select, "merge_request_target_project_id", {}).value.should == project.id.to_s
find(:select, "merge_request_source_project_id", {}).value.should == project.id.to_s
#using "notes_refactoring" because "Bug NS-04" uses master/stable, this will fail merge_request validation if the branches are the same
find(:select, "merge_request_target_branch", {}).find(:option, "notes_refactoring", {}).value.should == "notes_refactoring"
select "notes_refactoring", :from => "merge_request_target_branch"
click_button "Submit merge request" click_button "Submit merge request"
end end
And 'project "Shop" have "Bug NS-04" open merge request' do And 'project "Shop" have "Bug NS-04" open merge request' do
create(:merge_request, create(:merge_request,
title: "Bug NS-04", title: "Bug NS-04",
project: project, source_project: project,
target_project: project,
author: project.users.first) author: project.users.first)
end end
And 'project "Shop" have "Bug NS-05" open merge request with diffs inside' do And 'project "Shop" have "Bug NS-05" open merge request with diffs inside' do
create(:merge_request_with_diffs, create(:merge_request_with_diffs,
title: "Bug NS-05", title: "Bug NS-05",
project: project, source_project: project,
target_project: project,
author: project.users.first) author: project.users.first)
end end
And 'project "Shop" have "Feature NS-03" closed merge request' do And 'project "Shop" have "Feature NS-03" closed merge request' do
create(:closed_merge_request, create(:closed_merge_request,
title: "Feature NS-03", title: "Feature NS-03",
project: project, source_project: project,
target_project: project,
author: project.users.first) author: project.users.first)
end end
......
...@@ -184,6 +184,10 @@ module SharedPaths ...@@ -184,6 +184,10 @@ module SharedPaths
visit project_path(project) visit project_path(project)
end end
step 'I visit project "Forked Shop" merge requests page' do
visit project_merge_requests_path(@forked_project)
end
step 'I visit edit project "Shop" page' do step 'I visit edit project "Shop" page' do
visit edit_project_path(project) visit edit_project_path(project)
end end
...@@ -239,18 +243,22 @@ module SharedPaths ...@@ -239,18 +243,22 @@ module SharedPaths
step 'I visit merge request page "Bug NS-04"' do step 'I visit merge request page "Bug NS-04"' do
mr = MergeRequest.find_by_title("Bug NS-04") mr = MergeRequest.find_by_title("Bug NS-04")
visit project_merge_request_path(mr.project, mr) visit project_merge_request_path(mr.target_project, mr)
end end
step 'I visit merge request page "Bug NS-05"' do step 'I visit merge request page "Bug NS-05"' do
mr = MergeRequest.find_by_title("Bug NS-05") mr = MergeRequest.find_by_title("Bug NS-05")
visit project_merge_request_path(mr.project, mr) visit project_merge_request_path(mr.target_project, mr)
end end
step 'I visit project "Shop" merge requests page' do step 'I visit project "Shop" merge requests page' do
visit project_merge_requests_path(project) visit project_merge_requests_path(project)
end end
step 'I visit forked project "Shop" merge requests page' do
visit project_merge_requests_path(project)
end
step 'I visit project "Shop" milestones page' do step 'I visit project "Shop" milestones page' do
visit project_milestones_path(project) visit project_milestones_path(project)
end end
......
...@@ -35,8 +35,7 @@ Capybara.ignore_hidden_elements = false ...@@ -35,8 +35,7 @@ Capybara.ignore_hidden_elements = false
DatabaseCleaner.strategy = :truncation DatabaseCleaner.strategy = :truncation
Spinach.hooks.before_scenario do Spinach.hooks.before_scenario do
TestEnv.init(mailer: false) TestEnv.setup_stubs
DatabaseCleaner.start DatabaseCleaner.start
end end
...@@ -45,6 +44,7 @@ Spinach.hooks.after_scenario do ...@@ -45,6 +44,7 @@ Spinach.hooks.after_scenario do
end end
Spinach.hooks.before_run do Spinach.hooks.before_run do
TestEnv.init(mailer: false, init_repos: true, repos: false)
RSpec::Mocks::setup self RSpec::Mocks::setup self
include FactoryGirl::Syntax::Methods include FactoryGirl::Syntax::Methods
......
...@@ -14,6 +14,14 @@ module API ...@@ -14,6 +14,14 @@ module API
end end
not_found! not_found!
end end
def not_fork?(target_project_id, user_project)
target_project_id.nil? || target_project_id == user_project.id.to_s
end
def target_matches_fork(target_project_id,user_project)
user_project.forked? && user_project.forked_from_project.id.to_s == target_project_id
end
end end
# List merge requests # List merge requests
...@@ -51,9 +59,10 @@ module API ...@@ -51,9 +59,10 @@ module API
# #
# Parameters: # Parameters:
# #
# id (required) - The ID of a project # id (required) - The ID of a project - this will be the source of the merge request
# source_branch (required) - The source branch # source_branch (required) - The source branch
# target_branch (required) - The target branch # target_branch (required) - The target branch
# target_project - The target project of the merge request defaults to the :id of the project
# assignee_id - Assignee user ID # assignee_id - Assignee user ID
# title (required) - Title of MR # title (required) - Title of MR
# #
...@@ -63,10 +72,20 @@ module API ...@@ -63,10 +72,20 @@ module API
post ":id/merge_requests" do post ":id/merge_requests" do
authorize! :write_merge_request, user_project authorize! :write_merge_request, user_project
required_attributes! [:source_branch, :target_branch, :title] required_attributes! [:source_branch, :target_branch, :title]
attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :target_project_id]
attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title]
merge_request = user_project.merge_requests.new(attrs) merge_request = user_project.merge_requests.new(attrs)
merge_request.author = current_user merge_request.author = current_user
merge_request.source_project = user_project
target_project_id = attrs[:target_project_id]
if not_fork?(target_project_id, user_project)
merge_request.target_project = user_project
else
if target_matches_fork(target_project_id,user_project)
merge_request.target_project = Project.find_by_id(attrs[:target_project_id])
else
render_api_error!('(Bad Request) Specified target project that is not the source project, or the source fork of the project.', 400)
end
end
if merge_request.save if merge_request.save
merge_request.reload_code merge_request.reload_code
......
...@@ -25,25 +25,31 @@ module Gitlab ...@@ -25,25 +25,31 @@ module Gitlab
end end
end end
rescue Errno::ENOMEM => ex rescue Errno::ENOMEM => ex
Gitlab::GitLogger.error(ex.message) return handle_exception(ex)
return false
rescue Grit::Git::GitTimeout => ex rescue Grit::Git::GitTimeout => ex
Gitlab::GitLogger.error(ex.message) return handle_exception(ex)
return false
ensure ensure
Gitlab::ShellEnv.reset_env Gitlab::ShellEnv.reset_env
end end
# * Clears the satellite # * Recreates the satellite
# * Updates the satellite from Gitolite
# * Sets up Git variables for the user # * Sets up Git variables for the user
# #
# Note: use this within #in_locked_and_timed_satellite # Note: use this within #in_locked_and_timed_satellite
def prepare_satellite!(repo) def prepare_satellite!(repo)
project.satellite.clear_and_update! project.satellite.clear_and_update!
repo.git.config({}, "user.name", user.name) repo.config['user.name'] = user.name
repo.git.config({}, "user.email", user.email) repo.config['user.email'] = user.email
end
def default_options(options = {})
{raise: true, timeout: true}.merge(options)
end
def handle_exception(exception)
Gitlab::GitLogger.error(exception.message)
false
end end
end end
end end
......
...@@ -5,48 +5,118 @@ module Gitlab ...@@ -5,48 +5,118 @@ module Gitlab
attr_accessor :merge_request attr_accessor :merge_request
def initialize(user, merge_request) def initialize(user, merge_request)
super user, merge_request.project super user, merge_request.target_project
@merge_request = merge_request @merge_request = merge_request
end end
# Checks if a merge request can be executed without user interaction # Checks if a merge request can be executed without user interaction
def can_be_merged? def can_be_merged?
in_locked_and_timed_satellite do |merge_repo| in_locked_and_timed_satellite do |merge_repo|
prepare_satellite!(merge_repo)
merge_in_satellite!(merge_repo) merge_in_satellite!(merge_repo)
end end
end end
# Merges the source branch into the target branch in the satellite and # Merges the source branch into the target branch in the satellite and
# pushes it back to Gitolite. # pushes it back to the repository.
# It also removes the source branch if requested in the merge request. # It also removes the source branch if requested in the merge request (and this is permitted by the merge request).
# #
# Returns false if the merge produced conflicts # Returns false if the merge produced conflicts
# Returns false if pushing from the satellite to Gitolite failed or was rejected # Returns false if pushing from the satellite to the repository failed or was rejected
# Returns true otherwise # Returns true otherwise
def merge! def merge!
in_locked_and_timed_satellite do |merge_repo| in_locked_and_timed_satellite do |merge_repo|
prepare_satellite!(merge_repo)
if merge_in_satellite!(merge_repo) if merge_in_satellite!(merge_repo)
# push merge back to Gitolite # push merge back to Gitolite
# will raise CommandFailed when push fails # will raise CommandFailed when push fails
merge_repo.git.push({raise: true, timeout: true}, :origin, merge_request.target_branch) merge_repo.git.push(default_options, :origin, merge_request.target_branch)
# remove source branch # remove source branch
if merge_request.should_remove_source_branch && !project.root_ref?(merge_request.source_branch) if merge_request.should_remove_source_branch && !project.root_ref?(merge_request.source_branch)
# will raise CommandFailed when push fails # will raise CommandFailed when push fails
merge_repo.git.push({raise: true, timeout: true}, :origin, ":#{merge_request.source_branch}") merge_repo.git.push(default_options, :origin, ":#{merge_request.source_branch}")
end end
# merge, push and branch removal successful # merge, push and branch removal successful
true true
end end
end end
rescue Grit::Git::CommandFailed => ex rescue Grit::Git::CommandFailed => ex
Gitlab::GitLogger.error(ex.message) handle_exception(ex)
false
end end
private # Get a raw diff of the source to the target
def diff_in_satellite
in_locked_and_timed_satellite do |merge_repo|
prepare_satellite!(merge_repo)
update_satellite_source_and_target!(merge_repo)
if merge_request.for_fork?
diff = merge_repo.git.native(:diff, default_options, "origin/#{merge_request.target_branch}", "source/#{merge_request.source_branch}")
else
diff = merge_repo.git.native(:diff, default_options, "#{merge_request.target_branch}", "#{merge_request.source_branch}")
end
return diff
end
rescue Grit::Git::CommandFailed => ex
handle_exception(ex)
end
# Only show what is new in the source branch compared to the target branch, not the other way around.
# The line below with merge_base is equivalent to diff with three dots (git diff branch1...branch2)
# From the git documentation: "git diff A...B" is equivalent to "git diff $(git-merge-base A B) B"
def diffs_between_satellite
in_locked_and_timed_satellite do |merge_repo|
prepare_satellite!(merge_repo)
update_satellite_source_and_target!(merge_repo)
if merge_request.for_fork?
common_commit = merge_repo.git.native(:merge_base, default_options, ["origin/#{merge_request.target_branch}", "source/#{merge_request.source_branch}"]).strip
#this method doesn't take default options
diffs = merge_repo.diff(common_commit, "source/#{merge_request.source_branch}")
else
raise "Attempt to determine diffs between for a non forked merge request in satellite MergeRequest.id:[#{merge_request.id}]"
end
diffs = diffs.map { |diff| Gitlab::Git::Diff.new(diff) }
return diffs
end
rescue Grit::Git::CommandFailed => ex
handle_exception(ex)
end
# Get commit as an email patch
def format_patch
in_locked_and_timed_satellite do |merge_repo|
prepare_satellite!(merge_repo)
update_satellite_source_and_target!(merge_repo)
if (merge_request.for_fork?)
patch = merge_repo.git.format_patch(default_options({stdout: true}), "origin/#{merge_request.target_branch}...source/#{merge_request.source_branch}")
else
patch = merge_repo.git.format_patch(default_options({stdout: true}), "#{merge_request.target_branch}...#{merge_request.source_branch}")
end
return patch
end
rescue Grit::Git::CommandFailed => ex
handle_exception(ex)
end
# Retrieve an array of commits between the source and the target
def commits_between
in_locked_and_timed_satellite do |merge_repo|
prepare_satellite!(merge_repo)
update_satellite_source_and_target!(merge_repo)
if (merge_request.for_fork?)
commits = merge_repo.commits_between("origin/#{merge_request.target_branch}", "source/#{merge_request.source_branch}")
else
raise "Attempt to determine commits between for a non forked merge request in satellite MergeRequest.id:[#{merge_request.id}]"
end
commits = commits.map { |commit| Gitlab::Git::Commit.new(commit, nil) }
return commits
end
rescue Grit::Git::CommandFailed => ex
handle_exception(ex)
end
private
# Merges the source_branch into the target_branch in the satellite. # Merges the source_branch into the target_branch in the satellite.
# #
# Note: it will clear out the satellite before doing anything # Note: it will clear out the satellite before doing anything
...@@ -54,18 +124,35 @@ module Gitlab ...@@ -54,18 +124,35 @@ module Gitlab
# Returns false if the merge produced conflicts # Returns false if the merge produced conflicts
# Returns true otherwise # Returns true otherwise
def merge_in_satellite!(repo) def merge_in_satellite!(repo)
prepare_satellite!(repo) update_satellite_source_and_target!(repo)
# create target branch in satellite at the corresponding commit from Gitolite
repo.git.checkout({raise: true, timeout: true, b: true}, merge_request.target_branch, "origin/#{merge_request.target_branch}")
# merge the source branch from Gitolite into the satellite # merge the source branch into the satellite
# will raise CommandFailed when merge fails # will raise CommandFailed when merge fails
repo.git.pull({raise: true, timeout: true, no_ff: true}, :origin, merge_request.source_branch) if merge_request.for_fork?
repo.git.pull(default_options({no_ff: true}), 'source', merge_request.source_branch)
else
repo.git.pull(default_options({no_ff: true}), 'origin', merge_request.source_branch)
end
rescue Grit::Git::CommandFailed => ex rescue Grit::Git::CommandFailed => ex
Gitlab::GitLogger.error(ex.message) handle_exception(ex)
false
end end
# Assumes a satellite exists that is a fresh clone of the projects repo, prepares satellite for merges, diffs etc
def update_satellite_source_and_target!(repo)
if merge_request.for_fork?
repo.remote_add('source', merge_request.source_project.repository.path_to_repo)
repo.remote_fetch('source')
repo.git.checkout(default_options({b: true}), merge_request.target_branch, "origin/#{merge_request.target_branch}")
else
# We can't trust the input here being branch names, we can't always check it out because it could be a relative ref i.e. HEAD~3
# we could actually remove the if true, because it should never ever happen (as long as the satellite has been prepared)
repo.git.checkout(default_options, "#{merge_request.source_branch}")
repo.git.checkout(default_options, "#{merge_request.target_branch}")
end
rescue Grit::Git::CommandFailed => ex
handle_exception(ex)
end
end end
end end
end end
...@@ -24,8 +24,11 @@ module Gitlab ...@@ -24,8 +24,11 @@ module Gitlab
def clear_and_update! def clear_and_update!
raise_no_satellite unless exists? raise_no_satellite unless exists?
File.exists? path
@repo = nil
clear_working_dir! clear_working_dir!
delete_heads! delete_heads!
remove_remotes!
update_from_source! update_from_source!
end end
...@@ -55,10 +58,11 @@ module Gitlab ...@@ -55,10 +58,11 @@ module Gitlab
raise_no_satellite unless exists? raise_no_satellite unless exists?
File.open(lock_file, "w+") do |f| File.open(lock_file, "w+") do |f|
f.flock(File::LOCK_EX) begin
f.flock File::LOCK_EX
Dir.chdir(path) do Dir.chdir(path) { return yield }
return yield ensure
f.flock File::LOCK_UN
end end
end end
end end
...@@ -100,20 +104,34 @@ module Gitlab ...@@ -100,20 +104,34 @@ module Gitlab
if heads.include? PARKING_BRANCH if heads.include? PARKING_BRANCH
repo.git.checkout({}, PARKING_BRANCH) repo.git.checkout({}, PARKING_BRANCH)
else else
repo.git.checkout({b: true}, PARKING_BRANCH) repo.git.checkout(default_options({b: true}), PARKING_BRANCH)
end end
# remove the parking branch from the list of heads ... # remove the parking branch from the list of heads ...
heads.delete(PARKING_BRANCH) heads.delete(PARKING_BRANCH)
# ... and delete all others # ... and delete all others
heads.each { |head| repo.git.branch({D: true}, head) } heads.each { |head| repo.git.branch(default_options({D: true}), head) }
end
# Deletes all remotes except origin
#
# This ensures we have no remote name clashes or issues updating branches when
# working with the satellite.
def remove_remotes!
remotes = repo.git.remote.split(' ')
remotes.delete('origin')
remotes.each { |name| repo.git.remote(default_options,'rm', name)}
end end
# Updates the satellite from Gitolite # Updates the satellite from Gitolite
# #
# Note: this will only update remote branches (i.e. origin/*) # Note: this will only update remote branches (i.e. origin/*)
def update_from_source! def update_from_source!
repo.git.fetch({timeout: true}, :origin) repo.git.fetch(default_options, :origin)
end
def default_options(options = {})
{raise: true, timeout: true}.merge(options)
end end
# Create directory for storing # Create directory for storing
......
require 'spec_helper'
describe FilterContext do
let(:user) { create :user }
let(:user2) { create :user }
let(:project1) { create(:project, creator_id: user.id) }
let(:project2) { create(:project, creator_id: user.id) }
let(:merge_request1) { create(:merge_request, author_id: user.id, source_project: project1, target_project: project2) }
let(:merge_request2) { create(:merge_request, author_id: user.id, source_project: project2, target_project: project1) }
let(:merge_request3) { create(:merge_request, author_id: user.id, source_project: project2, target_project: project2) }
let(:merge_request4) { create(:merge_request, author_id: user2.id, source_project: project2, target_project: project2, target_branch:"notes_refactoring") }
let(:issue1) { create(:issue, assignee_id: user.id, project: project1) }
let(:issue2) { create(:issue, assignee_id: user.id, project: project2) }
let(:issue3) { create(:issue, assignee_id: user2.id, project: project2) }
describe 'merge requests' do
before :each do
merge_request1
merge_request2
merge_request3
merge_request4
end
it 'should by default filter properly' do
merge_requests = user.cared_merge_requests
params ={}
merge_requests = FilterContext.new(merge_requests, params).execute
merge_requests.size.should == 3
end
it 'should apply blocks passed in on creation to the filters' do
merge_requests = user.cared_merge_requests
params = {:project_id => project1.id}
merge_requests = FilterContext.new(merge_requests, params).execute
merge_requests.size.should == 2
end
end
describe 'issues' do
before :each do
issue1
issue2
issue3
end
it 'should by default filter projects properly' do
issues = user.assigned_issues
params = {}
issues = FilterContext.new(issues, params).execute
issues.size.should == 2
end
it 'should apply blocks passed in on creation to the filters' do
issues = user.assigned_issues
params = {:project_id => project1.id}
issues = FilterContext.new(issues, params).execute
issues.size.should == 1
end
end
end
\ No newline at end of file
...@@ -7,7 +7,6 @@ describe Projects::CommitController do ...@@ -7,7 +7,6 @@ describe Projects::CommitController do
before do before do
sign_in(user) sign_in(user)
project.team << [user, :master] project.team << [user, :master]
end end
......
...@@ -3,7 +3,7 @@ require 'spec_helper' ...@@ -3,7 +3,7 @@ require 'spec_helper'
describe Projects::MergeRequestsController do describe Projects::MergeRequestsController do
let(:project) { create(:project_with_code) } let(:project) { create(:project_with_code) }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:merge_request) { create(:merge_request_with_diffs, project: project, target_branch: "bcf03b5d~3", source_branch: "bcf03b5d") } let(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project, target_branch: "bcf03b5d~3", source_branch: "bcf03b5d") }
before do before do
sign_in(user) sign_in(user)
...@@ -28,7 +28,7 @@ describe Projects::MergeRequestsController do ...@@ -28,7 +28,7 @@ describe Projects::MergeRequestsController do
it "should render it" do it "should render it" do
get :show, project_id: project.code, id: merge_request.id, format: format get :show, project_id: project.code, id: merge_request.id, format: format
expect(response.body).to eq(merge_request.send(:"to_#{format}")) expect(response.body).to eq((merge_request.send(:"to_#{format}",user)).to_s)
end end
it "should not escape Html" do it "should not escape Html" do
......
...@@ -29,8 +29,19 @@ FactoryGirl.define do ...@@ -29,8 +29,19 @@ FactoryGirl.define do
sequence(:name) { |n| "project#{n}" } sequence(:name) { |n| "project#{n}" }
path { name.downcase.gsub(/\s/, '_') } path { name.downcase.gsub(/\s/, '_') }
creator creator
trait :source do
sequence(:name) { |n| "source project#{n}" }
end
trait :target do
sequence(:name) { |n| "target project#{n}" }
end
factory :source_project, traits: [:source]
factory :target_project, traits: [:target]
end end
factory :redmine_project, parent: :project do factory :redmine_project, parent: :project do
issues_tracker { "redmine" } issues_tracker { "redmine" }
issues_tracker_id { "project_name_in_redmine" } issues_tracker_id { "project_name_in_redmine" }
...@@ -39,11 +50,20 @@ FactoryGirl.define do ...@@ -39,11 +50,20 @@ FactoryGirl.define do
factory :project_with_code, parent: :project do factory :project_with_code, parent: :project do
path { 'gitlabhq' } path { 'gitlabhq' }
trait :source_path do
path { 'source_gitlabhq' }
end
trait :target_path do
path { 'target_gitlabhq' }
end
factory :source_project_with_code, traits: [:source, :source_path]
factory :target_project_with_code, traits: [:target, :target_path]
after :create do |project| after :create do |project|
repos_path = Rails.root.join('tmp', 'test-git-base-path') TestEnv.clear_repo_dir(project.namespace, project.path)
seed_repo = Rails.root.join('tmp', 'repositories', 'gitlabhq') TestEnv.create_repo(project.namespace, project.path)
target_repo = File.join(repos_path, project.path_with_namespace + '.git')
system("ln -s #{seed_repo} #{target_repo}")
end end
end end
...@@ -86,7 +106,8 @@ FactoryGirl.define do ...@@ -86,7 +106,8 @@ FactoryGirl.define do
factory :merge_request do factory :merge_request do
title title
author author
project factory: :project_with_code source_project factory: :source_project_with_code
target_project factory: :target_project_with_code
source_branch "master" source_branch "master"
target_branch "stable" target_branch "stable"
...@@ -96,13 +117,13 @@ FactoryGirl.define do ...@@ -96,13 +117,13 @@ FactoryGirl.define do
source_branch "stable" # pretend bcf03b5d source_branch "stable" # pretend bcf03b5d
st_commits do st_commits do
[ [
project.repository.commit('bcf03b5d').to_hash, source_project.repository.commit('bcf03b5d').to_hash,
project.repository.commit('bcf03b5d~1').to_hash, source_project.repository.commit('bcf03b5d~1').to_hash,
project.repository.commit('bcf03b5d~2').to_hash source_project.repository.commit('bcf03b5d~2').to_hash
] ]
end end
st_diffs do st_diffs do
project.repo.diff("bcf03b5d~3", "bcf03b5d") source_project.repo.diff("bcf03b5d~3", "bcf03b5d")
end end
end end
......
...@@ -3,7 +3,7 @@ require 'spec_helper' ...@@ -3,7 +3,7 @@ require 'spec_helper'
describe "GitLab Flavored Markdown" do describe "GitLab Flavored Markdown" do
let(:project) { create(:project_with_code) } let(:project) { create(:project_with_code) }
let(:issue) { create(:issue, project: project) } let(:issue) { create(:issue, project: project) }
let(:merge_request) { create(:merge_request, project: project) } let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
let(:fred) do let(:fred) do
u = create(:user, name: "fred") u = create(:user, name: "fred")
project.team << [u, :master] project.team << [u, :master]
...@@ -83,9 +83,7 @@ describe "GitLab Flavored Markdown" do ...@@ -83,9 +83,7 @@ describe "GitLab Flavored Markdown" do
describe "for merge requests" do describe "for merge requests" do
before do before do
@merge_request = create(:merge_request, @merge_request = create(:merge_request, source_project: project, target_project: project, title: "fix ##{issue.id}")
project: project,
title: "fix ##{issue.id}")
end end
it "should render title in merge_requests#index" do it "should render title in merge_requests#index" do
......
...@@ -2,7 +2,7 @@ require 'spec_helper' ...@@ -2,7 +2,7 @@ require 'spec_helper'
describe "On a merge request", js: true do describe "On a merge request", js: true do
let!(:project) { create(:project_with_code) } let!(:project) { create(:project_with_code) }
let!(:merge_request) { create(:merge_request, project: project) } let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
let!(:note) { create(:note_on_merge_request_with_attachment, project: project) } let!(:note) { create(:note_on_merge_request_with_attachment, project: project) }
before do before do
...@@ -135,8 +135,8 @@ describe "On a merge request", js: true do ...@@ -135,8 +135,8 @@ describe "On a merge request", js: true do
end end
describe "On a merge request diff", js: true, focus: true do describe "On a merge request diff", js: true, focus: true do
let!(:project) { create(:project_with_code) } let!(:project) { create(:source_project_with_code) }
let!(:merge_request) { create(:merge_request_with_diffs, project: project) } let!(:merge_request) { create(:merge_request_with_diffs, source_project: project, target_project: project) }
before do before do
login_as :user login_as :user
...@@ -144,6 +144,7 @@ describe "On a merge request diff", js: true, focus: true do ...@@ -144,6 +144,7 @@ describe "On a merge request diff", js: true, focus: true do
visit diffs_project_merge_request_path(project, merge_request) visit diffs_project_merge_request_path(project, merge_request)
end end
subject { page } subject { page }
describe "when adding a note" do describe "when adding a note" do
...@@ -183,6 +184,9 @@ describe "On a merge request diff", js: true, focus: true do ...@@ -183,6 +184,9 @@ describe "On a merge request diff", js: true, focus: true do
end end
describe "with muliple note forms" do describe "with muliple note forms" do
let!(:project) { create(:source_project_with_code) }
let!(:merge_request) { create(:merge_request_with_diffs, source_project: project, target_project: project) }
before do before do
find('a[data-line-code="4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185"]').click find('a[data-line-code="4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185"]').click
find('a[data-line-code="342e16cbbd482ac2047dc679b2749d248cc1428f_18_17"]').click find('a[data-line-code="342e16cbbd482ac2047dc679b2749d248cc1428f_18_17"]').click
...@@ -238,11 +242,10 @@ describe "On a merge request diff", js: true, focus: true do ...@@ -238,11 +242,10 @@ describe "On a merge request diff", js: true, focus: true do
# TODO: fix # TODO: fix
#it "should remove last note of a discussion" do #it "should remove last note of a discussion" do
#within("tr[id='342e16cbbd482ac2047dc679b2749d248cc1428f_18_17'] + .notes-holder") do # within("tr[id='342e16cbbd482ac2047dc679b2749d248cc1428f_18_17'] + .notes-holder") do
#find(".js-note-delete").click # find(".js-note-delete").click
#end # end
# should_not have_css(".note_holder")
#should_not have_css(".note_holder")
#end #end
end end
end end
...@@ -251,26 +254,26 @@ describe "On a merge request diff", js: true, focus: true do ...@@ -251,26 +254,26 @@ describe "On a merge request diff", js: true, focus: true do
#describe "when replying to a note" do #describe "when replying to a note" do
#before do #before do
## create first note ## create first note
#find('a[data-line-code="4735dfc552ad7bf15ca468adc3cad9d05b624490_184_184"]').click # find('a[data-line-code="4735dfc552ad7bf15ca468adc3cad9d05b624490_184_184"]').click
#within(".js-temp-notes-holder") do # within(".js-temp-notes-holder") do
#fill_in "note[note]", with: "One comment on line 184" # fill_in "note[note]", with: "One comment on line 184"
#click_button("Add Comment") # click_button("Add Comment")
#end #end
#within(".js-temp-notes-holder") do # within(".js-temp-notes-holder") do
#find(".js-discussion-reply-button").click # find(".js-discussion-reply-button").click
#fill_in "note[note]", with: "An additional comment in reply" # fill_in "note[note]", with: "An additional comment in reply"
#click_button("Add Comment") # click_button("Add Comment")
#end # end
#end #end
#it 'should be inserted and form removed from reply' do #it 'should be inserted and form removed from reply' do
#should have_content("An additional comment in reply") # should have_content("An additional comment in reply")
#within(".notes_holder") { should have_css(".note", count: 2) } # within(".notes_holder") { should have_css(".note", count: 2) }
#within(".notes_holder") { should have_no_css("form") } # within(".notes_holder") { should have_no_css("form") }
#within(".notes_holder") { should have_link("Reply") } # within(".notes_holder") { should have_link("Reply") }
#end # end
#end #end
end end
......
...@@ -2,6 +2,7 @@ require 'spec_helper' ...@@ -2,6 +2,7 @@ require 'spec_helper'
describe "Profile account page" do describe "Profile account page" do
before(:each) { enable_observers } before(:each) { enable_observers }
after(:each) {disable_observers}
let(:user) { create(:user) } let(:user) { create(:user) }
before do before do
......
...@@ -2,6 +2,7 @@ require 'spec_helper' ...@@ -2,6 +2,7 @@ require 'spec_helper'
describe "Projects" do describe "Projects" do
before(:each) { enable_observers } before(:each) { enable_observers }
after(:each) {disable_observers}
before { login_as :user } before { login_as :user }
describe "DELETE /projects/:id" do describe "DELETE /projects/:id" do
......
...@@ -108,7 +108,7 @@ describe "Application access" do ...@@ -108,7 +108,7 @@ describe "Application access" do
describe "GET /project_code/blob" do describe "GET /project_code/blob" do
before do before do
commit = project.repository.commit commit = project.repository.commit
path = commit.tree.contents.select { |i| i.is_a?(Grit::Blob)}.first.name path = commit.tree.contents.select { |i| i.is_a?(Grit::Blob) }.first.name
@blob_path = project_blob_path(project, File.join(commit.id, path)) @blob_path = project_blob_path(project, File.join(commit.id, path))
end end
...@@ -339,7 +339,7 @@ describe "Application access" do ...@@ -339,7 +339,7 @@ describe "Application access" do
describe "GET /project_code/blob" do describe "GET /project_code/blob" do
before do before do
commit = project.repository.commit commit = project.repository.commit
path = commit.tree.contents.select { |i| i.is_a?(Grit::Blob)}.first.name path = commit.tree.contents.select { |i| i.is_a?(Grit::Blob) }.first.name
@blob_path = project_blob_path(project, File.join(commit.id, path)) @blob_path = project_blob_path(project, File.join(commit.id, path))
end end
......
...@@ -9,7 +9,7 @@ describe GitlabMarkdownHelper do ...@@ -9,7 +9,7 @@ describe GitlabMarkdownHelper do
let(:user) { create(:user, username: 'gfm') } let(:user) { create(:user, username: 'gfm') }
let(:commit) { project.repository.commit } let(:commit) { project.repository.commit }
let(:issue) { create(:issue, project: project) } let(:issue) { create(:issue, project: project) }
let(:merge_request) { create(:merge_request, project: project) } let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
let(:snippet) { create(:project_snippet, project: project) } let(:snippet) { create(:project_snippet, project: project) }
let(:member) { project.users_projects.where(user_id: user).first } let(:member) { project.users_projects.where(user_id: user).first }
......
require 'spec_helper'
describe 'Gitlab::Satellite::Action' do
let(:project) { create(:project_with_code) }
let(:user) { create(:user) }
describe '#prepare_satellite!' do
it 'create a repository with a parking branch and one remote: origin' do
repo = project.satellite.repo
#now lets dirty it up
starting_remote_count = repo.git.list_remotes.size
starting_remote_count.should >= 1
#kind of hookey way to add a second remote
origin_uri = repo.git.remote({v: true}).split(" ")[1]
begin
repo.git.remote({raise: true}, 'add', 'another-remote', origin_uri)
repo.git.branch({raise: true}, 'a-new-branch')
repo.heads.size.should > (starting_remote_count)
repo.git.remote().split(" ").size.should > (starting_remote_count)
rescue
end
repo.git.config({}, "user.name", "#{user.name} -- foo")
repo.git.config({}, "user.email", "#{user.email} -- foo")
repo.config['user.name'].should =="#{user.name} -- foo"
repo.config['user.email'].should =="#{user.email} -- foo"
#These must happen in the context of the satellite directory...
satellite_action = Gitlab::Satellite::Action.new(user, project)
project.satellite.lock {
#Now clean it up, use send to get around prepare_satellite! being protected
satellite_action.send(:prepare_satellite!, repo)
}
#verify it's clean
heads = repo.heads.map(&:name)
heads.size.should == 1
heads.include?(Gitlab::Satellite::Satellite::PARKING_BRANCH).should == true
remotes = repo.git.remote().split(' ')
remotes.size.should == 1
remotes.include?('origin').should == true
repo.config['user.name'].should ==user.name
repo.config['user.email'].should ==user.email
end
end
describe '#in_locked_and_timed_satellite' do
it 'should make use of a lockfile' do
repo = project.satellite.repo
called = false
#set assumptions
File.rm(project.satellite.lock_file) unless !File.exists? project.satellite.lock_file
File.exists?(project.satellite.lock_file).should be_false
satellite_action = Gitlab::Satellite::Action.new(user, project)
satellite_action.send(:in_locked_and_timed_satellite) do |sat_repo|
repo.should == sat_repo
(File.exists? project.satellite.lock_file).should be_true
called = true
end
called.should be_true
end
it 'should be able to use the satellite after locking' do
repo = project.satellite.repo
called = false
# Set base assumptions
if File.exists? project.satellite.lock_file
FileLockStatusChecker.new(project.satellite.lock_file).flocked?.should be_false
end
satellite_action = Gitlab::Satellite::Action.new(user, project)
satellite_action.send(:in_locked_and_timed_satellite) do |sat_repo|
called = true
repo.should == sat_repo
(File.exists? project.satellite.lock_file).should be_true
FileLockStatusChecker.new(project.satellite.lock_file).flocked?.should be_true
end
called.should be_true
FileLockStatusChecker.new(project.satellite.lock_file).flocked?.should be_false
end
class FileLockStatusChecker < File
def flocked? &block
status = flock LOCK_EX|LOCK_NB
case status
when false
return true
when 0
begin
block ? block.call : false
ensure
flock LOCK_UN
end
else
raise SystemCallError, status
end
end
end
end
end
require 'spec_helper'
describe 'Gitlab::Satellite::MergeAction' do
before(:each) do
# TestEnv.init(mailer: false, init_repos: true, repos: true)
@master = ['master', 'bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a']
@one_after_stable = ['stable', '6ea87c47f0f8a24ae031c3fff17bc913889ecd00'] #this commit sha is one after stable
@wiki_branch = ['wiki', '635d3e09b72232b6e92a38de6cc184147e5bcb41'] #this is the commit sha where the wiki branch goes off from master
@conflicting_metior = ['metior', '313d96e42b313a0af5ab50fa233bf43e27118b3f'] #this branch conflicts with the wiki branch
#these commits are quite close together, itended to make string diffs/format patches small
@close_commit1 = ['2_3_notes_fix', '8470d70da67355c9c009e4401746b1d5410af2e3']
@close_commit2 = ['scss_refactoring', 'f0f14c8eaba69ebddd766498a9d0b0e79becd633']
end
let(:project) { create(:project_with_code) }
let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
let(:merge_request_fork) { create(:merge_request) }
describe '#commits_between' do
def verify_commits(commits, first_commit_sha, last_commit_sha)
commits.each { |commit| commit.class.should == Gitlab::Git::Commit }
commits.first.id.should == first_commit_sha
commits.last.id.should == last_commit_sha
end
context 'on fork' do
it 'should get proper commits between' do
merge_request_fork.target_branch = @one_after_stable[0]
merge_request_fork.source_branch = @master[0]
commits = Gitlab::Satellite::MergeAction.new(merge_request_fork.author, merge_request_fork).commits_between
verify_commits(commits, @one_after_stable[1], @master[1])
merge_request_fork.target_branch = @wiki_branch[0]
merge_request_fork.source_branch = @master[0]
commits = Gitlab::Satellite::MergeAction.new(merge_request_fork.author, merge_request_fork).commits_between
verify_commits(commits, @wiki_branch[1], @master[1])
end
end
context 'between branches' do
it 'should raise exception -- not expected to be used by non forks' do
merge_request.target_branch = @one_after_stable[0]
merge_request.source_branch = @master[0]
expect {Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).commits_between}.to raise_error
merge_request.target_branch = @wiki_branch[0]
merge_request.source_branch = @master[0]
expect {Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).commits_between}.to raise_error
end
end
end
describe '#format_patch' do
context 'on fork' do
it 'should build a format patch' do
merge_request_fork.target_branch = @close_commit1[0]
merge_request_fork.source_branch = @close_commit2[0]
patch = Gitlab::Satellite::MergeAction.new(merge_request_fork.author, merge_request_fork).format_patch
(patch.include? "From #{@close_commit2[1]}").should be_true
(patch.include? "From #{@close_commit1[1]}").should be_true
end
end
context 'between branches' do
it 'should build a format patch' do
merge_request.target_branch = @close_commit1[0]
merge_request.source_branch = @close_commit2[0]
patch = Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).format_patch
(patch.include? "From #{@close_commit2[1]}").should be_true
(patch.include? "From #{@close_commit1[1]}").should be_true
end
end
end
describe '#diffs_between_satellite tested against diff_in_satellite' do
def is_a_matching_diff(diff, diffs)
diff_count = diff.scan('diff --git').size
diff_count.should >= 1
diffs.size.should == diff_count
diffs.each do |a_diff|
a_diff.class.should == Gitlab::Git::Diff
(diff.include? a_diff.diff).should be_true
end
end
context 'on fork' do
it 'should get proper diffs' do
merge_request_fork.target_branch = @close_commit1[0]
merge_request_fork.source_branch = @master[0]
diffs = Gitlab::Satellite::MergeAction.new(merge_request_fork.author, merge_request_fork).diffs_between_satellite
merge_request_fork.target_branch = @close_commit1[0]
merge_request_fork.source_branch = @master[0]
diff = Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request_fork).diff_in_satellite
is_a_matching_diff(diff, diffs)
end
end
context 'between branches' do
it 'should get proper diffs' do
merge_request.target_branch = @close_commit1[0]
merge_request.source_branch = @master[0]
expect{Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).diffs_between_satellite}.to raise_error
end
end
end
describe '#can_be_merged?' do
context 'on fork' do
it 'return true or false depending on if something is mergable' do
merge_request_fork.target_branch = @one_after_stable[0]
merge_request_fork.source_branch = @master[0]
Gitlab::Satellite::MergeAction.new(merge_request_fork.author, merge_request_fork).can_be_merged?.should be_true
merge_request_fork.target_branch = @conflicting_metior[0]
merge_request_fork.source_branch = @wiki_branch[0]
Gitlab::Satellite::MergeAction.new(merge_request_fork.author, merge_request_fork).can_be_merged?.should be_false
end
end
context 'between branches' do
it 'return true or false depending on if something is mergable' do
merge_request.target_branch = @one_after_stable[0]
merge_request.source_branch = @master[0]
Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).can_be_merged?.should be_true
merge_request.target_branch = @conflicting_metior[0]
merge_request.source_branch = @wiki_branch[0]
Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).can_be_merged?.should be_false
end
end
end
end
\ No newline at end of file
...@@ -167,7 +167,7 @@ describe Notify do ...@@ -167,7 +167,7 @@ describe Notify do
end end
context 'for merge requests' do context 'for merge requests' do
let(:merge_request) { create(:merge_request, assignee: assignee, project: project) } let(:merge_request) { create(:merge_request, assignee: assignee, source_project: project, target_project: project) }
describe 'that are new' do describe 'that are new' do
subject { Notify.new_merge_request_email(merge_request.assignee_id, merge_request.id) } subject { Notify.new_merge_request_email(merge_request.assignee_id, merge_request.id) }
...@@ -311,7 +311,7 @@ describe Notify do ...@@ -311,7 +311,7 @@ describe Notify do
end end
describe 'on a merge request' do describe 'on a merge request' do
let(:merge_request) { create(:merge_request, project: project) } let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
let(:note_on_merge_request_path) { project_merge_request_path(project, merge_request, anchor: "note_#{note.id}") } let(:note_on_merge_request_path) { project_merge_request_path(project, merge_request, anchor: "note_#{note.id}") }
before(:each) { note.stub(:noteable).and_return(merge_request) } before(:each) { note.stub(:noteable).and_return(merge_request) }
......
...@@ -3,7 +3,6 @@ require 'spec_helper' ...@@ -3,7 +3,6 @@ require 'spec_helper'
describe Commit do describe Commit do
let(:commit) { create(:project_with_code).repository.commit } let(:commit) { create(:project_with_code).repository.commit }
describe '#title' do describe '#title' do
it "returns no_commit_message when safe_message is blank" do it "returns no_commit_message when safe_message is blank" do
commit.stub(:safe_message).and_return('') commit.stub(:safe_message).and_return('')
......
...@@ -12,9 +12,9 @@ ...@@ -12,9 +12,9 @@
require 'spec_helper' require 'spec_helper'
describe ForkedProjectLink, "add link on fork" do describe ForkedProjectLink, "add link on fork" do
let(:project_from) {create(:project)} let(:project_from) { create(:project) }
let(:namespace) {create(:namespace)} let(:namespace) { create(:namespace) }
let(:user) {create(:user, namespace: namespace)} let(:user) { create(:user, namespace: namespace) }
before do before do
@project_to = fork_project(project_from, user) @project_to = fork_project(project_from, user)
...@@ -30,9 +30,9 @@ describe ForkedProjectLink, "add link on fork" do ...@@ -30,9 +30,9 @@ describe ForkedProjectLink, "add link on fork" do
end end
describe :forked_from_project do describe :forked_from_project do
let(:forked_project_link) {build(:forked_project_link)} let(:forked_project_link) { build(:forked_project_link) }
let(:project_from) {create(:project)} let(:project_from) { create(:project) }
let(:project_to) {create(:project, forked_project_link: forked_project_link)} let(:project_to) { create(:project, forked_project_link: forked_project_link) }
before :each do before :each do
......
...@@ -41,15 +41,12 @@ describe MergeRequest do ...@@ -41,15 +41,12 @@ describe MergeRequest do
it { should include_module(Issuable) } it { should include_module(Issuable) }
end end
describe "#mr_and_commit_notes" do
end
describe "#mr_and_commit_notes" do describe "#mr_and_commit_notes" do
let!(:merge_request) { create(:merge_request) } let!(:merge_request) { create(:merge_request) }
before do before do
merge_request.stub(:commits) { [merge_request.project.repository.commit] } merge_request.stub(:commits) { [merge_request.source_project.repository.commit] }
create(:note, commit_id: merge_request.commits.first.id, noteable_type: 'Commit') create(:note, commit_id: merge_request.commits.first.id, noteable_type: 'Commit')
create(:note, noteable: merge_request) create(:note, noteable: merge_request)
end end
...@@ -71,4 +68,38 @@ describe MergeRequest do ...@@ -71,4 +68,38 @@ describe MergeRequest do
subject.is_being_reassigned?.should be_false subject.is_being_reassigned?.should be_false
end end
end end
describe '#for_fork?' do
it 'returns true if the merge request is for a fork' do
subject.source_project = create(:source_project)
subject.target_project = create(:target_project)
subject.for_fork?.should be_true
end
it 'returns false if is not for a fork' do
subject.source_project = create(:source_project)
subject.target_project = subject.source_project
subject.for_fork?.should be_false
end
end
describe '#allow_source_branch_removal?' do
it 'should not allow removal when mr is a fork' do
subject.disallow_source_branch_removal?.should be_true
end
it 'should not allow removal when the mr is not a fork, but the source branch is the root reference' do
subject.target_project = subject.source_project
subject.source_branch = subject.source_project.repository.root_ref
subject.disallow_source_branch_removal?.should be_true
end
it 'should not disallow removal when the mr is not a fork, and but source branch is not the root reference' do
subject.target_project = subject.source_project
subject.source_branch = "Something Different #{subject.source_project.repository.root_ref}"
subject.for_fork?.should be_false
subject.disallow_source_branch_removal?.should be_false
end
end
end end
...@@ -149,7 +149,7 @@ describe Note do ...@@ -149,7 +149,7 @@ describe Note do
let(:author) { create(:user) } let(:author) { create(:user) }
let(:status) { 'new_status' } let(:status) { 'new_status' }
subject { Note.create_status_change_note(thing, author, status) } subject { Note.create_status_change_note(thing, project, author, status) }
it 'creates and saves a Note' do it 'creates and saves a Note' do
should be_a Note should be_a Note
......
...@@ -26,6 +26,9 @@ ...@@ -26,6 +26,9 @@
require 'spec_helper' require 'spec_helper'
describe Project do describe Project do
before(:each) { enable_observers }
after(:each) { disable_observers }
describe "Associations" do describe "Associations" do
it { should belong_to(:group) } it { should belong_to(:group) }
it { should belong_to(:namespace) } it { should belong_to(:namespace) }
...@@ -95,12 +98,11 @@ describe Project do ...@@ -95,12 +98,11 @@ describe Project do
end end
describe "last_activity methods" do describe "last_activity methods" do
before { enable_observers }
let(:project) { create(:project) } let(:project) { create(:project) }
let(:last_event) { double(created_at: Time.now) } let(:last_event) { double(created_at: Time.now) }
describe "last_activity" do describe "last_activity" do
it "should alias last_activity to last_event"do it "should alias last_activity to last_event" do
project.stub(last_event: last_event) project.stub(last_event: last_event)
project.last_activity.should == last_event project.last_activity.should == last_event
end end
...@@ -122,7 +124,7 @@ describe Project do ...@@ -122,7 +124,7 @@ describe Project do
let(:project) { create(:project_with_code) } let(:project) { create(:project_with_code) }
before do before do
@merge_request = create(:merge_request, project: project) @merge_request = create(:merge_request, source_project: project, target_project: project)
@key = create(:key, user_id: project.owner.id) @key = create(:key, user_id: project.owner.id)
end end
......
...@@ -8,18 +8,6 @@ describe ActivityObserver do ...@@ -8,18 +8,6 @@ describe ActivityObserver do
it { @event.project.should == project } it { @event.project.should == project }
end end
describe "Merge Request created" do
before do
MergeRequest.observers.enable :activity_observer do
@merge_request = create(:merge_request, project: project)
@event = Event.last
end
end
it_should_be_valid_event
it { @event.action.should == Event::CREATED }
it { @event.target.should == @merge_request }
end
describe "Issue created" do describe "Issue created" do
before do before do
......
...@@ -26,14 +26,13 @@ describe IssueObserver do ...@@ -26,14 +26,13 @@ describe IssueObserver do
before { mock_issue.stub(state: 'closed') } before { mock_issue.stub(state: 'closed') }
it 'note is created if the issue is being closed' do it 'note is created if the issue is being closed' do
Note.should_receive(:create_status_change_note).with(mock_issue, some_user, 'closed') Note.should_receive(:create_status_change_note).with(mock_issue, mock_issue.project, some_user, 'closed')
subject.after_close(mock_issue, nil) subject.after_close(mock_issue, nil)
end end
it 'trigger notification to send emails' do it 'trigger notification to send emails' do
subject.notification.should_receive(:close_issue).with(mock_issue, some_user) subject.notification.should_receive(:close_issue).with(mock_issue, some_user)
subject.after_close(mock_issue, nil) subject.after_close(mock_issue, nil)
end end
end end
...@@ -42,8 +41,7 @@ describe IssueObserver do ...@@ -42,8 +41,7 @@ describe IssueObserver do
before { mock_issue.stub(state: 'reopened') } before { mock_issue.stub(state: 'reopened') }
it 'note is created if the issue is being reopened' do it 'note is created if the issue is being reopened' do
Note.should_receive(:create_status_change_note).with(mock_issue, some_user, 'reopened') Note.should_receive(:create_status_change_note).with(mock_issue, mock_issue.project, some_user, 'reopened')
subject.after_reopen(mock_issue, nil) subject.after_reopen(mock_issue, nil)
end end
end end
......
...@@ -12,8 +12,10 @@ describe MergeRequestObserver do ...@@ -12,8 +12,10 @@ describe MergeRequestObserver do
before { subject.stub(:current_user).and_return(some_user) } before { subject.stub(:current_user).and_return(some_user) }
before { subject.stub(notification: mock('NotificationService').as_null_object) } before { subject.stub(notification: mock('NotificationService').as_null_object) }
before { mr_mock.stub(:author_id) }
before { mr_mock.stub(:target_project) }
before(:each) { enable_observers } before(:each) { enable_observers }
after(:each) { disable_observers }
subject { MergeRequestObserver.instance } subject { MergeRequestObserver.instance }
...@@ -30,7 +32,7 @@ describe MergeRequestObserver do ...@@ -30,7 +32,7 @@ describe MergeRequestObserver do
end end
it 'is called when a merge request is changed' do it 'is called when a merge request is changed' do
changed = create(:merge_request, project: create(:project)) changed = create(:merge_request, source_project: create(:project))
subject.should_receive(:after_update) subject.should_receive(:after_update)
MergeRequest.observers.enable :merge_request_observer do MergeRequest.observers.enable :merge_request_observer do
...@@ -59,13 +61,13 @@ describe MergeRequestObserver do ...@@ -59,13 +61,13 @@ describe MergeRequestObserver do
context '#after_close' do context '#after_close' do
context 'a status "closed"' do context 'a status "closed"' do
it 'note is created if the merge request is being closed' do it 'note is created if the merge request is being closed' do
Note.should_receive(:create_status_change_note).with(assigned_mr, some_user, 'closed') Note.should_receive(:create_status_change_note).with(assigned_mr, assigned_mr.target_project, some_user, 'closed')
assigned_mr.close assigned_mr.close
end end
it 'notification is delivered only to author if the merge request is being closed' do it 'notification is delivered only to author if the merge request is being closed' do
Note.should_receive(:create_status_change_note).with(unassigned_mr, some_user, 'closed') Note.should_receive(:create_status_change_note).with(unassigned_mr, unassigned_mr.target_project, some_user, 'closed')
unassigned_mr.close unassigned_mr.close
end end
...@@ -75,16 +77,41 @@ describe MergeRequestObserver do ...@@ -75,16 +77,41 @@ describe MergeRequestObserver do
context '#after_reopen' do context '#after_reopen' do
context 'a status "reopened"' do context 'a status "reopened"' do
it 'note is created if the merge request is being reopened' do it 'note is created if the merge request is being reopened' do
Note.should_receive(:create_status_change_note).with(closed_assigned_mr, some_user, 'reopened') Note.should_receive(:create_status_change_note).with(closed_assigned_mr, closed_assigned_mr.target_project, some_user, 'reopened')
closed_assigned_mr.reopen closed_assigned_mr.reopen
end end
it 'notification is delivered only to author if the merge request is being reopened' do it 'notification is delivered only to author if the merge request is being reopened' do
Note.should_receive(:create_status_change_note).with(closed_unassigned_mr, some_user, 'reopened') Note.should_receive(:create_status_change_note).with(closed_unassigned_mr, closed_unassigned_mr.target_project, some_user, 'reopened')
closed_unassigned_mr.reopen closed_unassigned_mr.reopen
end end
end end
end end
describe "Merge Request created" do
def self.it_should_be_valid_event
it { @event.should_not be_nil }
it { @event.should_not be_nil }
it { @event.project.should == project }
it { @event.project.should == project }
end
let(:project) { create(:project) }
before do
TestEnv.enable_observers
@merge_request = create(:merge_request, source_project: project, target_project: project)
@event = Event.last
end
after do
TestEnv.disable_observers
end
it_should_be_valid_event
it { @event.action.should == Event::CREATED }
it { @event.target.should == @merge_request }
end
end end
...@@ -2,6 +2,7 @@ require 'spec_helper' ...@@ -2,6 +2,7 @@ require 'spec_helper'
describe UserObserver do describe UserObserver do
before(:each) { enable_observers } before(:each) { enable_observers }
after(:each) {disable_observers}
subject { UserObserver.instance } subject { UserObserver.instance }
before { subject.stub(notification: mock('NotificationService').as_null_object) } before { subject.stub(notification: mock('NotificationService').as_null_object) }
......
...@@ -2,6 +2,7 @@ require 'spec_helper' ...@@ -2,6 +2,7 @@ require 'spec_helper'
describe UsersProjectObserver do describe UsersProjectObserver do
before(:each) { enable_observers } before(:each) { enable_observers }
after(:each) { disable_observers }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project) } let(:project) { create(:project) }
......
...@@ -3,10 +3,12 @@ require "spec_helper" ...@@ -3,10 +3,12 @@ require "spec_helper"
describe API::API do describe API::API do
include ApiHelpers include ApiHelpers
let(:user) { create(:user ) } let(:user) { create(:user) }
let!(:project) { create(:project_with_code, creator_id: user.id) } let!(:project) {create(:project_with_code, creator_id: user.id) }
let!(:merge_request) { create(:merge_request, author: user, assignee: user, project: project, title: "Test") } let!(:merge_request) { create(:merge_request, author: user, assignee: user, source_project: project, target_project: project, title: "Test") }
before { project.team << [user, :reporters] } before {
project.team << [user, :reporters]
}
describe "GET /projects/:id/merge_requests" do describe "GET /projects/:id/merge_requests" do
context "when unauthenticated" do context "when unauthenticated" do
...@@ -40,6 +42,7 @@ describe API::API do ...@@ -40,6 +42,7 @@ describe API::API do
end end
describe "POST /projects/:id/merge_requests" do describe "POST /projects/:id/merge_requests" do
context 'between branches projects' do
it "should return merge_request" do it "should return merge_request" do
post api("/projects/#{project.id}/merge_requests", user), post api("/projects/#{project.id}/merge_requests", user),
title: 'Test merge_request', source_branch: "stable", target_branch: "master", author: user title: 'Test merge_request', source_branch: "stable", target_branch: "master", author: user
...@@ -72,6 +75,74 @@ describe API::API do ...@@ -72,6 +75,74 @@ describe API::API do
end end
end end
context 'forked projects' do
let!(:user2) {create(:user)}
let!(:forked_project_link) { build(:forked_project_link) }
let!(:fork_project) { create(:source_project_with_code, forked_project_link: forked_project_link, namespace: user2.namespace, creator_id: user2.id) }
let!(:unrelated_project) { create(:target_project_with_code, namespace: user2.namespace, creator_id: user2.id) }
before :each do |each|
fork_project.team << [user2, :reporters]
forked_project_link.forked_from_project = project
forked_project_link.forked_to_project = fork_project
forked_project_link.save!
end
it "should return merge_request" do
post api("/projects/#{fork_project.id}/merge_requests", user2),
title: 'Test merge_request', source_branch: "stable", target_branch: "master", author: user2, target_project_id: project.id
response.status.should == 201
json_response['title'].should == 'Test merge_request'
end
it "should not return 422 when source_branch equals target_branch" do
project.id.should_not == fork_project.id
fork_project.forked?.should be_true
fork_project.forked_from_project.should == project
post api("/projects/#{fork_project.id}/merge_requests", user2),
title: 'Test merge_request', source_branch: "master", target_branch: "master", author: user2, target_project_id: project.id
response.status.should == 201
json_response['title'].should == 'Test merge_request'
end
it "should return 400 when source_branch is missing" do
post api("/projects/#{fork_project.id}/merge_requests", user2),
title: 'Test merge_request', target_branch: "master", author: user2, target_project_id: project.id
response.status.should == 400
end
it "should return 400 when target_branch is missing" do
post api("/projects/#{fork_project.id}/merge_requests", user2),
title: 'Test merge_request', target_branch: "master", author: user2, target_project_id: project.id
response.status.should == 400
end
it "should return 400 when title is missing" do
post api("/projects/#{fork_project.id}/merge_requests", user2),
target_branch: 'master', source_branch: 'stable', author: user2, target_project_id: project.id
response.status.should == 400
end
it "should return 400 when target_branch is specified and not a forked project" do
post api("/projects/#{project.id}/merge_requests", user),
title: 'Test merge_request', target_branch: 'master', source_branch: 'stable', author: user, target_project_id: fork_project.id
response.status.should == 400
end
it "should return 400 when target_branch is specified and for a different fork" do
post api("/projects/#{fork_project.id}/merge_requests", user2),
title: 'Test merge_request', target_branch: 'master', source_branch: 'stable', author: user2, target_project_id: unrelated_project.id
response.status.should == 400
end
it "should return 201 when target_branch is specified and for the same project" do
post api("/projects/#{fork_project.id}/merge_requests", user2),
title: 'Test merge_request', target_branch: 'master', source_branch: 'stable', author: user2, target_project_id: fork_project.id
response.status.should == 201
end
end
end
describe "PUT /projects/:id/merge_request/:merge_request_id to close MR" do describe "PUT /projects/:id/merge_request/:merge_request_id to close MR" do
it "should return merge_request" do it "should return merge_request" do
put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), state_event: "close" put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), state_event: "close"
...@@ -102,9 +173,9 @@ describe API::API do ...@@ -102,9 +173,9 @@ describe API::API do
end end
it "should return merge_request with renamed target_branch" do it "should return merge_request with renamed target_branch" do
put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), target_branch: "test" put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), target_branch: "wiki"
response.status.should == 200 response.status.should == 200
json_response['target_branch'].should == 'test' json_response['target_branch'].should == 'wiki'
end end
end end
......
...@@ -3,6 +3,7 @@ require 'spec_helper' ...@@ -3,6 +3,7 @@ require 'spec_helper'
describe API::API do describe API::API do
include ApiHelpers include ApiHelpers
before(:each) { enable_observers } before(:each) { enable_observers }
after(:each) {disable_observers}
let(:user) { create(:user) } let(:user) { create(:user) }
let!(:project) { create(:project, namespace: user.namespace ) } let!(:project) { create(:project, namespace: user.namespace ) }
......
...@@ -6,7 +6,7 @@ describe API::API do ...@@ -6,7 +6,7 @@ describe API::API do
let(:user) { create(:user) } let(:user) { create(:user) }
let!(:project) { create(:project, namespace: user.namespace ) } let!(:project) { create(:project, namespace: user.namespace ) }
let!(:issue) { create(:issue, project: project, author: user) } let!(:issue) { create(:issue, project: project, author: user) }
let!(:merge_request) { create(:merge_request, project: project, author: user) } let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: user) }
let!(:snippet) { create(:project_snippet, project: project, author: user) } let!(:snippet) { create(:project_snippet, project: project, author: user) }
let!(:issue_note) { create(:note, noteable: issue, project: project, author: user) } let!(:issue_note) { create(:note, noteable: issue, project: project, author: user) }
let!(:merge_request_note) { create(:note, noteable: merge_request, project: project, author: user) } let!(:merge_request_note) { create(:note, noteable: merge_request, project: project, author: user) }
......
...@@ -3,6 +3,7 @@ require 'spec_helper' ...@@ -3,6 +3,7 @@ require 'spec_helper'
describe API::API do describe API::API do
include ApiHelpers include ApiHelpers
before(:each) { enable_observers } before(:each) { enable_observers }
after(:each) { disable_observers }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:user2) { create(:user) } let(:user2) { create(:user) }
...@@ -475,7 +476,6 @@ describe API::API do ...@@ -475,7 +476,6 @@ describe API::API do
end end
end end
describe "GET /projects/:id/snippets" do describe "GET /projects/:id/snippets" do
it "should return an array of project snippets" do it "should return an array of project snippets" do
get api("/projects/#{project.id}/snippets", user) get api("/projects/#{project.id}/snippets", user)
......
...@@ -3,6 +3,7 @@ require 'spec_helper' ...@@ -3,6 +3,7 @@ require 'spec_helper'
describe API::API do describe API::API do
include ApiHelpers include ApiHelpers
before(:each) { enable_observers } before(:each) { enable_observers }
after(:each) {disable_observers}
let(:user) { create(:user) } let(:user) { create(:user) }
let(:user2) { create(:user) } let(:user2) { create(:user) }
......
...@@ -2,7 +2,6 @@ require 'spec_helper' ...@@ -2,7 +2,6 @@ require 'spec_helper'
describe NotificationService do describe NotificationService do
let(:notification) { NotificationService.new } let(:notification) { NotificationService.new }
describe 'Keys' do describe 'Keys' do
describe :new_key do describe :new_key do
let(:key) { create(:personal_key) } let(:key) { create(:personal_key) }
...@@ -156,7 +155,8 @@ describe NotificationService do ...@@ -156,7 +155,8 @@ describe NotificationService do
let(:merge_request) { create :merge_request, assignee: create(:user) } let(:merge_request) { create :merge_request, assignee: create(:user) }
before do before do
build_team(merge_request.project) build_team(merge_request.source_project)
build_team(merge_request.target_project)
end end
describe :new_merge_request do describe :new_merge_request do
...@@ -232,11 +232,16 @@ describe NotificationService do ...@@ -232,11 +232,16 @@ describe NotificationService do
end end
end end
let(:u_watcher) { create(:user, notification_level: Notification::N_WATCH) }
let(:u_participating) { create(:user, notification_level: Notification::N_PARTICIPATING) }
let(:u_disabled) { create(:user, notification_level: Notification::N_DISABLED) }
let(:u_mentioned) { create(:user, username: 'mention', notification_level: Notification::N_WATCH) }
def build_team(project) def build_team(project)
@u_watcher = create(:user, notification_level: Notification::N_WATCH) @u_watcher = u_watcher
@u_participating = create(:user, notification_level: Notification::N_PARTICIPATING) @u_participating = u_participating
@u_disabled = create(:user, notification_level: Notification::N_DISABLED) @u_disabled = u_disabled
@u_mentioned = create(:user, username: 'mention', notification_level: Notification::N_WATCH) @u_mentioned = u_mentioned
project.team << [@u_watcher, :master] project.team << [@u_watcher, :master]
project.team << [@u_participating, :master] project.team << [@u_participating, :master]
......
...@@ -2,6 +2,7 @@ require 'spec_helper' ...@@ -2,6 +2,7 @@ require 'spec_helper'
describe ProjectTransferService do describe ProjectTransferService do
before(:each) { enable_observers } before(:each) { enable_observers }
after(:each) {disable_observers}
context 'namespace -> namespace' do context 'namespace -> namespace' do
let(:user) { create(:user) } let(:user) { create(:user) }
...@@ -24,6 +25,7 @@ describe ProjectTransferService do ...@@ -24,6 +25,7 @@ describe ProjectTransferService do
@result = service.transfer(project, nil) @result = service.transfer(project, nil)
end end
it { @result.should be_true } it { @result.should be_true }
it { project.namespace.should == nil } it { project.namespace.should == nil }
end end
......
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment