##// END OF EJS Templates
Merged r1500 from trunk....
Jean-Philippe Lang -
r1516:6f6f1f1ba339
parent child
Show More
@@ -1,184 +1,187
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 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 require 'rexml/document'
19 require 'rexml/document'
20
20
21 module Redmine
21 module Redmine
22 module Scm
22 module Scm
23 module Adapters
23 module Adapters
24 class SubversionAdapter < AbstractAdapter
24 class SubversionAdapter < AbstractAdapter
25
25
26 # SVN executable name
26 # SVN executable name
27 SVN_BIN = "svn"
27 SVN_BIN = "svn"
28
28
29 # Get info about the svn repository
29 # Get info about the svn repository
30 def info
30 def info
31 cmd = "#{SVN_BIN} info --xml #{target('')}"
31 cmd = "#{SVN_BIN} info --xml #{target('')}"
32 cmd << credentials_string
32 cmd << credentials_string
33 info = nil
33 info = nil
34 shellout(cmd) do |io|
34 shellout(cmd) do |io|
35 begin
35 begin
36 doc = REXML::Document.new(io)
36 doc = REXML::Document.new(io)
37 #root_url = doc.elements["info/entry/repository/root"].text
37 #root_url = doc.elements["info/entry/repository/root"].text
38 info = Info.new({:root_url => doc.elements["info/entry/repository/root"].text,
38 info = Info.new({:root_url => doc.elements["info/entry/repository/root"].text,
39 :lastrev => Revision.new({
39 :lastrev => Revision.new({
40 :identifier => doc.elements["info/entry/commit"].attributes['revision'],
40 :identifier => doc.elements["info/entry/commit"].attributes['revision'],
41 :time => Time.parse(doc.elements["info/entry/commit/date"].text).localtime,
41 :time => Time.parse(doc.elements["info/entry/commit/date"].text).localtime,
42 :author => (doc.elements["info/entry/commit/author"] ? doc.elements["info/entry/commit/author"].text : "")
42 :author => (doc.elements["info/entry/commit/author"] ? doc.elements["info/entry/commit/author"].text : "")
43 })
43 })
44 })
44 })
45 rescue
45 rescue
46 end
46 end
47 end
47 end
48 return nil if $? && $?.exitstatus != 0
48 return nil if $? && $?.exitstatus != 0
49 info
49 info
50 rescue CommandFailed
50 rescue CommandFailed
51 return nil
51 return nil
52 end
52 end
53
53
54 # Returns an Entries collection
54 # Returns an Entries collection
55 # or nil if the given path doesn't exist in the repository
55 # or nil if the given path doesn't exist in the repository
56 def entries(path=nil, identifier=nil)
56 def entries(path=nil, identifier=nil)
57 path ||= ''
57 path ||= ''
58 identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD"
58 identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD"
59 entries = Entries.new
59 entries = Entries.new
60 cmd = "#{SVN_BIN} list --xml #{target(path)}@#{identifier}"
60 cmd = "#{SVN_BIN} list --xml #{target(path)}@#{identifier}"
61 cmd << credentials_string
61 cmd << credentials_string
62 shellout(cmd) do |io|
62 shellout(cmd) do |io|
63 output = io.read
63 output = io.read
64 begin
64 begin
65 doc = REXML::Document.new(output)
65 doc = REXML::Document.new(output)
66 doc.elements.each("lists/list/entry") do |entry|
66 doc.elements.each("lists/list/entry") do |entry|
67 # Skip directory if there is no commit date (usually that
68 # means that we don't have read access to it)
69 next if entry.attributes['kind'] == 'dir' && entry.elements['commit'].elements['date'].nil?
67 entries << Entry.new({:name => entry.elements['name'].text,
70 entries << Entry.new({:name => entry.elements['name'].text,
68 :path => ((path.empty? ? "" : "#{path}/") + entry.elements['name'].text),
71 :path => ((path.empty? ? "" : "#{path}/") + entry.elements['name'].text),
69 :kind => entry.attributes['kind'],
72 :kind => entry.attributes['kind'],
70 :size => (entry.elements['size'] and entry.elements['size'].text).to_i,
73 :size => (entry.elements['size'] and entry.elements['size'].text).to_i,
71 :lastrev => Revision.new({
74 :lastrev => Revision.new({
72 :identifier => entry.elements['commit'].attributes['revision'],
75 :identifier => entry.elements['commit'].attributes['revision'],
73 :time => Time.parse(entry.elements['commit'].elements['date'].text).localtime,
76 :time => Time.parse(entry.elements['commit'].elements['date'].text).localtime,
74 :author => (entry.elements['commit'].elements['author'] ? entry.elements['commit'].elements['author'].text : "")
77 :author => (entry.elements['commit'].elements['author'] ? entry.elements['commit'].elements['author'].text : "")
75 })
78 })
76 })
79 })
77 end
80 end
78 rescue Exception => e
81 rescue Exception => e
79 logger.error("Error parsing svn output: #{e.message}")
82 logger.error("Error parsing svn output: #{e.message}")
80 logger.error("Output was:\n #{output}")
83 logger.error("Output was:\n #{output}")
81 end
84 end
82 end
85 end
83 return nil if $? && $?.exitstatus != 0
86 return nil if $? && $?.exitstatus != 0
84 logger.debug("Found #{entries.size} entries in the repository for #{target(path)}") if logger && logger.debug?
87 logger.debug("Found #{entries.size} entries in the repository for #{target(path)}") if logger && logger.debug?
85 entries.sort_by_name
88 entries.sort_by_name
86 end
89 end
87
90
88 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
91 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
89 path ||= ''
92 path ||= ''
90 identifier_from = (identifier_from and identifier_from.to_i > 0) ? identifier_from.to_i : "HEAD"
93 identifier_from = (identifier_from and identifier_from.to_i > 0) ? identifier_from.to_i : "HEAD"
91 identifier_to = (identifier_to and identifier_to.to_i > 0) ? identifier_to.to_i : 1
94 identifier_to = (identifier_to and identifier_to.to_i > 0) ? identifier_to.to_i : 1
92 revisions = Revisions.new
95 revisions = Revisions.new
93 cmd = "#{SVN_BIN} log --xml -r #{identifier_from}:#{identifier_to}"
96 cmd = "#{SVN_BIN} log --xml -r #{identifier_from}:#{identifier_to}"
94 cmd << credentials_string
97 cmd << credentials_string
95 cmd << " --verbose " if options[:with_paths]
98 cmd << " --verbose " if options[:with_paths]
96 cmd << ' ' + target(path)
99 cmd << ' ' + target(path)
97 shellout(cmd) do |io|
100 shellout(cmd) do |io|
98 begin
101 begin
99 doc = REXML::Document.new(io)
102 doc = REXML::Document.new(io)
100 doc.elements.each("log/logentry") do |logentry|
103 doc.elements.each("log/logentry") do |logentry|
101 paths = []
104 paths = []
102 logentry.elements.each("paths/path") do |path|
105 logentry.elements.each("paths/path") do |path|
103 paths << {:action => path.attributes['action'],
106 paths << {:action => path.attributes['action'],
104 :path => path.text,
107 :path => path.text,
105 :from_path => path.attributes['copyfrom-path'],
108 :from_path => path.attributes['copyfrom-path'],
106 :from_revision => path.attributes['copyfrom-rev']
109 :from_revision => path.attributes['copyfrom-rev']
107 }
110 }
108 end
111 end
109 paths.sort! { |x,y| x[:path] <=> y[:path] }
112 paths.sort! { |x,y| x[:path] <=> y[:path] }
110
113
111 revisions << Revision.new({:identifier => logentry.attributes['revision'],
114 revisions << Revision.new({:identifier => logentry.attributes['revision'],
112 :author => (logentry.elements['author'] ? logentry.elements['author'].text : ""),
115 :author => (logentry.elements['author'] ? logentry.elements['author'].text : ""),
113 :time => Time.parse(logentry.elements['date'].text).localtime,
116 :time => Time.parse(logentry.elements['date'].text).localtime,
114 :message => logentry.elements['msg'].text,
117 :message => logentry.elements['msg'].text,
115 :paths => paths
118 :paths => paths
116 })
119 })
117 end
120 end
118 rescue
121 rescue
119 end
122 end
120 end
123 end
121 return nil if $? && $?.exitstatus != 0
124 return nil if $? && $?.exitstatus != 0
122 revisions
125 revisions
123 end
126 end
124
127
125 def diff(path, identifier_from, identifier_to=nil, type="inline")
128 def diff(path, identifier_from, identifier_to=nil, type="inline")
126 path ||= ''
129 path ||= ''
127 identifier_from = (identifier_from and identifier_from.to_i > 0) ? identifier_from.to_i : ''
130 identifier_from = (identifier_from and identifier_from.to_i > 0) ? identifier_from.to_i : ''
128 identifier_to = (identifier_to and identifier_to.to_i > 0) ? identifier_to.to_i : (identifier_from.to_i - 1)
131 identifier_to = (identifier_to and identifier_to.to_i > 0) ? identifier_to.to_i : (identifier_from.to_i - 1)
129
132
130 cmd = "#{SVN_BIN} diff -r "
133 cmd = "#{SVN_BIN} diff -r "
131 cmd << "#{identifier_to}:"
134 cmd << "#{identifier_to}:"
132 cmd << "#{identifier_from}"
135 cmd << "#{identifier_from}"
133 cmd << " #{target(path)}@#{identifier_from}"
136 cmd << " #{target(path)}@#{identifier_from}"
134 cmd << credentials_string
137 cmd << credentials_string
135 diff = []
138 diff = []
136 shellout(cmd) do |io|
139 shellout(cmd) do |io|
137 io.each_line do |line|
140 io.each_line do |line|
138 diff << line
141 diff << line
139 end
142 end
140 end
143 end
141 return nil if $? && $?.exitstatus != 0
144 return nil if $? && $?.exitstatus != 0
142 DiffTableList.new diff, type
145 DiffTableList.new diff, type
143 end
146 end
144
147
145 def cat(path, identifier=nil)
148 def cat(path, identifier=nil)
146 identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD"
149 identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD"
147 cmd = "#{SVN_BIN} cat #{target(path)}@#{identifier}"
150 cmd = "#{SVN_BIN} cat #{target(path)}@#{identifier}"
148 cmd << credentials_string
151 cmd << credentials_string
149 cat = nil
152 cat = nil
150 shellout(cmd) do |io|
153 shellout(cmd) do |io|
151 io.binmode
154 io.binmode
152 cat = io.read
155 cat = io.read
153 end
156 end
154 return nil if $? && $?.exitstatus != 0
157 return nil if $? && $?.exitstatus != 0
155 cat
158 cat
156 end
159 end
157
160
158 def annotate(path, identifier=nil)
161 def annotate(path, identifier=nil)
159 identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD"
162 identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD"
160 cmd = "#{SVN_BIN} blame #{target(path)}@#{identifier}"
163 cmd = "#{SVN_BIN} blame #{target(path)}@#{identifier}"
161 cmd << credentials_string
164 cmd << credentials_string
162 blame = Annotate.new
165 blame = Annotate.new
163 shellout(cmd) do |io|
166 shellout(cmd) do |io|
164 io.each_line do |line|
167 io.each_line do |line|
165 next unless line =~ %r{^\s*(\d+)\s*(\S+)\s(.*)$}
168 next unless line =~ %r{^\s*(\d+)\s*(\S+)\s(.*)$}
166 blame.add_line($3.rstrip, Revision.new(:identifier => $1.to_i, :author => $2.strip))
169 blame.add_line($3.rstrip, Revision.new(:identifier => $1.to_i, :author => $2.strip))
167 end
170 end
168 end
171 end
169 return nil if $? && $?.exitstatus != 0
172 return nil if $? && $?.exitstatus != 0
170 blame
173 blame
171 end
174 end
172
175
173 private
176 private
174
177
175 def credentials_string
178 def credentials_string
176 str = ''
179 str = ''
177 str << " --username #{shell_quote(@login)}" unless @login.blank?
180 str << " --username #{shell_quote(@login)}" unless @login.blank?
178 str << " --password #{shell_quote(@password)}" unless @login.blank? || @password.blank?
181 str << " --password #{shell_quote(@password)}" unless @login.blank? || @password.blank?
179 str
182 str
180 end
183 end
181 end
184 end
182 end
185 end
183 end
186 end
184 end
187 end
General Comments 0
You need to be logged in to leave comments. Login now