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