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 3 class ProjectsController < ProjectResourceController
4 4 skip_before_filter :project, only: [:new, :create]
... ... @@ -79,7 +79,9 @@ class ProjectsController &lt; ProjectResourceController
79 79 end
80 80  
81 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 85 end
84 86  
85 87 def destroy
... ...
features/steps/project/project_network_graph.rb
... ... @@ -11,8 +11,8 @@ class ProjectNetworkGraph &lt; Spinach::FeatureSteps
11 11 end
12 12  
13 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 17 project = Project.find_by_name("Shop")
18 18 visit graph_project_path(project)
... ...
features/steps/shared/paths.rb
... ... @@ -126,8 +126,8 @@ module SharedPaths
126 126 end
127 127  
128 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 132 visit graph_project_path(@project)
133 133 end
... ...
lib/gitlab/graph/' 0 → 100644
... ... @@ -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 @@
  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 @@
  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   -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