27fdd3fe50
`Gitlab::Git::Compare` will already have the correct order; sorting in Ruby can only ruin that. (The correct order being the same as `git log` - reverse chronological while maintaining the commit graph.) As an example, imagine a branch where someone has their system clock set wrong for some of the commits. Not only will those commits be in the wrong order - which is maybe reasonable - but sorting in Ruby can also put commits with the same timestamp out of order, as Ruby's sorting isn't stable.
192 lines
4.1 KiB
Ruby
192 lines
4.1 KiB
Ruby
class MergeRequestDiff < ActiveRecord::Base
|
|
include Sortable
|
|
|
|
# Prevent store of diff if commits amount more then 500
|
|
COMMITS_SAFE_SIZE = 100
|
|
|
|
belongs_to :merge_request
|
|
|
|
delegate :head_source_sha, :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
|
|
state :overflow_diff_lines_limit
|
|
end
|
|
|
|
serialize :st_commits
|
|
serialize :st_diffs
|
|
|
|
after_create :reload_content
|
|
|
|
def reload_content
|
|
reload_commits
|
|
reload_diffs
|
|
end
|
|
|
|
def size
|
|
real_size.presence || diffs.size
|
|
end
|
|
|
|
def diffs(options={})
|
|
if options[:ignore_whitespace_change]
|
|
@diffs_no_whitespace ||= begin
|
|
compare = Gitlab::Git::Compare.new(
|
|
self.repository.raw_repository,
|
|
self.base,
|
|
self.head,
|
|
)
|
|
compare.diffs(options)
|
|
end
|
|
else
|
|
@diffs ||= load_diffs(st_diffs, options)
|
|
end
|
|
end
|
|
|
|
def commits
|
|
@commits ||= load_commits(st_commits || [])
|
|
end
|
|
|
|
def last_commit
|
|
commits.first
|
|
end
|
|
|
|
def first_commit
|
|
commits.last
|
|
end
|
|
|
|
def base_commit
|
|
return nil unless self.base_commit_sha
|
|
|
|
merge_request.target_project.commit(self.base_commit_sha)
|
|
end
|
|
|
|
def last_commit_short_sha
|
|
@last_commit_short_sha ||= last_commit.short_id
|
|
end
|
|
|
|
def dump_commits(commits)
|
|
commits.map(&:to_hash)
|
|
end
|
|
|
|
def load_commits(array)
|
|
array.map { |hash| Commit.new(Gitlab::Git::Commit.new(hash), merge_request.source_project) }
|
|
end
|
|
|
|
def dump_diffs(diffs)
|
|
if diffs.respond_to?(:map)
|
|
diffs.map(&:to_hash)
|
|
end
|
|
end
|
|
|
|
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.commits
|
|
|
|
if commits.present?
|
|
commits = Commit.decorate(commits, merge_request.source_project).reverse
|
|
end
|
|
|
|
commits
|
|
end
|
|
|
|
# Reload all commits related to current merge request from repo
|
|
# and save it as array of hashes in st_commits db field
|
|
def reload_commits
|
|
commit_objects = unmerged_commits
|
|
|
|
if commit_objects.present?
|
|
self.st_commits = dump_commits(commit_objects)
|
|
end
|
|
|
|
save
|
|
end
|
|
|
|
# Reload diffs between branches related to current merge request from repo
|
|
# and save it as array of hashes in st_diffs db field
|
|
def reload_diffs
|
|
new_diffs = []
|
|
|
|
if commits.size.zero?
|
|
self.state = :empty
|
|
else
|
|
diff_collection = unmerged_diffs
|
|
|
|
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
|
|
|
|
self.real_size = diff_collection.real_size
|
|
|
|
if diff_collection.any?
|
|
new_diffs = dump_diffs(diff_collection)
|
|
self.state = :collected
|
|
end
|
|
end
|
|
|
|
self.st_diffs = new_diffs
|
|
|
|
self.base_commit_sha = self.repository.merge_base(self.head, self.base)
|
|
|
|
self.save
|
|
end
|
|
|
|
# Collect array of Git::Diff objects
|
|
# between target and source branches
|
|
def unmerged_diffs
|
|
compare.diffs(Commit.max_diff_options)
|
|
end
|
|
|
|
def repository
|
|
merge_request.target_project.repository
|
|
end
|
|
|
|
def source_sha
|
|
return head_source_sha if head_source_sha.present?
|
|
|
|
source_commit = merge_request.source_project.commit(source_branch)
|
|
source_commit.try(:sha)
|
|
end
|
|
|
|
def target_sha
|
|
merge_request.target_sha
|
|
end
|
|
|
|
def base
|
|
self.target_sha || self.target_branch
|
|
end
|
|
|
|
def head
|
|
self.source_sha
|
|
end
|
|
|
|
def compare
|
|
@compare ||=
|
|
begin
|
|
# Update ref for merge request
|
|
merge_request.fetch_ref
|
|
|
|
Gitlab::Git::Compare.new(
|
|
self.repository.raw_repository,
|
|
self.base,
|
|
self.head
|
|
)
|
|
end
|
|
end
|
|
end
|