To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / .svn / pristine / 39 / 39e07ef2d5632f8e7ca245cda7735bd6e7674f6d.svn-base @ 1298:4f746d8966dd

History | View | Annotate | Download (11.5 KB)

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