Commit 3d7194f0 authored by Izaak Alpert's avatar Izaak Alpert Committed by Izaak Alpert

Merge Request on forked projects

The good:

 - You can do a merge request for a forked commit and it will merge properly (i.e. it does work).
 - Push events take into account merge requests on forked projects
 - Tests around merge_actions now present, spinach, and other rspec tests
 - Satellites now clean themselves up rather then recreate

The questionable:

 - Events only know about target projects
 - Project's merge requests only hold on to MR's where they are the target
 - All operations performed in the satellite

The bad:

  -  Duplication between project's repositories and satellites (e.g. commits_between)

(for reference: http://feedback.gitlab.com/forums/176466-general/suggestions/3456722-merge-requests-between-projects-repos)

Fixes:

Make test repos/satellites only create when needed
-Spinach/Rspec now only initialize test directory, and setup stubs (things that are relatively cheap)
-project_with_code, source_project_with_code, and target_project_with_code now create/destroy their repos individually
-fixed remote removal
-How to merge renders properly
-Update emails to show project/branches
-Edit MR doesn't set target branch
-Fix some failures on editing/creating merge requests, added a test
-Added back a test around merge request observer
-Clean up project_transfer_spec, Remove duplicate enable/disable observers
-Ensure satellite lock files are cleaned up, Attempted to add some testing around these as well
-Signifant speed ups for tests
-Update formatting ordering in notes_on_merge_requests
-Remove wiki schema update
Fixes for search/search results
-Search results was using by_project for a list of projects, updated this to use in_projects
-updated search results to reference the correct (target) project
-udpated search results to print both sides of the merge request

Change-Id: I19407990a0950945cc95d62089cbcc6262dab1a8
parent fd033671
...@@ -415,6 +415,17 @@ img.emoji { ...@@ -415,6 +415,17 @@ img.emoji {
@extend .light-well; @extend .light-well;
@extend .light; @extend .light;
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 {
......
...@@ -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?
...@@ -20,12 +20,12 @@ class FilterContext ...@@ -20,12 +20,12 @@ class FilterContext
end end
case params[:status] case params[:status]
when 'closed' when 'closed'
items.closed items.closed
when 'all' when 'all'
items items
else else
items.opened items.opened
end end
end end
end end
...@@ -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,25 +33,39 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -33,25 +33,39 @@ 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 = @project.merge_requests.new(params[:merge_request])
if params[:merge_request] && params[:merge_request][:source_project_id]
@merge_request.source_project = Project.find_by_id(params[:merge_request][:source_project_id])
else
@merge_request.source_project = @project
end
if params[:merge_request] && params[:merge_request][:target_project_id]
@merge_request.target_project = Project.find_by_id(params[:merge_request][:target_project_id])
end
@target_branches = @merge_request.target_project.nil? ? [] : @merge_request.target_project.repository.branch_names
@merge_request
end end
def edit def edit
@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 = @project.merge_requests.new(params[:merge_request])
@merge_request.author = current_user @merge_request.author = current_user
@merge_request.source_project_id = params[:merge_request][:source_project_id].to_i
@merge_request.target_project_id = params[:merge_request][:target_project_id].to_i
@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" render "new"
end end
...@@ -89,22 +103,36 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -89,22 +103,36 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end end
def branch_from def branch_from
#This is always source
@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 end
def update_branches
@target_project = selected_target_project
@target_branches = (@target_project.repository.branch_names).unshift("Select branch")
@target_branches
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 +151,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -123,11 +151,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)
......
module MergeRequestsHelper 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 +32,6 @@ module MergeRequestsHelper ...@@ -18,6 +32,6 @@ 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 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: @merge_request.assignee_email, 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
...@@ -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
......
...@@ -2,30 +2,37 @@ ...@@ -2,30 +2,37 @@
# #
# Table name: merge_requests # Table name: merge_requests
# #
# id :integer not null, primary key # id :integer not null, primary key
# target_branch :string(255) not null # target_project_id :integer not null
# source_branch :string(255) not null # target_branch :string(255) not null
# project_id :integer not null # source_project_id :integer not null
# author_id :integer # source_branch :string(255) not null
# assignee_id :integer # author_id :integer
# title :string(255) # assignee_id :integer
# created_at :datetime # title :string(255)
# updated_at :datetime # created_at :datetime
# st_commits :text(2147483647) # updated_at :datetime
# st_diffs :text(2147483647) # st_commits :text(2147483647)
# milestone_id :integer # st_diffs :text(2147483647)
# state :string(255) # milestone_id :integer
# merge_status :string(255) # state :string(255)
# merge_status :string(255)
# #
require Rails.root.join("app/models/commit") 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"
BROKEN_DIFF = "--broken-diff"
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,22 +81,29 @@ class MergeRequest < ActiveRecord::Base ...@@ -74,22 +81,29 @@ 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?
...@@ -137,7 +151,14 @@ class MergeRequest < ActiveRecord::Base ...@@ -137,7 +151,14 @@ class MergeRequest < ActiveRecord::Base
end end
def unmerged_diffs def unmerged_diffs
project.repository.diffs_between(source_branch, target_branch) #TODO:[IA-8] this needs to be handled better -- logged etc
diffs = Gitlab::Satellite::MergeAction.new(author, self).diffs_between_satellite
if diffs
diffs = diffs.map { |diff| Gitlab::Git::Diff.new(diff) }
else
diffs = []
end
diffs
end end
def last_commit def last_commit
...@@ -145,11 +166,11 @@ class MergeRequest < ActiveRecord::Base ...@@ -145,11 +166,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
...@@ -158,24 +179,30 @@ class MergeRequest < ActiveRecord::Base ...@@ -158,24 +179,30 @@ class MergeRequest < ActiveRecord::Base
def probably_merged? def probably_merged?
unmerged_commits.empty? && unmerged_commits.empty? &&
commits.any? && opened? commits.any? && opened?
end end
def reloaded_commits def reloaded_commits
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. commits = Gitlab::Satellite::MergeAction.new(self.author,self).commits_between
commits_between(self.target_branch, self.source_branch). commits = commits.map{ |commit| Gitlab::Git::Commit.new(commit, nil) }
sort_by(&:created_at). if commits.present?
reverse commits = Commit.decorate(commits).
sort_by(&:created_at).
reverse
end
commits
end end
def merge!(user_id) def merge!(user_id)
self.author_id_of_changes = user_id self.author_id_of_changes = user_id
self.merge self.merge
...@@ -195,25 +222,33 @@ class MergeRequest < ActiveRecord::Base ...@@ -195,25 +222,33 @@ class MergeRequest < ActiveRecord::Base
commit_ids = commits.map(&:id) commit_ids = commits.map(&:id)
Note.where("(noteable_type = 'MergeRequest' AND noteable_id = :mr_id) OR (noteable_type = 'Commit' AND commit_id IN (:commit_ids))", mr_id: id, commit_ids: commit_ids) Note.where("(noteable_type = 'MergeRequest' AND noteable_id = :mr_id) OR (noteable_type = 'Commit' AND commit_id IN (:commit_ids))", mr_id: id, commit_ids: commit_ids)
end end
# 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)
......
...@@ -32,8 +32,8 @@ class Note < ActiveRecord::Base ...@@ -32,8 +32,8 @@ class Note < ActiveRecord::Base
delegate :name, :email, to: :author, prefix: true delegate :name, :email, to: :author, prefix: true
validates :note, :project, presence: true validates :note, :project, presence: true
validates :line_code, format: { with: /\A[a-z0-9]+_\d+_\d+\Z/ }, allow_blank: true validates :line_code, format: {with: /\A[a-z0-9]+_\d+_\d+\Z/}, allow_blank: true
validates :attachment, file_size: { maximum: 10.megabytes.to_i } validates :attachment, file_size: {maximum: 10.megabytes.to_i}
validates :noteable_id, presence: true, if: ->(n) { n.noteable_type.present? && n.noteable_type != 'Commit' } validates :noteable_id, presence: true, if: ->(n) { n.noteable_type.present? && n.noteable_type != 'Commit' }
validates :commit_id, presence: true, if: ->(n) { n.noteable_type == 'Commit' } validates :commit_id, presence: true, if: ->(n) { n.noteable_type == 'Commit' }
...@@ -45,24 +45,24 @@ class Note < ActiveRecord::Base ...@@ -45,24 +45,24 @@ class Note < ActiveRecord::Base
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)
end end
def commit_author def commit_author
@commit_author ||= @commit_author ||=
project.users.find_by_email(noteable.author_email) || project.users.find_by_email(noteable.author_email) ||
project.users.find_by_name(noteable.author_name) project.users.find_by_name(noteable.author_name)
rescue rescue
nil nil
end end
...@@ -97,8 +97,8 @@ class Note < ActiveRecord::Base ...@@ -97,8 +97,8 @@ class Note < ActiveRecord::Base
# otherwise false is returned # otherwise false is returned
def downvote? def downvote?
votable? && (note.start_with?('-1') || votable? && (note.start_with?('-1') ||
note.start_with?(':-1:') note.start_with?(':-1:')
) )
end end
def for_commit? def for_commit?
...@@ -136,8 +136,8 @@ class Note < ActiveRecord::Base ...@@ -136,8 +136,8 @@ class Note < ActiveRecord::Base
else else
super super
end end
# Temp fix to prevent app crash # Temp fix to prevent app crash
# if note commit id doesn't exist # if note commit id doesn't exist
rescue rescue
nil nil
end end
...@@ -146,8 +146,8 @@ class Note < ActiveRecord::Base ...@@ -146,8 +146,8 @@ class Note < ActiveRecord::Base
# otherwise false is returned # otherwise false is returned
def upvote? def upvote?
votable? && (note.start_with?('+1') || votable? && (note.start_with?('+1') ||
note.start_with?(':+1:') note.start_with?(':+1:')
) )
end end
def votable? def votable?
......
...@@ -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
cattr_accessor :current_user
def after_create(merge_request) def after_create(merge_request)
event_author_id = merge_request.author_id
if event_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,10 @@ class NotificationService ...@@ -80,8 +80,10 @@ 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.source_project)
recipients = recipients.concat(project_watchers(merge_request.project)).uniq recipients = recipients.concat(reject_muted_users([merge_request.author, merge_request.assignee], merge_request.target_project))
recipients = recipients.concat(project_watchers(merge_request.source_project))
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)
...@@ -102,7 +104,7 @@ class NotificationService ...@@ -102,7 +104,7 @@ class NotificationService
# ignore wall messages # ignore wall messages
return true unless note.noteable_type.present? return true unless note.noteable_type.present?
opts = { noteable_type: note.noteable_type, project_id: note.project_id } opts = {noteable_type: note.noteable_type, project_id: note.project_id}
if note.commit_id.present? if note.commit_id.present?
opts.merge!(commit_id: note.commit_id) opts.merge!(commit_id: note.commit_id)
...@@ -191,14 +193,14 @@ class NotificationService ...@@ -191,14 +193,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 +208,9 @@ class NotificationService ...@@ -206,9 +208,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 +218,14 @@ class NotificationService ...@@ -216,14 +218,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
:plain
$(".target_branch").html("#{escape_javascript(options_for_select(@target_branches))}");
$(".target_branch").trigger("liszt:updated");
$(".mr_target_commit").html("");
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
%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.project, @merge_request)
%p %p
Branches: #{@merge_request.source_branch} &rarr; #{@merge_request.target_branch} Projects:Branches: #{@merge_request.source_project.path_with_namespace}:#{@merge_request.source_branch} &rarr; #{@merge_request.target_project.path_with_namespace}:#{@merge_request.target_branch}
%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} Project:Branches: #{@merge_request.source_project.path_with_namespace}/#{@merge_request.source_branch} - #{@merge_request.target_project.path_with_namespace}#{@merge_request.target_branch}
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} Projects:Branches: #{@merge_request.source_project.path_with_namespace}:#{@merge_request.source_branch} &rarr; #{@merge_request.target_project.path_with_namespace}:#{@merge_request.target_branch}
%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} Project:Branches: #{@merge_request.source_project.path_with_namespace}/#{@merge_request.source_branch} - #{@merge_request.target_project.path_with_namespace}#{@merge_request.target_branch}
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} Project:Branches: #{@merge_request.source_project.path_with_namespace}/#{@merge_request.source_branch} - #{@merge_request.target_project.path_with_namespace}#{@merge_request.target_branch}
%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)) %>
Branches: <%= @merge_request.source_branch %> to <%= @merge_request.target_branch %> From: <%= @merge_request.source_project.path_with_namespace%>:<%= @merge_request.source_branch %> to <%= @merge_request.target_project.path_with_namespace%>:<%= @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 %>
......
%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
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
%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
......
= form_for [@project, @merge_request], html: { class: "#{controller.action_name}-merge-request form-horizontal" } do |f| = form_for [@project, @merge_request], html: { class: "#{controller.action_name}-merge-request form-horizontal" } do |form_helper|
-if @merge_request.errors.any? -if @merge_request.errors.any?
.alert.alert-error .alert.alert-error
%ul %ul
...@@ -12,18 +12,20 @@ ...@@ -12,18 +12,20 @@
.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= form_helper.select(:source_project_id,[[@merge_request.source_project.path_with_namespace,@merge_request.source_project.id]] , {}, {class: 'source_project chosen span4'})
.padded= form_helper.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]
.mr_target_commit.prepend-top-10 .padded= form_helper.select(:target_project_id, projects.map { |proj| [proj.path_with_namespace,proj.id] }, {include_blank: "Select Target Project" }, {class: 'target_project chosen span4'})
.padded= form_helper.select(:target_branch, @target_branches, { include_blank: "Select branch" }, {class: 'target_branch chosen span4'})
.mr_target_commit.prepend-top-10
%hr %hr
...@@ -47,12 +49,11 @@ ...@@ -47,12 +49,11 @@
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" = form_helper.submit 'Submit merge request', class: "btn btn-create"
-else -else
= f.submit 'Save changes', class: "btn btn-save" = form_helper.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(@project), class: "btn btn-cancel" do
Cancel Cancel
...@@ -63,16 +64,23 @@ ...@@ -63,16 +64,23 @@
: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(@project)}", {ref: source_branch.val() });
$.get("#{branch_to_project_merge_requests_path(@project)}", {ref: target_branch.val() }); $.get("#{branch_to_project_merge_requests_path(@project)}", {target_project_id: target_project.val(),ref: target_branch.val() });
source_branch.live("change", function() { target_project.live("change", function() {
$.get("#{branch_from_project_merge_requests_path(@project)}", {ref: $(this).val() }); $.get("#{update_branches_project_merge_requests_path(@project)}", {target_project_id: $(this).val() });
}); });
source_branch.live("change", function() {
$.get("#{branch_from_project_merge_requests_path(@project)}", {ref: $(this).val() });
});
target_branch.live("change", function() {
$.get("#{branch_to_project_merge_requests_path(@project)}", {target_project_id: target_project.val(),ref: $(this).val() });
});
target_branch.live("change", function() {
$.get("#{branch_to_project_merge_requests_path(@project)}", {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
= "#{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}"
.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, @project)}");
: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
......
...@@ -3,17 +3,49 @@ ...@@ -3,17 +3,49 @@
%a.close{href: "#"} × %a.close{href: "#"} ×
%h3 How To Merge %h3 How To Merge
.modal-body .modal-body
%p - if @merge_request.for_fork?
%strong Step 1. - source_remote = @merge_request.source_project.namespace.nil? ? "source" :@merge_request.source_project.namespace.path
Checkout target branch and get recent objects from GitLab - target_remote = @merge_request.target_project.namespace.nil? ? "target" :@merge_request.target_project.namespace.path
%pre.dark %p
:preserve %strong Step 1.
git checkout #{@merge_request.target_branch} Checkout target branch and get recent objects from GitLab
git fetch origin Assuming remote for #{@merge_request.target_project.path_with_namespace} is called #{target_remote}
%p remote for #{@merge_request.source_project.path_with_namespace} is called #{source_remote}
%strong Step 2. %pre.dark
Merge source branch into target branch and push changes to GitLab :preserve
%pre.dark git checkout #{target_remote} #{@merge_request.target_branch}
:preserve git fetch #{source_remote}
git merge origin/#{@merge_request.source_branch} %p
git push origin #{@merge_request.target_branch} %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
%strong Step 1.
Checkout target branch and get recent objects from GitLab
%pre.dark
:preserve
git checkout #{@merge_request.target_branch}
git fetch origin
%p
%strong Step 2.
Merge source branch into target branch and push changes to GitLab
%pre.dark
:preserve
git merge origin/#{@merge_request.source_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;
%span.label-project= @merge_request.source_project.path_with_namespace
%span.label-branch= @merge_request.source_branch %span.label-branch= @merge_request.source_branch
&rarr; &rarr;
%span.label-project= @merge_request.target_project.path_with_namespace
%span.label-branch= @merge_request.target_branch %span.label-branch= @merge_request.target_branch
%span.pull-right %span.pull-right
......
...@@ -22,11 +22,11 @@ ...@@ -22,11 +22,11 @@
- @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}) %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})
- @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
......
...@@ -255,6 +255,7 @@ Gitlab::Application.routes.draw do ...@@ -255,6 +255,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,15 @@ FileUtils.cd(REPO_PATH) do ...@@ -19,5 +19,15 @@ 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)
# 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.connection.execute("update merge_requests set 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
...@@ -84,9 +84,9 @@ ActiveRecord::Schema.define(:version => 20130624162710) do ...@@ -84,9 +84,9 @@ ActiveRecord::Schema.define(:version => 20130624162710) do
add_index "keys", ["user_id"], :name => "index_keys_on_user_id" add_index "keys", ["user_id"], :name => "index_keys_on_user_id"
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
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"
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
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 prefilled "Merge Request On Forked Project"
...@@ -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
......
...@@ -3,10 +3,13 @@ class Spinach::Features::ProjectDeployKeys < Spinach::FeatureSteps ...@@ -3,10 +3,13 @@ class Spinach::Features::ProjectDeployKeys < Spinach::FeatureSteps
include SharedProject include SharedProject
include SharedPaths include SharedPaths
step 'project has deploy key' do step 'project has deploy key' do
create(:deploy_keys_project, project: @project) create(:deploy_keys_project, project: @project)
end end
step 'I should see project deploy keys' do step 'I should see project deploy keys' do
within '.enabled-keys' do within '.enabled-keys' do
page.should have_content deploy_key.title page.should have_content deploy_key.title
......
...@@ -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
...@@ -14,12 +16,17 @@ class ForkProject < Spinach::FeatureSteps ...@@ -14,12 +16,17 @@ class ForkProject < Spinach::FeatureSteps
@project.team << [@user, :reporter] @project.team << [@user, :reporter]
end end
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)
forked_project_link.forked_from_project = @project
forked_project_link.forked_to_project = @forked_project
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
end
And 'I fill out a "Merge Request On Forked Project" merge request' do
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
select @project.path_with_namespace, from: "merge_request_target_project_id"
find(:select, "merge_request_target_project_id", {}).value.should == @project.id.to_s
select "master", from: "merge_request_source_branch"
find(:select, "merge_request_source_branch", {}).value.should == "master"
select "stable", from: "merge_request_target_branch"
find(:select, "merge_request_target_branch", {}).value.should == "stable"
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 == "new_design"
find("#merge_request_target_branch").value.should == "master"
find("#merge_request_title").value.should == "New Design"
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
#there are other edit buttons in this page for replies
# links = page.all("a.btn.grouped")
# links.each {|e|puts e.inspect }
#TODO:[IA-08] there has got to be a better way to find this button -- there are multiple "Edit" buttons, so that won't work, maybe if we give it an explicit class in the haml
#click_link "Edit" # doesn't work, multiple "Edit" buttons
# find(:link, "a.btn:nth-child(3)").click
# find(:link, "/html/body/div[2]/div/div/h3/span[5]/a[2]").click
page.first(:xpath, "/html/body/div[2]/div/div/h3/span[5]/a[2]").click
end
Then 'I see prefilled "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_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 == "master"
find("#merge_request_target_branch").value.should == "stable"
find("#merge_request_title").value.should == "Merge Request On Forked Project"
end
def project
@project ||= Project.find_by_name!("Shop")
end
end
...@@ -24,6 +24,7 @@ class ProjectMergeRequests < Spinach::FeatureSteps ...@@ -24,6 +24,7 @@ class ProjectMergeRequests < Spinach::FeatureSteps
page.should have_content "Wiki Feature" page.should have_content "Wiki Feature"
end end
Then 'I should see closed merge request "Bug NS-04"' do Then 'I should see closed merge request "Bug NS-04"' do
merge_request = MergeRequest.find_by_title!("Bug NS-04") merge_request = MergeRequest.find_by_title!("Bug NS-04")
merge_request.closed?.should be_true merge_request.closed?.should be_true
...@@ -56,30 +57,35 @@ class ProjectMergeRequests < Spinach::FeatureSteps ...@@ -56,30 +57,35 @@ 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" fill_in "merge_request_title", :with => "Wiki Feature"
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" select "master", :from => "merge_request_source_branch"
select "stable", :from => "merge_request_target_branch"
find(:select, "merge_request_target_branch", {}).find(:option, "stable", {}).value.should == "stable"
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
......
...@@ -51,9 +51,10 @@ module API ...@@ -51,9 +51,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,11 +64,15 @@ module API ...@@ -63,11 +64,15 @@ 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
if !attrs[:target_project_id].nil? && user_project.forked? && user_project.forked_from_project.id.to_s == attrs[:target_project_id]
merge_request.target_project = Project.find_by_id(attrs[:target_project_id])
elsif attrs[:target_project].nil?
merge_request.target_project = user_project
end
if merge_request.save if merge_request.save
merge_request.reload_code merge_request.reload_code
present merge_request, with: Entities::MergeRequest present merge_request, with: Entities::MergeRequest
......
module Gitlab module Gitlab
module Satellite module Satellite
class Action class Action
DEFAULT_OPTIONS = { git_timeout: 30.seconds } DEFAULT_OPTIONS = { git_timeout: 30.seconds}
attr_accessor :options, :project, :user attr_accessor :options, :project, :user
...@@ -34,16 +34,19 @@ module Gitlab ...@@ -34,16 +34,19 @@ module Gitlab
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 end
end end
end end
......
...@@ -5,37 +5,37 @@ module Gitlab ...@@ -5,37 +5,37 @@ 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
...@@ -45,6 +45,82 @@ module Gitlab ...@@ -45,6 +45,82 @@ module Gitlab
false false
end end
# 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
Gitlab::GitLogger.error(ex.message)
false
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
diffs = merge_repo.diff(default_options, common_commit, "source/#{merge_request.source_branch}")
else
common_commit = merge_repo.git.native(:merge_base, default_options, ["#{merge_request.target_branch}", "#{merge_request.source_branch}"]).strip
diffs = merge_repo.diff(default_options, common_commit, "#{merge_request.source_branch}")
end
return diffs
end
rescue Grit::Git::CommandFailed => ex
Gitlab::GitLogger.error(ex.message)
false
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
Gitlab::GitLogger.error(ex.message)
false
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
commits = merge_repo.commits_between("#{merge_request.target_branch}", "#{merge_request.source_branch}")
end
return commits
end
rescue Grit::Git::CommandFailed => ex
Gitlab::GitLogger.error(ex.message)
false
end
private private
# Merges the source_branch into the target_branch in the satellite. # Merges the source_branch into the target_branch in the satellite.
...@@ -54,18 +130,38 @@ module Gitlab ...@@ -54,18 +130,38 @@ 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
Gitlab::GitLogger.error(ex.message)
false
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 rescue Grit::Git::CommandFailed => ex
Gitlab::GitLogger.error(ex.message) Gitlab::GitLogger.error(ex.message)
false false
end end
end end
end end
end end
module Gitlab module Gitlab
class SatelliteNotExistError < StandardError; end class SatelliteNotExistError < StandardError;
end
module Satellite module Satellite
class Satellite class Satellite
...@@ -21,11 +22,14 @@ module Gitlab ...@@ -21,11 +22,14 @@ module Gitlab
raise SatelliteNotExistError.new("Satellite doesn't exist") raise SatelliteNotExistError.new("Satellite doesn't exist")
end end
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,14 +59,16 @@ module Gitlab ...@@ -55,14 +59,16 @@ 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
def lock_file def lock_file
create_locks_dir unless File.exists?(lock_files_dir) create_locks_dir unless File.exists?(lock_files_dir)
File.join(lock_files_dir, "satellite_#{project.id}.lock") File.join(lock_files_dir, "satellite_#{project.id}.lock")
...@@ -100,20 +106,35 @@ module Gitlab ...@@ -100,20 +106,35 @@ 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 stroing # Create directory for stroing
......
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) }
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
...@@ -2,12 +2,11 @@ require 'spec_helper' ...@@ -2,12 +2,11 @@ require 'spec_helper'
describe Projects::CommitController do describe Projects::CommitController do
let(:project) { create(:project_with_code) } let(:project) { create(:project_with_code) }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:commit) { project.repository.last_commit_for("master") } let(:commit) { project.repository.last_commit_for("master") }
before do before do
sign_in(user) sign_in(user)
project.team << [user, :master] project.team << [user, :master]
end end
......
...@@ -2,7 +2,7 @@ require 'spec_helper' ...@@ -2,7 +2,7 @@ require 'spec_helper'
describe Projects::CommitsController do describe Projects::CommitsController do
let(:project) { create(:project_with_code) } let(:project) { create(:project_with_code) }
let(:user) { create(:user) } let(:user) { create(:user) }
before do before do
sign_in(user) sign_in(user)
......
...@@ -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
......
<<<<<<< HEAD
include ActionDispatch::TestProcess include ActionDispatch::TestProcess
=======
require Rails.root.join('spec', 'support', 'test_env.rb')
>>>>>>> Merge Request on forked projects
FactoryGirl.define do FactoryGirl.define do
sequence :sentence, aliases: [:title, :content] do sequence :sentence, aliases: [:title, :content] do
...@@ -29,8 +33,19 @@ FactoryGirl.define do ...@@ -29,8 +33,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,14 +54,24 @@ FactoryGirl.define do ...@@ -39,14 +54,24 @@ 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
factory :group do factory :group do
sequence(:name) { |n| "group#{n}" } sequence(:name) { |n| "group#{n}" }
path { name.downcase.gsub(/\s/, '_') } path { name.downcase.gsub(/\s/, '_') }
...@@ -86,7 +111,8 @@ FactoryGirl.define do ...@@ -86,7 +111,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 +122,13 @@ FactoryGirl.define do ...@@ -96,13 +122,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
...@@ -133,7 +159,7 @@ FactoryGirl.define do ...@@ -133,7 +159,7 @@ FactoryGirl.define do
trait :on_commit do trait :on_commit do
project factory: :project_with_code project factory: :project_with_code
commit_id "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a" commit_id "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a"
noteable_type "Commit" noteable_type "Commit"
end end
...@@ -143,12 +169,12 @@ FactoryGirl.define do ...@@ -143,12 +169,12 @@ FactoryGirl.define do
trait :on_merge_request do trait :on_merge_request do
project factory: :project_with_code project factory: :project_with_code
noteable_id 1 noteable_id 1
noteable_type "MergeRequest" noteable_type "MergeRequest"
end end
trait :on_issue do trait :on_issue do
noteable_id 1 noteable_id 1
noteable_type "Issue" noteable_type "Issue"
end end
......
...@@ -5,8 +5,10 @@ INVALID_FACTORIES = [ ...@@ -5,8 +5,10 @@ INVALID_FACTORIES = [
:invalid_key, :invalid_key,
] ]
FactoryGirl.factories.map(&:name).each do |factory_name| FactoryGirl.factories.map(&:name).each do |factory_name|
next if INVALID_FACTORIES.include?(factory_name) next if INVALID_FACTORIES.include?(factory_name)
describe "#{factory_name} factory" do describe "#{factory_name} factory" do
it 'should be valid' do it 'should be valid' do
build(factory_name).should be_valid build(factory_name).should be_valid
......
...@@ -3,11 +3,11 @@ require 'spec_helper' ...@@ -3,11 +3,11 @@ 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]
u u
end end
before do before do
...@@ -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,8 +2,8 @@ require 'spec_helper' ...@@ -2,8 +2,8 @@ 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, source_project: project, target_project: project) }
before do before do
login_as :user login_as :user
...@@ -62,7 +62,7 @@ describe "On a merge request", js: true do ...@@ -62,7 +62,7 @@ describe "On a merge request", js: true do
it 'should be added and form reset' do it 'should be added and form reset' do
should have_content("This is awsome!") should have_content("This is awsome!")
within(".js-main-target-form") { should have_no_field("note[note]", with: "This is awesome!") } within(".js-main-target-form") { should have_no_field("note[note]", with: "This is awesome!") }
within(".js-main-target-form") { should have_css(".js-note-preview", visible: false) } within(".js-main-target-form") { should have_css(".js-note-preview", visible: false) }
within(".js-main-target-form") { should have_css(".js-note-text", visible: true) } within(".js-main-target-form") { should have_css(".js-note-text", visible: true) }
end end
...@@ -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
...@@ -205,13 +206,13 @@ describe "On a merge request diff", js: true, focus: true do ...@@ -205,13 +206,13 @@ describe "On a merge request diff", js: true, focus: true do
# TODO: fix # TODO: fix
#it 'should check if previews were rendered separately' do #it 'should check if previews were rendered separately' do
#within("tr[id='4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185'] + .js-temp-notes-holder") do #within("tr[id='4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185'] + .js-temp-notes-holder") do
#should have_css(".js-note-preview", text: "One comment on line 185") #should have_css(".js-note-preview", text: "One comment on line 185")
#end #end
#within("tr[id='342e16cbbd482ac2047dc679b2749d248cc1428f_18_17'] + .js-temp-notes-holder") do #within("tr[id='342e16cbbd482ac2047dc679b2749d248cc1428f_18_17'] + .js-temp-notes-holder") do
#should have_css(".js-note-preview", text: "Another comment on line 17") #should have_css(".js-note-preview", text: "Another comment on line 17")
#end #end
#end #end
end end
...@@ -238,39 +239,38 @@ describe "On a merge request diff", js: true, focus: true do ...@@ -238,39 +239,38 @@ 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
# TODO: fix # TODO: fix
#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
......
...@@ -14,13 +14,14 @@ describe "Application access" do ...@@ -14,13 +14,14 @@ describe "Application access" do
end end
describe "Project" do describe "Project" do
let(:project) { create(:project_with_code) } let(:project) { create(:project_with_code) }
let(:master) { create(:user) } let(:master) { create(:user) }
let(:guest) { create(:user) } let(:guest) { create(:user) }
let(:reporter) { create(:user) } let(:reporter) { create(:user) }
before do before do
# full access # full access
project.team << [master, :master] project.team << [master, :master]
...@@ -108,7 +109,7 @@ describe "Application access" do ...@@ -108,7 +109,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
...@@ -232,13 +233,13 @@ describe "Application access" do ...@@ -232,13 +233,13 @@ describe "Application access" do
describe "PublicProject" do describe "PublicProject" do
let(:project) { create(:project_with_code) } let(:project) { create(:project_with_code) }
let(:master) { create(:user) } let(:master) { create(:user) }
let(:guest) { create(:user) } let(:guest) { create(:user) }
let(:reporter) { create(:user) } let(:reporter) { create(:user) }
let(:admin) { create(:user) } let(:admin) { create(:user) }
before do before do
# public project # public project
...@@ -339,7 +340,7 @@ describe "Application access" do ...@@ -339,7 +340,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
pending "can't test this, doesn't seem to be a way to the the flock status on a file, throwing piles of processes at it seems lousy too"
repo = project.satellite.repo
first_call = false
(File.exists? project.satellite.lock_file).should be_false
test_file = ->(called) {
File.exists?(project.satellite.lock_file).should be_true
called.should be_true
File.readlines.should == "some test code"
File.truncate(project.satellite.lock, 0)
File.readlines.should == ""
}
write_file = ->(called, checker) {
if (File.exists?(project.satellite.lock_file))
file = File.open(project.satellite.lock, '+w')
file.write("some test code")
file.close
checker.call(called)
end
}
satellite_action = Gitlab::Satellite::Action.new(user, project)
satellite_action.send(:in_locked_and_timed_satellite) do |sat_repo|
write_file.call(first_call, test_file)
first_call = true
repo.should == sat_repo
(File.exists? project.satellite.lock_file).should be_true
end
first_call.should be_true
puts File.stat(project.satellite.lock_file).inspect
second_call = false
satellite_action.send(:in_locked_and_timed_satellite) do |sat_repo|
write_file.call(second_call, test_file)
second_call = true
repo.should == sat_repo
(File.exists? project.satellite.lock_file).should be_true
end
second_call.should be_true
(File.exists? project.satellite.lock_file).should be_true
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
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
commits.first.id.should == @one_after_stable[1]
commits.last.id.should == @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
commits.first.id.should == @wiki_branch[1]
commits.last.id.should == @master[1]
end
end
context 'between branches' do
it 'should get proper commits between' do
merge_request.target_branch = @one_after_stable[0]
merge_request.source_branch = @master[0]
commits = Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).commits_between
commits.first.id.should == @one_after_stable[1]
commits.last.id.should == @master[1]
merge_request.target_branch = @wiki_branch[0]
merge_request.source_branch = @master[0]
commits = Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).commits_between
commits.first.id.should == @wiki_branch[1]
commits.last.id.should == @master[1]
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
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).diffs_between_satellite
diffs.each {|a_diff| (diff.include? a_diff.diff).should be_true}
end
end
context 'between branches' do
it 'should get proper diffs' do
merge_request.target_branch = @close_commit1[0]
merge_request.source_branch = @wiki_branch[0]
diffs = Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).diffs_between_satellite
merge_request.target_branch = @close_commit1[0]
merge_request.source_branch = @master[0]
diff = Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).diffs_between_satellite
diffs.each {|a_diff| (diff.include? a_diff.diff).should be_true}
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,39 @@ describe MergeRequest do ...@@ -71,4 +68,39 @@ 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
...@@ -144,12 +144,12 @@ describe Note do ...@@ -144,12 +144,12 @@ describe Note do
end end
describe '#create_status_change_note' do describe '#create_status_change_note' do
let(:project) { create(:project) } let(:project) { create(:project) }
let(:thing) { create(:issue, project: project) } let(:thing) { create(:issue, project: project) }
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
...@@ -157,9 +157,9 @@ describe Note do ...@@ -157,9 +157,9 @@ describe Note do
end end
its(:noteable) { should == thing } its(:noteable) { should == thing }
its(:project) { should == thing.project } its(:project) { should == thing.project }
its(:author) { should == author } its(:author) { should == author }
its(:note) { should =~ /Status changed to #{status}/ } its(:note) { should =~ /Status changed to #{status}/ }
end end
describe :authorization do describe :authorization do
......
...@@ -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
......
require 'spec_helper' require 'spec_helper'
describe MergeRequestObserver do describe MergeRequestObserver do
let(:some_user) { create :user } let(:some_user) { create :user }
let(:assignee) { create :user } let(:assignee) { create :user }
let(:author) { create :user } let(:author) { create :user }
let(:mr_mock) { double(:merge_request, id: 42, assignee: assignee, author: author) } let(:mr_mock) { double(:merge_request, id: 42, assignee: assignee, author: author) }
let(:assigned_mr) { create(:merge_request, assignee: assignee, author: author) } let(:assigned_mr) { create(:merge_request, assignee: assignee, author: author) }
let(:unassigned_mr) { create(:merge_request, author: author) } let(:unassigned_mr) { create(:merge_request, author: author) }
let(:closed_assigned_mr) { create(:closed_merge_request, assignee: assignee, author: author) } let(:closed_assigned_mr) { create(:closed_merge_request, assignee: assignee, author: author) }
let(:closed_unassigned_mr) { create(:closed_merge_request, author: author) } let(:closed_unassigned_mr) { create(:closed_merge_request, author: author) }
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,42 @@ describe MergeRequestObserver do ...@@ -75,16 +77,42 @@ 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.enable_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,35 +42,91 @@ describe API::API do ...@@ -40,35 +42,91 @@ describe API::API do
end end
describe "POST /projects/:id/merge_requests" do describe "POST /projects/:id/merge_requests" do
it "should return merge_request" do context 'between branches projects' do
post api("/projects/#{project.id}/merge_requests", user), it "should return merge_request" do
title: 'Test merge_request', source_branch: "stable", target_branch: "master", author: user post api("/projects/#{project.id}/merge_requests", user),
response.status.should == 201 title: 'Test merge_request', source_branch: "stable", target_branch: "master", author: user
json_response['title'].should == 'Test merge_request' response.status.should == 201
end json_response['title'].should == 'Test merge_request'
end
it "should return 422 when source_branch equals target_branch" do it "should return 422 when source_branch equals target_branch" do
post api("/projects/#{project.id}/merge_requests", user), post api("/projects/#{project.id}/merge_requests", user),
title: "Test merge_request", source_branch: "master", target_branch: "master", author: user title: "Test merge_request", source_branch: "master", target_branch: "master", author: user
response.status.should == 422 response.status.should == 422
end end
it "should return 400 when source_branch is missing" do it "should return 400 when source_branch is missing" do
post api("/projects/#{project.id}/merge_requests", user), post api("/projects/#{project.id}/merge_requests", user),
title: "Test merge_request", target_branch: "master", author: user title: "Test merge_request", target_branch: "master", author: user
response.status.should == 400 response.status.should == 400
end end
it "should return 400 when target_branch is missing" do it "should return 400 when target_branch is missing" do
post api("/projects/#{project.id}/merge_requests", user), post api("/projects/#{project.id}/merge_requests", user),
title: "Test merge_request", source_branch: "stable", author: user title: "Test merge_request", source_branch: "stable", author: user
response.status.should == 400 response.status.should == 400
end
it "should return 400 when title is missing" do
post api("/projects/#{project.id}/merge_requests", user),
target_branch: 'master', source_branch: 'stable'
response.status.should == 400
end
end end
it "should return 400 when title is missing" do context 'forked projects' do
post api("/projects/#{project.id}/merge_requests", user), let!(:user2) {create(:user)}
target_branch: 'master', source_branch: 'stable' let!(:forked_project_link) { build(:forked_project_link) }
response.status.should == 400 let!(:fork_project) { create(:source_project_with_code, forked_project_link: forked_project_link, 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),
target_branch: 'master', source_branch: 'stable', author: user, target_project: fork_project.id
response.status.should == 400
end
end end
end end
...@@ -97,14 +155,14 @@ describe API::API do ...@@ -97,14 +155,14 @@ describe API::API do
it "should return 422 when source_branch and target_branch are renamed the same" do it "should return 422 when source_branch and target_branch are renamed the same" do
put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user),
source_branch: "master", target_branch: "master" source_branch: "master", target_branch: "master"
response.status.should == 422 response.status.should == 422
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) }
...@@ -14,7 +15,8 @@ describe API::API do ...@@ -14,7 +15,8 @@ describe API::API do
let!(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) } let!(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) }
let!(:users_project2) { create(:users_project, user: user3, project: project, project_access: UsersProject::DEVELOPER) } let!(:users_project2) { create(:users_project, user: user3, project: project, project_access: UsersProject::DEVELOPER) }
before { project.team << [user, :reporter] } before {
project.team << [user, :reporter] }
describe "GET /projects" do describe "GET /projects" do
context "when unauthenticated" do context "when unauthenticated" do
...@@ -46,16 +48,16 @@ describe API::API do ...@@ -46,16 +48,16 @@ describe API::API do
it "should not create new project" do it "should not create new project" do
expect { expect {
post api("/projects", user2), name: 'foo' post api("/projects", user2), name: 'foo'
}.to change {Project.count}.by(0) }.to change { Project.count }.by(0)
end end
end end
it "should create new project without path" do it "should create new project without path" do
expect { post api("/projects", user), name: 'foo' }.to change {Project.count}.by(1) expect { post api("/projects", user), name: 'foo' }.to change { Project.count }.by(1)
end end
it "should not create new project without name" do it "should not create new project without name" do
expect { post api("/projects", user) }.to_not change {Project.count} expect { post api("/projects", user) }.to_not change { Project.count }
end end
it "should return a 400 error if name not given" do it "should return a 400 error if name not given" do
...@@ -89,17 +91,17 @@ describe API::API do ...@@ -89,17 +91,17 @@ describe API::API do
it "should assign attributes to project" do it "should assign attributes to project" do
project = attributes_for(:project, { project = attributes_for(:project, {
description: Faker::Lorem.sentence, description: Faker::Lorem.sentence,
default_branch: 'stable', default_branch: 'stable',
issues_enabled: false, issues_enabled: false,
wall_enabled: false, wall_enabled: false,
merge_requests_enabled: false, merge_requests_enabled: false,
wiki_enabled: false wiki_enabled: false
}) })
post api("/projects", user), project post api("/projects", user), project
project.each_pair do |k,v| project.each_pair do |k, v|
next if k == :path next if k == :path
json_response[k.to_s].should == v json_response[k.to_s].should == v
end end
...@@ -125,11 +127,11 @@ describe API::API do ...@@ -125,11 +127,11 @@ describe API::API do
before { admin } before { admin }
it "should create new project without path" do it "should create new project without path" do
expect { post api("/projects/user/#{user.id}", admin), name: 'foo' }.to change {Project.count}.by(1) expect { post api("/projects/user/#{user.id}", admin), name: 'foo' }.to change { Project.count }.by(1)
end end
it "should not create new project without name" do it "should not create new project without name" do
expect { post api("/projects/user/#{user.id}", admin) }.to_not change {Project.count} expect { post api("/projects/user/#{user.id}", admin) }.to_not change { Project.count }
end end
it "should respond with 201 on success" do it "should respond with 201 on success" do
...@@ -144,17 +146,17 @@ describe API::API do ...@@ -144,17 +146,17 @@ describe API::API do
it "should assign attributes to project" do it "should assign attributes to project" do
project = attributes_for(:project, { project = attributes_for(:project, {
description: Faker::Lorem.sentence, description: Faker::Lorem.sentence,
default_branch: 'stable', default_branch: 'stable',
issues_enabled: false, issues_enabled: false,
wall_enabled: false, wall_enabled: false,
merge_requests_enabled: false, merge_requests_enabled: false,
wiki_enabled: false wiki_enabled: false
}) })
post api("/projects/user/#{user.id}", admin), project post api("/projects/user/#{user.id}", admin), project
project.each_pair do |k,v| project.each_pair do |k, v|
next if k == :path next if k == :path
json_response[k.to_s].should == v json_response[k.to_s].should == v
end end
...@@ -267,7 +269,7 @@ describe API::API do ...@@ -267,7 +269,7 @@ describe API::API do
it "should add user to project team" do it "should add user to project team" do
expect { expect {
post api("/projects/#{project.id}/members", user), user_id: user2.id, post api("/projects/#{project.id}/members", user), user_id: user2.id,
access_level: UsersProject::DEVELOPER access_level: UsersProject::DEVELOPER
}.to change { UsersProject.count }.by(1) }.to change { UsersProject.count }.by(1)
response.status.should == 201 response.status.should == 201
...@@ -277,10 +279,10 @@ describe API::API do ...@@ -277,10 +279,10 @@ describe API::API do
it "should return a 201 status if user is already project member" do it "should return a 201 status if user is already project member" do
post api("/projects/#{project.id}/members", user), user_id: user2.id, post api("/projects/#{project.id}/members", user), user_id: user2.id,
access_level: UsersProject::DEVELOPER access_level: UsersProject::DEVELOPER
expect { expect {
post api("/projects/#{project.id}/members", user), user_id: user2.id, post api("/projects/#{project.id}/members", user), user_id: user2.id,
access_level: UsersProject::DEVELOPER access_level: UsersProject::DEVELOPER
}.not_to change { UsersProject.count }.by(1) }.not_to change { UsersProject.count }.by(1)
response.status.should == 201 response.status.should == 201
...@@ -411,8 +413,8 @@ describe API::API do ...@@ -411,8 +413,8 @@ describe API::API do
it "should add hook to project" do it "should add hook to project" do
expect { expect {
post api("/projects/#{project.id}/hooks", user), post api("/projects/#{project.id}/hooks", user),
url: "http://example.com" url: "http://example.com"
}.to change {project.hooks.count}.by(1) }.to change { project.hooks.count }.by(1)
response.status.should == 201 response.status.should == 201
end end
...@@ -430,7 +432,7 @@ describe API::API do ...@@ -430,7 +432,7 @@ describe API::API do
describe "PUT /projects/:id/hooks/:hook_id" do describe "PUT /projects/:id/hooks/:hook_id" do
it "should update an existing project hook" do it "should update an existing project hook" do
put api("/projects/#{project.id}/hooks/#{hook.id}", user), put api("/projects/#{project.id}/hooks/#{hook.id}", user),
url: 'http://example.org' url: 'http://example.org'
response.status.should == 200 response.status.should == 200
json_response['url'].should == 'http://example.org' json_response['url'].should == 'http://example.org'
end end
...@@ -455,7 +457,7 @@ describe API::API do ...@@ -455,7 +457,7 @@ describe API::API do
it "should delete hook from project" do it "should delete hook from project" do
expect { expect {
delete api("/projects/#{project.id}/hooks/#{hook.id}", user) delete api("/projects/#{project.id}/hooks/#{hook.id}", user)
}.to change {project.hooks.count}.by(-1) }.to change { project.hooks.count }.by(-1)
response.status.should == 200 response.status.should == 200
end end
...@@ -501,26 +503,26 @@ describe API::API do ...@@ -501,26 +503,26 @@ describe API::API do
describe "POST /projects/:id/snippets" do describe "POST /projects/:id/snippets" do
it "should create a new project snippet" do it "should create a new project snippet" do
post api("/projects/#{project.id}/snippets", user), post api("/projects/#{project.id}/snippets", user),
title: 'api test', file_name: 'sample.rb', code: 'test' title: 'api test', file_name: 'sample.rb', code: 'test'
response.status.should == 201 response.status.should == 201
json_response['title'].should == 'api test' json_response['title'].should == 'api test'
end end
it "should return a 400 error if title is not given" do it "should return a 400 error if title is not given" do
post api("/projects/#{project.id}/snippets", user), post api("/projects/#{project.id}/snippets", user),
file_name: 'sample.rb', code: 'test' file_name: 'sample.rb', code: 'test'
response.status.should == 400 response.status.should == 400
end end
it "should return a 400 error if file_name not given" do it "should return a 400 error if file_name not given" do
post api("/projects/#{project.id}/snippets", user), post api("/projects/#{project.id}/snippets", user),
title: 'api test', code: 'test' title: 'api test', code: 'test'
response.status.should == 400 response.status.should == 400
end end
it "should return a 400 error if code not given" do it "should return a 400 error if code not given" do
post api("/projects/#{project.id}/snippets", user), post api("/projects/#{project.id}/snippets", user),
title: 'api test', file_name: 'sample.rb' title: 'api test', file_name: 'sample.rb'
response.status.should == 400 response.status.should == 400
end end
end end
...@@ -528,7 +530,7 @@ describe API::API do ...@@ -528,7 +530,7 @@ describe API::API do
describe "PUT /projects/:id/snippets/:shippet_id" do describe "PUT /projects/:id/snippets/:shippet_id" do
it "should update an existing project snippet" do it "should update an existing project snippet" do
put api("/projects/#{project.id}/snippets/#{snippet.id}", user), put api("/projects/#{project.id}/snippets/#{snippet.id}", user),
code: 'updated code' code: 'updated code'
response.status.should == 200 response.status.should == 200
json_response['title'].should == 'example' json_response['title'].should == 'example'
snippet.reload.content.should == 'updated code' snippet.reload.content.should == 'updated code'
...@@ -536,7 +538,7 @@ describe API::API do ...@@ -536,7 +538,7 @@ describe API::API do
it "should update an existing project snippet with new title" do it "should update an existing project snippet with new title" do
put api("/projects/#{project.id}/snippets/#{snippet.id}", user), put api("/projects/#{project.id}/snippets/#{snippet.id}", user),
title: 'other api test' title: 'other api test'
response.status.should == 200 response.status.should == 200
json_response['title'].should == 'other api test' json_response['title'].should == 'other api test'
end end
...@@ -598,7 +600,7 @@ describe API::API do ...@@ -598,7 +600,7 @@ describe API::API do
describe "POST /projects/:id/keys" do describe "POST /projects/:id/keys" do
it "should not create an invalid ssh key" do it "should not create an invalid ssh key" do
post api("/projects/#{project.id}/keys", user), { title: "invalid key" } post api("/projects/#{project.id}/keys", user), {title: "invalid key"}
response.status.should == 404 response.status.should == 404
end end
...@@ -606,7 +608,7 @@ describe API::API do ...@@ -606,7 +608,7 @@ describe API::API do
key_attrs = attributes_for :key key_attrs = attributes_for :key
expect { expect {
post api("/projects/#{project.id}/keys", user), key_attrs post api("/projects/#{project.id}/keys", user), key_attrs
}.to change{ project.deploy_keys.count }.by(1) }.to change { project.deploy_keys.count }.by(1)
end end
end end
...@@ -616,7 +618,7 @@ describe API::API do ...@@ -616,7 +618,7 @@ describe API::API do
it "should delete existing key" do it "should delete existing key" do
expect { expect {
delete api("/projects/#{project.id}/keys/#{deploy_key.id}", user) delete api("/projects/#{project.id}/keys/#{deploy_key.id}", user)
}.to change{ project.deploy_keys.count }.by(-1) }.to change { project.deploy_keys.count }.by(-1)
end end
it "should return 404 Not Found with invalid ID" do it "should return 404 Not Found with invalid ID" do
......
...@@ -156,7 +156,8 @@ describe NotificationService do ...@@ -156,7 +156,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
......
...@@ -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
......
...@@ -48,8 +48,11 @@ Spork.prefork do ...@@ -48,8 +48,11 @@ Spork.prefork do
# instead of true. # instead of true.
config.use_transactional_fixtures = false config.use_transactional_fixtures = false
config.before do config.before(:suite) do
TestEnv.init(observers: false) TestEnv.init(observers: false, init_repos: true, repos: false)
end
config.before(:each) do
TestEnv.setup_stubs
end end
end end
end end
......
...@@ -27,17 +27,65 @@ module TestEnv ...@@ -27,17 +27,65 @@ module TestEnv
# Disable mailer for spinach tests # Disable mailer for spinach tests
disable_mailer if opts[:mailer] == false disable_mailer if opts[:mailer] == false
setup_stubs
# Use tmp dir for FS manipulations clear_test_repo_dir if opts[:init_repos] == true
repos_path = Rails.root.join('tmp', 'test-git-base-path') setup_test_repos(opts) if opts[:repos] == true
Gitlab.config.gitlab_shell.stub(repos_path: repos_path) end
Gitlab::Git::Repository.stub(repos_path: repos_path)
def testing_path
Rails.root.join('tmp', 'test-git-base-path')
end
def seed_repo_path
Rails.root.join('tmp', 'repositories', 'gitlabhq')
end
def seed_satellite_path
Rails.root.join('tmp', 'satellite', 'gitlabhq')
end
def satellite_path
"#{testing_path()}/satellite"
end
def repo(namespace, name)
unless (namespace.nil? || namespace.path.nil? || namespace.path.strip.empty?)
repo = File.join(testing_path(), "#{namespace.path}/#{name}.git")
else
repo = File.join(testing_path(), "#{name}.git")
end
end
def satellite(namespace, name)
unless (namespace.nil? || namespace.path.nil? || namespace.path.strip.empty?)
satellite_repo = File.join(satellite_path, namespace.path, name)
else
satellite_repo = File.join(satellite_path, name)
end
end
def setup_test_repos(opts ={})
create_repo(nil, 'gitlabhq') #unless opts[:repo].nil? || !opts[:repo].include?('')
create_repo(nil, 'source_gitlabhq') #unless opts[:repo].nil? || !opts[:repo].include?('source_')
create_repo(nil, 'target_gitlabhq') #unless opts[:repo].nil? || !opts[:repo].include?('target_')
end
def setup_stubs()
# Use tmp dir for FS manipulations
repos_path = testing_path()
GollumWiki.any_instance.stub(:init_repo) do |path| GollumWiki.any_instance.stub(:init_repo) do |path|
create_temp_repo(File.join(repos_path, "#{path}.git")) create_temp_repo(File.join(repos_path, "#{path}.git"))
end end
Gitlab.config.gitlab_shell.stub(repos_path: repos_path)
Gitlab.config.satellites.stub(path: satellite_path)
Gitlab::Git::Repository.stub(repos_path: repos_path)
Gitlab::Shell.any_instance.stub( Gitlab::Shell.any_instance.stub(
add_repository: true, add_repository: true,
mv_repository: true, mv_repository: true,
...@@ -48,24 +96,54 @@ module TestEnv ...@@ -48,24 +96,54 @@ module TestEnv
) )
Gitlab::Satellite::Satellite.any_instance.stub( Gitlab::Satellite::Satellite.any_instance.stub(
exists?: true, exists?: true,
destroy: true, destroy: true,
create: true create: true,
lock_files_dir: repos_path
) )
MergeRequest.any_instance.stub( MergeRequest.any_instance.stub(
check_if_can_be_merged: true check_if_can_be_merged: true
) )
Repository.any_instance.stub( Repository.any_instance.stub(
size: 12.45 size: 12.45
) )
end
def clear_test_repo_dir
setup_stubs
# Use tmp dir for FS manipulations
repos_path = testing_path()
# Remove tmp/test-git-base-path # Remove tmp/test-git-base-path
FileUtils.rm_rf Gitlab.config.gitlab_shell.repos_path FileUtils.rm_rf Gitlab.config.gitlab_shell.repos_path
# Recreate tmp/test-git-base-path # Recreate tmp/test-git-base-path
FileUtils.mkdir_p Gitlab.config.gitlab_shell.repos_path FileUtils.mkdir_p Gitlab.config.gitlab_shell.repos_path
#Since much more is happening in satellites
FileUtils.mkdir_p Gitlab.config.satellites.path
end
def clear_repo_dir(namespace, name)
setup_stubs
#Clean any .wiki.git that may have been created
FileUtils.rm_rf File.join(testing_path(), "#{name}.wiki.git")
end
#Create a repo and it's satellite
def create_repo(namespace, name)
setup_stubs
repo = repo(namespace, name)
# Symlink tmp/repositories/gitlabhq to tmp/test-git-base-path/gitlabhq
system("ln -s -f #{seed_repo_path()} #{repo}")
create_satellite(repo, namespace, name)
end
# Create a testing satellite, and clone the source repo into it
def create_satellite(source_repo, namespace, satellite_name)
satellite_repo = satellite(namespace, satellite_name)
# Symlink tmp/satellite/gitlabhq to tmp/test-git-base-path/satellite/gitlabhq
system("ln -s -f #{seed_satellite_path()} #{satellite_repo}")
end end
def create_temp_repo(path) def create_temp_repo(path)
......
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