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
This commit is contained in:
parent
fd03367193
commit
3d7194f011
|
@ -415,6 +415,17 @@ img.emoji {
|
|||
@extend .light-well;
|
||||
@extend .light;
|
||||
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 {
|
||||
|
|
|
@ -12,7 +12,7 @@ class FilterContext
|
|||
|
||||
def apply_filter items
|
||||
if params[:project_id]
|
||||
items = items.where(project_id: params[:project_id])
|
||||
items = items.by_project(params[:project_id])
|
||||
end
|
||||
|
||||
if params[:search].present?
|
||||
|
@ -20,12 +20,12 @@ class FilterContext
|
|||
end
|
||||
|
||||
case params[:status]
|
||||
when 'closed'
|
||||
items.closed
|
||||
when 'all'
|
||||
items
|
||||
else
|
||||
items.opened
|
||||
when 'closed'
|
||||
items.closed
|
||||
when 'all'
|
||||
items
|
||||
else
|
||||
items.opened
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -14,7 +14,7 @@ class MergeRequestsLoadContext < BaseContext
|
|||
end
|
||||
|
||||
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)?
|
||||
if params[:assignee_id].present?
|
||||
|
|
|
@ -19,7 +19,7 @@ class SearchContext
|
|||
if params[:search_code].present?
|
||||
result[:blobs] = project.repository.search_files(query, params[:repository_ref]) unless project.empty_repo?
|
||||
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[:wiki_pages] = []
|
||||
end
|
||||
|
|
|
@ -24,8 +24,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
format.html
|
||||
format.js
|
||||
|
||||
format.diff { render text: @merge_request.to_diff }
|
||||
format.patch { render text: @merge_request.to_patch }
|
||||
format.diff { render text: @merge_request.to_diff(current_user) }
|
||||
format.patch { render text: @merge_request.to_patch(current_user) }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -33,25 +33,39 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
@commit = @merge_request.last_commit
|
||||
|
||||
@comments_allowed = @reply_allowed = true
|
||||
@comments_target = { noteable_type: 'MergeRequest',
|
||||
noteable_id: @merge_request.id }
|
||||
@comments_target = {noteable_type: 'MergeRequest',
|
||||
noteable_id: @merge_request.id}
|
||||
@line_notes = @merge_request.notes.where("line_code is not null")
|
||||
end
|
||||
|
||||
def new
|
||||
@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
|
||||
|
||||
def edit
|
||||
@target_branches = @merge_request.target_project.repository.branch_names
|
||||
end
|
||||
|
||||
def create
|
||||
@merge_request = @project.merge_requests.new(params[:merge_request])
|
||||
@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
|
||||
@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
|
||||
render "new"
|
||||
end
|
||||
|
@ -89,22 +103,36 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def branch_from
|
||||
#This is always source
|
||||
@commit = @repository.commit(params[:ref])
|
||||
end
|
||||
|
||||
def branch_to
|
||||
@commit = @repository.commit(params[:ref])
|
||||
@target_project = selected_target_project
|
||||
@commit = @target_project.repository.commit(params[:ref])
|
||||
end
|
||||
|
||||
def update_branches
|
||||
@target_project = selected_target_project
|
||||
@target_branches = (@target_project.repository.branch_names).unshift("Select branch")
|
||||
@target_branches
|
||||
end
|
||||
|
||||
|
||||
def ci_status
|
||||
status = project.gitlab_ci_service.commit_status(merge_request.last_commit.sha)
|
||||
response = { status: status }
|
||||
response = {status: status}
|
||||
|
||||
render json: response
|
||||
end
|
||||
|
||||
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
|
||||
@merge_request ||= @project.merge_requests.find(params[:id])
|
||||
end
|
||||
|
@ -123,11 +151,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
|
||||
def validates_merge_request
|
||||
# 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
|
||||
# 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
|
||||
|
||||
def define_show_vars
|
||||
|
|
|
@ -108,8 +108,8 @@ module CommitsHelper
|
|||
end
|
||||
end
|
||||
|
||||
def commit_to_html commit
|
||||
escape_javascript(render 'projects/commits/commit', commit: commit)
|
||||
def commit_to_html commit, project
|
||||
escape_javascript(render 'projects/commits/commit', commit: commit, project: project) unless commit.nil?
|
||||
end
|
||||
|
||||
def diff_line_content(line)
|
||||
|
|
|
@ -1,15 +1,29 @@
|
|||
module MergeRequestsHelper
|
||||
def new_mr_path_from_push_event(event)
|
||||
new_project_merge_request_path(
|
||||
event.project,
|
||||
merge_request: {
|
||||
source_branch: event.branch_name,
|
||||
target_branch: event.project.repository.root_ref,
|
||||
title: event.branch_name.titleize
|
||||
}
|
||||
event.project,
|
||||
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,
|
||||
target_branch: target_project.repository.root_ref,
|
||||
title: event.branch_name.titleize
|
||||
}
|
||||
end
|
||||
|
||||
def mr_css_classes mr
|
||||
classes = "merge-request"
|
||||
classes << " closed" if mr.closed?
|
||||
|
@ -18,6 +32,6 @@ module MergeRequestsHelper
|
|||
end
|
||||
|
||||
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
|
||||
|
|
|
@ -2,28 +2,65 @@ module Emails
|
|||
module MergeRequests
|
||||
def new_merge_request_email(recipient_id, merge_request_id)
|
||||
@merge_request = MergeRequest.find(merge_request_id)
|
||||
@project = @merge_request.project
|
||||
mail(to: recipient(recipient_id), subject: subject("new merge request !#{@merge_request.id}", @merge_request.title))
|
||||
mail(to: @merge_request.assignee_email, subject: subject("new merge request !#{@merge_request.id}", @merge_request.title))
|
||||
end
|
||||
|
||||
def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id)
|
||||
@merge_request = MergeRequest.find(merge_request_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))
|
||||
end
|
||||
|
||||
def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id)
|
||||
@merge_request = MergeRequest.find(merge_request_id)
|
||||
@project = @merge_request.project
|
||||
@updated_by = User.find updated_by_user_id
|
||||
mail(to: recipient(recipient_id), subject: subject("Closed merge request !#{@merge_request.id}", @merge_request.title))
|
||||
end
|
||||
|
||||
def merged_merge_request_email(recipient_id, 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))
|
||||
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
|
||||
|
|
|
@ -9,19 +9,14 @@ module Issuable
|
|||
include Mentionable
|
||||
|
||||
included do
|
||||
belongs_to :project
|
||||
belongs_to :author, class_name: "User"
|
||||
belongs_to :assignee, class_name: "User"
|
||||
belongs_to :milestone
|
||||
has_many :notes, as: :noteable, dependent: :destroy
|
||||
|
||||
validates :project, presence: true
|
||||
validates :author, presence: true
|
||||
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 :recent, -> { order("created_at DESC") }
|
||||
scope :assigned, -> { where("assignee_id IS NOT NULL") }
|
||||
|
|
|
@ -17,8 +17,18 @@
|
|||
#
|
||||
|
||||
class Issue < ActiveRecord::Base
|
||||
|
||||
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,
|
||||
:milestone_id, :label_list, :author_id_of_changes,
|
||||
:state_event
|
||||
|
|
|
@ -2,30 +2,37 @@
|
|||
#
|
||||
# Table name: merge_requests
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# target_branch :string(255) not null
|
||||
# source_branch :string(255) not null
|
||||
# project_id :integer not null
|
||||
# author_id :integer
|
||||
# assignee_id :integer
|
||||
# title :string(255)
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# st_commits :text(2147483647)
|
||||
# st_diffs :text(2147483647)
|
||||
# milestone_id :integer
|
||||
# state :string(255)
|
||||
# merge_status :string(255)
|
||||
# id :integer not null, primary key
|
||||
# target_project_id :integer not null
|
||||
# target_branch :string(255) not null
|
||||
# source_project_id :integer not null
|
||||
# source_branch :string(255) not null
|
||||
# author_id :integer
|
||||
# assignee_id :integer
|
||||
# title :string(255)
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# st_commits :text(2147483647)
|
||||
# st_diffs :text(2147483647)
|
||||
# milestone_id :integer
|
||||
# state :string(255)
|
||||
# merge_status :string(255)
|
||||
#
|
||||
|
||||
require Rails.root.join("app/models/commit")
|
||||
require Rails.root.join("lib/static_model")
|
||||
|
||||
class MergeRequest < ActiveRecord::Base
|
||||
|
||||
include Issuable
|
||||
|
||||
attr_accessible :title, :assignee_id, :target_branch, :source_branch, :milestone_id,
|
||||
:author_id_of_changes, :state_event
|
||||
belongs_to :target_project,:foreign_key => :target_project_id, class_name: "Project"
|
||||
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
|
||||
|
||||
|
@ -74,22 +81,29 @@ class MergeRequest < ActiveRecord::Base
|
|||
serialize :st_commits
|
||||
serialize :st_diffs
|
||||
|
||||
validates :source_project, presence: true
|
||||
validates :source_branch, presence: true
|
||||
validates :target_project, 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 :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 :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
|
||||
# both merged and closed mr's
|
||||
scope :closed, -> { with_states(:closed, :merged) }
|
||||
|
||||
def validate_branches
|
||||
if target_branch == source_branch
|
||||
errors.add :branch_conflict, "You can not use same branch for source and target branches"
|
||||
if target_project==source_project && target_branch == source_branch
|
||||
errors.add :branch_conflict, "You can not use same project/branch for source and target"
|
||||
end
|
||||
|
||||
if opened? || reopened?
|
||||
|
@ -137,7 +151,14 @@ class MergeRequest < ActiveRecord::Base
|
|||
end
|
||||
|
||||
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
|
||||
|
||||
def last_commit
|
||||
|
@ -145,11 +166,11 @@ class MergeRequest < ActiveRecord::Base
|
|||
end
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
def commits
|
||||
|
@ -158,24 +179,30 @@ class MergeRequest < ActiveRecord::Base
|
|||
|
||||
def probably_merged?
|
||||
unmerged_commits.empty? &&
|
||||
commits.any? && opened?
|
||||
commits.any? && opened?
|
||||
end
|
||||
|
||||
def reloaded_commits
|
||||
if opened? && unmerged_commits.any?
|
||||
self.st_commits = dump_commits(unmerged_commits)
|
||||
save
|
||||
|
||||
end
|
||||
commits
|
||||
end
|
||||
|
||||
def unmerged_commits
|
||||
self.project.repository.
|
||||
commits_between(self.target_branch, self.source_branch).
|
||||
sort_by(&:created_at).
|
||||
reverse
|
||||
commits = Gitlab::Satellite::MergeAction.new(self.author,self).commits_between
|
||||
commits = commits.map{ |commit| Gitlab::Git::Commit.new(commit, nil) }
|
||||
if commits.present?
|
||||
commits = Commit.decorate(commits).
|
||||
sort_by(&:created_at).
|
||||
reverse
|
||||
end
|
||||
commits
|
||||
end
|
||||
|
||||
|
||||
def merge!(user_id)
|
||||
self.author_id_of_changes = user_id
|
||||
self.merge
|
||||
|
@ -195,25 +222,33 @@ class MergeRequest < ActiveRecord::Base
|
|||
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)
|
||||
end
|
||||
|
||||
# Returns the raw diff for this merge request
|
||||
#
|
||||
# see "git diff"
|
||||
def to_diff
|
||||
project.repo.git.native(:diff, {timeout: 30, raise: true}, "#{target_branch}...#{source_branch}")
|
||||
def to_diff(current_user)
|
||||
Gitlab::Satellite::MergeAction.new(current_user, self).diff_in_satellite
|
||||
end
|
||||
|
||||
|
||||
# Returns the commit as a series of email patches.
|
||||
#
|
||||
# see "git format-patch"
|
||||
def to_patch
|
||||
project.repo.git.format_patch({timeout: 30, raise: true, stdout: true}, "#{target_branch}..#{source_branch}")
|
||||
def to_patch(current_user)
|
||||
Gitlab::Satellite::MergeAction.new(current_user, self).format_patch
|
||||
end
|
||||
|
||||
def last_commit_short_sha
|
||||
@last_commit_short_sha ||= last_commit.sha[0..10]
|
||||
end
|
||||
|
||||
def for_fork?
|
||||
target_project != source_project
|
||||
end
|
||||
|
||||
def disallow_source_branch_removal?
|
||||
(source_project.root_ref? source_branch) || for_fork?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def dump_commits(commits)
|
||||
|
|
|
@ -32,8 +32,8 @@ class Note < ActiveRecord::Base
|
|||
delegate :name, :email, to: :author, prefix: true
|
||||
|
||||
validates :note, :project, presence: 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 :line_code, format: {with: /\A[a-z0-9]+_\d+_\d+\Z/}, allow_blank: true
|
||||
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 :commit_id, presence: true, if: ->(n) { n.noteable_type == 'Commit' }
|
||||
|
@ -45,24 +45,24 @@ class Note < ActiveRecord::Base
|
|||
scope :inline, -> { where("line_code IS NOT NULL") }
|
||||
scope :not_inline, -> { where(line_code: [nil, '']) }
|
||||
|
||||
scope :common, ->{ where(noteable_type: ["", nil]) }
|
||||
scope :fresh, ->{ order("created_at ASC, id ASC") }
|
||||
scope :inc_author_project, ->{ includes(:project, :author) }
|
||||
scope :inc_author, ->{ includes(:author) }
|
||||
scope :common, -> { where(noteable_type: ["", nil]) }
|
||||
scope :fresh, -> { order("created_at ASC, id ASC") }
|
||||
scope :inc_author_project, -> { includes(:project, :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({
|
||||
noteable: noteable,
|
||||
project: noteable.project,
|
||||
author: author,
|
||||
note: "_Status changed to #{status}_"
|
||||
}, without_protection: true)
|
||||
noteable: noteable,
|
||||
project: project,
|
||||
author: author,
|
||||
note: "_Status changed to #{status}_"
|
||||
}, without_protection: true)
|
||||
end
|
||||
|
||||
def commit_author
|
||||
@commit_author ||=
|
||||
project.users.find_by_email(noteable.author_email) ||
|
||||
project.users.find_by_name(noteable.author_name)
|
||||
project.users.find_by_email(noteable.author_email) ||
|
||||
project.users.find_by_name(noteable.author_name)
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
|
@ -97,8 +97,8 @@ class Note < ActiveRecord::Base
|
|||
# otherwise false is returned
|
||||
def downvote?
|
||||
votable? && (note.start_with?('-1') ||
|
||||
note.start_with?(':-1:')
|
||||
)
|
||||
note.start_with?(':-1:')
|
||||
)
|
||||
end
|
||||
|
||||
def for_commit?
|
||||
|
@ -136,8 +136,8 @@ class Note < ActiveRecord::Base
|
|||
else
|
||||
super
|
||||
end
|
||||
# Temp fix to prevent app crash
|
||||
# if note commit id doesn't exist
|
||||
# Temp fix to prevent app crash
|
||||
# if note commit id doesn't exist
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
|
@ -146,8 +146,8 @@ class Note < ActiveRecord::Base
|
|||
# otherwise false is returned
|
||||
def upvote?
|
||||
votable? && (note.start_with?('+1') ||
|
||||
note.start_with?(':+1:')
|
||||
)
|
||||
note.start_with?(':+1:')
|
||||
)
|
||||
end
|
||||
|
||||
def votable?
|
||||
|
|
|
@ -53,7 +53,7 @@ class Project < ActiveRecord::Base
|
|||
|
||||
has_many :services, 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 :milestones, dependent: :destroy
|
||||
has_many :notes, dependent: :destroy
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
class ActivityObserver < BaseObserver
|
||||
observe :issue, :merge_request, :note, :milestone
|
||||
observe :issue, :note, :milestone
|
||||
|
||||
def after_create(record)
|
||||
event_author_id = record.author_id
|
||||
|
@ -13,47 +13,27 @@ class ActivityObserver < BaseObserver
|
|||
end
|
||||
|
||||
if event_author_id
|
||||
Event.create(
|
||||
project: record.project,
|
||||
target_id: record.id,
|
||||
target_type: record.class.name,
|
||||
action: Event.determine_action(record),
|
||||
author_id: event_author_id
|
||||
)
|
||||
create_event(record, Event.determine_action(record))
|
||||
end
|
||||
end
|
||||
|
||||
def after_close(record, transition)
|
||||
Event.create(
|
||||
project: record.project,
|
||||
target_id: record.id,
|
||||
target_type: record.class.name,
|
||||
action: Event::CLOSED,
|
||||
author_id: record.author_id_of_changes
|
||||
)
|
||||
create_event(record, Event::CLOSED)
|
||||
end
|
||||
|
||||
def after_reopen(record, transition)
|
||||
Event.create(
|
||||
project: record.project,
|
||||
target_id: record.id,
|
||||
target_type: record.class.name,
|
||||
action: Event::REOPENED,
|
||||
author_id: record.author_id_of_changes
|
||||
)
|
||||
create_event(record, Event::REOPENED)
|
||||
end
|
||||
|
||||
def after_merge(record, transition)
|
||||
# Since MR can be merged via sidekiq
|
||||
# to prevent event duplication do this check
|
||||
return true if record.merge_event
|
||||
protected
|
||||
|
||||
def create_event(record, status)
|
||||
Event.create(
|
||||
project: record.project,
|
||||
target_id: record.id,
|
||||
target_type: record.class.name,
|
||||
action: Event::MERGED,
|
||||
author_id: record.author_id_of_changes
|
||||
project: record.project,
|
||||
target_id: record.id,
|
||||
target_type: record.class.name,
|
||||
action: status,
|
||||
author_id: record.author_id
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -23,6 +23,6 @@ class IssueObserver < BaseObserver
|
|||
|
||||
# Create issue note with service comment like 'Status changed to closed'
|
||||
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
|
||||
|
|
|
@ -1,23 +1,56 @@
|
|||
class MergeRequestObserver < BaseObserver
|
||||
class MergeRequestObserver < ActivityObserver
|
||||
observe :merge_request
|
||||
cattr_accessor :current_user
|
||||
|
||||
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)
|
||||
end
|
||||
|
||||
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)
|
||||
end
|
||||
|
||||
def after_merge(merge_request, transition)
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
def after_update(merge_request)
|
||||
notification.reassigned_merge_request(merge_request, current_user) if merge_request.is_being_reassigned?
|
||||
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
|
||||
|
|
|
@ -23,7 +23,7 @@ class NotificationService
|
|||
# * project team members with notification level higher then Participating
|
||||
#
|
||||
def new_issue(issue, current_user)
|
||||
new_resource_email(issue, 'new_issue_email')
|
||||
new_resource_email(issue, issue.project, 'new_issue_email')
|
||||
end
|
||||
|
||||
# When we close an issue we should send next emails:
|
||||
|
@ -33,7 +33,7 @@ class NotificationService
|
|||
# * project team members with notification level higher then Participating
|
||||
#
|
||||
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
|
||||
|
||||
# When we reassign an issue we should send next emails:
|
||||
|
@ -42,7 +42,7 @@ class NotificationService
|
|||
# * issue new assignee if his notification level is not Disabled
|
||||
#
|
||||
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
|
||||
|
||||
|
||||
|
@ -51,7 +51,7 @@ class NotificationService
|
|||
# * mr assignee if his notification level is not Disabled
|
||||
#
|
||||
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
|
||||
|
||||
# When we reassign a merge_request we should send next emails:
|
||||
|
@ -60,7 +60,7 @@ class NotificationService
|
|||
# * merge_request assignee if his notification level is not Disabled
|
||||
#
|
||||
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
|
||||
|
||||
# When we close a merge request we should send next emails:
|
||||
|
@ -70,7 +70,7 @@ class NotificationService
|
|||
# * project team members with notification level higher then Participating
|
||||
#
|
||||
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
|
||||
|
||||
# When we merge a merge request we should send next emails:
|
||||
|
@ -80,8 +80,10 @@ class NotificationService
|
|||
# * project team members with notification level higher then Participating
|
||||
#
|
||||
def merge_mr(merge_request)
|
||||
recipients = reject_muted_users([merge_request.author, merge_request.assignee], merge_request.project)
|
||||
recipients = recipients.concat(project_watchers(merge_request.project)).uniq
|
||||
recipients = reject_muted_users([merge_request.author, merge_request.assignee], merge_request.source_project)
|
||||
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|
|
||||
mailer.merged_merge_request_email(recipient.id, merge_request.id)
|
||||
|
@ -102,7 +104,7 @@ class NotificationService
|
|||
# ignore wall messages
|
||||
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?
|
||||
opts.merge!(commit_id: note.commit_id)
|
||||
|
@ -191,14 +193,14 @@ class NotificationService
|
|||
end
|
||||
end
|
||||
|
||||
def new_resource_email(target, method)
|
||||
def new_resource_email(target, project, method)
|
||||
if target.respond_to?(:participants)
|
||||
recipients = target.participants
|
||||
else
|
||||
recipients = []
|
||||
end
|
||||
recipients = reject_muted_users(recipients, target.project)
|
||||
recipients = recipients.concat(project_watchers(target.project)).uniq
|
||||
recipients = reject_muted_users(recipients, project)
|
||||
recipients = recipients.concat(project_watchers(project)).uniq
|
||||
recipients.delete(target.author)
|
||||
|
||||
recipients.each do |recipient|
|
||||
|
@ -206,9 +208,9 @@ class NotificationService
|
|||
end
|
||||
end
|
||||
|
||||
def close_resource_email(target, current_user, method)
|
||||
recipients = reject_muted_users([target.author, target.assignee], target.project)
|
||||
recipients = recipients.concat(project_watchers(target.project)).uniq
|
||||
def close_resource_email(target, project, current_user, method)
|
||||
recipients = reject_muted_users([target.author, target.assignee], project)
|
||||
recipients = recipients.concat(project_watchers(project)).uniq
|
||||
recipients.delete(current_user)
|
||||
|
||||
recipients.each do |recipient|
|
||||
|
@ -216,14 +218,14 @@ class NotificationService
|
|||
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])
|
||||
|
||||
# Add watchers to email list
|
||||
recipients = recipients.concat(project_watchers(target.project))
|
||||
recipients = recipients.concat(project_watchers(project))
|
||||
|
||||
# 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
|
||||
recipients.delete(current_user)
|
||||
|
|
|
@ -9,6 +9,9 @@
|
|||
= time_ago_in_words(event.created_at)
|
||||
ago.
|
||||
.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
|
||||
- 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
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
:plain
|
||||
$(".target_branch").html("#{escape_javascript(options_for_select(@target_branches))}");
|
||||
$(".target_branch").trigger("liszt:updated");
|
||||
$(".mr_target_commit").html("");
|
||||
|
||||
|
||||
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
%p
|
||||
= link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.project, @merge_request)
|
||||
%p
|
||||
Branches: #{@merge_request.source_branch} → #{@merge_request.target_branch}
|
||||
Projects:Branches: #{@merge_request.source_project.path_with_namespace}:#{@merge_request.source_branch} → #{@merge_request.target_project.path_with_namespace}:#{@merge_request.target_branch}
|
||||
%p
|
||||
Assignee: #{@merge_request.author_name} → #{@merge_request.assignee_name}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
= "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}
|
||||
Assignee: #{@merge_request.assignee_name}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
%p
|
||||
= "Merge Request #{@merge_request.id} was merged"
|
||||
%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
|
||||
Branches: #{@merge_request.source_branch} → #{@merge_request.target_branch}
|
||||
Projects:Branches: #{@merge_request.source_project.path_with_namespace}:#{@merge_request.source_branch} → #{@merge_request.target_project.path_with_namespace}:#{@merge_request.target_branch}
|
||||
%p
|
||||
Assignee: #{@merge_request.author_name} → #{@merge_request.assignee_name}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
= "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}
|
||||
Assignee: #{@merge_request.assignee_name}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
%p
|
||||
= "New Merge Request !#{@merge_request.id}"
|
||||
%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
|
||||
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}
|
||||
%p
|
||||
Assignee: #{@merge_request.author_name} → #{@merge_request.assignee_name}
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
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 %>
|
||||
Asignee: <%= @merge_request.assignee_name %>
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
%p
|
||||
- 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
|
||||
= 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}
|
||||
%cite "#{truncate(@merge_request.title, length: 20)}"
|
||||
= render 'note_message'
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
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 %>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
%p
|
||||
= "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
|
||||
Assignee changed
|
||||
- if @previous_assignee
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
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 %>
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
%li.commit
|
||||
.browse_code_link_holder
|
||||
%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
|
||||
= 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)
|
||||
|
||||
= 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_ago_in_words(commit.committed_date)
|
||||
|
@ -14,7 +14,7 @@
|
|||
|
||||
|
||||
%span.notes_count
|
||||
- notes = @project.notes.for_commit_id(commit.id)
|
||||
- notes = project.notes.for_commit_id(commit.id)
|
||||
- if notes.any?
|
||||
%span.badge.badge-info
|
||||
%i.icon-comment
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
.title
|
||||
%i.icon-calendar
|
||||
%span= day.stamp("28 Aug, 2010")
|
||||
|
||||
.pull-right
|
||||
%small= pluralize(commits.count, 'commit')
|
||||
%ul.well-list= render commits
|
||||
%ul.well-list= render commits, :project => @project
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
%div.ui-box
|
||||
.title
|
||||
Commits (#{@commits.count})
|
||||
%ul.well-list= render Commit.decorate(@commits)
|
||||
%ul.well-list= render Commit.decorate(@commits), project: @project
|
||||
|
||||
- unless @diffs.empty?
|
||||
%h4 Diff
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
= 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?
|
||||
.alert.alert-error
|
||||
%ul
|
||||
|
@ -12,18 +12,20 @@
|
|||
.row
|
||||
.span5
|
||||
.light-well
|
||||
%h5.cgray From (Head Branch)
|
||||
= f.select(:source_branch, @repository.branch_names, { include_blank: "Select branch" }, {class: 'chosen span4'})
|
||||
%h5.cgray From
|
||||
.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
|
||||
|
||||
.span2
|
||||
%h1.merge-request-angle
|
||||
%i.icon-angle-right
|
||||
.span5
|
||||
.light-well
|
||||
%h5.cgray To (Base Branch)
|
||||
= f.select(:target_branch, @repository.branch_names, { include_blank: "Select branch" }, {class: 'chosen span4'})
|
||||
.mr_target_commit.prepend-top-10
|
||||
%h5.cgray To
|
||||
- projects = @project.forked_from_project.nil? ? [@project] : [ @project,@project.forked_from_project]
|
||||
.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
|
||||
|
||||
|
@ -47,12 +49,11 @@
|
|||
Milestone
|
||||
.input= f.select(:milestone_id, @project.milestones.active.all.map {|p| [ p.title, p.id ] }, { include_blank: "Select milestone" }, {class: 'chosen'})
|
||||
|
||||
|
||||
.form-actions
|
||||
- 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
|
||||
= f.submit 'Save changes', class: "btn btn-save"
|
||||
= form_helper.submit 'Save changes', class: "btn btn-save"
|
||||
- if @merge_request.new_record?
|
||||
= link_to project_merge_requests_path(@project), class: "btn btn-cancel" do
|
||||
Cancel
|
||||
|
@ -63,16 +64,23 @@
|
|||
:javascript
|
||||
disableButtonIfEmptyField("#merge_request_title", ".btn-save");
|
||||
|
||||
var source_branch = $("#merge_request_source_branch")
|
||||
, target_branch = $("#merge_request_target_branch");
|
||||
var source_branch = $("#merge_request_source_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_to_project_merge_requests_path(@project)}", {ref: target_branch.val() });
|
||||
$.get("#{branch_from_project_merge_requests_path(@project)}", {ref: source_branch.val() });
|
||||
$.get("#{branch_to_project_merge_requests_path(@project)}", {target_project_id: target_project.val(),ref: target_branch.val() });
|
||||
|
||||
target_project.live("change", function() {
|
||||
$.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() });
|
||||
});
|
||||
|
||||
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)}", {ref: $(this).val() });
|
||||
});
|
||||
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
%li{ class: mr_css_classes(merge_request) }
|
||||
.merge-request-title
|
||||
%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?
|
||||
%small.pull-right
|
||||
%i.icon-ok
|
||||
= "MERGED"
|
||||
- else
|
||||
%span.pull-right
|
||||
= "#{merge_request.source_project.path_with_namespace}/#{merge_request.source_branch}"
|
||||
%i.icon-angle-right
|
||||
= merge_request.target_branch
|
||||
= "#{merge_request.target_project.path_with_namespace}/#{merge_request.target_branch}"
|
||||
.merge-request-info
|
||||
- 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
|
||||
= render 'votes/votes_inline', votable: merge_request
|
||||
- if merge_request.notes.any?
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
:plain
|
||||
$(".mr_source_commit").html("#{commit_to_html(@commit)}");
|
||||
$(".mr_source_commit").html("#{commit_to_html(@commit, @project)}");
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
:plain
|
||||
$(".mr_target_commit").html("#{commit_to_html(@commit)}");
|
||||
$(".mr_target_commit").html("#{commit_to_html(@commit, @target_project)}");
|
||||
|
|
|
@ -7,19 +7,19 @@
|
|||
- if @commits.count > 8
|
||||
%ul.first-commits.well-list
|
||||
- @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
|
||||
8 of #{@commits.count} commits displayed.
|
||||
%strong
|
||||
%a.show-all-commits Click here to show all
|
||||
%ul.all-commits.hide.well-list
|
||||
- @commits.each do |commit|
|
||||
= render "projects/commits/commit", commit: commit
|
||||
= render "projects/commits/commit", commit: commit, project: @merge_request.source_project
|
||||
|
||||
- else
|
||||
%ul.well-list
|
||||
- @commits.each do |commit|
|
||||
= render "projects/commits/commit", commit: commit
|
||||
= render "projects/commits/commit", commit: commit, project: @merge_request.source_project
|
||||
|
||||
- else
|
||||
%h4.nothing_here_message
|
||||
|
|
|
@ -3,17 +3,49 @@
|
|||
%a.close{href: "#"} ×
|
||||
%h3 How To Merge
|
||||
.modal-body
|
||||
%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}
|
||||
- if @merge_request.for_fork?
|
||||
- source_remote = @merge_request.source_project.namespace.nil? ? "source" :@merge_request.source_project.namespace.path
|
||||
- target_remote = @merge_request.target_project.namespace.nil? ? "target" :@merge_request.target_project.namespace.path
|
||||
%p
|
||||
%strong Step 1.
|
||||
Checkout target branch and get recent objects from GitLab
|
||||
Assuming remote for #{@merge_request.target_project.path_with_namespace} is called #{target_remote}
|
||||
remote for #{@merge_request.source_project.path_with_namespace} is called #{source_remote}
|
||||
%pre.dark
|
||||
:preserve
|
||||
git checkout #{target_remote} #{@merge_request.target_branch}
|
||||
git fetch #{source_remote}
|
||||
%p
|
||||
%strong Step 2.
|
||||
Merge source branch into target branch and push changes to GitLab
|
||||
%pre.dark
|
||||
:preserve
|
||||
git merge #{source_remote}/#{@merge_request.source_branch}
|
||||
git push #{target_remote} #{@merge_request.target_branch}
|
||||
- else
|
||||
%p
|
||||
%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 @@
|
|||
for instructions
|
||||
.accept_group
|
||||
= 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
|
||||
= label_tag :should_remove_source_branch, class: "checkbox" do
|
||||
= check_box_tag :should_remove_source_branch
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
%h3.page-title
|
||||
= "Merge Request ##{@merge_request.id}:"
|
||||
|
||||
%span.label-project= @merge_request.source_project.path_with_namespace
|
||||
%span.label-branch= @merge_request.source_branch
|
||||
→
|
||||
%span.label-project= @merge_request.target_project.path_with_namespace
|
||||
%span.label-branch= @merge_request.target_branch
|
||||
|
||||
%span.pull-right
|
||||
|
|
|
@ -22,11 +22,11 @@
|
|||
- @merge_requests.each do |merge_request|
|
||||
%li
|
||||
merge request:
|
||||
= link_to [merge_request.project, merge_request] do
|
||||
= link_to [merge_request.target_project, merge_request] do
|
||||
%span ##{merge_request.id}
|
||||
%strong.term
|
||||
= 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} → #{merge_request.target_project.name_with_namespace}:#{merge_request.target_branch})
|
||||
- @issues.each do |issue|
|
||||
%li
|
||||
issue:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
- if @merge_requests.any?
|
||||
- @merge_requests.group_by(&:project).each do |group|
|
||||
- @merge_requests.group_by(&:target_project).each do |group|
|
||||
.ui-box
|
||||
- project = group[0]
|
||||
.title
|
||||
|
|
|
@ -255,6 +255,7 @@ Gitlab::Application.routes.draw do
|
|||
collection do
|
||||
get :branch_from
|
||||
get :branch_to
|
||||
get :update_branches
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -23,7 +23,8 @@ Gitlab::Seeder.quiet do
|
|||
id: i,
|
||||
source_branch: branches.first,
|
||||
target_branch: branches.last,
|
||||
project_id: project.id,
|
||||
source_project_id: project.id,
|
||||
target_project_id: project.id,
|
||||
author_id: user_id,
|
||||
assignee_id: user_id,
|
||||
milestone: project.milestones.sample,
|
||||
|
|
|
@ -19,5 +19,15 @@ FileUtils.cd(REPO_PATH) do
|
|||
# Remove the copy
|
||||
FileUtils.rm(SEED_REPO)
|
||||
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.'
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
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
|
|||
add_index "keys", ["user_id"], :name => "index_keys_on_user_id"
|
||||
|
||||
create_table "merge_requests", :force => true do |t|
|
||||
t.string "target_branch", :null => false
|
||||
t.string "source_branch", :null => false
|
||||
t.integer "project_id", :null => false
|
||||
t.string "target_branch", :null => false
|
||||
t.string "source_branch", :null => false
|
||||
t.integer "source_project_id", :null => false
|
||||
t.integer "author_id"
|
||||
t.integer "assignee_id"
|
||||
t.string "title"
|
||||
|
@ -97,14 +97,15 @@ ActiveRecord::Schema.define(:version => 20130624162710) do
|
|||
t.integer "milestone_id"
|
||||
t.string "state"
|
||||
t.string "merge_status"
|
||||
t.integer "target_project_id", :null => false
|
||||
end
|
||||
|
||||
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", ["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", ["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_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", ["title"], :name => "index_merge_requests_on_title"
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ Feature: Dashboard
|
|||
And I visit dashboard page
|
||||
Then I should see groups list
|
||||
|
||||
@javascript
|
||||
Scenario: I should see last push widget
|
||||
Then I should see last push widget
|
||||
And I click "Create Merge Request" link
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
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
|
|||
And I click link "Close"
|
||||
Then I should see closed merge request "Bug NS-04"
|
||||
|
||||
@javascript
|
||||
Scenario: I submit new unassigned merge request
|
||||
Given I click link "New Merge Request"
|
||||
And I submit new merge request "Wiki Feature"
|
||||
|
|
|
@ -22,6 +22,7 @@ class Dashboard < Spinach::FeatureSteps
|
|||
|
||||
Then 'I see prefilled new Merge Request page' do
|
||||
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_target_branch").value.should == "master"
|
||||
find("#merge_request_title").value.should == "New Design"
|
||||
|
|
|
@ -61,7 +61,7 @@ class EventFilters < Spinach::FeatureSteps
|
|||
end
|
||||
|
||||
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(
|
||||
project: @project,
|
||||
action: Event::MERGED,
|
||||
|
|
|
@ -6,18 +6,24 @@ class DashboardMergeRequests < Spinach::FeatureSteps
|
|||
merge_requests = @user.merge_requests
|
||||
merge_requests.each do |mr|
|
||||
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
|
||||
|
||||
And 'I have authored merge requests' do
|
||||
project1 = create :project
|
||||
project2 = create :project
|
||||
project1_source = 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
|
||||
merge_request2 = create :merge_request, author: @user, project: project2
|
||||
project1_source.team << [@user, :master]
|
||||
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
|
||||
|
|
|
@ -60,7 +60,8 @@ class Groups < Spinach::FeatureSteps
|
|||
|
||||
Given 'project from group has merge requests assigned to me' do
|
||||
create :merge_request,
|
||||
project: project,
|
||||
source_project: project,
|
||||
target_project: project,
|
||||
assignee: current_user,
|
||||
author: current_user
|
||||
end
|
||||
|
|
|
@ -3,10 +3,13 @@ class Spinach::Features::ProjectDeployKeys < Spinach::FeatureSteps
|
|||
include SharedProject
|
||||
include SharedPaths
|
||||
|
||||
|
||||
|
||||
step 'project has deploy key' do
|
||||
create(:deploy_keys_project, project: @project)
|
||||
end
|
||||
|
||||
|
||||
step 'I should see project deploy keys' do
|
||||
within '.enabled-keys' do
|
||||
page.should have_content deploy_key.title
|
||||
|
|
|
@ -4,6 +4,8 @@ class ForkProject < Spinach::FeatureSteps
|
|||
include SharedProject
|
||||
|
||||
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)
|
||||
click_link "Fork"
|
||||
end
|
||||
|
@ -14,12 +16,17 @@ class ForkProject < Spinach::FeatureSteps
|
|||
@project.team << [@user, :reporter]
|
||||
end
|
||||
|
||||
|
||||
step 'I should see the forked project page' do
|
||||
page.should have_content "Project was successfully forked."
|
||||
current_path.should include current_user.namespace.path
|
||||
@forked_project = Project.find_by_namespace_id(current_user.namespace.path)
|
||||
end
|
||||
|
||||
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)
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
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
|
|||
page.should have_content "Wiki Feature"
|
||||
end
|
||||
|
||||
|
||||
Then 'I should see closed merge request "Bug NS-04"' do
|
||||
merge_request = MergeRequest.find_by_title!("Bug NS-04")
|
||||
merge_request.closed?.should be_true
|
||||
|
@ -56,30 +57,35 @@ class ProjectMergeRequests < Spinach::FeatureSteps
|
|||
end
|
||||
|
||||
And 'I submit new merge request "Wiki Feature"' do
|
||||
fill_in "merge_request_title", with: "Wiki Feature"
|
||||
select "bootstrap", from: "merge_request_source_branch"
|
||||
select "master", from: "merge_request_target_branch"
|
||||
fill_in "merge_request_title", :with => "Wiki Feature"
|
||||
select project.path_with_namespace, :from => "merge_request_target_project_id"
|
||||
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"
|
||||
end
|
||||
|
||||
And 'project "Shop" have "Bug NS-04" open merge request' do
|
||||
create(:merge_request,
|
||||
title: "Bug NS-04",
|
||||
project: project,
|
||||
source_project: project,
|
||||
target_project: project,
|
||||
author: project.users.first)
|
||||
end
|
||||
|
||||
And 'project "Shop" have "Bug NS-05" open merge request with diffs inside' do
|
||||
create(:merge_request_with_diffs,
|
||||
title: "Bug NS-05",
|
||||
project: project,
|
||||
source_project: project,
|
||||
target_project: project,
|
||||
author: project.users.first)
|
||||
end
|
||||
|
||||
And 'project "Shop" have "Feature NS-03" closed merge request' do
|
||||
create(:closed_merge_request,
|
||||
title: "Feature NS-03",
|
||||
project: project,
|
||||
source_project: project,
|
||||
target_project: project,
|
||||
author: project.users.first)
|
||||
end
|
||||
|
||||
|
|
|
@ -184,6 +184,10 @@ module SharedPaths
|
|||
visit project_path(project)
|
||||
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
|
||||
visit edit_project_path(project)
|
||||
end
|
||||
|
@ -239,18 +243,22 @@ module SharedPaths
|
|||
|
||||
step 'I visit merge request page "Bug NS-04"' do
|
||||
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
|
||||
|
||||
step 'I visit merge request page "Bug NS-05"' do
|
||||
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
|
||||
|
||||
step 'I visit project "Shop" merge requests page' do
|
||||
visit project_merge_requests_path(project)
|
||||
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
|
||||
visit project_milestones_path(project)
|
||||
end
|
||||
|
|
|
@ -35,8 +35,7 @@ Capybara.ignore_hidden_elements = false
|
|||
DatabaseCleaner.strategy = :truncation
|
||||
|
||||
Spinach.hooks.before_scenario do
|
||||
TestEnv.init(mailer: false)
|
||||
|
||||
TestEnv.setup_stubs
|
||||
DatabaseCleaner.start
|
||||
end
|
||||
|
||||
|
@ -45,6 +44,7 @@ Spinach.hooks.after_scenario do
|
|||
end
|
||||
|
||||
Spinach.hooks.before_run do
|
||||
TestEnv.init(mailer: false, init_repos: true, repos: false)
|
||||
RSpec::Mocks::setup self
|
||||
|
||||
include FactoryGirl::Syntax::Methods
|
||||
|
|
|
@ -51,9 +51,10 @@ module API
|
|||
#
|
||||
# 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
|
||||
# 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
|
||||
# title (required) - Title of MR
|
||||
#
|
||||
|
@ -63,11 +64,15 @@ module API
|
|||
post ":id/merge_requests" do
|
||||
authorize! :write_merge_request, user_project
|
||||
required_attributes! [:source_branch, :target_branch, :title]
|
||||
|
||||
attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title]
|
||||
attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :target_project_id]
|
||||
merge_request = user_project.merge_requests.new(attrs)
|
||||
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
|
||||
merge_request.reload_code
|
||||
present merge_request, with: Entities::MergeRequest
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
module Gitlab
|
||||
module Satellite
|
||||
class Action
|
||||
DEFAULT_OPTIONS = { git_timeout: 30.seconds }
|
||||
DEFAULT_OPTIONS = { git_timeout: 30.seconds}
|
||||
|
||||
attr_accessor :options, :project, :user
|
||||
|
||||
|
@ -34,16 +34,19 @@ module Gitlab
|
|||
Gitlab::ShellEnv.reset_env
|
||||
end
|
||||
|
||||
# * Clears the satellite
|
||||
# * Updates the satellite from Gitolite
|
||||
# * Recreates the satellite
|
||||
# * Sets up Git variables for the user
|
||||
#
|
||||
# Note: use this within #in_locked_and_timed_satellite
|
||||
def prepare_satellite!(repo)
|
||||
project.satellite.clear_and_update!
|
||||
|
||||
repo.git.config({}, "user.name", user.name)
|
||||
repo.git.config({}, "user.email", user.email)
|
||||
repo.config['user.name']=user.name
|
||||
repo.config['user.email']=user.email
|
||||
end
|
||||
|
||||
def default_options(options = {})
|
||||
{raise: true, timeout: true}.merge(options)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,37 +5,37 @@ module Gitlab
|
|||
attr_accessor :merge_request
|
||||
|
||||
def initialize(user, merge_request)
|
||||
super user, merge_request.project
|
||||
super user, merge_request.target_project
|
||||
@merge_request = merge_request
|
||||
end
|
||||
|
||||
# Checks if a merge request can be executed without user interaction
|
||||
def can_be_merged?
|
||||
in_locked_and_timed_satellite do |merge_repo|
|
||||
prepare_satellite!(merge_repo)
|
||||
merge_in_satellite!(merge_repo)
|
||||
end
|
||||
end
|
||||
|
||||
# Merges the source branch into the target branch in the satellite and
|
||||
# pushes it back to Gitolite.
|
||||
# It also removes the source branch if requested in the merge request.
|
||||
# pushes it back to the repository.
|
||||
# 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 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
|
||||
def merge!
|
||||
in_locked_and_timed_satellite do |merge_repo|
|
||||
prepare_satellite!(merge_repo)
|
||||
if merge_in_satellite!(merge_repo)
|
||||
# push merge back to Gitolite
|
||||
# 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
|
||||
if merge_request.should_remove_source_branch && !project.root_ref?(merge_request.source_branch)
|
||||
# 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
|
||||
|
||||
# merge, push and branch removal successful
|
||||
true
|
||||
end
|
||||
|
@ -45,6 +45,82 @@ module Gitlab
|
|||
false
|
||||
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
|
||||
|
||||
# Merges the source_branch into the target_branch in the satellite.
|
||||
|
@ -54,18 +130,38 @@ module Gitlab
|
|||
# Returns false if the merge produced conflicts
|
||||
# Returns true otherwise
|
||||
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
|
||||
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
|
||||
Gitlab::GitLogger.error(ex.message)
|
||||
false
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
module Gitlab
|
||||
class SatelliteNotExistError < StandardError; end
|
||||
class SatelliteNotExistError < StandardError;
|
||||
end
|
||||
|
||||
module Satellite
|
||||
class Satellite
|
||||
|
@ -21,11 +22,14 @@ module Gitlab
|
|||
raise SatelliteNotExistError.new("Satellite doesn't exist")
|
||||
end
|
||||
|
||||
|
||||
def clear_and_update!
|
||||
raise_no_satellite unless exists?
|
||||
|
||||
File.exists? path
|
||||
@repo = nil
|
||||
clear_working_dir!
|
||||
delete_heads!
|
||||
remove_remotes!
|
||||
update_from_source!
|
||||
end
|
||||
|
||||
|
@ -55,14 +59,16 @@ module Gitlab
|
|||
raise_no_satellite unless exists?
|
||||
|
||||
File.open(lock_file, "w+") do |f|
|
||||
f.flock(File::LOCK_EX)
|
||||
|
||||
Dir.chdir(path) do
|
||||
return yield
|
||||
begin
|
||||
f.flock File::LOCK_EX
|
||||
Dir.chdir(path) { return yield }
|
||||
ensure
|
||||
f.flock File::LOCK_UN
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def lock_file
|
||||
create_locks_dir unless File.exists?(lock_files_dir)
|
||||
File.join(lock_files_dir, "satellite_#{project.id}.lock")
|
||||
|
@ -100,20 +106,35 @@ module Gitlab
|
|||
if heads.include? PARKING_BRANCH
|
||||
repo.git.checkout({}, PARKING_BRANCH)
|
||||
else
|
||||
repo.git.checkout({b: true}, PARKING_BRANCH)
|
||||
repo.git.checkout(default_options({b: true}), PARKING_BRANCH)
|
||||
end
|
||||
|
||||
# remove the parking branch from the list of heads ...
|
||||
heads.delete(PARKING_BRANCH)
|
||||
# ... 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
|
||||
|
||||
# Updates the satellite from Gitolite
|
||||
#
|
||||
# Note: this will only update remote branches (i.e. origin/*)
|
||||
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
|
||||
|
||||
# Create directory for stroing
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
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
|
|
@ -2,12 +2,11 @@ require 'spec_helper'
|
|||
|
||||
describe Projects::CommitController do
|
||||
let(:project) { create(:project_with_code) }
|
||||
let(:user) { create(:user) }
|
||||
let(:commit) { project.repository.last_commit_for("master") }
|
||||
let(:user) { create(:user) }
|
||||
let(:commit) { project.repository.last_commit_for("master") }
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
|
||||
project.team << [user, :master]
|
||||
end
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ require 'spec_helper'
|
|||
|
||||
describe Projects::CommitsController do
|
||||
let(:project) { create(:project_with_code) }
|
||||
let(:user) { create(:user) }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
|
|
|
@ -3,7 +3,7 @@ require 'spec_helper'
|
|||
describe Projects::MergeRequestsController do
|
||||
let(:project) { create(:project_with_code) }
|
||||
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
|
||||
sign_in(user)
|
||||
|
@ -28,7 +28,7 @@ describe Projects::MergeRequestsController do
|
|||
it "should render it" do
|
||||
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
|
||||
|
||||
it "should not escape Html" do
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
<<<<<<< HEAD
|
||||
include ActionDispatch::TestProcess
|
||||
=======
|
||||
require Rails.root.join('spec', 'support', 'test_env.rb')
|
||||
>>>>>>> Merge Request on forked projects
|
||||
|
||||
FactoryGirl.define do
|
||||
sequence :sentence, aliases: [:title, :content] do
|
||||
|
@ -29,8 +33,19 @@ FactoryGirl.define do
|
|||
sequence(:name) { |n| "project#{n}" }
|
||||
path { name.downcase.gsub(/\s/, '_') }
|
||||
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
|
||||
|
||||
|
||||
factory :redmine_project, parent: :project do
|
||||
issues_tracker { "redmine" }
|
||||
issues_tracker_id { "project_name_in_redmine" }
|
||||
|
@ -39,14 +54,24 @@ FactoryGirl.define do
|
|||
factory :project_with_code, parent: :project do
|
||||
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|
|
||||
repos_path = Rails.root.join('tmp', 'test-git-base-path')
|
||||
seed_repo = Rails.root.join('tmp', 'repositories', 'gitlabhq')
|
||||
target_repo = File.join(repos_path, project.path_with_namespace + '.git')
|
||||
system("ln -s #{seed_repo} #{target_repo}")
|
||||
TestEnv.clear_repo_dir(project.namespace, project.path)
|
||||
TestEnv.create_repo(project.namespace, project.path)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
factory :group do
|
||||
sequence(:name) { |n| "group#{n}" }
|
||||
path { name.downcase.gsub(/\s/, '_') }
|
||||
|
@ -86,7 +111,8 @@ FactoryGirl.define do
|
|||
factory :merge_request do
|
||||
title
|
||||
author
|
||||
project factory: :project_with_code
|
||||
source_project factory: :source_project_with_code
|
||||
target_project factory: :target_project_with_code
|
||||
source_branch "master"
|
||||
target_branch "stable"
|
||||
|
||||
|
@ -96,13 +122,13 @@ FactoryGirl.define do
|
|||
source_branch "stable" # pretend bcf03b5d
|
||||
st_commits do
|
||||
[
|
||||
project.repository.commit('bcf03b5d').to_hash,
|
||||
project.repository.commit('bcf03b5d~1').to_hash,
|
||||
project.repository.commit('bcf03b5d~2').to_hash
|
||||
source_project.repository.commit('bcf03b5d').to_hash,
|
||||
source_project.repository.commit('bcf03b5d~1').to_hash,
|
||||
source_project.repository.commit('bcf03b5d~2').to_hash
|
||||
]
|
||||
end
|
||||
st_diffs do
|
||||
project.repo.diff("bcf03b5d~3", "bcf03b5d")
|
||||
source_project.repo.diff("bcf03b5d~3", "bcf03b5d")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -133,7 +159,7 @@ FactoryGirl.define do
|
|||
|
||||
trait :on_commit do
|
||||
project factory: :project_with_code
|
||||
commit_id "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a"
|
||||
commit_id "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a"
|
||||
noteable_type "Commit"
|
||||
end
|
||||
|
||||
|
@ -143,12 +169,12 @@ FactoryGirl.define do
|
|||
|
||||
trait :on_merge_request do
|
||||
project factory: :project_with_code
|
||||
noteable_id 1
|
||||
noteable_id 1
|
||||
noteable_type "MergeRequest"
|
||||
end
|
||||
|
||||
trait :on_issue do
|
||||
noteable_id 1
|
||||
noteable_id 1
|
||||
noteable_type "Issue"
|
||||
end
|
||||
|
||||
|
|
|
@ -5,8 +5,10 @@ INVALID_FACTORIES = [
|
|||
:invalid_key,
|
||||
]
|
||||
|
||||
|
||||
FactoryGirl.factories.map(&:name).each do |factory_name|
|
||||
next if INVALID_FACTORIES.include?(factory_name)
|
||||
|
||||
describe "#{factory_name} factory" do
|
||||
it 'should be valid' do
|
||||
build(factory_name).should be_valid
|
||||
|
|
|
@ -3,11 +3,11 @@ require 'spec_helper'
|
|||
describe "GitLab Flavored Markdown" do
|
||||
let(:project) { create(:project_with_code) }
|
||||
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
|
||||
u = create(:user, name: "fred")
|
||||
project.team << [u, :master]
|
||||
u
|
||||
u = create(:user, name: "fred")
|
||||
project.team << [u, :master]
|
||||
u
|
||||
end
|
||||
|
||||
before do
|
||||
|
@ -83,9 +83,7 @@ describe "GitLab Flavored Markdown" do
|
|||
|
||||
describe "for merge requests" do
|
||||
before do
|
||||
@merge_request = create(:merge_request,
|
||||
project: project,
|
||||
title: "fix ##{issue.id}")
|
||||
@merge_request = create(:merge_request, source_project: project, target_project: project, title: "fix ##{issue.id}")
|
||||
end
|
||||
|
||||
it "should render title in merge_requests#index" do
|
||||
|
|
|
@ -2,8 +2,8 @@ require 'spec_helper'
|
|||
|
||||
describe "On a merge request", js: true do
|
||||
let!(:project) { create(:project_with_code) }
|
||||
let!(:merge_request) { create(:merge_request, project: project) }
|
||||
let!(:note) { create(:note_on_merge_request_with_attachment, project: project) }
|
||||
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
|
||||
let!(:note) { create(:note_on_merge_request_with_attachment, source_project: project, target_project: project) }
|
||||
|
||||
before do
|
||||
login_as :user
|
||||
|
@ -62,7 +62,7 @@ describe "On a merge request", js: true do
|
|||
|
||||
it 'should be added and form reset' do
|
||||
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-text", visible: true) }
|
||||
end
|
||||
|
@ -135,8 +135,8 @@ describe "On a merge request", js: true do
|
|||
end
|
||||
|
||||
describe "On a merge request diff", js: true, focus: true do
|
||||
let!(:project) { create(:project_with_code) }
|
||||
let!(:merge_request) { create(:merge_request_with_diffs, project: project) }
|
||||
let!(:project) { create(:source_project_with_code) }
|
||||
let!(:merge_request) { create(:merge_request_with_diffs, source_project: project, target_project: project) }
|
||||
|
||||
before do
|
||||
login_as :user
|
||||
|
@ -144,6 +144,7 @@ describe "On a merge request diff", js: true, focus: true do
|
|||
visit diffs_project_merge_request_path(project, merge_request)
|
||||
end
|
||||
|
||||
|
||||
subject { page }
|
||||
|
||||
describe "when adding a note" do
|
||||
|
@ -205,13 +206,13 @@ describe "On a merge request diff", js: true, focus: true do
|
|||
|
||||
# TODO: fix
|
||||
#it 'should check if previews were rendered separately' do
|
||||
#within("tr[id='4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185'] + .js-temp-notes-holder") do
|
||||
#should have_css(".js-note-preview", text: "One comment on line 185")
|
||||
#end
|
||||
#within("tr[id='4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185'] + .js-temp-notes-holder") do
|
||||
#should have_css(".js-note-preview", text: "One comment on line 185")
|
||||
#end
|
||||
|
||||
#within("tr[id='342e16cbbd482ac2047dc679b2749d248cc1428f_18_17'] + .js-temp-notes-holder") do
|
||||
#should have_css(".js-note-preview", text: "Another comment on line 17")
|
||||
#end
|
||||
#within("tr[id='342e16cbbd482ac2047dc679b2749d248cc1428f_18_17'] + .js-temp-notes-holder") do
|
||||
#should have_css(".js-note-preview", text: "Another comment on line 17")
|
||||
#end
|
||||
#end
|
||||
end
|
||||
|
||||
|
@ -238,39 +239,38 @@ describe "On a merge request diff", js: true, focus: true do
|
|||
|
||||
# TODO: fix
|
||||
#it "should remove last note of a discussion" do
|
||||
#within("tr[id='342e16cbbd482ac2047dc679b2749d248cc1428f_18_17'] + .notes-holder") do
|
||||
#find(".js-note-delete").click
|
||||
#end
|
||||
|
||||
#should_not have_css(".note_holder")
|
||||
# within("tr[id='342e16cbbd482ac2047dc679b2749d248cc1428f_18_17'] + .notes-holder") do
|
||||
# find(".js-note-delete").click
|
||||
# end
|
||||
# should_not have_css(".note_holder")
|
||||
#end
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: fix
|
||||
#describe "when replying to a note" do
|
||||
#before do
|
||||
## create first note
|
||||
#find('a[data-line-code="4735dfc552ad7bf15ca468adc3cad9d05b624490_184_184"]').click
|
||||
#before do
|
||||
## create first note
|
||||
# find('a[data-line-code="4735dfc552ad7bf15ca468adc3cad9d05b624490_184_184"]').click
|
||||
|
||||
#within(".js-temp-notes-holder") do
|
||||
#fill_in "note[note]", with: "One comment on line 184"
|
||||
#click_button("Add Comment")
|
||||
#end
|
||||
# within(".js-temp-notes-holder") do
|
||||
# fill_in "note[note]", with: "One comment on line 184"
|
||||
# click_button("Add Comment")
|
||||
#end
|
||||
|
||||
#within(".js-temp-notes-holder") do
|
||||
#find(".js-discussion-reply-button").click
|
||||
#fill_in "note[note]", with: "An additional comment in reply"
|
||||
#click_button("Add Comment")
|
||||
#end
|
||||
#end
|
||||
# within(".js-temp-notes-holder") do
|
||||
# find(".js-discussion-reply-button").click
|
||||
# fill_in "note[note]", with: "An additional comment in reply"
|
||||
# click_button("Add Comment")
|
||||
# end
|
||||
#end
|
||||
|
||||
#it 'should be inserted and form removed from reply' do
|
||||
#should have_content("An additional comment in reply")
|
||||
#within(".notes_holder") { should have_css(".note", count: 2) }
|
||||
#within(".notes_holder") { should have_no_css("form") }
|
||||
#within(".notes_holder") { should have_link("Reply") }
|
||||
#end
|
||||
#it 'should be inserted and form removed from reply' do
|
||||
# should have_content("An additional comment in reply")
|
||||
# within(".notes_holder") { should have_css(".note", count: 2) }
|
||||
# within(".notes_holder") { should have_no_css("form") }
|
||||
# within(".notes_holder") { should have_link("Reply") }
|
||||
# end
|
||||
#end
|
||||
end
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ require 'spec_helper'
|
|||
|
||||
describe "Profile account page" do
|
||||
before(:each) { enable_observers }
|
||||
after(:each) {disable_observers}
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
|
|
|
@ -2,6 +2,7 @@ require 'spec_helper'
|
|||
|
||||
describe "Projects" do
|
||||
before(:each) { enable_observers }
|
||||
after(:each) {disable_observers}
|
||||
before { login_as :user }
|
||||
|
||||
describe "DELETE /projects/:id" do
|
||||
|
|
|
@ -14,13 +14,14 @@ describe "Application access" do
|
|||
end
|
||||
|
||||
describe "Project" do
|
||||
let(:project) { create(:project_with_code) }
|
||||
let(:project) { create(:project_with_code) }
|
||||
|
||||
let(:master) { create(:user) }
|
||||
let(:guest) { create(:user) }
|
||||
let(:master) { create(:user) }
|
||||
let(:guest) { create(:user) }
|
||||
let(:reporter) { create(:user) }
|
||||
|
||||
before do
|
||||
|
||||
# full access
|
||||
project.team << [master, :master]
|
||||
|
||||
|
@ -108,7 +109,7 @@ describe "Application access" do
|
|||
describe "GET /project_code/blob" do
|
||||
before do
|
||||
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))
|
||||
end
|
||||
|
||||
|
@ -232,13 +233,13 @@ describe "Application access" do
|
|||
|
||||
|
||||
describe "PublicProject" do
|
||||
let(:project) { create(:project_with_code) }
|
||||
let(:project) { create(:project_with_code) }
|
||||
|
||||
let(:master) { create(:user) }
|
||||
let(:guest) { create(:user) }
|
||||
let(:master) { create(:user) }
|
||||
let(:guest) { create(:user) }
|
||||
let(:reporter) { create(:user) }
|
||||
|
||||
let(:admin) { create(:user) }
|
||||
let(:admin) { create(:user) }
|
||||
|
||||
before do
|
||||
# public project
|
||||
|
@ -339,7 +340,7 @@ describe "Application access" do
|
|||
describe "GET /project_code/blob" do
|
||||
before do
|
||||
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))
|
||||
end
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ describe GitlabMarkdownHelper do
|
|||
let(:user) { create(:user, username: 'gfm') }
|
||||
let(:commit) { project.repository.commit }
|
||||
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(:member) { project.users_projects.where(user_id: user).first }
|
||||
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
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
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
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
|
|
@ -167,7 +167,7 @@ describe Notify do
|
|||
end
|
||||
|
||||
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
|
||||
subject { Notify.new_merge_request_email(merge_request.assignee_id, merge_request.id) }
|
||||
|
@ -311,7 +311,7 @@ describe Notify do
|
|||
end
|
||||
|
||||
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}") }
|
||||
before(:each) { note.stub(:noteable).and_return(merge_request) }
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ require 'spec_helper'
|
|||
describe Commit do
|
||||
let(:commit) { create(:project_with_code).repository.commit }
|
||||
|
||||
|
||||
describe '#title' do
|
||||
it "returns no_commit_message when safe_message is blank" do
|
||||
commit.stub(:safe_message).and_return('')
|
||||
|
|
|
@ -12,9 +12,9 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe ForkedProjectLink, "add link on fork" do
|
||||
let(:project_from) {create(:project)}
|
||||
let(:namespace) {create(:namespace)}
|
||||
let(:user) {create(:user, namespace: namespace)}
|
||||
let(:project_from) { create(:project) }
|
||||
let(:namespace) { create(:namespace) }
|
||||
let(:user) { create(:user, namespace: namespace) }
|
||||
|
||||
before do
|
||||
@project_to = fork_project(project_from, user)
|
||||
|
@ -30,9 +30,9 @@ describe ForkedProjectLink, "add link on fork" do
|
|||
end
|
||||
|
||||
describe :forked_from_project do
|
||||
let(:forked_project_link) {build(:forked_project_link)}
|
||||
let(:project_from) {create(:project)}
|
||||
let(:project_to) {create(:project, forked_project_link: forked_project_link)}
|
||||
let(:forked_project_link) { build(:forked_project_link) }
|
||||
let(:project_from) { create(:project) }
|
||||
let(:project_to) { create(:project, forked_project_link: forked_project_link) }
|
||||
|
||||
|
||||
before :each do
|
||||
|
|
|
@ -41,15 +41,12 @@ describe MergeRequest do
|
|||
it { should include_module(Issuable) }
|
||||
end
|
||||
|
||||
describe "#mr_and_commit_notes" do
|
||||
|
||||
end
|
||||
|
||||
describe "#mr_and_commit_notes" do
|
||||
let!(:merge_request) { create(:merge_request) }
|
||||
|
||||
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, noteable: merge_request)
|
||||
end
|
||||
|
@ -71,4 +68,39 @@ describe MergeRequest do
|
|||
subject.is_being_reassigned?.should be_false
|
||||
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
|
||||
|
|
|
@ -144,12 +144,12 @@ describe Note do
|
|||
end
|
||||
|
||||
describe '#create_status_change_note' do
|
||||
let(:project) { create(:project) }
|
||||
let(:thing) { create(:issue, project: project) }
|
||||
let(:author) { create(:user) }
|
||||
let(:status) { 'new_status' }
|
||||
let(:project) { create(:project) }
|
||||
let(:thing) { create(:issue, project: project) }
|
||||
let(:author) { create(:user) }
|
||||
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
|
||||
should be_a Note
|
||||
|
@ -157,9 +157,9 @@ describe Note do
|
|||
end
|
||||
|
||||
its(:noteable) { should == thing }
|
||||
its(:project) { should == thing.project }
|
||||
its(:author) { should == author }
|
||||
its(:note) { should =~ /Status changed to #{status}/ }
|
||||
its(:project) { should == thing.project }
|
||||
its(:author) { should == author }
|
||||
its(:note) { should =~ /Status changed to #{status}/ }
|
||||
end
|
||||
|
||||
describe :authorization do
|
||||
|
|
|
@ -26,6 +26,9 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Project do
|
||||
before(:each) { enable_observers }
|
||||
after(:each) { disable_observers }
|
||||
|
||||
describe "Associations" do
|
||||
it { should belong_to(:group) }
|
||||
it { should belong_to(:namespace) }
|
||||
|
@ -95,12 +98,11 @@ describe Project do
|
|||
end
|
||||
|
||||
describe "last_activity methods" do
|
||||
before { enable_observers }
|
||||
let(:project) { create(:project) }
|
||||
let(:project) { create(:project) }
|
||||
let(:last_event) { double(created_at: Time.now) }
|
||||
|
||||
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.last_activity.should == last_event
|
||||
end
|
||||
|
@ -122,7 +124,7 @@ describe Project do
|
|||
let(:project) { create(:project_with_code) }
|
||||
|
||||
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)
|
||||
end
|
||||
|
||||
|
|
|
@ -8,18 +8,6 @@ describe ActivityObserver do
|
|||
it { @event.project.should == project }
|
||||
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
|
||||
before do
|
||||
|
|
|
@ -26,14 +26,13 @@ describe IssueObserver do
|
|||
before { mock_issue.stub(state: 'closed') }
|
||||
|
||||
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)
|
||||
end
|
||||
|
||||
it 'trigger notification to send emails' do
|
||||
subject.notification.should_receive(:close_issue).with(mock_issue, some_user)
|
||||
|
||||
subject.after_close(mock_issue, nil)
|
||||
end
|
||||
end
|
||||
|
@ -42,8 +41,7 @@ describe IssueObserver do
|
|||
before { mock_issue.stub(state: 'reopened') }
|
||||
|
||||
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)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,19 +1,21 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe MergeRequestObserver do
|
||||
let(:some_user) { create :user }
|
||||
let(:assignee) { create :user }
|
||||
let(:author) { create :user }
|
||||
let(:mr_mock) { double(:merge_request, id: 42, assignee: assignee, author: author) }
|
||||
let(:assigned_mr) { create(:merge_request, assignee: assignee, author: author) }
|
||||
let(:some_user) { create :user }
|
||||
let(:assignee) { create :user }
|
||||
let(:author) { create :user }
|
||||
let(:mr_mock) { double(:merge_request, id: 42, assignee: assignee, author: author) }
|
||||
let(:assigned_mr) { create(:merge_request, assignee: assignee, 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) }
|
||||
|
||||
before { subject.stub(:current_user).and_return(some_user) }
|
||||
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 }
|
||||
|
||||
after(:each) { disable_observers }
|
||||
|
||||
subject { MergeRequestObserver.instance }
|
||||
|
||||
|
@ -30,7 +32,7 @@ describe MergeRequestObserver do
|
|||
end
|
||||
|
||||
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)
|
||||
|
||||
MergeRequest.observers.enable :merge_request_observer do
|
||||
|
@ -59,13 +61,13 @@ describe MergeRequestObserver do
|
|||
context '#after_close' do
|
||||
context 'a status "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
|
||||
end
|
||||
|
||||
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
|
||||
end
|
||||
|
@ -75,16 +77,42 @@ describe MergeRequestObserver do
|
|||
context '#after_reopen' do
|
||||
context 'a status "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
|
||||
end
|
||||
|
||||
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
|
||||
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
|
||||
|
|
|
@ -2,6 +2,7 @@ require 'spec_helper'
|
|||
|
||||
describe UserObserver do
|
||||
before(:each) { enable_observers }
|
||||
after(:each) {disable_observers}
|
||||
subject { UserObserver.instance }
|
||||
before { subject.stub(notification: mock('NotificationService').as_null_object) }
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ require 'spec_helper'
|
|||
|
||||
describe UsersProjectObserver do
|
||||
before(:each) { enable_observers }
|
||||
after(:each) { disable_observers }
|
||||
|
||||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project) }
|
||||
|
|
|
@ -3,10 +3,12 @@ require "spec_helper"
|
|||
describe API::API do
|
||||
include ApiHelpers
|
||||
|
||||
let(:user) { create(:user ) }
|
||||
let!(:project) { create(:project_with_code, creator_id: user.id) }
|
||||
let!(:merge_request) { create(:merge_request, author: user, assignee: user, project: project, title: "Test") }
|
||||
before { project.team << [user, :reporters] }
|
||||
let(:user) { create(:user) }
|
||||
let!(:project) {create(:project_with_code, creator_id: user.id) }
|
||||
let!(:merge_request) { create(:merge_request, author: user, assignee: user, source_project: project, target_project: project, title: "Test") }
|
||||
before {
|
||||
project.team << [user, :reporters]
|
||||
}
|
||||
|
||||
describe "GET /projects/:id/merge_requests" do
|
||||
context "when unauthenticated" do
|
||||
|
@ -40,35 +42,91 @@ describe API::API do
|
|||
end
|
||||
|
||||
describe "POST /projects/:id/merge_requests" do
|
||||
it "should return merge_request" do
|
||||
post api("/projects/#{project.id}/merge_requests", user),
|
||||
title: 'Test merge_request', source_branch: "stable", target_branch: "master", author: user
|
||||
response.status.should == 201
|
||||
json_response['title'].should == 'Test merge_request'
|
||||
context 'between branches projects' do
|
||||
it "should return merge_request" do
|
||||
post api("/projects/#{project.id}/merge_requests", user),
|
||||
title: 'Test merge_request', source_branch: "stable", target_branch: "master", author: user
|
||||
response.status.should == 201
|
||||
json_response['title'].should == 'Test merge_request'
|
||||
end
|
||||
|
||||
it "should return 422 when source_branch equals target_branch" do
|
||||
post api("/projects/#{project.id}/merge_requests", user),
|
||||
title: "Test merge_request", source_branch: "master", target_branch: "master", author: user
|
||||
response.status.should == 422
|
||||
end
|
||||
|
||||
it "should return 400 when source_branch is missing" do
|
||||
post api("/projects/#{project.id}/merge_requests", user),
|
||||
title: "Test merge_request", target_branch: "master", author: user
|
||||
response.status.should == 400
|
||||
end
|
||||
|
||||
it "should return 400 when target_branch is missing" do
|
||||
post api("/projects/#{project.id}/merge_requests", user),
|
||||
title: "Test merge_request", source_branch: "stable", author: user
|
||||
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
|
||||
|
||||
it "should return 422 when source_branch equals target_branch" do
|
||||
post api("/projects/#{project.id}/merge_requests", user),
|
||||
title: "Test merge_request", source_branch: "master", target_branch: "master", author: user
|
||||
response.status.should == 422
|
||||
end
|
||||
context 'forked projects' do
|
||||
let!(:user2) {create(:user)}
|
||||
let!(:forked_project_link) { build(:forked_project_link) }
|
||||
let!(:fork_project) { create(:source_project_with_code, forked_project_link: forked_project_link, namespace: user2.namespace, creator_id: user2.id) }
|
||||
|
||||
it "should return 400 when source_branch is missing" do
|
||||
post api("/projects/#{project.id}/merge_requests", user),
|
||||
title: "Test merge_request", target_branch: "master", author: user
|
||||
response.status.should == 400
|
||||
end
|
||||
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 400 when target_branch is missing" do
|
||||
post api("/projects/#{project.id}/merge_requests", user),
|
||||
title: "Test merge_request", source_branch: "stable", author: user
|
||||
response.status.should == 400
|
||||
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 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
|
||||
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
|
||||
|
||||
|
@ -97,14 +155,14 @@ describe API::API 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),
|
||||
source_branch: "master", target_branch: "master"
|
||||
source_branch: "master", target_branch: "master"
|
||||
response.status.should == 422
|
||||
end
|
||||
|
||||
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
|
||||
json_response['target_branch'].should == 'test'
|
||||
json_response['target_branch'].should == 'wiki'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ require 'spec_helper'
|
|||
describe API::API do
|
||||
include ApiHelpers
|
||||
before(:each) { enable_observers }
|
||||
after(:each) {disable_observers}
|
||||
|
||||
let(:user) { create(:user) }
|
||||
let!(:project) { create(:project, namespace: user.namespace ) }
|
||||
|
|
|
@ -6,7 +6,7 @@ describe API::API do
|
|||
let(:user) { create(:user) }
|
||||
let!(:project) { create(:project, namespace: user.namespace ) }
|
||||
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!(:issue_note) { create(:note, noteable: issue, project: project, author: user) }
|
||||
let!(:merge_request_note) { create(:note, noteable: merge_request, project: project, author: user) }
|
||||
|
|
|
@ -3,6 +3,7 @@ require 'spec_helper'
|
|||
describe API::API do
|
||||
include ApiHelpers
|
||||
before(:each) { enable_observers }
|
||||
after(:each) { disable_observers }
|
||||
|
||||
let(:user) { create(:user) }
|
||||
let(:user2) { create(:user) }
|
||||
|
@ -14,7 +15,8 @@ describe API::API do
|
|||
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) }
|
||||
|
||||
before { project.team << [user, :reporter] }
|
||||
before {
|
||||
project.team << [user, :reporter] }
|
||||
|
||||
describe "GET /projects" do
|
||||
context "when unauthenticated" do
|
||||
|
@ -46,16 +48,16 @@ describe API::API do
|
|||
it "should not create new project" do
|
||||
expect {
|
||||
post api("/projects", user2), name: 'foo'
|
||||
}.to change {Project.count}.by(0)
|
||||
}.to change { Project.count }.by(0)
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
it "should return a 400 error if name not given" do
|
||||
|
@ -89,17 +91,17 @@ describe API::API do
|
|||
|
||||
it "should assign attributes to project" do
|
||||
project = attributes_for(:project, {
|
||||
description: Faker::Lorem.sentence,
|
||||
default_branch: 'stable',
|
||||
issues_enabled: false,
|
||||
wall_enabled: false,
|
||||
merge_requests_enabled: false,
|
||||
wiki_enabled: false
|
||||
description: Faker::Lorem.sentence,
|
||||
default_branch: 'stable',
|
||||
issues_enabled: false,
|
||||
wall_enabled: false,
|
||||
merge_requests_enabled: false,
|
||||
wiki_enabled: false
|
||||
})
|
||||
|
||||
post api("/projects", user), project
|
||||
|
||||
project.each_pair do |k,v|
|
||||
project.each_pair do |k, v|
|
||||
next if k == :path
|
||||
json_response[k.to_s].should == v
|
||||
end
|
||||
|
@ -125,11 +127,11 @@ describe API::API do
|
|||
before { admin }
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
it "should respond with 201 on success" do
|
||||
|
@ -144,17 +146,17 @@ describe API::API do
|
|||
|
||||
it "should assign attributes to project" do
|
||||
project = attributes_for(:project, {
|
||||
description: Faker::Lorem.sentence,
|
||||
default_branch: 'stable',
|
||||
issues_enabled: false,
|
||||
wall_enabled: false,
|
||||
merge_requests_enabled: false,
|
||||
wiki_enabled: false
|
||||
description: Faker::Lorem.sentence,
|
||||
default_branch: 'stable',
|
||||
issues_enabled: false,
|
||||
wall_enabled: false,
|
||||
merge_requests_enabled: false,
|
||||
wiki_enabled: false
|
||||
})
|
||||
|
||||
post api("/projects/user/#{user.id}", admin), project
|
||||
|
||||
project.each_pair do |k,v|
|
||||
project.each_pair do |k, v|
|
||||
next if k == :path
|
||||
json_response[k.to_s].should == v
|
||||
end
|
||||
|
@ -267,7 +269,7 @@ describe API::API do
|
|||
it "should add user to project team" do
|
||||
expect {
|
||||
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)
|
||||
|
||||
response.status.should == 201
|
||||
|
@ -277,10 +279,10 @@ describe API::API 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,
|
||||
access_level: UsersProject::DEVELOPER
|
||||
access_level: UsersProject::DEVELOPER
|
||||
expect {
|
||||
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)
|
||||
|
||||
response.status.should == 201
|
||||
|
@ -411,8 +413,8 @@ describe API::API do
|
|||
it "should add hook to project" do
|
||||
expect {
|
||||
post api("/projects/#{project.id}/hooks", user),
|
||||
url: "http://example.com"
|
||||
}.to change {project.hooks.count}.by(1)
|
||||
url: "http://example.com"
|
||||
}.to change { project.hooks.count }.by(1)
|
||||
response.status.should == 201
|
||||
end
|
||||
|
||||
|
@ -430,7 +432,7 @@ describe API::API do
|
|||
describe "PUT /projects/:id/hooks/:hook_id" do
|
||||
it "should update an existing project hook" do
|
||||
put api("/projects/#{project.id}/hooks/#{hook.id}", user),
|
||||
url: 'http://example.org'
|
||||
url: 'http://example.org'
|
||||
response.status.should == 200
|
||||
json_response['url'].should == 'http://example.org'
|
||||
end
|
||||
|
@ -455,7 +457,7 @@ describe API::API do
|
|||
it "should delete hook from project" do
|
||||
expect {
|
||||
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
|
||||
end
|
||||
|
||||
|
@ -501,26 +503,26 @@ describe API::API do
|
|||
describe "POST /projects/:id/snippets" do
|
||||
it "should create a new project snippet" do
|
||||
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
|
||||
json_response['title'].should == 'api test'
|
||||
end
|
||||
|
||||
it "should return a 400 error if title is not given" do
|
||||
post api("/projects/#{project.id}/snippets", user),
|
||||
file_name: 'sample.rb', code: 'test'
|
||||
file_name: 'sample.rb', code: 'test'
|
||||
response.status.should == 400
|
||||
end
|
||||
|
||||
it "should return a 400 error if file_name not given" do
|
||||
post api("/projects/#{project.id}/snippets", user),
|
||||
title: 'api test', code: 'test'
|
||||
title: 'api test', code: 'test'
|
||||
response.status.should == 400
|
||||
end
|
||||
|
||||
it "should return a 400 error if code not given" do
|
||||
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
|
||||
end
|
||||
end
|
||||
|
@ -528,7 +530,7 @@ describe API::API do
|
|||
describe "PUT /projects/:id/snippets/:shippet_id" do
|
||||
it "should update an existing project snippet" do
|
||||
put api("/projects/#{project.id}/snippets/#{snippet.id}", user),
|
||||
code: 'updated code'
|
||||
code: 'updated code'
|
||||
response.status.should == 200
|
||||
json_response['title'].should == 'example'
|
||||
snippet.reload.content.should == 'updated code'
|
||||
|
@ -536,7 +538,7 @@ describe API::API do
|
|||
|
||||
it "should update an existing project snippet with new title" do
|
||||
put api("/projects/#{project.id}/snippets/#{snippet.id}", user),
|
||||
title: 'other api test'
|
||||
title: 'other api test'
|
||||
response.status.should == 200
|
||||
json_response['title'].should == 'other api test'
|
||||
end
|
||||
|
@ -598,7 +600,7 @@ describe API::API do
|
|||
|
||||
describe "POST /projects/:id/keys" 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
|
||||
end
|
||||
|
||||
|
@ -606,7 +608,7 @@ describe API::API do
|
|||
key_attrs = attributes_for :key
|
||||
expect {
|
||||
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
|
||||
|
||||
|
@ -616,7 +618,7 @@ describe API::API do
|
|||
it "should delete existing key" do
|
||||
expect {
|
||||
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
|
||||
|
||||
it "should return 404 Not Found with invalid ID" do
|
||||
|
|
|
@ -156,7 +156,8 @@ describe NotificationService do
|
|||
let(:merge_request) { create :merge_request, assignee: create(:user) }
|
||||
|
||||
before do
|
||||
build_team(merge_request.project)
|
||||
build_team(merge_request.source_project)
|
||||
build_team(merge_request.target_project)
|
||||
end
|
||||
|
||||
describe :new_merge_request do
|
||||
|
|
|
@ -2,6 +2,7 @@ require 'spec_helper'
|
|||
|
||||
describe ProjectTransferService do
|
||||
before(:each) { enable_observers }
|
||||
after(:each) {disable_observers}
|
||||
|
||||
context 'namespace -> namespace' do
|
||||
let(:user) { create(:user) }
|
||||
|
@ -24,6 +25,7 @@ describe ProjectTransferService do
|
|||
@result = service.transfer(project, nil)
|
||||
end
|
||||
|
||||
|
||||
it { @result.should be_true }
|
||||
it { project.namespace.should == nil }
|
||||
end
|
||||
|
|
|
@ -48,8 +48,11 @@ Spork.prefork do
|
|||
# instead of true.
|
||||
config.use_transactional_fixtures = false
|
||||
|
||||
config.before do
|
||||
TestEnv.init(observers: false)
|
||||
config.before(:suite) do
|
||||
TestEnv.init(observers: false, init_repos: true, repos: false)
|
||||
end
|
||||
config.before(:each) do
|
||||
TestEnv.setup_stubs
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -27,17 +27,65 @@ module TestEnv
|
|||
|
||||
# Disable mailer for spinach tests
|
||||
disable_mailer if opts[:mailer] == false
|
||||
setup_stubs
|
||||
|
||||
|
||||
clear_test_repo_dir if opts[:init_repos] == true
|
||||
setup_test_repos(opts) if opts[:repos] == true
|
||||
end
|
||||
|
||||
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 = Rails.root.join('tmp', 'test-git-base-path')
|
||||
Gitlab.config.gitlab_shell.stub(repos_path: repos_path)
|
||||
Gitlab::Git::Repository.stub(repos_path: repos_path)
|
||||
|
||||
repos_path = testing_path()
|
||||
GollumWiki.any_instance.stub(:init_repo) do |path|
|
||||
create_temp_repo(File.join(repos_path, "#{path}.git"))
|
||||
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(
|
||||
add_repository: true,
|
||||
mv_repository: true,
|
||||
|
@ -48,24 +96,54 @@ module TestEnv
|
|||
)
|
||||
|
||||
Gitlab::Satellite::Satellite.any_instance.stub(
|
||||
exists?: true,
|
||||
destroy: true,
|
||||
create: true
|
||||
exists?: true,
|
||||
destroy: true,
|
||||
create: true,
|
||||
lock_files_dir: repos_path
|
||||
)
|
||||
|
||||
MergeRequest.any_instance.stub(
|
||||
check_if_can_be_merged: true
|
||||
check_if_can_be_merged: true
|
||||
)
|
||||
|
||||
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
|
||||
FileUtils.rm_rf Gitlab.config.gitlab_shell.repos_path
|
||||
|
||||
# Recreate tmp/test-git-base-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
|
||||
|
||||
def create_temp_repo(path)
|
||||
|
|
Loading…
Reference in New Issue