##// END OF EJS Templates
scm: git: use default branch from HEAD (#10207)...
Toshi MARUYAMA -
r8736:2fa19441a7ff
parent child
Show More
@@ -1,399 +1,406
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require 'redmine/scm/adapters/abstract_adapter'
19 19
20 20 module Redmine
21 21 module Scm
22 22 module Adapters
23 23 class GitAdapter < AbstractAdapter
24 24
25 25 # Git executable name
26 26 GIT_BIN = Redmine::Configuration['scm_git_command'] || "git"
27 27
28 class GitBranch < Branch
29 attr_accessor :is_default
30 end
31
28 32 class << self
29 33 def client_command
30 34 @@bin ||= GIT_BIN
31 35 end
32 36
33 37 def sq_bin
34 38 @@sq_bin ||= shell_quote_command
35 39 end
36 40
37 41 def client_version
38 42 @@client_version ||= (scm_command_version || [])
39 43 end
40 44
41 45 def client_available
42 46 !client_version.empty?
43 47 end
44 48
45 49 def scm_command_version
46 50 scm_version = scm_version_from_command_line.dup
47 51 if scm_version.respond_to?(:force_encoding)
48 52 scm_version.force_encoding('ASCII-8BIT')
49 53 end
50 54 if m = scm_version.match(%r{\A(.*?)((\d+\.)+\d+)})
51 55 m[2].scan(%r{\d+}).collect(&:to_i)
52 56 end
53 57 end
54 58
55 59 def scm_version_from_command_line
56 60 shellout("#{sq_bin} --version --no-color") { |io| io.read }.to_s
57 61 end
58 62 end
59 63
60 64 def initialize(url, root_url=nil, login=nil, password=nil, path_encoding=nil)
61 65 super
62 66 @path_encoding = path_encoding.blank? ? 'UTF-8' : path_encoding
63 67 end
64 68
65 69 def path_encoding
66 70 @path_encoding
67 71 end
68 72
69 73 def info
70 74 begin
71 75 Info.new(:root_url => url, :lastrev => lastrev('',nil))
72 76 rescue
73 77 nil
74 78 end
75 79 end
76 80
77 81 def branches
78 82 return @branches if @branches
79 83 @branches = []
80 84 cmd_args = %w|branch --no-color --verbose --no-abbrev|
81 85 scm_cmd(*cmd_args) do |io|
82 86 io.each_line do |line|
83 branch_rev = line.match('\s*\*?\s*(.*?)\s*([0-9a-f]{40}).*$')
84 bran = Branch.new(branch_rev[1])
85 bran.revision = branch_rev[2]
86 bran.scmid = branch_rev[2]
87 branch_rev = line.match('\s*(\*?)\s*(.*?)\s*([0-9a-f]{40}).*$')
88 bran = GitBranch.new(branch_rev[2])
89 bran.revision = branch_rev[3]
90 bran.scmid = branch_rev[3]
91 bran.is_default = ( branch_rev[1] == '*' )
87 92 @branches << bran
88 93 end
89 94 end
90 95 @branches.sort!
91 96 rescue ScmCommandAborted
92 97 nil
93 98 end
94 99
95 100 def tags
96 101 return @tags if @tags
97 102 cmd_args = %w|tag|
98 103 scm_cmd(*cmd_args) do |io|
99 104 @tags = io.readlines.sort!.map{|t| t.strip}
100 105 end
101 106 rescue ScmCommandAborted
102 107 nil
103 108 end
104 109
105 110 def default_branch
106 111 bras = self.branches
107 112 return nil if bras.nil?
113 default_bras = bras.select{|x| x.is_default == true}
114 return default_bras.first if ! default_bras.empty?
108 115 bras.include?('master') ? 'master' : bras.first
109 116 end
110 117
111 118 def entry(path=nil, identifier=nil)
112 119 parts = path.to_s.split(%r{[\/\\]}).select {|n| !n.blank?}
113 120 search_path = parts[0..-2].join('/')
114 121 search_name = parts[-1]
115 122 if search_path.blank? && search_name.blank?
116 123 # Root entry
117 124 Entry.new(:path => '', :kind => 'dir')
118 125 else
119 126 # Search for the entry in the parent directory
120 127 es = entries(search_path, identifier,
121 128 options = {:report_last_commit => false})
122 129 es ? es.detect {|e| e.name == search_name} : nil
123 130 end
124 131 end
125 132
126 133 def entries(path=nil, identifier=nil, options={})
127 134 path ||= ''
128 135 p = scm_iconv(@path_encoding, 'UTF-8', path)
129 136 entries = Entries.new
130 137 cmd_args = %w|ls-tree -l|
131 138 cmd_args << "HEAD:#{p}" if identifier.nil?
132 139 cmd_args << "#{identifier}:#{p}" if identifier
133 140 scm_cmd(*cmd_args) do |io|
134 141 io.each_line do |line|
135 142 e = line.chomp.to_s
136 143 if e =~ /^\d+\s+(\w+)\s+([0-9a-f]{40})\s+([0-9-]+)\t(.+)$/
137 144 type = $1
138 145 sha = $2
139 146 size = $3
140 147 name = $4
141 148 if name.respond_to?(:force_encoding)
142 149 name.force_encoding(@path_encoding)
143 150 end
144 151 full_path = p.empty? ? name : "#{p}/#{name}"
145 152 n = scm_iconv('UTF-8', @path_encoding, name)
146 153 full_p = scm_iconv('UTF-8', @path_encoding, full_path)
147 154 entries << Entry.new({:name => n,
148 155 :path => full_p,
149 156 :kind => (type == "tree") ? 'dir' : 'file',
150 157 :size => (type == "tree") ? nil : size,
151 158 :lastrev => options[:report_last_commit] ?
152 159 lastrev(full_path, identifier) : Revision.new
153 160 }) unless entries.detect{|entry| entry.name == name}
154 161 end
155 162 end
156 163 end
157 164 entries.sort_by_name
158 165 rescue ScmCommandAborted
159 166 nil
160 167 end
161 168
162 169 def lastrev(path, rev)
163 170 return nil if path.nil?
164 171 cmd_args = %w|log --no-color --encoding=UTF-8 --date=iso --pretty=fuller --no-merges -n 1|
165 172 cmd_args << rev if rev
166 173 cmd_args << "--" << path unless path.empty?
167 174 lines = []
168 175 scm_cmd(*cmd_args) { |io| lines = io.readlines }
169 176 begin
170 177 id = lines[0].split[1]
171 178 author = lines[1].match('Author:\s+(.*)$')[1]
172 179 time = Time.parse(lines[4].match('CommitDate:\s+(.*)$')[1])
173 180
174 181 Revision.new({
175 182 :identifier => id,
176 183 :scmid => id,
177 184 :author => author,
178 185 :time => time,
179 186 :message => nil,
180 187 :paths => nil
181 188 })
182 189 rescue NoMethodError => e
183 190 logger.error("The revision '#{path}' has a wrong format")
184 191 return nil
185 192 end
186 193 rescue ScmCommandAborted
187 194 nil
188 195 end
189 196
190 197 def revisions(path, identifier_from, identifier_to, options={})
191 198 revs = Revisions.new
192 199 cmd_args = %w|log --no-color --encoding=UTF-8 --raw --date=iso --pretty=fuller --parents|
193 200 cmd_args << "--reverse" if options[:reverse]
194 201 cmd_args << "-n" << "#{options[:limit].to_i}" if options[:limit]
195 202 from_to = ""
196 203 if identifier_from || identifier_to
197 204 from_to << "#{identifier_from}.." if identifier_from
198 205 from_to << "#{identifier_to}" if identifier_to
199 206 cmd_args << from_to if !from_to.empty?
200 207 else
201 208 cmd_args += options[:includes] unless options[:includes].blank?
202 209 unless options[:excludes].blank?
203 210 cmd_args << "--not"
204 211 cmd_args += options[:excludes]
205 212 end
206 213 end
207 214 cmd_args << "--" << scm_iconv(@path_encoding, 'UTF-8', path) if path && !path.empty?
208 215
209 216 scm_cmd *cmd_args do |io|
210 217 files=[]
211 218 changeset = {}
212 219 parsing_descr = 0 #0: not parsing desc or files, 1: parsing desc, 2: parsing files
213 220
214 221 io.each_line do |line|
215 222 if line =~ /^commit ([0-9a-f]{40})(( [0-9a-f]{40})*)$/
216 223 key = "commit"
217 224 value = $1
218 225 parents_str = $2
219 226 if (parsing_descr == 1 || parsing_descr == 2)
220 227 parsing_descr = 0
221 228 revision = Revision.new({
222 229 :identifier => changeset[:commit],
223 230 :scmid => changeset[:commit],
224 231 :author => changeset[:author],
225 232 :time => Time.parse(changeset[:date]),
226 233 :message => changeset[:description],
227 234 :paths => files,
228 235 :parents => changeset[:parents]
229 236 })
230 237 if block_given?
231 238 yield revision
232 239 else
233 240 revs << revision
234 241 end
235 242 changeset = {}
236 243 files = []
237 244 end
238 245 changeset[:commit] = $1
239 246 unless parents_str.nil? or parents_str == ""
240 247 changeset[:parents] = parents_str.strip.split(' ')
241 248 end
242 249 elsif (parsing_descr == 0) && line =~ /^(\w+):\s*(.*)$/
243 250 key = $1
244 251 value = $2
245 252 if key == "Author"
246 253 changeset[:author] = value
247 254 elsif key == "CommitDate"
248 255 changeset[:date] = value
249 256 end
250 257 elsif (parsing_descr == 0) && line.chomp.to_s == ""
251 258 parsing_descr = 1
252 259 changeset[:description] = ""
253 260 elsif (parsing_descr == 1 || parsing_descr == 2) \
254 261 && line =~ /^:\d+\s+\d+\s+[0-9a-f.]+\s+[0-9a-f.]+\s+(\w)\t(.+)$/
255 262 parsing_descr = 2
256 263 fileaction = $1
257 264 filepath = $2
258 265 p = scm_iconv('UTF-8', @path_encoding, filepath)
259 266 files << {:action => fileaction, :path => p}
260 267 elsif (parsing_descr == 1 || parsing_descr == 2) \
261 268 && line =~ /^:\d+\s+\d+\s+[0-9a-f.]+\s+[0-9a-f.]+\s+(\w)\d+\s+(\S+)\t(.+)$/
262 269 parsing_descr = 2
263 270 fileaction = $1
264 271 filepath = $3
265 272 p = scm_iconv('UTF-8', @path_encoding, filepath)
266 273 files << {:action => fileaction, :path => p}
267 274 elsif (parsing_descr == 1) && line.chomp.to_s == ""
268 275 parsing_descr = 2
269 276 elsif (parsing_descr == 1)
270 277 changeset[:description] << line[4..-1]
271 278 end
272 279 end
273 280
274 281 if changeset[:commit]
275 282 revision = Revision.new({
276 283 :identifier => changeset[:commit],
277 284 :scmid => changeset[:commit],
278 285 :author => changeset[:author],
279 286 :time => Time.parse(changeset[:date]),
280 287 :message => changeset[:description],
281 288 :paths => files,
282 289 :parents => changeset[:parents]
283 290 })
284 291 if block_given?
285 292 yield revision
286 293 else
287 294 revs << revision
288 295 end
289 296 end
290 297 end
291 298 revs
292 299 rescue ScmCommandAborted => e
293 300 err_msg = "git log error: #{e.message}"
294 301 logger.error(err_msg)
295 302 if block_given?
296 303 raise CommandFailed, err_msg
297 304 else
298 305 revs
299 306 end
300 307 end
301 308
302 309 def diff(path, identifier_from, identifier_to=nil)
303 310 path ||= ''
304 311 cmd_args = []
305 312 if identifier_to
306 313 cmd_args << "diff" << "--no-color" << identifier_to << identifier_from
307 314 else
308 315 cmd_args << "show" << "--no-color" << identifier_from
309 316 end
310 317 cmd_args << "--" << scm_iconv(@path_encoding, 'UTF-8', path) unless path.empty?
311 318 diff = []
312 319 scm_cmd *cmd_args do |io|
313 320 io.each_line do |line|
314 321 diff << line
315 322 end
316 323 end
317 324 diff
318 325 rescue ScmCommandAborted
319 326 nil
320 327 end
321 328
322 329 def annotate(path, identifier=nil)
323 330 identifier = 'HEAD' if identifier.blank?
324 331 cmd_args = %w|blame|
325 332 cmd_args << "-p" << identifier << "--" << scm_iconv(@path_encoding, 'UTF-8', path)
326 333 blame = Annotate.new
327 334 content = nil
328 335 scm_cmd(*cmd_args) { |io| io.binmode; content = io.read }
329 336 # git annotates binary files
330 337 return nil if content.is_binary_data?
331 338 identifier = ''
332 339 # git shows commit author on the first occurrence only
333 340 authors_by_commit = {}
334 341 content.split("\n").each do |line|
335 342 if line =~ /^([0-9a-f]{39,40})\s.*/
336 343 identifier = $1
337 344 elsif line =~ /^author (.+)/
338 345 authors_by_commit[identifier] = $1.strip
339 346 elsif line =~ /^\t(.*)/
340 347 blame.add_line($1, Revision.new(
341 348 :identifier => identifier,
342 349 :revision => identifier,
343 350 :scmid => identifier,
344 351 :author => authors_by_commit[identifier]
345 352 ))
346 353 identifier = ''
347 354 author = ''
348 355 end
349 356 end
350 357 blame
351 358 rescue ScmCommandAborted
352 359 nil
353 360 end
354 361
355 362 def cat(path, identifier=nil)
356 363 if identifier.nil?
357 364 identifier = 'HEAD'
358 365 end
359 366 cmd_args = %w|show --no-color|
360 367 cmd_args << "#{identifier}:#{scm_iconv(@path_encoding, 'UTF-8', path)}"
361 368 cat = nil
362 369 scm_cmd(*cmd_args) do |io|
363 370 io.binmode
364 371 cat = io.read
365 372 end
366 373 cat
367 374 rescue ScmCommandAborted
368 375 nil
369 376 end
370 377
371 378 class Revision < Redmine::Scm::Adapters::Revision
372 379 # Returns the readable identifier
373 380 def format_identifier
374 381 identifier[0,8]
375 382 end
376 383 end
377 384
378 385 def scm_cmd(*args, &block)
379 386 repo_path = root_url || url
380 387 full_args = ['--git-dir', repo_path]
381 388 if self.class.client_version_above?([1, 7, 2])
382 389 full_args << '-c' << 'core.quotepath=false'
383 390 full_args << '-c' << 'log.decorate=no'
384 391 end
385 392 full_args += args
386 393 ret = shellout(
387 394 self.class.sq_bin + ' ' + full_args.map { |e| shell_quote e.to_s }.join(' '),
388 395 &block
389 396 )
390 397 if $? && $?.exitstatus != 0
391 398 raise ScmCommandAborted, "git exited with non-zero status: #{$?.exitstatus}"
392 399 end
393 400 ret
394 401 end
395 402 private :scm_cmd
396 403 end
397 404 end
398 405 end
399 406 end
@@ -1,528 +1,538
1 1 # encoding: utf-8
2 2
3 3 # This file includes UTF-8 "Felix SchΓ€fer".
4 4 # We need to consider Ruby 1.9 compatibility.
5 5
6 6 require File.expand_path('../../../../../../test_helper', __FILE__)
7 7 begin
8 8 require 'mocha'
9 9
10 10 class GitAdapterTest < ActiveSupport::TestCase
11 11 REPOSITORY_PATH = Rails.root.join('tmp/test/git_repository').to_s
12 12
13 13 FELIX_UTF8 = "Felix SchΓ€fer"
14 14 FELIX_HEX = "Felix Sch\xC3\xA4fer"
15 15 CHAR_1_HEX = "\xc3\x9c"
16 16
17 17 ## Ruby uses ANSI api to fork a process on Windows.
18 18 ## Japanese Shift_JIS and Traditional Chinese Big5 have 0x5c(backslash) problem
19 19 ## and these are incompatible with ASCII.
20 20 # WINDOWS_PASS = Redmine::Platform.mswin?
21 21 WINDOWS_PASS = false
22 22
23 23 ## Git, Mercurial and CVS path encodings are binary.
24 24 ## Subversion supports URL encoding for path.
25 25 ## Redmine Mercurial adapter and extension use URL encoding.
26 26 ## Git accepts only binary path in command line parameter.
27 27 ## So, there is no way to use binary command line parameter in JRuby.
28 28 JRUBY_SKIP = (RUBY_PLATFORM == 'java')
29 29 JRUBY_SKIP_STR = "TODO: This test fails in JRuby"
30 30
31 31 if File.directory?(REPOSITORY_PATH)
32 32 def setup
33 33 adapter_class = Redmine::Scm::Adapters::GitAdapter
34 34 assert adapter_class
35 35 assert adapter_class.client_command
36 36 assert_equal true, adapter_class.client_available
37 37 assert_equal true, adapter_class.client_version_above?([1])
38 38 assert_equal true, adapter_class.client_version_above?([1, 0])
39 39
40 40 @adapter = Redmine::Scm::Adapters::GitAdapter.new(
41 41 REPOSITORY_PATH,
42 42 nil,
43 43 nil,
44 44 nil,
45 45 'ISO-8859-1'
46 46 )
47 47 assert @adapter
48 48 @char_1 = CHAR_1_HEX.dup
49 49 if @char_1.respond_to?(:force_encoding)
50 50 @char_1.force_encoding('UTF-8')
51 51 end
52 52 end
53 53
54 54 def test_scm_version
55 55 to_test = { "git version 1.7.3.4\n" => [1,7,3,4],
56 56 "1.6.1\n1.7\n1.8" => [1,6,1],
57 57 "1.6.2\r\n1.8.1\r\n1.9.1" => [1,6,2]}
58 58 to_test.each do |s, v|
59 59 test_scm_version_for(s, v)
60 60 end
61 61 end
62 62
63 63 def test_branches
64 64 brs = []
65 65 @adapter.branches.each do |b|
66 66 brs << b
67 67 end
68 68 assert_equal 6, brs.length
69 69 br_issue_8857 = brs[0]
70 70 assert_equal 'issue-8857', br_issue_8857.to_s
71 71 assert_equal '2a682156a3b6e77a8bf9cd4590e8db757f3c6c78', br_issue_8857.revision
72 72 assert_equal br_issue_8857.scmid, br_issue_8857.revision
73 assert_equal false, br_issue_8857.is_default
73 74 br_latin_1_path = brs[1]
74 75 assert_equal 'latin-1-path-encoding', br_latin_1_path.to_s
75 76 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', br_latin_1_path.revision
76 77 assert_equal br_latin_1_path.scmid, br_latin_1_path.revision
78 assert_equal false, br_latin_1_path.is_default
77 79 br_master = brs[2]
78 80 assert_equal 'master', br_master.to_s
79 81 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', br_master.revision
80 82 assert_equal br_master.scmid, br_master.revision
83 assert_equal false, br_master.is_default
81 84 br_master_20120212 = brs[3]
82 85 assert_equal 'master-20120212', br_master_20120212.to_s
83 86 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', br_master_20120212.revision
84 87 assert_equal br_master_20120212.scmid, br_master_20120212.revision
88 assert_equal true, br_master_20120212.is_default
85 89 br_latin_1 = brs[-2]
86 90 assert_equal 'test-latin-1', br_latin_1.to_s
87 91 assert_equal '67e7792ce20ccae2e4bb73eed09bb397819c8834', br_latin_1.revision
88 92 assert_equal br_latin_1.scmid, br_latin_1.revision
93 assert_equal false, br_latin_1.is_default
89 94 br_test = brs[-1]
90 95 assert_equal 'test_branch', br_test.to_s
91 96 assert_equal 'fba357b886984ee71185ad2065e65fc0417d9b92', br_test.revision
92 97 assert_equal br_test.scmid, br_test.revision
98 assert_equal false, br_test.is_default
99 end
100
101 def test_default_branch
102 assert_equal 'master-20120212', @adapter.default_branch
93 103 end
94 104
95 105 def test_tags
96 106 assert_equal [
97 107 "tag00.lightweight",
98 108 "tag01.annotated",
99 109 ], @adapter.tags
100 110 end
101 111
102 112 def test_revisions_master_all
103 113 revs1 = []
104 114 @adapter.revisions('', nil, "master",{}) do |rev|
105 115 revs1 << rev
106 116 end
107 117 assert_equal 15, revs1.length
108 118 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[ 0].identifier
109 119 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518', revs1[-1].identifier
110 120
111 121 revs2 = []
112 122 @adapter.revisions('', nil, "master",
113 123 {:reverse => true}) do |rev|
114 124 revs2 << rev
115 125 end
116 126 assert_equal 15, revs2.length
117 127 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs2[-1].identifier
118 128 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518', revs2[ 0].identifier
119 129 end
120 130
121 131 def test_revisions_master_merged_rev
122 132 revs1 = []
123 133 @adapter.revisions('',
124 134 "713f4944648826f558cf548222f813dabe7cbb04",
125 135 "master",
126 136 {:reverse => true}) do |rev|
127 137 revs1 << rev
128 138 end
129 139 assert_equal 8, revs1.length
130 140 assert_equal 'fba357b886984ee71185ad2065e65fc0417d9b92', revs1[ 0].identifier
131 141 assert_equal '7e61ac704deecde634b51e59daa8110435dcb3da', revs1[ 1].identifier
132 142 # 4a07fe31b is not a child of 713f49446
133 143 assert_equal '4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8', revs1[ 2].identifier
134 144 # Merged revision
135 145 assert_equal '32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf', revs1[ 3].identifier
136 146 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[-1].identifier
137 147
138 148 revs2 = []
139 149 @adapter.revisions('',
140 150 "fba357b886984ee71185ad2065e65fc0417d9b92",
141 151 "master",
142 152 {:reverse => true}) do |rev|
143 153 revs2 << rev
144 154 end
145 155 assert_equal 7, revs2.length
146 156 assert_equal '7e61ac704deecde634b51e59daa8110435dcb3da', revs2[ 0].identifier
147 157 # 4a07fe31b is not a child of fba357b8869
148 158 assert_equal '4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8', revs2[ 1].identifier
149 159 # Merged revision
150 160 assert_equal '32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf', revs2[ 2].identifier
151 161 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs2[-1].identifier
152 162 end
153 163
154 164 def test_revisions_branch_latin_1_path_encoding_all
155 165 revs1 = []
156 166 @adapter.revisions('', nil, "latin-1-path-encoding",{}) do |rev|
157 167 revs1 << rev
158 168 end
159 169 assert_equal 8, revs1.length
160 170 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', revs1[ 0].identifier
161 171 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518', revs1[-1].identifier
162 172
163 173 revs2 = []
164 174 @adapter.revisions('', nil, "latin-1-path-encoding",
165 175 {:reverse => true}) do |rev|
166 176 revs2 << rev
167 177 end
168 178 assert_equal 8, revs2.length
169 179 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', revs2[-1].identifier
170 180 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518', revs2[ 0].identifier
171 181 end
172 182
173 183 def test_revisions_branch_latin_1_path_encoding_with_rev
174 184 revs1 = []
175 185 @adapter.revisions('',
176 186 '7234cb2750b63f47bff735edc50a1c0a433c2518',
177 187 "latin-1-path-encoding",
178 188 {:reverse => true}) do |rev|
179 189 revs1 << rev
180 190 end
181 191 assert_equal 7, revs1.length
182 192 assert_equal '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', revs1[ 0].identifier
183 193 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', revs1[-1].identifier
184 194
185 195 revs2 = []
186 196 @adapter.revisions('',
187 197 '57ca437c0acbbcb749821fdf3726a1367056d364',
188 198 "latin-1-path-encoding",
189 199 {:reverse => true}) do |rev|
190 200 revs2 << rev
191 201 end
192 202 assert_equal 3, revs2.length
193 203 assert_equal '4fc55c43bf3d3dc2efb66145365ddc17639ce81e', revs2[ 0].identifier
194 204 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', revs2[-1].identifier
195 205 end
196 206
197 207 def test_revisions_invalid_rev
198 208 assert_equal [], @adapter.revisions('', '1234abcd', "master")
199 209 assert_raise Redmine::Scm::Adapters::CommandFailed do
200 210 revs1 = []
201 211 @adapter.revisions('',
202 212 '1234abcd',
203 213 "master",
204 214 {:reverse => true}) do |rev|
205 215 revs1 << rev
206 216 end
207 217 end
208 218 end
209 219
210 220 def test_revisions_includes_master_two_revs
211 221 revs1 = []
212 222 @adapter.revisions('', nil, nil,
213 223 {:reverse => true,
214 224 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c'],
215 225 :excludes => ['4f26664364207fa8b1af9f8722647ab2d4ac5d43']}) do |rev|
216 226 revs1 << rev
217 227 end
218 228 assert_equal 2, revs1.length
219 229 assert_equal 'ed5bb786bbda2dee66a2d50faf51429dbc043a7b', revs1[ 0].identifier
220 230 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[-1].identifier
221 231 end
222 232
223 233 def test_revisions_includes_master_two_revs_from_origin
224 234 revs1 = []
225 235 @adapter.revisions('', nil, nil,
226 236 {:reverse => true,
227 237 :includes => ['899a15dba03a3b350b89c3f537e4bbe02a03cdc9'],
228 238 :excludes => []}) do |rev|
229 239 revs1 << rev
230 240 end
231 241 assert_equal 2, revs1.length
232 242 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518', revs1[ 0].identifier
233 243 assert_equal '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', revs1[ 1].identifier
234 244 end
235 245
236 246 def test_revisions_includes_merged_revs
237 247 revs1 = []
238 248 @adapter.revisions('', nil, nil,
239 249 {:reverse => true,
240 250 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c'],
241 251 :excludes => ['fba357b886984ee71185ad2065e65fc0417d9b92']}) do |rev|
242 252 revs1 << rev
243 253 end
244 254 assert_equal 7, revs1.length
245 255 assert_equal '7e61ac704deecde634b51e59daa8110435dcb3da', revs1[ 0].identifier
246 256 assert_equal '4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8', revs1[ 1].identifier
247 257 assert_equal '32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf', revs1[ 2].identifier
248 258 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[-1].identifier
249 259 end
250 260
251 261 def test_revisions_includes_two_heads
252 262 revs1 = []
253 263 @adapter.revisions('', nil, nil,
254 264 {:reverse => true,
255 265 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c',
256 266 '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127'],
257 267 :excludes => ['4f26664364207fa8b1af9f8722647ab2d4ac5d43',
258 268 '4fc55c43bf3d3dc2efb66145365ddc17639ce81e']}) do |rev|
259 269 revs1 << rev
260 270 end
261 271 assert_equal 4, revs1.length
262 272 assert_equal 'ed5bb786bbda2dee66a2d50faf51429dbc043a7b', revs1[ 0].identifier
263 273 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[ 1].identifier
264 274 assert_equal '64f1f3e89ad1cb57976ff0ad99a107012ba3481d', revs1[-2].identifier
265 275 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', revs1[-1].identifier
266 276 end
267 277
268 278 def test_revisions_disjointed_histories_revisions
269 279 revs1 = []
270 280 @adapter.revisions('', nil, nil,
271 281 {:reverse => true,
272 282 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c',
273 283 '92397af84d22f27389c822848ecd5b463c181583'],
274 284 :excludes => ['95488a44bc25f7d1f97d775a31359539ff333a63',
275 285 '4f26664364207fa8b1af9f8722647ab2d4ac5d43'] }) do |rev|
276 286 revs1 << rev
277 287 end
278 288 assert_equal 4, revs1.length
279 289 assert_equal 'ed5bb786bbda2dee66a2d50faf51429dbc043a7b', revs1[ 0].identifier
280 290 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[ 1].identifier
281 291 assert_equal 'bc201c95999c4f10d018b0aa03b541cd6a2ff0ee', revs1[-2].identifier
282 292 assert_equal '92397af84d22f27389c822848ecd5b463c181583', revs1[-1].identifier
283 293 end
284 294
285 295 def test_revisions_invalid_rev_excludes
286 296 assert_equal [],
287 297 @adapter.revisions('', nil, nil,
288 298 {:reverse => true,
289 299 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c'],
290 300 :excludes => ['0123abcd4567']})
291 301 assert_raise Redmine::Scm::Adapters::CommandFailed do
292 302 revs1 = []
293 303 @adapter.revisions('', nil, nil,
294 304 {:reverse => true,
295 305 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c'],
296 306 :excludes => ['0123abcd4567']}) do |rev|
297 307 revs1 << rev
298 308 end
299 309 end
300 310 end
301 311
302 312 def test_getting_revisions_with_spaces_in_filename
303 313 assert_equal 1, @adapter.revisions("filemane with spaces.txt",
304 314 nil, "master").length
305 315 end
306 316
307 317 def test_parents
308 318 revs1 = []
309 319 @adapter.revisions('',
310 320 nil,
311 321 "master",
312 322 {:reverse => true}) do |rev|
313 323 revs1 << rev
314 324 end
315 325 assert_equal 15, revs1.length
316 326 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518",
317 327 revs1[0].identifier
318 328 assert_equal nil, revs1[0].parents
319 329 assert_equal "899a15dba03a3b350b89c3f537e4bbe02a03cdc9",
320 330 revs1[1].identifier
321 331 assert_equal 1, revs1[1].parents.length
322 332 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518",
323 333 revs1[1].parents[0]
324 334 assert_equal "32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf",
325 335 revs1[10].identifier
326 336 assert_equal 2, revs1[10].parents.length
327 337 assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8",
328 338 revs1[10].parents[0]
329 339 assert_equal "7e61ac704deecde634b51e59daa8110435dcb3da",
330 340 revs1[10].parents[1]
331 341 end
332 342
333 343 def test_getting_revisions_with_leading_and_trailing_spaces_in_filename
334 344 assert_equal " filename with a leading space.txt ",
335 345 @adapter.revisions(" filename with a leading space.txt ",
336 346 nil, "master")[0].paths[0][:path]
337 347 end
338 348
339 349 def test_getting_entries_with_leading_and_trailing_spaces_in_filename
340 350 assert_equal " filename with a leading space.txt ",
341 351 @adapter.entries('',
342 352 '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c')[3].name
343 353 end
344 354
345 355 def test_annotate
346 356 annotate = @adapter.annotate('sources/watchers_controller.rb')
347 357 assert_kind_of Redmine::Scm::Adapters::Annotate, annotate
348 358 assert_equal 41, annotate.lines.size
349 359 assert_equal "# This program is free software; you can redistribute it and/or",
350 360 annotate.lines[4].strip
351 361 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518",
352 362 annotate.revisions[4].identifier
353 363 assert_equal "jsmith", annotate.revisions[4].author
354 364 end
355 365
356 366 def test_annotate_moved_file
357 367 annotate = @adapter.annotate('renamed_test.txt')
358 368 assert_kind_of Redmine::Scm::Adapters::Annotate, annotate
359 369 assert_equal 2, annotate.lines.size
360 370 end
361 371
362 372 def test_last_rev
363 373 last_rev = @adapter.lastrev("README",
364 374 "4f26664364207fa8b1af9f8722647ab2d4ac5d43")
365 375 assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8", last_rev.scmid
366 376 assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8", last_rev.identifier
367 377 assert_equal "Adam Soltys <asoltys@gmail.com>", last_rev.author
368 378 assert_equal "2009-06-24 05:27:38".to_time, last_rev.time
369 379 end
370 380
371 381 def test_last_rev_with_spaces_in_filename
372 382 last_rev = @adapter.lastrev("filemane with spaces.txt",
373 383 "ed5bb786bbda2dee66a2d50faf51429dbc043a7b")
374 384 str_felix_utf8 = FELIX_UTF8.dup
375 385 str_felix_hex = FELIX_HEX.dup
376 386 last_rev_author = last_rev.author
377 387 if last_rev_author.respond_to?(:force_encoding)
378 388 last_rev_author.force_encoding('UTF-8')
379 389 end
380 390 assert_equal "ed5bb786bbda2dee66a2d50faf51429dbc043a7b", last_rev.scmid
381 391 assert_equal "ed5bb786bbda2dee66a2d50faf51429dbc043a7b", last_rev.identifier
382 392 assert_equal "#{str_felix_utf8} <felix@fachschaften.org>",
383 393 last_rev.author
384 394 assert_equal "#{str_felix_hex} <felix@fachschaften.org>",
385 395 last_rev.author
386 396 assert_equal "2010-09-18 19:59:46".to_time, last_rev.time
387 397 end
388 398
389 399 def test_latin_1_path
390 400 if WINDOWS_PASS
391 401 #
392 402 elsif JRUBY_SKIP
393 403 puts JRUBY_SKIP_STR
394 404 else
395 405 p2 = "latin-1-dir/test-#{@char_1}-2.txt"
396 406 ['4fc55c43bf3d3dc2efb66145365ddc17639ce81e', '4fc55c43bf3'].each do |r1|
397 407 assert @adapter.diff(p2, r1)
398 408 assert @adapter.cat(p2, r1)
399 409 assert_equal 1, @adapter.annotate(p2, r1).lines.length
400 410 ['64f1f3e89ad1cb57976ff0ad99a107012ba3481d', '64f1f3e89ad1cb5797'].each do |r2|
401 411 assert @adapter.diff(p2, r1, r2)
402 412 end
403 413 end
404 414 end
405 415 end
406 416
407 417 def test_entries_tag
408 418 entries1 = @adapter.entries(nil, 'tag01.annotated',
409 419 options = {:report_last_commit => true})
410 420 assert entries1
411 421 assert_equal 3, entries1.size
412 422 assert_equal 'sources', entries1[1].name
413 423 assert_equal 'sources', entries1[1].path
414 424 assert_equal 'dir', entries1[1].kind
415 425 readme = entries1[2]
416 426 assert_equal 'README', readme.name
417 427 assert_equal 'README', readme.path
418 428 assert_equal 'file', readme.kind
419 429 assert_equal 27, readme.size
420 430 assert_equal '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', readme.lastrev.identifier
421 431 assert_equal Time.gm(2007, 12, 14, 9, 24, 1), readme.lastrev.time
422 432 end
423 433
424 434 def test_entries_branch
425 435 entries1 = @adapter.entries(nil, 'test_branch',
426 436 options = {:report_last_commit => true})
427 437 assert entries1
428 438 assert_equal 4, entries1.size
429 439 assert_equal 'sources', entries1[1].name
430 440 assert_equal 'sources', entries1[1].path
431 441 assert_equal 'dir', entries1[1].kind
432 442 readme = entries1[2]
433 443 assert_equal 'README', readme.name
434 444 assert_equal 'README', readme.path
435 445 assert_equal 'file', readme.kind
436 446 assert_equal 159, readme.size
437 447 assert_equal '713f4944648826f558cf548222f813dabe7cbb04', readme.lastrev.identifier
438 448 assert_equal Time.gm(2009, 6, 19, 4, 37, 23), readme.lastrev.time
439 449 end
440 450
441 451 def test_entries_latin_1_files
442 452 entries1 = @adapter.entries('latin-1-dir', '64f1f3e8')
443 453 assert entries1
444 454 assert_equal 3, entries1.size
445 455 f1 = entries1[1]
446 456 assert_equal "test-#{@char_1}-2.txt", f1.name
447 457 assert_equal "latin-1-dir/test-#{@char_1}-2.txt", f1.path
448 458 assert_equal 'file', f1.kind
449 459 end
450 460
451 461 def test_entries_latin_1_dir
452 462 if WINDOWS_PASS
453 463 #
454 464 elsif JRUBY_SKIP
455 465 puts JRUBY_SKIP_STR
456 466 else
457 467 entries1 = @adapter.entries("latin-1-dir/test-#{@char_1}-subdir",
458 468 '1ca7f5ed')
459 469 assert entries1
460 470 assert_equal 3, entries1.size
461 471 f1 = entries1[1]
462 472 assert_equal "test-#{@char_1}-2.txt", f1.name
463 473 assert_equal "latin-1-dir/test-#{@char_1}-subdir/test-#{@char_1}-2.txt", f1.path
464 474 assert_equal 'file', f1.kind
465 475 end
466 476 end
467 477
468 478 def test_path_encoding_default_utf8
469 479 adpt1 = Redmine::Scm::Adapters::GitAdapter.new(
470 480 REPOSITORY_PATH
471 481 )
472 482 assert_equal "UTF-8", adpt1.path_encoding
473 483 adpt2 = Redmine::Scm::Adapters::GitAdapter.new(
474 484 REPOSITORY_PATH,
475 485 nil,
476 486 nil,
477 487 nil,
478 488 ""
479 489 )
480 490 assert_equal "UTF-8", adpt2.path_encoding
481 491 end
482 492
483 493 def test_cat_path_invalid
484 494 assert_nil @adapter.cat('invalid')
485 495 end
486 496
487 497 def test_cat_revision_invalid
488 498 assert @adapter.cat('README')
489 499 assert_nil @adapter.cat('README', 'abcd1234efgh')
490 500 end
491 501
492 502 def test_diff_path_invalid
493 503 assert_equal [], @adapter.diff('invalid', '713f4944648826f5')
494 504 end
495 505
496 506 def test_diff_revision_invalid
497 507 assert_nil @adapter.diff(nil, 'abcd1234efgh')
498 508 assert_nil @adapter.diff(nil, '713f4944648826f5', 'abcd1234efgh')
499 509 assert_nil @adapter.diff(nil, 'abcd1234efgh', '713f4944648826f5')
500 510 end
501 511
502 512 def test_annotate_path_invalid
503 513 assert_nil @adapter.annotate('invalid')
504 514 end
505 515
506 516 def test_annotate_revision_invalid
507 517 assert @adapter.annotate('README')
508 518 assert_nil @adapter.annotate('README', 'abcd1234efgh')
509 519 end
510 520
511 521 private
512 522
513 523 def test_scm_version_for(scm_command_version, version)
514 524 @adapter.class.expects(:scm_version_from_command_line).returns(scm_command_version)
515 525 assert_equal version, @adapter.class.scm_command_version
516 526 end
517 527
518 528 else
519 529 puts "Git test repository NOT FOUND. Skipping unit tests !!!"
520 530 def test_fake; assert true end
521 531 end
522 532 end
523 533
524 534 rescue LoadError
525 535 class GitMochaFake < ActiveSupport::TestCase
526 536 def test_fake; assert(false, "Requires mocha to run those tests") end
527 537 end
528 538 end
General Comments 0
You need to be logged in to leave comments. Login now