Skip to content
Snippets Groups Projects
Commit 67948368 authored by Alfredo Sumaran's avatar Alfredo Sumaran
Browse files

Merge branch 'master' into sidebar-sizing-higher-viewport

parents 60801262 7c809985
No related branches found
No related tags found
No related merge requests found
Showing
with 348 additions and 106 deletions
Loading
Loading
@@ -69,10 +69,35 @@ module Issuable
case method.to_s
when 'milestone_due_asc' then order_milestone_due_asc
when 'milestone_due_desc' then order_milestone_due_desc
when 'downvotes_desc' then order_downvotes_desc
when 'upvotes_desc' then order_upvotes_desc
else
order_by(method)
end
end
def order_downvotes_desc
order_votes_desc('thumbsdown')
end
def order_upvotes_desc
order_votes_desc('thumbsup')
end
def order_votes_desc(award_emoji_name)
issuable_table = self.arel_table
note_table = Note.arel_table
join_clause = issuable_table.join(note_table, Arel::Nodes::OuterJoin).on(
note_table[:noteable_id].eq(issuable_table[:id]).and(
note_table[:noteable_type].eq(self.name).and(
note_table[:is_award].eq(true).and(note_table[:note].eq(award_emoji_name))
)
)
).join_sources
joins(join_clause).group(issuable_table[:id]).reorder("COUNT(notes.id) DESC")
end
end
 
def today?
Loading
Loading
Loading
Loading
@@ -2,13 +2,14 @@
#
# Table name: labels
#
# id :integer not null, primary key
# title :string(255)
# color :string(255)
# project_id :integer
# created_at :datetime
# updated_at :datetime
# template :boolean default(FALSE)
# id :integer not null, primary key
# title :string(255)
# color :string(255)
# project_id :integer
# created_at :datetime
# updated_at :datetime
# template :boolean default(FALSE)
# description :string(255)
#
 
class Label < ActiveRecord::Base
Loading
Loading
@@ -85,6 +86,10 @@ class Label < ActiveRecord::Base
issues.opened.count
end
 
def closed_issues_count
issues.closed.count
end
def template?
template
end
Loading
Loading
Loading
Loading
@@ -24,6 +24,7 @@
# merge_params :text
# merge_when_build_succeeds :boolean default(FALSE), not null
# merge_user_id :integer
# merge_commit_sha :string
#
 
require Rails.root.join("app/models/commit")
Loading
Loading
@@ -137,7 +138,7 @@ class MergeRequest < ActiveRecord::Base
scope :by_milestone, ->(milestone) { where(milestone_id: milestone) }
scope :in_projects, ->(project_ids) { where("source_project_id in (:project_ids) OR target_project_id in (:project_ids)", project_ids: project_ids) }
scope :of_projects, ->(ids) { where(target_project_id: ids) }
scope :opened, -> { with_state(:opened) }
scope :opened, -> { with_states(:opened, :reopened) }
scope :merged, -> { with_state(:merged) }
scope :closed, -> { with_state(:closed) }
scope :closed_and_merged, -> { with_states(:closed, :merged) }
Loading
Loading
@@ -532,4 +533,12 @@ class MergeRequest < ActiveRecord::Base
 
[diff_base_commit, last_commit]
end
def merge_commit
@merge_commit ||= project.commit(merge_commit_sha) if merge_commit_sha
end
def can_be_reverted?(current_user = nil)
merge_commit && !merge_commit.has_been_reverted?(current_user, self)
end
end
Loading
Loading
@@ -27,6 +27,7 @@ class Milestone < ActiveRecord::Base
 
belongs_to :project
has_many :issues
has_many :labels, through: :issues
has_many :merge_requests
has_many :participants, through: :issues, source: :assignee
 
Loading
Loading
@@ -109,6 +110,19 @@ class Milestone < ActiveRecord::Base
0
end
 
# Returns the elapsed time (in percent) since the Milestone creation date until today.
# If the Milestone doesn't have a due_date then returns 0 since we can't calculate the elapsed time.
# If the Milestone is overdue then it returns 100%.
def percent_time_used
return 0 unless due_date
return 100 if expired?
duration = ((created_at - due_date.to_datetime) / 1.day)
days_elapsed = ((created_at - Time.now) / 1.day)
((days_elapsed.to_f / duration) * 100).floor
end
def expires_at
if due_date
if due_date.past?
Loading
Loading
Loading
Loading
@@ -37,6 +37,8 @@ class Note < ActiveRecord::Base
belongs_to :author, class_name: "User"
belongs_to :updated_by, class_name: "User"
 
has_many :todos, dependent: :destroy
delegate :name, to: :project, prefix: true
delegate :name, :email, to: :author, prefix: true
 
Loading
Loading
@@ -375,6 +377,7 @@ class Note < ActiveRecord::Base
#
def set_award!
return unless awards_supported? && contains_emoji_only?
self.is_award = true
self.note = award_emoji_name
end
Loading
Loading
@@ -382,7 +385,7 @@ class Note < ActiveRecord::Base
private
 
def awards_supported?
noteable.kind_of?(Issue) || noteable.is_a?(MergeRequest)
(noteable.kind_of?(Issue) || noteable.is_a?(MergeRequest)) && !for_diff_line?
end
 
def contains_emoji_only?
Loading
Loading
Loading
Loading
@@ -382,6 +382,10 @@ class Project < ActiveRecord::Base
external_import? || forked?
end
 
def no_import?
import_status == 'none'
end
def external_import?
import_url.present?
end
Loading
Loading
Loading
Loading
@@ -136,7 +136,7 @@ class ProjectTeam
end
 
def human_max_access(user_id)
Gitlab::Access.options.key max_member_access(user_id)
Gitlab::Access.options_with_owner.key(max_member_access(user_id))
end
 
# This method assumes project and group members are eager loaded for optimal
Loading
Loading
Loading
Loading
@@ -23,13 +23,11 @@ class Repository
def raw_repository
return nil unless path_with_namespace
 
@raw_repository ||= begin
repo = Gitlab::Git::Repository.new(path_to_repo)
repo.autocrlf = :input
repo
rescue Gitlab::Git::Repository::NoRepository
nil
end
@raw_repository ||= Gitlab::Git::Repository.new(path_to_repo)
end
def update_autocrlf_option
raw_repository.autocrlf = :input if raw_repository.autocrlf != :input
end
 
# Return absolute path to repository
Loading
Loading
@@ -40,7 +38,12 @@ class Repository
end
 
def exists?
raw_repository
return false unless raw_repository
raw_repository.rugged
true
rescue Gitlab::Git::Repository::NoRepository
false
end
 
def empty?
Loading
Loading
@@ -67,7 +70,7 @@ class Repository
end
 
def commit(id = 'HEAD')
return nil unless raw_repository
return nil unless exists?
commit = Gitlab::Git::Commit.find(raw_repository, id)
commit = Commit.new(commit, @project) if commit
commit
Loading
Loading
@@ -236,6 +239,19 @@ class Repository
end
 
expire_branch_cache(branch_name)
# This ensures this particular cache is flushed after the first commit to a
# new repository.
expire_emptiness_caches if empty?
end
# Expires _all_ caches, including those that would normally only be expired
# under specific conditions.
def expire_all_caches!
expire_cache
expire_root_ref_cache
expire_emptiness_caches
expire_has_visible_content_cache
end
 
def expire_branch_cache(branch_name = nil)
Loading
Loading
@@ -258,6 +274,14 @@ class Repository
@root_ref = nil
end
 
# Expires the cache(s) used to determine if a repository is empty or not.
def expire_emptiness_caches
cache.expire(:empty?)
@empty = nil
expire_has_visible_content_cache
end
def expire_has_visible_content_cache
cache.expire(:has_visible_content?)
@has_visible_content = nil
Loading
Loading
@@ -599,6 +623,34 @@ class Repository
end
end
 
def revert(user, commit, base_branch, target_branch = nil)
source_sha = find_branch(base_branch).target
target_branch ||= base_branch
args = [commit.id, source_sha]
args << { mainline: 1 } if commit.merge_commit?
revert_index = rugged.revert_commit(*args)
return false if revert_index.conflicts?
tree_id = revert_index.write_tree(rugged)
return false unless diff_exists?(source_sha, tree_id)
commit_with_hooks(user, target_branch) do |ref|
committer = user_to_committer(user)
source_sha = Rugged::Commit.create(rugged,
message: commit.revert_message,
author: committer,
committer: committer,
tree: tree_id,
parents: [rugged.lookup(source_sha)],
update_ref: ref)
end
end
def diff_exists?(sha1, sha2)
rugged.diff(sha1, sha2).size > 0
end
def merged_to_root_ref?(branch_name)
branch_commit = commit(branch_name)
root_ref_commit = commit(root_ref)
Loading
Loading
@@ -611,6 +663,8 @@ class Repository
end
 
def merge_base(first_commit_id, second_commit_id)
first_commit_id = commit(first_commit_id).try(:id) || first_commit_id
second_commit_id = commit(second_commit_id).try(:id) || second_commit_id
rugged.merge_base(first_commit_id, second_commit_id)
rescue Rugged::ReferenceError
nil
Loading
Loading
@@ -674,12 +728,15 @@ class Repository
end
 
def commit_with_hooks(current_user, branch)
update_autocrlf_option
oldrev = Gitlab::Git::BLANK_SHA
ref = Gitlab::Git::BRANCH_REF_PREFIX + branch
target_branch = find_branch(branch)
was_empty = empty?
 
unless was_empty
oldrev = find_branch(branch).target
if !was_empty && target_branch
oldrev = target_branch.target
end
 
with_tmp_ref(oldrev) do |tmp_ref|
Loading
Loading
@@ -691,7 +748,7 @@ class Repository
end
 
GitHooksService.new.execute(current_user, path_to_repo, oldrev, newrev, ref) do
if was_empty
if was_empty || !target_branch
# Create branch
rugged.references.create(ref, newrev)
else
Loading
Loading
@@ -706,6 +763,8 @@ class Repository
end
end
end
newrev
end
end
 
Loading
Loading
# == Schema Information
#
# Table name: todos
#
# id :integer not null, primary key
# user_id :integer not null
# project_id :integer not null
# target_id :integer not null
# target_type :string not null
# author_id :integer
# note_id :integer
# action :integer not null
# state :string not null
# created_at :datetime
# updated_at :datetime
#
class Todo < ActiveRecord::Base
ASSIGNED = 1
MENTIONED = 2
belongs_to :author, class_name: "User"
belongs_to :note
belongs_to :project
belongs_to :target, polymorphic: true, touch: true
belongs_to :user
delegate :name, :email, to: :author, prefix: true, allow_nil: true
validates :action, :project, :target, :user, presence: true
default_scope { reorder(id: :desc) }
scope :pending, -> { with_state(:pending) }
scope :done, -> { with_state(:done) }
state_machine :state, initial: :pending do
event :done do
transition pending: :done
end
state :pending
state :done
end
def body
if note.present?
note.note
else
target.title
end
end
end
Loading
Loading
@@ -140,7 +140,7 @@ class User < ActiveRecord::Base
has_one :abuse_report, dependent: :destroy
has_many :spam_logs, dependent: :destroy
has_many :builds, dependent: :nullify, class_name: 'Ci::Build'
has_many :todos, dependent: :destroy
 
#
# Validations
Loading
Loading
class ArchiveRepositoryService
attr_reader :project, :ref, :format
def initialize(project, ref, format)
format ||= 'tar.gz'
@project, @ref, @format = project, ref, format.downcase
end
def execute(options = {})
RepositoryArchiveCacheWorker.perform_async
metadata = project.repository.archive_metadata(ref, storage_path, format)
raise "Repository or ref not found" if metadata.empty?
metadata
end
private
def storage_path
Gitlab.config.gitlab.repository_downloads_path
end
end
Loading
Loading
@@ -23,6 +23,10 @@ class BaseService
EventCreateService.new
end
 
def todo_service
TodoService.new
end
def log_info(message)
Gitlab::AppLogger.info message
end
Loading
Loading
Loading
Loading
@@ -34,6 +34,7 @@ module Ci
 
build = commit.builds.create!(build_attrs)
build.execute_hooks
build
end
end
end
Loading
Loading
module Commits
class RevertService < ::BaseService
class ValidationError < StandardError; end
class ReversionError < StandardError; end
def execute
@source_project = params[:source_project] || @project
@target_branch = params[:target_branch]
@commit = params[:commit]
@create_merge_request = params[:create_merge_request].present?
validate and commit
rescue Repository::CommitError, Gitlab::Git::Repository::InvalidBlobName, GitHooksService::PreReceiveError,
ValidationError, ReversionError => ex
error(ex.message)
end
def commit
revert_into = @create_merge_request ? @commit.revert_branch_name : @target_branch
if @create_merge_request
# Temporary branch exists and contains the revert commit
return success if repository.find_branch(revert_into)
create_target_branch
end
unless repository.revert(current_user, @commit, revert_into)
error_msg = "Sorry, we cannot revert this #{params[:revert_type_title]} automatically.
It may have already been reverted, or a more recent commit may have updated some of its content."
raise ReversionError, error_msg
end
success
end
private
def create_target_branch
result = CreateBranchService.new(@project, current_user)
.execute(@commit.revert_branch_name, @target_branch, source_project: @source_project)
if result[:status] == :error
raise ReversionError, "There was an error creating the source branch: #{result[:message]}"
end
end
def validate
allowed = ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(@target_branch)
unless allowed
raise_error('You are not allowed to push into this branch')
end
true
end
end
end
class GitPushService
attr_accessor :project, :user, :push_data, :push_commits
class GitPushService < BaseService
attr_accessor :push_data, :push_commits
include Gitlab::CurrentSettings
include Gitlab::Access
 
# This method will be called after each git update
# and only if the provided user and project is present in GitLab.
# and only if the provided user and project are present in GitLab.
#
# All callbacks for post receive action should be placed here.
#
Loading
Loading
@@ -15,67 +15,67 @@ class GitPushService
# 4. Executes the project's web hooks
# 5. Executes the project's services
#
def execute(project, user, oldrev, newrev, ref)
@project, @user = project, user
branch_name = Gitlab::Git.ref_name(ref)
project.repository.expire_cache(branch_name)
if push_remove_branch?(ref, newrev)
project.repository.expire_has_visible_content_cache
def execute
@project.repository.expire_cache(branch_name)
 
if push_remove_branch?
@project.repository.expire_has_visible_content_cache
@push_commits = []
elsif push_to_new_branch?(ref, oldrev)
project.repository.expire_has_visible_content_cache
elsif push_to_new_branch?
@project.repository.expire_has_visible_content_cache
 
# Re-find the pushed commits.
if is_default_branch?(ref)
if is_default_branch?
# Initial push to the default branch. Take the full history of that branch as "newly pushed".
@push_commits = project.repository.commits(newrev)
# Ensure HEAD points to the default branch in case it is not master
project.change_head(branch_name)
# Set protection on the default branch if configured
if (current_application_settings.default_branch_protection != PROTECTION_NONE)
developers_can_push = current_application_settings.default_branch_protection == PROTECTION_DEV_CAN_PUSH ? true : false
project.protected_branches.create({ name: project.default_branch, developers_can_push: developers_can_push })
end
process_default_branch
else
# Use the pushed commits that aren't reachable by the default branch
# as a heuristic. This may include more commits than are actually pushed, but
# that shouldn't matter because we check for existing cross-references later.
@push_commits = project.repository.commits_between(project.default_branch, newrev)
@push_commits = @project.repository.commits_between(@project.default_branch, params[:newrev])
 
# don't process commits for the initial push to the default branch
process_commit_messages(ref)
process_commit_messages
end
elsif push_to_existing_branch?(ref, oldrev)
elsif push_to_existing_branch?
# Collect data for this git push
@push_commits = project.repository.commits_between(oldrev, newrev)
process_commit_messages(ref)
@push_commits = @project.repository.commits_between(params[:oldrev], params[:newrev])
process_commit_messages
end
# Update merge requests that may be affected by this push. A new branch
# could cause the last commit of a merge request to change.
project.update_merge_requests(oldrev, newrev, ref, @user)
update_merge_requests
end
 
@push_data = build_push_data(oldrev, newrev, ref)
protected
 
EventCreateService.new.push(project, user, @push_data)
project.execute_hooks(@push_data.dup, :push_hooks)
project.execute_services(@push_data.dup, :push_hooks)
CreateCommitBuildsService.new.execute(project, @user, @push_data)
ProjectCacheWorker.perform_async(project.id)
def update_merge_requests
@project.update_merge_requests(params[:oldrev], params[:newrev], params[:ref], current_user)
EventCreateService.new.push(@project, current_user, build_push_data)
@project.execute_hooks(build_push_data.dup, :push_hooks)
@project.execute_services(build_push_data.dup, :push_hooks)
CreateCommitBuildsService.new.execute(@project, current_user, build_push_data)
ProjectCacheWorker.perform_async(@project.id)
end
 
protected
def process_default_branch
@push_commits = project.repository.commits(params[:newrev])
# Ensure HEAD points to the default branch in case it is not master
project.change_head(branch_name)
# Set protection on the default branch if configured
if (current_application_settings.default_branch_protection != PROTECTION_NONE)
developers_can_push = current_application_settings.default_branch_protection == PROTECTION_DEV_CAN_PUSH ? true : false
@project.protected_branches.create({ name: @project.default_branch, developers_can_push: developers_can_push })
end
end
 
# Extract any GFM references from the pushed commit messages. If the configured issue-closing regex is matched,
# close the referenced Issue. Create cross-reference Notes corresponding to any other referenced Mentionables.
def process_commit_messages(ref)
is_default_branch = is_default_branch?(ref)
def process_commit_messages
is_default_branch = is_default_branch?
 
authors = Hash.new do |hash, commit|
email = commit.author_email
Loading
Loading
@@ -94,7 +94,7 @@ class GitPushService
# Close issues if these commits were pushed to the project's default branch and the commit message matches the
# closing regex. Exclude any mentioned Issues from cross-referencing even if the commits are being pushed to
# a different branch.
closed_issues = commit.closes_issues(user)
closed_issues = commit.closes_issues(current_user)
closed_issues.each do |issue|
Issues::CloseService.new(project, authors[commit], {}).execute(issue, commit)
end
Loading
Loading
@@ -104,34 +104,38 @@ class GitPushService
end
end
 
def build_push_data(oldrev, newrev, ref)
Gitlab::PushDataBuilder.
build(project, user, oldrev, newrev, ref, push_commits)
def build_push_data
@push_data ||= Gitlab::PushDataBuilder.
build(@project, current_user, params[:oldrev], params[:newrev], params[:ref], push_commits)
end
 
def push_to_existing_branch?(ref, oldrev)
def push_to_existing_branch?
# Return if this is not a push to a branch (e.g. new commits)
Gitlab::Git.branch_ref?(ref) && !Gitlab::Git.blank_ref?(oldrev)
Gitlab::Git.branch_ref?(params[:ref]) && !Gitlab::Git.blank_ref?(params[:oldrev])
end
 
def push_to_new_branch?(ref, oldrev)
Gitlab::Git.branch_ref?(ref) && Gitlab::Git.blank_ref?(oldrev)
def push_to_new_branch?
Gitlab::Git.branch_ref?(params[:ref]) && Gitlab::Git.blank_ref?(params[:oldrev])
end
 
def push_remove_branch?(ref, newrev)
Gitlab::Git.branch_ref?(ref) && Gitlab::Git.blank_ref?(newrev)
def push_remove_branch?
Gitlab::Git.branch_ref?(params[:ref]) && Gitlab::Git.blank_ref?(params[:newrev])
end
 
def push_to_branch?(ref)
Gitlab::Git.branch_ref?(ref)
def push_to_branch?
Gitlab::Git.branch_ref?(params[:ref])
end
 
def is_default_branch?(ref)
Gitlab::Git.branch_ref?(ref) &&
(Gitlab::Git.ref_name(ref) == project.default_branch || project.default_branch.nil?)
def is_default_branch?
Gitlab::Git.branch_ref?(params[:ref]) &&
(Gitlab::Git.ref_name(params[:ref]) == project.default_branch || project.default_branch.nil?)
end
 
def commit_user(commit)
commit.author || user
commit.author || current_user
end
def branch_name
@branch_name ||= Gitlab::Git.ref_name(params[:ref])
end
end
Loading
Loading
@@ -54,7 +54,7 @@ class IssuableBaseService < BaseService
if params.present? && issuable.update_attributes(params.merge(updated_by: current_user))
issuable.reset_events_cache
handle_common_system_notes(issuable, old_labels: old_labels)
handle_changes(issuable)
handle_changes(issuable, old_labels: old_labels)
issuable.create_new_cross_references!(current_user)
execute_hooks(issuable, 'update')
end
Loading
Loading
@@ -71,6 +71,19 @@ class IssuableBaseService < BaseService
end
end
 
def has_changes?(issuable, options = {})
valid_attrs = [:title, :description, :assignee_id, :milestone_id, :target_branch]
attrs_changed = valid_attrs.any? do |attr|
issuable.previous_changes.include?(attr.to_s)
end
old_labels = options[:old_labels]
labels_changed = old_labels && issuable.labels != old_labels
attrs_changed || labels_changed
end
def handle_common_system_notes(issuable, options = {})
if issuable.previous_changes.include?('title')
create_title_change_note(issuable, issuable.previous_changes['title'].first)
Loading
Loading
Loading
Loading
@@ -3,6 +3,7 @@ module Issues
def execute(issue, commit = nil)
if project.jira_tracker? && project.jira_service.active
project.jira_service.execute(commit, issue)
todo_service.close_issue(issue, current_user)
return issue
end
 
Loading
Loading
@@ -10,6 +11,7 @@ module Issues
event_service.close_issue(issue, current_user)
create_note(issue, commit)
notification_service.close_issue(issue, current_user)
todo_service.close_issue(issue, current_user)
execute_hooks(issue, 'close')
end
 
Loading
Loading
Loading
Loading
@@ -9,6 +9,7 @@ module Issues
if issue.save
issue.update_attributes(label_ids: label_params)
notification_service.new_issue(issue, current_user)
todo_service.new_issue(issue, current_user)
event_service.open_issue(issue, current_user)
issue.create_cross_references!(current_user)
execute_hooks(issue, 'open')
Loading
Loading
Loading
Loading
@@ -4,7 +4,16 @@ module Issues
update(issue)
end
 
def handle_changes(issue)
def handle_changes(issue, options = {})
if has_changes?(issue, options)
todo_service.mark_pending_todos_as_done(issue, current_user)
end
if issue.previous_changes.include?('title') ||
issue.previous_changes.include?('description')
todo_service.update_issue(issue, current_user)
end
if issue.previous_changes.include?('milestone_id')
create_milestone_note(issue)
end
Loading
Loading
@@ -12,6 +21,7 @@ module Issues
if issue.previous_changes.include?('assignee_id')
create_assignee_note(issue)
notification_service.reassigned_issue(issue, current_user)
todo_service.reassigned_issue(issue, current_user)
end
end
 
Loading
Loading
Loading
Loading
@@ -56,7 +56,7 @@ module MergeRequests
if commits && commits.count == 1
commit = commits.first
merge_request.title = commit.title
merge_request.description = commit.description.try(:strip)
merge_request.description ||= commit.description.try(:strip)
else
merge_request.title = merge_request.source_branch.titleize.humanize
end
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment