Mercurial > hg > soundsoftware-site
comparison lib/redmine/scm/adapters/mercurial_adapter.rb @ 120:cd2282d2aa55 cannam
Merge from the default branch. Note that this is not a valid SVN repository any more (use default, redmine-1.1 etc for SVN updates).
author | Chris Cannam |
---|---|
date | Thu, 13 Jan 2011 14:33:08 +0000 |
parents | b859cc0c4fa1 8661b858af72 |
children | eeebe205a056 |
comparison
equal
deleted
inserted
replaced
118:b859cc0c4fa1 | 120:cd2282d2aa55 |
---|---|
20 | 20 |
21 module Redmine | 21 module Redmine |
22 module Scm | 22 module Scm |
23 module Adapters | 23 module Adapters |
24 class MercurialAdapter < AbstractAdapter | 24 class MercurialAdapter < AbstractAdapter |
25 | 25 |
26 # Mercurial executable name | 26 # Mercurial executable name |
27 HG_BIN = "hg" | 27 HG_BIN = "hg" |
28 TEMPLATES_DIR = File.dirname(__FILE__) + "/mercurial" | 28 TEMPLATES_DIR = File.dirname(__FILE__) + "/mercurial" |
29 TEMPLATE_NAME = "hg-template" | 29 TEMPLATE_NAME = "hg-template" |
30 TEMPLATE_EXTENSION = "tmpl" | 30 TEMPLATE_EXTENSION = "tmpl" |
31 | 31 |
32 class << self | 32 class << self |
33 def client_version | 33 def client_version |
34 @@client_version ||= (hgversion || []) | 34 @@client_version ||= (hgversion || []) |
35 end | 35 end |
36 | 36 |
37 def hgversion | 37 def hgversion |
38 # The hg version is expressed either as a | 38 # The hg version is expressed either as a |
39 # release number (eg 0.9.5 or 1.0) or as a revision | 39 # release number (eg 0.9.5 or 1.0) or as a revision |
40 # id composed of 12 hexa characters. | 40 # id composed of 12 hexa characters. |
41 theversion = hgversion_from_command_line | 41 theversion = hgversion_from_command_line |
42 if m = theversion.match(%r{\A(.*?)((\d+\.)+\d+)}) | 42 if m = theversion.match(%r{\A(.*?)((\d+\.)+\d+)}) |
43 m[2].scan(%r{\d+}).collect(&:to_i) | 43 m[2].scan(%r{\d+}).collect(&:to_i) |
44 end | 44 end |
45 end | 45 end |
46 | 46 |
47 def hgversion_from_command_line | 47 def hgversion_from_command_line |
48 shellout("#{HG_BIN} --version") { |io| io.read }.to_s | 48 shellout("#{HG_BIN} --version") { |io| io.read }.to_s |
49 end | 49 end |
50 | 50 |
51 def template_path | 51 def template_path |
52 @@template_path ||= template_path_for(client_version) | 52 @@template_path ||= template_path_for(client_version) |
53 end | 53 end |
54 | 54 |
55 def template_path_for(version) | 55 def template_path_for(version) |
56 if ((version <=> [0,9,5]) > 0) || version.empty? | 56 if ((version <=> [0,9,5]) > 0) || version.empty? |
57 ver = "1.0" | 57 ver = "1.0" |
58 else | 58 else |
59 ver = "0.9.5" | 59 ver = "0.9.5" |
60 end | 60 end |
61 "#{TEMPLATES_DIR}/#{TEMPLATE_NAME}-#{ver}.#{TEMPLATE_EXTENSION}" | 61 "#{TEMPLATES_DIR}/#{TEMPLATE_NAME}-#{ver}.#{TEMPLATE_EXTENSION}" |
62 end | 62 end |
63 end | 63 end |
64 | 64 |
65 def info | 65 def info |
66 cmd = "#{HG_BIN} -R #{target('')} root" | 66 cmd = "#{HG_BIN} -R #{target('')} root" |
67 root_url = nil | 67 root_url = nil |
68 shellout(cmd) do |io| | 68 shellout(cmd) do |io| |
69 root_url = io.read | 69 root_url = io.read |
74 }) | 74 }) |
75 info | 75 info |
76 rescue CommandFailed | 76 rescue CommandFailed |
77 return nil | 77 return nil |
78 end | 78 end |
79 | 79 |
80 def entries(path=nil, identifier=nil) | 80 def entries(path=nil, identifier=nil) |
81 path ||= '' | 81 path ||= '' |
82 entries = Entries.new | 82 entries = Entries.new |
83 cmd = "#{HG_BIN} -R #{target('')} --cwd #{target('')} locate" | 83 cmd = "#{HG_BIN} -R #{target('')} --cwd #{target('')} locate" |
84 cmd << " -r " + shell_quote(identifier ? identifier.to_s : "tip") | 84 cmd << " -r #{hgrev(identifier)}" |
85 cmd << " " + shell_quote("path:#{path}") unless path.empty? | 85 cmd << " " + shell_quote("path:#{path}") unless path.empty? |
86 shellout(cmd) do |io| | 86 shellout(cmd) do |io| |
87 io.each_line do |line| | 87 io.each_line do |line| |
88 # HG uses antislashs as separator on Windows | 88 # HG uses antislashs as separator on Windows |
89 line = line.gsub(/\\/, "/") | 89 line = line.gsub(/\\/, "/") |
99 end | 99 end |
100 end | 100 end |
101 return nil if $? && $?.exitstatus != 0 | 101 return nil if $? && $?.exitstatus != 0 |
102 entries.sort_by_name | 102 entries.sort_by_name |
103 end | 103 end |
104 | 104 |
105 # Fetch the revisions by using a template file that | 105 # Fetch the revisions by using a template file that |
106 # makes Mercurial produce a xml output. | 106 # makes Mercurial produce a xml output. |
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 revisions = Revisions.new | 108 revisions = Revisions.new |
109 cmd = "#{HG_BIN} --debug --encoding utf8 -R #{target('')} log -C --style #{shell_quote self.class.template_path}" | 109 cmd = "#{HG_BIN} --debug --encoding utf8 -R #{target('')} log -C --style #{shell_quote self.class.template_path}" |
110 if identifier_from && identifier_to | 110 if identifier_from && identifier_to |
111 cmd << " -r #{identifier_from.to_i}:#{identifier_to.to_i}" | 111 cmd << " -r #{hgrev(identifier_from)}:#{hgrev(identifier_to)}" |
112 elsif identifier_from | 112 elsif identifier_from |
113 cmd << " -r #{identifier_from.to_i}:" | 113 cmd << " -r #{hgrev(identifier_from)}:" |
114 end | 114 end |
115 cmd << " --limit #{options[:limit].to_i}" if options[:limit] | 115 cmd << " --limit #{options[:limit].to_i}" if options[:limit] |
116 cmd << " #{shell_quote path}" unless path.blank? | 116 cmd << " #{shell_quote path}" unless path.blank? |
117 shellout(cmd) do |io| | 117 shellout(cmd) do |io| |
118 begin | 118 begin |
132 :from_path => from_path ? "/#{CGI.unescape(from_path)}" : nil, | 132 :from_path => from_path ? "/#{CGI.unescape(from_path)}" : nil, |
133 :from_revision => from_rev ? from_rev : nil | 133 :from_revision => from_rev ? from_rev : nil |
134 } | 134 } |
135 end | 135 end |
136 paths.sort! { |x,y| x[:path] <=> y[:path] } | 136 paths.sort! { |x,y| x[:path] <=> y[:path] } |
137 | 137 |
138 revisions << Revision.new({:identifier => logentry.attributes['revision'], | 138 revisions << Revision.new({:identifier => logentry.attributes['revision'], |
139 :scmid => logentry.attributes['node'], | 139 :scmid => logentry.attributes['node'], |
140 :author => (logentry.elements['author'] ? logentry.elements['author'].text : ""), | 140 :author => (logentry.elements['author'] ? logentry.elements['author'].text : ""), |
141 :time => Time.parse(logentry.elements['date'].text).localtime, | 141 :time => Time.parse(logentry.elements['date'].text).localtime, |
142 :message => logentry.elements['msg'].text, | 142 :message => logentry.elements['msg'].text, |
148 end | 148 end |
149 end | 149 end |
150 return nil if $? && $?.exitstatus != 0 | 150 return nil if $? && $?.exitstatus != 0 |
151 revisions | 151 revisions |
152 end | 152 end |
153 | 153 |
154 def diff(path, identifier_from, identifier_to=nil) | 154 def diff(path, identifier_from, identifier_to=nil) |
155 path ||= '' | 155 path ||= '' |
156 diff_args = '' | |
157 diff = [] | |
156 if identifier_to | 158 if identifier_to |
157 identifier_to = identifier_to.to_i | 159 diff_args = "-r #{hgrev(identifier_to)} -r #{hgrev(identifier_from)}" |
158 else | 160 else |
159 identifier_to = identifier_from.to_i - 1 | 161 if self.class.client_version_above?([1, 2]) |
160 end | 162 diff_args = "-c #{hgrev(identifier_from)}" |
161 if identifier_from | 163 else |
162 identifier_from = identifier_from.to_i | 164 return [] |
163 end | 165 end |
164 cmd = "#{HG_BIN} -R #{target('')} diff -r #{identifier_to} -r #{identifier_from} --nodates" | 166 end |
167 cmd = "#{HG_BIN} -R #{target('')} --config diff.git=false diff --nodates #{diff_args}" | |
165 cmd << " -I #{target(path)}" unless path.empty? | 168 cmd << " -I #{target(path)}" unless path.empty? |
166 diff = [] | |
167 shellout(cmd) do |io| | 169 shellout(cmd) do |io| |
168 io.each_line do |line| | 170 io.each_line do |line| |
169 diff << line | 171 diff << line |
170 end | 172 end |
171 end | 173 end |
172 return nil if $? && $?.exitstatus != 0 | 174 return nil if $? && $?.exitstatus != 0 |
173 diff | 175 diff |
174 end | 176 end |
175 | 177 |
176 def cat(path, identifier=nil) | 178 def cat(path, identifier=nil) |
177 cmd = "#{HG_BIN} -R #{target('')} cat" | 179 cmd = "#{HG_BIN} -R #{target('')} cat" |
178 cmd << " -r " + shell_quote(identifier ? identifier.to_s : "tip") | 180 cmd << " -r #{hgrev(identifier)}" |
179 cmd << " #{target(path)}" | 181 cmd << " #{target(path)}" |
180 cat = nil | 182 cat = nil |
181 shellout(cmd) do |io| | 183 shellout(cmd) do |io| |
182 io.binmode | 184 io.binmode |
183 cat = io.read | 185 cat = io.read |
184 end | 186 end |
185 return nil if $? && $?.exitstatus != 0 | 187 return nil if $? && $?.exitstatus != 0 |
186 cat | 188 cat |
187 end | 189 end |
188 | 190 |
189 def annotate(path, identifier=nil) | 191 def annotate(path, identifier=nil) |
190 path ||= '' | 192 path ||= '' |
191 cmd = "#{HG_BIN} -R #{target('')}" | 193 cmd = "#{HG_BIN} -R #{target('')}" |
192 cmd << " annotate -n -u" | 194 cmd << " annotate -ncu" |
193 cmd << " -r " + shell_quote(identifier ? identifier.to_s : "tip") | 195 cmd << " -r #{hgrev(identifier)}" |
194 cmd << " -r #{identifier.to_i}" if identifier | |
195 cmd << " #{target(path)}" | 196 cmd << " #{target(path)}" |
196 blame = Annotate.new | 197 blame = Annotate.new |
197 shellout(cmd) do |io| | 198 shellout(cmd) do |io| |
198 io.each_line do |line| | 199 io.each_line do |line| |
199 next unless line =~ %r{^([^:]+)\s(\d+):(.*)$} | 200 next unless line =~ %r{^([^:]+)\s(\d+)\s([0-9a-f]+):\s(.*)$} |
200 blame.add_line($3.rstrip, Revision.new(:identifier => $2.to_i, :author => $1.strip)) | 201 r = Revision.new(:author => $1.strip, :revision => $2, :scmid => $3, |
202 :identifier => $3) | |
203 blame.add_line($4.rstrip, r) | |
201 end | 204 end |
202 end | 205 end |
203 return nil if $? && $?.exitstatus != 0 | 206 return nil if $? && $?.exitstatus != 0 |
204 blame | 207 blame |
205 end | 208 end |
209 | |
210 class Revision < Redmine::Scm::Adapters::Revision | |
211 # Returns the readable identifier | |
212 def format_identifier | |
213 "#{revision}:#{scmid}" | |
214 end | |
215 end | |
216 | |
217 # Returns correct revision identifier | |
218 def hgrev(identifier) | |
219 shell_quote(identifier.blank? ? 'tip' : identifier.to_s) | |
220 end | |
221 private :hgrev | |
206 end | 222 end |
207 end | 223 end |
208 end | 224 end |
209 end | 225 end |