Chris@0
|
1 # redMine - project management software
|
Chris@0
|
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
|
Chris@0
|
3 #
|
Chris@0
|
4 # This program is free software; you can redistribute it and/or
|
Chris@0
|
5 # modify it under the terms of the GNU General Public License
|
Chris@0
|
6 # as published by the Free Software Foundation; either version 2
|
Chris@0
|
7 # of the License, or (at your option) any later version.
|
Chris@0
|
8 #
|
Chris@0
|
9 # This program is distributed in the hope that it will be useful,
|
Chris@0
|
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
|
Chris@0
|
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
Chris@0
|
12 # GNU General Public License for more details.
|
Chris@0
|
13 #
|
Chris@0
|
14 # You should have received a copy of the GNU General Public License
|
Chris@0
|
15 # along with this program; if not, write to the Free Software
|
Chris@0
|
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
Chris@0
|
17
|
Chris@0
|
18 require 'redmine/scm/adapters/abstract_adapter'
|
Chris@0
|
19
|
Chris@0
|
20 module Redmine
|
Chris@0
|
21 module Scm
|
Chris@0
|
22 module Adapters
|
Chris@0
|
23 class BazaarAdapter < AbstractAdapter
|
Chris@0
|
24
|
Chris@0
|
25 # Bazaar executable name
|
Chris@0
|
26 BZR_BIN = "bzr"
|
Chris@0
|
27
|
Chris@0
|
28 # Get info about the repository
|
Chris@0
|
29 def info
|
Chris@0
|
30 cmd = "#{BZR_BIN} revno #{target('')}"
|
Chris@0
|
31 info = nil
|
Chris@0
|
32 shellout(cmd) do |io|
|
Chris@0
|
33 if io.read =~ %r{^(\d+)\r?$}
|
Chris@0
|
34 info = Info.new({:root_url => url,
|
Chris@0
|
35 :lastrev => Revision.new({
|
Chris@0
|
36 :identifier => $1
|
Chris@0
|
37 })
|
Chris@0
|
38 })
|
Chris@0
|
39 end
|
Chris@0
|
40 end
|
Chris@0
|
41 return nil if $? && $?.exitstatus != 0
|
Chris@0
|
42 info
|
Chris@0
|
43 rescue CommandFailed
|
Chris@0
|
44 return nil
|
Chris@0
|
45 end
|
Chris@0
|
46
|
Chris@0
|
47 # Returns an Entries collection
|
Chris@0
|
48 # or nil if the given path doesn't exist in the repository
|
Chris@0
|
49 def entries(path=nil, identifier=nil)
|
Chris@0
|
50 path ||= ''
|
Chris@0
|
51 entries = Entries.new
|
Chris@0
|
52 cmd = "#{BZR_BIN} ls -v --show-ids"
|
Chris@0
|
53 identifier = -1 unless identifier && identifier.to_i > 0
|
Chris@0
|
54 cmd << " -r#{identifier.to_i}"
|
Chris@0
|
55 cmd << " #{target(path)}"
|
Chris@0
|
56 shellout(cmd) do |io|
|
Chris@0
|
57 prefix = "#{url}/#{path}".gsub('\\', '/')
|
Chris@0
|
58 logger.debug "PREFIX: #{prefix}"
|
Chris@0
|
59 re = %r{^V\s+(#{Regexp.escape(prefix)})?(\/?)([^\/]+)(\/?)\s+(\S+)\r?$}
|
Chris@0
|
60 io.each_line do |line|
|
Chris@0
|
61 next unless line =~ re
|
Chris@0
|
62 entries << Entry.new({:name => $3.strip,
|
Chris@0
|
63 :path => ((path.empty? ? "" : "#{path}/") + $3.strip),
|
Chris@0
|
64 :kind => ($4.blank? ? 'file' : 'dir'),
|
Chris@0
|
65 :size => nil,
|
Chris@0
|
66 :lastrev => Revision.new(:revision => $5.strip)
|
Chris@0
|
67 })
|
Chris@0
|
68 end
|
Chris@0
|
69 end
|
Chris@0
|
70 return nil if $? && $?.exitstatus != 0
|
Chris@0
|
71 logger.debug("Found #{entries.size} entries in the repository for #{target(path)}") if logger && logger.debug?
|
Chris@0
|
72 entries.sort_by_name
|
Chris@0
|
73 end
|
Chris@0
|
74
|
Chris@0
|
75 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
|
Chris@0
|
76 path ||= ''
|
Chris@117
|
77 identifier_from = (identifier_from and identifier_from.to_i > 0) ? identifier_from.to_i : 'last:1'
|
Chris@117
|
78 identifier_to = (identifier_to and identifier_to.to_i > 0) ? identifier_to.to_i : 1
|
Chris@0
|
79 revisions = Revisions.new
|
Chris@117
|
80 cmd = "#{BZR_BIN} log -v --show-ids -r#{identifier_to}..#{identifier_from} #{target(path)}"
|
Chris@0
|
81 shellout(cmd) do |io|
|
Chris@0
|
82 revision = nil
|
Chris@0
|
83 parsing = nil
|
Chris@0
|
84 io.each_line do |line|
|
Chris@0
|
85 if line =~ /^----/
|
Chris@0
|
86 revisions << revision if revision
|
Chris@0
|
87 revision = Revision.new(:paths => [], :message => '')
|
Chris@0
|
88 parsing = nil
|
Chris@0
|
89 else
|
Chris@0
|
90 next unless revision
|
Chris@0
|
91
|
Chris@0
|
92 if line =~ /^revno: (\d+)($|\s\[merge\]$)/
|
Chris@0
|
93 revision.identifier = $1.to_i
|
Chris@0
|
94 elsif line =~ /^committer: (.+)$/
|
Chris@0
|
95 revision.author = $1.strip
|
Chris@0
|
96 elsif line =~ /^revision-id:(.+)$/
|
Chris@0
|
97 revision.scmid = $1.strip
|
Chris@0
|
98 elsif line =~ /^timestamp: (.+)$/
|
Chris@0
|
99 revision.time = Time.parse($1).localtime
|
Chris@0
|
100 elsif line =~ /^ -----/
|
Chris@0
|
101 # partial revisions
|
Chris@0
|
102 parsing = nil unless parsing == 'message'
|
Chris@0
|
103 elsif line =~ /^(message|added|modified|removed|renamed):/
|
Chris@0
|
104 parsing = $1
|
Chris@0
|
105 elsif line =~ /^ (.*)$/
|
Chris@0
|
106 if parsing == 'message'
|
Chris@0
|
107 revision.message << "#{$1}\n"
|
Chris@0
|
108 else
|
Chris@0
|
109 if $1 =~ /^(.*)\s+(\S+)$/
|
Chris@0
|
110 path = $1.strip
|
Chris@0
|
111 revid = $2
|
Chris@0
|
112 case parsing
|
Chris@0
|
113 when 'added'
|
Chris@0
|
114 revision.paths << {:action => 'A', :path => "/#{path}", :revision => revid}
|
Chris@0
|
115 when 'modified'
|
Chris@0
|
116 revision.paths << {:action => 'M', :path => "/#{path}", :revision => revid}
|
Chris@0
|
117 when 'removed'
|
Chris@0
|
118 revision.paths << {:action => 'D', :path => "/#{path}", :revision => revid}
|
Chris@0
|
119 when 'renamed'
|
Chris@0
|
120 new_path = path.split('=>').last
|
Chris@0
|
121 revision.paths << {:action => 'M', :path => "/#{new_path.strip}", :revision => revid} if new_path
|
Chris@0
|
122 end
|
Chris@0
|
123 end
|
Chris@0
|
124 end
|
Chris@0
|
125 else
|
Chris@0
|
126 parsing = nil
|
Chris@0
|
127 end
|
Chris@0
|
128 end
|
Chris@0
|
129 end
|
Chris@0
|
130 revisions << revision if revision
|
Chris@0
|
131 end
|
Chris@0
|
132 return nil if $? && $?.exitstatus != 0
|
Chris@0
|
133 revisions
|
Chris@0
|
134 end
|
Chris@0
|
135
|
Chris@0
|
136 def diff(path, identifier_from, identifier_to=nil)
|
Chris@0
|
137 path ||= ''
|
Chris@0
|
138 if identifier_to
|
Chris@0
|
139 identifier_to = identifier_to.to_i
|
Chris@0
|
140 else
|
Chris@0
|
141 identifier_to = identifier_from.to_i - 1
|
Chris@0
|
142 end
|
Chris@117
|
143 if identifier_from
|
Chris@117
|
144 identifier_from = identifier_from.to_i
|
Chris@117
|
145 end
|
Chris@0
|
146 cmd = "#{BZR_BIN} diff -r#{identifier_to}..#{identifier_from} #{target(path)}"
|
Chris@0
|
147 diff = []
|
Chris@0
|
148 shellout(cmd) do |io|
|
Chris@0
|
149 io.each_line do |line|
|
Chris@0
|
150 diff << line
|
Chris@0
|
151 end
|
Chris@0
|
152 end
|
Chris@0
|
153 #return nil if $? && $?.exitstatus != 0
|
Chris@0
|
154 diff
|
Chris@0
|
155 end
|
Chris@0
|
156
|
Chris@0
|
157 def cat(path, identifier=nil)
|
Chris@0
|
158 cmd = "#{BZR_BIN} cat"
|
Chris@0
|
159 cmd << " -r#{identifier.to_i}" if identifier && identifier.to_i > 0
|
Chris@0
|
160 cmd << " #{target(path)}"
|
Chris@0
|
161 cat = nil
|
Chris@0
|
162 shellout(cmd) do |io|
|
Chris@0
|
163 io.binmode
|
Chris@0
|
164 cat = io.read
|
Chris@0
|
165 end
|
Chris@0
|
166 return nil if $? && $?.exitstatus != 0
|
Chris@0
|
167 cat
|
Chris@0
|
168 end
|
Chris@0
|
169
|
Chris@0
|
170 def annotate(path, identifier=nil)
|
Chris@0
|
171 cmd = "#{BZR_BIN} annotate --all"
|
Chris@0
|
172 cmd << " -r#{identifier.to_i}" if identifier && identifier.to_i > 0
|
Chris@0
|
173 cmd << " #{target(path)}"
|
Chris@0
|
174 blame = Annotate.new
|
Chris@0
|
175 shellout(cmd) do |io|
|
Chris@0
|
176 author = nil
|
Chris@0
|
177 identifier = nil
|
Chris@0
|
178 io.each_line do |line|
|
Chris@0
|
179 next unless line =~ %r{^(\d+) ([^|]+)\| (.*)$}
|
Chris@0
|
180 blame.add_line($3.rstrip, Revision.new(:identifier => $1.to_i, :author => $2.strip))
|
Chris@0
|
181 end
|
Chris@0
|
182 end
|
Chris@0
|
183 return nil if $? && $?.exitstatus != 0
|
Chris@0
|
184 blame
|
Chris@0
|
185 end
|
Chris@0
|
186 end
|
Chris@0
|
187 end
|
Chris@0
|
188 end
|
Chris@0
|
189 end
|