##// END OF EJS Templates
scm: cvs: code clean up lib/redmine/scm/adapters/cvs_adapter.rb....
Toshi MARUYAMA -
r5260:c6ec41473e62
parent child
Show More
@@ -1,439 +1,435
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|rls -e|
102 cmd_args = %w|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:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
159 logger.debug "<cvs> revisions path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
160
160
161 path_with_project="#{url}#{with_leading_slash(path)}"
161 path_with_project="#{url}#{with_leading_slash(path)}"
162 cmd_args = %w|rlog|
162 cmd_args = %w|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
167 commit_log = String.new
168 commit_log=String.new
168 revision = nil
169 revision=nil
169 date = nil
170 date=nil
170 author = nil
171 author=nil
171 entry_path = nil
172 entry_path=nil
172 entry_name = nil
173 entry_name=nil
173 file_state = nil
174 file_state=nil
174 branch_map = nil
175 branch_map=nil
176
177 io.each_line() do |line|
175 io.each_line() do |line|
178
176 if state != "revision" && /^#{ENDLOG}/ =~ line
179 if state!="revision" && /^#{ENDLOG}/ =~ line
177 commit_log = String.new
180 commit_log=String.new
178 revision = nil
181 revision=nil
179 state = "entry_start"
182 state="entry_start"
183 end
180 end
184
185 if state=="entry_start"
181 if state=="entry_start"
186 branch_map=Hash.new
182 branch_map=Hash.new
187 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
188 entry_path = normalize_cvs_path($1)
184 entry_path = normalize_cvs_path($1)
189 entry_name = normalize_path(File.basename($1))
185 entry_name = normalize_path(File.basename($1))
190 logger.debug("Path #{entry_path} <=> Name #{entry_name}")
186 logger.debug("Path #{entry_path} <=> Name #{entry_name}")
191 elsif /^head: (.+)$/ =~ line
187 elsif /^head: (.+)$/ =~ line
192 entry_headRev = $1 #unless entry.nil?
188 entry_headRev = $1 #unless entry.nil?
193 elsif /^symbolic names:/ =~ line
189 elsif /^symbolic names:/ =~ line
194 state="symbolic" #unless entry.nil?
190 state="symbolic" #unless entry.nil?
195 elsif /^#{STARTLOG}/ =~ line
191 elsif /^#{STARTLOG}/ =~ line
196 commit_log=String.new
192 commit_log=String.new
197 state="revision"
193 state="revision"
198 end
194 end
199 next
195 next
200 elsif state=="symbolic"
196 elsif state=="symbolic"
201 if /^(.*):\s(.*)/ =~ (line.strip)
197 if /^(.*):\s(.*)/ =~ (line.strip)
202 branch_map[$1]=$2
198 branch_map[$1]=$2
203 else
199 else
204 state="tags"
200 state="tags"
205 next
201 next
206 end
202 end
207 elsif state=="tags"
203 elsif state=="tags"
208 if /^#{STARTLOG}/ =~ line
204 if /^#{STARTLOG}/ =~ line
209 commit_log = ""
205 commit_log = ""
210 state="revision"
206 state="revision"
211 elsif /^#{ENDLOG}/ =~ line
207 elsif /^#{ENDLOG}/ =~ line
212 state="head"
208 state="head"
213 end
209 end
214 next
210 next
215 elsif state=="revision"
211 elsif state=="revision"
216 if /^#{ENDLOG}/ =~ line || /^#{STARTLOG}/ =~ line
212 if /^#{ENDLOG}/ =~ line || /^#{STARTLOG}/ =~ line
217 if revision
213 if revision
218
214
219 revHelper=CvsRevisionHelper.new(revision)
215 revHelper=CvsRevisionHelper.new(revision)
220 revBranch="HEAD"
216 revBranch="HEAD"
221
217
222 branch_map.each() do |branch_name,branch_point|
218 branch_map.each() do |branch_name,branch_point|
223 if revHelper.is_in_branch_with_symbol(branch_point)
219 if revHelper.is_in_branch_with_symbol(branch_point)
224 revBranch=branch_name
220 revBranch=branch_name
225 end
221 end
226 end
222 end
227
223
228 logger.debug("********** YIELD Revision #{revision}::#{revBranch}")
224 logger.debug("********** YIELD Revision #{revision}::#{revBranch}")
229
225
230 yield Revision.new({
226 yield Revision.new({
231 :time => date,
227 :time => date,
232 :author => author,
228 :author => author,
233 :message=>commit_log.chomp,
229 :message=>commit_log.chomp,
234 :paths => [{
230 :paths => [{
235 :revision => revision,
231 :revision => revision,
236 :branch=> revBranch,
232 :branch=> revBranch,
237 :path=>entry_path,
233 :path=>entry_path,
238 :name=>entry_name,
234 :name=>entry_name,
239 :kind=>'file',
235 :kind=>'file',
240 :action=>file_state
236 :action=>file_state
241 }]
237 }]
242 })
238 })
243 end
239 end
244
240
245 commit_log=String.new
241 commit_log=String.new
246 revision=nil
242 revision=nil
247
243
248 if /^#{ENDLOG}/ =~ line
244 if /^#{ENDLOG}/ =~ line
249 state="entry_start"
245 state="entry_start"
250 end
246 end
251 next
247 next
252 end
248 end
253
249
254 if /^branches: (.+)$/ =~ line
250 if /^branches: (.+)$/ =~ line
255 #TODO: version.branch = $1
251 #TODO: version.branch = $1
256 elsif /^revision (\d+(?:\.\d+)+).*$/ =~ line
252 elsif /^revision (\d+(?:\.\d+)+).*$/ =~ line
257 revision = $1
253 revision = $1
258 elsif /^date:\s+(\d+.\d+.\d+\s+\d+:\d+:\d+)/ =~ line
254 elsif /^date:\s+(\d+.\d+.\d+\s+\d+:\d+:\d+)/ =~ line
259 date = Time.parse($1)
255 date = Time.parse($1)
260 author = /author: ([^;]+)/.match(line)[1]
256 author = /author: ([^;]+)/.match(line)[1]
261 file_state = /state: ([^;]+)/.match(line)[1]
257 file_state = /state: ([^;]+)/.match(line)[1]
262 #TODO: linechanges only available in CVS.... maybe a feature our SVN implementation. i'm sure, they are
258 #TODO: linechanges only available in CVS.... maybe a feature our SVN implementation. i'm sure, they are
263 # useful for stats or something else
259 # useful for stats or something else
264 # linechanges =/lines: \+(\d+) -(\d+)/.match(line)
260 # linechanges =/lines: \+(\d+) -(\d+)/.match(line)
265 # unless linechanges.nil?
261 # unless linechanges.nil?
266 # version.line_plus = linechanges[1]
262 # version.line_plus = linechanges[1]
267 # version.line_minus = linechanges[2]
263 # version.line_minus = linechanges[2]
268 # else
264 # else
269 # version.line_plus = 0
265 # version.line_plus = 0
270 # version.line_minus = 0
266 # version.line_minus = 0
271 # end
267 # end
272 else
268 else
273 commit_log << line unless line =~ /^\*\*\* empty log message \*\*\*/
269 commit_log << line unless line =~ /^\*\*\* empty log message \*\*\*/
274 end
270 end
275 end
271 end
276 end
272 end
277 end
273 end
278 rescue ScmCommandAborted
274 rescue ScmCommandAborted
279 Revisions.new
275 Revisions.new
280 end
276 end
281
277
282 def diff(path, identifier_from, identifier_to=nil)
278 def diff(path, identifier_from, identifier_to=nil)
283 logger.debug "<cvs> diff path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
279 logger.debug "<cvs> diff path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
284 path_with_project="#{url}#{with_leading_slash(path)}"
280 path_with_project="#{url}#{with_leading_slash(path)}"
285 cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rdiff -u -r#{identifier_to} -r#{identifier_from} #{shell_quote path_with_project}"
281 cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rdiff -u -r#{identifier_to} -r#{identifier_from} #{shell_quote path_with_project}"
286 diff = []
282 diff = []
287 shellout(cmd) do |io|
283 shellout(cmd) do |io|
288 io.each_line do |line|
284 io.each_line do |line|
289 diff << line
285 diff << line
290 end
286 end
291 end
287 end
292 return nil if $? && $?.exitstatus != 0
288 return nil if $? && $?.exitstatus != 0
293 diff
289 diff
294 end
290 end
295
291
296 def cat(path, identifier=nil)
292 def cat(path, identifier=nil)
297 identifier = (identifier) ? identifier : "HEAD"
293 identifier = (identifier) ? identifier : "HEAD"
298 logger.debug "<cvs> cat path:'#{path}',identifier #{identifier}"
294 logger.debug "<cvs> cat path:'#{path}',identifier #{identifier}"
299 path_with_project="#{url}#{with_leading_slash(path)}"
295 path_with_project="#{url}#{with_leading_slash(path)}"
300 cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} co"
296 cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} co"
301 cmd << " -D \"#{time_to_cvstime(identifier)}\"" if identifier
297 cmd << " -D \"#{time_to_cvstime(identifier)}\"" if identifier
302 cmd << " -p #{shell_quote path_with_project}"
298 cmd << " -p #{shell_quote path_with_project}"
303 cat = nil
299 cat = nil
304 shellout(cmd) do |io|
300 shellout(cmd) do |io|
305 io.binmode
301 io.binmode
306 cat = io.read
302 cat = io.read
307 end
303 end
308 return nil if $? && $?.exitstatus != 0
304 return nil if $? && $?.exitstatus != 0
309 cat
305 cat
310 end
306 end
311
307
312 def annotate(path, identifier=nil)
308 def annotate(path, identifier=nil)
313 identifier = (identifier) ? identifier.to_i : "HEAD"
309 identifier = (identifier) ? identifier.to_i : "HEAD"
314 logger.debug "<cvs> annotate path:'#{path}',identifier #{identifier}"
310 logger.debug "<cvs> annotate path:'#{path}',identifier #{identifier}"
315 path_with_project="#{url}#{with_leading_slash(path)}"
311 path_with_project="#{url}#{with_leading_slash(path)}"
316 cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rannotate -r#{identifier} #{shell_quote path_with_project}"
312 cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rannotate -r#{identifier} #{shell_quote path_with_project}"
317 blame = Annotate.new
313 blame = Annotate.new
318 shellout(cmd) do |io|
314 shellout(cmd) do |io|
319 io.each_line do |line|
315 io.each_line do |line|
320 next unless line =~ %r{^([\d\.]+)\s+\(([^\)]+)\s+[^\)]+\):\s(.*)$}
316 next unless line =~ %r{^([\d\.]+)\s+\(([^\)]+)\s+[^\)]+\):\s(.*)$}
321 blame.add_line($3.rstrip, Revision.new(:revision => $1, :author => $2.strip))
317 blame.add_line($3.rstrip, Revision.new(:revision => $1, :author => $2.strip))
322 end
318 end
323 end
319 end
324 return nil if $? && $?.exitstatus != 0
320 return nil if $? && $?.exitstatus != 0
325 blame
321 blame
326 end
322 end
327
323
328 private
324 private
329
325
330 # Returns the root url without the connexion string
326 # Returns the root url without the connexion string
331 # :pserver:anonymous@foo.bar:/path => /path
327 # :pserver:anonymous@foo.bar:/path => /path
332 # :ext:cvsservername:/path => /path
328 # :ext:cvsservername:/path => /path
333 def root_url_path
329 def root_url_path
334 root_url.to_s.gsub(/^:.+:\d*/, '')
330 root_url.to_s.gsub(/^:.+:\d*/, '')
335 end
331 end
336
332
337 # convert a date/time into the CVS-format
333 # convert a date/time into the CVS-format
338 def time_to_cvstime(time)
334 def time_to_cvstime(time)
339 return nil if time.nil?
335 return nil if time.nil?
340 return Time.now if time == 'HEAD'
336 return Time.now if time == 'HEAD'
341
337
342 unless time.kind_of? Time
338 unless time.kind_of? Time
343 time = Time.parse(time)
339 time = Time.parse(time)
344 end
340 end
345 return time.strftime("%Y-%m-%d %H:%M:%S")
341 return time.strftime("%Y-%m-%d %H:%M:%S")
346 end
342 end
347
343
348 def time_to_cvstime_rlog(time)
344 def time_to_cvstime_rlog(time)
349 return nil if time.nil?
345 return nil if time.nil?
350 t1 = time.clone.localtime
346 t1 = time.clone.localtime
351 return t1.strftime("%Y-%m-%d %H:%M:%S")
347 return t1.strftime("%Y-%m-%d %H:%M:%S")
352 end
348 end
353
349
354 def normalize_cvs_path(path)
350 def normalize_cvs_path(path)
355 normalize_path(path.gsub(/Attic\//,''))
351 normalize_path(path.gsub(/Attic\//,''))
356 end
352 end
357
353
358 def normalize_path(path)
354 def normalize_path(path)
359 path.sub(/^(\/)*(.*)/,'\2').sub(/(.*)(,v)+/,'\1')
355 path.sub(/^(\/)*(.*)/,'\2').sub(/(.*)(,v)+/,'\1')
360 end
356 end
361
357
362 def scm_cmd(*args, &block)
358 def scm_cmd(*args, &block)
363 full_args = [CVS_BIN, '-d', root_url]
359 full_args = [CVS_BIN, '-d', root_url]
364 full_args += args
360 full_args += args
365 ret = shellout(full_args.map { |e| shell_quote e.to_s }.join(' '), &block)
361 ret = shellout(full_args.map { |e| shell_quote e.to_s }.join(' '), &block)
366 if $? && $?.exitstatus != 0
362 if $? && $?.exitstatus != 0
367 raise ScmCommandAborted, "cvs exited with non-zero status: #{$?.exitstatus}"
363 raise ScmCommandAborted, "cvs exited with non-zero status: #{$?.exitstatus}"
368 end
364 end
369 ret
365 ret
370 end
366 end
371 private :scm_cmd
367 private :scm_cmd
372 end
368 end
373
369
374 class CvsRevisionHelper
370 class CvsRevisionHelper
375 attr_accessor :complete_rev, :revision, :base, :branchid
371 attr_accessor :complete_rev, :revision, :base, :branchid
376
372
377 def initialize(complete_rev)
373 def initialize(complete_rev)
378 @complete_rev = complete_rev
374 @complete_rev = complete_rev
379 parseRevision()
375 parseRevision()
380 end
376 end
381
377
382 def branchPoint
378 def branchPoint
383 return @base
379 return @base
384 end
380 end
385
381
386 def branchVersion
382 def branchVersion
387 if isBranchRevision
383 if isBranchRevision
388 return @base+"."+@branchid
384 return @base+"."+@branchid
389 end
385 end
390 return @base
386 return @base
391 end
387 end
392
388
393 def isBranchRevision
389 def isBranchRevision
394 !@branchid.nil?
390 !@branchid.nil?
395 end
391 end
396
392
397 def prevRev
393 def prevRev
398 unless @revision == 0
394 unless @revision == 0
399 return buildRevision( @revision - 1 )
395 return buildRevision( @revision - 1 )
400 end
396 end
401 return buildRevision( @revision )
397 return buildRevision( @revision )
402 end
398 end
403
399
404 def is_in_branch_with_symbol(branch_symbol)
400 def is_in_branch_with_symbol(branch_symbol)
405 bpieces = branch_symbol.split(".")
401 bpieces = branch_symbol.split(".")
406 branch_start = "#{bpieces[0..-3].join(".")}.#{bpieces[-1]}"
402 branch_start = "#{bpieces[0..-3].join(".")}.#{bpieces[-1]}"
407 return ( branchVersion == branch_start )
403 return ( branchVersion == branch_start )
408 end
404 end
409
405
410 private
406 private
411 def buildRevision(rev)
407 def buildRevision(rev)
412 if rev== 0
408 if rev== 0
413 if @branchid.nil?
409 if @branchid.nil?
414 @base+".0"
410 @base+".0"
415 else
411 else
416 @base
412 @base
417 end
413 end
418 elsif @branchid.nil?
414 elsif @branchid.nil?
419 @base+"."+rev.to_s
415 @base+"."+rev.to_s
420 else
416 else
421 @base+"."+@branchid+"."+rev.to_s
417 @base+"."+@branchid+"."+rev.to_s
422 end
418 end
423 end
419 end
424
420
425 # Interpretiert die cvs revisionsnummern wie z.b. 1.14 oder 1.3.0.15
421 # Interpretiert die cvs revisionsnummern wie z.b. 1.14 oder 1.3.0.15
426 def parseRevision()
422 def parseRevision()
427 pieces = @complete_rev.split(".")
423 pieces = @complete_rev.split(".")
428 @revision = pieces.last.to_i
424 @revision = pieces.last.to_i
429 baseSize = 1
425 baseSize = 1
430 baseSize += (pieces.size / 2)
426 baseSize += (pieces.size / 2)
431 @base = pieces[0..-baseSize].join(".")
427 @base = pieces[0..-baseSize].join(".")
432 if baseSize > 2
428 if baseSize > 2
433 @branchid = pieces[-2]
429 @branchid = pieces[-2]
434 end
430 end
435 end
431 end
436 end
432 end
437 end
433 end
438 end
434 end
439 end
435 end
General Comments 0
You need to be logged in to leave comments. Login now