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