##// END OF EJS Templates
scm: cvs: use scm_cmd() in annotate of adapter....
Toshi MARUYAMA -
r5292:e9a33eff147c
parent child
Show More
@@ -1,450 +1,453
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require 'redmine/scm/adapters/abstract_adapter'
18 require 'redmine/scm/adapters/abstract_adapter'
19
19
20 module Redmine
20 module Redmine
21 module Scm
21 module Scm
22 module Adapters
22 module Adapters
23 class CvsAdapter < AbstractAdapter
23 class CvsAdapter < AbstractAdapter
24
24
25 # CVS executable name
25 # CVS executable name
26 CVS_BIN = Redmine::Configuration['scm_cvs_command'] || "cvs"
26 CVS_BIN = Redmine::Configuration['scm_cvs_command'] || "cvs"
27
27
28 # raised if scm command exited with error, e.g. unknown revision.
28 # raised if scm command exited with error, e.g. unknown revision.
29 class ScmCommandAborted < CommandFailed; end
29 class ScmCommandAborted < CommandFailed; end
30
30
31 class << self
31 class << self
32 def client_command
32 def client_command
33 @@bin ||= CVS_BIN
33 @@bin ||= CVS_BIN
34 end
34 end
35
35
36 def sq_bin
36 def sq_bin
37 @@sq_bin ||= shell_quote(CVS_BIN)
37 @@sq_bin ||= shell_quote(CVS_BIN)
38 end
38 end
39
39
40 def client_version
40 def client_version
41 @@client_version ||= (scm_command_version || [])
41 @@client_version ||= (scm_command_version || [])
42 end
42 end
43
43
44 def client_available
44 def client_available
45 client_version_above?([1, 12])
45 client_version_above?([1, 12])
46 end
46 end
47
47
48 def scm_command_version
48 def scm_command_version
49 scm_version = scm_version_from_command_line.dup
49 scm_version = scm_version_from_command_line.dup
50 if scm_version.respond_to?(:force_encoding)
50 if scm_version.respond_to?(:force_encoding)
51 scm_version.force_encoding('ASCII-8BIT')
51 scm_version.force_encoding('ASCII-8BIT')
52 end
52 end
53 if m = scm_version.match(%r{\A(.*?)((\d+\.)+\d+)}m)
53 if m = scm_version.match(%r{\A(.*?)((\d+\.)+\d+)}m)
54 m[2].scan(%r{\d+}).collect(&:to_i)
54 m[2].scan(%r{\d+}).collect(&:to_i)
55 end
55 end
56 end
56 end
57
57
58 def scm_version_from_command_line
58 def scm_version_from_command_line
59 shellout("#{sq_bin} --version") { |io| io.read }.to_s
59 shellout("#{sq_bin} --version") { |io| io.read }.to_s
60 end
60 end
61 end
61 end
62
62
63 # Guidelines for the input:
63 # Guidelines for the input:
64 # url -> the project-path, relative to the cvsroot (eg. module name)
64 # url -> the project-path, relative to the cvsroot (eg. module name)
65 # root_url -> the good old, sometimes damned, CVSROOT
65 # root_url -> the good old, sometimes damned, CVSROOT
66 # login -> unnecessary
66 # login -> unnecessary
67 # password -> unnecessary too
67 # password -> unnecessary too
68 def initialize(url, root_url=nil, login=nil, password=nil,
68 def initialize(url, root_url=nil, login=nil, password=nil,
69 path_encoding=nil)
69 path_encoding=nil)
70 @url = url
70 @url = url
71 @login = login if login && !login.empty?
71 @login = login if login && !login.empty?
72 @password = (password || "") if @login
72 @password = (password || "") if @login
73 # TODO: better Exception here (IllegalArgumentException)
73 # TODO: better Exception here (IllegalArgumentException)
74 raise CommandFailed if root_url.blank?
74 raise CommandFailed if root_url.blank?
75 @root_url = root_url
75 @root_url = root_url
76 end
76 end
77
77
78 def root_url
78 def root_url
79 @root_url
79 @root_url
80 end
80 end
81
81
82 def url
82 def url
83 @url
83 @url
84 end
84 end
85
85
86 def info
86 def info
87 logger.debug "<cvs> info"
87 logger.debug "<cvs> info"
88 Info.new({:root_url => @root_url, :lastrev => nil})
88 Info.new({:root_url => @root_url, :lastrev => nil})
89 end
89 end
90
90
91 def get_previous_revision(revision)
91 def get_previous_revision(revision)
92 CvsRevisionHelper.new(revision).prevRev
92 CvsRevisionHelper.new(revision).prevRev
93 end
93 end
94
94
95 # Returns an Entries collection
95 # Returns an Entries collection
96 # or nil if the given path doesn't exist in the repository
96 # or nil if the given path doesn't exist in the repository
97 # this method is used by the repository-browser (aka LIST)
97 # this method is used by the repository-browser (aka LIST)
98 def entries(path=nil, identifier=nil)
98 def entries(path=nil, identifier=nil)
99 logger.debug "<cvs> entries '#{path}' with identifier '#{identifier}'"
99 logger.debug "<cvs> entries '#{path}' with identifier '#{identifier}'"
100 path_with_project="#{url}#{with_leading_slash(path)}"
100 path_with_project="#{url}#{with_leading_slash(path)}"
101 entries = Entries.new
101 entries = Entries.new
102 cmd_args = %w|-q rls -e|
102 cmd_args = %w|-q rls -e|
103 cmd_args << "-D" << time_to_cvstime_rlog(identifier) if identifier
103 cmd_args << "-D" << time_to_cvstime_rlog(identifier) if identifier
104 cmd_args << path_with_project
104 cmd_args << path_with_project
105 scm_cmd(*cmd_args) do |io|
105 scm_cmd(*cmd_args) do |io|
106 io.each_line() do |line|
106 io.each_line() do |line|
107 fields = line.chop.split('/',-1)
107 fields = line.chop.split('/',-1)
108 logger.debug(">>InspectLine #{fields.inspect}")
108 logger.debug(">>InspectLine #{fields.inspect}")
109 if fields[0]!="D"
109 if fields[0]!="D"
110 time = nil
110 time = nil
111 # Thu Dec 13 16:27:22 2007
111 # Thu Dec 13 16:27:22 2007
112 time_l = fields[-3].split(' ')
112 time_l = fields[-3].split(' ')
113 if time_l.size == 5 && time_l[4].length == 4
113 if time_l.size == 5 && time_l[4].length == 4
114 begin
114 begin
115 time = Time.parse(
115 time = Time.parse(
116 "#{time_l[1]} #{time_l[2]} #{time_l[3]} GMT #{time_l[4]}")
116 "#{time_l[1]} #{time_l[2]} #{time_l[3]} GMT #{time_l[4]}")
117 rescue
117 rescue
118 end
118 end
119 end
119 end
120 entries << Entry.new(
120 entries << Entry.new(
121 {
121 {
122 :name => fields[-5],
122 :name => fields[-5],
123 #:path => fields[-4].include?(path)?fields[-4]:(path + "/"+ fields[-4]),
123 #:path => fields[-4].include?(path)?fields[-4]:(path + "/"+ fields[-4]),
124 :path => "#{path}/#{fields[-5]}",
124 :path => "#{path}/#{fields[-5]}",
125 :kind => 'file',
125 :kind => 'file',
126 :size => nil,
126 :size => nil,
127 :lastrev => Revision.new(
127 :lastrev => Revision.new(
128 {
128 {
129 :revision => fields[-4],
129 :revision => fields[-4],
130 :name => fields[-4],
130 :name => fields[-4],
131 :time => time,
131 :time => time,
132 :author => ''
132 :author => ''
133 })
133 })
134 })
134 })
135 else
135 else
136 entries << Entry.new(
136 entries << Entry.new(
137 {
137 {
138 :name => fields[1],
138 :name => fields[1],
139 :path => "#{path}/#{fields[1]}",
139 :path => "#{path}/#{fields[1]}",
140 :kind => 'dir',
140 :kind => 'dir',
141 :size => nil,
141 :size => nil,
142 :lastrev => nil
142 :lastrev => nil
143 })
143 })
144 end
144 end
145 end
145 end
146 end
146 end
147 entries.sort_by_name
147 entries.sort_by_name
148 rescue ScmCommandAborted
148 rescue ScmCommandAborted
149 nil
149 nil
150 end
150 end
151
151
152 STARTLOG="----------------------------"
152 STARTLOG="----------------------------"
153 ENDLOG ="============================================================================="
153 ENDLOG ="============================================================================="
154
154
155 # Returns all revisions found between identifier_from and identifier_to
155 # Returns all revisions found between identifier_from and identifier_to
156 # in the repository. both identifier have to be dates or nil.
156 # in the repository. both identifier have to be dates or nil.
157 # these method returns nothing but yield every result in block
157 # these method returns nothing but yield every result in block
158 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}, &block)
158 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}, &block)
159 logger.debug "<cvs> revisions path:" +
159 logger.debug "<cvs> revisions path:" +
160 "'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
160 "'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
161 path_with_project = "#{url}#{with_leading_slash(path)}"
161 path_with_project = "#{url}#{with_leading_slash(path)}"
162 cmd_args = %w|-q rlog|
162 cmd_args = %w|-q rlog|
163 cmd_args << "-d" << ">#{time_to_cvstime_rlog(identifier_from)}" if identifier_from
163 cmd_args << "-d" << ">#{time_to_cvstime_rlog(identifier_from)}" if identifier_from
164 cmd_args << path_with_project
164 cmd_args << path_with_project
165 scm_cmd(*cmd_args) do |io|
165 scm_cmd(*cmd_args) do |io|
166 state = "entry_start"
166 state = "entry_start"
167 commit_log = String.new
167 commit_log = String.new
168 revision = nil
168 revision = nil
169 date = nil
169 date = nil
170 author = nil
170 author = nil
171 entry_path = nil
171 entry_path = nil
172 entry_name = nil
172 entry_name = nil
173 file_state = nil
173 file_state = nil
174 branch_map = nil
174 branch_map = nil
175 io.each_line() do |line|
175 io.each_line() do |line|
176 if state != "revision" && /^#{ENDLOG}/ =~ line
176 if state != "revision" && /^#{ENDLOG}/ =~ line
177 commit_log = String.new
177 commit_log = String.new
178 revision = nil
178 revision = nil
179 state = "entry_start"
179 state = "entry_start"
180 end
180 end
181 if state=="entry_start"
181 if state=="entry_start"
182 branch_map=Hash.new
182 branch_map=Hash.new
183 if /^RCS file: #{Regexp.escape(root_url_path)}\/#{Regexp.escape(path_with_project)}(.+),v$/ =~ line
183 if /^RCS file: #{Regexp.escape(root_url_path)}\/#{Regexp.escape(path_with_project)}(.+),v$/ =~ line
184 entry_path = normalize_cvs_path($1)
184 entry_path = normalize_cvs_path($1)
185 entry_name = normalize_path(File.basename($1))
185 entry_name = normalize_path(File.basename($1))
186 logger.debug("Path #{entry_path} <=> Name #{entry_name}")
186 logger.debug("Path #{entry_path} <=> Name #{entry_name}")
187 elsif /^head: (.+)$/ =~ line
187 elsif /^head: (.+)$/ =~ line
188 entry_headRev = $1 #unless entry.nil?
188 entry_headRev = $1 #unless entry.nil?
189 elsif /^symbolic names:/ =~ line
189 elsif /^symbolic names:/ =~ line
190 state = "symbolic" #unless entry.nil?
190 state = "symbolic" #unless entry.nil?
191 elsif /^#{STARTLOG}/ =~ line
191 elsif /^#{STARTLOG}/ =~ line
192 commit_log = String.new
192 commit_log = String.new
193 state = "revision"
193 state = "revision"
194 end
194 end
195 next
195 next
196 elsif state=="symbolic"
196 elsif state=="symbolic"
197 if /^(.*):\s(.*)/ =~ (line.strip)
197 if /^(.*):\s(.*)/ =~ (line.strip)
198 branch_map[$1]=$2
198 branch_map[$1]=$2
199 else
199 else
200 state="tags"
200 state="tags"
201 next
201 next
202 end
202 end
203 elsif state=="tags"
203 elsif state=="tags"
204 if /^#{STARTLOG}/ =~ line
204 if /^#{STARTLOG}/ =~ line
205 commit_log = ""
205 commit_log = ""
206 state="revision"
206 state="revision"
207 elsif /^#{ENDLOG}/ =~ line
207 elsif /^#{ENDLOG}/ =~ line
208 state="head"
208 state="head"
209 end
209 end
210 next
210 next
211 elsif state=="revision"
211 elsif state=="revision"
212 if /^#{ENDLOG}/ =~ line || /^#{STARTLOG}/ =~ line
212 if /^#{ENDLOG}/ =~ line || /^#{STARTLOG}/ =~ line
213 if revision
213 if revision
214 revHelper = CvsRevisionHelper.new(revision)
214 revHelper = CvsRevisionHelper.new(revision)
215 revBranch = "HEAD"
215 revBranch = "HEAD"
216 branch_map.each() do |branch_name, branch_point|
216 branch_map.each() do |branch_name, branch_point|
217 if revHelper.is_in_branch_with_symbol(branch_point)
217 if revHelper.is_in_branch_with_symbol(branch_point)
218 revBranch=branch_name
218 revBranch=branch_name
219 end
219 end
220 end
220 end
221 logger.debug("********** YIELD Revision #{revision}::#{revBranch}")
221 logger.debug("********** YIELD Revision #{revision}::#{revBranch}")
222 yield Revision.new({
222 yield Revision.new({
223 :time => date,
223 :time => date,
224 :author => author,
224 :author => author,
225 :message => commit_log.chomp,
225 :message => commit_log.chomp,
226 :paths => [{
226 :paths => [{
227 :revision => revision,
227 :revision => revision,
228 :branch => revBranch,
228 :branch => revBranch,
229 :path => entry_path,
229 :path => entry_path,
230 :name => entry_name,
230 :name => entry_name,
231 :kind => 'file',
231 :kind => 'file',
232 :action => file_state
232 :action => file_state
233 }]
233 }]
234 })
234 })
235 end
235 end
236 commit_log = String.new
236 commit_log = String.new
237 revision = nil
237 revision = nil
238 if /^#{ENDLOG}/ =~ line
238 if /^#{ENDLOG}/ =~ line
239 state = "entry_start"
239 state = "entry_start"
240 end
240 end
241 next
241 next
242 end
242 end
243
243
244 if /^branches: (.+)$/ =~ line
244 if /^branches: (.+)$/ =~ line
245 # TODO: version.branch = $1
245 # TODO: version.branch = $1
246 elsif /^revision (\d+(?:\.\d+)+).*$/ =~ line
246 elsif /^revision (\d+(?:\.\d+)+).*$/ =~ line
247 revision = $1
247 revision = $1
248 elsif /^date:\s+(\d+.\d+.\d+\s+\d+:\d+:\d+)/ =~ line
248 elsif /^date:\s+(\d+.\d+.\d+\s+\d+:\d+:\d+)/ =~ line
249 date = Time.parse($1)
249 date = Time.parse($1)
250 author = /author: ([^;]+)/.match(line)[1]
250 author = /author: ([^;]+)/.match(line)[1]
251 file_state = /state: ([^;]+)/.match(line)[1]
251 file_state = /state: ([^;]+)/.match(line)[1]
252 # TODO:
252 # TODO:
253 # linechanges only available in CVS....
253 # linechanges only available in CVS....
254 # maybe a feature our SVN implementation.
254 # maybe a feature our SVN implementation.
255 # I'm sure, they are useful for stats or something else
255 # I'm sure, they are useful for stats or something else
256 # linechanges =/lines: \+(\d+) -(\d+)/.match(line)
256 # linechanges =/lines: \+(\d+) -(\d+)/.match(line)
257 # unless linechanges.nil?
257 # unless linechanges.nil?
258 # version.line_plus = linechanges[1]
258 # version.line_plus = linechanges[1]
259 # version.line_minus = linechanges[2]
259 # version.line_minus = linechanges[2]
260 # else
260 # else
261 # version.line_plus = 0
261 # version.line_plus = 0
262 # version.line_minus = 0
262 # version.line_minus = 0
263 # end
263 # end
264 else
264 else
265 commit_log << line unless line =~ /^\*\*\* empty log message \*\*\*/
265 commit_log << line unless line =~ /^\*\*\* empty log message \*\*\*/
266 end
266 end
267 end
267 end
268 end
268 end
269 end
269 end
270 rescue ScmCommandAborted
270 rescue ScmCommandAborted
271 Revisions.new
271 Revisions.new
272 end
272 end
273
273
274 def diff(path, identifier_from, identifier_to=nil)
274 def diff(path, identifier_from, identifier_to=nil)
275 logger.debug "<cvs> diff path:'#{path}'" +
275 logger.debug "<cvs> diff path:'#{path}'" +
276 ",identifier_from #{identifier_from}, identifier_to #{identifier_to}"
276 ",identifier_from #{identifier_from}, identifier_to #{identifier_to}"
277 path_with_project="#{url}#{with_leading_slash(path)}"
277 path_with_project="#{url}#{with_leading_slash(path)}"
278 cmd_args = %w|rdiff -u|
278 cmd_args = %w|rdiff -u|
279 cmd_args << "-r#{identifier_to}"
279 cmd_args << "-r#{identifier_to}"
280 cmd_args << "-r#{identifier_from}"
280 cmd_args << "-r#{identifier_from}"
281 cmd_args << path_with_project
281 cmd_args << path_with_project
282 diff = []
282 diff = []
283 scm_cmd(*cmd_args) do |io|
283 scm_cmd(*cmd_args) do |io|
284 io.each_line do |line|
284 io.each_line do |line|
285 diff << line
285 diff << line
286 end
286 end
287 end
287 end
288 diff
288 diff
289 rescue ScmCommandAborted
289 rescue ScmCommandAborted
290 nil
290 nil
291 end
291 end
292
292
293 def cat(path, identifier=nil)
293 def cat(path, identifier=nil)
294 identifier = (identifier) ? identifier : "HEAD"
294 identifier = (identifier) ? identifier : "HEAD"
295 logger.debug "<cvs> cat path:'#{path}',identifier #{identifier}"
295 logger.debug "<cvs> cat path:'#{path}',identifier #{identifier}"
296 path_with_project="#{url}#{with_leading_slash(path)}"
296 path_with_project="#{url}#{with_leading_slash(path)}"
297 cmd_args = %w|-q co|
297 cmd_args = %w|-q co|
298 cmd_args << "-D" << "#{time_to_cvstime(identifier)}" if identifier
298 cmd_args << "-D" << "#{time_to_cvstime(identifier)}" if identifier
299 cmd_args << "-p" << path_with_project
299 cmd_args << "-p" << path_with_project
300 cat = nil
300 cat = nil
301 scm_cmd(*cmd_args) do |io|
301 scm_cmd(*cmd_args) do |io|
302 io.binmode
302 io.binmode
303 cat = io.read
303 cat = io.read
304 end
304 end
305 cat
305 cat
306 rescue ScmCommandAborted
306 rescue ScmCommandAborted
307 nil
307 nil
308 end
308 end
309
309
310 def annotate(path, identifier=nil)
310 def annotate(path, identifier=nil)
311 identifier = (identifier) ? identifier.to_i : "HEAD"
311 identifier = (identifier) ? identifier : "HEAD"
312 logger.debug "<cvs> annotate path:'#{path}',identifier #{identifier}"
312 logger.debug "<cvs> annotate path:'#{path}',identifier #{identifier}"
313 path_with_project="#{url}#{with_leading_slash(path)}"
313 path_with_project="#{url}#{with_leading_slash(path)}"
314 cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rannotate -r#{identifier} #{shell_quote path_with_project}"
314 cmd_args = %w|rannotate|
315 cmd_args << "-D" << "#{time_to_cvstime(identifier)}" if identifier
316 cmd_args << path_with_project
315 blame = Annotate.new
317 blame = Annotate.new
316 shellout(cmd) do |io|
318 scm_cmd(*cmd_args) do |io|
317 io.each_line do |line|
319 io.each_line do |line|
318 next unless line =~ %r{^([\d\.]+)\s+\(([^\)]+)\s+[^\)]+\):\s(.*)$}
320 next unless line =~ %r{^([\d\.]+)\s+\(([^\)]+)\s+[^\)]+\):\s(.*)$}
319 blame.add_line(
321 blame.add_line(
320 $3.rstrip,
322 $3.rstrip,
321 Revision.new(
323 Revision.new(
322 :revision => $1,
324 :revision => $1,
323 :identifier => nil,
325 :identifier => nil,
324 :author => $2.strip
326 :author => $2.strip
325 ))
327 ))
326 end
328 end
327 end
329 end
328 return nil if $? && $?.exitstatus != 0
329 blame
330 blame
331 rescue ScmCommandAborted
332 Annotate.new
330 end
333 end
331
334
332 private
335 private
333
336
334 # Returns the root url without the connexion string
337 # Returns the root url without the connexion string
335 # :pserver:anonymous@foo.bar:/path => /path
338 # :pserver:anonymous@foo.bar:/path => /path
336 # :ext:cvsservername:/path => /path
339 # :ext:cvsservername:/path => /path
337 def root_url_path
340 def root_url_path
338 root_url.to_s.gsub(/^:.+:\d*/, '')
341 root_url.to_s.gsub(/^:.+:\d*/, '')
339 end
342 end
340
343
341 # convert a date/time into the CVS-format
344 # convert a date/time into the CVS-format
342 def time_to_cvstime(time)
345 def time_to_cvstime(time)
343 return nil if time.nil?
346 return nil if time.nil?
344 return Time.now if time == 'HEAD'
347 return Time.now if time == 'HEAD'
345
348
346 unless time.kind_of? Time
349 unless time.kind_of? Time
347 time = Time.parse(time)
350 time = Time.parse(time)
348 end
351 end
349 return time.strftime("%Y-%m-%d %H:%M:%S")
352 return time.strftime("%Y-%m-%d %H:%M:%S")
350 end
353 end
351
354
352 def time_to_cvstime_rlog(time)
355 def time_to_cvstime_rlog(time)
353 return nil if time.nil?
356 return nil if time.nil?
354 t1 = time.clone.localtime
357 t1 = time.clone.localtime
355 return t1.strftime("%Y-%m-%d %H:%M:%S")
358 return t1.strftime("%Y-%m-%d %H:%M:%S")
356 end
359 end
357
360
358 def normalize_cvs_path(path)
361 def normalize_cvs_path(path)
359 normalize_path(path.gsub(/Attic\//,''))
362 normalize_path(path.gsub(/Attic\//,''))
360 end
363 end
361
364
362 def normalize_path(path)
365 def normalize_path(path)
363 path.sub(/^(\/)*(.*)/,'\2').sub(/(.*)(,v)+/,'\1')
366 path.sub(/^(\/)*(.*)/,'\2').sub(/(.*)(,v)+/,'\1')
364 end
367 end
365
368
366 class Revision < Redmine::Scm::Adapters::Revision
369 class Revision < Redmine::Scm::Adapters::Revision
367 # Returns the readable identifier
370 # Returns the readable identifier
368 def format_identifier
371 def format_identifier
369 revision.to_s
372 revision.to_s
370 end
373 end
371 end
374 end
372
375
373 def scm_cmd(*args, &block)
376 def scm_cmd(*args, &block)
374 full_args = [CVS_BIN, '-d', root_url]
377 full_args = [CVS_BIN, '-d', root_url]
375 full_args += args
378 full_args += args
376 ret = shellout(full_args.map { |e| shell_quote e.to_s }.join(' '), &block)
379 ret = shellout(full_args.map { |e| shell_quote e.to_s }.join(' '), &block)
377 if $? && $?.exitstatus != 0
380 if $? && $?.exitstatus != 0
378 raise ScmCommandAborted, "cvs exited with non-zero status: #{$?.exitstatus}"
381 raise ScmCommandAborted, "cvs exited with non-zero status: #{$?.exitstatus}"
379 end
382 end
380 ret
383 ret
381 end
384 end
382 private :scm_cmd
385 private :scm_cmd
383 end
386 end
384
387
385 class CvsRevisionHelper
388 class CvsRevisionHelper
386 attr_accessor :complete_rev, :revision, :base, :branchid
389 attr_accessor :complete_rev, :revision, :base, :branchid
387
390
388 def initialize(complete_rev)
391 def initialize(complete_rev)
389 @complete_rev = complete_rev
392 @complete_rev = complete_rev
390 parseRevision()
393 parseRevision()
391 end
394 end
392
395
393 def branchPoint
396 def branchPoint
394 return @base
397 return @base
395 end
398 end
396
399
397 def branchVersion
400 def branchVersion
398 if isBranchRevision
401 if isBranchRevision
399 return @base+"."+@branchid
402 return @base+"."+@branchid
400 end
403 end
401 return @base
404 return @base
402 end
405 end
403
406
404 def isBranchRevision
407 def isBranchRevision
405 !@branchid.nil?
408 !@branchid.nil?
406 end
409 end
407
410
408 def prevRev
411 def prevRev
409 unless @revision == 0
412 unless @revision == 0
410 return buildRevision( @revision - 1 )
413 return buildRevision( @revision - 1 )
411 end
414 end
412 return buildRevision( @revision )
415 return buildRevision( @revision )
413 end
416 end
414
417
415 def is_in_branch_with_symbol(branch_symbol)
418 def is_in_branch_with_symbol(branch_symbol)
416 bpieces = branch_symbol.split(".")
419 bpieces = branch_symbol.split(".")
417 branch_start = "#{bpieces[0..-3].join(".")}.#{bpieces[-1]}"
420 branch_start = "#{bpieces[0..-3].join(".")}.#{bpieces[-1]}"
418 return ( branchVersion == branch_start )
421 return ( branchVersion == branch_start )
419 end
422 end
420
423
421 private
424 private
422 def buildRevision(rev)
425 def buildRevision(rev)
423 if rev == 0
426 if rev == 0
424 if @branchid.nil?
427 if @branchid.nil?
425 @base + ".0"
428 @base + ".0"
426 else
429 else
427 @base
430 @base
428 end
431 end
429 elsif @branchid.nil?
432 elsif @branchid.nil?
430 @base + "." + rev.to_s
433 @base + "." + rev.to_s
431 else
434 else
432 @base + "." + @branchid + "." + rev.to_s
435 @base + "." + @branchid + "." + rev.to_s
433 end
436 end
434 end
437 end
435
438
436 # Interpretiert die cvs revisionsnummern wie z.b. 1.14 oder 1.3.0.15
439 # Interpretiert die cvs revisionsnummern wie z.b. 1.14 oder 1.3.0.15
437 def parseRevision()
440 def parseRevision()
438 pieces = @complete_rev.split(".")
441 pieces = @complete_rev.split(".")
439 @revision = pieces.last.to_i
442 @revision = pieces.last.to_i
440 baseSize = 1
443 baseSize = 1
441 baseSize += (pieces.size / 2)
444 baseSize += (pieces.size / 2)
442 @base = pieces[0..-baseSize].join(".")
445 @base = pieces[0..-baseSize].join(".")
443 if baseSize > 2
446 if baseSize > 2
444 @branchid = pieces[-2]
447 @branchid = pieces[-2]
445 end
448 end
446 end
449 end
447 end
450 end
448 end
451 end
449 end
452 end
450 end
453 end
General Comments 0
You need to be logged in to leave comments. Login now