##// END OF EJS Templates
Merged r12456 from trunk to 2.3-stable (#15756)...
Toshi MARUYAMA -
r12184:292cf401008a 2.3-stable
parent child
Show More
@@ -1,341 +1,341
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2013 Jean-Philippe Lang
2 # Copyright (C) 2006-2013 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'
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 BazaarAdapter < AbstractAdapter
23 class BazaarAdapter < AbstractAdapter
24
24
25 # Bazaar executable name
25 # Bazaar executable name
26 BZR_BIN = Redmine::Configuration['scm_bazaar_command'] || "bzr"
26 BZR_BIN = Redmine::Configuration['scm_bazaar_command'] || "bzr"
27
27
28 class << self
28 class << self
29 def client_command
29 def client_command
30 @@bin ||= BZR_BIN
30 @@bin ||= BZR_BIN
31 end
31 end
32
32
33 def sq_bin
33 def sq_bin
34 @@sq_bin ||= shell_quote_command
34 @@sq_bin ||= shell_quote_command
35 end
35 end
36
36
37 def client_version
37 def client_version
38 @@client_version ||= (scm_command_version || [])
38 @@client_version ||= (scm_command_version || [])
39 end
39 end
40
40
41 def client_available
41 def client_available
42 !client_version.empty?
42 !client_version.empty?
43 end
43 end
44
44
45 def scm_command_version
45 def scm_command_version
46 scm_version = scm_version_from_command_line.dup
46 scm_version = scm_version_from_command_line.dup
47 if scm_version.respond_to?(:force_encoding)
47 if scm_version.respond_to?(:force_encoding)
48 scm_version.force_encoding('ASCII-8BIT')
48 scm_version.force_encoding('ASCII-8BIT')
49 end
49 end
50 if m = scm_version.match(%r{\A(.*?)((\d+\.)+\d+)})
50 if m = scm_version.match(%r{\A(.*?)((\d+\.)+\d+)})
51 m[2].scan(%r{\d+}).collect(&:to_i)
51 m[2].scan(%r{\d+}).collect(&:to_i)
52 end
52 end
53 end
53 end
54
54
55 def scm_version_from_command_line
55 def scm_version_from_command_line
56 shellout("#{sq_bin} --version") { |io| io.read }.to_s
56 shellout("#{sq_bin} --version") { |io| io.read }.to_s
57 end
57 end
58 end
58 end
59
59
60 def initialize(url, root_url=nil, login=nil, password=nil, path_encoding=nil)
60 def initialize(url, root_url=nil, login=nil, password=nil, path_encoding=nil)
61 @url = url
61 @url = url
62 @root_url = url
62 @root_url = url
63 @path_encoding = 'UTF-8'
63 @path_encoding = 'UTF-8'
64 # do not call *super* for non ASCII repository path
64 # do not call *super* for non ASCII repository path
65 end
65 end
66
66
67 def bzr_path_encodig=(encoding)
67 def bzr_path_encodig=(encoding)
68 @path_encoding = encoding
68 @path_encoding = encoding
69 end
69 end
70
70
71 # Get info about the repository
71 # Get info about the repository
72 def info
72 def info
73 cmd_args = %w|revno|
73 cmd_args = %w|revno|
74 cmd_args << bzr_target('')
74 cmd_args << bzr_target('')
75 info = nil
75 info = nil
76 scm_cmd(*cmd_args) do |io|
76 scm_cmd(*cmd_args) do |io|
77 if io.read =~ %r{^(\d+)\r?$}
77 if io.read =~ %r{^(\d+)\r?$}
78 info = Info.new({:root_url => url,
78 info = Info.new({:root_url => url,
79 :lastrev => Revision.new({
79 :lastrev => Revision.new({
80 :identifier => $1
80 :identifier => $1
81 })
81 })
82 })
82 })
83 end
83 end
84 end
84 end
85 info
85 info
86 rescue ScmCommandAborted
86 rescue ScmCommandAborted
87 return nil
87 return nil
88 end
88 end
89
89
90 # Returns an Entries collection
90 # Returns an Entries collection
91 # or nil if the given path doesn't exist in the repository
91 # or nil if the given path doesn't exist in the repository
92 def entries(path=nil, identifier=nil, options={})
92 def entries(path=nil, identifier=nil, options={})
93 path ||= ''
93 path ||= ''
94 entries = Entries.new
94 entries = Entries.new
95 identifier = -1 unless identifier && identifier.to_i > 0
95 identifier = -1 unless identifier && identifier.to_i > 0
96 cmd_args = %w|ls -v --show-ids|
96 cmd_args = %w|ls -v --show-ids|
97 cmd_args << "-r#{identifier.to_i}"
97 cmd_args << "-r#{identifier.to_i}"
98 cmd_args << bzr_target(path)
98 cmd_args << bzr_target(path)
99 scm_cmd(*cmd_args) do |io|
99 scm_cmd(*cmd_args) do |io|
100 prefix_utf8 = "#{url}/#{path}".gsub('\\', '/')
100 prefix_utf8 = "#{url}/#{path}".gsub('\\', '/')
101 logger.debug "PREFIX: #{prefix_utf8}"
101 logger.debug "PREFIX: #{prefix_utf8}"
102 prefix = scm_iconv(@path_encoding, 'UTF-8', prefix_utf8)
102 prefix = scm_iconv(@path_encoding, 'UTF-8', prefix_utf8)
103 prefix.force_encoding('ASCII-8BIT') if prefix.respond_to?(:force_encoding)
103 prefix.force_encoding('ASCII-8BIT') if prefix.respond_to?(:force_encoding)
104 re = %r{^V\s+(#{Regexp.escape(prefix)})?(\/?)([^\/]+)(\/?)\s+(\S+)\r?$}
104 re = %r{^V\s+(#{Regexp.escape(prefix)})?(\/?)([^\/]+)(\/?)\s+(\S+)\r?$}
105 io.each_line do |line|
105 io.each_line do |line|
106 next unless line =~ re
106 next unless line =~ re
107 name_locale, slash, revision = $3.strip, $4, $5.strip
107 name_locale, slash, revision = $3.strip, $4, $5.strip
108 name = scm_iconv('UTF-8', @path_encoding, name_locale)
108 name = scm_iconv('UTF-8', @path_encoding, name_locale)
109 entries << Entry.new({:name => name,
109 entries << Entry.new({:name => name,
110 :path => ((path.empty? ? "" : "#{path}/") + name),
110 :path => ((path.empty? ? "" : "#{path}/") + name),
111 :kind => (slash.blank? ? 'file' : 'dir'),
111 :kind => (slash.blank? ? 'file' : 'dir'),
112 :size => nil,
112 :size => nil,
113 :lastrev => Revision.new(:revision => revision)
113 :lastrev => Revision.new(:revision => revision)
114 })
114 })
115 end
115 end
116 end
116 end
117 if logger && logger.debug?
117 if logger && logger.debug?
118 logger.debug("Found #{entries.size} entries in the repository for #{target(path)}")
118 logger.debug("Found #{entries.size} entries in the repository for #{target(path)}")
119 end
119 end
120 entries.sort_by_name
120 entries.sort_by_name
121 rescue ScmCommandAborted
121 rescue ScmCommandAborted
122 return nil
122 return nil
123 end
123 end
124
124
125 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
125 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
126 path ||= ''
126 path ||= ''
127 identifier_from = (identifier_from and identifier_from.to_i > 0) ? identifier_from.to_i : 'last:1'
127 identifier_from = (identifier_from and identifier_from.to_i > 0) ? identifier_from.to_i : 'last:1'
128 identifier_to = (identifier_to and identifier_to.to_i > 0) ? identifier_to.to_i : 1
128 identifier_to = (identifier_to and identifier_to.to_i > 0) ? identifier_to.to_i : 1
129 revisions = Revisions.new
129 revisions = Revisions.new
130 cmd_args = %w|log -v --show-ids|
130 cmd_args = %w|log -v --show-ids|
131 cmd_args << "-r#{identifier_to}..#{identifier_from}"
131 cmd_args << "-r#{identifier_to}..#{identifier_from}"
132 cmd_args << bzr_target(path)
132 cmd_args << bzr_target(path)
133 scm_cmd(*cmd_args) do |io|
133 scm_cmd(*cmd_args) do |io|
134 revision = nil
134 revision = nil
135 parsing = nil
135 parsing = nil
136 io.each_line do |line|
136 io.each_line do |line|
137 if line =~ /^----/
137 if line =~ /^----/
138 revisions << revision if revision
138 revisions << revision if revision
139 revision = Revision.new(:paths => [], :message => '')
139 revision = Revision.new(:paths => [], :message => '')
140 parsing = nil
140 parsing = nil
141 else
141 else
142 next unless revision
142 next unless revision
143 if line =~ /^revno: (\d+)($|\s\[merge\]$)/
143 if line =~ /^revno: (\d+)($|\s\[merge\]$)/
144 revision.identifier = $1.to_i
144 revision.identifier = $1.to_i
145 elsif line =~ /^committer: (.+)$/
145 elsif line =~ /^committer: (.+)$/
146 revision.author = $1.strip
146 revision.author = $1.strip
147 elsif line =~ /^revision-id:(.+)$/
147 elsif line =~ /^revision-id:(.+)$/
148 revision.scmid = $1.strip
148 revision.scmid = $1.strip
149 elsif line =~ /^timestamp: (.+)$/
149 elsif line =~ /^timestamp: (.+)$/
150 revision.time = Time.parse($1).localtime
150 revision.time = Time.parse($1).localtime
151 elsif line =~ /^ -----/
151 elsif line =~ /^ -----/
152 # partial revisions
152 # partial revisions
153 parsing = nil unless parsing == 'message'
153 parsing = nil unless parsing == 'message'
154 elsif line =~ /^(message|added|modified|removed|renamed):/
154 elsif line =~ /^(message|added|modified|removed|renamed):/
155 parsing = $1
155 parsing = $1
156 elsif line =~ /^ (.*)$/
156 elsif line =~ /^ (.*)$/
157 if parsing == 'message'
157 if parsing == 'message'
158 revision.message << "#{$1}\n"
158 revision.message << "#{$1}\n"
159 else
159 else
160 if $1 =~ /^(.*)\s+(\S+)$/
160 if $1 =~ /^(.*)\s+(\S+)$/
161 path_locale = $1.strip
161 path_locale = $1.strip
162 path = scm_iconv('UTF-8', @path_encoding, path_locale)
162 path = scm_iconv('UTF-8', @path_encoding, path_locale)
163 revid = $2
163 revid = $2
164 case parsing
164 case parsing
165 when 'added'
165 when 'added'
166 revision.paths << {:action => 'A', :path => "/#{path}", :revision => revid}
166 revision.paths << {:action => 'A', :path => "/#{path}", :revision => revid}
167 when 'modified'
167 when 'modified'
168 revision.paths << {:action => 'M', :path => "/#{path}", :revision => revid}
168 revision.paths << {:action => 'M', :path => "/#{path}", :revision => revid}
169 when 'removed'
169 when 'removed'
170 revision.paths << {:action => 'D', :path => "/#{path}", :revision => revid}
170 revision.paths << {:action => 'D', :path => "/#{path}", :revision => revid}
171 when 'renamed'
171 when 'renamed'
172 new_path = path.split('=>').last
172 new_path = path.split('=>').last
173 if new_path
173 if new_path
174 revision.paths << {:action => 'M', :path => "/#{new_path.strip}",
174 revision.paths << {:action => 'M', :path => "/#{new_path.strip}",
175 :revision => revid}
175 :revision => revid}
176 end
176 end
177 end
177 end
178 end
178 end
179 end
179 end
180 else
180 else
181 parsing = nil
181 parsing = nil
182 end
182 end
183 end
183 end
184 end
184 end
185 revisions << revision if revision
185 revisions << revision if revision
186 end
186 end
187 revisions
187 revisions
188 rescue ScmCommandAborted
188 rescue ScmCommandAborted
189 return nil
189 return nil
190 end
190 end
191
191
192 def diff(path, identifier_from, identifier_to=nil)
192 def diff(path, identifier_from, identifier_to=nil)
193 path ||= ''
193 path ||= ''
194 if identifier_to
194 if identifier_to
195 identifier_to = identifier_to.to_i
195 identifier_to = identifier_to.to_i
196 else
196 else
197 identifier_to = identifier_from.to_i - 1
197 identifier_to = identifier_from.to_i - 1
198 end
198 end
199 if identifier_from
199 if identifier_from
200 identifier_from = identifier_from.to_i
200 identifier_from = identifier_from.to_i
201 end
201 end
202 diff = []
202 diff = []
203 cmd_args = %w|diff|
203 cmd_args = %w|diff|
204 cmd_args << "-r#{identifier_to}..#{identifier_from}"
204 cmd_args << "-r#{identifier_to}..#{identifier_from}"
205 cmd_args << bzr_target(path)
205 cmd_args << bzr_target(path)
206 scm_cmd_no_raise(*cmd_args) do |io|
206 scm_cmd_no_raise(*cmd_args) do |io|
207 io.each_line do |line|
207 io.each_line do |line|
208 diff << line
208 diff << line
209 end
209 end
210 end
210 end
211 diff
211 diff
212 end
212 end
213
213
214 def cat(path, identifier=nil)
214 def cat(path, identifier=nil)
215 cat = nil
215 cat = nil
216 cmd_args = %w|cat|
216 cmd_args = %w|cat|
217 cmd_args << "-r#{identifier.to_i}" if identifier && identifier.to_i > 0
217 cmd_args << "-r#{identifier.to_i}" if identifier && identifier.to_i > 0
218 cmd_args << bzr_target(path)
218 cmd_args << bzr_target(path)
219 scm_cmd(*cmd_args) do |io|
219 scm_cmd(*cmd_args) do |io|
220 io.binmode
220 io.binmode
221 cat = io.read
221 cat = io.read
222 end
222 end
223 cat
223 cat
224 rescue ScmCommandAborted
224 rescue ScmCommandAborted
225 return nil
225 return nil
226 end
226 end
227
227
228 def annotate(path, identifier=nil)
228 def annotate(path, identifier=nil)
229 blame = Annotate.new
229 blame = Annotate.new
230 cmd_args = %w|annotate -q --all|
230 cmd_args = %w|annotate -q --all|
231 cmd_args << "-r#{identifier.to_i}" if identifier && identifier.to_i > 0
231 cmd_args << "-r#{identifier.to_i}" if identifier && identifier.to_i > 0
232 cmd_args << bzr_target(path)
232 cmd_args << bzr_target(path)
233 scm_cmd(*cmd_args) do |io|
233 scm_cmd(*cmd_args) do |io|
234 author = nil
234 author = nil
235 identifier = nil
235 identifier = nil
236 io.each_line do |line|
236 io.each_line do |line|
237 next unless line =~ %r{^(\d+) ([^|]+)\| (.*)$}
237 next unless line =~ %r{^(\d+) ([^|]+)\| (.*)$}
238 rev = $1
238 rev = $1
239 blame.add_line($3.rstrip,
239 blame.add_line($3.rstrip,
240 Revision.new(
240 Revision.new(
241 :identifier => rev,
241 :identifier => rev,
242 :revision => rev,
242 :revision => rev,
243 :author => $2.strip
243 :author => $2.strip
244 ))
244 ))
245 end
245 end
246 end
246 end
247 blame
247 blame
248 rescue ScmCommandAborted
248 rescue ScmCommandAborted
249 return nil
249 return nil
250 end
250 end
251
251
252 def self.branch_conf_path(path)
252 def self.branch_conf_path(path)
253 bcp = nil
253 bcp = nil
254 m = path.match(%r{^(.*[/\\])\.bzr.*$})
254 m = path.match(%r{^(.*[/\\])\.bzr.*$})
255 if m
255 if m
256 bcp = m[1]
256 bcp = m[1]
257 else
257 else
258 bcp = path
258 bcp = path
259 end
259 end
260 bcp.gsub!(%r{[\/\\]$}, "")
260 bcp.gsub!(%r{[\/\\]$}, "")
261 if bcp
261 if bcp
262 bcp = File.join(bcp, ".bzr", "branch", "branch.conf")
262 bcp = File.join(bcp, ".bzr", "branch", "branch.conf")
263 end
263 end
264 bcp
264 bcp
265 end
265 end
266
266
267 def append_revisions_only
267 def append_revisions_only
268 return @aro if ! @aro.nil?
268 return @aro if ! @aro.nil?
269 @aro = false
269 @aro = false
270 bcp = self.class.branch_conf_path(url)
270 bcp = self.class.branch_conf_path(url)
271 if bcp && File.exist?(bcp)
271 if bcp && File.exist?(bcp)
272 begin
272 begin
273 f = File::open(bcp, "r")
273 f = File::open(bcp, "r")
274 cnt = 0
274 cnt = 0
275 f.each_line do |line|
275 f.each_line do |line|
276 l = line.chomp.to_s
276 l = line.chomp.to_s
277 if l =~ /^\s*append_revisions_only\s*=\s*(\w+)\s*$/
277 if l =~ /^\s*append_revisions_only\s*=\s*(\w+)\s*$/
278 str_aro = $1
278 str_aro = $1
279 if str_aro.upcase == "TRUE"
279 if str_aro.upcase == "TRUE"
280 @aro = true
280 @aro = true
281 cnt += 1
281 cnt += 1
282 elsif str_aro.upcase == "FALSE"
282 elsif str_aro.upcase == "FALSE"
283 @aro = false
283 @aro = false
284 cnt += 1
284 cnt += 1
285 end
285 end
286 if cnt > 1
286 if cnt > 1
287 @aro = false
287 @aro = false
288 break
288 break
289 end
289 end
290 end
290 end
291 end
291 end
292 ensure
292 ensure
293 f.close
293 f.close
294 end
294 end
295 end
295 end
296 @aro
296 @aro
297 end
297 end
298
298
299 def scm_cmd(*args, &block)
299 def scm_cmd(*args, &block)
300 full_args = []
300 full_args = []
301 full_args += args
301 full_args += args
302 full_args_locale = []
302 full_args_locale = []
303 full_args.map do |e|
303 full_args.map do |e|
304 full_args_locale << scm_iconv(@path_encoding, 'UTF-8', e)
304 full_args_locale << scm_iconv(@path_encoding, 'UTF-8', e)
305 end
305 end
306 ret = shellout(
306 ret = shellout(
307 self.class.sq_bin + ' ' +
307 self.class.sq_bin + ' ' +
308 full_args_locale.map { |e| shell_quote e.to_s }.join(' '),
308 full_args_locale.map { |e| shell_quote e.to_s }.join(' '),
309 &block
309 &block
310 )
310 )
311 if $? && $?.exitstatus != 0
311 if $? && $?.exitstatus != 0
312 raise ScmCommandAborted, "bzr exited with non-zero status: #{$?.exitstatus}"
312 raise ScmCommandAborted, "bzr exited with non-zero status: #{$?.exitstatus}"
313 end
313 end
314 ret
314 ret
315 end
315 end
316 private :scm_cmd
316 private :scm_cmd
317
317
318 def scm_cmd_no_raise(*args, &block)
318 def scm_cmd_no_raise(*args, &block)
319 full_args = []
319 full_args = []
320 full_args += args
320 full_args += args
321 full_args_locale = []
321 full_args_locale = []
322 full_args.map do |e|
322 full_args.map do |e|
323 full_args_locale << scm_iconv(@path_encoding, 'UTF-8', e)
323 full_args_locale << scm_iconv(@path_encoding, 'UTF-8', e)
324 end
324 end
325 ret = shellout(
325 ret = shellout(
326 self.class.sq_bin + ' ' +
326 self.class.sq_bin + ' ' +
327 full_args_locale.map { |e| shell_quote e.to_s }.join(' '),
327 full_args_locale.map { |e| shell_quote e.to_s }.join(' '),
328 &block
328 &block
329 )
329 )
330 ret
330 ret
331 end
331 end
332 private :scm_cmd_no_raise
332 private :scm_cmd_no_raise
333
333
334 def bzr_target(path)
334 def bzr_target(path)
335 target(path, false)
335 target(path, false)
336 end
336 end
337 private :bzr_target
337 private :bzr_target
338 end
338 end
339 end
339 end
340 end
340 end
341 end
341 end
General Comments 0
You need to be logged in to leave comments. Login now