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