Commit d9be420ee603fef634107c576b41715de18b5385

Authored by Dmitriy Zaporozhets
1 parent de724a7a

MergeRequestDiff model responsoible for storing commits/diff info

Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
Showing 1 changed file with 160 additions and 0 deletions   Show diff stats
app/models/merge_request_diff.rb 0 → 100644
... ... @@ -0,0 +1,160 @@
  1 +require Rails.root.join("app/models/commit")
  2 +
  3 +class MergeRequestDiff < ActiveRecord::Base
  4 + # Prevent store of diff
  5 + # if commits amount more then 200
  6 + COMMITS_SAFE_SIZE = 200
  7 +
  8 + attr_reader :commits, :diffs
  9 +
  10 + belongs_to :merge_request
  11 +
  12 + attr_accessible :state, :st_commits, :st_diffs
  13 +
  14 + delegate :target_branch, :source_branch, to: :merge_request, prefix: nil
  15 +
  16 + state_machine :state, initial: :empty do
  17 + state :collected
  18 + state :timeout
  19 + state :overflow_commits_safe_size
  20 + state :overflow_diff_files_limit
  21 + state :overflow_diff_lines_limit
  22 + end
  23 +
  24 + serialize :st_commits
  25 + serialize :st_diffs
  26 +
  27 + after_create :reload_content
  28 +
  29 + def reload_content
  30 + reload_commits
  31 + reload_diffs
  32 + end
  33 +
  34 + def diffs
  35 + @diffs ||= (load_diffs(st_diffs) || [])
  36 + end
  37 +
  38 + def commits
  39 + @commits ||= load_commits(st_commits || [])
  40 + end
  41 +
  42 + def last_commit
  43 + commits.first
  44 + end
  45 +
  46 + def last_commit_short_sha
  47 + @last_commit_short_sha ||= last_commit.sha[0..10]
  48 + end
  49 +
  50 + private
  51 +
  52 + def dump_commits(commits)
  53 + commits.map(&:to_hash)
  54 + end
  55 +
  56 + def load_commits(array)
  57 + array.map { |hash| Commit.new(Gitlab::Git::Commit.new(hash)) }
  58 + end
  59 +
  60 + def dump_diffs(diffs)
  61 + if diffs.respond_to?(:map)
  62 + diffs.map(&:to_hash)
  63 + end
  64 + end
  65 +
  66 + def load_diffs(raw)
  67 + if raw.respond_to?(:map)
  68 + raw.map { |hash| Gitlab::Git::Diff.new(hash) }
  69 + end
  70 + end
  71 +
  72 + # When Git::Diff is not able to get diff
  73 + # because of git timeout it return this value
  74 + def broken_diffs
  75 + [Gitlab::Git::Diff::BROKEN_DIFF]
  76 + end
  77 +
  78 + # Collect array of Git::Commit objects
  79 + # between target and source branches
  80 + def unmerged_commits
  81 + commits = if merge_request.for_fork?
  82 + Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).commits_between
  83 + else
  84 + repository.commits_between(target_branch, source_branch)
  85 + end
  86 +
  87 + if commits.present?
  88 + commits = Commit.decorate(commits).
  89 + sort_by(&:created_at).
  90 + reverse
  91 + end
  92 +
  93 + commits
  94 + end
  95 +
  96 + # Reload all commits related to current merge request from repo
  97 + # and save it as array of hashes in st_commits db field
  98 + def reload_commits
  99 + commit_objects = unmerged_commits
  100 +
  101 + if commit_objects.present?
  102 + self.st_commits = dump_commits(commit_objects)
  103 + end
  104 +
  105 + save
  106 + end
  107 +
  108 + # Reload diffs between branches related to current merge request from repo
  109 + # and save it as array of hashes in st_diffs db field
  110 + def reload_diffs
  111 + new_diffs = []
  112 +
  113 + if commits.size.zero?
  114 + self.state = :empty
  115 + elsif commits.size > COMMITS_SAFE_SIZE
  116 + self.state = :overflow_commits_safe_size
  117 + else
  118 + new_diffs = unmerged_diffs
  119 + end
  120 +
  121 + if new_diffs.any?
  122 + if new_diffs.size > Commit::DIFF_HARD_LIMIT_FILES
  123 + self.state = :overflow_diff_files_limit
  124 + new_diffs = []
  125 + end
  126 +
  127 + if new_diffs.sum { |diff| diff.diff.lines.count } > Commit::DIFF_HARD_LIMIT_LINES
  128 + self.state = :overflow_diff_lines_limit
  129 + new_diffs = []
  130 + end
  131 + end
  132 +
  133 + new_diffs = dump_commits(new_diffs) if new_diffs.present?
  134 +
  135 + self.st_diffs = new_diffs
  136 + self.save
  137 + end
  138 +
  139 + # Collect array of Git::Diff objects
  140 + # between target and source branches
  141 + def unmerged_diffs
  142 + diffs = if merge_request.for_fork?
  143 + Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).diffs_between_satellite
  144 + else
  145 + Gitlab::Git::Diff.between(repository, source_branch, target_branch)
  146 + end
  147 +
  148 + if diffs == broken_diffs
  149 + self.state = :timeout
  150 + diffs = []
  151 + end
  152 +
  153 + diffs ||= []
  154 + diffs
  155 + end
  156 +
  157 + def repository
  158 + merge_request.target_project.repository
  159 + end
  160 +end
... ...