Commit d9be420ee603fef634107c576b41715de18b5385
1 parent
de724a7a
Exists in
spb-stable
and in
3 other branches
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
... | ... | @@ -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 | ... | ... |