diff options
author | James Lopez <james@jameslopez.es> | 2016-03-08 15:55:04 +0100 |
---|---|---|
committer | James Lopez <james@jameslopez.es> | 2016-03-08 15:55:04 +0100 |
commit | ecb1c59665b46b9b52552cdcea056159c6bdbfa0 (patch) | |
tree | 588f9c33b46bfc8c8fa72f8b339fff404fdd45b0 /app/models | |
parent | 70623cd423b0c7e26e56422bf25c413d9921ee88 (diff) | |
parent | 36730e8e63c0ebe16785f33cb2b740f8d67b2b25 (diff) | |
download | gitlab-ce-ecb1c59665b46b9b52552cdcea056159c6bdbfa0.tar.gz |
Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into fix/token-timing-attack
Diffstat (limited to 'app/models')
-rw-r--r-- | app/models/ability.rb | 2 | ||||
-rw-r--r-- | app/models/blob.rb | 3 | ||||
-rw-r--r-- | app/models/commit.rb | 16 | ||||
-rw-r--r-- | app/models/concerns/issuable.rb | 4 | ||||
-rw-r--r-- | app/models/concerns/milestoneish.rb | 25 | ||||
-rw-r--r-- | app/models/diff_line.rb | 3 | ||||
-rw-r--r-- | app/models/global_label.rb | 7 | ||||
-rw-r--r-- | app/models/global_milestone.rb | 55 | ||||
-rw-r--r-- | app/models/label.rb | 49 | ||||
-rw-r--r-- | app/models/merge_request.rb | 44 | ||||
-rw-r--r-- | app/models/merge_request_diff.rb | 86 | ||||
-rw-r--r-- | app/models/milestone.rb | 34 | ||||
-rw-r--r-- | app/models/note.rb | 49 | ||||
-rw-r--r-- | app/models/project.rb | 21 | ||||
-rw-r--r-- | app/models/project_services/jira_service.rb | 6 | ||||
-rw-r--r-- | app/models/repository.rb | 38 | ||||
-rw-r--r-- | app/models/user.rb | 16 |
17 files changed, 250 insertions, 208 deletions
diff --git a/app/models/ability.rb b/app/models/ability.rb index a866eadeebb..f34554d557c 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -188,6 +188,7 @@ class Ability def project_dev_rules @project_dev_rules ||= project_report_rules + [ :admin_merge_request, + :update_merge_request, :create_commit_status, :update_commit_status, :create_build, @@ -212,7 +213,6 @@ class Ability @project_master_rules ||= project_dev_rules + [ :push_code_to_protected_branches, :update_project_snippet, - :update_merge_request, :admin_milestone, :admin_project_snippet, :admin_project_member, diff --git a/app/models/blob.rb b/app/models/blob.rb index 8ee9f3006b2..72e6c5fa3fd 100644 --- a/app/models/blob.rb +++ b/app/models/blob.rb @@ -1,5 +1,8 @@ # Blob is a Rails-specific wrapper around Gitlab::Git::Blob objects class Blob < SimpleDelegator + CACHE_TIME = 60 # Cache raw blobs referred to by a (mutable) ref for 1 minute + CACHE_TIME_IMMUTABLE = 3600 # Cache blobs referred to by an immutable reference for 1 hour + # Wrap a Gitlab::Git::Blob object, or return nil when given nil # # This method prevents the decorated object from evaluating to "truthy" when diff --git a/app/models/commit.rb b/app/models/commit.rb index 3224f5457f0..ce0b85d50cf 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -12,12 +12,7 @@ class Commit attr_accessor :project - # Safe amount of changes (files and lines) in one commit to render - # Used to prevent 500 error on huge commits by suppressing diff - # - # User can force display of diff above this size - DIFF_SAFE_FILES = 100 unless defined?(DIFF_SAFE_FILES) - DIFF_SAFE_LINES = 5000 unless defined?(DIFF_SAFE_LINES) + DIFF_SAFE_LINES = Gitlab::Git::DiffCollection::DEFAULT_LIMITS[:max_lines] # Commits above this size will not be rendered in HTML DIFF_HARD_LIMIT_FILES = 1000 unless defined?(DIFF_HARD_LIMIT_FILES) @@ -36,13 +31,20 @@ class Commit # Calculate number of lines to render for diffs def diff_line_count(diffs) - diffs.reduce(0) { |sum, d| sum + d.diff.lines.count } + diffs.reduce(0) { |sum, d| sum + Gitlab::Git::Util.count_lines(d.diff) } end # Truncate sha to 8 characters def truncate_sha(sha) sha[0..7] end + + def max_diff_options + { + max_files: DIFF_HARD_LIMIT_FILES, + max_lines: DIFF_HARD_LIMIT_LINES, + } + end end attr_accessor :raw diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index e5f089fb8a0..27b97944e38 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -29,15 +29,19 @@ module Issuable scope :assigned, -> { where("assignee_id IS NOT NULL") } scope :unassigned, -> { where("assignee_id IS NULL") } scope :of_projects, ->(ids) { where(project_id: ids) } + scope :of_milestones, ->(ids) { where(milestone_id: ids) } scope :opened, -> { with_state(:opened, :reopened) } scope :only_opened, -> { with_state(:opened) } scope :only_reopened, -> { with_state(:reopened) } scope :closed, -> { with_state(:closed) } scope :order_milestone_due_desc, -> { joins(:milestone).reorder('milestones.due_date DESC, milestones.id DESC') } scope :order_milestone_due_asc, -> { joins(:milestone).reorder('milestones.due_date ASC, milestones.id ASC') } + scope :with_label, ->(title) { joins(:labels).where(labels: { title: title }) } + scope :without_label, -> { joins("LEFT OUTER JOIN label_links ON label_links.target_type = '#{name}' AND label_links.target_id = #{table_name}.id").where(label_links: { id: nil }) } scope :join_project, -> { joins(:project) } scope :references_project, -> { references(:project) } + scope :non_archived, -> { join_project.merge(Project.non_archived) } delegate :name, :email, diff --git a/app/models/concerns/milestoneish.rb b/app/models/concerns/milestoneish.rb new file mode 100644 index 00000000000..d67df7c1d9c --- /dev/null +++ b/app/models/concerns/milestoneish.rb @@ -0,0 +1,25 @@ +module Milestoneish + def closed_items_count + issues.closed.size + merge_requests.closed_and_merged.size + end + + def total_items_count + issues.size + merge_requests.size + end + + def complete? + total_items_count == closed_items_count + end + + def percent_complete + ((closed_items_count * 100) / total_items_count).abs + rescue ZeroDivisionError + 0 + end + + def remaining_days + return 0 if !due_date || expired? + + (due_date - Date.today).to_i + end +end diff --git a/app/models/diff_line.rb b/app/models/diff_line.rb deleted file mode 100644 index ad37945874a..00000000000 --- a/app/models/diff_line.rb +++ /dev/null @@ -1,3 +0,0 @@ -class DiffLine - attr_accessor :type, :content, :num, :code -end diff --git a/app/models/global_label.rb b/app/models/global_label.rb index 0171f7d54b7..ddd4bad5c21 100644 --- a/app/models/global_label.rb +++ b/app/models/global_label.rb @@ -2,16 +2,19 @@ class GlobalLabel attr_accessor :title, :labels alias_attribute :name, :title + delegate :color, :description, to: :@first_label + def self.build_collection(labels) labels = labels.group_by(&:title) - labels.map do |title, label| - new(title, label) + labels.map do |title, labels| + new(title, labels) end end def initialize(title, labels) @title = title @labels = labels + @first_label = labels.find { |lbl| lbl.description.present? } || labels.first end end diff --git a/app/models/global_milestone.rb b/app/models/global_milestone.rb index 7ee276255a0..97bd79af083 100644 --- a/app/models/global_milestone.rb +++ b/app/models/global_milestone.rb @@ -1,4 +1,6 @@ class GlobalMilestone + include Milestoneish + attr_accessor :title, :milestones alias_attribute :name, :title @@ -28,33 +30,7 @@ class GlobalMilestone end def projects - milestones.map { |milestone| milestone.project } - end - - def issue_count - milestones.map { |milestone| milestone.issues.count }.sum - end - - def merge_requests_count - milestones.map { |milestone| milestone.merge_requests.count }.sum - end - - def open_items_count - milestones.map { |milestone| milestone.open_items_count }.sum - end - - def closed_items_count - milestones.map { |milestone| milestone.closed_items_count }.sum - end - - def total_items_count - milestones.map { |milestone| milestone.total_items_count }.sum - end - - def percent_complete - ((closed_items_count * 100) / total_items_count).abs - rescue ZeroDivisionError - 0 + @projects ||= Project.for_milestones(milestones.map(&:id)) end def state @@ -76,35 +52,20 @@ class GlobalMilestone end def issues - @issues ||= milestones.map(&:issues).flatten.group_by(&:state) + @issues ||= Issue.of_milestones(milestones.map(&:id)).includes(:project) end def merge_requests - @merge_requests ||= milestones.map(&:merge_requests).flatten.group_by(&:state) + @merge_requests ||= MergeRequest.of_milestones(milestones.map(&:id)).includes(:target_project) end def participants @participants ||= milestones.map(&:participants).flatten.compact.uniq end - def opened_issues - issues.values_at("opened", "reopened").compact.flatten - end - - def closed_issues - issues['closed'] - end - - def opened_merge_requests - merge_requests.values_at("opened", "reopened").compact.flatten - end - - def closed_merge_requests - merge_requests.values_at("closed", "merged", "locked").compact.flatten - end - - def complete? - total_items_count == closed_items_count + def labels + @labels ||= GlobalLabel.build_collection(milestones.map(&:labels).flatten) + .sort_by!(&:title) end def due_date diff --git a/app/models/label.rb b/app/models/label.rb index 07a1db4abe5..5ff644b8426 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -27,6 +27,7 @@ class Label < ActiveRecord::Base belongs_to :project has_many :label_links, dependent: :destroy has_many :issues, through: :label_links, source: :target, source_type: 'Issue' + has_many :merge_requests, through: :label_links, source: :target, source_type: 'MergeRequest' validates :color, color: true, allow_blank: false validates :project, presence: true, unless: Proc.new { |service| service.template? } @@ -47,10 +48,15 @@ class Label < ActiveRecord::Base '~' end + ## # Pattern used to extract label references from text + # + # This pattern supports cross-project references. + # def self.reference_pattern %r{ - #{reference_prefix} + (#{Project.reference_pattern})? + #{Regexp.escape(reference_prefix)} (?: (?<label_id>\d+) | # Integer-based label ID, or (?<label_name> @@ -61,24 +67,31 @@ class Label < ActiveRecord::Base }x end + def self.link_reference_pattern + nil + end + + ## # Returns the String necessary to reference this Label in Markdown # # format - Symbol format to use (default: :id, optional: :name) # - # Note that its argument differs from other objects implementing Referable. If - # a non-Symbol argument is given (such as a Project), it will default to :id. - # # Examples: # - # Label.first.to_reference # => "~1" - # Label.first.to_reference(:name) # => "~\"bug\"" + # Label.first.to_reference # => "~1" + # Label.first.to_reference(format: :name) # => "~\"bug\"" + # Label.first.to_reference(project) # => "gitlab-org/gitlab-ce~1" # # Returns a String - def to_reference(format = :id) - if format == :name && !name.include?('"') - %(#{self.class.reference_prefix}"#{name}") + # + def to_reference(from_project = nil, format: :id) + format_reference = label_format_reference(format) + reference = "#{self.class.reference_prefix}#{format_reference}" + + if cross_project_reference?(from_project) + project.to_reference + reference else - "#{self.class.reference_prefix}#{id}" + reference end end @@ -90,7 +103,23 @@ class Label < ActiveRecord::Base issues.closed.count end + def open_merge_requests_count + merge_requests.opened.count + end + def template? template end + + private + + def label_format_reference(format = :id) + raise StandardError, 'Unknown format' unless [:id, :name].include?(format) + + if format == :name && !name.include?('"') + %("#{name}") + else + id + end + end end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 1543ef311d7..c1e18bb3cc5 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -48,7 +48,7 @@ class MergeRequest < ActiveRecord::Base after_create :create_merge_request_diff after_update :update_merge_request_diff - delegate :commits, :diffs, :diffs_no_whitespace, to: :merge_request_diff, prefix: nil + delegate :commits, :diffs, :real_size, to: :merge_request_diff, prefix: nil # When this attribute is true some MR validation is ignored # It allows us to close or modify broken merge requests @@ -56,8 +56,7 @@ class MergeRequest < ActiveRecord::Base # Temporary fields to store compare vars # when creating new merge request - attr_accessor :can_be_created, :compare_failed, - :compare_commits, :compare_diffs + attr_accessor :can_be_created, :compare_commits, :compare state_machine :state, initial: :opened do event :close do @@ -138,9 +137,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_states(:opened, :reopened) } scope :merged, -> { with_state(:merged) } - scope :closed, -> { with_state(:closed) } scope :closed_and_merged, -> { with_states(:closed, :merged) } scope :join_project, -> { joins(:target_project) } @@ -182,6 +179,10 @@ class MergeRequest < ActiveRecord::Base merge_request_diff ? merge_request_diff.first_commit : compare_commits.first end + def diff_size + merge_request_diff.size + end + def diff_base_commit if merge_request_diff merge_request_diff.base_commit @@ -487,6 +488,16 @@ class MergeRequest < ActiveRecord::Base end end + def state_icon_name + if merged? + "check" + elsif closed? + "times" + else + "circle-o" + end + end + def target_sha @target_sha ||= target_project.repository.commit(target_branch).sha end @@ -524,6 +535,29 @@ class MergeRequest < ActiveRecord::Base end end + def diverged_commits_count + cache = Rails.cache.read(:"merge_request_#{id}_diverged_commits") + + if cache.blank? || cache[:source_sha] != source_sha || cache[:target_sha] != target_sha + cache = { + source_sha: source_sha, + target_sha: target_sha, + diverged_commits_count: compute_diverged_commits_count + } + Rails.cache.write(:"merge_request_#{id}_diverged_commits", cache) + end + + cache[:diverged_commits_count] + end + + def compute_diverged_commits_count + Gitlab::Git::Commit.between(target_project.repository.raw_repository, source_sha, target_sha).size + end + + def diverged_from_target_branch? + diverged_commits_count > 0 + end + def ci_commit @ci_commit ||= source_project.ci_commit(last_commit.id) if last_commit && source_project end diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index c95179d6046..df08d3a6dfb 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -19,14 +19,15 @@ class MergeRequestDiff < ActiveRecord::Base # Prevent store of diff if commits amount more then 500 COMMITS_SAFE_SIZE = 500 - attr_reader :commits, :diffs, :diffs_no_whitespace - belongs_to :merge_request delegate :target_branch, :source_branch, to: :merge_request, prefix: nil state_machine :state, initial: :empty do state :collected + state :overflow + # Deprecated states: these are no longer used but these values may still occur + # in the database. state :timeout state :overflow_commits_safe_size state :overflow_diff_files_limit @@ -43,19 +44,23 @@ class MergeRequestDiff < ActiveRecord::Base reload_diffs end - def diffs - @diffs ||= (load_diffs(st_diffs) || []) + def size + real_size.presence || diffs.size end - def diffs_no_whitespace - compare_result = Gitlab::CompareResult.new( - Gitlab::Git::Compare.new( - self.repository.raw_repository, - self.target_branch, - self.source_sha, - ), { ignore_whitespace_change: true } - ) - @diffs_no_whitespace ||= load_diffs(dump_commits(compare_result.diffs)) + def diffs(options={}) + if options[:ignore_whitespace_change] + @diffs_no_whitespace ||= begin + compare = Gitlab::Git::Compare.new( + self.repository.raw_repository, + self.target_branch, + self.source_sha, + ) + compare.diffs(options) + end + else + @diffs ||= load_diffs(st_diffs, options) + end end def commits @@ -94,16 +99,18 @@ class MergeRequestDiff < ActiveRecord::Base end end - def load_diffs(raw) - if raw.respond_to?(:map) - raw.map { |hash| Gitlab::Git::Diff.new(hash) } + def load_diffs(raw, options) + if raw.respond_to?(:each) + Gitlab::Git::DiffCollection.new(raw, options) + else + Gitlab::Git::DiffCollection.new([]) end end # Collect array of Git::Commit objects # between target and source branches def unmerged_commits - commits = compare_result.commits + commits = compare.commits if commits.present? commits = Commit.decorate(commits, merge_request.source_project). @@ -133,27 +140,21 @@ class MergeRequestDiff < ActiveRecord::Base if commits.size.zero? self.state = :empty - elsif commits.size > COMMITS_SAFE_SIZE - self.state = :overflow_commits_safe_size else - new_diffs = unmerged_diffs - end + diff_collection = unmerged_diffs - if new_diffs.any? - if new_diffs.size > Commit::DIFF_HARD_LIMIT_FILES - self.state = :overflow_diff_files_limit - new_diffs = new_diffs.first(Commit::DIFF_HARD_LIMIT_LINES) + if diff_collection.overflow? + # Set our state to 'overflow' to make the #empty? and #collected? + # methods (generated by StateMachine) return false. + self.state = :overflow end - if new_diffs.sum { |diff| diff.diff.lines.count } > Commit::DIFF_HARD_LIMIT_LINES - self.state = :overflow_diff_lines_limit - new_diffs = new_diffs.first(Commit::DIFF_HARD_LIMIT_LINES) - end - end + self.real_size = diff_collection.real_size - if new_diffs.present? - new_diffs = dump_commits(new_diffs) - self.state = :collected + if diff_collection.any? + new_diffs = dump_diffs(diff_collection) + self.state = :collected + end end self.st_diffs = new_diffs @@ -166,10 +167,7 @@ class MergeRequestDiff < ActiveRecord::Base # Collect array of Git::Diff objects # between target and source branches def unmerged_diffs - compare_result.diffs || [] - rescue Gitlab::Git::Diff::TimeoutError - self.state = :timeout - [] + compare.diffs(Commit.max_diff_options) end def repository @@ -181,18 +179,16 @@ class MergeRequestDiff < ActiveRecord::Base source_commit.try(:sha) end - def compare_result - @compare_result ||= + def compare + @compare ||= begin # Update ref for merge request merge_request.fetch_ref - Gitlab::CompareResult.new( - Gitlab::Git::Compare.new( - self.repository.raw_repository, - self.target_branch, - self.source_sha - ) + Gitlab::Git::Compare.new( + self.repository.raw_repository, + self.target_branch, + self.source_sha ) end end diff --git a/app/models/milestone.rb b/app/models/milestone.rb index 8f99e3bef9b..e3969f32dd6 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -24,12 +24,13 @@ class Milestone < ActiveRecord::Base include Sortable include Referable include StripAttribute + include Milestoneish belongs_to :project has_many :issues has_many :labels, -> { distinct.reorder('labels.title') }, through: :issues has_many :merge_requests - has_many :participants, through: :issues, source: :assignee + has_many :participants, -> { distinct.reorder('users.name') }, through: :issues, source: :assignee scope :active, -> { with_state(:active) } scope :closed, -> { with_state(:closed) } @@ -92,37 +93,6 @@ class Milestone < ActiveRecord::Base end end - def open_items_count - self.issues.opened.count + self.merge_requests.opened.count - end - - def closed_items_count - self.issues.closed.count + self.merge_requests.closed_and_merged.count - end - - def total_items_count - self.issues.count + self.merge_requests.count - end - - def percent_complete - ((closed_items_count * 100) / total_items_count).abs - rescue ZeroDivisionError - 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? diff --git a/app/models/note.rb b/app/models/note.rb index d287e0f3c6d..3b20d5d22b6 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -39,6 +39,7 @@ class Note < ActiveRecord::Base has_many :todos, dependent: :destroy + delegate :gfm_reference, :local_reference, to: :noteable delegate :name, to: :project, prefix: true delegate :name, :email, to: :author, prefix: true @@ -87,7 +88,7 @@ class Note < ActiveRecord::Base next if discussion_ids.include?(note.discussion_id) # don't group notes for the main target - if !note.for_diff_line? && note.noteable_type == "MergeRequest" + if !note.for_diff_line? && note.for_merge_request? discussions << [note] else discussions << notes.select do |other_note| @@ -131,9 +132,11 @@ class Note < ActiveRecord::Base end def find_diff - return nil unless noteable && noteable.diffs.present? + return nil unless noteable + return @diff if defined?(@diff) - @diff ||= noteable.diffs.find do |d| + # Don't use ||= because nil is a valid value for @diff + @diff = noteable.diffs(Commit.max_diff_options).find do |d| Digest::SHA1.hexdigest(d.new_path) == diff_file_index if d.new_path end end @@ -165,20 +168,16 @@ class Note < ActiveRecord::Base def active? return true unless self.diff return false unless noteable + return @active if defined?(@active) - noteable.diffs.each do |mr_diff| - next unless mr_diff.new_path == self.diff.new_path + diffs = noteable.diffs(Commit.max_diff_options) + notable_diff = diffs.find { |d| d.new_path == self.diff.new_path } - lines = Gitlab::Diff::Parser.new.parse(mr_diff.diff.lines.to_a) + return @active = false if notable_diff.nil? - lines.each do |line| - if line.text == diff_line - return true - end - end - end - - false + parsed_lines = Gitlab::Diff::Parser.new.parse(notable_diff.diff.each_line) + # We cannot use ||= because @active may be false + @active = parsed_lines.any? { |line_obj| line_obj.text == diff_line } end def outdated? @@ -263,7 +262,7 @@ class Note < ActiveRecord::Base end def diff_lines - @diff_lines ||= Gitlab::Diff::Parser.new.parse(diff.diff.lines) + @diff_lines ||= Gitlab::Diff::Parser.new.parse(diff.diff.each_line) end def highlighted_diff_lines @@ -315,20 +314,6 @@ class Note < ActiveRecord::Base nil end - # Mentionable override. - def gfm_reference(from_project = nil) - noteable.gfm_reference(from_project) - end - - # Mentionable override. - def local_reference - noteable - end - - def noteable_type_name - noteable_type.downcase if noteable_type.present? - end - # FIXME: Hack for polymorphic associations with STI # For more information visit http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Polymorphic+Associations def noteable_type=(noteable_type) @@ -348,10 +333,6 @@ class Note < ActiveRecord::Base Event.reset_event_cache_for(self) end - def system? - read_attribute(:system) - end - def downvote? is_award && note == "thumbsdown" end @@ -385,7 +366,7 @@ class Note < ActiveRecord::Base private def awards_supported? - (noteable.kind_of?(Issue) || noteable.is_a?(MergeRequest)) && !for_diff_line? + (for_issue? || for_merge_request?) && !for_diff_line? end def contains_emoji_only? diff --git a/app/models/project.rb b/app/models/project.rb index 6c9377448e2..c0f2ab91fa4 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -215,6 +215,7 @@ class Project < ActiveRecord::Base scope :public_only, -> { where(visibility_level: Project::PUBLIC) } scope :public_and_internal_only, -> { where(visibility_level: Project.public_and_internal_levels) } scope :non_archived, -> { where(archived: false) } + scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids).distinct } state_machine :import_status, initial: :none do event :import_start do @@ -278,7 +279,7 @@ class Project < ActiveRecord::Base end def search_by_title(query) - where('projects.archived = ?', false).where('LOWER(projects.name) LIKE :query', query: "%#{query.downcase}%") + non_archived.where('LOWER(projects.name) LIKE :query', query: "%#{query.downcase}%") end def find_with_namespace(id) @@ -711,6 +712,8 @@ class Project < ActiveRecord::Base old_path_with_namespace = File.join(namespace_dir, path_was) new_path_with_namespace = File.join(namespace_dir, path) + expire_caches_before_rename(old_path_with_namespace) + if gitlab_shell.mv_repository(old_path_with_namespace, new_path_with_namespace) # If repository moved successfully we need to send update instructions to users. # However we cannot allow rollback since we moved repository @@ -739,6 +742,22 @@ class Project < ActiveRecord::Base Gitlab::UploadsTransfer.new.rename_project(path_was, path, namespace.path) end + # Expires various caches before a project is renamed. + def expire_caches_before_rename(old_path) + repo = Repository.new(old_path, self) + wiki = Repository.new("#{old_path}.wiki", self) + + if repo.exists? + repo.expire_cache + repo.expire_emptiness_caches + end + + if wiki.exists? + wiki.expire_cache + wiki.expire_emptiness_caches + end + end + def hook_attrs { name: name, diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index f6571fc063e..aba37921c09 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -108,7 +108,8 @@ class JiraService < IssueTrackerService }, entity: { name: noteable_name.humanize.downcase, - url: entity_url + url: entity_url, + title: noteable.title } } @@ -196,10 +197,11 @@ class JiraService < IssueTrackerService user_url = data[:user][:url] entity_name = data[:entity][:name] entity_url = data[:entity][:url] + entity_title = data[:entity][:title] project_name = data[:project][:name] message = { - body: "[#{user_name}|#{user_url}] mentioned this issue in [a #{entity_name} of #{project_name}|#{entity_url}]." + body: %Q{[#{user_name}|#{user_url}] mentioned this issue in [a #{entity_name} of #{project_name}|#{entity_url}]:\n'#{entity_title}'} } unless existing_comment?(issue_name, message[:body]) diff --git a/app/models/repository.rb b/app/models/repository.rb index a214a69d749..ff48f993d42 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -654,30 +654,38 @@ 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? + def revert(user, commit, base_branch, revert_tree_id = nil) + source_sha = find_branch(base_branch).target + revert_tree_id ||= check_revert_content(commit, base_branch) - 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) + return false unless revert_tree_id - commit_with_hooks(user, target_branch) do |ref| + commit_with_hooks(user, base_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, + tree: revert_tree_id, parents: [rugged.lookup(source_sha)], update_ref: ref) end end + def check_revert_content(commit, base_branch) + source_sha = find_branch(base_branch).target + 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) + + tree_id + end + def diff_exists?(sha1, sha2) rugged.diff(sha1, sha2).size > 0 end @@ -804,6 +812,12 @@ class Repository raw_repository.ls_files(actual_ref) end + def main_language + unless empty? + Linguist::Repository.new(rugged, rugged.head.target_id).language + end + end + private def cache diff --git a/app/models/user.rb b/app/models/user.rb index 6baf2468ade..3098d49d58a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -169,7 +169,7 @@ class User < ActiveRecord::Base validates :avatar_crop_x, :avatar_crop_y, :avatar_crop_size, numericality: { only_integer: true }, presence: true, - if: ->(user) { user.avatar? } + if: ->(user) { user.avatar? && user.avatar_changed? } before_validation :generate_password, on: :create before_validation :restricted_signup_domains, on: :create @@ -362,17 +362,19 @@ class User < ActiveRecord::Base def disable_two_factor! update_attributes( - two_factor_enabled: false, - encrypted_otp_secret: nil, - encrypted_otp_secret_iv: nil, - encrypted_otp_secret_salt: nil, - otp_backup_codes: nil + two_factor_enabled: false, + encrypted_otp_secret: nil, + encrypted_otp_secret_iv: nil, + encrypted_otp_secret_salt: nil, + otp_grace_period_started_at: nil, + otp_backup_codes: nil ) end def namespace_uniq # Return early if username already failed the first uniqueness validation - return if self.errors[:username].include?('has already been taken') + return if self.errors.key?(:username) && + self.errors[:username].include?('has already been taken') namespace_name = self.username existing_namespace = Namespace.by_path(namespace_name) |