##// END OF EJS Templates
ruby1.9 compatibility...
Jean-Philippe Lang -
r4481:9748567452cc
parent child
Show More
@@ -1,362 +1,364
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 = "cvs"
26 CVS_BIN = "cvs"
27
27
28 # Guidelines for the input:
28 # Guidelines for the input:
29 # url -> the project-path, relative to the cvsroot (eg. module name)
29 # url -> the project-path, relative to the cvsroot (eg. module name)
30 # root_url -> the good old, sometimes damned, CVSROOT
30 # root_url -> the good old, sometimes damned, CVSROOT
31 # login -> unnecessary
31 # login -> unnecessary
32 # password -> unnecessary too
32 # password -> unnecessary too
33 def initialize(url, root_url=nil, login=nil, password=nil)
33 def initialize(url, root_url=nil, login=nil, password=nil)
34 @url = url
34 @url = url
35 @login = login if login && !login.empty?
35 @login = login if login && !login.empty?
36 @password = (password || "") if @login
36 @password = (password || "") if @login
37 #TODO: better Exception here (IllegalArgumentException)
37 #TODO: better Exception here (IllegalArgumentException)
38 raise CommandFailed if root_url.blank?
38 raise CommandFailed if root_url.blank?
39 @root_url = root_url
39 @root_url = root_url
40 end
40 end
41
41
42 def root_url
42 def root_url
43 @root_url
43 @root_url
44 end
44 end
45
45
46 def url
46 def url
47 @url
47 @url
48 end
48 end
49
49
50 def info
50 def info
51 logger.debug "<cvs> info"
51 logger.debug "<cvs> info"
52 Info.new({:root_url => @root_url, :lastrev => nil})
52 Info.new({:root_url => @root_url, :lastrev => nil})
53 end
53 end
54
54
55 def get_previous_revision(revision)
55 def get_previous_revision(revision)
56 CvsRevisionHelper.new(revision).prevRev
56 CvsRevisionHelper.new(revision).prevRev
57 end
57 end
58
58
59 # Returns an Entries collection
59 # Returns an Entries collection
60 # or nil if the given path doesn't exist in the repository
60 # or nil if the given path doesn't exist in the repository
61 # this method is used by the repository-browser (aka LIST)
61 # this method is used by the repository-browser (aka LIST)
62 def entries(path=nil, identifier=nil)
62 def entries(path=nil, identifier=nil)
63 logger.debug "<cvs> entries '#{path}' with identifier '#{identifier}'"
63 logger.debug "<cvs> entries '#{path}' with identifier '#{identifier}'"
64 path_with_project="#{url}#{with_leading_slash(path)}"
64 path_with_project="#{url}#{with_leading_slash(path)}"
65 entries = Entries.new
65 entries = Entries.new
66 cmd = "#{CVS_BIN} -d #{shell_quote root_url} rls -e"
66 cmd = "#{CVS_BIN} -d #{shell_quote root_url} rls -e"
67 cmd << " -D \"#{time_to_cvstime(identifier)}\"" if identifier
67 cmd << " -D \"#{time_to_cvstime(identifier)}\"" if identifier
68 cmd << " #{shell_quote path_with_project}"
68 cmd << " #{shell_quote path_with_project}"
69 shellout(cmd) do |io|
69 shellout(cmd) do |io|
70 io.each_line(){|line|
70 io.each_line(){|line|
71 fields=line.chop.split('/',-1)
71 fields=line.chop.split('/',-1)
72 logger.debug(">>InspectLine #{fields.inspect}")
72 logger.debug(">>InspectLine #{fields.inspect}")
73
73
74 if fields[0]!="D"
74 if fields[0]!="D"
75 entries << Entry.new({:name => fields[-5],
75 entries << Entry.new({:name => fields[-5],
76 #:path => fields[-4].include?(path)?fields[-4]:(path + "/"+ fields[-4]),
76 #:path => fields[-4].include?(path)?fields[-4]:(path + "/"+ fields[-4]),
77 :path => "#{path}/#{fields[-5]}",
77 :path => "#{path}/#{fields[-5]}",
78 :kind => 'file',
78 :kind => 'file',
79 :size => nil,
79 :size => nil,
80 :lastrev => Revision.new({
80 :lastrev => Revision.new({
81 :revision => fields[-4],
81 :revision => fields[-4],
82 :name => fields[-4],
82 :name => fields[-4],
83 :time => Time.parse(fields[-3]),
83 :time => Time.parse(fields[-3]),
84 :author => ''
84 :author => ''
85 })
85 })
86 })
86 })
87 else
87 else
88 entries << Entry.new({:name => fields[1],
88 entries << Entry.new({:name => fields[1],
89 :path => "#{path}/#{fields[1]}",
89 :path => "#{path}/#{fields[1]}",
90 :kind => 'dir',
90 :kind => 'dir',
91 :size => nil,
91 :size => nil,
92 :lastrev => nil
92 :lastrev => nil
93 })
93 })
94 end
94 end
95 }
95 }
96 end
96 end
97 return nil if $? && $?.exitstatus != 0
97 return nil if $? && $?.exitstatus != 0
98 entries.sort_by_name
98 entries.sort_by_name
99 end
99 end
100
100
101 STARTLOG="----------------------------"
101 STARTLOG="----------------------------"
102 ENDLOG ="============================================================================="
102 ENDLOG ="============================================================================="
103
103
104 # Returns all revisions found between identifier_from and identifier_to
104 # Returns all revisions found between identifier_from and identifier_to
105 # in the repository. both identifier have to be dates or nil.
105 # in the repository. both identifier have to be dates or nil.
106 # these method returns nothing but yield every result in block
106 # these method returns nothing but yield every result in block
107 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}, &block)
107 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}, &block)
108 logger.debug "<cvs> revisions path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
108 logger.debug "<cvs> revisions path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
109
109
110 path_with_project="#{url}#{with_leading_slash(path)}"
110 path_with_project="#{url}#{with_leading_slash(path)}"
111 cmd = "#{CVS_BIN} -d #{shell_quote root_url} rlog"
111 cmd = "#{CVS_BIN} -d #{shell_quote root_url} rlog"
112 cmd << " -d\">#{time_to_cvstime(identifier_from)}\"" if identifier_from
112 cmd << " -d\">#{time_to_cvstime(identifier_from)}\"" if identifier_from
113 cmd << " #{shell_quote path_with_project}"
113 cmd << " #{shell_quote path_with_project}"
114 shellout(cmd) do |io|
114 shellout(cmd) do |io|
115 state="entry_start"
115 state="entry_start"
116
116
117 commit_log=String.new
117 commit_log=String.new
118 revision=nil
118 revision=nil
119 date=nil
119 date=nil
120 author=nil
120 author=nil
121 entry_path=nil
121 entry_path=nil
122 entry_name=nil
122 entry_name=nil
123 file_state=nil
123 file_state=nil
124 branch_map=nil
124 branch_map=nil
125
125
126 io.each_line() do |line|
126 io.each_line() do |line|
127
127
128 if state!="revision" && /^#{ENDLOG}/ =~ line
128 if state!="revision" && /^#{ENDLOG}/ =~ line
129 commit_log=String.new
129 commit_log=String.new
130 revision=nil
130 revision=nil
131 state="entry_start"
131 state="entry_start"
132 end
132 end
133
133
134 if state=="entry_start"
134 if state=="entry_start"
135 branch_map=Hash.new
135 branch_map=Hash.new
136 if /^RCS file: #{Regexp.escape(root_url_path)}\/#{Regexp.escape(path_with_project)}(.+),v$/ =~ line
136 if /^RCS file: #{Regexp.escape(root_url_path)}\/#{Regexp.escape(path_with_project)}(.+),v$/ =~ line
137 entry_path = normalize_cvs_path($1)
137 entry_path = normalize_cvs_path($1)
138 entry_name = normalize_path(File.basename($1))
138 entry_name = normalize_path(File.basename($1))
139 logger.debug("Path #{entry_path} <=> Name #{entry_name}")
139 logger.debug("Path #{entry_path} <=> Name #{entry_name}")
140 elsif /^head: (.+)$/ =~ line
140 elsif /^head: (.+)$/ =~ line
141 entry_headRev = $1 #unless entry.nil?
141 entry_headRev = $1 #unless entry.nil?
142 elsif /^symbolic names:/ =~ line
142 elsif /^symbolic names:/ =~ line
143 state="symbolic" #unless entry.nil?
143 state="symbolic" #unless entry.nil?
144 elsif /^#{STARTLOG}/ =~ line
144 elsif /^#{STARTLOG}/ =~ line
145 commit_log=String.new
145 commit_log=String.new
146 state="revision"
146 state="revision"
147 end
147 end
148 next
148 next
149 elsif state=="symbolic"
149 elsif state=="symbolic"
150 if /^(.*):\s(.*)/ =~ (line.strip)
150 if /^(.*):\s(.*)/ =~ (line.strip)
151 branch_map[$1]=$2
151 branch_map[$1]=$2
152 else
152 else
153 state="tags"
153 state="tags"
154 next
154 next
155 end
155 end
156 elsif state=="tags"
156 elsif state=="tags"
157 if /^#{STARTLOG}/ =~ line
157 if /^#{STARTLOG}/ =~ line
158 commit_log = ""
158 commit_log = ""
159 state="revision"
159 state="revision"
160 elsif /^#{ENDLOG}/ =~ line
160 elsif /^#{ENDLOG}/ =~ line
161 state="head"
161 state="head"
162 end
162 end
163 next
163 next
164 elsif state=="revision"
164 elsif state=="revision"
165 if /^#{ENDLOG}/ =~ line || /^#{STARTLOG}/ =~ line
165 if /^#{ENDLOG}/ =~ line || /^#{STARTLOG}/ =~ line
166 if revision
166 if revision
167
167
168 revHelper=CvsRevisionHelper.new(revision)
168 revHelper=CvsRevisionHelper.new(revision)
169 revBranch="HEAD"
169 revBranch="HEAD"
170
170
171 branch_map.each() do |branch_name,branch_point|
171 branch_map.each() do |branch_name,branch_point|
172 if revHelper.is_in_branch_with_symbol(branch_point)
172 if revHelper.is_in_branch_with_symbol(branch_point)
173 revBranch=branch_name
173 revBranch=branch_name
174 end
174 end
175 end
175 end
176
176
177 logger.debug("********** YIELD Revision #{revision}::#{revBranch}")
177 logger.debug("********** YIELD Revision #{revision}::#{revBranch}")
178
178
179 yield Revision.new({
179 yield Revision.new({
180 :time => date,
180 :time => date,
181 :author => author,
181 :author => author,
182 :message=>commit_log.chomp,
182 :message=>commit_log.chomp,
183 :paths => [{
183 :paths => [{
184 :revision => revision,
184 :revision => revision,
185 :branch=> revBranch,
185 :branch=> revBranch,
186 :path=>entry_path,
186 :path=>entry_path,
187 :name=>entry_name,
187 :name=>entry_name,
188 :kind=>'file',
188 :kind=>'file',
189 :action=>file_state
189 :action=>file_state
190 }]
190 }]
191 })
191 })
192 end
192 end
193
193
194 commit_log=String.new
194 commit_log=String.new
195 revision=nil
195 revision=nil
196
196
197 if /^#{ENDLOG}/ =~ line
197 if /^#{ENDLOG}/ =~ line
198 state="entry_start"
198 state="entry_start"
199 end
199 end
200 next
200 next
201 end
201 end
202
202
203 if /^branches: (.+)$/ =~ line
203 if /^branches: (.+)$/ =~ line
204 #TODO: version.branch = $1
204 #TODO: version.branch = $1
205 elsif /^revision (\d+(?:\.\d+)+).*$/ =~ line
205 elsif /^revision (\d+(?:\.\d+)+).*$/ =~ line
206 revision = $1
206 revision = $1
207 elsif /^date:\s+(\d+.\d+.\d+\s+\d+:\d+:\d+)/ =~ line
207 elsif /^date:\s+(\d+.\d+.\d+\s+\d+:\d+:\d+)/ =~ line
208 date = Time.parse($1)
208 date = Time.parse($1)
209 author = /author: ([^;]+)/.match(line)[1]
209 author = /author: ([^;]+)/.match(line)[1]
210 file_state = /state: ([^;]+)/.match(line)[1]
210 file_state = /state: ([^;]+)/.match(line)[1]
211 #TODO: linechanges only available in CVS.... maybe a feature our SVN implementation. i'm sure, they are
211 #TODO: linechanges only available in CVS.... maybe a feature our SVN implementation. i'm sure, they are
212 # useful for stats or something else
212 # useful for stats or something else
213 # linechanges =/lines: \+(\d+) -(\d+)/.match(line)
213 # linechanges =/lines: \+(\d+) -(\d+)/.match(line)
214 # unless linechanges.nil?
214 # unless linechanges.nil?
215 # version.line_plus = linechanges[1]
215 # version.line_plus = linechanges[1]
216 # version.line_minus = linechanges[2]
216 # version.line_minus = linechanges[2]
217 # else
217 # else
218 # version.line_plus = 0
218 # version.line_plus = 0
219 # version.line_minus = 0
219 # version.line_minus = 0
220 # end
220 # end
221 else
221 else
222 commit_log << line unless line =~ /^\*\*\* empty log message \*\*\*/
222 commit_log << line unless line =~ /^\*\*\* empty log message \*\*\*/
223 end
223 end
224 end
224 end
225 end
225 end
226 end
226 end
227 end
227 end
228
228
229 def diff(path, identifier_from, identifier_to=nil)
229 def diff(path, identifier_from, identifier_to=nil)
230 logger.debug "<cvs> diff path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
230 logger.debug "<cvs> diff path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
231 path_with_project="#{url}#{with_leading_slash(path)}"
231 path_with_project="#{url}#{with_leading_slash(path)}"
232 cmd = "#{CVS_BIN} -d #{shell_quote root_url} rdiff -u -r#{identifier_to} -r#{identifier_from} #{shell_quote path_with_project}"
232 cmd = "#{CVS_BIN} -d #{shell_quote root_url} rdiff -u -r#{identifier_to} -r#{identifier_from} #{shell_quote path_with_project}"
233 diff = []
233 diff = []
234 shellout(cmd) do |io|
234 shellout(cmd) do |io|
235 io.each_line do |line|
235 io.each_line do |line|
236 diff << line
236 diff << line
237 end
237 end
238 end
238 end
239 return nil if $? && $?.exitstatus != 0
239 return nil if $? && $?.exitstatus != 0
240 diff
240 diff
241 end
241 end
242
242
243 def cat(path, identifier=nil)
243 def cat(path, identifier=nil)
244 identifier = (identifier) ? identifier : "HEAD"
244 identifier = (identifier) ? identifier : "HEAD"
245 logger.debug "<cvs> cat path:'#{path}',identifier #{identifier}"
245 logger.debug "<cvs> cat path:'#{path}',identifier #{identifier}"
246 path_with_project="#{url}#{with_leading_slash(path)}"
246 path_with_project="#{url}#{with_leading_slash(path)}"
247 cmd = "#{CVS_BIN} -d #{shell_quote root_url} co"
247 cmd = "#{CVS_BIN} -d #{shell_quote root_url} co"
248 cmd << " -D \"#{time_to_cvstime(identifier)}\"" if identifier
248 cmd << " -D \"#{time_to_cvstime(identifier)}\"" if identifier
249 cmd << " -p #{shell_quote path_with_project}"
249 cmd << " -p #{shell_quote path_with_project}"
250 cat = nil
250 cat = nil
251 shellout(cmd) do |io|
251 shellout(cmd) do |io|
252 cat = io.read
252 cat = io.read
253 end
253 end
254 return nil if $? && $?.exitstatus != 0
254 return nil if $? && $?.exitstatus != 0
255 cat
255 cat
256 end
256 end
257
257
258 def annotate(path, identifier=nil)
258 def annotate(path, identifier=nil)
259 identifier = (identifier) ? identifier.to_i : "HEAD"
259 identifier = (identifier) ? identifier.to_i : "HEAD"
260 logger.debug "<cvs> annotate path:'#{path}',identifier #{identifier}"
260 logger.debug "<cvs> annotate path:'#{path}',identifier #{identifier}"
261 path_with_project="#{url}#{with_leading_slash(path)}"
261 path_with_project="#{url}#{with_leading_slash(path)}"
262 cmd = "#{CVS_BIN} -d #{shell_quote root_url} rannotate -r#{identifier} #{shell_quote path_with_project}"
262 cmd = "#{CVS_BIN} -d #{shell_quote root_url} rannotate -r#{identifier} #{shell_quote path_with_project}"
263 blame = Annotate.new
263 blame = Annotate.new
264 shellout(cmd) do |io|
264 shellout(cmd) do |io|
265 io.each_line do |line|
265 io.each_line do |line|
266 next unless line =~ %r{^([\d\.]+)\s+\(([^\)]+)\s+[^\)]+\):\s(.*)$}
266 next unless line =~ %r{^([\d\.]+)\s+\(([^\)]+)\s+[^\)]+\):\s(.*)$}
267 blame.add_line($3.rstrip, Revision.new(:revision => $1, :author => $2.strip))
267 blame.add_line($3.rstrip, Revision.new(:revision => $1, :author => $2.strip))
268 end
268 end
269 end
269 end
270 return nil if $? && $?.exitstatus != 0
270 return nil if $? && $?.exitstatus != 0
271 blame
271 blame
272 end
272 end
273
273
274 private
274 private
275
275
276 # Returns the root url without the connexion string
276 # Returns the root url without the connexion string
277 # :pserver:anonymous@foo.bar:/path => /path
277 # :pserver:anonymous@foo.bar:/path => /path
278 # :ext:cvsservername:/path => /path
278 # :ext:cvsservername:/path => /path
279 def root_url_path
279 def root_url_path
280 root_url.to_s.gsub(/^:.+:\d*/, '')
280 root_url.to_s.gsub(/^:.+:\d*/, '')
281 end
281 end
282
282
283 # convert a date/time into the CVS-format
283 # convert a date/time into the CVS-format
284 def time_to_cvstime(time)
284 def time_to_cvstime(time)
285 return nil if time.nil?
285 return nil if time.nil?
286 return Time.now if time == 'HEAD'
287
286 unless time.kind_of? Time
288 unless time.kind_of? Time
287 time = Time.parse(time)
289 time = Time.parse(time)
288 end
290 end
289 return time.strftime("%Y-%m-%d %H:%M:%S")
291 return time.strftime("%Y-%m-%d %H:%M:%S")
290 end
292 end
291
293
292 def normalize_cvs_path(path)
294 def normalize_cvs_path(path)
293 normalize_path(path.gsub(/Attic\//,''))
295 normalize_path(path.gsub(/Attic\//,''))
294 end
296 end
295
297
296 def normalize_path(path)
298 def normalize_path(path)
297 path.sub(/^(\/)*(.*)/,'\2').sub(/(.*)(,v)+/,'\1')
299 path.sub(/^(\/)*(.*)/,'\2').sub(/(.*)(,v)+/,'\1')
298 end
300 end
299 end
301 end
300
302
301 class CvsRevisionHelper
303 class CvsRevisionHelper
302 attr_accessor :complete_rev, :revision, :base, :branchid
304 attr_accessor :complete_rev, :revision, :base, :branchid
303
305
304 def initialize(complete_rev)
306 def initialize(complete_rev)
305 @complete_rev = complete_rev
307 @complete_rev = complete_rev
306 parseRevision()
308 parseRevision()
307 end
309 end
308
310
309 def branchPoint
311 def branchPoint
310 return @base
312 return @base
311 end
313 end
312
314
313 def branchVersion
315 def branchVersion
314 if isBranchRevision
316 if isBranchRevision
315 return @base+"."+@branchid
317 return @base+"."+@branchid
316 end
318 end
317 return @base
319 return @base
318 end
320 end
319
321
320 def isBranchRevision
322 def isBranchRevision
321 !@branchid.nil?
323 !@branchid.nil?
322 end
324 end
323
325
324 def prevRev
326 def prevRev
325 unless @revision==0
327 unless @revision==0
326 return buildRevision(@revision-1)
328 return buildRevision(@revision-1)
327 end
329 end
328 return buildRevision(@revision)
330 return buildRevision(@revision)
329 end
331 end
330
332
331 def is_in_branch_with_symbol(branch_symbol)
333 def is_in_branch_with_symbol(branch_symbol)
332 bpieces=branch_symbol.split(".")
334 bpieces=branch_symbol.split(".")
333 branch_start="#{bpieces[0..-3].join(".")}.#{bpieces[-1]}"
335 branch_start="#{bpieces[0..-3].join(".")}.#{bpieces[-1]}"
334 return (branchVersion==branch_start)
336 return (branchVersion==branch_start)
335 end
337 end
336
338
337 private
339 private
338 def buildRevision(rev)
340 def buildRevision(rev)
339 if rev== 0
341 if rev== 0
340 @base
342 @base
341 elsif @branchid.nil?
343 elsif @branchid.nil?
342 @base+"."+rev.to_s
344 @base+"."+rev.to_s
343 else
345 else
344 @base+"."+@branchid+"."+rev.to_s
346 @base+"."+@branchid+"."+rev.to_s
345 end
347 end
346 end
348 end
347
349
348 # Interpretiert die cvs revisionsnummern wie z.b. 1.14 oder 1.3.0.15
350 # Interpretiert die cvs revisionsnummern wie z.b. 1.14 oder 1.3.0.15
349 def parseRevision()
351 def parseRevision()
350 pieces=@complete_rev.split(".")
352 pieces=@complete_rev.split(".")
351 @revision=pieces.last.to_i
353 @revision=pieces.last.to_i
352 baseSize=1
354 baseSize=1
353 baseSize+=(pieces.size/2)
355 baseSize+=(pieces.size/2)
354 @base=pieces[0..-baseSize].join(".")
356 @base=pieces[0..-baseSize].join(".")
355 if baseSize > 2
357 if baseSize > 2
356 @branchid=pieces[-2]
358 @branchid=pieces[-2]
357 end
359 end
358 end
360 end
359 end
361 end
360 end
362 end
361 end
363 end
362 end
364 end
General Comments 0
You need to be logged in to leave comments. Login now