Commit f082c8ae2a12188be6bd1c1e3f1b5b6edca8ebb3

Authored by randx
1 parent f8e27b92

Decouple and refactor GraphCommit

app/controllers/projects_controller.rb
1 -require Rails.root.join('lib', 'gitlab', 'graph_commit') 1 +require Rails.root.join('lib', 'gitlab', 'graph', 'json_builder')
2 2
3 class ProjectsController < ProjectResourceController 3 class ProjectsController < ProjectResourceController
4 skip_before_filter :project, only: [:new, :create] 4 skip_before_filter :project, only: [:new, :create]
@@ -79,7 +79,9 @@ class ProjectsController &lt; ProjectResourceController @@ -79,7 +79,9 @@ class ProjectsController &lt; ProjectResourceController
79 end 79 end
80 80
81 def graph 81 def graph
82 - @days_json, @commits_json = Gitlab::GraphCommit.to_graph(project) 82 + graph = Gitlab::Graph::JsonBuilder.new(project)
  83 +
  84 + @days_json, @commits_json = graph.days_json, graph.commits_json
83 end 85 end
84 86
85 def destroy 87 def destroy
features/steps/project/project_network_graph.rb
@@ -11,8 +11,8 @@ class ProjectNetworkGraph &lt; Spinach::FeatureSteps @@ -11,8 +11,8 @@ class ProjectNetworkGraph &lt; Spinach::FeatureSteps
11 end 11 end
12 12
13 And 'I visit project "Shop" network page' do 13 And 'I visit project "Shop" network page' do
14 - # Stub GraphCommit max_size to speed up test (10 commits vs. 650)  
15 - Gitlab::GraphCommit.stub(max_count: 10) 14 + # Stub Graph::JsonBuilder max_size to speed up test (10 commits vs. 650)
  15 + Gitlab::Graph::JsonBuilder.stub(max_count: 10)
16 16
17 project = Project.find_by_name("Shop") 17 project = Project.find_by_name("Shop")
18 visit graph_project_path(project) 18 visit graph_project_path(project)
features/steps/shared/paths.rb
@@ -126,8 +126,8 @@ module SharedPaths @@ -126,8 +126,8 @@ module SharedPaths
126 end 126 end
127 127
128 Given "I visit my project's network page" do 128 Given "I visit my project's network page" do
129 - # Stub GraphCommit max_size to speed up test (10 commits vs. 650)  
130 - Gitlab::GraphCommit.stub(max_count: 10) 129 + # Stub Graph::JsonBuilder max_size to speed up test (10 commits vs. 650)
  130 + Gitlab::Graph::JsonBuilder.stub(max_count: 10)
131 131
132 visit graph_project_path(@project) 132 visit graph_project_path(@project)
133 end 133 end
lib/gitlab/graph/' 0 → 100644
@@ -0,0 +1,165 @@ @@ -0,0 +1,165 @@
  1 +require "grit"
  2 +
  3 +module Gitlab
  4 + module Graph
  5 + class JsonBuilder
  6 + attr_accessor :max_count, :days, :commits
  7 +
  8 + def initialize project
  9 + @project = project
  10 + @repo = project.repo
  11 + @commits = collect_commits(@repo).dup
  12 + @ref_cache = {}
  13 +
  14 + @commits.map! { |commit| Graph::Commit.new(Commit.new(commit))}
  15 + @commits.each { |commit| commit.add_refs(ref_cache, @repo) }
  16 +
  17 + days = Graph::Commit.index_commits(@commits)
  18 +
  19 + return @days_json, @commits_json
  20 + end
  21 +
  22 + def collect_commits
  23 + end
  24 +
  25 + def days_json
  26 + @days_json = @days.compact.map { |d| [d.day, d.strftime("%b")] }.to_json
  27 + end
  28 +
  29 + def commits_json
  30 + @commits_json = @commits.map(&:to_graph_hash).to_json
  31 + end
  32 +
  33 + # Get commits from repository
  34 + #
  35 + def collect_commits repo
  36 + Grit::Commit.find_all(repo, nil, {max_count: self.max_count})
  37 + end
  38 +
  39 + def max_count
  40 + @max_count ||= 650
  41 + end
  42 +
  43 + # Method is adding time and space on the
  44 + # list of commits. As well as returns date list
  45 + # corelated with time set on commits.
  46 + #
  47 + # @param [Array<Graph::Commit>] comits to index
  48 + #
  49 + # @return [Array<TimeDate>] list of commit dates corelated with time on commits
  50 + def index_commits(commits)
  51 + days, heads = [], []
  52 + map = {}
  53 +
  54 + commits.reverse.each_with_index do |c,i|
  55 + c.time = i
  56 + days[i] = c.committed_date
  57 + map[c.id] = c
  58 + heads += c.refs unless c.refs.nil?
  59 + end
  60 +
  61 + heads.select!{|h| h.is_a? Grit::Head or h.is_a? Grit::Remote}
  62 + # sort heads so the master is top and current branches are closer
  63 + heads.sort! do |a,b|
  64 + if a.name == "master"
  65 + -1
  66 + elsif b.name == "master"
  67 + 1
  68 + else
  69 + b.commit.committed_date <=> a.commit.committed_date
  70 + end
  71 + end
  72 +
  73 + @_reserved = {}
  74 + days.each_index do |i|
  75 + @_reserved[i] = []
  76 + end
  77 +
  78 + heads.each do |h|
  79 + if map.include? h.commit.id then
  80 + place_chain(map[h.commit.id], map)
  81 + end
  82 + end
  83 + days
  84 + end
  85 +
  86 + # Add space mark on commit and its parents
  87 + #
  88 + # @param [Graph::Commit] the commit object.
  89 + # @param [Hash<String,Graph::Commit>] map of commits
  90 + def place_chain(commit, map, parent_time = nil)
  91 + leaves = take_left_leaves(commit, map)
  92 + if leaves.empty? then
  93 + return
  94 + end
  95 + space = find_free_space(leaves.last.time..leaves.first.time)
  96 + leaves.each{|l| l.space = space}
  97 + # and mark it as reserved
  98 + min_time = leaves.last.time
  99 + parents = leaves.last.parents.collect
  100 + parents.each do |p|
  101 + if map.include? p.id then
  102 + parent = map[p.id]
  103 + if parent.time < min_time then
  104 + min_time = parent.time
  105 + end
  106 + end
  107 + end
  108 + if parent_time.nil? then
  109 + max_time = leaves.first.time
  110 + else
  111 + max_time = parent_time - 1
  112 + end
  113 + mark_reserved(min_time..max_time, space)
  114 + # Visit branching chains
  115 + leaves.each do |l|
  116 + parents = l.parents.collect
  117 + .select{|p| map.include? p.id and map[p.id].space == 0}
  118 + for p in parents
  119 + place_chain(map[p.id], map, l.time)
  120 + end
  121 + end
  122 + end
  123 +
  124 + def mark_reserved(time_range, space)
  125 + for day in time_range
  126 + @_reserved[day].push(space)
  127 + end
  128 + end
  129 +
  130 + def find_free_space(time_range)
  131 + reserved = []
  132 + for day in time_range
  133 + reserved += @_reserved[day]
  134 + end
  135 + space = 1
  136 + while reserved.include? space do
  137 + space += 1
  138 + end
  139 + space
  140 + end
  141 +
  142 + # Takes most left subtree branch of commits
  143 + # which don't have space mark yet.
  144 + #
  145 + # @param [Graph::Commit] the commit object.
  146 + # @param [Hash<String,Graph::Commit>] map of commits
  147 + #
  148 + # @return [Array<Graph::Commit>] list of branch commits
  149 + def take_left_leaves(commit, map)
  150 + leaves = []
  151 + leaves.push(commit) if commit.space == 0
  152 + while true
  153 + parent = commit.parents.collect
  154 + self.select{|p| map.include? p.id and map[p.id].space == 0}
  155 + if parent.count == 0 then
  156 + return leaves
  157 + else
  158 + commit = map[parent.first.id]
  159 + leaves.push(commit)
  160 + end
  161 + end
  162 + end
  163 + end
  164 + end
  165 +end
lib/gitlab/graph/commit.rb 0 → 100644
@@ -0,0 +1,48 @@ @@ -0,0 +1,48 @@
  1 +require "grit"
  2 +
  3 +module Gitlab
  4 + module Graph
  5 + class Commit
  6 + include ActionView::Helpers::TagHelper
  7 +
  8 + attr_accessor :time, :space, :refs
  9 +
  10 + def initialize(commit)
  11 + @_commit = commit
  12 + @time = -1
  13 + @space = 0
  14 + end
  15 +
  16 + def method_missing(m, *args, &block)
  17 + @_commit.send(m, *args, &block)
  18 + end
  19 +
  20 + def to_graph_hash
  21 + h = {}
  22 + h[:parents] = self.parents.collect do |p|
  23 + [p.id,0,0]
  24 + end
  25 + h[:author] = Gitlab::Encode.utf8(author.name)
  26 + h[:time] = time
  27 + h[:space] = space
  28 + h[:refs] = refs.collect{|r|r.name}.join(" ") unless refs.nil?
  29 + h[:id] = sha
  30 + h[:date] = date
  31 + h[:message] = escape_once(Gitlab::Encode.utf8(message))
  32 + h[:login] = author.email
  33 + h
  34 + end
  35 +
  36 + def add_refs(ref_cache, repo)
  37 + if ref_cache.empty?
  38 + repo.refs.each do |ref|
  39 + ref_cache[ref.commit.id] ||= []
  40 + ref_cache[ref.commit.id] << ref
  41 + end
  42 + end
  43 + @refs = ref_cache[@_commit.id] if ref_cache.include?(@_commit.id)
  44 + @refs ||= []
  45 + end
  46 + end
  47 + end
  48 +end
lib/gitlab/graph/json_builder.rb 0 → 100644
@@ -0,0 +1,172 @@ @@ -0,0 +1,172 @@
  1 +require "grit"
  2 +
  3 +module Gitlab
  4 + module Graph
  5 + class JsonBuilder
  6 + attr_accessor :days, :commits, :ref_cache, :repo
  7 +
  8 + def self.max_count
  9 + @max_count ||= 650
  10 + end
  11 +
  12 + def initialize project
  13 + @project = project
  14 + @repo = project.repo
  15 + @ref_cache = {}
  16 +
  17 + @commits = collect_commits
  18 + @days = index_commits
  19 + end
  20 +
  21 + def days_json
  22 + @days_json = @days.compact.map { |d| [d.day, d.strftime("%b")] }.to_json
  23 + end
  24 +
  25 + def commits_json
  26 + @commits_json = @commits.map(&:to_graph_hash).to_json
  27 + end
  28 +
  29 + protected
  30 +
  31 + # Get commits from repository
  32 + #
  33 + def collect_commits
  34 + @commits = Grit::Commit.find_all(repo, nil, {max_count: self.class.max_count}).dup
  35 +
  36 + # Decorate with app/models/commit.rb
  37 + @commits.map! { |commit| ::Commit.new(commit) }
  38 +
  39 + # Decorate with lib/gitlab/graph/commit.rb
  40 + @commits.map! { |commit| Gitlab::Graph::Commit.new(commit) }
  41 +
  42 + # add refs to each commit
  43 + @commits.each { |commit| commit.add_refs(ref_cache, repo) }
  44 +
  45 + @commits
  46 + end
  47 +
  48 + # Method is adding time and space on the
  49 + # list of commits. As well as returns date list
  50 + # corelated with time set on commits.
  51 + #
  52 + # @param [Array<Graph::Commit>] comits to index
  53 + #
  54 + # @return [Array<TimeDate>] list of commit dates corelated with time on commits
  55 + def index_commits
  56 + days, heads = [], []
  57 + map = {}
  58 +
  59 + commits.reverse.each_with_index do |c,i|
  60 + c.time = i
  61 + days[i] = c.committed_date
  62 + map[c.id] = c
  63 + heads += c.refs unless c.refs.nil?
  64 + end
  65 +
  66 + heads.select!{|h| h.is_a? Grit::Head or h.is_a? Grit::Remote}
  67 + # sort heads so the master is top and current branches are closer
  68 + heads.sort! do |a,b|
  69 + if a.name == "master"
  70 + -1
  71 + elsif b.name == "master"
  72 + 1
  73 + else
  74 + b.commit.committed_date <=> a.commit.committed_date
  75 + end
  76 + end
  77 +
  78 + @_reserved = {}
  79 + days.each_index do |i|
  80 + @_reserved[i] = []
  81 + end
  82 +
  83 + heads.each do |h|
  84 + if map.include? h.commit.id then
  85 + place_chain(map[h.commit.id], map)
  86 + end
  87 + end
  88 +
  89 + days
  90 + end
  91 +
  92 + # Add space mark on commit and its parents
  93 + #
  94 + # @param [Graph::Commit] the commit object.
  95 + # @param [Hash<String,Graph::Commit>] map of commits
  96 + def place_chain(commit, map, parent_time = nil)
  97 + leaves = take_left_leaves(commit, map)
  98 + if leaves.empty?
  99 + return
  100 + end
  101 + space = find_free_space(leaves.last.time..leaves.first.time)
  102 + leaves.each{|l| l.space = space}
  103 + # and mark it as reserved
  104 + min_time = leaves.last.time
  105 + parents = leaves.last.parents.collect
  106 + parents.each do |p|
  107 + if map.include? p.id
  108 + parent = map[p.id]
  109 + if parent.time < min_time
  110 + min_time = parent.time
  111 + end
  112 + end
  113 + end
  114 + if parent_time.nil?
  115 + max_time = leaves.first.time
  116 + else
  117 + max_time = parent_time - 1
  118 + end
  119 + mark_reserved(min_time..max_time, space)
  120 +
  121 + # Visit branching chains
  122 + leaves.each do |l|
  123 + parents = l.parents.collect.select{|p| map.include? p.id and map[p.id].space == 0}
  124 + for p in parents
  125 + place_chain(map[p.id], map, l.time)
  126 + end
  127 + end
  128 + end
  129 +
  130 + def mark_reserved(time_range, space)
  131 + for day in time_range
  132 + @_reserved[day].push(space)
  133 + end
  134 + end
  135 +
  136 + def find_free_space(time_range)
  137 + reserved = []
  138 + for day in time_range
  139 + reserved += @_reserved[day]
  140 + end
  141 + space = 1
  142 + while reserved.include? space do
  143 + space += 1
  144 + end
  145 + space
  146 + end
  147 +
  148 + # Takes most left subtree branch of commits
  149 + # which don't have space mark yet.
  150 + #
  151 + # @param [Graph::Commit] the commit object.
  152 + # @param [Hash<String,Graph::Commit>] map of commits
  153 + #
  154 + # @return [Array<Graph::Commit>] list of branch commits
  155 + def take_left_leaves(commit, map)
  156 + leaves = []
  157 + leaves.push(commit) if commit.space.zero?
  158 +
  159 + while true
  160 + parent = commit.parents.collect.select do |p|
  161 + map.include? p.id and map[p.id].space == 0
  162 + end
  163 +
  164 + return leaves if parent.count.zero?
  165 +
  166 + commit = map[parent.first.id]
  167 + leaves.push(commit)
  168 + end
  169 + end
  170 + end
  171 + end
  172 +end
lib/gitlab/graph_commit.rb
@@ -1,195 +0,0 @@ @@ -1,195 +0,0 @@
1 -require "grit"  
2 -  
3 -module Gitlab  
4 - class GraphCommit  
5 - attr_accessor :time, :space, :refs  
6 -  
7 - include ActionView::Helpers::TagHelper  
8 -  
9 - def self.to_graph(project)  
10 - @repo = project.repo  
11 -  
12 - commits = collect_commits(@repo).dup  
13 -  
14 - ref_cache = {}  
15 -  
16 - commits.map! { |commit| GraphCommit.new(Commit.new(commit))}  
17 - commits.each { |commit| commit.add_refs(ref_cache, @repo) }  
18 -  
19 - days = GraphCommit.index_commits(commits)  
20 - @days_json = days.compact.collect{|d| [d.day, d.strftime("%b")] }.to_json  
21 - @commits_json = commits.map(&:to_graph_hash).to_json  
22 -  
23 - return @days_json, @commits_json  
24 - end  
25 -  
26 - # Get commits from repository  
27 - #  
28 - def self.collect_commits repo  
29 - Grit::Commit.find_all(repo, nil, {max_count: self.max_count})  
30 - end  
31 -  
32 - def self.max_count  
33 - @max_count ||= 650  
34 - end  
35 -  
36 - # Method is adding time and space on the  
37 - # list of commits. As well as returns date list  
38 - # corelated with time set on commits.  
39 - #  
40 - # @param [Array<GraphCommit>] comits to index  
41 - #  
42 - # @return [Array<TimeDate>] list of commit dates corelated with time on commits  
43 - def self.index_commits(commits)  
44 - days, heads = [], []  
45 - map = {}  
46 -  
47 - commits.reverse.each_with_index do |c,i|  
48 - c.time = i  
49 - days[i] = c.committed_date  
50 - map[c.id] = c  
51 - heads += c.refs unless c.refs.nil?  
52 - end  
53 -  
54 - heads.select!{|h| h.is_a? Grit::Head or h.is_a? Grit::Remote}  
55 - # sort heads so the master is top and current branches are closer  
56 - heads.sort! do |a,b|  
57 - if a.name == "master"  
58 - -1  
59 - elsif b.name == "master"  
60 - 1  
61 - else  
62 - b.commit.committed_date <=> a.commit.committed_date  
63 - end  
64 - end  
65 -  
66 - @_reserved = {}  
67 - days.each_index do |i|  
68 - @_reserved[i] = []  
69 - end  
70 -  
71 - heads.each do |h|  
72 - if map.include? h.commit.id then  
73 - place_chain(map[h.commit.id], map)  
74 - end  
75 - end  
76 - days  
77 - end  
78 -  
79 - # Add space mark on commit and its parents  
80 - #  
81 - # @param [GraphCommit] the commit object.  
82 - # @param [Hash<String,GraphCommit>] map of commits  
83 - def self.place_chain(commit, map, parent_time = nil)  
84 - leaves = take_left_leaves(commit, map)  
85 - if leaves.empty? then  
86 - return  
87 - end  
88 - space = find_free_space(leaves.last.time..leaves.first.time)  
89 - leaves.each{|l| l.space = space}  
90 - # and mark it as reserved  
91 - min_time = leaves.last.time  
92 - parents = leaves.last.parents.collect  
93 - parents.each do |p|  
94 - if map.include? p.id then  
95 - parent = map[p.id]  
96 - if parent.time < min_time then  
97 - min_time = parent.time  
98 - end  
99 - end  
100 - end  
101 - if parent_time.nil? then  
102 - max_time = leaves.first.time  
103 - else  
104 - max_time = parent_time - 1  
105 - end  
106 - mark_reserved(min_time..max_time, space)  
107 - # Visit branching chains  
108 - leaves.each do |l|  
109 - parents = l.parents.collect  
110 - .select{|p| map.include? p.id and map[p.id].space == 0}  
111 - for p in parents  
112 - place_chain(map[p.id], map, l.time)  
113 - end  
114 - end  
115 - end  
116 -  
117 - def self.mark_reserved(time_range, space)  
118 - for day in time_range  
119 - @_reserved[day].push(space)  
120 - end  
121 - end  
122 -  
123 - def self.find_free_space(time_range)  
124 - reserved = []  
125 - for day in time_range  
126 - reserved += @_reserved[day]  
127 - end  
128 - space = 1  
129 - while reserved.include? space do  
130 - space += 1  
131 - end  
132 - space  
133 - end  
134 -  
135 - # Takes most left subtree branch of commits  
136 - # which don't have space mark yet.  
137 - #  
138 - # @param [GraphCommit] the commit object.  
139 - # @param [Hash<String,GraphCommit>] map of commits  
140 - #  
141 - # @return [Array<GraphCommit>] list of branch commits  
142 - def self.take_left_leaves(commit, map)  
143 - leaves = []  
144 - leaves.push(commit) if commit.space == 0  
145 - while true  
146 - parent = commit.parents.collect  
147 - .select{|p| map.include? p.id and map[p.id].space == 0}  
148 - if parent.count == 0 then  
149 - return leaves  
150 - else  
151 - commit = map[parent.first.id]  
152 - leaves.push(commit)  
153 - end  
154 - end  
155 - end  
156 -  
157 -  
158 - def initialize(commit)  
159 - @_commit = commit  
160 - @time = -1  
161 - @space = 0  
162 - end  
163 -  
164 - def method_missing(m, *args, &block)  
165 - @_commit.send(m, *args, &block)  
166 - end  
167 -  
168 - def to_graph_hash  
169 - h = {}  
170 - h[:parents] = self.parents.collect do |p|  
171 - [p.id,0,0]  
172 - end  
173 - h[:author] = Gitlab::Encode.utf8(author.name)  
174 - h[:time] = time  
175 - h[:space] = space  
176 - h[:refs] = refs.collect{|r|r.name}.join(" ") unless refs.nil?  
177 - h[:id] = sha  
178 - h[:date] = date  
179 - h[:message] = escape_once(Gitlab::Encode.utf8(message))  
180 - h[:login] = author.email  
181 - h  
182 - end  
183 -  
184 - def add_refs(ref_cache, repo)  
185 - if ref_cache.empty?  
186 - repo.refs.each do |ref|  
187 - ref_cache[ref.commit.id] ||= []  
188 - ref_cache[ref.commit.id] << ref  
189 - end  
190 - end  
191 - @refs = ref_cache[@_commit.id] if ref_cache.include?(@_commit.id)  
192 - @refs ||= []  
193 - end  
194 - end  
195 -end