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