##// END OF EJS Templates
Wraps changeset creation inside a single transation....
Jean-Philippe Lang -
r3355:103698b371ee
parent child
Show More
@@ -1,331 +1,333
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require 'cgi'
19 19
20 20 module Redmine
21 21 module Scm
22 22 module Adapters
23 23 class CommandFailed < StandardError #:nodoc:
24 24 end
25 25
26 26 class AbstractAdapter #:nodoc:
27 27 class << self
28 28 # Returns the version of the scm client
29 29 # Eg: [1, 5, 0] or [] if unknown
30 30 def client_version
31 31 []
32 32 end
33 33
34 34 # Returns the version string of the scm client
35 35 # Eg: '1.5.0' or 'Unknown version' if unknown
36 36 def client_version_string
37 37 v = client_version || 'Unknown version'
38 38 v.is_a?(Array) ? v.join('.') : v.to_s
39 39 end
40 40
41 41 # Returns true if the current client version is above
42 42 # or equals the given one
43 43 # If option is :unknown is set to true, it will return
44 44 # true if the client version is unknown
45 45 def client_version_above?(v, options={})
46 46 ((client_version <=> v) >= 0) || (client_version.empty? && options[:unknown])
47 47 end
48 48 end
49 49
50 50 def initialize(url, root_url=nil, login=nil, password=nil)
51 51 @url = url
52 52 @login = login if login && !login.empty?
53 53 @password = (password || "") if @login
54 54 @root_url = root_url.blank? ? retrieve_root_url : root_url
55 55 end
56 56
57 57 def adapter_name
58 58 'Abstract'
59 59 end
60 60
61 61 def supports_cat?
62 62 true
63 63 end
64 64
65 65 def supports_annotate?
66 66 respond_to?('annotate')
67 67 end
68 68
69 69 def root_url
70 70 @root_url
71 71 end
72 72
73 73 def url
74 74 @url
75 75 end
76 76
77 77 # get info about the svn repository
78 78 def info
79 79 return nil
80 80 end
81 81
82 82 # Returns the entry identified by path and revision identifier
83 83 # or nil if entry doesn't exist in the repository
84 84 def entry(path=nil, identifier=nil)
85 85 parts = path.to_s.split(%r{[\/\\]}).select {|n| !n.blank?}
86 86 search_path = parts[0..-2].join('/')
87 87 search_name = parts[-1]
88 88 if search_path.blank? && search_name.blank?
89 89 # Root entry
90 90 Entry.new(:path => '', :kind => 'dir')
91 91 else
92 92 # Search for the entry in the parent directory
93 93 es = entries(search_path, identifier)
94 94 es ? es.detect {|e| e.name == search_name} : nil
95 95 end
96 96 end
97 97
98 98 # Returns an Entries collection
99 99 # or nil if the given path doesn't exist in the repository
100 100 def entries(path=nil, identifier=nil)
101 101 return nil
102 102 end
103 103
104 104 def branches
105 105 return nil
106 106 end
107 107
108 108 def tags
109 109 return nil
110 110 end
111 111
112 112 def default_branch
113 113 return nil
114 114 end
115 115
116 116 def properties(path, identifier=nil)
117 117 return nil
118 118 end
119 119
120 120 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
121 121 return nil
122 122 end
123 123
124 124 def diff(path, identifier_from, identifier_to=nil)
125 125 return nil
126 126 end
127 127
128 128 def cat(path, identifier=nil)
129 129 return nil
130 130 end
131 131
132 132 def with_leading_slash(path)
133 133 path ||= ''
134 134 (path[0,1]!="/") ? "/#{path}" : path
135 135 end
136 136
137 137 def with_trailling_slash(path)
138 138 path ||= ''
139 139 (path[-1,1] == "/") ? path : "#{path}/"
140 140 end
141 141
142 142 def without_leading_slash(path)
143 143 path ||= ''
144 144 path.gsub(%r{^/+}, '')
145 145 end
146 146
147 147 def without_trailling_slash(path)
148 148 path ||= ''
149 149 (path[-1,1] == "/") ? path[0..-2] : path
150 150 end
151 151
152 152 def shell_quote(str)
153 153 if Redmine::Platform.mswin?
154 154 '"' + str.gsub(/"/, '\\"') + '"'
155 155 else
156 156 "'" + str.gsub(/'/, "'\"'\"'") + "'"
157 157 end
158 158 end
159 159
160 160 private
161 161 def retrieve_root_url
162 162 info = self.info
163 163 info ? info.root_url : nil
164 164 end
165 165
166 166 def target(path)
167 167 path ||= ''
168 168 base = path.match(/^\//) ? root_url : url
169 169 shell_quote("#{base}/#{path}".gsub(/[?<>\*]/, ''))
170 170 end
171 171
172 172 def logger
173 173 self.class.logger
174 174 end
175 175
176 176 def shellout(cmd, &block)
177 177 self.class.shellout(cmd, &block)
178 178 end
179 179
180 180 def self.logger
181 181 RAILS_DEFAULT_LOGGER
182 182 end
183 183
184 184 def self.shellout(cmd, &block)
185 185 logger.debug "Shelling out: #{strip_credential(cmd)}" if logger && logger.debug?
186 186 if Rails.env == 'development'
187 187 # Capture stderr when running in dev environment
188 188 cmd = "#{cmd} 2>>#{RAILS_ROOT}/log/scm.stderr.log"
189 189 end
190 190 begin
191 191 IO.popen(cmd, "r+") do |io|
192 192 io.close_write
193 193 block.call(io) if block_given?
194 194 end
195 195 rescue Errno::ENOENT => e
196 196 msg = strip_credential(e.message)
197 197 # The command failed, log it and re-raise
198 198 logger.error("SCM command failed, make sure that your SCM binary (eg. svn) is in PATH (#{ENV['PATH']}): #{strip_credential(cmd)}\n with: #{msg}")
199 199 raise CommandFailed.new(msg)
200 200 end
201 201 end
202 202
203 203 # Hides username/password in a given command
204 204 def self.strip_credential(cmd)
205 205 q = (Redmine::Platform.mswin? ? '"' : "'")
206 206 cmd.to_s.gsub(/(\-\-(password|username))\s+(#{q}[^#{q}]+#{q}|[^#{q}]\S+)/, '\\1 xxxx')
207 207 end
208 208
209 209 def strip_credential(cmd)
210 210 self.class.strip_credential(cmd)
211 211 end
212 212 end
213 213
214 214 class Entries < Array
215 215 def sort_by_name
216 216 sort {|x,y|
217 217 if x.kind == y.kind
218 218 x.name.to_s <=> y.name.to_s
219 219 else
220 220 x.kind <=> y.kind
221 221 end
222 222 }
223 223 end
224 224
225 225 def revisions
226 226 revisions ||= Revisions.new(collect{|entry| entry.lastrev}.compact)
227 227 end
228 228 end
229 229
230 230 class Info
231 231 attr_accessor :root_url, :lastrev
232 232 def initialize(attributes={})
233 233 self.root_url = attributes[:root_url] if attributes[:root_url]
234 234 self.lastrev = attributes[:lastrev]
235 235 end
236 236 end
237 237
238 238 class Entry
239 239 attr_accessor :name, :path, :kind, :size, :lastrev
240 240 def initialize(attributes={})
241 241 self.name = attributes[:name] if attributes[:name]
242 242 self.path = attributes[:path] if attributes[:path]
243 243 self.kind = attributes[:kind] if attributes[:kind]
244 244 self.size = attributes[:size].to_i if attributes[:size]
245 245 self.lastrev = attributes[:lastrev]
246 246 end
247 247
248 248 def is_file?
249 249 'file' == self.kind
250 250 end
251 251
252 252 def is_dir?
253 253 'dir' == self.kind
254 254 end
255 255
256 256 def is_text?
257 257 Redmine::MimeType.is_type?('text', name)
258 258 end
259 259 end
260 260
261 261 class Revisions < Array
262 262 def latest
263 263 sort {|x,y|
264 264 unless x.time.nil? or y.time.nil?
265 265 x.time <=> y.time
266 266 else
267 267 0
268 268 end
269 269 }.last
270 270 end
271 271 end
272 272
273 273 class Revision
274 274 attr_accessor :identifier, :scmid, :name, :author, :time, :message, :paths, :revision, :branch
275 275
276 276 def initialize(attributes={})
277 277 self.identifier = attributes[:identifier]
278 278 self.scmid = attributes[:scmid]
279 279 self.name = attributes[:name] || self.identifier
280 280 self.author = attributes[:author]
281 281 self.time = attributes[:time]
282 282 self.message = attributes[:message] || ""
283 283 self.paths = attributes[:paths]
284 284 self.revision = attributes[:revision]
285 285 self.branch = attributes[:branch]
286 286 end
287 287
288 288 def save(repo)
289 if repo.changesets.find_by_scmid(scmid.to_s).nil?
290 changeset = Changeset.create!(
289 Changeset.transaction do
290 changeset = Changeset.new(
291 291 :repository => repo,
292 292 :revision => identifier,
293 293 :scmid => scmid,
294 294 :committer => author,
295 295 :committed_on => time,
296 296 :comments => message)
297 297
298 if changeset.save
298 299 paths.each do |file|
299 Change.create!(
300 Change.create(
300 301 :changeset => changeset,
301 302 :action => file[:action],
302 303 :path => file[:path])
303 304 end
304 305 end
305 306 end
306 307 end
308 end
307 309
308 310 class Annotate
309 311 attr_reader :lines, :revisions
310 312
311 313 def initialize
312 314 @lines = []
313 315 @revisions = []
314 316 end
315 317
316 318 def add_line(line, revision)
317 319 @lines << line
318 320 @revisions << revision
319 321 end
320 322
321 323 def content
322 324 content = lines.join("\n")
323 325 end
324 326
325 327 def empty?
326 328 lines.empty?
327 329 end
328 330 end
329 331 end
330 332 end
331 333 end
General Comments 0
You need to be logged in to leave comments. Login now