@@ -1,90 +1,90 | |||||
1 | source 'http://rubygems.org' |
|
1 | source 'http://rubygems.org' | |
2 |
|
2 | |||
3 |
gem 'rails', '3.2. |
|
3 | gem 'rails', '3.2.5' | |
4 | gem 'prototype-rails', '3.2.1' |
|
4 | gem 'prototype-rails', '3.2.1' | |
5 | gem "i18n", "~> 0.6.0" |
|
5 | gem "i18n", "~> 0.6.0" | |
6 | gem "coderay", "~> 1.0.6" |
|
6 | gem "coderay", "~> 1.0.6" | |
7 | gem "fastercsv", "~> 1.5.0", :platforms => [:mri_18, :mingw_18, :jruby] |
|
7 | gem "fastercsv", "~> 1.5.0", :platforms => [:mri_18, :mingw_18, :jruby] | |
8 | gem "builder" |
|
8 | gem "builder" | |
9 |
|
9 | |||
10 | # Optional gem for LDAP authentication |
|
10 | # Optional gem for LDAP authentication | |
11 | group :ldap do |
|
11 | group :ldap do | |
12 | gem "net-ldap", "~> 0.3.1" |
|
12 | gem "net-ldap", "~> 0.3.1" | |
13 | end |
|
13 | end | |
14 |
|
14 | |||
15 | # Optional gem for OpenID authentication |
|
15 | # Optional gem for OpenID authentication | |
16 | group :openid do |
|
16 | group :openid do | |
17 | gem "ruby-openid", "~> 2.1.4", :require => "openid" |
|
17 | gem "ruby-openid", "~> 2.1.4", :require => "openid" | |
18 | gem "rack-openid" |
|
18 | gem "rack-openid" | |
19 | end |
|
19 | end | |
20 |
|
20 | |||
21 | # Optional gem for exporting the gantt to a PNG file, not supported with jruby |
|
21 | # Optional gem for exporting the gantt to a PNG file, not supported with jruby | |
22 | platforms :mri, :mingw do |
|
22 | platforms :mri, :mingw do | |
23 | group :rmagick do |
|
23 | group :rmagick do | |
24 | # RMagick 2 supports ruby 1.9 |
|
24 | # RMagick 2 supports ruby 1.9 | |
25 | # RMagick 1 would be fine for ruby 1.8 but Bundler does not support |
|
25 | # RMagick 1 would be fine for ruby 1.8 but Bundler does not support | |
26 | # different requirements for the same gem on different platforms |
|
26 | # different requirements for the same gem on different platforms | |
27 | gem "rmagick", ">= 2.0.0" |
|
27 | gem "rmagick", ">= 2.0.0" | |
28 | end |
|
28 | end | |
29 | end |
|
29 | end | |
30 |
|
30 | |||
31 | # Database gems |
|
31 | # Database gems | |
32 | platforms :mri, :mingw do |
|
32 | platforms :mri, :mingw do | |
33 | group :postgresql do |
|
33 | group :postgresql do | |
34 | gem "pg", ">= 0.11.0" |
|
34 | gem "pg", ">= 0.11.0" | |
35 | end |
|
35 | end | |
36 |
|
36 | |||
37 | group :sqlite do |
|
37 | group :sqlite do | |
38 | gem "sqlite3" |
|
38 | gem "sqlite3" | |
39 | end |
|
39 | end | |
40 | end |
|
40 | end | |
41 |
|
41 | |||
42 | platforms :mri_18, :mingw_18 do |
|
42 | platforms :mri_18, :mingw_18 do | |
43 | group :mysql do |
|
43 | group :mysql do | |
44 | gem "mysql" |
|
44 | gem "mysql" | |
45 | end |
|
45 | end | |
46 | end |
|
46 | end | |
47 |
|
47 | |||
48 | platforms :mri_19, :mingw_19 do |
|
48 | platforms :mri_19, :mingw_19 do | |
49 | group :mysql do |
|
49 | group :mysql do | |
50 | gem "mysql2", "~> 0.3.11" |
|
50 | gem "mysql2", "~> 0.3.11" | |
51 | end |
|
51 | end | |
52 | end |
|
52 | end | |
53 |
|
53 | |||
54 | platforms :jruby do |
|
54 | platforms :jruby do | |
55 | gem "jruby-openssl" |
|
55 | gem "jruby-openssl" | |
56 |
|
56 | |||
57 | group :mysql do |
|
57 | group :mysql do | |
58 | gem "activerecord-jdbcmysql-adapter" |
|
58 | gem "activerecord-jdbcmysql-adapter" | |
59 | end |
|
59 | end | |
60 |
|
60 | |||
61 | group :postgresql do |
|
61 | group :postgresql do | |
62 | gem "activerecord-jdbcpostgresql-adapter" |
|
62 | gem "activerecord-jdbcpostgresql-adapter" | |
63 | end |
|
63 | end | |
64 |
|
64 | |||
65 | group :sqlite do |
|
65 | group :sqlite do | |
66 | gem "activerecord-jdbcsqlite3-adapter" |
|
66 | gem "activerecord-jdbcsqlite3-adapter" | |
67 | end |
|
67 | end | |
68 | end |
|
68 | end | |
69 |
|
69 | |||
70 | group :development do |
|
70 | group :development do | |
71 | gem "rdoc", ">= 2.4.2" |
|
71 | gem "rdoc", ">= 2.4.2" | |
72 | gem "yard" |
|
72 | gem "yard" | |
73 | end |
|
73 | end | |
74 |
|
74 | |||
75 | group :test do |
|
75 | group :test do | |
76 | gem "shoulda", "~> 2.11" |
|
76 | gem "shoulda", "~> 2.11" | |
77 | gem "mocha" |
|
77 | gem "mocha" | |
78 | end |
|
78 | end | |
79 |
|
79 | |||
80 | local_gemfile = File.join(File.dirname(__FILE__), "Gemfile.local") |
|
80 | local_gemfile = File.join(File.dirname(__FILE__), "Gemfile.local") | |
81 | if File.exists?(local_gemfile) |
|
81 | if File.exists?(local_gemfile) | |
82 | puts "Loading Gemfile.local ..." if $DEBUG # `ruby -d` or `bundle -v` |
|
82 | puts "Loading Gemfile.local ..." if $DEBUG # `ruby -d` or `bundle -v` | |
83 | instance_eval File.read(local_gemfile) |
|
83 | instance_eval File.read(local_gemfile) | |
84 | end |
|
84 | end | |
85 |
|
85 | |||
86 | # Load plugins' Gemfiles |
|
86 | # Load plugins' Gemfiles | |
87 | Dir.glob File.expand_path("../plugins/*/Gemfile", __FILE__) do |file| |
|
87 | Dir.glob File.expand_path("../plugins/*/Gemfile", __FILE__) do |file| | |
88 | puts "Loading #{file} ..." if $DEBUG # `ruby -d` or `bundle -v` |
|
88 | puts "Loading #{file} ..." if $DEBUG # `ruby -d` or `bundle -v` | |
89 | instance_eval File.read(file) |
|
89 | instance_eval File.read(file) | |
90 | end |
|
90 | end |
@@ -1,318 +1,318 | |||||
1 | # encoding: utf-8 |
|
1 | # encoding: utf-8 | |
2 | # |
|
2 | # | |
3 | # Redmine - project management software |
|
3 | # Redmine - project management software | |
4 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
4 | # Copyright (C) 2006-2012 Jean-Philippe Lang | |
5 | # |
|
5 | # | |
6 | # This program is free software; you can redistribute it and/or |
|
6 | # This program is free software; you can redistribute it and/or | |
7 | # modify it under the terms of the GNU General Public License |
|
7 | # modify it under the terms of the GNU General Public License | |
8 | # as published by the Free Software Foundation; either version 2 |
|
8 | # as published by the Free Software Foundation; either version 2 | |
9 | # of the License, or (at your option) any later version. |
|
9 | # of the License, or (at your option) any later version. | |
10 | # |
|
10 | # | |
11 | # This program is distributed in the hope that it will be useful, |
|
11 | # This program is distributed in the hope that it will be useful, | |
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | # GNU General Public License for more details. |
|
14 | # GNU General Public License for more details. | |
15 | # |
|
15 | # | |
16 | # You should have received a copy of the GNU General Public License |
|
16 | # You should have received a copy of the GNU General Public License | |
17 | # along with this program; if not, write to the Free Software |
|
17 | # along with this program; if not, write to the Free Software | |
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
19 |
|
19 | |||
20 | require 'iconv' |
|
20 | require 'iconv' | |
21 | require 'redmine/codeset_util' |
|
21 | require 'redmine/codeset_util' | |
22 |
|
22 | |||
23 | module RepositoriesHelper |
|
23 | module RepositoriesHelper | |
24 | def format_revision(revision) |
|
24 | def format_revision(revision) | |
25 | if revision.respond_to? :format_identifier |
|
25 | if revision.respond_to? :format_identifier | |
26 | revision.format_identifier |
|
26 | revision.format_identifier | |
27 | else |
|
27 | else | |
28 | revision.to_s |
|
28 | revision.to_s | |
29 | end |
|
29 | end | |
30 | end |
|
30 | end | |
31 |
|
31 | |||
32 | def truncate_at_line_break(text, length = 255) |
|
32 | def truncate_at_line_break(text, length = 255) | |
33 | if text |
|
33 | if text | |
34 | text.gsub(%r{^(.{#{length}}[^\n]*)\n.+$}m, '\\1...') |
|
34 | text.gsub(%r{^(.{#{length}}[^\n]*)\n.+$}m, '\\1...') | |
35 | end |
|
35 | end | |
36 | end |
|
36 | end | |
37 |
|
37 | |||
38 | def render_properties(properties) |
|
38 | def render_properties(properties) | |
39 | unless properties.nil? || properties.empty? |
|
39 | unless properties.nil? || properties.empty? | |
40 | content = '' |
|
40 | content = '' | |
41 | properties.keys.sort.each do |property| |
|
41 | properties.keys.sort.each do |property| | |
42 | content << content_tag('li', "<b>#{h property}</b>: <span>#{h properties[property]}</span>".html_safe) |
|
42 | content << content_tag('li', "<b>#{h property}</b>: <span>#{h properties[property]}</span>".html_safe) | |
43 | end |
|
43 | end | |
44 | content_tag('ul', content.html_safe, :class => 'properties') |
|
44 | content_tag('ul', content.html_safe, :class => 'properties') | |
45 | end |
|
45 | end | |
46 | end |
|
46 | end | |
47 |
|
47 | |||
48 | def render_changeset_changes |
|
48 | def render_changeset_changes | |
49 | changes = @changeset.changes.find(:all, :limit => 1000, :order => 'path').collect do |change| |
|
49 | changes = @changeset.filechanges.find(:all, :limit => 1000, :order => 'path').collect do |change| | |
50 | case change.action |
|
50 | case change.action | |
51 | when 'A' |
|
51 | when 'A' | |
52 | # Detects moved/copied files |
|
52 | # Detects moved/copied files | |
53 | if !change.from_path.blank? |
|
53 | if !change.from_path.blank? | |
54 | change.action = |
|
54 | change.action = | |
55 | @changeset.changes.detect {|c| c.action == 'D' && c.path == change.from_path} ? 'R' : 'C' |
|
55 | @changeset.filechanges.detect {|c| c.action == 'D' && c.path == change.from_path} ? 'R' : 'C' | |
56 | end |
|
56 | end | |
57 | change |
|
57 | change | |
58 | when 'D' |
|
58 | when 'D' | |
59 | @changeset.changes.detect {|c| c.from_path == change.path} ? nil : change |
|
59 | @changeset.filechanges.detect {|c| c.from_path == change.path} ? nil : change | |
60 | else |
|
60 | else | |
61 | change |
|
61 | change | |
62 | end |
|
62 | end | |
63 | end.compact |
|
63 | end.compact | |
64 |
|
64 | |||
65 | tree = { } |
|
65 | tree = { } | |
66 | changes.each do |change| |
|
66 | changes.each do |change| | |
67 | p = tree |
|
67 | p = tree | |
68 | dirs = change.path.to_s.split('/').select {|d| !d.blank?} |
|
68 | dirs = change.path.to_s.split('/').select {|d| !d.blank?} | |
69 | path = '' |
|
69 | path = '' | |
70 | dirs.each do |dir| |
|
70 | dirs.each do |dir| | |
71 | path += '/' + dir |
|
71 | path += '/' + dir | |
72 | p[:s] ||= {} |
|
72 | p[:s] ||= {} | |
73 | p = p[:s] |
|
73 | p = p[:s] | |
74 | p[path] ||= {} |
|
74 | p[path] ||= {} | |
75 | p = p[path] |
|
75 | p = p[path] | |
76 | end |
|
76 | end | |
77 | p[:c] = change |
|
77 | p[:c] = change | |
78 | end |
|
78 | end | |
79 | render_changes_tree(tree[:s]) |
|
79 | render_changes_tree(tree[:s]) | |
80 | end |
|
80 | end | |
81 |
|
81 | |||
82 | def render_changes_tree(tree) |
|
82 | def render_changes_tree(tree) | |
83 | return '' if tree.nil? |
|
83 | return '' if tree.nil? | |
84 | output = '' |
|
84 | output = '' | |
85 | output << '<ul>' |
|
85 | output << '<ul>' | |
86 | tree.keys.sort.each do |file| |
|
86 | tree.keys.sort.each do |file| | |
87 | style = 'change' |
|
87 | style = 'change' | |
88 | text = File.basename(h(file)) |
|
88 | text = File.basename(h(file)) | |
89 | if s = tree[file][:s] |
|
89 | if s = tree[file][:s] | |
90 | style << ' folder' |
|
90 | style << ' folder' | |
91 | path_param = to_path_param(@repository.relative_path(file)) |
|
91 | path_param = to_path_param(@repository.relative_path(file)) | |
92 | text = link_to(h(text), :controller => 'repositories', |
|
92 | text = link_to(h(text), :controller => 'repositories', | |
93 | :action => 'show', |
|
93 | :action => 'show', | |
94 | :id => @project, |
|
94 | :id => @project, | |
95 | :repository_id => @repository.identifier_param, |
|
95 | :repository_id => @repository.identifier_param, | |
96 | :path => path_param, |
|
96 | :path => path_param, | |
97 | :rev => @changeset.identifier) |
|
97 | :rev => @changeset.identifier) | |
98 | output << "<li class='#{style}'>#{text}" |
|
98 | output << "<li class='#{style}'>#{text}" | |
99 | output << render_changes_tree(s) |
|
99 | output << render_changes_tree(s) | |
100 | output << "</li>" |
|
100 | output << "</li>" | |
101 | elsif c = tree[file][:c] |
|
101 | elsif c = tree[file][:c] | |
102 | style << " change-#{c.action}" |
|
102 | style << " change-#{c.action}" | |
103 | path_param = to_path_param(@repository.relative_path(c.path)) |
|
103 | path_param = to_path_param(@repository.relative_path(c.path)) | |
104 | text = link_to(h(text), :controller => 'repositories', |
|
104 | text = link_to(h(text), :controller => 'repositories', | |
105 | :action => 'entry', |
|
105 | :action => 'entry', | |
106 | :id => @project, |
|
106 | :id => @project, | |
107 | :repository_id => @repository.identifier_param, |
|
107 | :repository_id => @repository.identifier_param, | |
108 | :path => path_param, |
|
108 | :path => path_param, | |
109 | :rev => @changeset.identifier) unless c.action == 'D' |
|
109 | :rev => @changeset.identifier) unless c.action == 'D' | |
110 | text << " - #{h(c.revision)}" unless c.revision.blank? |
|
110 | text << " - #{h(c.revision)}" unless c.revision.blank? | |
111 | text << ' ('.html_safe + link_to(l(:label_diff), :controller => 'repositories', |
|
111 | text << ' ('.html_safe + link_to(l(:label_diff), :controller => 'repositories', | |
112 | :action => 'diff', |
|
112 | :action => 'diff', | |
113 | :id => @project, |
|
113 | :id => @project, | |
114 | :repository_id => @repository.identifier_param, |
|
114 | :repository_id => @repository.identifier_param, | |
115 | :path => path_param, |
|
115 | :path => path_param, | |
116 | :rev => @changeset.identifier) + ') '.html_safe if c.action == 'M' |
|
116 | :rev => @changeset.identifier) + ') '.html_safe if c.action == 'M' | |
117 | text << ' '.html_safe + content_tag('span', h(c.from_path), :class => 'copied-from') unless c.from_path.blank? |
|
117 | text << ' '.html_safe + content_tag('span', h(c.from_path), :class => 'copied-from') unless c.from_path.blank? | |
118 | output << "<li class='#{style}'>#{text}</li>" |
|
118 | output << "<li class='#{style}'>#{text}</li>" | |
119 | end |
|
119 | end | |
120 | end |
|
120 | end | |
121 | output << '</ul>' |
|
121 | output << '</ul>' | |
122 | output.html_safe |
|
122 | output.html_safe | |
123 | end |
|
123 | end | |
124 |
|
124 | |||
125 | def repository_field_tags(form, repository) |
|
125 | def repository_field_tags(form, repository) | |
126 | method = repository.class.name.demodulize.underscore + "_field_tags" |
|
126 | method = repository.class.name.demodulize.underscore + "_field_tags" | |
127 | if repository.is_a?(Repository) && |
|
127 | if repository.is_a?(Repository) && | |
128 | respond_to?(method) && method != 'repository_field_tags' |
|
128 | respond_to?(method) && method != 'repository_field_tags' | |
129 | send(method, form, repository) |
|
129 | send(method, form, repository) | |
130 | end |
|
130 | end | |
131 | end |
|
131 | end | |
132 |
|
132 | |||
133 | def scm_select_tag(repository) |
|
133 | def scm_select_tag(repository) | |
134 | scm_options = [["--- #{l(:actionview_instancetag_blank_option)} ---", '']] |
|
134 | scm_options = [["--- #{l(:actionview_instancetag_blank_option)} ---", '']] | |
135 | Redmine::Scm::Base.all.each do |scm| |
|
135 | Redmine::Scm::Base.all.each do |scm| | |
136 | if Setting.enabled_scm.include?(scm) || |
|
136 | if Setting.enabled_scm.include?(scm) || | |
137 | (repository && repository.class.name.demodulize == scm) |
|
137 | (repository && repository.class.name.demodulize == scm) | |
138 | scm_options << ["Repository::#{scm}".constantize.scm_name, scm] |
|
138 | scm_options << ["Repository::#{scm}".constantize.scm_name, scm] | |
139 | end |
|
139 | end | |
140 | end |
|
140 | end | |
141 | select_tag('repository_scm', |
|
141 | select_tag('repository_scm', | |
142 | options_for_select(scm_options, repository.class.name.demodulize), |
|
142 | options_for_select(scm_options, repository.class.name.demodulize), | |
143 | :disabled => (repository && !repository.new_record?), |
|
143 | :disabled => (repository && !repository.new_record?), | |
144 | :onchange => remote_function( |
|
144 | :onchange => remote_function( | |
145 | :url => new_project_repository_path(@project), |
|
145 | :url => new_project_repository_path(@project), | |
146 | :method => :get, |
|
146 | :method => :get, | |
147 | :update => 'content', |
|
147 | :update => 'content', | |
148 | :with => "Form.serialize(this.form)") |
|
148 | :with => "Form.serialize(this.form)") | |
149 | ) |
|
149 | ) | |
150 | end |
|
150 | end | |
151 |
|
151 | |||
152 | def with_leading_slash(path) |
|
152 | def with_leading_slash(path) | |
153 | path.to_s.starts_with?('/') ? path : "/#{path}" |
|
153 | path.to_s.starts_with?('/') ? path : "/#{path}" | |
154 | end |
|
154 | end | |
155 |
|
155 | |||
156 | def without_leading_slash(path) |
|
156 | def without_leading_slash(path) | |
157 | path.gsub(%r{^/+}, '') |
|
157 | path.gsub(%r{^/+}, '') | |
158 | end |
|
158 | end | |
159 |
|
159 | |||
160 | def subversion_field_tags(form, repository) |
|
160 | def subversion_field_tags(form, repository) | |
161 | content_tag('p', form.text_field(:url, :size => 60, :required => true, |
|
161 | content_tag('p', form.text_field(:url, :size => 60, :required => true, | |
162 | :disabled => (repository && !repository.root_url.blank?)) + |
|
162 | :disabled => (repository && !repository.root_url.blank?)) + | |
163 | '<br />'.html_safe + |
|
163 | '<br />'.html_safe + | |
164 | '(file:///, http://, https://, svn://, svn+[tunnelscheme]://)') + |
|
164 | '(file:///, http://, https://, svn://, svn+[tunnelscheme]://)') + | |
165 | content_tag('p', form.text_field(:login, :size => 30)) + |
|
165 | content_tag('p', form.text_field(:login, :size => 30)) + | |
166 | content_tag('p', form.password_field( |
|
166 | content_tag('p', form.password_field( | |
167 | :password, :size => 30, :name => 'ignore', |
|
167 | :password, :size => 30, :name => 'ignore', | |
168 | :value => ((repository.new_record? || repository.password.blank?) ? '' : ('x'*15)), |
|
168 | :value => ((repository.new_record? || repository.password.blank?) ? '' : ('x'*15)), | |
169 | :onfocus => "this.value=''; this.name='repository[password]';", |
|
169 | :onfocus => "this.value=''; this.name='repository[password]';", | |
170 | :onchange => "this.name='repository[password]';")) |
|
170 | :onchange => "this.name='repository[password]';")) | |
171 | end |
|
171 | end | |
172 |
|
172 | |||
173 | def darcs_field_tags(form, repository) |
|
173 | def darcs_field_tags(form, repository) | |
174 | content_tag('p', form.text_field( |
|
174 | content_tag('p', form.text_field( | |
175 | :url, :label => l(:field_path_to_repository), |
|
175 | :url, :label => l(:field_path_to_repository), | |
176 | :size => 60, :required => true, |
|
176 | :size => 60, :required => true, | |
177 | :disabled => (repository && !repository.new_record?))) + |
|
177 | :disabled => (repository && !repository.new_record?))) + | |
178 | content_tag('p', form.select( |
|
178 | content_tag('p', form.select( | |
179 | :log_encoding, [nil] + Setting::ENCODINGS, |
|
179 | :log_encoding, [nil] + Setting::ENCODINGS, | |
180 | :label => l(:field_commit_logs_encoding), :required => true)) |
|
180 | :label => l(:field_commit_logs_encoding), :required => true)) | |
181 | end |
|
181 | end | |
182 |
|
182 | |||
183 | def mercurial_field_tags(form, repository) |
|
183 | def mercurial_field_tags(form, repository) | |
184 | content_tag('p', form.text_field( |
|
184 | content_tag('p', form.text_field( | |
185 | :url, :label => l(:field_path_to_repository), |
|
185 | :url, :label => l(:field_path_to_repository), | |
186 | :size => 60, :required => true, |
|
186 | :size => 60, :required => true, | |
187 | :disabled => (repository && !repository.root_url.blank?) |
|
187 | :disabled => (repository && !repository.root_url.blank?) | |
188 | ) + |
|
188 | ) + | |
189 | '<br />'.html_safe + l(:text_mercurial_repository_note)) + |
|
189 | '<br />'.html_safe + l(:text_mercurial_repository_note)) + | |
190 | content_tag('p', form.select( |
|
190 | content_tag('p', form.select( | |
191 | :path_encoding, [nil] + Setting::ENCODINGS, |
|
191 | :path_encoding, [nil] + Setting::ENCODINGS, | |
192 | :label => l(:field_scm_path_encoding) |
|
192 | :label => l(:field_scm_path_encoding) | |
193 | ) + |
|
193 | ) + | |
194 | '<br />'.html_safe + l(:text_scm_path_encoding_note)) |
|
194 | '<br />'.html_safe + l(:text_scm_path_encoding_note)) | |
195 | end |
|
195 | end | |
196 |
|
196 | |||
197 | def git_field_tags(form, repository) |
|
197 | def git_field_tags(form, repository) | |
198 | content_tag('p', form.text_field( |
|
198 | content_tag('p', form.text_field( | |
199 | :url, :label => l(:field_path_to_repository), |
|
199 | :url, :label => l(:field_path_to_repository), | |
200 | :size => 60, :required => true, |
|
200 | :size => 60, :required => true, | |
201 | :disabled => (repository && !repository.root_url.blank?) |
|
201 | :disabled => (repository && !repository.root_url.blank?) | |
202 | ) + |
|
202 | ) + | |
203 | '<br />'.html_safe + |
|
203 | '<br />'.html_safe + | |
204 | l(:text_git_repository_note)) + |
|
204 | l(:text_git_repository_note)) + | |
205 | content_tag('p', form.select( |
|
205 | content_tag('p', form.select( | |
206 | :path_encoding, [nil] + Setting::ENCODINGS, |
|
206 | :path_encoding, [nil] + Setting::ENCODINGS, | |
207 | :label => l(:field_scm_path_encoding) |
|
207 | :label => l(:field_scm_path_encoding) | |
208 | ) + |
|
208 | ) + | |
209 | '<br />'.html_safe + l(:text_scm_path_encoding_note)) + |
|
209 | '<br />'.html_safe + l(:text_scm_path_encoding_note)) + | |
210 | content_tag('p', form.check_box( |
|
210 | content_tag('p', form.check_box( | |
211 | :extra_report_last_commit, |
|
211 | :extra_report_last_commit, | |
212 | :label => l(:label_git_report_last_commit) |
|
212 | :label => l(:label_git_report_last_commit) | |
213 | )) |
|
213 | )) | |
214 | end |
|
214 | end | |
215 |
|
215 | |||
216 | def cvs_field_tags(form, repository) |
|
216 | def cvs_field_tags(form, repository) | |
217 | content_tag('p', form.text_field( |
|
217 | content_tag('p', form.text_field( | |
218 | :root_url, |
|
218 | :root_url, | |
219 | :label => l(:field_cvsroot), |
|
219 | :label => l(:field_cvsroot), | |
220 | :size => 60, :required => true, |
|
220 | :size => 60, :required => true, | |
221 | :disabled => !repository.new_record?)) + |
|
221 | :disabled => !repository.new_record?)) + | |
222 | content_tag('p', form.text_field( |
|
222 | content_tag('p', form.text_field( | |
223 | :url, |
|
223 | :url, | |
224 | :label => l(:field_cvs_module), |
|
224 | :label => l(:field_cvs_module), | |
225 | :size => 30, :required => true, |
|
225 | :size => 30, :required => true, | |
226 | :disabled => !repository.new_record?)) + |
|
226 | :disabled => !repository.new_record?)) + | |
227 | content_tag('p', form.select( |
|
227 | content_tag('p', form.select( | |
228 | :log_encoding, [nil] + Setting::ENCODINGS, |
|
228 | :log_encoding, [nil] + Setting::ENCODINGS, | |
229 | :label => l(:field_commit_logs_encoding), :required => true)) + |
|
229 | :label => l(:field_commit_logs_encoding), :required => true)) + | |
230 | content_tag('p', form.select( |
|
230 | content_tag('p', form.select( | |
231 | :path_encoding, [nil] + Setting::ENCODINGS, |
|
231 | :path_encoding, [nil] + Setting::ENCODINGS, | |
232 | :label => l(:field_scm_path_encoding) |
|
232 | :label => l(:field_scm_path_encoding) | |
233 | ) + |
|
233 | ) + | |
234 | '<br />'.html_safe + l(:text_scm_path_encoding_note)) |
|
234 | '<br />'.html_safe + l(:text_scm_path_encoding_note)) | |
235 | end |
|
235 | end | |
236 |
|
236 | |||
237 | def bazaar_field_tags(form, repository) |
|
237 | def bazaar_field_tags(form, repository) | |
238 | content_tag('p', form.text_field( |
|
238 | content_tag('p', form.text_field( | |
239 | :url, :label => l(:field_path_to_repository), |
|
239 | :url, :label => l(:field_path_to_repository), | |
240 | :size => 60, :required => true, |
|
240 | :size => 60, :required => true, | |
241 | :disabled => (repository && !repository.new_record?))) + |
|
241 | :disabled => (repository && !repository.new_record?))) + | |
242 | content_tag('p', form.select( |
|
242 | content_tag('p', form.select( | |
243 | :log_encoding, [nil] + Setting::ENCODINGS, |
|
243 | :log_encoding, [nil] + Setting::ENCODINGS, | |
244 | :label => l(:field_commit_logs_encoding), :required => true)) |
|
244 | :label => l(:field_commit_logs_encoding), :required => true)) | |
245 | end |
|
245 | end | |
246 |
|
246 | |||
247 | def filesystem_field_tags(form, repository) |
|
247 | def filesystem_field_tags(form, repository) | |
248 | content_tag('p', form.text_field( |
|
248 | content_tag('p', form.text_field( | |
249 | :url, :label => l(:field_root_directory), |
|
249 | :url, :label => l(:field_root_directory), | |
250 | :size => 60, :required => true, |
|
250 | :size => 60, :required => true, | |
251 | :disabled => (repository && !repository.root_url.blank?))) + |
|
251 | :disabled => (repository && !repository.root_url.blank?))) + | |
252 | content_tag('p', form.select( |
|
252 | content_tag('p', form.select( | |
253 | :path_encoding, [nil] + Setting::ENCODINGS, |
|
253 | :path_encoding, [nil] + Setting::ENCODINGS, | |
254 | :label => l(:field_scm_path_encoding) |
|
254 | :label => l(:field_scm_path_encoding) | |
255 | ) + |
|
255 | ) + | |
256 | '<br />'.html_safe + l(:text_scm_path_encoding_note)) |
|
256 | '<br />'.html_safe + l(:text_scm_path_encoding_note)) | |
257 | end |
|
257 | end | |
258 |
|
258 | |||
259 | def index_commits(commits, heads) |
|
259 | def index_commits(commits, heads) | |
260 | return nil if commits.nil? or commits.first.parents.nil? |
|
260 | return nil if commits.nil? or commits.first.parents.nil? | |
261 |
|
261 | |||
262 | refs_map = {} |
|
262 | refs_map = {} | |
263 | heads.each do |head| |
|
263 | heads.each do |head| | |
264 | refs_map[head.scmid] ||= [] |
|
264 | refs_map[head.scmid] ||= [] | |
265 | refs_map[head.scmid] << head |
|
265 | refs_map[head.scmid] << head | |
266 | end |
|
266 | end | |
267 |
|
267 | |||
268 | commits_by_scmid = {} |
|
268 | commits_by_scmid = {} | |
269 | commits.reverse.each_with_index do |commit, commit_index| |
|
269 | commits.reverse.each_with_index do |commit, commit_index| | |
270 |
|
270 | |||
271 | commits_by_scmid[commit.scmid] = { |
|
271 | commits_by_scmid[commit.scmid] = { | |
272 | :parent_scmids => commit.parents.collect { |parent| parent.scmid }, |
|
272 | :parent_scmids => commit.parents.collect { |parent| parent.scmid }, | |
273 | :rdmid => commit_index, |
|
273 | :rdmid => commit_index, | |
274 | :refs => refs_map.include?(commit.scmid) ? refs_map[commit.scmid].join(" ") : nil, |
|
274 | :refs => refs_map.include?(commit.scmid) ? refs_map[commit.scmid].join(" ") : nil, | |
275 | :scmid => commit.scmid, |
|
275 | :scmid => commit.scmid, | |
276 | :href => block_given? ? yield(commit.scmid) : commit.scmid |
|
276 | :href => block_given? ? yield(commit.scmid) : commit.scmid | |
277 | } |
|
277 | } | |
278 | end |
|
278 | end | |
279 |
|
279 | |||
280 | heads.sort! { |head1, head2| head1.to_s <=> head2.to_s } |
|
280 | heads.sort! { |head1, head2| head1.to_s <=> head2.to_s } | |
281 |
|
281 | |||
282 | space = nil |
|
282 | space = nil | |
283 | heads.each do |head| |
|
283 | heads.each do |head| | |
284 | if commits_by_scmid.include? head.scmid |
|
284 | if commits_by_scmid.include? head.scmid | |
285 | space = index_head((space || -1) + 1, head, commits_by_scmid) |
|
285 | space = index_head((space || -1) + 1, head, commits_by_scmid) | |
286 | end |
|
286 | end | |
287 | end |
|
287 | end | |
288 |
|
288 | |||
289 | # when no head matched anything use first commit |
|
289 | # when no head matched anything use first commit | |
290 | space ||= index_head(0, commits.first, commits_by_scmid) |
|
290 | space ||= index_head(0, commits.first, commits_by_scmid) | |
291 |
|
291 | |||
292 | return commits_by_scmid, space |
|
292 | return commits_by_scmid, space | |
293 | end |
|
293 | end | |
294 |
|
294 | |||
295 | def index_head(space, commit, commits_by_scmid) |
|
295 | def index_head(space, commit, commits_by_scmid) | |
296 |
|
296 | |||
297 | stack = [[space, commits_by_scmid[commit.scmid]]] |
|
297 | stack = [[space, commits_by_scmid[commit.scmid]]] | |
298 | max_space = space |
|
298 | max_space = space | |
299 |
|
299 | |||
300 | until stack.empty? |
|
300 | until stack.empty? | |
301 | space, commit = stack.pop |
|
301 | space, commit = stack.pop | |
302 | commit[:space] = space if commit[:space].nil? |
|
302 | commit[:space] = space if commit[:space].nil? | |
303 |
|
303 | |||
304 | space -= 1 |
|
304 | space -= 1 | |
305 | commit[:parent_scmids].each_with_index do |parent_scmid, parent_index| |
|
305 | commit[:parent_scmids].each_with_index do |parent_scmid, parent_index| | |
306 |
|
306 | |||
307 | parent_commit = commits_by_scmid[parent_scmid] |
|
307 | parent_commit = commits_by_scmid[parent_scmid] | |
308 |
|
308 | |||
309 | if parent_commit and parent_commit[:space].nil? |
|
309 | if parent_commit and parent_commit[:space].nil? | |
310 |
|
310 | |||
311 | stack.unshift [space += 1, parent_commit] |
|
311 | stack.unshift [space += 1, parent_commit] | |
312 | end |
|
312 | end | |
313 | end |
|
313 | end | |
314 | max_space = space if max_space < space |
|
314 | max_space = space if max_space < space | |
315 | end |
|
315 | end | |
316 | max_space |
|
316 | max_space | |
317 | end |
|
317 | end | |
318 | end |
|
318 | end |
@@ -1,286 +1,286 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2012 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 'iconv' |
|
18 | require 'iconv' | |
19 |
|
19 | |||
20 | class Changeset < ActiveRecord::Base |
|
20 | class Changeset < ActiveRecord::Base | |
21 | belongs_to :repository |
|
21 | belongs_to :repository | |
22 | belongs_to :user |
|
22 | belongs_to :user | |
23 | has_many :changes, :dependent => :delete_all |
|
23 | has_many :filechanges, :class_name => 'Change', :dependent => :delete_all | |
24 | has_and_belongs_to_many :issues |
|
24 | has_and_belongs_to_many :issues | |
25 | has_and_belongs_to_many :parents, |
|
25 | has_and_belongs_to_many :parents, | |
26 | :class_name => "Changeset", |
|
26 | :class_name => "Changeset", | |
27 | :join_table => "#{table_name_prefix}changeset_parents#{table_name_suffix}", |
|
27 | :join_table => "#{table_name_prefix}changeset_parents#{table_name_suffix}", | |
28 | :association_foreign_key => 'parent_id', :foreign_key => 'changeset_id' |
|
28 | :association_foreign_key => 'parent_id', :foreign_key => 'changeset_id' | |
29 | has_and_belongs_to_many :children, |
|
29 | has_and_belongs_to_many :children, | |
30 | :class_name => "Changeset", |
|
30 | :class_name => "Changeset", | |
31 | :join_table => "#{table_name_prefix}changeset_parents#{table_name_suffix}", |
|
31 | :join_table => "#{table_name_prefix}changeset_parents#{table_name_suffix}", | |
32 | :association_foreign_key => 'changeset_id', :foreign_key => 'parent_id' |
|
32 | :association_foreign_key => 'changeset_id', :foreign_key => 'parent_id' | |
33 |
|
33 | |||
34 | acts_as_event :title => Proc.new {|o| o.title}, |
|
34 | acts_as_event :title => Proc.new {|o| o.title}, | |
35 | :description => :long_comments, |
|
35 | :description => :long_comments, | |
36 | :datetime => :committed_on, |
|
36 | :datetime => :committed_on, | |
37 | :url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.repository.project, :repository_id => o.repository.identifier_param, :rev => o.identifier}} |
|
37 | :url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.repository.project, :repository_id => o.repository.identifier_param, :rev => o.identifier}} | |
38 |
|
38 | |||
39 | acts_as_searchable :columns => 'comments', |
|
39 | acts_as_searchable :columns => 'comments', | |
40 | :include => {:repository => :project}, |
|
40 | :include => {:repository => :project}, | |
41 | :project_key => "#{Repository.table_name}.project_id", |
|
41 | :project_key => "#{Repository.table_name}.project_id", | |
42 | :date_column => 'committed_on' |
|
42 | :date_column => 'committed_on' | |
43 |
|
43 | |||
44 | acts_as_activity_provider :timestamp => "#{table_name}.committed_on", |
|
44 | acts_as_activity_provider :timestamp => "#{table_name}.committed_on", | |
45 | :author_key => :user_id, |
|
45 | :author_key => :user_id, | |
46 | :find_options => {:include => [:user, {:repository => :project}]} |
|
46 | :find_options => {:include => [:user, {:repository => :project}]} | |
47 |
|
47 | |||
48 | validates_presence_of :repository_id, :revision, :committed_on, :commit_date |
|
48 | validates_presence_of :repository_id, :revision, :committed_on, :commit_date | |
49 | validates_uniqueness_of :revision, :scope => :repository_id |
|
49 | validates_uniqueness_of :revision, :scope => :repository_id | |
50 | validates_uniqueness_of :scmid, :scope => :repository_id, :allow_nil => true |
|
50 | validates_uniqueness_of :scmid, :scope => :repository_id, :allow_nil => true | |
51 |
|
51 | |||
52 | scope :visible, |
|
52 | scope :visible, | |
53 | lambda {|*args| { :include => {:repository => :project}, |
|
53 | lambda {|*args| { :include => {:repository => :project}, | |
54 | :conditions => Project.allowed_to_condition(args.shift || User.current, :view_changesets, *args) } } |
|
54 | :conditions => Project.allowed_to_condition(args.shift || User.current, :view_changesets, *args) } } | |
55 |
|
55 | |||
56 | after_create :scan_for_issues |
|
56 | after_create :scan_for_issues | |
57 | before_create :before_create_cs |
|
57 | before_create :before_create_cs | |
58 |
|
58 | |||
59 | def revision=(r) |
|
59 | def revision=(r) | |
60 | write_attribute :revision, (r.nil? ? nil : r.to_s) |
|
60 | write_attribute :revision, (r.nil? ? nil : r.to_s) | |
61 | end |
|
61 | end | |
62 |
|
62 | |||
63 | # Returns the identifier of this changeset; depending on repository backends |
|
63 | # Returns the identifier of this changeset; depending on repository backends | |
64 | def identifier |
|
64 | def identifier | |
65 | if repository.class.respond_to? :changeset_identifier |
|
65 | if repository.class.respond_to? :changeset_identifier | |
66 | repository.class.changeset_identifier self |
|
66 | repository.class.changeset_identifier self | |
67 | else |
|
67 | else | |
68 | revision.to_s |
|
68 | revision.to_s | |
69 | end |
|
69 | end | |
70 | end |
|
70 | end | |
71 |
|
71 | |||
72 | def committed_on=(date) |
|
72 | def committed_on=(date) | |
73 | self.commit_date = date |
|
73 | self.commit_date = date | |
74 | super |
|
74 | super | |
75 | end |
|
75 | end | |
76 |
|
76 | |||
77 | # Returns the readable identifier |
|
77 | # Returns the readable identifier | |
78 | def format_identifier |
|
78 | def format_identifier | |
79 | if repository.class.respond_to? :format_changeset_identifier |
|
79 | if repository.class.respond_to? :format_changeset_identifier | |
80 | repository.class.format_changeset_identifier self |
|
80 | repository.class.format_changeset_identifier self | |
81 | else |
|
81 | else | |
82 | identifier |
|
82 | identifier | |
83 | end |
|
83 | end | |
84 | end |
|
84 | end | |
85 |
|
85 | |||
86 | def project |
|
86 | def project | |
87 | repository.project |
|
87 | repository.project | |
88 | end |
|
88 | end | |
89 |
|
89 | |||
90 | def author |
|
90 | def author | |
91 | user || committer.to_s.split('<').first |
|
91 | user || committer.to_s.split('<').first | |
92 | end |
|
92 | end | |
93 |
|
93 | |||
94 | def before_create_cs |
|
94 | def before_create_cs | |
95 | self.committer = self.class.to_utf8(self.committer, repository.repo_log_encoding) |
|
95 | self.committer = self.class.to_utf8(self.committer, repository.repo_log_encoding) | |
96 | self.comments = self.class.normalize_comments( |
|
96 | self.comments = self.class.normalize_comments( | |
97 | self.comments, repository.repo_log_encoding) |
|
97 | self.comments, repository.repo_log_encoding) | |
98 | self.user = repository.find_committer_user(self.committer) |
|
98 | self.user = repository.find_committer_user(self.committer) | |
99 | end |
|
99 | end | |
100 |
|
100 | |||
101 | def scan_for_issues |
|
101 | def scan_for_issues | |
102 | scan_comment_for_issue_ids |
|
102 | scan_comment_for_issue_ids | |
103 | end |
|
103 | end | |
104 |
|
104 | |||
105 | TIMELOG_RE = / |
|
105 | TIMELOG_RE = / | |
106 | ( |
|
106 | ( | |
107 | ((\d+)(h|hours?))((\d+)(m|min)?)? |
|
107 | ((\d+)(h|hours?))((\d+)(m|min)?)? | |
108 | | |
|
108 | | | |
109 | ((\d+)(h|hours?|m|min)) |
|
109 | ((\d+)(h|hours?|m|min)) | |
110 | | |
|
110 | | | |
111 | (\d+):(\d+) |
|
111 | (\d+):(\d+) | |
112 | | |
|
112 | | | |
113 | (\d+([\.,]\d+)?)h? |
|
113 | (\d+([\.,]\d+)?)h? | |
114 | ) |
|
114 | ) | |
115 | /x |
|
115 | /x | |
116 |
|
116 | |||
117 | def scan_comment_for_issue_ids |
|
117 | def scan_comment_for_issue_ids | |
118 | return if comments.blank? |
|
118 | return if comments.blank? | |
119 | # keywords used to reference issues |
|
119 | # keywords used to reference issues | |
120 | ref_keywords = Setting.commit_ref_keywords.downcase.split(",").collect(&:strip) |
|
120 | ref_keywords = Setting.commit_ref_keywords.downcase.split(",").collect(&:strip) | |
121 | ref_keywords_any = ref_keywords.delete('*') |
|
121 | ref_keywords_any = ref_keywords.delete('*') | |
122 | # keywords used to fix issues |
|
122 | # keywords used to fix issues | |
123 | fix_keywords = Setting.commit_fix_keywords.downcase.split(",").collect(&:strip) |
|
123 | fix_keywords = Setting.commit_fix_keywords.downcase.split(",").collect(&:strip) | |
124 |
|
124 | |||
125 | kw_regexp = (ref_keywords + fix_keywords).collect{|kw| Regexp.escape(kw)}.join("|") |
|
125 | kw_regexp = (ref_keywords + fix_keywords).collect{|kw| Regexp.escape(kw)}.join("|") | |
126 |
|
126 | |||
127 | referenced_issues = [] |
|
127 | referenced_issues = [] | |
128 |
|
128 | |||
129 | comments.scan(/([\s\(\[,-]|^)((#{kw_regexp})[\s:]+)?(#\d+(\s+@#{TIMELOG_RE})?([\s,;&]+#\d+(\s+@#{TIMELOG_RE})?)*)(?=[[:punct:]]|\s|<|$)/i) do |match| |
|
129 | comments.scan(/([\s\(\[,-]|^)((#{kw_regexp})[\s:]+)?(#\d+(\s+@#{TIMELOG_RE})?([\s,;&]+#\d+(\s+@#{TIMELOG_RE})?)*)(?=[[:punct:]]|\s|<|$)/i) do |match| | |
130 | action, refs = match[2], match[3] |
|
130 | action, refs = match[2], match[3] | |
131 | next unless action.present? || ref_keywords_any |
|
131 | next unless action.present? || ref_keywords_any | |
132 |
|
132 | |||
133 | refs.scan(/#(\d+)(\s+@#{TIMELOG_RE})?/).each do |m| |
|
133 | refs.scan(/#(\d+)(\s+@#{TIMELOG_RE})?/).each do |m| | |
134 | issue, hours = find_referenced_issue_by_id(m[0].to_i), m[2] |
|
134 | issue, hours = find_referenced_issue_by_id(m[0].to_i), m[2] | |
135 | if issue |
|
135 | if issue | |
136 | referenced_issues << issue |
|
136 | referenced_issues << issue | |
137 | fix_issue(issue) if fix_keywords.include?(action.to_s.downcase) |
|
137 | fix_issue(issue) if fix_keywords.include?(action.to_s.downcase) | |
138 | log_time(issue, hours) if hours && Setting.commit_logtime_enabled? |
|
138 | log_time(issue, hours) if hours && Setting.commit_logtime_enabled? | |
139 | end |
|
139 | end | |
140 | end |
|
140 | end | |
141 | end |
|
141 | end | |
142 |
|
142 | |||
143 | referenced_issues.uniq! |
|
143 | referenced_issues.uniq! | |
144 | self.issues = referenced_issues unless referenced_issues.empty? |
|
144 | self.issues = referenced_issues unless referenced_issues.empty? | |
145 | end |
|
145 | end | |
146 |
|
146 | |||
147 | def short_comments |
|
147 | def short_comments | |
148 | @short_comments || split_comments.first |
|
148 | @short_comments || split_comments.first | |
149 | end |
|
149 | end | |
150 |
|
150 | |||
151 | def long_comments |
|
151 | def long_comments | |
152 | @long_comments || split_comments.last |
|
152 | @long_comments || split_comments.last | |
153 | end |
|
153 | end | |
154 |
|
154 | |||
155 | def text_tag(ref_project=nil) |
|
155 | def text_tag(ref_project=nil) | |
156 | tag = if scmid? |
|
156 | tag = if scmid? | |
157 | "commit:#{scmid}" |
|
157 | "commit:#{scmid}" | |
158 | else |
|
158 | else | |
159 | "r#{revision}" |
|
159 | "r#{revision}" | |
160 | end |
|
160 | end | |
161 | if repository && repository.identifier.present? |
|
161 | if repository && repository.identifier.present? | |
162 | tag = "#{repository.identifier}|#{tag}" |
|
162 | tag = "#{repository.identifier}|#{tag}" | |
163 | end |
|
163 | end | |
164 | if ref_project && project && ref_project != project |
|
164 | if ref_project && project && ref_project != project | |
165 | tag = "#{project.identifier}:#{tag}" |
|
165 | tag = "#{project.identifier}:#{tag}" | |
166 | end |
|
166 | end | |
167 | tag |
|
167 | tag | |
168 | end |
|
168 | end | |
169 |
|
169 | |||
170 | # Returns the title used for the changeset in the activity/search results |
|
170 | # Returns the title used for the changeset in the activity/search results | |
171 | def title |
|
171 | def title | |
172 | repo = (repository && repository.identifier.present?) ? " (#{repository.identifier})" : '' |
|
172 | repo = (repository && repository.identifier.present?) ? " (#{repository.identifier})" : '' | |
173 | comm = short_comments.blank? ? '' : (': ' + short_comments) |
|
173 | comm = short_comments.blank? ? '' : (': ' + short_comments) | |
174 | "#{l(:label_revision)} #{format_identifier}#{repo}#{comm}" |
|
174 | "#{l(:label_revision)} #{format_identifier}#{repo}#{comm}" | |
175 | end |
|
175 | end | |
176 |
|
176 | |||
177 | # Returns the previous changeset |
|
177 | # Returns the previous changeset | |
178 | def previous |
|
178 | def previous | |
179 | @previous ||= Changeset.find(:first, |
|
179 | @previous ||= Changeset.find(:first, | |
180 | :conditions => ['id < ? AND repository_id = ?', |
|
180 | :conditions => ['id < ? AND repository_id = ?', | |
181 | self.id, self.repository_id], |
|
181 | self.id, self.repository_id], | |
182 | :order => 'id DESC') |
|
182 | :order => 'id DESC') | |
183 | end |
|
183 | end | |
184 |
|
184 | |||
185 | # Returns the next changeset |
|
185 | # Returns the next changeset | |
186 | def next |
|
186 | def next | |
187 | @next ||= Changeset.find(:first, |
|
187 | @next ||= Changeset.find(:first, | |
188 | :conditions => ['id > ? AND repository_id = ?', |
|
188 | :conditions => ['id > ? AND repository_id = ?', | |
189 | self.id, self.repository_id], |
|
189 | self.id, self.repository_id], | |
190 | :order => 'id ASC') |
|
190 | :order => 'id ASC') | |
191 | end |
|
191 | end | |
192 |
|
192 | |||
193 | # Creates a new Change from it's common parameters |
|
193 | # Creates a new Change from it's common parameters | |
194 | def create_change(change) |
|
194 | def create_change(change) | |
195 | Change.create(:changeset => self, |
|
195 | Change.create(:changeset => self, | |
196 | :action => change[:action], |
|
196 | :action => change[:action], | |
197 | :path => change[:path], |
|
197 | :path => change[:path], | |
198 | :from_path => change[:from_path], |
|
198 | :from_path => change[:from_path], | |
199 | :from_revision => change[:from_revision]) |
|
199 | :from_revision => change[:from_revision]) | |
200 | end |
|
200 | end | |
201 |
|
201 | |||
202 | # Finds an issue that can be referenced by the commit message |
|
202 | # Finds an issue that can be referenced by the commit message | |
203 | def find_referenced_issue_by_id(id) |
|
203 | def find_referenced_issue_by_id(id) | |
204 | return nil if id.blank? |
|
204 | return nil if id.blank? | |
205 | issue = Issue.find_by_id(id.to_i, :include => :project) |
|
205 | issue = Issue.find_by_id(id.to_i, :include => :project) | |
206 | if Setting.commit_cross_project_ref? |
|
206 | if Setting.commit_cross_project_ref? | |
207 | # all issues can be referenced/fixed |
|
207 | # all issues can be referenced/fixed | |
208 | elsif issue |
|
208 | elsif issue | |
209 | # issue that belong to the repository project, a subproject or a parent project only |
|
209 | # issue that belong to the repository project, a subproject or a parent project only | |
210 | unless issue.project && |
|
210 | unless issue.project && | |
211 | (project == issue.project || project.is_ancestor_of?(issue.project) || |
|
211 | (project == issue.project || project.is_ancestor_of?(issue.project) || | |
212 | project.is_descendant_of?(issue.project)) |
|
212 | project.is_descendant_of?(issue.project)) | |
213 | issue = nil |
|
213 | issue = nil | |
214 | end |
|
214 | end | |
215 | end |
|
215 | end | |
216 | issue |
|
216 | issue | |
217 | end |
|
217 | end | |
218 |
|
218 | |||
219 | private |
|
219 | private | |
220 |
|
220 | |||
221 | def fix_issue(issue) |
|
221 | def fix_issue(issue) | |
222 | status = IssueStatus.find_by_id(Setting.commit_fix_status_id.to_i) |
|
222 | status = IssueStatus.find_by_id(Setting.commit_fix_status_id.to_i) | |
223 | if status.nil? |
|
223 | if status.nil? | |
224 | logger.warn("No status matches commit_fix_status_id setting (#{Setting.commit_fix_status_id})") if logger |
|
224 | logger.warn("No status matches commit_fix_status_id setting (#{Setting.commit_fix_status_id})") if logger | |
225 | return issue |
|
225 | return issue | |
226 | end |
|
226 | end | |
227 |
|
227 | |||
228 | # the issue may have been updated by the closure of another one (eg. duplicate) |
|
228 | # the issue may have been updated by the closure of another one (eg. duplicate) | |
229 | issue.reload |
|
229 | issue.reload | |
230 | # don't change the status is the issue is closed |
|
230 | # don't change the status is the issue is closed | |
231 | return if issue.status && issue.status.is_closed? |
|
231 | return if issue.status && issue.status.is_closed? | |
232 |
|
232 | |||
233 | journal = issue.init_journal(user || User.anonymous, ll(Setting.default_language, :text_status_changed_by_changeset, text_tag(issue.project))) |
|
233 | journal = issue.init_journal(user || User.anonymous, ll(Setting.default_language, :text_status_changed_by_changeset, text_tag(issue.project))) | |
234 | issue.status = status |
|
234 | issue.status = status | |
235 | unless Setting.commit_fix_done_ratio.blank? |
|
235 | unless Setting.commit_fix_done_ratio.blank? | |
236 | issue.done_ratio = Setting.commit_fix_done_ratio.to_i |
|
236 | issue.done_ratio = Setting.commit_fix_done_ratio.to_i | |
237 | end |
|
237 | end | |
238 | Redmine::Hook.call_hook(:model_changeset_scan_commit_for_issue_ids_pre_issue_update, |
|
238 | Redmine::Hook.call_hook(:model_changeset_scan_commit_for_issue_ids_pre_issue_update, | |
239 | { :changeset => self, :issue => issue }) |
|
239 | { :changeset => self, :issue => issue }) | |
240 | unless issue.save |
|
240 | unless issue.save | |
241 | logger.warn("Issue ##{issue.id} could not be saved by changeset #{id}: #{issue.errors.full_messages}") if logger |
|
241 | logger.warn("Issue ##{issue.id} could not be saved by changeset #{id}: #{issue.errors.full_messages}") if logger | |
242 | end |
|
242 | end | |
243 | issue |
|
243 | issue | |
244 | end |
|
244 | end | |
245 |
|
245 | |||
246 | def log_time(issue, hours) |
|
246 | def log_time(issue, hours) | |
247 | time_entry = TimeEntry.new( |
|
247 | time_entry = TimeEntry.new( | |
248 | :user => user, |
|
248 | :user => user, | |
249 | :hours => hours, |
|
249 | :hours => hours, | |
250 | :issue => issue, |
|
250 | :issue => issue, | |
251 | :spent_on => commit_date, |
|
251 | :spent_on => commit_date, | |
252 | :comments => l(:text_time_logged_by_changeset, :value => text_tag(issue.project), |
|
252 | :comments => l(:text_time_logged_by_changeset, :value => text_tag(issue.project), | |
253 | :locale => Setting.default_language) |
|
253 | :locale => Setting.default_language) | |
254 | ) |
|
254 | ) | |
255 | time_entry.activity = log_time_activity unless log_time_activity.nil? |
|
255 | time_entry.activity = log_time_activity unless log_time_activity.nil? | |
256 |
|
256 | |||
257 | unless time_entry.save |
|
257 | unless time_entry.save | |
258 | logger.warn("TimeEntry could not be created by changeset #{id}: #{time_entry.errors.full_messages}") if logger |
|
258 | logger.warn("TimeEntry could not be created by changeset #{id}: #{time_entry.errors.full_messages}") if logger | |
259 | end |
|
259 | end | |
260 | time_entry |
|
260 | time_entry | |
261 | end |
|
261 | end | |
262 |
|
262 | |||
263 | def log_time_activity |
|
263 | def log_time_activity | |
264 | if Setting.commit_logtime_activity_id.to_i > 0 |
|
264 | if Setting.commit_logtime_activity_id.to_i > 0 | |
265 | TimeEntryActivity.find_by_id(Setting.commit_logtime_activity_id.to_i) |
|
265 | TimeEntryActivity.find_by_id(Setting.commit_logtime_activity_id.to_i) | |
266 | end |
|
266 | end | |
267 | end |
|
267 | end | |
268 |
|
268 | |||
269 | def split_comments |
|
269 | def split_comments | |
270 | comments =~ /\A(.+?)\r?\n(.*)$/m |
|
270 | comments =~ /\A(.+?)\r?\n(.*)$/m | |
271 | @short_comments = $1 || comments |
|
271 | @short_comments = $1 || comments | |
272 | @long_comments = $2.to_s.strip |
|
272 | @long_comments = $2.to_s.strip | |
273 | return @short_comments, @long_comments |
|
273 | return @short_comments, @long_comments | |
274 | end |
|
274 | end | |
275 |
|
275 | |||
276 | public |
|
276 | public | |
277 |
|
277 | |||
278 | # Strips and reencodes a commit log before insertion into the database |
|
278 | # Strips and reencodes a commit log before insertion into the database | |
279 | def self.normalize_comments(str, encoding) |
|
279 | def self.normalize_comments(str, encoding) | |
280 | Changeset.to_utf8(str.to_s.strip, encoding) |
|
280 | Changeset.to_utf8(str.to_s.strip, encoding) | |
281 | end |
|
281 | end | |
282 |
|
282 | |||
283 | def self.to_utf8(str, encoding) |
|
283 | def self.to_utf8(str, encoding) | |
284 | Redmine::CodesetUtil.to_utf8(str, encoding) |
|
284 | Redmine::CodesetUtil.to_utf8(str, encoding) | |
285 | end |
|
285 | end | |
286 | end |
|
286 | end |
@@ -1,397 +1,397 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2012 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 | class ScmFetchError < Exception; end |
|
18 | class ScmFetchError < Exception; end | |
19 |
|
19 | |||
20 | class Repository < ActiveRecord::Base |
|
20 | class Repository < ActiveRecord::Base | |
21 | include Redmine::Ciphering |
|
21 | include Redmine::Ciphering | |
22 |
|
22 | |||
23 | belongs_to :project |
|
23 | belongs_to :project | |
24 | has_many :changesets, :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC" |
|
24 | has_many :changesets, :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC" | |
25 | has_many :changes, :through => :changesets |
|
25 | has_many :filechanges, :class_name => 'Change', :through => :changesets | |
26 |
|
26 | |||
27 | serialize :extra_info |
|
27 | serialize :extra_info | |
28 |
|
28 | |||
29 | before_save :check_default |
|
29 | before_save :check_default | |
30 |
|
30 | |||
31 | # Raw SQL to delete changesets and changes in the database |
|
31 | # Raw SQL to delete changesets and changes in the database | |
32 | # has_many :changesets, :dependent => :destroy is too slow for big repositories |
|
32 | # has_many :changesets, :dependent => :destroy is too slow for big repositories | |
33 | before_destroy :clear_changesets |
|
33 | before_destroy :clear_changesets | |
34 |
|
34 | |||
35 | validates_length_of :password, :maximum => 255, :allow_nil => true |
|
35 | validates_length_of :password, :maximum => 255, :allow_nil => true | |
36 | validates_length_of :identifier, :maximum => 255, :allow_blank => true |
|
36 | validates_length_of :identifier, :maximum => 255, :allow_blank => true | |
37 | validates_presence_of :identifier, :unless => Proc.new { |r| r.is_default? || r.set_as_default? } |
|
37 | validates_presence_of :identifier, :unless => Proc.new { |r| r.is_default? || r.set_as_default? } | |
38 | validates_uniqueness_of :identifier, :scope => :project_id, :allow_blank => true |
|
38 | validates_uniqueness_of :identifier, :scope => :project_id, :allow_blank => true | |
39 | validates_exclusion_of :identifier, :in => %w(show entry raw changes annotate diff show stats graph) |
|
39 | validates_exclusion_of :identifier, :in => %w(show entry raw changes annotate diff show stats graph) | |
40 | # donwcase letters, digits, dashes but not digits only |
|
40 | # donwcase letters, digits, dashes but not digits only | |
41 | validates_format_of :identifier, :with => /^(?!\d+$)[a-z0-9\-]*$/, :allow_blank => true |
|
41 | validates_format_of :identifier, :with => /^(?!\d+$)[a-z0-9\-]*$/, :allow_blank => true | |
42 | # Checks if the SCM is enabled when creating a repository |
|
42 | # Checks if the SCM is enabled when creating a repository | |
43 | validate :repo_create_validation, :on => :create |
|
43 | validate :repo_create_validation, :on => :create | |
44 |
|
44 | |||
45 | def repo_create_validation |
|
45 | def repo_create_validation | |
46 | unless Setting.enabled_scm.include?(self.class.name.demodulize) |
|
46 | unless Setting.enabled_scm.include?(self.class.name.demodulize) | |
47 | errors.add(:type, :invalid) |
|
47 | errors.add(:type, :invalid) | |
48 | end |
|
48 | end | |
49 | end |
|
49 | end | |
50 |
|
50 | |||
51 | def self.human_attribute_name(attribute_key_name, *args) |
|
51 | def self.human_attribute_name(attribute_key_name, *args) | |
52 | attr_name = attribute_key_name.to_s |
|
52 | attr_name = attribute_key_name.to_s | |
53 | if attr_name == "log_encoding" |
|
53 | if attr_name == "log_encoding" | |
54 | attr_name = "commit_logs_encoding" |
|
54 | attr_name = "commit_logs_encoding" | |
55 | end |
|
55 | end | |
56 | super(attr_name, *args) |
|
56 | super(attr_name, *args) | |
57 | end |
|
57 | end | |
58 |
|
58 | |||
59 | # Removes leading and trailing whitespace |
|
59 | # Removes leading and trailing whitespace | |
60 | def url=(arg) |
|
60 | def url=(arg) | |
61 | write_attribute(:url, arg ? arg.to_s.strip : nil) |
|
61 | write_attribute(:url, arg ? arg.to_s.strip : nil) | |
62 | end |
|
62 | end | |
63 |
|
63 | |||
64 | # Removes leading and trailing whitespace |
|
64 | # Removes leading and trailing whitespace | |
65 | def root_url=(arg) |
|
65 | def root_url=(arg) | |
66 | write_attribute(:root_url, arg ? arg.to_s.strip : nil) |
|
66 | write_attribute(:root_url, arg ? arg.to_s.strip : nil) | |
67 | end |
|
67 | end | |
68 |
|
68 | |||
69 | def password |
|
69 | def password | |
70 | read_ciphered_attribute(:password) |
|
70 | read_ciphered_attribute(:password) | |
71 | end |
|
71 | end | |
72 |
|
72 | |||
73 | def password=(arg) |
|
73 | def password=(arg) | |
74 | write_ciphered_attribute(:password, arg) |
|
74 | write_ciphered_attribute(:password, arg) | |
75 | end |
|
75 | end | |
76 |
|
76 | |||
77 | def scm_adapter |
|
77 | def scm_adapter | |
78 | self.class.scm_adapter_class |
|
78 | self.class.scm_adapter_class | |
79 | end |
|
79 | end | |
80 |
|
80 | |||
81 | def scm |
|
81 | def scm | |
82 | unless @scm |
|
82 | unless @scm | |
83 | @scm = self.scm_adapter.new(url, root_url, |
|
83 | @scm = self.scm_adapter.new(url, root_url, | |
84 | login, password, path_encoding) |
|
84 | login, password, path_encoding) | |
85 | if root_url.blank? && @scm.root_url.present? |
|
85 | if root_url.blank? && @scm.root_url.present? | |
86 | update_attribute(:root_url, @scm.root_url) |
|
86 | update_attribute(:root_url, @scm.root_url) | |
87 | end |
|
87 | end | |
88 | end |
|
88 | end | |
89 | @scm |
|
89 | @scm | |
90 | end |
|
90 | end | |
91 |
|
91 | |||
92 | def scm_name |
|
92 | def scm_name | |
93 | self.class.scm_name |
|
93 | self.class.scm_name | |
94 | end |
|
94 | end | |
95 |
|
95 | |||
96 | def name |
|
96 | def name | |
97 | if identifier.present? |
|
97 | if identifier.present? | |
98 | identifier |
|
98 | identifier | |
99 | elsif is_default? |
|
99 | elsif is_default? | |
100 | l(:field_repository_is_default) |
|
100 | l(:field_repository_is_default) | |
101 | else |
|
101 | else | |
102 | scm_name |
|
102 | scm_name | |
103 | end |
|
103 | end | |
104 | end |
|
104 | end | |
105 |
|
105 | |||
106 | def identifier_param |
|
106 | def identifier_param | |
107 | if is_default? |
|
107 | if is_default? | |
108 | nil |
|
108 | nil | |
109 | elsif identifier.present? |
|
109 | elsif identifier.present? | |
110 | identifier |
|
110 | identifier | |
111 | else |
|
111 | else | |
112 | id.to_s |
|
112 | id.to_s | |
113 | end |
|
113 | end | |
114 | end |
|
114 | end | |
115 |
|
115 | |||
116 | def <=>(repository) |
|
116 | def <=>(repository) | |
117 | if is_default? |
|
117 | if is_default? | |
118 | -1 |
|
118 | -1 | |
119 | elsif repository.is_default? |
|
119 | elsif repository.is_default? | |
120 | 1 |
|
120 | 1 | |
121 | else |
|
121 | else | |
122 | identifier.to_s <=> repository.identifier.to_s |
|
122 | identifier.to_s <=> repository.identifier.to_s | |
123 | end |
|
123 | end | |
124 | end |
|
124 | end | |
125 |
|
125 | |||
126 | def self.find_by_identifier_param(param) |
|
126 | def self.find_by_identifier_param(param) | |
127 | if param.to_s =~ /^\d+$/ |
|
127 | if param.to_s =~ /^\d+$/ | |
128 | find_by_id(param) |
|
128 | find_by_id(param) | |
129 | else |
|
129 | else | |
130 | find_by_identifier(param) |
|
130 | find_by_identifier(param) | |
131 | end |
|
131 | end | |
132 | end |
|
132 | end | |
133 |
|
133 | |||
134 | def merge_extra_info(arg) |
|
134 | def merge_extra_info(arg) | |
135 | h = extra_info || {} |
|
135 | h = extra_info || {} | |
136 | return h if arg.nil? |
|
136 | return h if arg.nil? | |
137 | h.merge!(arg) |
|
137 | h.merge!(arg) | |
138 | write_attribute(:extra_info, h) |
|
138 | write_attribute(:extra_info, h) | |
139 | end |
|
139 | end | |
140 |
|
140 | |||
141 | def report_last_commit |
|
141 | def report_last_commit | |
142 | true |
|
142 | true | |
143 | end |
|
143 | end | |
144 |
|
144 | |||
145 | def supports_cat? |
|
145 | def supports_cat? | |
146 | scm.supports_cat? |
|
146 | scm.supports_cat? | |
147 | end |
|
147 | end | |
148 |
|
148 | |||
149 | def supports_annotate? |
|
149 | def supports_annotate? | |
150 | scm.supports_annotate? |
|
150 | scm.supports_annotate? | |
151 | end |
|
151 | end | |
152 |
|
152 | |||
153 | def supports_all_revisions? |
|
153 | def supports_all_revisions? | |
154 | true |
|
154 | true | |
155 | end |
|
155 | end | |
156 |
|
156 | |||
157 | def supports_directory_revisions? |
|
157 | def supports_directory_revisions? | |
158 | false |
|
158 | false | |
159 | end |
|
159 | end | |
160 |
|
160 | |||
161 | def supports_revision_graph? |
|
161 | def supports_revision_graph? | |
162 | false |
|
162 | false | |
163 | end |
|
163 | end | |
164 |
|
164 | |||
165 | def entry(path=nil, identifier=nil) |
|
165 | def entry(path=nil, identifier=nil) | |
166 | scm.entry(path, identifier) |
|
166 | scm.entry(path, identifier) | |
167 | end |
|
167 | end | |
168 |
|
168 | |||
169 | def entries(path=nil, identifier=nil) |
|
169 | def entries(path=nil, identifier=nil) | |
170 | scm.entries(path, identifier) |
|
170 | scm.entries(path, identifier) | |
171 | end |
|
171 | end | |
172 |
|
172 | |||
173 | def branches |
|
173 | def branches | |
174 | scm.branches |
|
174 | scm.branches | |
175 | end |
|
175 | end | |
176 |
|
176 | |||
177 | def tags |
|
177 | def tags | |
178 | scm.tags |
|
178 | scm.tags | |
179 | end |
|
179 | end | |
180 |
|
180 | |||
181 | def default_branch |
|
181 | def default_branch | |
182 | nil |
|
182 | nil | |
183 | end |
|
183 | end | |
184 |
|
184 | |||
185 | def properties(path, identifier=nil) |
|
185 | def properties(path, identifier=nil) | |
186 | scm.properties(path, identifier) |
|
186 | scm.properties(path, identifier) | |
187 | end |
|
187 | end | |
188 |
|
188 | |||
189 | def cat(path, identifier=nil) |
|
189 | def cat(path, identifier=nil) | |
190 | scm.cat(path, identifier) |
|
190 | scm.cat(path, identifier) | |
191 | end |
|
191 | end | |
192 |
|
192 | |||
193 | def diff(path, rev, rev_to) |
|
193 | def diff(path, rev, rev_to) | |
194 | scm.diff(path, rev, rev_to) |
|
194 | scm.diff(path, rev, rev_to) | |
195 | end |
|
195 | end | |
196 |
|
196 | |||
197 | def diff_format_revisions(cs, cs_to, sep=':') |
|
197 | def diff_format_revisions(cs, cs_to, sep=':') | |
198 | text = "" |
|
198 | text = "" | |
199 | text << cs_to.format_identifier + sep if cs_to |
|
199 | text << cs_to.format_identifier + sep if cs_to | |
200 | text << cs.format_identifier if cs |
|
200 | text << cs.format_identifier if cs | |
201 | text |
|
201 | text | |
202 | end |
|
202 | end | |
203 |
|
203 | |||
204 | # Returns a path relative to the url of the repository |
|
204 | # Returns a path relative to the url of the repository | |
205 | def relative_path(path) |
|
205 | def relative_path(path) | |
206 | path |
|
206 | path | |
207 | end |
|
207 | end | |
208 |
|
208 | |||
209 | # Finds and returns a revision with a number or the beginning of a hash |
|
209 | # Finds and returns a revision with a number or the beginning of a hash | |
210 | def find_changeset_by_name(name) |
|
210 | def find_changeset_by_name(name) | |
211 | return nil if name.blank? |
|
211 | return nil if name.blank? | |
212 | s = name.to_s |
|
212 | s = name.to_s | |
213 | changesets.find(:first, :conditions => (s.match(/^\d*$/) ? |
|
213 | changesets.find(:first, :conditions => (s.match(/^\d*$/) ? | |
214 | ["revision = ?", s] : ["revision LIKE ?", s + '%'])) |
|
214 | ["revision = ?", s] : ["revision LIKE ?", s + '%'])) | |
215 | end |
|
215 | end | |
216 |
|
216 | |||
217 | def latest_changeset |
|
217 | def latest_changeset | |
218 | @latest_changeset ||= changesets.find(:first) |
|
218 | @latest_changeset ||= changesets.find(:first) | |
219 | end |
|
219 | end | |
220 |
|
220 | |||
221 | # Returns the latest changesets for +path+ |
|
221 | # Returns the latest changesets for +path+ | |
222 | # Default behaviour is to search in cached changesets |
|
222 | # Default behaviour is to search in cached changesets | |
223 | def latest_changesets(path, rev, limit=10) |
|
223 | def latest_changesets(path, rev, limit=10) | |
224 | if path.blank? |
|
224 | if path.blank? | |
225 | changesets.find( |
|
225 | changesets.find( | |
226 | :all, |
|
226 | :all, | |
227 | :include => :user, |
|
227 | :include => :user, | |
228 | :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC", |
|
228 | :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC", | |
229 | :limit => limit) |
|
229 | :limit => limit) | |
230 | else |
|
230 | else | |
231 | changes.find( |
|
231 | filechanges.find( | |
232 | :all, |
|
232 | :all, | |
233 | :include => {:changeset => :user}, |
|
233 | :include => {:changeset => :user}, | |
234 | :conditions => ["path = ?", path.with_leading_slash], |
|
234 | :conditions => ["path = ?", path.with_leading_slash], | |
235 | :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC", |
|
235 | :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC", | |
236 | :limit => limit |
|
236 | :limit => limit | |
237 | ).collect(&:changeset) |
|
237 | ).collect(&:changeset) | |
238 | end |
|
238 | end | |
239 | end |
|
239 | end | |
240 |
|
240 | |||
241 | def scan_changesets_for_issue_ids |
|
241 | def scan_changesets_for_issue_ids | |
242 | self.changesets.each(&:scan_comment_for_issue_ids) |
|
242 | self.changesets.each(&:scan_comment_for_issue_ids) | |
243 | end |
|
243 | end | |
244 |
|
244 | |||
245 | # Returns an array of committers usernames and associated user_id |
|
245 | # Returns an array of committers usernames and associated user_id | |
246 | def committers |
|
246 | def committers | |
247 | @committers ||= Changeset.connection.select_rows( |
|
247 | @committers ||= Changeset.connection.select_rows( | |
248 | "SELECT DISTINCT committer, user_id FROM #{Changeset.table_name} WHERE repository_id = #{id}") |
|
248 | "SELECT DISTINCT committer, user_id FROM #{Changeset.table_name} WHERE repository_id = #{id}") | |
249 | end |
|
249 | end | |
250 |
|
250 | |||
251 | # Maps committers username to a user ids |
|
251 | # Maps committers username to a user ids | |
252 | def committer_ids=(h) |
|
252 | def committer_ids=(h) | |
253 | if h.is_a?(Hash) |
|
253 | if h.is_a?(Hash) | |
254 | committers.each do |committer, user_id| |
|
254 | committers.each do |committer, user_id| | |
255 | new_user_id = h[committer] |
|
255 | new_user_id = h[committer] | |
256 | if new_user_id && (new_user_id.to_i != user_id.to_i) |
|
256 | if new_user_id && (new_user_id.to_i != user_id.to_i) | |
257 | new_user_id = (new_user_id.to_i > 0 ? new_user_id.to_i : nil) |
|
257 | new_user_id = (new_user_id.to_i > 0 ? new_user_id.to_i : nil) | |
258 | Changeset.update_all( |
|
258 | Changeset.update_all( | |
259 | "user_id = #{ new_user_id.nil? ? 'NULL' : new_user_id }", |
|
259 | "user_id = #{ new_user_id.nil? ? 'NULL' : new_user_id }", | |
260 | ["repository_id = ? AND committer = ?", id, committer]) |
|
260 | ["repository_id = ? AND committer = ?", id, committer]) | |
261 | end |
|
261 | end | |
262 | end |
|
262 | end | |
263 | @committers = nil |
|
263 | @committers = nil | |
264 | @found_committer_users = nil |
|
264 | @found_committer_users = nil | |
265 | true |
|
265 | true | |
266 | else |
|
266 | else | |
267 | false |
|
267 | false | |
268 | end |
|
268 | end | |
269 | end |
|
269 | end | |
270 |
|
270 | |||
271 | # Returns the Redmine User corresponding to the given +committer+ |
|
271 | # Returns the Redmine User corresponding to the given +committer+ | |
272 | # It will return nil if the committer is not yet mapped and if no User |
|
272 | # It will return nil if the committer is not yet mapped and if no User | |
273 | # with the same username or email was found |
|
273 | # with the same username or email was found | |
274 | def find_committer_user(committer) |
|
274 | def find_committer_user(committer) | |
275 | unless committer.blank? |
|
275 | unless committer.blank? | |
276 | @found_committer_users ||= {} |
|
276 | @found_committer_users ||= {} | |
277 | return @found_committer_users[committer] if @found_committer_users.has_key?(committer) |
|
277 | return @found_committer_users[committer] if @found_committer_users.has_key?(committer) | |
278 |
|
278 | |||
279 | user = nil |
|
279 | user = nil | |
280 | c = changesets.find(:first, :conditions => {:committer => committer}, :include => :user) |
|
280 | c = changesets.find(:first, :conditions => {:committer => committer}, :include => :user) | |
281 | if c && c.user |
|
281 | if c && c.user | |
282 | user = c.user |
|
282 | user = c.user | |
283 | elsif committer.strip =~ /^([^<]+)(<(.*)>)?$/ |
|
283 | elsif committer.strip =~ /^([^<]+)(<(.*)>)?$/ | |
284 | username, email = $1.strip, $3 |
|
284 | username, email = $1.strip, $3 | |
285 | u = User.find_by_login(username) |
|
285 | u = User.find_by_login(username) | |
286 | u ||= User.find_by_mail(email) unless email.blank? |
|
286 | u ||= User.find_by_mail(email) unless email.blank? | |
287 | user = u |
|
287 | user = u | |
288 | end |
|
288 | end | |
289 | @found_committer_users[committer] = user |
|
289 | @found_committer_users[committer] = user | |
290 | user |
|
290 | user | |
291 | end |
|
291 | end | |
292 | end |
|
292 | end | |
293 |
|
293 | |||
294 | def repo_log_encoding |
|
294 | def repo_log_encoding | |
295 | encoding = log_encoding.to_s.strip |
|
295 | encoding = log_encoding.to_s.strip | |
296 | encoding.blank? ? 'UTF-8' : encoding |
|
296 | encoding.blank? ? 'UTF-8' : encoding | |
297 | end |
|
297 | end | |
298 |
|
298 | |||
299 | # Fetches new changesets for all repositories of active projects |
|
299 | # Fetches new changesets for all repositories of active projects | |
300 | # Can be called periodically by an external script |
|
300 | # Can be called periodically by an external script | |
301 | # eg. ruby script/runner "Repository.fetch_changesets" |
|
301 | # eg. ruby script/runner "Repository.fetch_changesets" | |
302 | def self.fetch_changesets |
|
302 | def self.fetch_changesets | |
303 | Project.active.has_module(:repository).all.each do |project| |
|
303 | Project.active.has_module(:repository).all.each do |project| | |
304 | project.repositories.each do |repository| |
|
304 | project.repositories.each do |repository| | |
305 | begin |
|
305 | begin | |
306 | repository.fetch_changesets |
|
306 | repository.fetch_changesets | |
307 | rescue Redmine::Scm::Adapters::CommandFailed => e |
|
307 | rescue Redmine::Scm::Adapters::CommandFailed => e | |
308 | logger.error "scm: error during fetching changesets: #{e.message}" |
|
308 | logger.error "scm: error during fetching changesets: #{e.message}" | |
309 | end |
|
309 | end | |
310 | end |
|
310 | end | |
311 | end |
|
311 | end | |
312 | end |
|
312 | end | |
313 |
|
313 | |||
314 | # scan changeset comments to find related and fixed issues for all repositories |
|
314 | # scan changeset comments to find related and fixed issues for all repositories | |
315 | def self.scan_changesets_for_issue_ids |
|
315 | def self.scan_changesets_for_issue_ids | |
316 | find(:all).each(&:scan_changesets_for_issue_ids) |
|
316 | find(:all).each(&:scan_changesets_for_issue_ids) | |
317 | end |
|
317 | end | |
318 |
|
318 | |||
319 | def self.scm_name |
|
319 | def self.scm_name | |
320 | 'Abstract' |
|
320 | 'Abstract' | |
321 | end |
|
321 | end | |
322 |
|
322 | |||
323 | def self.available_scm |
|
323 | def self.available_scm | |
324 | subclasses.collect {|klass| [klass.scm_name, klass.name]} |
|
324 | subclasses.collect {|klass| [klass.scm_name, klass.name]} | |
325 | end |
|
325 | end | |
326 |
|
326 | |||
327 | def self.factory(klass_name, *args) |
|
327 | def self.factory(klass_name, *args) | |
328 | klass = "Repository::#{klass_name}".constantize |
|
328 | klass = "Repository::#{klass_name}".constantize | |
329 | klass.new(*args) |
|
329 | klass.new(*args) | |
330 | rescue |
|
330 | rescue | |
331 | nil |
|
331 | nil | |
332 | end |
|
332 | end | |
333 |
|
333 | |||
334 | def self.scm_adapter_class |
|
334 | def self.scm_adapter_class | |
335 | nil |
|
335 | nil | |
336 | end |
|
336 | end | |
337 |
|
337 | |||
338 | def self.scm_command |
|
338 | def self.scm_command | |
339 | ret = "" |
|
339 | ret = "" | |
340 | begin |
|
340 | begin | |
341 | ret = self.scm_adapter_class.client_command if self.scm_adapter_class |
|
341 | ret = self.scm_adapter_class.client_command if self.scm_adapter_class | |
342 | rescue Exception => e |
|
342 | rescue Exception => e | |
343 | logger.error "scm: error during get command: #{e.message}" |
|
343 | logger.error "scm: error during get command: #{e.message}" | |
344 | end |
|
344 | end | |
345 | ret |
|
345 | ret | |
346 | end |
|
346 | end | |
347 |
|
347 | |||
348 | def self.scm_version_string |
|
348 | def self.scm_version_string | |
349 | ret = "" |
|
349 | ret = "" | |
350 | begin |
|
350 | begin | |
351 | ret = self.scm_adapter_class.client_version_string if self.scm_adapter_class |
|
351 | ret = self.scm_adapter_class.client_version_string if self.scm_adapter_class | |
352 | rescue Exception => e |
|
352 | rescue Exception => e | |
353 | logger.error "scm: error during get version string: #{e.message}" |
|
353 | logger.error "scm: error during get version string: #{e.message}" | |
354 | end |
|
354 | end | |
355 | ret |
|
355 | ret | |
356 | end |
|
356 | end | |
357 |
|
357 | |||
358 | def self.scm_available |
|
358 | def self.scm_available | |
359 | ret = false |
|
359 | ret = false | |
360 | begin |
|
360 | begin | |
361 | ret = self.scm_adapter_class.client_available if self.scm_adapter_class |
|
361 | ret = self.scm_adapter_class.client_available if self.scm_adapter_class | |
362 | rescue Exception => e |
|
362 | rescue Exception => e | |
363 | logger.error "scm: error during get scm available: #{e.message}" |
|
363 | logger.error "scm: error during get scm available: #{e.message}" | |
364 | end |
|
364 | end | |
365 | ret |
|
365 | ret | |
366 | end |
|
366 | end | |
367 |
|
367 | |||
368 | def set_as_default? |
|
368 | def set_as_default? | |
369 | new_record? && project && !Repository.first(:conditions => {:project_id => project.id}) |
|
369 | new_record? && project && !Repository.first(:conditions => {:project_id => project.id}) | |
370 | end |
|
370 | end | |
371 |
|
371 | |||
372 | protected |
|
372 | protected | |
373 |
|
373 | |||
374 | def check_default |
|
374 | def check_default | |
375 | if !is_default? && set_as_default? |
|
375 | if !is_default? && set_as_default? | |
376 | self.is_default = true |
|
376 | self.is_default = true | |
377 | end |
|
377 | end | |
378 | if is_default? && is_default_changed? |
|
378 | if is_default? && is_default_changed? | |
379 | Repository.update_all(["is_default = ?", false], ["project_id = ?", project_id]) |
|
379 | Repository.update_all(["is_default = ?", false], ["project_id = ?", project_id]) | |
380 | end |
|
380 | end | |
381 | end |
|
381 | end | |
382 |
|
382 | |||
383 | private |
|
383 | private | |
384 |
|
384 | |||
385 | # Deletes repository data |
|
385 | # Deletes repository data | |
386 | def clear_changesets |
|
386 | def clear_changesets | |
387 | cs = Changeset.table_name |
|
387 | cs = Changeset.table_name | |
388 | ch = Change.table_name |
|
388 | ch = Change.table_name | |
389 | ci = "#{table_name_prefix}changesets_issues#{table_name_suffix}" |
|
389 | ci = "#{table_name_prefix}changesets_issues#{table_name_suffix}" | |
390 | cp = "#{table_name_prefix}changeset_parents#{table_name_suffix}" |
|
390 | cp = "#{table_name_prefix}changeset_parents#{table_name_suffix}" | |
391 |
|
391 | |||
392 | connection.delete("DELETE FROM #{ch} WHERE #{ch}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})") |
|
392 | connection.delete("DELETE FROM #{ch} WHERE #{ch}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})") | |
393 | connection.delete("DELETE FROM #{ci} WHERE #{ci}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})") |
|
393 | connection.delete("DELETE FROM #{ci} WHERE #{ci}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})") | |
394 | connection.delete("DELETE FROM #{cp} WHERE #{cp}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})") |
|
394 | connection.delete("DELETE FROM #{cp} WHERE #{cp}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})") | |
395 | connection.delete("DELETE FROM #{cs} WHERE #{cs}.repository_id = #{id}") |
|
395 | connection.delete("DELETE FROM #{cs} WHERE #{cs}.repository_id = #{id}") | |
396 | end |
|
396 | end | |
397 | end |
|
397 | end |
@@ -1,204 +1,204 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2012 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/cvs_adapter' |
|
18 | require 'redmine/scm/adapters/cvs_adapter' | |
19 | require 'digest/sha1' |
|
19 | require 'digest/sha1' | |
20 |
|
20 | |||
21 | class Repository::Cvs < Repository |
|
21 | class Repository::Cvs < Repository | |
22 | validates_presence_of :url, :root_url, :log_encoding |
|
22 | validates_presence_of :url, :root_url, :log_encoding | |
23 |
|
23 | |||
24 | def self.human_attribute_name(attribute_key_name, *args) |
|
24 | def self.human_attribute_name(attribute_key_name, *args) | |
25 | attr_name = attribute_key_name.to_s |
|
25 | attr_name = attribute_key_name.to_s | |
26 | if attr_name == "root_url" |
|
26 | if attr_name == "root_url" | |
27 | attr_name = "cvsroot" |
|
27 | attr_name = "cvsroot" | |
28 | elsif attr_name == "url" |
|
28 | elsif attr_name == "url" | |
29 | attr_name = "cvs_module" |
|
29 | attr_name = "cvs_module" | |
30 | end |
|
30 | end | |
31 | super(attr_name, *args) |
|
31 | super(attr_name, *args) | |
32 | end |
|
32 | end | |
33 |
|
33 | |||
34 | def self.scm_adapter_class |
|
34 | def self.scm_adapter_class | |
35 | Redmine::Scm::Adapters::CvsAdapter |
|
35 | Redmine::Scm::Adapters::CvsAdapter | |
36 | end |
|
36 | end | |
37 |
|
37 | |||
38 | def self.scm_name |
|
38 | def self.scm_name | |
39 | 'CVS' |
|
39 | 'CVS' | |
40 | end |
|
40 | end | |
41 |
|
41 | |||
42 | def entry(path=nil, identifier=nil) |
|
42 | def entry(path=nil, identifier=nil) | |
43 | rev = identifier.nil? ? nil : changesets.find_by_revision(identifier) |
|
43 | rev = identifier.nil? ? nil : changesets.find_by_revision(identifier) | |
44 | scm.entry(path, rev.nil? ? nil : rev.committed_on) |
|
44 | scm.entry(path, rev.nil? ? nil : rev.committed_on) | |
45 | end |
|
45 | end | |
46 |
|
46 | |||
47 | def entries(path=nil, identifier=nil) |
|
47 | def entries(path=nil, identifier=nil) | |
48 | rev = nil |
|
48 | rev = nil | |
49 | if ! identifier.nil? |
|
49 | if ! identifier.nil? | |
50 | rev = changesets.find_by_revision(identifier) |
|
50 | rev = changesets.find_by_revision(identifier) | |
51 | return nil if rev.nil? |
|
51 | return nil if rev.nil? | |
52 | end |
|
52 | end | |
53 | entries = scm.entries(path, rev.nil? ? nil : rev.committed_on) |
|
53 | entries = scm.entries(path, rev.nil? ? nil : rev.committed_on) | |
54 | if entries |
|
54 | if entries | |
55 | entries.each() do |entry| |
|
55 | entries.each() do |entry| | |
56 | if ( ! entry.lastrev.nil? ) && ( ! entry.lastrev.revision.nil? ) |
|
56 | if ( ! entry.lastrev.nil? ) && ( ! entry.lastrev.revision.nil? ) | |
57 | change=changes.find_by_revision_and_path( |
|
57 | change = filechanges.find_by_revision_and_path( | |
58 | entry.lastrev.revision, |
|
58 | entry.lastrev.revision, | |
59 | scm.with_leading_slash(entry.path) ) |
|
59 | scm.with_leading_slash(entry.path) ) | |
60 | if change |
|
60 | if change | |
61 | entry.lastrev.identifier = change.changeset.revision |
|
61 | entry.lastrev.identifier = change.changeset.revision | |
62 | entry.lastrev.revision = change.changeset.revision |
|
62 | entry.lastrev.revision = change.changeset.revision | |
63 | entry.lastrev.author = change.changeset.committer |
|
63 | entry.lastrev.author = change.changeset.committer | |
64 | # entry.lastrev.branch = change.branch |
|
64 | # entry.lastrev.branch = change.branch | |
65 | end |
|
65 | end | |
66 | end |
|
66 | end | |
67 | end |
|
67 | end | |
68 | end |
|
68 | end | |
69 | entries |
|
69 | entries | |
70 | end |
|
70 | end | |
71 |
|
71 | |||
72 | def cat(path, identifier=nil) |
|
72 | def cat(path, identifier=nil) | |
73 | rev = nil |
|
73 | rev = nil | |
74 | if ! identifier.nil? |
|
74 | if ! identifier.nil? | |
75 | rev = changesets.find_by_revision(identifier) |
|
75 | rev = changesets.find_by_revision(identifier) | |
76 | return nil if rev.nil? |
|
76 | return nil if rev.nil? | |
77 | end |
|
77 | end | |
78 | scm.cat(path, rev.nil? ? nil : rev.committed_on) |
|
78 | scm.cat(path, rev.nil? ? nil : rev.committed_on) | |
79 | end |
|
79 | end | |
80 |
|
80 | |||
81 | def annotate(path, identifier=nil) |
|
81 | def annotate(path, identifier=nil) | |
82 | rev = nil |
|
82 | rev = nil | |
83 | if ! identifier.nil? |
|
83 | if ! identifier.nil? | |
84 | rev = changesets.find_by_revision(identifier) |
|
84 | rev = changesets.find_by_revision(identifier) | |
85 | return nil if rev.nil? |
|
85 | return nil if rev.nil? | |
86 | end |
|
86 | end | |
87 | scm.annotate(path, rev.nil? ? nil : rev.committed_on) |
|
87 | scm.annotate(path, rev.nil? ? nil : rev.committed_on) | |
88 | end |
|
88 | end | |
89 |
|
89 | |||
90 | def diff(path, rev, rev_to) |
|
90 | def diff(path, rev, rev_to) | |
91 | # convert rev to revision. CVS can't handle changesets here |
|
91 | # convert rev to revision. CVS can't handle changesets here | |
92 | diff=[] |
|
92 | diff=[] | |
93 | changeset_from = changesets.find_by_revision(rev) |
|
93 | changeset_from = changesets.find_by_revision(rev) | |
94 | if rev_to.to_i > 0 |
|
94 | if rev_to.to_i > 0 | |
95 | changeset_to = changesets.find_by_revision(rev_to) |
|
95 | changeset_to = changesets.find_by_revision(rev_to) | |
96 | end |
|
96 | end | |
97 | changeset_from.changes.each() do |change_from| |
|
97 | changeset_from.filechanges.each() do |change_from| | |
98 | revision_from = nil |
|
98 | revision_from = nil | |
99 | revision_to = nil |
|
99 | revision_to = nil | |
100 | if path.nil? || (change_from.path.starts_with? scm.with_leading_slash(path)) |
|
100 | if path.nil? || (change_from.path.starts_with? scm.with_leading_slash(path)) | |
101 | revision_from = change_from.revision |
|
101 | revision_from = change_from.revision | |
102 | end |
|
102 | end | |
103 | if revision_from |
|
103 | if revision_from | |
104 | if changeset_to |
|
104 | if changeset_to | |
105 | changeset_to.changes.each() do |change_to| |
|
105 | changeset_to.filechanges.each() do |change_to| | |
106 | revision_to = change_to.revision if change_to.path == change_from.path |
|
106 | revision_to = change_to.revision if change_to.path == change_from.path | |
107 | end |
|
107 | end | |
108 | end |
|
108 | end | |
109 | unless revision_to |
|
109 | unless revision_to | |
110 | revision_to = scm.get_previous_revision(revision_from) |
|
110 | revision_to = scm.get_previous_revision(revision_from) | |
111 | end |
|
111 | end | |
112 | file_diff = scm.diff(change_from.path, revision_from, revision_to) |
|
112 | file_diff = scm.diff(change_from.path, revision_from, revision_to) | |
113 | diff = diff + file_diff unless file_diff.nil? |
|
113 | diff = diff + file_diff unless file_diff.nil? | |
114 | end |
|
114 | end | |
115 | end |
|
115 | end | |
116 | return diff |
|
116 | return diff | |
117 | end |
|
117 | end | |
118 |
|
118 | |||
119 | def fetch_changesets |
|
119 | def fetch_changesets | |
120 | # some nifty bits to introduce a commit-id with cvs |
|
120 | # some nifty bits to introduce a commit-id with cvs | |
121 | # natively cvs doesn't provide any kind of changesets, |
|
121 | # natively cvs doesn't provide any kind of changesets, | |
122 | # there is only a revision per file. |
|
122 | # there is only a revision per file. | |
123 | # we now take a guess using the author, the commitlog and the commit-date. |
|
123 | # we now take a guess using the author, the commitlog and the commit-date. | |
124 |
|
124 | |||
125 | # last one is the next step to take. the commit-date is not equal for all |
|
125 | # last one is the next step to take. the commit-date is not equal for all | |
126 | # commits in one changeset. cvs update the commit-date when the *,v file was touched. so |
|
126 | # commits in one changeset. cvs update the commit-date when the *,v file was touched. so | |
127 | # we use a small delta here, to merge all changes belonging to _one_ changeset |
|
127 | # we use a small delta here, to merge all changes belonging to _one_ changeset | |
128 | time_delta = 10.seconds |
|
128 | time_delta = 10.seconds | |
129 | fetch_since = latest_changeset ? latest_changeset.committed_on : nil |
|
129 | fetch_since = latest_changeset ? latest_changeset.committed_on : nil | |
130 | transaction do |
|
130 | transaction do | |
131 | tmp_rev_num = 1 |
|
131 | tmp_rev_num = 1 | |
132 | scm.revisions('', fetch_since, nil, :log_encoding => repo_log_encoding) do |revision| |
|
132 | scm.revisions('', fetch_since, nil, :log_encoding => repo_log_encoding) do |revision| | |
133 | # only add the change to the database, if it doen't exists. the cvs log |
|
133 | # only add the change to the database, if it doen't exists. the cvs log | |
134 | # is not exclusive at all. |
|
134 | # is not exclusive at all. | |
135 | tmp_time = revision.time.clone |
|
135 | tmp_time = revision.time.clone | |
136 | unless changes.find_by_path_and_revision( |
|
136 | unless filechanges.find_by_path_and_revision( | |
137 | scm.with_leading_slash(revision.paths[0][:path]), |
|
137 | scm.with_leading_slash(revision.paths[0][:path]), | |
138 | revision.paths[0][:revision] |
|
138 | revision.paths[0][:revision] | |
139 | ) |
|
139 | ) | |
140 | cmt = Changeset.normalize_comments(revision.message, repo_log_encoding) |
|
140 | cmt = Changeset.normalize_comments(revision.message, repo_log_encoding) | |
141 | author_utf8 = Changeset.to_utf8(revision.author, repo_log_encoding) |
|
141 | author_utf8 = Changeset.to_utf8(revision.author, repo_log_encoding) | |
142 | cs = changesets.find( |
|
142 | cs = changesets.find( | |
143 | :first, |
|
143 | :first, | |
144 | :conditions => { |
|
144 | :conditions => { | |
145 | :committed_on => tmp_time - time_delta .. tmp_time + time_delta, |
|
145 | :committed_on => tmp_time - time_delta .. tmp_time + time_delta, | |
146 | :committer => author_utf8, |
|
146 | :committer => author_utf8, | |
147 | :comments => cmt |
|
147 | :comments => cmt | |
148 | } |
|
148 | } | |
149 | ) |
|
149 | ) | |
150 | # create a new changeset.... |
|
150 | # create a new changeset.... | |
151 | unless cs |
|
151 | unless cs | |
152 | # we use a temporaray revision number here (just for inserting) |
|
152 | # we use a temporaray revision number here (just for inserting) | |
153 | # later on, we calculate a continous positive number |
|
153 | # later on, we calculate a continous positive number | |
154 | tmp_time2 = tmp_time.clone.gmtime |
|
154 | tmp_time2 = tmp_time.clone.gmtime | |
155 | branch = revision.paths[0][:branch] |
|
155 | branch = revision.paths[0][:branch] | |
156 | scmid = branch + "-" + tmp_time2.strftime("%Y%m%d-%H%M%S") |
|
156 | scmid = branch + "-" + tmp_time2.strftime("%Y%m%d-%H%M%S") | |
157 | cs = Changeset.create(:repository => self, |
|
157 | cs = Changeset.create(:repository => self, | |
158 | :revision => "tmp#{tmp_rev_num}", |
|
158 | :revision => "tmp#{tmp_rev_num}", | |
159 | :scmid => scmid, |
|
159 | :scmid => scmid, | |
160 | :committer => revision.author, |
|
160 | :committer => revision.author, | |
161 | :committed_on => tmp_time, |
|
161 | :committed_on => tmp_time, | |
162 | :comments => revision.message) |
|
162 | :comments => revision.message) | |
163 | tmp_rev_num += 1 |
|
163 | tmp_rev_num += 1 | |
164 | end |
|
164 | end | |
165 | # convert CVS-File-States to internal Action-abbrevations |
|
165 | # convert CVS-File-States to internal Action-abbrevations | |
166 | # default action is (M)odified |
|
166 | # default action is (M)odified | |
167 | action = "M" |
|
167 | action = "M" | |
168 | if revision.paths[0][:action] == "Exp" && revision.paths[0][:revision] == "1.1" |
|
168 | if revision.paths[0][:action] == "Exp" && revision.paths[0][:revision] == "1.1" | |
169 | action = "A" # add-action always at first revision (= 1.1) |
|
169 | action = "A" # add-action always at first revision (= 1.1) | |
170 | elsif revision.paths[0][:action] == "dead" |
|
170 | elsif revision.paths[0][:action] == "dead" | |
171 | action = "D" # dead-state is similar to Delete |
|
171 | action = "D" # dead-state is similar to Delete | |
172 | end |
|
172 | end | |
173 | Change.create( |
|
173 | Change.create( | |
174 | :changeset => cs, |
|
174 | :changeset => cs, | |
175 | :action => action, |
|
175 | :action => action, | |
176 | :path => scm.with_leading_slash(revision.paths[0][:path]), |
|
176 | :path => scm.with_leading_slash(revision.paths[0][:path]), | |
177 | :revision => revision.paths[0][:revision], |
|
177 | :revision => revision.paths[0][:revision], | |
178 | :branch => revision.paths[0][:branch] |
|
178 | :branch => revision.paths[0][:branch] | |
179 | ) |
|
179 | ) | |
180 | end |
|
180 | end | |
181 | end |
|
181 | end | |
182 |
|
182 | |||
183 | # Renumber new changesets in chronological order |
|
183 | # Renumber new changesets in chronological order | |
184 | Changeset.all( |
|
184 | Changeset.all( | |
185 | :order => 'committed_on ASC, id ASC', |
|
185 | :order => 'committed_on ASC, id ASC', | |
186 | :conditions => ["repository_id = ? AND revision LIKE 'tmp%'", id] |
|
186 | :conditions => ["repository_id = ? AND revision LIKE 'tmp%'", id] | |
187 | ).each do |changeset| |
|
187 | ).each do |changeset| | |
188 | changeset.update_attribute :revision, next_revision_number |
|
188 | changeset.update_attribute :revision, next_revision_number | |
189 | end |
|
189 | end | |
190 | end # transaction |
|
190 | end # transaction | |
191 | @current_revision_number = nil |
|
191 | @current_revision_number = nil | |
192 | end |
|
192 | end | |
193 |
|
193 | |||
194 | private |
|
194 | private | |
195 |
|
195 | |||
196 | # Returns the next revision number to assign to a CVS changeset |
|
196 | # Returns the next revision number to assign to a CVS changeset | |
197 | def next_revision_number |
|
197 | def next_revision_number | |
198 | # Need to retrieve existing revision numbers to sort them as integers |
|
198 | # Need to retrieve existing revision numbers to sort them as integers | |
199 | sql = "SELECT revision FROM #{Changeset.table_name} " |
|
199 | sql = "SELECT revision FROM #{Changeset.table_name} " | |
200 | sql << "WHERE repository_id = #{id} AND revision NOT LIKE 'tmp%'" |
|
200 | sql << "WHERE repository_id = #{id} AND revision NOT LIKE 'tmp%'" | |
201 | @current_revision_number ||= (connection.select_values(sql).collect(&:to_i).max || 0) |
|
201 | @current_revision_number ||= (connection.select_values(sql).collect(&:to_i).max || 0) | |
202 | @current_revision_number += 1 |
|
202 | @current_revision_number += 1 | |
203 | end |
|
203 | end | |
204 | end |
|
204 | end |
@@ -1,113 +1,113 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2012 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/darcs_adapter' |
|
18 | require 'redmine/scm/adapters/darcs_adapter' | |
19 |
|
19 | |||
20 | class Repository::Darcs < Repository |
|
20 | class Repository::Darcs < Repository | |
21 | validates_presence_of :url, :log_encoding |
|
21 | validates_presence_of :url, :log_encoding | |
22 |
|
22 | |||
23 | def self.human_attribute_name(attribute_key_name, *args) |
|
23 | def self.human_attribute_name(attribute_key_name, *args) | |
24 | attr_name = attribute_key_name.to_s |
|
24 | attr_name = attribute_key_name.to_s | |
25 | if attr_name == "url" |
|
25 | if attr_name == "url" | |
26 | attr_name = "path_to_repository" |
|
26 | attr_name = "path_to_repository" | |
27 | end |
|
27 | end | |
28 | super(attr_name, *args) |
|
28 | super(attr_name, *args) | |
29 | end |
|
29 | end | |
30 |
|
30 | |||
31 | def self.scm_adapter_class |
|
31 | def self.scm_adapter_class | |
32 | Redmine::Scm::Adapters::DarcsAdapter |
|
32 | Redmine::Scm::Adapters::DarcsAdapter | |
33 | end |
|
33 | end | |
34 |
|
34 | |||
35 | def self.scm_name |
|
35 | def self.scm_name | |
36 | 'Darcs' |
|
36 | 'Darcs' | |
37 | end |
|
37 | end | |
38 |
|
38 | |||
39 | def supports_directory_revisions? |
|
39 | def supports_directory_revisions? | |
40 | true |
|
40 | true | |
41 | end |
|
41 | end | |
42 |
|
42 | |||
43 | def entry(path=nil, identifier=nil) |
|
43 | def entry(path=nil, identifier=nil) | |
44 | patch = identifier.nil? ? nil : changesets.find_by_revision(identifier) |
|
44 | patch = identifier.nil? ? nil : changesets.find_by_revision(identifier) | |
45 | scm.entry(path, patch.nil? ? nil : patch.scmid) |
|
45 | scm.entry(path, patch.nil? ? nil : patch.scmid) | |
46 | end |
|
46 | end | |
47 |
|
47 | |||
48 | def entries(path=nil, identifier=nil) |
|
48 | def entries(path=nil, identifier=nil) | |
49 | patch = nil |
|
49 | patch = nil | |
50 | if ! identifier.nil? |
|
50 | if ! identifier.nil? | |
51 | patch = changesets.find_by_revision(identifier) |
|
51 | patch = changesets.find_by_revision(identifier) | |
52 | return nil if patch.nil? |
|
52 | return nil if patch.nil? | |
53 | end |
|
53 | end | |
54 | entries = scm.entries(path, patch.nil? ? nil : patch.scmid) |
|
54 | entries = scm.entries(path, patch.nil? ? nil : patch.scmid) | |
55 | if entries |
|
55 | if entries | |
56 | entries.each do |entry| |
|
56 | entries.each do |entry| | |
57 | # Search the DB for the entry's last change |
|
57 | # Search the DB for the entry's last change | |
58 | if entry.lastrev && !entry.lastrev.scmid.blank? |
|
58 | if entry.lastrev && !entry.lastrev.scmid.blank? | |
59 | changeset = changesets.find_by_scmid(entry.lastrev.scmid) |
|
59 | changeset = changesets.find_by_scmid(entry.lastrev.scmid) | |
60 | end |
|
60 | end | |
61 | if changeset |
|
61 | if changeset | |
62 | entry.lastrev.identifier = changeset.revision |
|
62 | entry.lastrev.identifier = changeset.revision | |
63 | entry.lastrev.name = changeset.revision |
|
63 | entry.lastrev.name = changeset.revision | |
64 | entry.lastrev.time = changeset.committed_on |
|
64 | entry.lastrev.time = changeset.committed_on | |
65 | entry.lastrev.author = changeset.committer |
|
65 | entry.lastrev.author = changeset.committer | |
66 | end |
|
66 | end | |
67 | end |
|
67 | end | |
68 | end |
|
68 | end | |
69 | entries |
|
69 | entries | |
70 | end |
|
70 | end | |
71 |
|
71 | |||
72 | def cat(path, identifier=nil) |
|
72 | def cat(path, identifier=nil) | |
73 | patch = identifier.nil? ? nil : changesets.find_by_revision(identifier.to_s) |
|
73 | patch = identifier.nil? ? nil : changesets.find_by_revision(identifier.to_s) | |
74 | scm.cat(path, patch.nil? ? nil : patch.scmid) |
|
74 | scm.cat(path, patch.nil? ? nil : patch.scmid) | |
75 | end |
|
75 | end | |
76 |
|
76 | |||
77 | def diff(path, rev, rev_to) |
|
77 | def diff(path, rev, rev_to) | |
78 | patch_from = changesets.find_by_revision(rev) |
|
78 | patch_from = changesets.find_by_revision(rev) | |
79 | return nil if patch_from.nil? |
|
79 | return nil if patch_from.nil? | |
80 | patch_to = changesets.find_by_revision(rev_to) if rev_to |
|
80 | patch_to = changesets.find_by_revision(rev_to) if rev_to | |
81 | if path.blank? |
|
81 | if path.blank? | |
82 | path = patch_from.changes.collect{|change| change.path}.join(' ') |
|
82 | path = patch_from.filechanges.collect{|change| change.path}.join(' ') | |
83 | end |
|
83 | end | |
84 | patch_from ? scm.diff(path, patch_from.scmid, patch_to ? patch_to.scmid : nil) : nil |
|
84 | patch_from ? scm.diff(path, patch_from.scmid, patch_to ? patch_to.scmid : nil) : nil | |
85 | end |
|
85 | end | |
86 |
|
86 | |||
87 | def fetch_changesets |
|
87 | def fetch_changesets | |
88 | scm_info = scm.info |
|
88 | scm_info = scm.info | |
89 | if scm_info |
|
89 | if scm_info | |
90 | db_last_id = latest_changeset ? latest_changeset.scmid : nil |
|
90 | db_last_id = latest_changeset ? latest_changeset.scmid : nil | |
91 | next_rev = latest_changeset ? latest_changeset.revision.to_i + 1 : 1 |
|
91 | next_rev = latest_changeset ? latest_changeset.revision.to_i + 1 : 1 | |
92 | # latest revision in the repository |
|
92 | # latest revision in the repository | |
93 | scm_revision = scm_info.lastrev.scmid |
|
93 | scm_revision = scm_info.lastrev.scmid | |
94 | unless changesets.find_by_scmid(scm_revision) |
|
94 | unless changesets.find_by_scmid(scm_revision) | |
95 | revisions = scm.revisions('', db_last_id, nil, :with_path => true) |
|
95 | revisions = scm.revisions('', db_last_id, nil, :with_path => true) | |
96 | transaction do |
|
96 | transaction do | |
97 | revisions.reverse_each do |revision| |
|
97 | revisions.reverse_each do |revision| | |
98 | changeset = Changeset.create(:repository => self, |
|
98 | changeset = Changeset.create(:repository => self, | |
99 | :revision => next_rev, |
|
99 | :revision => next_rev, | |
100 | :scmid => revision.scmid, |
|
100 | :scmid => revision.scmid, | |
101 | :committer => revision.author, |
|
101 | :committer => revision.author, | |
102 | :committed_on => revision.time, |
|
102 | :committed_on => revision.time, | |
103 | :comments => revision.message) |
|
103 | :comments => revision.message) | |
104 | revision.paths.each do |change| |
|
104 | revision.paths.each do |change| | |
105 | changeset.create_change(change) |
|
105 | changeset.create_change(change) | |
106 | end |
|
106 | end | |
107 | next_rev += 1 |
|
107 | next_rev += 1 | |
108 | end if revisions |
|
108 | end if revisions | |
109 | end |
|
109 | end | |
110 | end |
|
110 | end | |
111 | end |
|
111 | end | |
112 | end |
|
112 | end | |
113 | end |
|
113 | end |
@@ -1,156 +1,156 | |||||
1 | <%= render :partial => 'action_menu' %> |
|
1 | <%= render :partial => 'action_menu' %> | |
2 |
|
2 | |||
3 | <h2><%= issue_heading(@issue) %></h2> |
|
3 | <h2><%= issue_heading(@issue) %></h2> | |
4 |
|
4 | |||
5 | <div class="<%= @issue.css_classes %> details"> |
|
5 | <div class="<%= @issue.css_classes %> details"> | |
6 | <% if @prev_issue_id || @next_issue_id %> |
|
6 | <% if @prev_issue_id || @next_issue_id %> | |
7 | <div class="next-prev-links contextual"> |
|
7 | <div class="next-prev-links contextual"> | |
8 | <%= link_to_if @prev_issue_id, |
|
8 | <%= link_to_if @prev_issue_id, | |
9 | "\xc2\xab #{l(:label_previous)}", |
|
9 | "\xc2\xab #{l(:label_previous)}", | |
10 | issue_path(@prev_issue_id), |
|
10 | (@prev_issue_id ? issue_path(@prev_issue_id) : nil), | |
11 | :title => "##{@prev_issue_id}" %> | |
|
11 | :title => "##{@prev_issue_id}" %> | | |
12 | <% if @issue_position && @issue_count %> |
|
12 | <% if @issue_position && @issue_count %> | |
13 | <span class="position"><%= l(:label_item_position, :position => @issue_position, :count => @issue_count) %></span> | |
|
13 | <span class="position"><%= l(:label_item_position, :position => @issue_position, :count => @issue_count) %></span> | | |
14 | <% end %> |
|
14 | <% end %> | |
15 | <%= link_to_if @next_issue_id, |
|
15 | <%= link_to_if @next_issue_id, | |
16 | "#{l(:label_next)} \xc2\xbb", |
|
16 | "#{l(:label_next)} \xc2\xbb", | |
17 | issue_path(@next_issue_id), |
|
17 | (@next_issue_id ? issue_path(@next_issue_id) : nil), | |
18 | :title => "##{@next_issue_id}" %> |
|
18 | :title => "##{@next_issue_id}" %> | |
19 | </div> |
|
19 | </div> | |
20 | <% end %> |
|
20 | <% end %> | |
21 |
|
21 | |||
22 | <%= avatar(@issue.author, :size => "50") %> |
|
22 | <%= avatar(@issue.author, :size => "50") %> | |
23 |
|
23 | |||
24 | <div class="subject"> |
|
24 | <div class="subject"> | |
25 | <%= render_issue_subject_with_tree(@issue) %> |
|
25 | <%= render_issue_subject_with_tree(@issue) %> | |
26 | </div> |
|
26 | </div> | |
27 | <p class="author"> |
|
27 | <p class="author"> | |
28 | <%= authoring @issue.created_on, @issue.author %>. |
|
28 | <%= authoring @issue.created_on, @issue.author %>. | |
29 | <% if @issue.created_on != @issue.updated_on %> |
|
29 | <% if @issue.created_on != @issue.updated_on %> | |
30 | <%= l(:label_updated_time, time_tag(@issue.updated_on)).html_safe %>. |
|
30 | <%= l(:label_updated_time, time_tag(@issue.updated_on)).html_safe %>. | |
31 | <% end %> |
|
31 | <% end %> | |
32 | </p> |
|
32 | </p> | |
33 |
|
33 | |||
34 | <table class="attributes"> |
|
34 | <table class="attributes"> | |
35 | <tr> |
|
35 | <tr> | |
36 | <th class="status"><%=l(:field_status)%>:</th><td class="status"><%= h(@issue.status.name) %></td> |
|
36 | <th class="status"><%=l(:field_status)%>:</th><td class="status"><%= h(@issue.status.name) %></td> | |
37 | <th class="start-date"><%=l(:field_start_date)%>:</th><td class="start-date"><%= format_date(@issue.start_date) %></td> |
|
37 | <th class="start-date"><%=l(:field_start_date)%>:</th><td class="start-date"><%= format_date(@issue.start_date) %></td> | |
38 | </tr> |
|
38 | </tr> | |
39 | <tr> |
|
39 | <tr> | |
40 | <th class="priority"><%=l(:field_priority)%>:</th><td class="priority"><%= h(@issue.priority.name) %></td> |
|
40 | <th class="priority"><%=l(:field_priority)%>:</th><td class="priority"><%= h(@issue.priority.name) %></td> | |
41 | <th class="due-date"><%=l(:field_due_date)%>:</th><td class="due-date"><%= format_date(@issue.due_date) %></td> |
|
41 | <th class="due-date"><%=l(:field_due_date)%>:</th><td class="due-date"><%= format_date(@issue.due_date) %></td> | |
42 | </tr> |
|
42 | </tr> | |
43 | <tr> |
|
43 | <tr> | |
44 | <th class="assigned-to"><%=l(:field_assigned_to)%>:</th><td class="assigned-to"><%= avatar(@issue.assigned_to, :size => "14") %><%= @issue.assigned_to ? link_to_user(@issue.assigned_to) : "-" %></td> |
|
44 | <th class="assigned-to"><%=l(:field_assigned_to)%>:</th><td class="assigned-to"><%= avatar(@issue.assigned_to, :size => "14") %><%= @issue.assigned_to ? link_to_user(@issue.assigned_to) : "-" %></td> | |
45 | <th class="progress"><%=l(:field_done_ratio)%>:</th><td class="progress"><%= progress_bar @issue.done_ratio, :width => '80px', :legend => "#{@issue.done_ratio}%" %></td> |
|
45 | <th class="progress"><%=l(:field_done_ratio)%>:</th><td class="progress"><%= progress_bar @issue.done_ratio, :width => '80px', :legend => "#{@issue.done_ratio}%" %></td> | |
46 | </tr> |
|
46 | </tr> | |
47 | <tr> |
|
47 | <tr> | |
48 | <th class="category"><%=l(:field_category)%>:</th><td class="category"><%=h(@issue.category ? @issue.category.name : "-") %></td> |
|
48 | <th class="category"><%=l(:field_category)%>:</th><td class="category"><%=h(@issue.category ? @issue.category.name : "-") %></td> | |
49 | <% if User.current.allowed_to?(:view_time_entries, @project) %> |
|
49 | <% if User.current.allowed_to?(:view_time_entries, @project) %> | |
50 | <th class="spent-time"><%=l(:label_spent_time)%>:</th> |
|
50 | <th class="spent-time"><%=l(:label_spent_time)%>:</th> | |
51 | <td class="spent-time"><%= @issue.total_spent_hours > 0 ? (link_to l_hours(@issue.total_spent_hours), {:controller => 'timelog', :action => 'index', :project_id => @project, :issue_id => @issue}) : "-" %></td> |
|
51 | <td class="spent-time"><%= @issue.total_spent_hours > 0 ? (link_to l_hours(@issue.total_spent_hours), {:controller => 'timelog', :action => 'index', :project_id => @project, :issue_id => @issue}) : "-" %></td> | |
52 | <% else %> |
|
52 | <% else %> | |
53 | <th></th> |
|
53 | <th></th> | |
54 | <td></td> |
|
54 | <td></td> | |
55 | <% end %> |
|
55 | <% end %> | |
56 | </tr> |
|
56 | </tr> | |
57 | <tr> |
|
57 | <tr> | |
58 | <th class="fixed-version"><%=l(:field_fixed_version)%>:</th><td class="fixed-version"><%= @issue.fixed_version ? link_to_version(@issue.fixed_version) : "-" %></td> |
|
58 | <th class="fixed-version"><%=l(:field_fixed_version)%>:</th><td class="fixed-version"><%= @issue.fixed_version ? link_to_version(@issue.fixed_version) : "-" %></td> | |
59 | <% if @issue.estimated_hours %> |
|
59 | <% if @issue.estimated_hours %> | |
60 | <th class="estimated-hours"><%=l(:field_estimated_hours)%>:</th><td class="estimated-hours"><%= l_hours(@issue.estimated_hours) %></td> |
|
60 | <th class="estimated-hours"><%=l(:field_estimated_hours)%>:</th><td class="estimated-hours"><%= l_hours(@issue.estimated_hours) %></td> | |
61 | <% end %> |
|
61 | <% end %> | |
62 | </tr> |
|
62 | </tr> | |
63 | <%= render_custom_fields_rows(@issue) %> |
|
63 | <%= render_custom_fields_rows(@issue) %> | |
64 | <%= call_hook(:view_issues_show_details_bottom, :issue => @issue) %> |
|
64 | <%= call_hook(:view_issues_show_details_bottom, :issue => @issue) %> | |
65 | </table> |
|
65 | </table> | |
66 |
|
66 | |||
67 | <% if @issue.description? || @issue.attachments.any? -%> |
|
67 | <% if @issue.description? || @issue.attachments.any? -%> | |
68 | <hr /> |
|
68 | <hr /> | |
69 | <% if @issue.description? %> |
|
69 | <% if @issue.description? %> | |
70 | <div class="contextual"> |
|
70 | <div class="contextual"> | |
71 | <%= link_to_remote_if_authorized( |
|
71 | <%= link_to_remote_if_authorized( | |
72 | l(:button_quote), |
|
72 | l(:button_quote), | |
73 | { :url => {:controller => 'journals', :action => 'new', :id => @issue} }, |
|
73 | { :url => {:controller => 'journals', :action => 'new', :id => @issue} }, | |
74 | :class => 'icon icon-comment') %> |
|
74 | :class => 'icon icon-comment') %> | |
75 | </div> |
|
75 | </div> | |
76 |
|
76 | |||
77 | <p><strong><%=l(:field_description)%></strong></p> |
|
77 | <p><strong><%=l(:field_description)%></strong></p> | |
78 | <div class="wiki"> |
|
78 | <div class="wiki"> | |
79 | <%= textilizable @issue, :description, :attachments => @issue.attachments %> |
|
79 | <%= textilizable @issue, :description, :attachments => @issue.attachments %> | |
80 | </div> |
|
80 | </div> | |
81 | <% end %> |
|
81 | <% end %> | |
82 | <%= link_to_attachments @issue %> |
|
82 | <%= link_to_attachments @issue %> | |
83 | <% end -%> |
|
83 | <% end -%> | |
84 |
|
84 | |||
85 | <%= call_hook(:view_issues_show_description_bottom, :issue => @issue) %> |
|
85 | <%= call_hook(:view_issues_show_description_bottom, :issue => @issue) %> | |
86 |
|
86 | |||
87 | <% if !@issue.leaf? || User.current.allowed_to?(:manage_subtasks, @project) %> |
|
87 | <% if !@issue.leaf? || User.current.allowed_to?(:manage_subtasks, @project) %> | |
88 | <hr /> |
|
88 | <hr /> | |
89 | <div id="issue_tree"> |
|
89 | <div id="issue_tree"> | |
90 | <div class="contextual"> |
|
90 | <div class="contextual"> | |
91 | <%= link_to(l(:button_add), {:controller => 'issues', :action => 'new', :project_id => @project, :issue => {:parent_issue_id => @issue}}) if User.current.allowed_to?(:manage_subtasks, @project) %> |
|
91 | <%= link_to(l(:button_add), {:controller => 'issues', :action => 'new', :project_id => @project, :issue => {:parent_issue_id => @issue}}) if User.current.allowed_to?(:manage_subtasks, @project) %> | |
92 | </div> |
|
92 | </div> | |
93 | <p><strong><%=l(:label_subtask_plural)%></strong></p> |
|
93 | <p><strong><%=l(:label_subtask_plural)%></strong></p> | |
94 | <%= render_descendants_tree(@issue) unless @issue.leaf? %> |
|
94 | <%= render_descendants_tree(@issue) unless @issue.leaf? %> | |
95 | </div> |
|
95 | </div> | |
96 | <% end %> |
|
96 | <% end %> | |
97 |
|
97 | |||
98 | <% if @relations.present? || User.current.allowed_to?(:manage_issue_relations, @project) %> |
|
98 | <% if @relations.present? || User.current.allowed_to?(:manage_issue_relations, @project) %> | |
99 | <hr /> |
|
99 | <hr /> | |
100 | <div id="relations"> |
|
100 | <div id="relations"> | |
101 | <%= render :partial => 'relations' %> |
|
101 | <%= render :partial => 'relations' %> | |
102 | </div> |
|
102 | </div> | |
103 | <% end %> |
|
103 | <% end %> | |
104 |
|
104 | |||
105 | </div> |
|
105 | </div> | |
106 |
|
106 | |||
107 | <% if @changesets.present? %> |
|
107 | <% if @changesets.present? %> | |
108 | <div id="issue-changesets"> |
|
108 | <div id="issue-changesets"> | |
109 | <h3><%=l(:label_associated_revisions)%></h3> |
|
109 | <h3><%=l(:label_associated_revisions)%></h3> | |
110 | <%= render :partial => 'changesets', :locals => { :changesets => @changesets} %> |
|
110 | <%= render :partial => 'changesets', :locals => { :changesets => @changesets} %> | |
111 | </div> |
|
111 | </div> | |
112 | <% end %> |
|
112 | <% end %> | |
113 |
|
113 | |||
114 | <% if @journals.present? %> |
|
114 | <% if @journals.present? %> | |
115 | <div id="history"> |
|
115 | <div id="history"> | |
116 | <h3><%=l(:label_history)%></h3> |
|
116 | <h3><%=l(:label_history)%></h3> | |
117 | <%= render :partial => 'history', :locals => { :issue => @issue, :journals => @journals } %> |
|
117 | <%= render :partial => 'history', :locals => { :issue => @issue, :journals => @journals } %> | |
118 | </div> |
|
118 | </div> | |
119 | <% end %> |
|
119 | <% end %> | |
120 |
|
120 | |||
121 |
|
121 | |||
122 | <div style="clear: both;"></div> |
|
122 | <div style="clear: both;"></div> | |
123 | <%= render :partial => 'action_menu' %> |
|
123 | <%= render :partial => 'action_menu' %> | |
124 |
|
124 | |||
125 | <div style="clear: both;"></div> |
|
125 | <div style="clear: both;"></div> | |
126 | <% if authorize_for('issues', 'edit') %> |
|
126 | <% if authorize_for('issues', 'edit') %> | |
127 | <div id="update" style="display:none;"> |
|
127 | <div id="update" style="display:none;"> | |
128 | <h3><%= l(:button_update) %></h3> |
|
128 | <h3><%= l(:button_update) %></h3> | |
129 | <%= render :partial => 'edit' %> |
|
129 | <%= render :partial => 'edit' %> | |
130 | </div> |
|
130 | </div> | |
131 | <% end %> |
|
131 | <% end %> | |
132 |
|
132 | |||
133 | <% other_formats_links do |f| %> |
|
133 | <% other_formats_links do |f| %> | |
134 | <%= f.link_to 'Atom', :url => {:key => User.current.rss_key} %> |
|
134 | <%= f.link_to 'Atom', :url => {:key => User.current.rss_key} %> | |
135 | <%= f.link_to 'PDF' %> |
|
135 | <%= f.link_to 'PDF' %> | |
136 | <% end %> |
|
136 | <% end %> | |
137 |
|
137 | |||
138 | <% html_title "#{@issue.tracker.name} ##{@issue.id}: #{@issue.subject}" %> |
|
138 | <% html_title "#{@issue.tracker.name} ##{@issue.id}: #{@issue.subject}" %> | |
139 |
|
139 | |||
140 | <% content_for :sidebar do %> |
|
140 | <% content_for :sidebar do %> | |
141 | <%= render :partial => 'issues/sidebar' %> |
|
141 | <%= render :partial => 'issues/sidebar' %> | |
142 |
|
142 | |||
143 | <% if User.current.allowed_to?(:add_issue_watchers, @project) || |
|
143 | <% if User.current.allowed_to?(:add_issue_watchers, @project) || | |
144 | (@issue.watchers.present? && User.current.allowed_to?(:view_issue_watchers, @project)) %> |
|
144 | (@issue.watchers.present? && User.current.allowed_to?(:view_issue_watchers, @project)) %> | |
145 | <div id="watchers"> |
|
145 | <div id="watchers"> | |
146 | <%= render :partial => 'watchers/watchers', :locals => {:watched => @issue} %> |
|
146 | <%= render :partial => 'watchers/watchers', :locals => {:watched => @issue} %> | |
147 | </div> |
|
147 | </div> | |
148 | <% end %> |
|
148 | <% end %> | |
149 | <% end %> |
|
149 | <% end %> | |
150 |
|
150 | |||
151 | <% content_for :header_tags do %> |
|
151 | <% content_for :header_tags do %> | |
152 | <%= auto_discovery_link_tag(:atom, {:format => 'atom', :key => User.current.rss_key}, :title => "#{@issue.project} - #{@issue.tracker} ##{@issue.id}: #{@issue.subject}") %> |
|
152 | <%= auto_discovery_link_tag(:atom, {:format => 'atom', :key => User.current.rss_key}, :title => "#{@issue.project} - #{@issue.tracker} ##{@issue.id}: #{@issue.subject}") %> | |
153 | <%= stylesheet_link_tag 'scm' %> |
|
153 | <%= stylesheet_link_tag 'scm' %> | |
154 | <% end %> |
|
154 | <% end %> | |
155 |
|
155 | |||
156 | <%= context_menu issues_context_menu_path %> |
|
156 | <%= context_menu issues_context_menu_path %> |
@@ -1,5 +1,5 | |||||
1 | <h2><%= l(:label_repository) %></h2> |
|
1 | <h2><%= l(:label_repository) %></h2> | |
2 |
|
2 | |||
3 |
<%= labelled_form_for :repository, @repository, :url => repository_path(@ |
|
3 | <%= labelled_form_for :repository, @repository, :url => repository_path(@repository), :html => {:method => :put} do |f| %> | |
4 | <%= render :partial => 'form', :locals => {:f => f} %> |
|
4 | <%= render :partial => 'form', :locals => {:f => f} %> | |
5 | <% end %> |
|
5 | <% end %> |
@@ -1,97 +1,97 | |||||
1 | <div class="contextual"> |
|
1 | <div class="contextual"> | |
2 | « |
|
2 | « | |
3 | <% unless @changeset.previous.nil? -%> |
|
3 | <% unless @changeset.previous.nil? -%> | |
4 | <%= link_to_revision(@changeset.previous, @repository, :text => l(:label_previous)) %> |
|
4 | <%= link_to_revision(@changeset.previous, @repository, :text => l(:label_previous)) %> | |
5 | <% else -%> |
|
5 | <% else -%> | |
6 | <%= l(:label_previous) %> |
|
6 | <%= l(:label_previous) %> | |
7 | <% end -%> |
|
7 | <% end -%> | |
8 | | |
|
8 | | | |
9 | <% unless @changeset.next.nil? -%> |
|
9 | <% unless @changeset.next.nil? -%> | |
10 | <%= link_to_revision(@changeset.next, @repository, :text => l(:label_next)) %> |
|
10 | <%= link_to_revision(@changeset.next, @repository, :text => l(:label_next)) %> | |
11 | <% else -%> |
|
11 | <% else -%> | |
12 | <%= l(:label_next) %> |
|
12 | <%= l(:label_next) %> | |
13 | <% end -%> |
|
13 | <% end -%> | |
14 | » |
|
14 | » | |
15 |
|
15 | |||
16 | <%= form_tag({:controller => 'repositories', |
|
16 | <%= form_tag({:controller => 'repositories', | |
17 | :action => 'revision', |
|
17 | :action => 'revision', | |
18 | :id => @project, |
|
18 | :id => @project, | |
19 | :repository_id => @repository.identifier_param, |
|
19 | :repository_id => @repository.identifier_param, | |
20 | :rev => nil}, |
|
20 | :rev => nil}, | |
21 | :method => :get) do %> |
|
21 | :method => :get) do %> | |
22 | <%= text_field_tag 'rev', @rev, :size => 8 %> |
|
22 | <%= text_field_tag 'rev', @rev, :size => 8 %> | |
23 | <%= submit_tag 'OK', :name => nil %> |
|
23 | <%= submit_tag 'OK', :name => nil %> | |
24 | <% end %> |
|
24 | <% end %> | |
25 | </div> |
|
25 | </div> | |
26 |
|
26 | |||
27 | <h2><%= avatar(@changeset.user, :size => "24") %><%= l(:label_revision) %> <%= format_revision(@changeset) %></h2> |
|
27 | <h2><%= avatar(@changeset.user, :size => "24") %><%= l(:label_revision) %> <%= format_revision(@changeset) %></h2> | |
28 |
|
28 | |||
29 | <% if @changeset.scmid.present? || @changeset.parents.present? || @changeset.children.present? %> |
|
29 | <% if @changeset.scmid.present? || @changeset.parents.present? || @changeset.children.present? %> | |
30 | <table class="revision-info"> |
|
30 | <table class="revision-info"> | |
31 | <% if @changeset.scmid.present? %> |
|
31 | <% if @changeset.scmid.present? %> | |
32 | <tr> |
|
32 | <tr> | |
33 | <td>ID</td><td><%= h(@changeset.scmid) %></td> |
|
33 | <td>ID</td><td><%= h(@changeset.scmid) %></td> | |
34 | </tr> |
|
34 | </tr> | |
35 | <% end %> |
|
35 | <% end %> | |
36 | <% if @changeset.parents.present? %> |
|
36 | <% if @changeset.parents.present? %> | |
37 | <tr> |
|
37 | <tr> | |
38 | <td><%= l(:label_parent_revision) %></td> |
|
38 | <td><%= l(:label_parent_revision) %></td> | |
39 | <td> |
|
39 | <td> | |
40 | <%= @changeset.parents.collect{ |
|
40 | <%= @changeset.parents.collect{ | |
41 | |p| link_to_revision(p, @repository, :text => format_revision(p)) |
|
41 | |p| link_to_revision(p, @repository, :text => format_revision(p)) | |
42 | }.join(", ").html_safe %> |
|
42 | }.join(", ").html_safe %> | |
43 | </td> |
|
43 | </td> | |
44 | </tr> |
|
44 | </tr> | |
45 | <% end %> |
|
45 | <% end %> | |
46 | <% if @changeset.children.present? %> |
|
46 | <% if @changeset.children.present? %> | |
47 | <tr> |
|
47 | <tr> | |
48 | <td><%= l(:label_child_revision) %></td> |
|
48 | <td><%= l(:label_child_revision) %></td> | |
49 | <td> |
|
49 | <td> | |
50 | <%= @changeset.children.collect{ |
|
50 | <%= @changeset.children.collect{ | |
51 | |p| link_to_revision(p, @repository, :text => format_revision(p)) |
|
51 | |p| link_to_revision(p, @repository, :text => format_revision(p)) | |
52 | }.join(", ").html_safe %> |
|
52 | }.join(", ").html_safe %> | |
53 | </td> |
|
53 | </td> | |
54 | </tr> |
|
54 | </tr> | |
55 | <% end %> |
|
55 | <% end %> | |
56 | </table> |
|
56 | </table> | |
57 | <% end %> |
|
57 | <% end %> | |
58 |
|
58 | |||
59 | <p> |
|
59 | <p> | |
60 | <span class="author"> |
|
60 | <span class="author"> | |
61 | <%= authoring(@changeset.committed_on, @changeset.author) %> |
|
61 | <%= authoring(@changeset.committed_on, @changeset.author) %> | |
62 | </span> |
|
62 | </span> | |
63 | </p> |
|
63 | </p> | |
64 |
|
64 | |||
65 | <%= textilizable @changeset.comments %> |
|
65 | <%= textilizable @changeset.comments %> | |
66 |
|
66 | |||
67 | <% if @changeset.issues.visible.any? || User.current.allowed_to?(:manage_related_issues, @repository.project) %> |
|
67 | <% if @changeset.issues.visible.any? || User.current.allowed_to?(:manage_related_issues, @repository.project) %> | |
68 | <%= render :partial => 'related_issues' %> |
|
68 | <%= render :partial => 'related_issues' %> | |
69 | <% end %> |
|
69 | <% end %> | |
70 |
|
70 | |||
71 | <% if User.current.allowed_to?(:browse_repository, @project) %> |
|
71 | <% if User.current.allowed_to?(:browse_repository, @project) %> | |
72 | <h3><%= l(:label_attachment_plural) %></h3> |
|
72 | <h3><%= l(:label_attachment_plural) %></h3> | |
73 | <ul id="changes-legend"> |
|
73 | <ul id="changes-legend"> | |
74 | <li class="change change-A"><%= l(:label_added) %></li> |
|
74 | <li class="change change-A"><%= l(:label_added) %></li> | |
75 | <li class="change change-M"><%= l(:label_modified) %></li> |
|
75 | <li class="change change-M"><%= l(:label_modified) %></li> | |
76 | <li class="change change-C"><%= l(:label_copied) %></li> |
|
76 | <li class="change change-C"><%= l(:label_copied) %></li> | |
77 | <li class="change change-R"><%= l(:label_renamed) %></li> |
|
77 | <li class="change change-R"><%= l(:label_renamed) %></li> | |
78 | <li class="change change-D"><%= l(:label_deleted) %></li> |
|
78 | <li class="change change-D"><%= l(:label_deleted) %></li> | |
79 | </ul> |
|
79 | </ul> | |
80 |
|
80 | |||
81 | <p><%= link_to(l(:label_view_diff), |
|
81 | <p><%= link_to(l(:label_view_diff), | |
82 | :action => 'diff', |
|
82 | :action => 'diff', | |
83 | :id => @project, |
|
83 | :id => @project, | |
84 | :repository_id => @repository.identifier_param, |
|
84 | :repository_id => @repository.identifier_param, | |
85 | :path => "", |
|
85 | :path => "", | |
86 | :rev => @changeset.identifier) if @changeset.changes.any? %></p> |
|
86 | :rev => @changeset.identifier) if @changeset.filechanges.any? %></p> | |
87 |
|
87 | |||
88 | <div class="changeset-changes"> |
|
88 | <div class="changeset-changes"> | |
89 | <%= render_changeset_changes %> |
|
89 | <%= render_changeset_changes %> | |
90 | </div> |
|
90 | </div> | |
91 | <% end %> |
|
91 | <% end %> | |
92 |
|
92 | |||
93 | <% content_for :header_tags do %> |
|
93 | <% content_for :header_tags do %> | |
94 | <%= stylesheet_link_tag "scm" %> |
|
94 | <%= stylesheet_link_tag "scm" %> | |
95 | <% end %> |
|
95 | <% end %> | |
96 |
|
96 | |||
97 | <% html_title("#{l(:label_revision)} #{format_revision(@changeset)}") -%> |
|
97 | <% html_title("#{l(:label_revision)} #{format_revision(@changeset)}") -%> |
@@ -1,772 +1,772 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2012 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 'active_record' |
|
18 | require 'active_record' | |
19 | require 'iconv' |
|
19 | require 'iconv' | |
20 | require 'pp' |
|
20 | require 'pp' | |
21 |
|
21 | |||
22 | namespace :redmine do |
|
22 | namespace :redmine do | |
23 | desc 'Trac migration script' |
|
23 | desc 'Trac migration script' | |
24 | task :migrate_from_trac => :environment do |
|
24 | task :migrate_from_trac => :environment do | |
25 |
|
25 | |||
26 | module TracMigrate |
|
26 | module TracMigrate | |
27 | TICKET_MAP = [] |
|
27 | TICKET_MAP = [] | |
28 |
|
28 | |||
29 | DEFAULT_STATUS = IssueStatus.default |
|
29 | DEFAULT_STATUS = IssueStatus.default | |
30 | assigned_status = IssueStatus.find_by_position(2) |
|
30 | assigned_status = IssueStatus.find_by_position(2) | |
31 | resolved_status = IssueStatus.find_by_position(3) |
|
31 | resolved_status = IssueStatus.find_by_position(3) | |
32 | feedback_status = IssueStatus.find_by_position(4) |
|
32 | feedback_status = IssueStatus.find_by_position(4) | |
33 | closed_status = IssueStatus.find :first, :conditions => { :is_closed => true } |
|
33 | closed_status = IssueStatus.find :first, :conditions => { :is_closed => true } | |
34 | STATUS_MAPPING = {'new' => DEFAULT_STATUS, |
|
34 | STATUS_MAPPING = {'new' => DEFAULT_STATUS, | |
35 | 'reopened' => feedback_status, |
|
35 | 'reopened' => feedback_status, | |
36 | 'assigned' => assigned_status, |
|
36 | 'assigned' => assigned_status, | |
37 | 'closed' => closed_status |
|
37 | 'closed' => closed_status | |
38 | } |
|
38 | } | |
39 |
|
39 | |||
40 | priorities = IssuePriority.all |
|
40 | priorities = IssuePriority.all | |
41 | DEFAULT_PRIORITY = priorities[0] |
|
41 | DEFAULT_PRIORITY = priorities[0] | |
42 | PRIORITY_MAPPING = {'lowest' => priorities[0], |
|
42 | PRIORITY_MAPPING = {'lowest' => priorities[0], | |
43 | 'low' => priorities[0], |
|
43 | 'low' => priorities[0], | |
44 | 'normal' => priorities[1], |
|
44 | 'normal' => priorities[1], | |
45 | 'high' => priorities[2], |
|
45 | 'high' => priorities[2], | |
46 | 'highest' => priorities[3], |
|
46 | 'highest' => priorities[3], | |
47 | # --- |
|
47 | # --- | |
48 | 'trivial' => priorities[0], |
|
48 | 'trivial' => priorities[0], | |
49 | 'minor' => priorities[1], |
|
49 | 'minor' => priorities[1], | |
50 | 'major' => priorities[2], |
|
50 | 'major' => priorities[2], | |
51 | 'critical' => priorities[3], |
|
51 | 'critical' => priorities[3], | |
52 | 'blocker' => priorities[4] |
|
52 | 'blocker' => priorities[4] | |
53 | } |
|
53 | } | |
54 |
|
54 | |||
55 | TRACKER_BUG = Tracker.find_by_position(1) |
|
55 | TRACKER_BUG = Tracker.find_by_position(1) | |
56 | TRACKER_FEATURE = Tracker.find_by_position(2) |
|
56 | TRACKER_FEATURE = Tracker.find_by_position(2) | |
57 | DEFAULT_TRACKER = TRACKER_BUG |
|
57 | DEFAULT_TRACKER = TRACKER_BUG | |
58 | TRACKER_MAPPING = {'defect' => TRACKER_BUG, |
|
58 | TRACKER_MAPPING = {'defect' => TRACKER_BUG, | |
59 | 'enhancement' => TRACKER_FEATURE, |
|
59 | 'enhancement' => TRACKER_FEATURE, | |
60 | 'task' => TRACKER_FEATURE, |
|
60 | 'task' => TRACKER_FEATURE, | |
61 | 'patch' =>TRACKER_FEATURE |
|
61 | 'patch' =>TRACKER_FEATURE | |
62 | } |
|
62 | } | |
63 |
|
63 | |||
64 | roles = Role.find(:all, :conditions => {:builtin => 0}, :order => 'position ASC') |
|
64 | roles = Role.find(:all, :conditions => {:builtin => 0}, :order => 'position ASC') | |
65 | manager_role = roles[0] |
|
65 | manager_role = roles[0] | |
66 | developer_role = roles[1] |
|
66 | developer_role = roles[1] | |
67 | DEFAULT_ROLE = roles.last |
|
67 | DEFAULT_ROLE = roles.last | |
68 | ROLE_MAPPING = {'admin' => manager_role, |
|
68 | ROLE_MAPPING = {'admin' => manager_role, | |
69 | 'developer' => developer_role |
|
69 | 'developer' => developer_role | |
70 | } |
|
70 | } | |
71 |
|
71 | |||
72 | class ::Time |
|
72 | class ::Time | |
73 | class << self |
|
73 | class << self | |
74 | alias :real_now :now |
|
74 | alias :real_now :now | |
75 | def now |
|
75 | def now | |
76 | real_now - @fake_diff.to_i |
|
76 | real_now - @fake_diff.to_i | |
77 | end |
|
77 | end | |
78 | def fake(time) |
|
78 | def fake(time) | |
79 | @fake_diff = real_now - time |
|
79 | @fake_diff = real_now - time | |
80 | res = yield |
|
80 | res = yield | |
81 | @fake_diff = 0 |
|
81 | @fake_diff = 0 | |
82 | res |
|
82 | res | |
83 | end |
|
83 | end | |
84 | end |
|
84 | end | |
85 | end |
|
85 | end | |
86 |
|
86 | |||
87 | class TracComponent < ActiveRecord::Base |
|
87 | class TracComponent < ActiveRecord::Base | |
88 | self.table_name = :component |
|
88 | self.table_name = :component | |
89 | end |
|
89 | end | |
90 |
|
90 | |||
91 | class TracMilestone < ActiveRecord::Base |
|
91 | class TracMilestone < ActiveRecord::Base | |
92 | self.table_name = :milestone |
|
92 | self.table_name = :milestone | |
93 | # If this attribute is set a milestone has a defined target timepoint |
|
93 | # If this attribute is set a milestone has a defined target timepoint | |
94 | def due |
|
94 | def due | |
95 | if read_attribute(:due) && read_attribute(:due) > 0 |
|
95 | if read_attribute(:due) && read_attribute(:due) > 0 | |
96 | Time.at(read_attribute(:due)).to_date |
|
96 | Time.at(read_attribute(:due)).to_date | |
97 | else |
|
97 | else | |
98 | nil |
|
98 | nil | |
99 | end |
|
99 | end | |
100 | end |
|
100 | end | |
101 | # This is the real timepoint at which the milestone has finished. |
|
101 | # This is the real timepoint at which the milestone has finished. | |
102 | def completed |
|
102 | def completed | |
103 | if read_attribute(:completed) && read_attribute(:completed) > 0 |
|
103 | if read_attribute(:completed) && read_attribute(:completed) > 0 | |
104 | Time.at(read_attribute(:completed)).to_date |
|
104 | Time.at(read_attribute(:completed)).to_date | |
105 | else |
|
105 | else | |
106 | nil |
|
106 | nil | |
107 | end |
|
107 | end | |
108 | end |
|
108 | end | |
109 |
|
109 | |||
110 | def description |
|
110 | def description | |
111 | # Attribute is named descr in Trac v0.8.x |
|
111 | # Attribute is named descr in Trac v0.8.x | |
112 | has_attribute?(:descr) ? read_attribute(:descr) : read_attribute(:description) |
|
112 | has_attribute?(:descr) ? read_attribute(:descr) : read_attribute(:description) | |
113 | end |
|
113 | end | |
114 | end |
|
114 | end | |
115 |
|
115 | |||
116 | class TracTicketCustom < ActiveRecord::Base |
|
116 | class TracTicketCustom < ActiveRecord::Base | |
117 | self.table_name = :ticket_custom |
|
117 | self.table_name = :ticket_custom | |
118 | end |
|
118 | end | |
119 |
|
119 | |||
120 | class TracAttachment < ActiveRecord::Base |
|
120 | class TracAttachment < ActiveRecord::Base | |
121 | self.table_name = :attachment |
|
121 | self.table_name = :attachment | |
122 | set_inheritance_column :none |
|
122 | set_inheritance_column :none | |
123 |
|
123 | |||
124 | def time; Time.at(read_attribute(:time)) end |
|
124 | def time; Time.at(read_attribute(:time)) end | |
125 |
|
125 | |||
126 | def original_filename |
|
126 | def original_filename | |
127 | filename |
|
127 | filename | |
128 | end |
|
128 | end | |
129 |
|
129 | |||
130 | def content_type |
|
130 | def content_type | |
131 | '' |
|
131 | '' | |
132 | end |
|
132 | end | |
133 |
|
133 | |||
134 | def exist? |
|
134 | def exist? | |
135 | File.file? trac_fullpath |
|
135 | File.file? trac_fullpath | |
136 | end |
|
136 | end | |
137 |
|
137 | |||
138 | def open |
|
138 | def open | |
139 | File.open("#{trac_fullpath}", 'rb') {|f| |
|
139 | File.open("#{trac_fullpath}", 'rb') {|f| | |
140 | @file = f |
|
140 | @file = f | |
141 | yield self |
|
141 | yield self | |
142 | } |
|
142 | } | |
143 | end |
|
143 | end | |
144 |
|
144 | |||
145 | def read(*args) |
|
145 | def read(*args) | |
146 | @file.read(*args) |
|
146 | @file.read(*args) | |
147 | end |
|
147 | end | |
148 |
|
148 | |||
149 | def description |
|
149 | def description | |
150 | read_attribute(:description).to_s.slice(0,255) |
|
150 | read_attribute(:description).to_s.slice(0,255) | |
151 | end |
|
151 | end | |
152 |
|
152 | |||
153 | private |
|
153 | private | |
154 | def trac_fullpath |
|
154 | def trac_fullpath | |
155 | attachment_type = read_attribute(:type) |
|
155 | attachment_type = read_attribute(:type) | |
156 | trac_file = filename.gsub( /[^a-zA-Z0-9\-_\.!~*']/n ) {|x| sprintf('%%%02x', x[0]) } |
|
156 | trac_file = filename.gsub( /[^a-zA-Z0-9\-_\.!~*']/n ) {|x| sprintf('%%%02x', x[0]) } | |
157 | "#{TracMigrate.trac_attachments_directory}/#{attachment_type}/#{id}/#{trac_file}" |
|
157 | "#{TracMigrate.trac_attachments_directory}/#{attachment_type}/#{id}/#{trac_file}" | |
158 | end |
|
158 | end | |
159 | end |
|
159 | end | |
160 |
|
160 | |||
161 | class TracTicket < ActiveRecord::Base |
|
161 | class TracTicket < ActiveRecord::Base | |
162 | self.table_name = :ticket |
|
162 | self.table_name = :ticket | |
163 | set_inheritance_column :none |
|
163 | set_inheritance_column :none | |
164 |
|
164 | |||
165 | # ticket changes: only migrate status changes and comments |
|
165 | # ticket changes: only migrate status changes and comments | |
166 | has_many :changes, :class_name => "TracTicketChange", :foreign_key => :ticket |
|
166 | has_many :ticket_changes, :class_name => "TracTicketChange", :foreign_key => :ticket | |
167 | has_many :customs, :class_name => "TracTicketCustom", :foreign_key => :ticket |
|
167 | has_many :customs, :class_name => "TracTicketCustom", :foreign_key => :ticket | |
168 |
|
168 | |||
169 | def attachments |
|
169 | def attachments | |
170 | TracMigrate::TracAttachment.all(:conditions => ["type = 'ticket' AND id = ?", self.id.to_s]) |
|
170 | TracMigrate::TracAttachment.all(:conditions => ["type = 'ticket' AND id = ?", self.id.to_s]) | |
171 | end |
|
171 | end | |
172 |
|
172 | |||
173 | def ticket_type |
|
173 | def ticket_type | |
174 | read_attribute(:type) |
|
174 | read_attribute(:type) | |
175 | end |
|
175 | end | |
176 |
|
176 | |||
177 | def summary |
|
177 | def summary | |
178 | read_attribute(:summary).blank? ? "(no subject)" : read_attribute(:summary) |
|
178 | read_attribute(:summary).blank? ? "(no subject)" : read_attribute(:summary) | |
179 | end |
|
179 | end | |
180 |
|
180 | |||
181 | def description |
|
181 | def description | |
182 | read_attribute(:description).blank? ? summary : read_attribute(:description) |
|
182 | read_attribute(:description).blank? ? summary : read_attribute(:description) | |
183 | end |
|
183 | end | |
184 |
|
184 | |||
185 | def time; Time.at(read_attribute(:time)) end |
|
185 | def time; Time.at(read_attribute(:time)) end | |
186 | def changetime; Time.at(read_attribute(:changetime)) end |
|
186 | def changetime; Time.at(read_attribute(:changetime)) end | |
187 | end |
|
187 | end | |
188 |
|
188 | |||
189 | class TracTicketChange < ActiveRecord::Base |
|
189 | class TracTicketChange < ActiveRecord::Base | |
190 | self.table_name = :ticket_change |
|
190 | self.table_name = :ticket_change | |
191 |
|
191 | |||
192 | def self.columns |
|
192 | def self.columns | |
193 | # Hides Trac field 'field' to prevent clash with AR field_changed? method (Rails 3.0) |
|
193 | # Hides Trac field 'field' to prevent clash with AR field_changed? method (Rails 3.0) | |
194 | super.select {|column| column.name.to_s != 'field'} |
|
194 | super.select {|column| column.name.to_s != 'field'} | |
195 | end |
|
195 | end | |
196 |
|
196 | |||
197 | def time; Time.at(read_attribute(:time)) end |
|
197 | def time; Time.at(read_attribute(:time)) end | |
198 | end |
|
198 | end | |
199 |
|
199 | |||
200 | TRAC_WIKI_PAGES = %w(InterMapTxt InterTrac InterWiki RecentChanges SandBox TracAccessibility TracAdmin TracBackup TracBrowser TracCgi TracChangeset \ |
|
200 | TRAC_WIKI_PAGES = %w(InterMapTxt InterTrac InterWiki RecentChanges SandBox TracAccessibility TracAdmin TracBackup TracBrowser TracCgi TracChangeset \ | |
201 | TracEnvironment TracFastCgi TracGuide TracImport TracIni TracInstall TracInterfaceCustomization \ |
|
201 | TracEnvironment TracFastCgi TracGuide TracImport TracIni TracInstall TracInterfaceCustomization \ | |
202 | TracLinks TracLogging TracModPython TracNotification TracPermissions TracPlugins TracQuery \ |
|
202 | TracLinks TracLogging TracModPython TracNotification TracPermissions TracPlugins TracQuery \ | |
203 | TracReports TracRevisionLog TracRoadmap TracRss TracSearch TracStandalone TracSupport TracSyntaxColoring TracTickets \ |
|
203 | TracReports TracRevisionLog TracRoadmap TracRss TracSearch TracStandalone TracSupport TracSyntaxColoring TracTickets \ | |
204 | TracTicketsCustomFields TracTimeline TracUnicode TracUpgrade TracWiki WikiDeletePage WikiFormatting \ |
|
204 | TracTicketsCustomFields TracTimeline TracUnicode TracUpgrade TracWiki WikiDeletePage WikiFormatting \ | |
205 | WikiHtml WikiMacros WikiNewPage WikiPageNames WikiProcessors WikiRestructuredText WikiRestructuredTextLinks \ |
|
205 | WikiHtml WikiMacros WikiNewPage WikiPageNames WikiProcessors WikiRestructuredText WikiRestructuredTextLinks \ | |
206 | CamelCase TitleIndex) |
|
206 | CamelCase TitleIndex) | |
207 |
|
207 | |||
208 | class TracWikiPage < ActiveRecord::Base |
|
208 | class TracWikiPage < ActiveRecord::Base | |
209 | self.table_name = :wiki |
|
209 | self.table_name = :wiki | |
210 | set_primary_key :name |
|
210 | set_primary_key :name | |
211 |
|
211 | |||
212 | def self.columns |
|
212 | def self.columns | |
213 | # Hides readonly Trac field to prevent clash with AR readonly? method (Rails 2.0) |
|
213 | # Hides readonly Trac field to prevent clash with AR readonly? method (Rails 2.0) | |
214 | super.select {|column| column.name.to_s != 'readonly'} |
|
214 | super.select {|column| column.name.to_s != 'readonly'} | |
215 | end |
|
215 | end | |
216 |
|
216 | |||
217 | def attachments |
|
217 | def attachments | |
218 | TracMigrate::TracAttachment.all(:conditions => ["type = 'wiki' AND id = ?", self.id.to_s]) |
|
218 | TracMigrate::TracAttachment.all(:conditions => ["type = 'wiki' AND id = ?", self.id.to_s]) | |
219 | end |
|
219 | end | |
220 |
|
220 | |||
221 | def time; Time.at(read_attribute(:time)) end |
|
221 | def time; Time.at(read_attribute(:time)) end | |
222 | end |
|
222 | end | |
223 |
|
223 | |||
224 | class TracPermission < ActiveRecord::Base |
|
224 | class TracPermission < ActiveRecord::Base | |
225 | self.table_name = :permission |
|
225 | self.table_name = :permission | |
226 | end |
|
226 | end | |
227 |
|
227 | |||
228 | class TracSessionAttribute < ActiveRecord::Base |
|
228 | class TracSessionAttribute < ActiveRecord::Base | |
229 | self.table_name = :session_attribute |
|
229 | self.table_name = :session_attribute | |
230 | end |
|
230 | end | |
231 |
|
231 | |||
232 | def self.find_or_create_user(username, project_member = false) |
|
232 | def self.find_or_create_user(username, project_member = false) | |
233 | return User.anonymous if username.blank? |
|
233 | return User.anonymous if username.blank? | |
234 |
|
234 | |||
235 | u = User.find_by_login(username) |
|
235 | u = User.find_by_login(username) | |
236 | if !u |
|
236 | if !u | |
237 | # Create a new user if not found |
|
237 | # Create a new user if not found | |
238 | mail = username[0, User::MAIL_LENGTH_LIMIT] |
|
238 | mail = username[0, User::MAIL_LENGTH_LIMIT] | |
239 | if mail_attr = TracSessionAttribute.find_by_sid_and_name(username, 'email') |
|
239 | if mail_attr = TracSessionAttribute.find_by_sid_and_name(username, 'email') | |
240 | mail = mail_attr.value |
|
240 | mail = mail_attr.value | |
241 | end |
|
241 | end | |
242 | mail = "#{mail}@foo.bar" unless mail.include?("@") |
|
242 | mail = "#{mail}@foo.bar" unless mail.include?("@") | |
243 |
|
243 | |||
244 | name = username |
|
244 | name = username | |
245 | if name_attr = TracSessionAttribute.find_by_sid_and_name(username, 'name') |
|
245 | if name_attr = TracSessionAttribute.find_by_sid_and_name(username, 'name') | |
246 | name = name_attr.value |
|
246 | name = name_attr.value | |
247 | end |
|
247 | end | |
248 | name =~ (/(.*)(\s+\w+)?/) |
|
248 | name =~ (/(.*)(\s+\w+)?/) | |
249 | fn = $1.strip |
|
249 | fn = $1.strip | |
250 | ln = ($2 || '-').strip |
|
250 | ln = ($2 || '-').strip | |
251 |
|
251 | |||
252 | u = User.new :mail => mail.gsub(/[^-@a-z0-9\.]/i, '-'), |
|
252 | u = User.new :mail => mail.gsub(/[^-@a-z0-9\.]/i, '-'), | |
253 | :firstname => fn[0, limit_for(User, 'firstname')], |
|
253 | :firstname => fn[0, limit_for(User, 'firstname')], | |
254 | :lastname => ln[0, limit_for(User, 'lastname')] |
|
254 | :lastname => ln[0, limit_for(User, 'lastname')] | |
255 |
|
255 | |||
256 | u.login = username[0, User::LOGIN_LENGTH_LIMIT].gsub(/[^a-z0-9_\-@\.]/i, '-') |
|
256 | u.login = username[0, User::LOGIN_LENGTH_LIMIT].gsub(/[^a-z0-9_\-@\.]/i, '-') | |
257 | u.password = 'trac' |
|
257 | u.password = 'trac' | |
258 | u.admin = true if TracPermission.find_by_username_and_action(username, 'admin') |
|
258 | u.admin = true if TracPermission.find_by_username_and_action(username, 'admin') | |
259 | # finally, a default user is used if the new user is not valid |
|
259 | # finally, a default user is used if the new user is not valid | |
260 | u = User.find(:first) unless u.save |
|
260 | u = User.find(:first) unless u.save | |
261 | end |
|
261 | end | |
262 | # Make sure he is a member of the project |
|
262 | # Make sure he is a member of the project | |
263 | if project_member && !u.member_of?(@target_project) |
|
263 | if project_member && !u.member_of?(@target_project) | |
264 | role = DEFAULT_ROLE |
|
264 | role = DEFAULT_ROLE | |
265 | if u.admin |
|
265 | if u.admin | |
266 | role = ROLE_MAPPING['admin'] |
|
266 | role = ROLE_MAPPING['admin'] | |
267 | elsif TracPermission.find_by_username_and_action(username, 'developer') |
|
267 | elsif TracPermission.find_by_username_and_action(username, 'developer') | |
268 | role = ROLE_MAPPING['developer'] |
|
268 | role = ROLE_MAPPING['developer'] | |
269 | end |
|
269 | end | |
270 | Member.create(:user => u, :project => @target_project, :roles => [role]) |
|
270 | Member.create(:user => u, :project => @target_project, :roles => [role]) | |
271 | u.reload |
|
271 | u.reload | |
272 | end |
|
272 | end | |
273 | u |
|
273 | u | |
274 | end |
|
274 | end | |
275 |
|
275 | |||
276 | # Basic wiki syntax conversion |
|
276 | # Basic wiki syntax conversion | |
277 | def self.convert_wiki_text(text) |
|
277 | def self.convert_wiki_text(text) | |
278 | # Titles |
|
278 | # Titles | |
279 | text = text.gsub(/^(\=+)\s(.+)\s(\=+)/) {|s| "\nh#{$1.length}. #{$2}\n"} |
|
279 | text = text.gsub(/^(\=+)\s(.+)\s(\=+)/) {|s| "\nh#{$1.length}. #{$2}\n"} | |
280 | # External Links |
|
280 | # External Links | |
281 | text = text.gsub(/\[(http[^\s]+)\s+([^\]]+)\]/) {|s| "\"#{$2}\":#{$1}"} |
|
281 | text = text.gsub(/\[(http[^\s]+)\s+([^\]]+)\]/) {|s| "\"#{$2}\":#{$1}"} | |
282 | # Ticket links: |
|
282 | # Ticket links: | |
283 | # [ticket:234 Text],[ticket:234 This is a test] |
|
283 | # [ticket:234 Text],[ticket:234 This is a test] | |
284 | text = text.gsub(/\[ticket\:([^\ ]+)\ (.+?)\]/, '"\2":/issues/show/\1') |
|
284 | text = text.gsub(/\[ticket\:([^\ ]+)\ (.+?)\]/, '"\2":/issues/show/\1') | |
285 | # ticket:1234 |
|
285 | # ticket:1234 | |
286 | # #1 is working cause Redmine uses the same syntax. |
|
286 | # #1 is working cause Redmine uses the same syntax. | |
287 | text = text.gsub(/ticket\:([^\ ]+)/, '#\1') |
|
287 | text = text.gsub(/ticket\:([^\ ]+)/, '#\1') | |
288 | # Milestone links: |
|
288 | # Milestone links: | |
289 | # [milestone:"0.1.0 Mercury" Milestone 0.1.0 (Mercury)] |
|
289 | # [milestone:"0.1.0 Mercury" Milestone 0.1.0 (Mercury)] | |
290 | # The text "Milestone 0.1.0 (Mercury)" is not converted, |
|
290 | # The text "Milestone 0.1.0 (Mercury)" is not converted, | |
291 | # cause Redmine's wiki does not support this. |
|
291 | # cause Redmine's wiki does not support this. | |
292 | text = text.gsub(/\[milestone\:\"([^\"]+)\"\ (.+?)\]/, 'version:"\1"') |
|
292 | text = text.gsub(/\[milestone\:\"([^\"]+)\"\ (.+?)\]/, 'version:"\1"') | |
293 | # [milestone:"0.1.0 Mercury"] |
|
293 | # [milestone:"0.1.0 Mercury"] | |
294 | text = text.gsub(/\[milestone\:\"([^\"]+)\"\]/, 'version:"\1"') |
|
294 | text = text.gsub(/\[milestone\:\"([^\"]+)\"\]/, 'version:"\1"') | |
295 | text = text.gsub(/milestone\:\"([^\"]+)\"/, 'version:"\1"') |
|
295 | text = text.gsub(/milestone\:\"([^\"]+)\"/, 'version:"\1"') | |
296 | # milestone:0.1.0 |
|
296 | # milestone:0.1.0 | |
297 | text = text.gsub(/\[milestone\:([^\ ]+)\]/, 'version:\1') |
|
297 | text = text.gsub(/\[milestone\:([^\ ]+)\]/, 'version:\1') | |
298 | text = text.gsub(/milestone\:([^\ ]+)/, 'version:\1') |
|
298 | text = text.gsub(/milestone\:([^\ ]+)/, 'version:\1') | |
299 | # Internal Links |
|
299 | # Internal Links | |
300 | text = text.gsub(/\[\[BR\]\]/, "\n") # This has to go before the rules below |
|
300 | text = text.gsub(/\[\[BR\]\]/, "\n") # This has to go before the rules below | |
301 | text = text.gsub(/\[\"(.+)\".*\]/) {|s| "[[#{$1.delete(',./?;|:')}]]"} |
|
301 | text = text.gsub(/\[\"(.+)\".*\]/) {|s| "[[#{$1.delete(',./?;|:')}]]"} | |
302 | text = text.gsub(/\[wiki:\"(.+)\".*\]/) {|s| "[[#{$1.delete(',./?;|:')}]]"} |
|
302 | text = text.gsub(/\[wiki:\"(.+)\".*\]/) {|s| "[[#{$1.delete(',./?;|:')}]]"} | |
303 | text = text.gsub(/\[wiki:\"(.+)\".*\]/) {|s| "[[#{$1.delete(',./?;|:')}]]"} |
|
303 | text = text.gsub(/\[wiki:\"(.+)\".*\]/) {|s| "[[#{$1.delete(',./?;|:')}]]"} | |
304 | text = text.gsub(/\[wiki:([^\s\]]+)\]/) {|s| "[[#{$1.delete(',./?;|:')}]]"} |
|
304 | text = text.gsub(/\[wiki:([^\s\]]+)\]/) {|s| "[[#{$1.delete(',./?;|:')}]]"} | |
305 | text = text.gsub(/\[wiki:([^\s\]]+)\s(.*)\]/) {|s| "[[#{$1.delete(',./?;|:')}|#{$2.delete(',./?;|:')}]]"} |
|
305 | text = text.gsub(/\[wiki:([^\s\]]+)\s(.*)\]/) {|s| "[[#{$1.delete(',./?;|:')}|#{$2.delete(',./?;|:')}]]"} | |
306 |
|
306 | |||
307 | # Links to pages UsingJustWikiCaps |
|
307 | # Links to pages UsingJustWikiCaps | |
308 | text = text.gsub(/([^!]|^)(^| )([A-Z][a-z]+[A-Z][a-zA-Z]+)/, '\\1\\2[[\3]]') |
|
308 | text = text.gsub(/([^!]|^)(^| )([A-Z][a-z]+[A-Z][a-zA-Z]+)/, '\\1\\2[[\3]]') | |
309 | # Normalize things that were supposed to not be links |
|
309 | # Normalize things that were supposed to not be links | |
310 | # like !NotALink |
|
310 | # like !NotALink | |
311 | text = text.gsub(/(^| )!([A-Z][A-Za-z]+)/, '\1\2') |
|
311 | text = text.gsub(/(^| )!([A-Z][A-Za-z]+)/, '\1\2') | |
312 | # Revisions links |
|
312 | # Revisions links | |
313 | text = text.gsub(/\[(\d+)\]/, 'r\1') |
|
313 | text = text.gsub(/\[(\d+)\]/, 'r\1') | |
314 | # Ticket number re-writing |
|
314 | # Ticket number re-writing | |
315 | text = text.gsub(/#(\d+)/) do |s| |
|
315 | text = text.gsub(/#(\d+)/) do |s| | |
316 | if $1.length < 10 |
|
316 | if $1.length < 10 | |
317 | # TICKET_MAP[$1.to_i] ||= $1 |
|
317 | # TICKET_MAP[$1.to_i] ||= $1 | |
318 | "\##{TICKET_MAP[$1.to_i] || $1}" |
|
318 | "\##{TICKET_MAP[$1.to_i] || $1}" | |
319 | else |
|
319 | else | |
320 | s |
|
320 | s | |
321 | end |
|
321 | end | |
322 | end |
|
322 | end | |
323 | # We would like to convert the Code highlighting too |
|
323 | # We would like to convert the Code highlighting too | |
324 | # This will go into the next line. |
|
324 | # This will go into the next line. | |
325 | shebang_line = false |
|
325 | shebang_line = false | |
326 | # Reguar expression for start of code |
|
326 | # Reguar expression for start of code | |
327 | pre_re = /\{\{\{/ |
|
327 | pre_re = /\{\{\{/ | |
328 | # Code hightlighing... |
|
328 | # Code hightlighing... | |
329 | shebang_re = /^\#\!([a-z]+)/ |
|
329 | shebang_re = /^\#\!([a-z]+)/ | |
330 | # Regular expression for end of code |
|
330 | # Regular expression for end of code | |
331 | pre_end_re = /\}\}\}/ |
|
331 | pre_end_re = /\}\}\}/ | |
332 |
|
332 | |||
333 | # Go through the whole text..extract it line by line |
|
333 | # Go through the whole text..extract it line by line | |
334 | text = text.gsub(/^(.*)$/) do |line| |
|
334 | text = text.gsub(/^(.*)$/) do |line| | |
335 | m_pre = pre_re.match(line) |
|
335 | m_pre = pre_re.match(line) | |
336 | if m_pre |
|
336 | if m_pre | |
337 | line = '<pre>' |
|
337 | line = '<pre>' | |
338 | else |
|
338 | else | |
339 | m_sl = shebang_re.match(line) |
|
339 | m_sl = shebang_re.match(line) | |
340 | if m_sl |
|
340 | if m_sl | |
341 | shebang_line = true |
|
341 | shebang_line = true | |
342 | line = '<code class="' + m_sl[1] + '">' |
|
342 | line = '<code class="' + m_sl[1] + '">' | |
343 | end |
|
343 | end | |
344 | m_pre_end = pre_end_re.match(line) |
|
344 | m_pre_end = pre_end_re.match(line) | |
345 | if m_pre_end |
|
345 | if m_pre_end | |
346 | line = '</pre>' |
|
346 | line = '</pre>' | |
347 | if shebang_line |
|
347 | if shebang_line | |
348 | line = '</code>' + line |
|
348 | line = '</code>' + line | |
349 | end |
|
349 | end | |
350 | end |
|
350 | end | |
351 | end |
|
351 | end | |
352 | line |
|
352 | line | |
353 | end |
|
353 | end | |
354 |
|
354 | |||
355 | # Highlighting |
|
355 | # Highlighting | |
356 | text = text.gsub(/'''''([^\s])/, '_*\1') |
|
356 | text = text.gsub(/'''''([^\s])/, '_*\1') | |
357 | text = text.gsub(/([^\s])'''''/, '\1*_') |
|
357 | text = text.gsub(/([^\s])'''''/, '\1*_') | |
358 | text = text.gsub(/'''/, '*') |
|
358 | text = text.gsub(/'''/, '*') | |
359 | text = text.gsub(/''/, '_') |
|
359 | text = text.gsub(/''/, '_') | |
360 | text = text.gsub(/__/, '+') |
|
360 | text = text.gsub(/__/, '+') | |
361 | text = text.gsub(/~~/, '-') |
|
361 | text = text.gsub(/~~/, '-') | |
362 | text = text.gsub(/`/, '@') |
|
362 | text = text.gsub(/`/, '@') | |
363 | text = text.gsub(/,,/, '~') |
|
363 | text = text.gsub(/,,/, '~') | |
364 | # Lists |
|
364 | # Lists | |
365 | text = text.gsub(/^([ ]+)\* /) {|s| '*' * $1.length + " "} |
|
365 | text = text.gsub(/^([ ]+)\* /) {|s| '*' * $1.length + " "} | |
366 |
|
366 | |||
367 | text |
|
367 | text | |
368 | end |
|
368 | end | |
369 |
|
369 | |||
370 | def self.migrate |
|
370 | def self.migrate | |
371 | establish_connection |
|
371 | establish_connection | |
372 |
|
372 | |||
373 | # Quick database test |
|
373 | # Quick database test | |
374 | TracComponent.count |
|
374 | TracComponent.count | |
375 |
|
375 | |||
376 | migrated_components = 0 |
|
376 | migrated_components = 0 | |
377 | migrated_milestones = 0 |
|
377 | migrated_milestones = 0 | |
378 | migrated_tickets = 0 |
|
378 | migrated_tickets = 0 | |
379 | migrated_custom_values = 0 |
|
379 | migrated_custom_values = 0 | |
380 | migrated_ticket_attachments = 0 |
|
380 | migrated_ticket_attachments = 0 | |
381 | migrated_wiki_edits = 0 |
|
381 | migrated_wiki_edits = 0 | |
382 | migrated_wiki_attachments = 0 |
|
382 | migrated_wiki_attachments = 0 | |
383 |
|
383 | |||
384 | #Wiki system initializing... |
|
384 | #Wiki system initializing... | |
385 | @target_project.wiki.destroy if @target_project.wiki |
|
385 | @target_project.wiki.destroy if @target_project.wiki | |
386 | @target_project.reload |
|
386 | @target_project.reload | |
387 | wiki = Wiki.new(:project => @target_project, :start_page => 'WikiStart') |
|
387 | wiki = Wiki.new(:project => @target_project, :start_page => 'WikiStart') | |
388 | wiki_edit_count = 0 |
|
388 | wiki_edit_count = 0 | |
389 |
|
389 | |||
390 | # Components |
|
390 | # Components | |
391 | print "Migrating components" |
|
391 | print "Migrating components" | |
392 | issues_category_map = {} |
|
392 | issues_category_map = {} | |
393 | TracComponent.find(:all).each do |component| |
|
393 | TracComponent.find(:all).each do |component| | |
394 | print '.' |
|
394 | print '.' | |
395 | STDOUT.flush |
|
395 | STDOUT.flush | |
396 | c = IssueCategory.new :project => @target_project, |
|
396 | c = IssueCategory.new :project => @target_project, | |
397 | :name => encode(component.name[0, limit_for(IssueCategory, 'name')]) |
|
397 | :name => encode(component.name[0, limit_for(IssueCategory, 'name')]) | |
398 | next unless c.save |
|
398 | next unless c.save | |
399 | issues_category_map[component.name] = c |
|
399 | issues_category_map[component.name] = c | |
400 | migrated_components += 1 |
|
400 | migrated_components += 1 | |
401 | end |
|
401 | end | |
402 | puts |
|
402 | puts | |
403 |
|
403 | |||
404 | # Milestones |
|
404 | # Milestones | |
405 | print "Migrating milestones" |
|
405 | print "Migrating milestones" | |
406 | version_map = {} |
|
406 | version_map = {} | |
407 | TracMilestone.find(:all).each do |milestone| |
|
407 | TracMilestone.find(:all).each do |milestone| | |
408 | print '.' |
|
408 | print '.' | |
409 | STDOUT.flush |
|
409 | STDOUT.flush | |
410 | # First we try to find the wiki page... |
|
410 | # First we try to find the wiki page... | |
411 | p = wiki.find_or_new_page(milestone.name.to_s) |
|
411 | p = wiki.find_or_new_page(milestone.name.to_s) | |
412 | p.content = WikiContent.new(:page => p) if p.new_record? |
|
412 | p.content = WikiContent.new(:page => p) if p.new_record? | |
413 | p.content.text = milestone.description.to_s |
|
413 | p.content.text = milestone.description.to_s | |
414 | p.content.author = find_or_create_user('trac') |
|
414 | p.content.author = find_or_create_user('trac') | |
415 | p.content.comments = 'Milestone' |
|
415 | p.content.comments = 'Milestone' | |
416 | p.save |
|
416 | p.save | |
417 |
|
417 | |||
418 | v = Version.new :project => @target_project, |
|
418 | v = Version.new :project => @target_project, | |
419 | :name => encode(milestone.name[0, limit_for(Version, 'name')]), |
|
419 | :name => encode(milestone.name[0, limit_for(Version, 'name')]), | |
420 | :description => nil, |
|
420 | :description => nil, | |
421 | :wiki_page_title => milestone.name.to_s, |
|
421 | :wiki_page_title => milestone.name.to_s, | |
422 | :effective_date => milestone.completed |
|
422 | :effective_date => milestone.completed | |
423 |
|
423 | |||
424 | next unless v.save |
|
424 | next unless v.save | |
425 | version_map[milestone.name] = v |
|
425 | version_map[milestone.name] = v | |
426 | migrated_milestones += 1 |
|
426 | migrated_milestones += 1 | |
427 | end |
|
427 | end | |
428 | puts |
|
428 | puts | |
429 |
|
429 | |||
430 | # Custom fields |
|
430 | # Custom fields | |
431 | # TODO: read trac.ini instead |
|
431 | # TODO: read trac.ini instead | |
432 | print "Migrating custom fields" |
|
432 | print "Migrating custom fields" | |
433 | custom_field_map = {} |
|
433 | custom_field_map = {} | |
434 | TracTicketCustom.find_by_sql("SELECT DISTINCT name FROM #{TracTicketCustom.table_name}").each do |field| |
|
434 | TracTicketCustom.find_by_sql("SELECT DISTINCT name FROM #{TracTicketCustom.table_name}").each do |field| | |
435 | print '.' |
|
435 | print '.' | |
436 | STDOUT.flush |
|
436 | STDOUT.flush | |
437 | # Redmine custom field name |
|
437 | # Redmine custom field name | |
438 | field_name = encode(field.name[0, limit_for(IssueCustomField, 'name')]).humanize |
|
438 | field_name = encode(field.name[0, limit_for(IssueCustomField, 'name')]).humanize | |
439 | # Find if the custom already exists in Redmine |
|
439 | # Find if the custom already exists in Redmine | |
440 | f = IssueCustomField.find_by_name(field_name) |
|
440 | f = IssueCustomField.find_by_name(field_name) | |
441 | # Or create a new one |
|
441 | # Or create a new one | |
442 | f ||= IssueCustomField.create(:name => encode(field.name[0, limit_for(IssueCustomField, 'name')]).humanize, |
|
442 | f ||= IssueCustomField.create(:name => encode(field.name[0, limit_for(IssueCustomField, 'name')]).humanize, | |
443 | :field_format => 'string') |
|
443 | :field_format => 'string') | |
444 |
|
444 | |||
445 | next if f.new_record? |
|
445 | next if f.new_record? | |
446 | f.trackers = Tracker.find(:all) |
|
446 | f.trackers = Tracker.find(:all) | |
447 | f.projects << @target_project |
|
447 | f.projects << @target_project | |
448 | custom_field_map[field.name] = f |
|
448 | custom_field_map[field.name] = f | |
449 | end |
|
449 | end | |
450 | puts |
|
450 | puts | |
451 |
|
451 | |||
452 | # Trac 'resolution' field as a Redmine custom field |
|
452 | # Trac 'resolution' field as a Redmine custom field | |
453 | r = IssueCustomField.find(:first, :conditions => { :name => "Resolution" }) |
|
453 | r = IssueCustomField.find(:first, :conditions => { :name => "Resolution" }) | |
454 | r = IssueCustomField.new(:name => 'Resolution', |
|
454 | r = IssueCustomField.new(:name => 'Resolution', | |
455 | :field_format => 'list', |
|
455 | :field_format => 'list', | |
456 | :is_filter => true) if r.nil? |
|
456 | :is_filter => true) if r.nil? | |
457 | r.trackers = Tracker.find(:all) |
|
457 | r.trackers = Tracker.find(:all) | |
458 | r.projects << @target_project |
|
458 | r.projects << @target_project | |
459 | r.possible_values = (r.possible_values + %w(fixed invalid wontfix duplicate worksforme)).flatten.compact.uniq |
|
459 | r.possible_values = (r.possible_values + %w(fixed invalid wontfix duplicate worksforme)).flatten.compact.uniq | |
460 | r.save! |
|
460 | r.save! | |
461 | custom_field_map['resolution'] = r |
|
461 | custom_field_map['resolution'] = r | |
462 |
|
462 | |||
463 | # Tickets |
|
463 | # Tickets | |
464 | print "Migrating tickets" |
|
464 | print "Migrating tickets" | |
465 | TracTicket.find_each(:batch_size => 200) do |ticket| |
|
465 | TracTicket.find_each(:batch_size => 200) do |ticket| | |
466 | print '.' |
|
466 | print '.' | |
467 | STDOUT.flush |
|
467 | STDOUT.flush | |
468 | i = Issue.new :project => @target_project, |
|
468 | i = Issue.new :project => @target_project, | |
469 | :subject => encode(ticket.summary[0, limit_for(Issue, 'subject')]), |
|
469 | :subject => encode(ticket.summary[0, limit_for(Issue, 'subject')]), | |
470 | :description => convert_wiki_text(encode(ticket.description)), |
|
470 | :description => convert_wiki_text(encode(ticket.description)), | |
471 | :priority => PRIORITY_MAPPING[ticket.priority] || DEFAULT_PRIORITY, |
|
471 | :priority => PRIORITY_MAPPING[ticket.priority] || DEFAULT_PRIORITY, | |
472 | :created_on => ticket.time |
|
472 | :created_on => ticket.time | |
473 | i.author = find_or_create_user(ticket.reporter) |
|
473 | i.author = find_or_create_user(ticket.reporter) | |
474 | i.category = issues_category_map[ticket.component] unless ticket.component.blank? |
|
474 | i.category = issues_category_map[ticket.component] unless ticket.component.blank? | |
475 | i.fixed_version = version_map[ticket.milestone] unless ticket.milestone.blank? |
|
475 | i.fixed_version = version_map[ticket.milestone] unless ticket.milestone.blank? | |
476 | i.status = STATUS_MAPPING[ticket.status] || DEFAULT_STATUS |
|
476 | i.status = STATUS_MAPPING[ticket.status] || DEFAULT_STATUS | |
477 | i.tracker = TRACKER_MAPPING[ticket.ticket_type] || DEFAULT_TRACKER |
|
477 | i.tracker = TRACKER_MAPPING[ticket.ticket_type] || DEFAULT_TRACKER | |
478 | i.id = ticket.id unless Issue.exists?(ticket.id) |
|
478 | i.id = ticket.id unless Issue.exists?(ticket.id) | |
479 | next unless Time.fake(ticket.changetime) { i.save } |
|
479 | next unless Time.fake(ticket.changetime) { i.save } | |
480 | TICKET_MAP[ticket.id] = i.id |
|
480 | TICKET_MAP[ticket.id] = i.id | |
481 | migrated_tickets += 1 |
|
481 | migrated_tickets += 1 | |
482 |
|
482 | |||
483 | # Owner |
|
483 | # Owner | |
484 | unless ticket.owner.blank? |
|
484 | unless ticket.owner.blank? | |
485 | i.assigned_to = find_or_create_user(ticket.owner, true) |
|
485 | i.assigned_to = find_or_create_user(ticket.owner, true) | |
486 | Time.fake(ticket.changetime) { i.save } |
|
486 | Time.fake(ticket.changetime) { i.save } | |
487 | end |
|
487 | end | |
488 |
|
488 | |||
489 | # Comments and status/resolution changes |
|
489 | # Comments and status/resolution changes | |
490 | ticket.changes.group_by(&:time).each do |time, changeset| |
|
490 | ticket.ticket_changes.group_by(&:time).each do |time, changeset| | |
491 | status_change = changeset.select {|change| change.field == 'status'}.first |
|
491 | status_change = changeset.select {|change| change.field == 'status'}.first | |
492 | resolution_change = changeset.select {|change| change.field == 'resolution'}.first |
|
492 | resolution_change = changeset.select {|change| change.field == 'resolution'}.first | |
493 | comment_change = changeset.select {|change| change.field == 'comment'}.first |
|
493 | comment_change = changeset.select {|change| change.field == 'comment'}.first | |
494 |
|
494 | |||
495 | n = Journal.new :notes => (comment_change ? convert_wiki_text(encode(comment_change.newvalue)) : ''), |
|
495 | n = Journal.new :notes => (comment_change ? convert_wiki_text(encode(comment_change.newvalue)) : ''), | |
496 | :created_on => time |
|
496 | :created_on => time | |
497 | n.user = find_or_create_user(changeset.first.author) |
|
497 | n.user = find_or_create_user(changeset.first.author) | |
498 | n.journalized = i |
|
498 | n.journalized = i | |
499 | if status_change && |
|
499 | if status_change && | |
500 | STATUS_MAPPING[status_change.oldvalue] && |
|
500 | STATUS_MAPPING[status_change.oldvalue] && | |
501 | STATUS_MAPPING[status_change.newvalue] && |
|
501 | STATUS_MAPPING[status_change.newvalue] && | |
502 | (STATUS_MAPPING[status_change.oldvalue] != STATUS_MAPPING[status_change.newvalue]) |
|
502 | (STATUS_MAPPING[status_change.oldvalue] != STATUS_MAPPING[status_change.newvalue]) | |
503 | n.details << JournalDetail.new(:property => 'attr', |
|
503 | n.details << JournalDetail.new(:property => 'attr', | |
504 | :prop_key => 'status_id', |
|
504 | :prop_key => 'status_id', | |
505 | :old_value => STATUS_MAPPING[status_change.oldvalue].id, |
|
505 | :old_value => STATUS_MAPPING[status_change.oldvalue].id, | |
506 | :value => STATUS_MAPPING[status_change.newvalue].id) |
|
506 | :value => STATUS_MAPPING[status_change.newvalue].id) | |
507 | end |
|
507 | end | |
508 | if resolution_change |
|
508 | if resolution_change | |
509 | n.details << JournalDetail.new(:property => 'cf', |
|
509 | n.details << JournalDetail.new(:property => 'cf', | |
510 | :prop_key => custom_field_map['resolution'].id, |
|
510 | :prop_key => custom_field_map['resolution'].id, | |
511 | :old_value => resolution_change.oldvalue, |
|
511 | :old_value => resolution_change.oldvalue, | |
512 | :value => resolution_change.newvalue) |
|
512 | :value => resolution_change.newvalue) | |
513 | end |
|
513 | end | |
514 | n.save unless n.details.empty? && n.notes.blank? |
|
514 | n.save unless n.details.empty? && n.notes.blank? | |
515 | end |
|
515 | end | |
516 |
|
516 | |||
517 | # Attachments |
|
517 | # Attachments | |
518 | ticket.attachments.each do |attachment| |
|
518 | ticket.attachments.each do |attachment| | |
519 | next unless attachment.exist? |
|
519 | next unless attachment.exist? | |
520 | attachment.open { |
|
520 | attachment.open { | |
521 | a = Attachment.new :created_on => attachment.time |
|
521 | a = Attachment.new :created_on => attachment.time | |
522 | a.file = attachment |
|
522 | a.file = attachment | |
523 | a.author = find_or_create_user(attachment.author) |
|
523 | a.author = find_or_create_user(attachment.author) | |
524 | a.container = i |
|
524 | a.container = i | |
525 | a.description = attachment.description |
|
525 | a.description = attachment.description | |
526 | migrated_ticket_attachments += 1 if a.save |
|
526 | migrated_ticket_attachments += 1 if a.save | |
527 | } |
|
527 | } | |
528 | end |
|
528 | end | |
529 |
|
529 | |||
530 | # Custom fields |
|
530 | # Custom fields | |
531 | custom_values = ticket.customs.inject({}) do |h, custom| |
|
531 | custom_values = ticket.customs.inject({}) do |h, custom| | |
532 | if custom_field = custom_field_map[custom.name] |
|
532 | if custom_field = custom_field_map[custom.name] | |
533 | h[custom_field.id] = custom.value |
|
533 | h[custom_field.id] = custom.value | |
534 | migrated_custom_values += 1 |
|
534 | migrated_custom_values += 1 | |
535 | end |
|
535 | end | |
536 | h |
|
536 | h | |
537 | end |
|
537 | end | |
538 | if custom_field_map['resolution'] && !ticket.resolution.blank? |
|
538 | if custom_field_map['resolution'] && !ticket.resolution.blank? | |
539 | custom_values[custom_field_map['resolution'].id] = ticket.resolution |
|
539 | custom_values[custom_field_map['resolution'].id] = ticket.resolution | |
540 | end |
|
540 | end | |
541 | i.custom_field_values = custom_values |
|
541 | i.custom_field_values = custom_values | |
542 | i.save_custom_field_values |
|
542 | i.save_custom_field_values | |
543 | end |
|
543 | end | |
544 |
|
544 | |||
545 | # update issue id sequence if needed (postgresql) |
|
545 | # update issue id sequence if needed (postgresql) | |
546 | Issue.connection.reset_pk_sequence!(Issue.table_name) if Issue.connection.respond_to?('reset_pk_sequence!') |
|
546 | Issue.connection.reset_pk_sequence!(Issue.table_name) if Issue.connection.respond_to?('reset_pk_sequence!') | |
547 | puts |
|
547 | puts | |
548 |
|
548 | |||
549 | # Wiki |
|
549 | # Wiki | |
550 | print "Migrating wiki" |
|
550 | print "Migrating wiki" | |
551 | if wiki.save |
|
551 | if wiki.save | |
552 | TracWikiPage.find(:all, :order => 'name, version').each do |page| |
|
552 | TracWikiPage.find(:all, :order => 'name, version').each do |page| | |
553 | # Do not migrate Trac manual wiki pages |
|
553 | # Do not migrate Trac manual wiki pages | |
554 | next if TRAC_WIKI_PAGES.include?(page.name) |
|
554 | next if TRAC_WIKI_PAGES.include?(page.name) | |
555 | wiki_edit_count += 1 |
|
555 | wiki_edit_count += 1 | |
556 | print '.' |
|
556 | print '.' | |
557 | STDOUT.flush |
|
557 | STDOUT.flush | |
558 | p = wiki.find_or_new_page(page.name) |
|
558 | p = wiki.find_or_new_page(page.name) | |
559 | p.content = WikiContent.new(:page => p) if p.new_record? |
|
559 | p.content = WikiContent.new(:page => p) if p.new_record? | |
560 | p.content.text = page.text |
|
560 | p.content.text = page.text | |
561 | p.content.author = find_or_create_user(page.author) unless page.author.blank? || page.author == 'trac' |
|
561 | p.content.author = find_or_create_user(page.author) unless page.author.blank? || page.author == 'trac' | |
562 | p.content.comments = page.comment |
|
562 | p.content.comments = page.comment | |
563 | Time.fake(page.time) { p.new_record? ? p.save : p.content.save } |
|
563 | Time.fake(page.time) { p.new_record? ? p.save : p.content.save } | |
564 |
|
564 | |||
565 | next if p.content.new_record? |
|
565 | next if p.content.new_record? | |
566 | migrated_wiki_edits += 1 |
|
566 | migrated_wiki_edits += 1 | |
567 |
|
567 | |||
568 | # Attachments |
|
568 | # Attachments | |
569 | page.attachments.each do |attachment| |
|
569 | page.attachments.each do |attachment| | |
570 | next unless attachment.exist? |
|
570 | next unless attachment.exist? | |
571 | next if p.attachments.find_by_filename(attachment.filename.gsub(/^.*(\\|\/)/, '').gsub(/[^\w\.\-]/,'_')) #add only once per page |
|
571 | next if p.attachments.find_by_filename(attachment.filename.gsub(/^.*(\\|\/)/, '').gsub(/[^\w\.\-]/,'_')) #add only once per page | |
572 | attachment.open { |
|
572 | attachment.open { | |
573 | a = Attachment.new :created_on => attachment.time |
|
573 | a = Attachment.new :created_on => attachment.time | |
574 | a.file = attachment |
|
574 | a.file = attachment | |
575 | a.author = find_or_create_user(attachment.author) |
|
575 | a.author = find_or_create_user(attachment.author) | |
576 | a.description = attachment.description |
|
576 | a.description = attachment.description | |
577 | a.container = p |
|
577 | a.container = p | |
578 | migrated_wiki_attachments += 1 if a.save |
|
578 | migrated_wiki_attachments += 1 if a.save | |
579 | } |
|
579 | } | |
580 | end |
|
580 | end | |
581 | end |
|
581 | end | |
582 |
|
582 | |||
583 | wiki.reload |
|
583 | wiki.reload | |
584 | wiki.pages.each do |page| |
|
584 | wiki.pages.each do |page| | |
585 | page.content.text = convert_wiki_text(page.content.text) |
|
585 | page.content.text = convert_wiki_text(page.content.text) | |
586 | Time.fake(page.content.updated_on) { page.content.save } |
|
586 | Time.fake(page.content.updated_on) { page.content.save } | |
587 | end |
|
587 | end | |
588 | end |
|
588 | end | |
589 | puts |
|
589 | puts | |
590 |
|
590 | |||
591 | puts |
|
591 | puts | |
592 | puts "Components: #{migrated_components}/#{TracComponent.count}" |
|
592 | puts "Components: #{migrated_components}/#{TracComponent.count}" | |
593 | puts "Milestones: #{migrated_milestones}/#{TracMilestone.count}" |
|
593 | puts "Milestones: #{migrated_milestones}/#{TracMilestone.count}" | |
594 | puts "Tickets: #{migrated_tickets}/#{TracTicket.count}" |
|
594 | puts "Tickets: #{migrated_tickets}/#{TracTicket.count}" | |
595 | puts "Ticket files: #{migrated_ticket_attachments}/" + TracAttachment.count(:conditions => {:type => 'ticket'}).to_s |
|
595 | puts "Ticket files: #{migrated_ticket_attachments}/" + TracAttachment.count(:conditions => {:type => 'ticket'}).to_s | |
596 | puts "Custom values: #{migrated_custom_values}/#{TracTicketCustom.count}" |
|
596 | puts "Custom values: #{migrated_custom_values}/#{TracTicketCustom.count}" | |
597 | puts "Wiki edits: #{migrated_wiki_edits}/#{wiki_edit_count}" |
|
597 | puts "Wiki edits: #{migrated_wiki_edits}/#{wiki_edit_count}" | |
598 | puts "Wiki files: #{migrated_wiki_attachments}/" + TracAttachment.count(:conditions => {:type => 'wiki'}).to_s |
|
598 | puts "Wiki files: #{migrated_wiki_attachments}/" + TracAttachment.count(:conditions => {:type => 'wiki'}).to_s | |
599 | end |
|
599 | end | |
600 |
|
600 | |||
601 | def self.limit_for(klass, attribute) |
|
601 | def self.limit_for(klass, attribute) | |
602 | klass.columns_hash[attribute.to_s].limit |
|
602 | klass.columns_hash[attribute.to_s].limit | |
603 | end |
|
603 | end | |
604 |
|
604 | |||
605 | def self.encoding(charset) |
|
605 | def self.encoding(charset) | |
606 | @ic = Iconv.new('UTF-8', charset) |
|
606 | @ic = Iconv.new('UTF-8', charset) | |
607 | rescue Iconv::InvalidEncoding |
|
607 | rescue Iconv::InvalidEncoding | |
608 | puts "Invalid encoding!" |
|
608 | puts "Invalid encoding!" | |
609 | return false |
|
609 | return false | |
610 | end |
|
610 | end | |
611 |
|
611 | |||
612 | def self.set_trac_directory(path) |
|
612 | def self.set_trac_directory(path) | |
613 | @@trac_directory = path |
|
613 | @@trac_directory = path | |
614 | raise "This directory doesn't exist!" unless File.directory?(path) |
|
614 | raise "This directory doesn't exist!" unless File.directory?(path) | |
615 | raise "#{trac_attachments_directory} doesn't exist!" unless File.directory?(trac_attachments_directory) |
|
615 | raise "#{trac_attachments_directory} doesn't exist!" unless File.directory?(trac_attachments_directory) | |
616 | @@trac_directory |
|
616 | @@trac_directory | |
617 | rescue Exception => e |
|
617 | rescue Exception => e | |
618 | puts e |
|
618 | puts e | |
619 | return false |
|
619 | return false | |
620 | end |
|
620 | end | |
621 |
|
621 | |||
622 | def self.trac_directory |
|
622 | def self.trac_directory | |
623 | @@trac_directory |
|
623 | @@trac_directory | |
624 | end |
|
624 | end | |
625 |
|
625 | |||
626 | def self.set_trac_adapter(adapter) |
|
626 | def self.set_trac_adapter(adapter) | |
627 | return false if adapter.blank? |
|
627 | return false if adapter.blank? | |
628 | raise "Unknown adapter: #{adapter}!" unless %w(sqlite3 mysql postgresql).include?(adapter) |
|
628 | raise "Unknown adapter: #{adapter}!" unless %w(sqlite3 mysql postgresql).include?(adapter) | |
629 | # If adapter is sqlite or sqlite3, make sure that trac.db exists |
|
629 | # If adapter is sqlite or sqlite3, make sure that trac.db exists | |
630 | raise "#{trac_db_path} doesn't exist!" if %w(sqlite3).include?(adapter) && !File.exist?(trac_db_path) |
|
630 | raise "#{trac_db_path} doesn't exist!" if %w(sqlite3).include?(adapter) && !File.exist?(trac_db_path) | |
631 | @@trac_adapter = adapter |
|
631 | @@trac_adapter = adapter | |
632 | rescue Exception => e |
|
632 | rescue Exception => e | |
633 | puts e |
|
633 | puts e | |
634 | return false |
|
634 | return false | |
635 | end |
|
635 | end | |
636 |
|
636 | |||
637 | def self.set_trac_db_host(host) |
|
637 | def self.set_trac_db_host(host) | |
638 | return nil if host.blank? |
|
638 | return nil if host.blank? | |
639 | @@trac_db_host = host |
|
639 | @@trac_db_host = host | |
640 | end |
|
640 | end | |
641 |
|
641 | |||
642 | def self.set_trac_db_port(port) |
|
642 | def self.set_trac_db_port(port) | |
643 | return nil if port.to_i == 0 |
|
643 | return nil if port.to_i == 0 | |
644 | @@trac_db_port = port.to_i |
|
644 | @@trac_db_port = port.to_i | |
645 | end |
|
645 | end | |
646 |
|
646 | |||
647 | def self.set_trac_db_name(name) |
|
647 | def self.set_trac_db_name(name) | |
648 | return nil if name.blank? |
|
648 | return nil if name.blank? | |
649 | @@trac_db_name = name |
|
649 | @@trac_db_name = name | |
650 | end |
|
650 | end | |
651 |
|
651 | |||
652 | def self.set_trac_db_username(username) |
|
652 | def self.set_trac_db_username(username) | |
653 | @@trac_db_username = username |
|
653 | @@trac_db_username = username | |
654 | end |
|
654 | end | |
655 |
|
655 | |||
656 | def self.set_trac_db_password(password) |
|
656 | def self.set_trac_db_password(password) | |
657 | @@trac_db_password = password |
|
657 | @@trac_db_password = password | |
658 | end |
|
658 | end | |
659 |
|
659 | |||
660 | def self.set_trac_db_schema(schema) |
|
660 | def self.set_trac_db_schema(schema) | |
661 | @@trac_db_schema = schema |
|
661 | @@trac_db_schema = schema | |
662 | end |
|
662 | end | |
663 |
|
663 | |||
664 | mattr_reader :trac_directory, :trac_adapter, :trac_db_host, :trac_db_port, :trac_db_name, :trac_db_schema, :trac_db_username, :trac_db_password |
|
664 | mattr_reader :trac_directory, :trac_adapter, :trac_db_host, :trac_db_port, :trac_db_name, :trac_db_schema, :trac_db_username, :trac_db_password | |
665 |
|
665 | |||
666 | def self.trac_db_path; "#{trac_directory}/db/trac.db" end |
|
666 | def self.trac_db_path; "#{trac_directory}/db/trac.db" end | |
667 | def self.trac_attachments_directory; "#{trac_directory}/attachments" end |
|
667 | def self.trac_attachments_directory; "#{trac_directory}/attachments" end | |
668 |
|
668 | |||
669 | def self.target_project_identifier(identifier) |
|
669 | def self.target_project_identifier(identifier) | |
670 | project = Project.find_by_identifier(identifier) |
|
670 | project = Project.find_by_identifier(identifier) | |
671 | if !project |
|
671 | if !project | |
672 | # create the target project |
|
672 | # create the target project | |
673 | project = Project.new :name => identifier.humanize, |
|
673 | project = Project.new :name => identifier.humanize, | |
674 | :description => '' |
|
674 | :description => '' | |
675 | project.identifier = identifier |
|
675 | project.identifier = identifier | |
676 | puts "Unable to create a project with identifier '#{identifier}'!" unless project.save |
|
676 | puts "Unable to create a project with identifier '#{identifier}'!" unless project.save | |
677 | # enable issues and wiki for the created project |
|
677 | # enable issues and wiki for the created project | |
678 | project.enabled_module_names = ['issue_tracking', 'wiki'] |
|
678 | project.enabled_module_names = ['issue_tracking', 'wiki'] | |
679 | else |
|
679 | else | |
680 | puts |
|
680 | puts | |
681 | puts "This project already exists in your Redmine database." |
|
681 | puts "This project already exists in your Redmine database." | |
682 | print "Are you sure you want to append data to this project ? [Y/n] " |
|
682 | print "Are you sure you want to append data to this project ? [Y/n] " | |
683 | STDOUT.flush |
|
683 | STDOUT.flush | |
684 | exit if STDIN.gets.match(/^n$/i) |
|
684 | exit if STDIN.gets.match(/^n$/i) | |
685 | end |
|
685 | end | |
686 | project.trackers << TRACKER_BUG unless project.trackers.include?(TRACKER_BUG) |
|
686 | project.trackers << TRACKER_BUG unless project.trackers.include?(TRACKER_BUG) | |
687 | project.trackers << TRACKER_FEATURE unless project.trackers.include?(TRACKER_FEATURE) |
|
687 | project.trackers << TRACKER_FEATURE unless project.trackers.include?(TRACKER_FEATURE) | |
688 | @target_project = project.new_record? ? nil : project |
|
688 | @target_project = project.new_record? ? nil : project | |
689 | @target_project.reload |
|
689 | @target_project.reload | |
690 | end |
|
690 | end | |
691 |
|
691 | |||
692 | def self.connection_params |
|
692 | def self.connection_params | |
693 | if trac_adapter == 'sqlite3' |
|
693 | if trac_adapter == 'sqlite3' | |
694 | {:adapter => 'sqlite3', |
|
694 | {:adapter => 'sqlite3', | |
695 | :database => trac_db_path} |
|
695 | :database => trac_db_path} | |
696 | else |
|
696 | else | |
697 | {:adapter => trac_adapter, |
|
697 | {:adapter => trac_adapter, | |
698 | :database => trac_db_name, |
|
698 | :database => trac_db_name, | |
699 | :host => trac_db_host, |
|
699 | :host => trac_db_host, | |
700 | :port => trac_db_port, |
|
700 | :port => trac_db_port, | |
701 | :username => trac_db_username, |
|
701 | :username => trac_db_username, | |
702 | :password => trac_db_password, |
|
702 | :password => trac_db_password, | |
703 | :schema_search_path => trac_db_schema |
|
703 | :schema_search_path => trac_db_schema | |
704 | } |
|
704 | } | |
705 | end |
|
705 | end | |
706 | end |
|
706 | end | |
707 |
|
707 | |||
708 | def self.establish_connection |
|
708 | def self.establish_connection | |
709 | constants.each do |const| |
|
709 | constants.each do |const| | |
710 | klass = const_get(const) |
|
710 | klass = const_get(const) | |
711 | next unless klass.respond_to? 'establish_connection' |
|
711 | next unless klass.respond_to? 'establish_connection' | |
712 | klass.establish_connection connection_params |
|
712 | klass.establish_connection connection_params | |
713 | end |
|
713 | end | |
714 | end |
|
714 | end | |
715 |
|
715 | |||
716 | private |
|
716 | private | |
717 | def self.encode(text) |
|
717 | def self.encode(text) | |
718 | @ic.iconv text |
|
718 | @ic.iconv text | |
719 | rescue |
|
719 | rescue | |
720 | text |
|
720 | text | |
721 | end |
|
721 | end | |
722 | end |
|
722 | end | |
723 |
|
723 | |||
724 | puts |
|
724 | puts | |
725 | if Redmine::DefaultData::Loader.no_data? |
|
725 | if Redmine::DefaultData::Loader.no_data? | |
726 | puts "Redmine configuration need to be loaded before importing data." |
|
726 | puts "Redmine configuration need to be loaded before importing data." | |
727 | puts "Please, run this first:" |
|
727 | puts "Please, run this first:" | |
728 | puts |
|
728 | puts | |
729 | puts " rake redmine:load_default_data RAILS_ENV=\"#{ENV['RAILS_ENV']}\"" |
|
729 | puts " rake redmine:load_default_data RAILS_ENV=\"#{ENV['RAILS_ENV']}\"" | |
730 | exit |
|
730 | exit | |
731 | end |
|
731 | end | |
732 |
|
732 | |||
733 | puts "WARNING: a new project will be added to Redmine during this process." |
|
733 | puts "WARNING: a new project will be added to Redmine during this process." | |
734 | print "Are you sure you want to continue ? [y/N] " |
|
734 | print "Are you sure you want to continue ? [y/N] " | |
735 | STDOUT.flush |
|
735 | STDOUT.flush | |
736 | break unless STDIN.gets.match(/^y$/i) |
|
736 | break unless STDIN.gets.match(/^y$/i) | |
737 | puts |
|
737 | puts | |
738 |
|
738 | |||
739 | def prompt(text, options = {}, &block) |
|
739 | def prompt(text, options = {}, &block) | |
740 | default = options[:default] || '' |
|
740 | default = options[:default] || '' | |
741 | while true |
|
741 | while true | |
742 | print "#{text} [#{default}]: " |
|
742 | print "#{text} [#{default}]: " | |
743 | STDOUT.flush |
|
743 | STDOUT.flush | |
744 | value = STDIN.gets.chomp! |
|
744 | value = STDIN.gets.chomp! | |
745 | value = default if value.blank? |
|
745 | value = default if value.blank? | |
746 | break if yield value |
|
746 | break if yield value | |
747 | end |
|
747 | end | |
748 | end |
|
748 | end | |
749 |
|
749 | |||
750 | DEFAULT_PORTS = {'mysql' => 3306, 'postgresql' => 5432} |
|
750 | DEFAULT_PORTS = {'mysql' => 3306, 'postgresql' => 5432} | |
751 |
|
751 | |||
752 | prompt('Trac directory') {|directory| TracMigrate.set_trac_directory directory.strip} |
|
752 | prompt('Trac directory') {|directory| TracMigrate.set_trac_directory directory.strip} | |
753 | prompt('Trac database adapter (sqlite3, mysql2, postgresql)', :default => 'sqlite3') {|adapter| TracMigrate.set_trac_adapter adapter} |
|
753 | prompt('Trac database adapter (sqlite3, mysql2, postgresql)', :default => 'sqlite3') {|adapter| TracMigrate.set_trac_adapter adapter} | |
754 | unless %w(sqlite3).include?(TracMigrate.trac_adapter) |
|
754 | unless %w(sqlite3).include?(TracMigrate.trac_adapter) | |
755 | prompt('Trac database host', :default => 'localhost') {|host| TracMigrate.set_trac_db_host host} |
|
755 | prompt('Trac database host', :default => 'localhost') {|host| TracMigrate.set_trac_db_host host} | |
756 | prompt('Trac database port', :default => DEFAULT_PORTS[TracMigrate.trac_adapter]) {|port| TracMigrate.set_trac_db_port port} |
|
756 | prompt('Trac database port', :default => DEFAULT_PORTS[TracMigrate.trac_adapter]) {|port| TracMigrate.set_trac_db_port port} | |
757 | prompt('Trac database name') {|name| TracMigrate.set_trac_db_name name} |
|
757 | prompt('Trac database name') {|name| TracMigrate.set_trac_db_name name} | |
758 | prompt('Trac database schema', :default => 'public') {|schema| TracMigrate.set_trac_db_schema schema} |
|
758 | prompt('Trac database schema', :default => 'public') {|schema| TracMigrate.set_trac_db_schema schema} | |
759 | prompt('Trac database username') {|username| TracMigrate.set_trac_db_username username} |
|
759 | prompt('Trac database username') {|username| TracMigrate.set_trac_db_username username} | |
760 | prompt('Trac database password') {|password| TracMigrate.set_trac_db_password password} |
|
760 | prompt('Trac database password') {|password| TracMigrate.set_trac_db_password password} | |
761 | end |
|
761 | end | |
762 | prompt('Trac database encoding', :default => 'UTF-8') {|encoding| TracMigrate.encoding encoding} |
|
762 | prompt('Trac database encoding', :default => 'UTF-8') {|encoding| TracMigrate.encoding encoding} | |
763 | prompt('Target project identifier') {|identifier| TracMigrate.target_project_identifier identifier} |
|
763 | prompt('Target project identifier') {|identifier| TracMigrate.target_project_identifier identifier} | |
764 | puts |
|
764 | puts | |
765 |
|
765 | |||
766 | # Turn off email notifications |
|
766 | # Turn off email notifications | |
767 | Setting.notified_events = [] |
|
767 | Setting.notified_events = [] | |
768 |
|
768 | |||
769 | TracMigrate.migrate |
|
769 | TracMigrate.migrate | |
770 | end |
|
770 | end | |
771 | end |
|
771 | end | |
772 |
|
772 |
@@ -1,113 +1,113 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2012 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 File.expand_path('../../test_helper', __FILE__) |
|
18 | require File.expand_path('../../test_helper', __FILE__) | |
19 |
|
19 | |||
20 | class GroupTest < ActiveSupport::TestCase |
|
20 | class GroupTest < ActiveSupport::TestCase | |
21 | fixtures :projects, :trackers, :issue_statuses, :issues, |
|
21 | fixtures :projects, :trackers, :issue_statuses, :issues, | |
22 | :enumerations, :users, :issue_categories, |
|
22 | :enumerations, :users, :issue_categories, | |
23 | :projects_trackers, |
|
23 | :projects_trackers, | |
24 | :roles, |
|
24 | :roles, | |
25 | :member_roles, |
|
25 | :member_roles, | |
26 | :members, |
|
26 | :members, | |
27 | :enabled_modules, |
|
27 | :enabled_modules, | |
28 | :workflows, |
|
28 | :workflows, | |
29 | :groups_users |
|
29 | :groups_users | |
30 |
|
30 | |||
31 | include Redmine::I18n |
|
31 | include Redmine::I18n | |
32 |
|
32 | |||
33 | def test_create |
|
33 | def test_create | |
34 | g = Group.new(:lastname => 'New group') |
|
34 | g = Group.new(:lastname => 'New group') | |
35 | assert g.save |
|
35 | assert g.save | |
36 | end |
|
36 | end | |
37 |
|
37 | |||
38 | def test_blank_name_error_message |
|
38 | def test_blank_name_error_message | |
39 | set_language_if_valid 'en' |
|
39 | set_language_if_valid 'en' | |
40 | g = Group.new |
|
40 | g = Group.new | |
41 | assert !g.save |
|
41 | assert !g.save | |
42 | assert_include "Name can't be blank", g.errors.full_messages |
|
42 | assert_include "Name can't be blank", g.errors.full_messages | |
43 | end |
|
43 | end | |
44 |
|
44 | |||
45 | def test_blank_name_error_message_fr |
|
45 | def test_blank_name_error_message_fr | |
46 | set_language_if_valid 'fr' |
|
46 | set_language_if_valid 'fr' | |
47 | str = "Nom doit \xc3\xaatre renseign\xc3\xa9(e)" |
|
47 | str = "Nom doit \xc3\xaatre renseign\xc3\xa9(e)" | |
48 | str.force_encoding('UTF-8') if str.respond_to?(:force_encoding) |
|
48 | str.force_encoding('UTF-8') if str.respond_to?(:force_encoding) | |
49 | g = Group.new |
|
49 | g = Group.new | |
50 | assert !g.save |
|
50 | assert !g.save | |
51 | assert_include str, g.errors.full_messages |
|
51 | assert_include str, g.errors.full_messages | |
52 | end |
|
52 | end | |
53 |
|
53 | |||
54 | def test_roles_given_to_new_user |
|
54 | def test_roles_given_to_new_user | |
55 | group = Group.find(11) |
|
55 | group = Group.find(11) | |
56 | user = User.find(9) |
|
56 | user = User.find(9) | |
57 | project = Project.first |
|
57 | project = Project.first | |
58 |
|
58 | |||
59 | Member.create!(:principal => group, :project => project, :role_ids => [1, 2]) |
|
59 | Member.create!(:principal => group, :project => project, :role_ids => [1, 2]) | |
60 | group.users << user |
|
60 | group.users << user | |
61 | assert user.member_of?(project) |
|
61 | assert user.member_of?(project) | |
62 | end |
|
62 | end | |
63 |
|
63 | |||
64 | def test_roles_given_to_existing_user |
|
64 | def test_roles_given_to_existing_user | |
65 | group = Group.find(11) |
|
65 | group = Group.find(11) | |
66 | user = User.find(9) |
|
66 | user = User.find(9) | |
67 | project = Project.first |
|
67 | project = Project.first | |
68 |
|
68 | |||
69 | group.users << user |
|
69 | group.users << user | |
70 | m = Member.create!(:principal => group, :project => project, :role_ids => [1, 2]) |
|
70 | m = Member.create!(:principal => group, :project => project, :role_ids => [1, 2]) | |
71 | assert user.member_of?(project) |
|
71 | assert user.member_of?(project) | |
72 | end |
|
72 | end | |
73 |
|
73 | |||
74 | def test_roles_updated |
|
74 | def test_roles_updated | |
75 | group = Group.find(11) |
|
75 | group = Group.find(11) | |
76 | user = User.find(9) |
|
76 | user = User.find(9) | |
77 | project = Project.first |
|
77 | project = Project.first | |
78 | group.users << user |
|
78 | group.users << user | |
79 | m = Member.create!(:principal => group, :project => project, :role_ids => [1]) |
|
79 | m = Member.create!(:principal => group, :project => project, :role_ids => [1]) | |
80 | assert_equal [1], user.reload.roles_for_project(project).collect(&:id).sort |
|
80 | assert_equal [1], user.reload.roles_for_project(project).collect(&:id).sort | |
81 |
|
81 | |||
82 | m.role_ids = [1, 2] |
|
82 | m.role_ids = [1, 2] | |
83 | assert_equal [1, 2], user.reload.roles_for_project(project).collect(&:id).sort |
|
83 | assert_equal [1, 2], user.reload.roles_for_project(project).collect(&:id).sort | |
84 |
|
84 | |||
85 | m.role_ids = [2] |
|
85 | m.role_ids = [2] | |
86 | assert_equal [2], user.reload.roles_for_project(project).collect(&:id).sort |
|
86 | assert_equal [2], user.reload.roles_for_project(project).collect(&:id).sort | |
87 |
|
87 | |||
88 | m.role_ids = [1] |
|
88 | m.role_ids = [1] | |
89 | assert_equal [1], user.reload.roles_for_project(project).collect(&:id).sort |
|
89 | assert_equal [1], user.reload.roles_for_project(project).collect(&:id).sort | |
90 | end |
|
90 | end | |
91 |
|
91 | |||
92 | def test_roles_removed_when_removing_group_membership |
|
92 | def test_roles_removed_when_removing_group_membership | |
93 | assert User.find(8).member_of?(Project.find(5)) |
|
93 | assert User.find(8).member_of?(Project.find(5)) | |
94 | Member.find_by_project_id_and_user_id(5, 10).destroy |
|
94 | Member.find_by_project_id_and_user_id(5, 10).destroy | |
95 | assert !User.find(8).member_of?(Project.find(5)) |
|
95 | assert !User.find(8).member_of?(Project.find(5)) | |
96 | end |
|
96 | end | |
97 |
|
97 | |||
98 | def test_roles_removed_when_removing_user_from_group |
|
98 | def test_roles_removed_when_removing_user_from_group | |
99 | assert User.find(8).member_of?(Project.find(5)) |
|
99 | assert User.find(8).member_of?(Project.find(5)) | |
100 |
User.find(8).groups |
|
100 | User.find(8).groups = [] | |
101 | assert !User.find(8).member_of?(Project.find(5)) |
|
101 | assert !User.find(8).member_of?(Project.find(5)) | |
102 | end |
|
102 | end | |
103 |
|
103 | |||
104 | def test_destroy_should_unassign_issues |
|
104 | def test_destroy_should_unassign_issues | |
105 | group = Group.first |
|
105 | group = Group.first | |
106 | Issue.update_all(["assigned_to_id = ?", group.id], 'id = 1') |
|
106 | Issue.update_all(["assigned_to_id = ?", group.id], 'id = 1') | |
107 |
|
107 | |||
108 | assert group.destroy |
|
108 | assert group.destroy | |
109 | assert group.destroyed? |
|
109 | assert group.destroyed? | |
110 |
|
110 | |||
111 | assert_equal nil, Issue.find(1).assigned_to_id |
|
111 | assert_equal nil, Issue.find(1).assigned_to_id | |
112 | end |
|
112 | end | |
113 | end |
|
113 | end |
@@ -1,147 +1,147 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2012 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 File.expand_path('../../test_helper', __FILE__) |
|
18 | require File.expand_path('../../test_helper', __FILE__) | |
19 |
|
19 | |||
20 | class RepositoryBazaarTest < ActiveSupport::TestCase |
|
20 | class RepositoryBazaarTest < ActiveSupport::TestCase | |
21 | fixtures :projects |
|
21 | fixtures :projects | |
22 |
|
22 | |||
23 | include Redmine::I18n |
|
23 | include Redmine::I18n | |
24 |
|
24 | |||
25 | REPOSITORY_PATH = Rails.root.join('tmp/test/bazaar_repository/trunk').to_s |
|
25 | REPOSITORY_PATH = Rails.root.join('tmp/test/bazaar_repository/trunk').to_s | |
26 | REPOSITORY_PATH.gsub!(/\/+/, '/') |
|
26 | REPOSITORY_PATH.gsub!(/\/+/, '/') | |
27 | NUM_REV = 4 |
|
27 | NUM_REV = 4 | |
28 |
|
28 | |||
29 | def setup |
|
29 | def setup | |
30 | @project = Project.find(3) |
|
30 | @project = Project.find(3) | |
31 | @repository = Repository::Bazaar.create( |
|
31 | @repository = Repository::Bazaar.create( | |
32 | :project => @project, :url => "file:///#{REPOSITORY_PATH}", |
|
32 | :project => @project, :url => "file:///#{REPOSITORY_PATH}", | |
33 | :log_encoding => 'UTF-8') |
|
33 | :log_encoding => 'UTF-8') | |
34 | assert @repository |
|
34 | assert @repository | |
35 | end |
|
35 | end | |
36 |
|
36 | |||
37 | def test_blank_path_to_repository_error_message |
|
37 | def test_blank_path_to_repository_error_message | |
38 | set_language_if_valid 'en' |
|
38 | set_language_if_valid 'en' | |
39 | repo = Repository::Bazaar.new( |
|
39 | repo = Repository::Bazaar.new( | |
40 | :project => @project, |
|
40 | :project => @project, | |
41 | :identifier => 'test', |
|
41 | :identifier => 'test', | |
42 | :log_encoding => 'UTF-8' |
|
42 | :log_encoding => 'UTF-8' | |
43 | ) |
|
43 | ) | |
44 | assert !repo.save |
|
44 | assert !repo.save | |
45 | assert_include "Path to repository can't be blank", |
|
45 | assert_include "Path to repository can't be blank", | |
46 | repo.errors.full_messages |
|
46 | repo.errors.full_messages | |
47 | end |
|
47 | end | |
48 |
|
48 | |||
49 | def test_blank_path_to_repository_error_message_fr |
|
49 | def test_blank_path_to_repository_error_message_fr | |
50 | set_language_if_valid 'fr' |
|
50 | set_language_if_valid 'fr' | |
51 | str = "Chemin du d\xc3\xa9p\xc3\xb4t doit \xc3\xaatre renseign\xc3\xa9(e)" |
|
51 | str = "Chemin du d\xc3\xa9p\xc3\xb4t doit \xc3\xaatre renseign\xc3\xa9(e)" | |
52 | str.force_encoding('UTF-8') if str.respond_to?(:force_encoding) |
|
52 | str.force_encoding('UTF-8') if str.respond_to?(:force_encoding) | |
53 | repo = Repository::Bazaar.new( |
|
53 | repo = Repository::Bazaar.new( | |
54 | :project => @project, |
|
54 | :project => @project, | |
55 | :url => "", |
|
55 | :url => "", | |
56 | :identifier => 'test', |
|
56 | :identifier => 'test', | |
57 | :log_encoding => 'UTF-8' |
|
57 | :log_encoding => 'UTF-8' | |
58 | ) |
|
58 | ) | |
59 | assert !repo.save |
|
59 | assert !repo.save | |
60 | assert_include str, repo.errors.full_messages |
|
60 | assert_include str, repo.errors.full_messages | |
61 | end |
|
61 | end | |
62 |
|
62 | |||
63 | if File.directory?(REPOSITORY_PATH) |
|
63 | if File.directory?(REPOSITORY_PATH) | |
64 | def test_fetch_changesets_from_scratch |
|
64 | def test_fetch_changesets_from_scratch | |
65 | assert_equal 0, @repository.changesets.count |
|
65 | assert_equal 0, @repository.changesets.count | |
66 | @repository.fetch_changesets |
|
66 | @repository.fetch_changesets | |
67 | @project.reload |
|
67 | @project.reload | |
68 |
|
68 | |||
69 | assert_equal NUM_REV, @repository.changesets.count |
|
69 | assert_equal NUM_REV, @repository.changesets.count | |
70 | assert_equal 9, @repository.changes.count |
|
70 | assert_equal 9, @repository.filechanges.count | |
71 | assert_equal 'Initial import', @repository.changesets.find_by_revision('1').comments |
|
71 | assert_equal 'Initial import', @repository.changesets.find_by_revision('1').comments | |
72 | end |
|
72 | end | |
73 |
|
73 | |||
74 | def test_fetch_changesets_incremental |
|
74 | def test_fetch_changesets_incremental | |
75 | assert_equal 0, @repository.changesets.count |
|
75 | assert_equal 0, @repository.changesets.count | |
76 | @repository.fetch_changesets |
|
76 | @repository.fetch_changesets | |
77 | @project.reload |
|
77 | @project.reload | |
78 | assert_equal NUM_REV, @repository.changesets.count |
|
78 | assert_equal NUM_REV, @repository.changesets.count | |
79 | # Remove changesets with revision > 5 |
|
79 | # Remove changesets with revision > 5 | |
80 | @repository.changesets.find(:all).each {|c| c.destroy if c.revision.to_i > 2} |
|
80 | @repository.changesets.find(:all).each {|c| c.destroy if c.revision.to_i > 2} | |
81 | @project.reload |
|
81 | @project.reload | |
82 | assert_equal 2, @repository.changesets.count |
|
82 | assert_equal 2, @repository.changesets.count | |
83 |
|
83 | |||
84 | @repository.fetch_changesets |
|
84 | @repository.fetch_changesets | |
85 | @project.reload |
|
85 | @project.reload | |
86 | assert_equal NUM_REV, @repository.changesets.count |
|
86 | assert_equal NUM_REV, @repository.changesets.count | |
87 | end |
|
87 | end | |
88 |
|
88 | |||
89 | def test_entries |
|
89 | def test_entries | |
90 | entries = @repository.entries |
|
90 | entries = @repository.entries | |
91 | assert_equal 2, entries.size |
|
91 | assert_equal 2, entries.size | |
92 |
|
92 | |||
93 | assert_equal 'dir', entries[0].kind |
|
93 | assert_equal 'dir', entries[0].kind | |
94 | assert_equal 'directory', entries[0].name |
|
94 | assert_equal 'directory', entries[0].name | |
95 |
|
95 | |||
96 | assert_equal 'file', entries[1].kind |
|
96 | assert_equal 'file', entries[1].kind | |
97 | assert_equal 'doc-mkdir.txt', entries[1].name |
|
97 | assert_equal 'doc-mkdir.txt', entries[1].name | |
98 | end |
|
98 | end | |
99 |
|
99 | |||
100 | def test_entries_in_subdirectory |
|
100 | def test_entries_in_subdirectory | |
101 | entries = @repository.entries('directory') |
|
101 | entries = @repository.entries('directory') | |
102 | assert_equal 3, entries.size |
|
102 | assert_equal 3, entries.size | |
103 |
|
103 | |||
104 | assert_equal 'file', entries.last.kind |
|
104 | assert_equal 'file', entries.last.kind | |
105 | assert_equal 'edit.png', entries.last.name |
|
105 | assert_equal 'edit.png', entries.last.name | |
106 | end |
|
106 | end | |
107 |
|
107 | |||
108 | def test_previous |
|
108 | def test_previous | |
109 | assert_equal 0, @repository.changesets.count |
|
109 | assert_equal 0, @repository.changesets.count | |
110 | @repository.fetch_changesets |
|
110 | @repository.fetch_changesets | |
111 | @project.reload |
|
111 | @project.reload | |
112 | assert_equal NUM_REV, @repository.changesets.count |
|
112 | assert_equal NUM_REV, @repository.changesets.count | |
113 | changeset = @repository.find_changeset_by_name('3') |
|
113 | changeset = @repository.find_changeset_by_name('3') | |
114 | assert_equal @repository.find_changeset_by_name('2'), changeset.previous |
|
114 | assert_equal @repository.find_changeset_by_name('2'), changeset.previous | |
115 | end |
|
115 | end | |
116 |
|
116 | |||
117 | def test_previous_nil |
|
117 | def test_previous_nil | |
118 | assert_equal 0, @repository.changesets.count |
|
118 | assert_equal 0, @repository.changesets.count | |
119 | @repository.fetch_changesets |
|
119 | @repository.fetch_changesets | |
120 | @project.reload |
|
120 | @project.reload | |
121 | assert_equal NUM_REV, @repository.changesets.count |
|
121 | assert_equal NUM_REV, @repository.changesets.count | |
122 | changeset = @repository.find_changeset_by_name('1') |
|
122 | changeset = @repository.find_changeset_by_name('1') | |
123 | assert_nil changeset.previous |
|
123 | assert_nil changeset.previous | |
124 | end |
|
124 | end | |
125 |
|
125 | |||
126 | def test_next |
|
126 | def test_next | |
127 | assert_equal 0, @repository.changesets.count |
|
127 | assert_equal 0, @repository.changesets.count | |
128 | @repository.fetch_changesets |
|
128 | @repository.fetch_changesets | |
129 | @project.reload |
|
129 | @project.reload | |
130 | assert_equal NUM_REV, @repository.changesets.count |
|
130 | assert_equal NUM_REV, @repository.changesets.count | |
131 | changeset = @repository.find_changeset_by_name('2') |
|
131 | changeset = @repository.find_changeset_by_name('2') | |
132 | assert_equal @repository.find_changeset_by_name('3'), changeset.next |
|
132 | assert_equal @repository.find_changeset_by_name('3'), changeset.next | |
133 | end |
|
133 | end | |
134 |
|
134 | |||
135 | def test_next_nil |
|
135 | def test_next_nil | |
136 | assert_equal 0, @repository.changesets.count |
|
136 | assert_equal 0, @repository.changesets.count | |
137 | @repository.fetch_changesets |
|
137 | @repository.fetch_changesets | |
138 | @project.reload |
|
138 | @project.reload | |
139 | assert_equal NUM_REV, @repository.changesets.count |
|
139 | assert_equal NUM_REV, @repository.changesets.count | |
140 | changeset = @repository.find_changeset_by_name('4') |
|
140 | changeset = @repository.find_changeset_by_name('4') | |
141 | assert_nil changeset.next |
|
141 | assert_nil changeset.next | |
142 | end |
|
142 | end | |
143 | else |
|
143 | else | |
144 | puts "Bazaar test repository NOT FOUND. Skipping unit tests !!!" |
|
144 | puts "Bazaar test repository NOT FOUND. Skipping unit tests !!!" | |
145 | def test_fake; assert true end |
|
145 | def test_fake; assert true end | |
146 | end |
|
146 | end | |
147 | end |
|
147 | end |
@@ -1,240 +1,240 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2012 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 File.expand_path('../../test_helper', __FILE__) |
|
18 | require File.expand_path('../../test_helper', __FILE__) | |
19 | require 'pp' |
|
19 | require 'pp' | |
20 | class RepositoryCvsTest < ActiveSupport::TestCase |
|
20 | class RepositoryCvsTest < ActiveSupport::TestCase | |
21 | fixtures :projects |
|
21 | fixtures :projects | |
22 |
|
22 | |||
23 | include Redmine::I18n |
|
23 | include Redmine::I18n | |
24 |
|
24 | |||
25 | REPOSITORY_PATH = Rails.root.join('tmp/test/cvs_repository').to_s |
|
25 | REPOSITORY_PATH = Rails.root.join('tmp/test/cvs_repository').to_s | |
26 | REPOSITORY_PATH.gsub!(/\//, "\\") if Redmine::Platform.mswin? |
|
26 | REPOSITORY_PATH.gsub!(/\//, "\\") if Redmine::Platform.mswin? | |
27 | # CVS module |
|
27 | # CVS module | |
28 | MODULE_NAME = 'test' |
|
28 | MODULE_NAME = 'test' | |
29 | CHANGESETS_NUM = 7 |
|
29 | CHANGESETS_NUM = 7 | |
30 |
|
30 | |||
31 | def setup |
|
31 | def setup | |
32 | @project = Project.find(3) |
|
32 | @project = Project.find(3) | |
33 | @repository = Repository::Cvs.create(:project => @project, |
|
33 | @repository = Repository::Cvs.create(:project => @project, | |
34 | :root_url => REPOSITORY_PATH, |
|
34 | :root_url => REPOSITORY_PATH, | |
35 | :url => MODULE_NAME, |
|
35 | :url => MODULE_NAME, | |
36 | :log_encoding => 'UTF-8') |
|
36 | :log_encoding => 'UTF-8') | |
37 | assert @repository |
|
37 | assert @repository | |
38 | end |
|
38 | end | |
39 |
|
39 | |||
40 | def test_blank_module_error_message |
|
40 | def test_blank_module_error_message | |
41 | set_language_if_valid 'en' |
|
41 | set_language_if_valid 'en' | |
42 | repo = Repository::Cvs.new( |
|
42 | repo = Repository::Cvs.new( | |
43 | :project => @project, |
|
43 | :project => @project, | |
44 | :identifier => 'test', |
|
44 | :identifier => 'test', | |
45 | :log_encoding => 'UTF-8', |
|
45 | :log_encoding => 'UTF-8', | |
46 | :root_url => REPOSITORY_PATH |
|
46 | :root_url => REPOSITORY_PATH | |
47 | ) |
|
47 | ) | |
48 | assert !repo.save |
|
48 | assert !repo.save | |
49 | assert_include "Module can't be blank", |
|
49 | assert_include "Module can't be blank", | |
50 | repo.errors.full_messages |
|
50 | repo.errors.full_messages | |
51 | end |
|
51 | end | |
52 |
|
52 | |||
53 | def test_blank_module_error_message_fr |
|
53 | def test_blank_module_error_message_fr | |
54 | set_language_if_valid 'fr' |
|
54 | set_language_if_valid 'fr' | |
55 | str = "Module doit \xc3\xaatre renseign\xc3\xa9(e)" |
|
55 | str = "Module doit \xc3\xaatre renseign\xc3\xa9(e)" | |
56 | str.force_encoding('UTF-8') if str.respond_to?(:force_encoding) |
|
56 | str.force_encoding('UTF-8') if str.respond_to?(:force_encoding) | |
57 | repo = Repository::Cvs.new( |
|
57 | repo = Repository::Cvs.new( | |
58 | :project => @project, |
|
58 | :project => @project, | |
59 | :identifier => 'test', |
|
59 | :identifier => 'test', | |
60 | :log_encoding => 'UTF-8', |
|
60 | :log_encoding => 'UTF-8', | |
61 | :path_encoding => '', |
|
61 | :path_encoding => '', | |
62 | :url => '', |
|
62 | :url => '', | |
63 | :root_url => REPOSITORY_PATH |
|
63 | :root_url => REPOSITORY_PATH | |
64 | ) |
|
64 | ) | |
65 | assert !repo.save |
|
65 | assert !repo.save | |
66 | assert_include str, repo.errors.full_messages |
|
66 | assert_include str, repo.errors.full_messages | |
67 | end |
|
67 | end | |
68 |
|
68 | |||
69 | def test_blank_cvsroot_error_message |
|
69 | def test_blank_cvsroot_error_message | |
70 | set_language_if_valid 'en' |
|
70 | set_language_if_valid 'en' | |
71 | repo = Repository::Cvs.new( |
|
71 | repo = Repository::Cvs.new( | |
72 | :project => @project, |
|
72 | :project => @project, | |
73 | :identifier => 'test', |
|
73 | :identifier => 'test', | |
74 | :log_encoding => 'UTF-8', |
|
74 | :log_encoding => 'UTF-8', | |
75 | :url => MODULE_NAME |
|
75 | :url => MODULE_NAME | |
76 | ) |
|
76 | ) | |
77 | assert !repo.save |
|
77 | assert !repo.save | |
78 | assert_include "CVSROOT can't be blank", |
|
78 | assert_include "CVSROOT can't be blank", | |
79 | repo.errors.full_messages |
|
79 | repo.errors.full_messages | |
80 | end |
|
80 | end | |
81 |
|
81 | |||
82 | def test_blank_cvsroot_error_message_fr |
|
82 | def test_blank_cvsroot_error_message_fr | |
83 | set_language_if_valid 'fr' |
|
83 | set_language_if_valid 'fr' | |
84 | str = "CVSROOT doit \xc3\xaatre renseign\xc3\xa9(e)" |
|
84 | str = "CVSROOT doit \xc3\xaatre renseign\xc3\xa9(e)" | |
85 | str.force_encoding('UTF-8') if str.respond_to?(:force_encoding) |
|
85 | str.force_encoding('UTF-8') if str.respond_to?(:force_encoding) | |
86 | repo = Repository::Cvs.new( |
|
86 | repo = Repository::Cvs.new( | |
87 | :project => @project, |
|
87 | :project => @project, | |
88 | :identifier => 'test', |
|
88 | :identifier => 'test', | |
89 | :log_encoding => 'UTF-8', |
|
89 | :log_encoding => 'UTF-8', | |
90 | :path_encoding => '', |
|
90 | :path_encoding => '', | |
91 | :url => MODULE_NAME, |
|
91 | :url => MODULE_NAME, | |
92 | :root_url => '' |
|
92 | :root_url => '' | |
93 | ) |
|
93 | ) | |
94 | assert !repo.save |
|
94 | assert !repo.save | |
95 | assert_include str, repo.errors.full_messages |
|
95 | assert_include str, repo.errors.full_messages | |
96 | end |
|
96 | end | |
97 |
|
97 | |||
98 | if File.directory?(REPOSITORY_PATH) |
|
98 | if File.directory?(REPOSITORY_PATH) | |
99 | def test_fetch_changesets_from_scratch |
|
99 | def test_fetch_changesets_from_scratch | |
100 | assert_equal 0, @repository.changesets.count |
|
100 | assert_equal 0, @repository.changesets.count | |
101 | @repository.fetch_changesets |
|
101 | @repository.fetch_changesets | |
102 | @project.reload |
|
102 | @project.reload | |
103 |
|
103 | |||
104 | assert_equal CHANGESETS_NUM, @repository.changesets.count |
|
104 | assert_equal CHANGESETS_NUM, @repository.changesets.count | |
105 | assert_equal 16, @repository.changes.count |
|
105 | assert_equal 16, @repository.filechanges.count | |
106 | assert_not_nil @repository.changesets.find_by_comments('Two files changed') |
|
106 | assert_not_nil @repository.changesets.find_by_comments('Two files changed') | |
107 |
|
107 | |||
108 | r2 = @repository.changesets.find_by_revision('2') |
|
108 | r2 = @repository.changesets.find_by_revision('2') | |
109 | assert_equal 'v1-20071213-162510', r2.scmid |
|
109 | assert_equal 'v1-20071213-162510', r2.scmid | |
110 | end |
|
110 | end | |
111 |
|
111 | |||
112 | def test_fetch_changesets_incremental |
|
112 | def test_fetch_changesets_incremental | |
113 | assert_equal 0, @repository.changesets.count |
|
113 | assert_equal 0, @repository.changesets.count | |
114 | @repository.fetch_changesets |
|
114 | @repository.fetch_changesets | |
115 | @project.reload |
|
115 | @project.reload | |
116 | assert_equal CHANGESETS_NUM, @repository.changesets.count |
|
116 | assert_equal CHANGESETS_NUM, @repository.changesets.count | |
117 |
|
117 | |||
118 | # Remove changesets with revision > 3 |
|
118 | # Remove changesets with revision > 3 | |
119 | @repository.changesets.find(:all).each {|c| c.destroy if c.revision.to_i > 3} |
|
119 | @repository.changesets.find(:all).each {|c| c.destroy if c.revision.to_i > 3} | |
120 | @project.reload |
|
120 | @project.reload | |
121 | assert_equal 3, @repository.changesets.count |
|
121 | assert_equal 3, @repository.changesets.count | |
122 | assert_equal %w|3 2 1|, @repository.changesets.all.collect(&:revision) |
|
122 | assert_equal %w|3 2 1|, @repository.changesets.all.collect(&:revision) | |
123 |
|
123 | |||
124 | rev3_commit = @repository.changesets.find(:first, :order => 'committed_on DESC') |
|
124 | rev3_commit = @repository.changesets.find(:first, :order => 'committed_on DESC') | |
125 | assert_equal '3', rev3_commit.revision |
|
125 | assert_equal '3', rev3_commit.revision | |
126 | # 2007-12-14 01:27:22 +0900 |
|
126 | # 2007-12-14 01:27:22 +0900 | |
127 | rev3_committed_on = Time.gm(2007, 12, 13, 16, 27, 22) |
|
127 | rev3_committed_on = Time.gm(2007, 12, 13, 16, 27, 22) | |
128 | assert_equal 'HEAD-20071213-162722', rev3_commit.scmid |
|
128 | assert_equal 'HEAD-20071213-162722', rev3_commit.scmid | |
129 | assert_equal rev3_committed_on, rev3_commit.committed_on |
|
129 | assert_equal rev3_committed_on, rev3_commit.committed_on | |
130 | latest_rev = @repository.latest_changeset |
|
130 | latest_rev = @repository.latest_changeset | |
131 | assert_equal rev3_committed_on, latest_rev.committed_on |
|
131 | assert_equal rev3_committed_on, latest_rev.committed_on | |
132 |
|
132 | |||
133 | @repository.fetch_changesets |
|
133 | @repository.fetch_changesets | |
134 | @project.reload |
|
134 | @project.reload | |
135 | assert_equal CHANGESETS_NUM, @repository.changesets.count |
|
135 | assert_equal CHANGESETS_NUM, @repository.changesets.count | |
136 | assert_equal %w|7 6 5 4 3 2 1|, @repository.changesets.all.collect(&:revision) |
|
136 | assert_equal %w|7 6 5 4 3 2 1|, @repository.changesets.all.collect(&:revision) | |
137 | rev5_commit = @repository.changesets.find_by_revision('5') |
|
137 | rev5_commit = @repository.changesets.find_by_revision('5') | |
138 | assert_equal 'HEAD-20071213-163001', rev5_commit.scmid |
|
138 | assert_equal 'HEAD-20071213-163001', rev5_commit.scmid | |
139 | # 2007-12-14 01:30:01 +0900 |
|
139 | # 2007-12-14 01:30:01 +0900 | |
140 | rev5_committed_on = Time.gm(2007, 12, 13, 16, 30, 1) |
|
140 | rev5_committed_on = Time.gm(2007, 12, 13, 16, 30, 1) | |
141 | assert_equal rev5_committed_on, rev5_commit.committed_on |
|
141 | assert_equal rev5_committed_on, rev5_commit.committed_on | |
142 | end |
|
142 | end | |
143 |
|
143 | |||
144 | def test_deleted_files_should_not_be_listed |
|
144 | def test_deleted_files_should_not_be_listed | |
145 | assert_equal 0, @repository.changesets.count |
|
145 | assert_equal 0, @repository.changesets.count | |
146 | @repository.fetch_changesets |
|
146 | @repository.fetch_changesets | |
147 | @project.reload |
|
147 | @project.reload | |
148 | assert_equal CHANGESETS_NUM, @repository.changesets.count |
|
148 | assert_equal CHANGESETS_NUM, @repository.changesets.count | |
149 |
|
149 | |||
150 | entries = @repository.entries('sources') |
|
150 | entries = @repository.entries('sources') | |
151 | assert entries.detect {|e| e.name == 'watchers_controller.rb'} |
|
151 | assert entries.detect {|e| e.name == 'watchers_controller.rb'} | |
152 | assert_nil entries.detect {|e| e.name == 'welcome_controller.rb'} |
|
152 | assert_nil entries.detect {|e| e.name == 'welcome_controller.rb'} | |
153 | end |
|
153 | end | |
154 |
|
154 | |||
155 | def test_entries_rev3 |
|
155 | def test_entries_rev3 | |
156 | assert_equal 0, @repository.changesets.count |
|
156 | assert_equal 0, @repository.changesets.count | |
157 | @repository.fetch_changesets |
|
157 | @repository.fetch_changesets | |
158 | @project.reload |
|
158 | @project.reload | |
159 | assert_equal CHANGESETS_NUM, @repository.changesets.count |
|
159 | assert_equal CHANGESETS_NUM, @repository.changesets.count | |
160 | entries = @repository.entries('', '3') |
|
160 | entries = @repository.entries('', '3') | |
161 | assert_equal 3, entries.size |
|
161 | assert_equal 3, entries.size | |
162 | assert_equal entries[2].name, "README" |
|
162 | assert_equal entries[2].name, "README" | |
163 | assert_equal entries[2].lastrev.time, Time.gm(2007, 12, 13, 16, 27, 22) |
|
163 | assert_equal entries[2].lastrev.time, Time.gm(2007, 12, 13, 16, 27, 22) | |
164 | assert_equal entries[2].lastrev.identifier, '3' |
|
164 | assert_equal entries[2].lastrev.identifier, '3' | |
165 | assert_equal entries[2].lastrev.revision, '3' |
|
165 | assert_equal entries[2].lastrev.revision, '3' | |
166 | assert_equal entries[2].lastrev.author, 'LANG' |
|
166 | assert_equal entries[2].lastrev.author, 'LANG' | |
167 | end |
|
167 | end | |
168 |
|
168 | |||
169 | def test_entries_invalid_path |
|
169 | def test_entries_invalid_path | |
170 | assert_equal 0, @repository.changesets.count |
|
170 | assert_equal 0, @repository.changesets.count | |
171 | @repository.fetch_changesets |
|
171 | @repository.fetch_changesets | |
172 | @project.reload |
|
172 | @project.reload | |
173 | assert_equal CHANGESETS_NUM, @repository.changesets.count |
|
173 | assert_equal CHANGESETS_NUM, @repository.changesets.count | |
174 | assert_nil @repository.entries('missing') |
|
174 | assert_nil @repository.entries('missing') | |
175 | assert_nil @repository.entries('missing', '3') |
|
175 | assert_nil @repository.entries('missing', '3') | |
176 | end |
|
176 | end | |
177 |
|
177 | |||
178 | def test_entries_invalid_revision |
|
178 | def test_entries_invalid_revision | |
179 | assert_equal 0, @repository.changesets.count |
|
179 | assert_equal 0, @repository.changesets.count | |
180 | @repository.fetch_changesets |
|
180 | @repository.fetch_changesets | |
181 | @project.reload |
|
181 | @project.reload | |
182 | assert_equal CHANGESETS_NUM, @repository.changesets.count |
|
182 | assert_equal CHANGESETS_NUM, @repository.changesets.count | |
183 | assert_nil @repository.entries('', '123') |
|
183 | assert_nil @repository.entries('', '123') | |
184 | end |
|
184 | end | |
185 |
|
185 | |||
186 | def test_cat |
|
186 | def test_cat | |
187 | assert_equal 0, @repository.changesets.count |
|
187 | assert_equal 0, @repository.changesets.count | |
188 | @repository.fetch_changesets |
|
188 | @repository.fetch_changesets | |
189 | @project.reload |
|
189 | @project.reload | |
190 | assert_equal CHANGESETS_NUM, @repository.changesets.count |
|
190 | assert_equal CHANGESETS_NUM, @repository.changesets.count | |
191 | buf = @repository.cat('README') |
|
191 | buf = @repository.cat('README') | |
192 | assert buf |
|
192 | assert buf | |
193 | lines = buf.split("\n") |
|
193 | lines = buf.split("\n") | |
194 | assert_equal 3, lines.length |
|
194 | assert_equal 3, lines.length | |
195 | buf = lines[1].gsub(/\r$/, "") |
|
195 | buf = lines[1].gsub(/\r$/, "") | |
196 | assert_equal 'with one change', buf |
|
196 | assert_equal 'with one change', buf | |
197 | buf = @repository.cat('README', '1') |
|
197 | buf = @repository.cat('README', '1') | |
198 | assert buf |
|
198 | assert buf | |
199 | lines = buf.split("\n") |
|
199 | lines = buf.split("\n") | |
200 | assert_equal 1, lines.length |
|
200 | assert_equal 1, lines.length | |
201 | buf = lines[0].gsub(/\r$/, "") |
|
201 | buf = lines[0].gsub(/\r$/, "") | |
202 | assert_equal 'CVS test repository', buf |
|
202 | assert_equal 'CVS test repository', buf | |
203 | assert_nil @repository.cat('missing.rb') |
|
203 | assert_nil @repository.cat('missing.rb') | |
204 |
|
204 | |||
205 | # sources/welcome_controller.rb is removed at revision 5. |
|
205 | # sources/welcome_controller.rb is removed at revision 5. | |
206 | assert @repository.cat('sources/welcome_controller.rb', '4') |
|
206 | assert @repository.cat('sources/welcome_controller.rb', '4') | |
207 | assert @repository.cat('sources/welcome_controller.rb', '5').blank? |
|
207 | assert @repository.cat('sources/welcome_controller.rb', '5').blank? | |
208 |
|
208 | |||
209 | # invalid revision |
|
209 | # invalid revision | |
210 | assert @repository.cat('README', '123').blank? |
|
210 | assert @repository.cat('README', '123').blank? | |
211 | end |
|
211 | end | |
212 |
|
212 | |||
213 | def test_annotate |
|
213 | def test_annotate | |
214 | assert_equal 0, @repository.changesets.count |
|
214 | assert_equal 0, @repository.changesets.count | |
215 | @repository.fetch_changesets |
|
215 | @repository.fetch_changesets | |
216 | @project.reload |
|
216 | @project.reload | |
217 | assert_equal CHANGESETS_NUM, @repository.changesets.count |
|
217 | assert_equal CHANGESETS_NUM, @repository.changesets.count | |
218 | ann = @repository.annotate('README') |
|
218 | ann = @repository.annotate('README') | |
219 | assert ann |
|
219 | assert ann | |
220 | assert_equal 3, ann.revisions.length |
|
220 | assert_equal 3, ann.revisions.length | |
221 | assert_equal '1.2', ann.revisions[1].revision |
|
221 | assert_equal '1.2', ann.revisions[1].revision | |
222 | assert_equal 'LANG', ann.revisions[1].author |
|
222 | assert_equal 'LANG', ann.revisions[1].author | |
223 | assert_equal 'with one change', ann.lines[1] |
|
223 | assert_equal 'with one change', ann.lines[1] | |
224 |
|
224 | |||
225 | ann = @repository.annotate('README', '1') |
|
225 | ann = @repository.annotate('README', '1') | |
226 | assert ann |
|
226 | assert ann | |
227 | assert_equal 1, ann.revisions.length |
|
227 | assert_equal 1, ann.revisions.length | |
228 | assert_equal '1.1', ann.revisions[0].revision |
|
228 | assert_equal '1.1', ann.revisions[0].revision | |
229 | assert_equal 'LANG', ann.revisions[0].author |
|
229 | assert_equal 'LANG', ann.revisions[0].author | |
230 | assert_equal 'CVS test repository', ann.lines[0] |
|
230 | assert_equal 'CVS test repository', ann.lines[0] | |
231 |
|
231 | |||
232 | # invalid revision |
|
232 | # invalid revision | |
233 | assert_nil @repository.annotate('README', '123') |
|
233 | assert_nil @repository.annotate('README', '123') | |
234 | end |
|
234 | end | |
235 |
|
235 | |||
236 | else |
|
236 | else | |
237 | puts "CVS test repository NOT FOUND. Skipping unit tests !!!" |
|
237 | puts "CVS test repository NOT FOUND. Skipping unit tests !!!" | |
238 | def test_fake; assert true end |
|
238 | def test_fake; assert true end | |
239 | end |
|
239 | end | |
240 | end |
|
240 | end |
@@ -1,124 +1,124 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2012 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 File.expand_path('../../test_helper', __FILE__) |
|
18 | require File.expand_path('../../test_helper', __FILE__) | |
19 |
|
19 | |||
20 | class RepositoryDarcsTest < ActiveSupport::TestCase |
|
20 | class RepositoryDarcsTest < ActiveSupport::TestCase | |
21 | fixtures :projects |
|
21 | fixtures :projects | |
22 |
|
22 | |||
23 | include Redmine::I18n |
|
23 | include Redmine::I18n | |
24 |
|
24 | |||
25 | REPOSITORY_PATH = Rails.root.join('tmp/test/darcs_repository').to_s |
|
25 | REPOSITORY_PATH = Rails.root.join('tmp/test/darcs_repository').to_s | |
26 | NUM_REV = 6 |
|
26 | NUM_REV = 6 | |
27 |
|
27 | |||
28 | def setup |
|
28 | def setup | |
29 | @project = Project.find(3) |
|
29 | @project = Project.find(3) | |
30 | @repository = Repository::Darcs.create( |
|
30 | @repository = Repository::Darcs.create( | |
31 | :project => @project, |
|
31 | :project => @project, | |
32 | :url => REPOSITORY_PATH, |
|
32 | :url => REPOSITORY_PATH, | |
33 | :log_encoding => 'UTF-8' |
|
33 | :log_encoding => 'UTF-8' | |
34 | ) |
|
34 | ) | |
35 | assert @repository |
|
35 | assert @repository | |
36 | end |
|
36 | end | |
37 |
|
37 | |||
38 | def test_blank_path_to_repository_error_message |
|
38 | def test_blank_path_to_repository_error_message | |
39 | set_language_if_valid 'en' |
|
39 | set_language_if_valid 'en' | |
40 | repo = Repository::Darcs.new( |
|
40 | repo = Repository::Darcs.new( | |
41 | :project => @project, |
|
41 | :project => @project, | |
42 | :identifier => 'test', |
|
42 | :identifier => 'test', | |
43 | :log_encoding => 'UTF-8' |
|
43 | :log_encoding => 'UTF-8' | |
44 | ) |
|
44 | ) | |
45 | assert !repo.save |
|
45 | assert !repo.save | |
46 | assert_include "Path to repository can't be blank", |
|
46 | assert_include "Path to repository can't be blank", | |
47 | repo.errors.full_messages |
|
47 | repo.errors.full_messages | |
48 | end |
|
48 | end | |
49 |
|
49 | |||
50 | def test_blank_path_to_repository_error_message_fr |
|
50 | def test_blank_path_to_repository_error_message_fr | |
51 | set_language_if_valid 'fr' |
|
51 | set_language_if_valid 'fr' | |
52 | str = "Chemin du d\xc3\xa9p\xc3\xb4t doit \xc3\xaatre renseign\xc3\xa9(e)" |
|
52 | str = "Chemin du d\xc3\xa9p\xc3\xb4t doit \xc3\xaatre renseign\xc3\xa9(e)" | |
53 | str.force_encoding('UTF-8') if str.respond_to?(:force_encoding) |
|
53 | str.force_encoding('UTF-8') if str.respond_to?(:force_encoding) | |
54 | repo = Repository::Darcs.new( |
|
54 | repo = Repository::Darcs.new( | |
55 | :project => @project, |
|
55 | :project => @project, | |
56 | :url => "", |
|
56 | :url => "", | |
57 | :identifier => 'test', |
|
57 | :identifier => 'test', | |
58 | :log_encoding => 'UTF-8' |
|
58 | :log_encoding => 'UTF-8' | |
59 | ) |
|
59 | ) | |
60 | assert !repo.save |
|
60 | assert !repo.save | |
61 | assert_include str, repo.errors.full_messages |
|
61 | assert_include str, repo.errors.full_messages | |
62 | end |
|
62 | end | |
63 |
|
63 | |||
64 | if File.directory?(REPOSITORY_PATH) |
|
64 | if File.directory?(REPOSITORY_PATH) | |
65 | def test_fetch_changesets_from_scratch |
|
65 | def test_fetch_changesets_from_scratch | |
66 | assert_equal 0, @repository.changesets.count |
|
66 | assert_equal 0, @repository.changesets.count | |
67 | @repository.fetch_changesets |
|
67 | @repository.fetch_changesets | |
68 | @project.reload |
|
68 | @project.reload | |
69 |
|
69 | |||
70 | assert_equal NUM_REV, @repository.changesets.count |
|
70 | assert_equal NUM_REV, @repository.changesets.count | |
71 | assert_equal 13, @repository.changes.count |
|
71 | assert_equal 13, @repository.filechanges.count | |
72 | assert_equal "Initial commit.", @repository.changesets.find_by_revision('1').comments |
|
72 | assert_equal "Initial commit.", @repository.changesets.find_by_revision('1').comments | |
73 | end |
|
73 | end | |
74 |
|
74 | |||
75 | def test_fetch_changesets_incremental |
|
75 | def test_fetch_changesets_incremental | |
76 | assert_equal 0, @repository.changesets.count |
|
76 | assert_equal 0, @repository.changesets.count | |
77 | @repository.fetch_changesets |
|
77 | @repository.fetch_changesets | |
78 | @project.reload |
|
78 | @project.reload | |
79 | assert_equal NUM_REV, @repository.changesets.count |
|
79 | assert_equal NUM_REV, @repository.changesets.count | |
80 |
|
80 | |||
81 | # Remove changesets with revision > 3 |
|
81 | # Remove changesets with revision > 3 | |
82 | @repository.changesets.find(:all).each {|c| c.destroy if c.revision.to_i > 3} |
|
82 | @repository.changesets.find(:all).each {|c| c.destroy if c.revision.to_i > 3} | |
83 | @project.reload |
|
83 | @project.reload | |
84 | assert_equal 3, @repository.changesets.count |
|
84 | assert_equal 3, @repository.changesets.count | |
85 |
|
85 | |||
86 | @repository.fetch_changesets |
|
86 | @repository.fetch_changesets | |
87 | @project.reload |
|
87 | @project.reload | |
88 | assert_equal NUM_REV, @repository.changesets.count |
|
88 | assert_equal NUM_REV, @repository.changesets.count | |
89 | end |
|
89 | end | |
90 |
|
90 | |||
91 | def test_entries_invalid_revision |
|
91 | def test_entries_invalid_revision | |
92 | assert_equal 0, @repository.changesets.count |
|
92 | assert_equal 0, @repository.changesets.count | |
93 | @repository.fetch_changesets |
|
93 | @repository.fetch_changesets | |
94 | @project.reload |
|
94 | @project.reload | |
95 | assert_equal NUM_REV, @repository.changesets.count |
|
95 | assert_equal NUM_REV, @repository.changesets.count | |
96 | assert_nil @repository.entries('', '123') |
|
96 | assert_nil @repository.entries('', '123') | |
97 | end |
|
97 | end | |
98 |
|
98 | |||
99 | def test_deleted_files_should_not_be_listed |
|
99 | def test_deleted_files_should_not_be_listed | |
100 | assert_equal 0, @repository.changesets.count |
|
100 | assert_equal 0, @repository.changesets.count | |
101 | @repository.fetch_changesets |
|
101 | @repository.fetch_changesets | |
102 | @project.reload |
|
102 | @project.reload | |
103 | assert_equal NUM_REV, @repository.changesets.count |
|
103 | assert_equal NUM_REV, @repository.changesets.count | |
104 | entries = @repository.entries('sources') |
|
104 | entries = @repository.entries('sources') | |
105 | assert entries.detect {|e| e.name == 'watchers_controller.rb'} |
|
105 | assert entries.detect {|e| e.name == 'watchers_controller.rb'} | |
106 | assert_nil entries.detect {|e| e.name == 'welcome_controller.rb'} |
|
106 | assert_nil entries.detect {|e| e.name == 'welcome_controller.rb'} | |
107 | end |
|
107 | end | |
108 |
|
108 | |||
109 | def test_cat |
|
109 | def test_cat | |
110 | if @repository.scm.supports_cat? |
|
110 | if @repository.scm.supports_cat? | |
111 | assert_equal 0, @repository.changesets.count |
|
111 | assert_equal 0, @repository.changesets.count | |
112 | @repository.fetch_changesets |
|
112 | @repository.fetch_changesets | |
113 | @project.reload |
|
113 | @project.reload | |
114 | assert_equal NUM_REV, @repository.changesets.count |
|
114 | assert_equal NUM_REV, @repository.changesets.count | |
115 | cat = @repository.cat("sources/welcome_controller.rb", 2) |
|
115 | cat = @repository.cat("sources/welcome_controller.rb", 2) | |
116 | assert_not_nil cat |
|
116 | assert_not_nil cat | |
117 | assert cat.include?('class WelcomeController < ApplicationController') |
|
117 | assert cat.include?('class WelcomeController < ApplicationController') | |
118 | end |
|
118 | end | |
119 | end |
|
119 | end | |
120 | else |
|
120 | else | |
121 | puts "Darcs test repository NOT FOUND. Skipping unit tests !!!" |
|
121 | puts "Darcs test repository NOT FOUND. Skipping unit tests !!!" | |
122 | def test_fake; assert true end |
|
122 | def test_fake; assert true end | |
123 | end |
|
123 | end | |
124 | end |
|
124 | end |
@@ -1,84 +1,84 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2012 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 File.expand_path('../../test_helper', __FILE__) |
|
18 | require File.expand_path('../../test_helper', __FILE__) | |
19 |
|
19 | |||
20 | class RepositoryFilesystemTest < ActiveSupport::TestCase |
|
20 | class RepositoryFilesystemTest < ActiveSupport::TestCase | |
21 | fixtures :projects |
|
21 | fixtures :projects | |
22 |
|
22 | |||
23 | include Redmine::I18n |
|
23 | include Redmine::I18n | |
24 |
|
24 | |||
25 | REPOSITORY_PATH = Rails.root.join('tmp/test/filesystem_repository').to_s |
|
25 | REPOSITORY_PATH = Rails.root.join('tmp/test/filesystem_repository').to_s | |
26 |
|
26 | |||
27 | def setup |
|
27 | def setup | |
28 | @project = Project.find(3) |
|
28 | @project = Project.find(3) | |
29 | Setting.enabled_scm << 'Filesystem' unless Setting.enabled_scm.include?('Filesystem') |
|
29 | Setting.enabled_scm << 'Filesystem' unless Setting.enabled_scm.include?('Filesystem') | |
30 | @repository = Repository::Filesystem.create( |
|
30 | @repository = Repository::Filesystem.create( | |
31 | :project => @project, |
|
31 | :project => @project, | |
32 | :url => REPOSITORY_PATH |
|
32 | :url => REPOSITORY_PATH | |
33 | ) |
|
33 | ) | |
34 | assert @repository |
|
34 | assert @repository | |
35 | end |
|
35 | end | |
36 |
|
36 | |||
37 | def test_blank_root_directory_error_message |
|
37 | def test_blank_root_directory_error_message | |
38 | set_language_if_valid 'en' |
|
38 | set_language_if_valid 'en' | |
39 | repo = Repository::Filesystem.new( |
|
39 | repo = Repository::Filesystem.new( | |
40 | :project => @project, |
|
40 | :project => @project, | |
41 | :identifier => 'test' |
|
41 | :identifier => 'test' | |
42 | ) |
|
42 | ) | |
43 | assert !repo.save |
|
43 | assert !repo.save | |
44 | assert_include "Root directory can't be blank", |
|
44 | assert_include "Root directory can't be blank", | |
45 | repo.errors.full_messages |
|
45 | repo.errors.full_messages | |
46 | end |
|
46 | end | |
47 |
|
47 | |||
48 | def test_blank_root_directory_error_message_fr |
|
48 | def test_blank_root_directory_error_message_fr | |
49 | set_language_if_valid 'fr' |
|
49 | set_language_if_valid 'fr' | |
50 | str = "R\xc3\xa9pertoire racine doit \xc3\xaatre renseign\xc3\xa9(e)" |
|
50 | str = "R\xc3\xa9pertoire racine doit \xc3\xaatre renseign\xc3\xa9(e)" | |
51 | str.force_encoding('UTF-8') if str.respond_to?(:force_encoding) |
|
51 | str.force_encoding('UTF-8') if str.respond_to?(:force_encoding) | |
52 | repo = Repository::Filesystem.new( |
|
52 | repo = Repository::Filesystem.new( | |
53 | :project => @project, |
|
53 | :project => @project, | |
54 | :url => "", |
|
54 | :url => "", | |
55 | :identifier => 'test', |
|
55 | :identifier => 'test', | |
56 | :path_encoding => '' |
|
56 | :path_encoding => '' | |
57 | ) |
|
57 | ) | |
58 | assert !repo.save |
|
58 | assert !repo.save | |
59 | assert_include str, repo.errors.full_messages |
|
59 | assert_include str, repo.errors.full_messages | |
60 | end |
|
60 | end | |
61 |
|
61 | |||
62 | if File.directory?(REPOSITORY_PATH) |
|
62 | if File.directory?(REPOSITORY_PATH) | |
63 | def test_fetch_changesets |
|
63 | def test_fetch_changesets | |
64 | assert_equal 0, @repository.changesets.count |
|
64 | assert_equal 0, @repository.changesets.count | |
65 | assert_equal 0, @repository.changes.count |
|
65 | assert_equal 0, @repository.filechanges.count | |
66 | @repository.fetch_changesets |
|
66 | @repository.fetch_changesets | |
67 | @project.reload |
|
67 | @project.reload | |
68 | assert_equal 0, @repository.changesets.count |
|
68 | assert_equal 0, @repository.changesets.count | |
69 | assert_equal 0, @repository.changes.count |
|
69 | assert_equal 0, @repository.filechanges.count | |
70 | end |
|
70 | end | |
71 |
|
71 | |||
72 | def test_entries |
|
72 | def test_entries | |
73 | assert_equal 3, @repository.entries("", 2).size |
|
73 | assert_equal 3, @repository.entries("", 2).size | |
74 | assert_equal 2, @repository.entries("dir", 3).size |
|
74 | assert_equal 2, @repository.entries("dir", 3).size | |
75 | end |
|
75 | end | |
76 |
|
76 | |||
77 | def test_cat |
|
77 | def test_cat | |
78 | assert_equal "TEST CAT\n", @repository.scm.cat("test") |
|
78 | assert_equal "TEST CAT\n", @repository.scm.cat("test") | |
79 | end |
|
79 | end | |
80 | else |
|
80 | else | |
81 | puts "Filesystem test repository NOT FOUND. Skipping unit tests !!! See doc/RUNNING_TESTS." |
|
81 | puts "Filesystem test repository NOT FOUND. Skipping unit tests !!! See doc/RUNNING_TESTS." | |
82 | def test_fake; assert true end |
|
82 | def test_fake; assert true end | |
83 | end |
|
83 | end | |
84 | end |
|
84 | end |
@@ -1,556 +1,556 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2012 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 File.expand_path('../../test_helper', __FILE__) |
|
18 | require File.expand_path('../../test_helper', __FILE__) | |
19 |
|
19 | |||
20 | class RepositoryGitTest < ActiveSupport::TestCase |
|
20 | class RepositoryGitTest < ActiveSupport::TestCase | |
21 | fixtures :projects, :repositories, :enabled_modules, :users, :roles |
|
21 | fixtures :projects, :repositories, :enabled_modules, :users, :roles | |
22 |
|
22 | |||
23 | include Redmine::I18n |
|
23 | include Redmine::I18n | |
24 |
|
24 | |||
25 | REPOSITORY_PATH = Rails.root.join('tmp/test/git_repository').to_s |
|
25 | REPOSITORY_PATH = Rails.root.join('tmp/test/git_repository').to_s | |
26 | REPOSITORY_PATH.gsub!(/\//, "\\") if Redmine::Platform.mswin? |
|
26 | REPOSITORY_PATH.gsub!(/\//, "\\") if Redmine::Platform.mswin? | |
27 |
|
27 | |||
28 | NUM_REV = 28 |
|
28 | NUM_REV = 28 | |
29 | NUM_HEAD = 6 |
|
29 | NUM_HEAD = 6 | |
30 |
|
30 | |||
31 | FELIX_HEX = "Felix Sch\xC3\xA4fer" |
|
31 | FELIX_HEX = "Felix Sch\xC3\xA4fer" | |
32 | CHAR_1_HEX = "\xc3\x9c" |
|
32 | CHAR_1_HEX = "\xc3\x9c" | |
33 |
|
33 | |||
34 | ## Ruby uses ANSI api to fork a process on Windows. |
|
34 | ## Ruby uses ANSI api to fork a process on Windows. | |
35 | ## Japanese Shift_JIS and Traditional Chinese Big5 have 0x5c(backslash) problem |
|
35 | ## Japanese Shift_JIS and Traditional Chinese Big5 have 0x5c(backslash) problem | |
36 | ## and these are incompatible with ASCII. |
|
36 | ## and these are incompatible with ASCII. | |
37 | # WINDOWS_PASS = Redmine::Platform.mswin? |
|
37 | # WINDOWS_PASS = Redmine::Platform.mswin? | |
38 | WINDOWS_PASS = false |
|
38 | WINDOWS_PASS = false | |
39 |
|
39 | |||
40 | ## Git, Mercurial and CVS path encodings are binary. |
|
40 | ## Git, Mercurial and CVS path encodings are binary. | |
41 | ## Subversion supports URL encoding for path. |
|
41 | ## Subversion supports URL encoding for path. | |
42 | ## Redmine Mercurial adapter and extension use URL encoding. |
|
42 | ## Redmine Mercurial adapter and extension use URL encoding. | |
43 | ## Git accepts only binary path in command line parameter. |
|
43 | ## Git accepts only binary path in command line parameter. | |
44 | ## So, there is no way to use binary command line parameter in JRuby. |
|
44 | ## So, there is no way to use binary command line parameter in JRuby. | |
45 | JRUBY_SKIP = (RUBY_PLATFORM == 'java') |
|
45 | JRUBY_SKIP = (RUBY_PLATFORM == 'java') | |
46 | JRUBY_SKIP_STR = "TODO: This test fails in JRuby" |
|
46 | JRUBY_SKIP_STR = "TODO: This test fails in JRuby" | |
47 |
|
47 | |||
48 | def setup |
|
48 | def setup | |
49 | @project = Project.find(3) |
|
49 | @project = Project.find(3) | |
50 | @repository = Repository::Git.create( |
|
50 | @repository = Repository::Git.create( | |
51 | :project => @project, |
|
51 | :project => @project, | |
52 | :url => REPOSITORY_PATH, |
|
52 | :url => REPOSITORY_PATH, | |
53 | :path_encoding => 'ISO-8859-1' |
|
53 | :path_encoding => 'ISO-8859-1' | |
54 | ) |
|
54 | ) | |
55 | assert @repository |
|
55 | assert @repository | |
56 | @char_1 = CHAR_1_HEX.dup |
|
56 | @char_1 = CHAR_1_HEX.dup | |
57 | if @char_1.respond_to?(:force_encoding) |
|
57 | if @char_1.respond_to?(:force_encoding) | |
58 | @char_1.force_encoding('UTF-8') |
|
58 | @char_1.force_encoding('UTF-8') | |
59 | end |
|
59 | end | |
60 | end |
|
60 | end | |
61 |
|
61 | |||
62 | def test_blank_path_to_repository_error_message |
|
62 | def test_blank_path_to_repository_error_message | |
63 | set_language_if_valid 'en' |
|
63 | set_language_if_valid 'en' | |
64 | repo = Repository::Git.new( |
|
64 | repo = Repository::Git.new( | |
65 | :project => @project, |
|
65 | :project => @project, | |
66 | :identifier => 'test' |
|
66 | :identifier => 'test' | |
67 | ) |
|
67 | ) | |
68 | assert !repo.save |
|
68 | assert !repo.save | |
69 | assert_include "Path to repository can't be blank", |
|
69 | assert_include "Path to repository can't be blank", | |
70 | repo.errors.full_messages |
|
70 | repo.errors.full_messages | |
71 | end |
|
71 | end | |
72 |
|
72 | |||
73 | def test_blank_path_to_repository_error_message_fr |
|
73 | def test_blank_path_to_repository_error_message_fr | |
74 | set_language_if_valid 'fr' |
|
74 | set_language_if_valid 'fr' | |
75 | str = "Chemin du d\xc3\xa9p\xc3\xb4t doit \xc3\xaatre renseign\xc3\xa9(e)" |
|
75 | str = "Chemin du d\xc3\xa9p\xc3\xb4t doit \xc3\xaatre renseign\xc3\xa9(e)" | |
76 | str.force_encoding('UTF-8') if str.respond_to?(:force_encoding) |
|
76 | str.force_encoding('UTF-8') if str.respond_to?(:force_encoding) | |
77 | repo = Repository::Git.new( |
|
77 | repo = Repository::Git.new( | |
78 | :project => @project, |
|
78 | :project => @project, | |
79 | :url => "", |
|
79 | :url => "", | |
80 | :identifier => 'test', |
|
80 | :identifier => 'test', | |
81 | :path_encoding => '' |
|
81 | :path_encoding => '' | |
82 | ) |
|
82 | ) | |
83 | assert !repo.save |
|
83 | assert !repo.save | |
84 | assert_include str, repo.errors.full_messages |
|
84 | assert_include str, repo.errors.full_messages | |
85 | end |
|
85 | end | |
86 |
|
86 | |||
87 | if File.directory?(REPOSITORY_PATH) |
|
87 | if File.directory?(REPOSITORY_PATH) | |
88 | def test_scm_available |
|
88 | def test_scm_available | |
89 | klass = Repository::Git |
|
89 | klass = Repository::Git | |
90 | assert_equal "Git", klass.scm_name |
|
90 | assert_equal "Git", klass.scm_name | |
91 | assert klass.scm_adapter_class |
|
91 | assert klass.scm_adapter_class | |
92 | assert_not_equal "", klass.scm_command |
|
92 | assert_not_equal "", klass.scm_command | |
93 | assert_equal true, klass.scm_available |
|
93 | assert_equal true, klass.scm_available | |
94 | end |
|
94 | end | |
95 |
|
95 | |||
96 | def test_fetch_changesets_from_scratch |
|
96 | def test_fetch_changesets_from_scratch | |
97 | assert_nil @repository.extra_info |
|
97 | assert_nil @repository.extra_info | |
98 |
|
98 | |||
99 | assert_equal 0, @repository.changesets.count |
|
99 | assert_equal 0, @repository.changesets.count | |
100 | @repository.fetch_changesets |
|
100 | @repository.fetch_changesets | |
101 | @project.reload |
|
101 | @project.reload | |
102 |
|
102 | |||
103 | assert_equal NUM_REV, @repository.changesets.count |
|
103 | assert_equal NUM_REV, @repository.changesets.count | |
104 | assert_equal 39, @repository.changes.count |
|
104 | assert_equal 39, @repository.filechanges.count | |
105 |
|
105 | |||
106 | commit = @repository.changesets.find_by_revision("7234cb2750b63f47bff735edc50a1c0a433c2518") |
|
106 | commit = @repository.changesets.find_by_revision("7234cb2750b63f47bff735edc50a1c0a433c2518") | |
107 | assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518", commit.scmid |
|
107 | assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518", commit.scmid | |
108 | assert_equal "Initial import.\nThe repository contains 3 files.", commit.comments |
|
108 | assert_equal "Initial import.\nThe repository contains 3 files.", commit.comments | |
109 | assert_equal "jsmith <jsmith@foo.bar>", commit.committer |
|
109 | assert_equal "jsmith <jsmith@foo.bar>", commit.committer | |
110 | assert_equal User.find_by_login('jsmith'), commit.user |
|
110 | assert_equal User.find_by_login('jsmith'), commit.user | |
111 | # TODO: add a commit with commit time <> author time to the test repository |
|
111 | # TODO: add a commit with commit time <> author time to the test repository | |
112 | assert_equal "2007-12-14 09:22:52".to_time, commit.committed_on |
|
112 | assert_equal "2007-12-14 09:22:52".to_time, commit.committed_on | |
113 | assert_equal "2007-12-14".to_date, commit.commit_date |
|
113 | assert_equal "2007-12-14".to_date, commit.commit_date | |
114 | assert_equal 3, commit.changes.count |
|
114 | assert_equal 3, commit.filechanges.count | |
115 | change = commit.changes.sort_by(&:path).first |
|
115 | change = commit.filechanges.sort_by(&:path).first | |
116 | assert_equal "README", change.path |
|
116 | assert_equal "README", change.path | |
117 | assert_equal nil, change.from_path |
|
117 | assert_equal nil, change.from_path | |
118 | assert_equal "A", change.action |
|
118 | assert_equal "A", change.action | |
119 |
|
119 | |||
120 | assert_equal NUM_HEAD, @repository.extra_info["heads"].size |
|
120 | assert_equal NUM_HEAD, @repository.extra_info["heads"].size | |
121 | end |
|
121 | end | |
122 |
|
122 | |||
123 | def test_fetch_changesets_incremental |
|
123 | def test_fetch_changesets_incremental | |
124 | assert_equal 0, @repository.changesets.count |
|
124 | assert_equal 0, @repository.changesets.count | |
125 | @repository.fetch_changesets |
|
125 | @repository.fetch_changesets | |
126 | @project.reload |
|
126 | @project.reload | |
127 | assert_equal NUM_REV, @repository.changesets.count |
|
127 | assert_equal NUM_REV, @repository.changesets.count | |
128 | extra_info_heads = @repository.extra_info["heads"].dup |
|
128 | extra_info_heads = @repository.extra_info["heads"].dup | |
129 | assert_equal NUM_HEAD, extra_info_heads.size |
|
129 | assert_equal NUM_HEAD, extra_info_heads.size | |
130 | extra_info_heads.delete_if { |x| x == "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c" } |
|
130 | extra_info_heads.delete_if { |x| x == "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c" } | |
131 | assert_equal 4, extra_info_heads.size |
|
131 | assert_equal 4, extra_info_heads.size | |
132 |
|
132 | |||
133 | del_revs = [ |
|
133 | del_revs = [ | |
134 | "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c", |
|
134 | "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c", | |
135 | "ed5bb786bbda2dee66a2d50faf51429dbc043a7b", |
|
135 | "ed5bb786bbda2dee66a2d50faf51429dbc043a7b", | |
136 | "4f26664364207fa8b1af9f8722647ab2d4ac5d43", |
|
136 | "4f26664364207fa8b1af9f8722647ab2d4ac5d43", | |
137 | "deff712f05a90d96edbd70facc47d944be5897e3", |
|
137 | "deff712f05a90d96edbd70facc47d944be5897e3", | |
138 | "32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf", |
|
138 | "32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf", | |
139 | "7e61ac704deecde634b51e59daa8110435dcb3da", |
|
139 | "7e61ac704deecde634b51e59daa8110435dcb3da", | |
140 | ] |
|
140 | ] | |
141 | @repository.changesets.each do |rev| |
|
141 | @repository.changesets.each do |rev| | |
142 | rev.destroy if del_revs.detect {|r| r == rev.scmid.to_s } |
|
142 | rev.destroy if del_revs.detect {|r| r == rev.scmid.to_s } | |
143 | end |
|
143 | end | |
144 | @project.reload |
|
144 | @project.reload | |
145 | cs1 = @repository.changesets |
|
145 | cs1 = @repository.changesets | |
146 | assert_equal NUM_REV - 6, cs1.count |
|
146 | assert_equal NUM_REV - 6, cs1.count | |
147 | extra_info_heads << "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8" |
|
147 | extra_info_heads << "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8" | |
148 | h = {} |
|
148 | h = {} | |
149 | h["heads"] = extra_info_heads |
|
149 | h["heads"] = extra_info_heads | |
150 | @repository.merge_extra_info(h) |
|
150 | @repository.merge_extra_info(h) | |
151 | @repository.save |
|
151 | @repository.save | |
152 | @project.reload |
|
152 | @project.reload | |
153 | assert @repository.extra_info["heads"].index("4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8") |
|
153 | assert @repository.extra_info["heads"].index("4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8") | |
154 | @repository.fetch_changesets |
|
154 | @repository.fetch_changesets | |
155 | @project.reload |
|
155 | @project.reload | |
156 | assert_equal NUM_REV, @repository.changesets.count |
|
156 | assert_equal NUM_REV, @repository.changesets.count | |
157 | assert_equal NUM_HEAD, @repository.extra_info["heads"].size |
|
157 | assert_equal NUM_HEAD, @repository.extra_info["heads"].size | |
158 | assert @repository.extra_info["heads"].index("83ca5fd546063a3c7dc2e568ba3355661a9e2b2c") |
|
158 | assert @repository.extra_info["heads"].index("83ca5fd546063a3c7dc2e568ba3355661a9e2b2c") | |
159 | end |
|
159 | end | |
160 |
|
160 | |||
161 | def test_fetch_changesets_history_editing |
|
161 | def test_fetch_changesets_history_editing | |
162 | assert_equal 0, @repository.changesets.count |
|
162 | assert_equal 0, @repository.changesets.count | |
163 | @repository.fetch_changesets |
|
163 | @repository.fetch_changesets | |
164 | @project.reload |
|
164 | @project.reload | |
165 | assert_equal NUM_REV, @repository.changesets.count |
|
165 | assert_equal NUM_REV, @repository.changesets.count | |
166 | extra_info_heads = @repository.extra_info["heads"].dup |
|
166 | extra_info_heads = @repository.extra_info["heads"].dup | |
167 | assert_equal NUM_HEAD, extra_info_heads.size |
|
167 | assert_equal NUM_HEAD, extra_info_heads.size | |
168 | extra_info_heads.delete_if { |x| x == "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c" } |
|
168 | extra_info_heads.delete_if { |x| x == "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c" } | |
169 | assert_equal 4, extra_info_heads.size |
|
169 | assert_equal 4, extra_info_heads.size | |
170 |
|
170 | |||
171 | del_revs = [ |
|
171 | del_revs = [ | |
172 | "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c", |
|
172 | "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c", | |
173 | "ed5bb786bbda2dee66a2d50faf51429dbc043a7b", |
|
173 | "ed5bb786bbda2dee66a2d50faf51429dbc043a7b", | |
174 | "4f26664364207fa8b1af9f8722647ab2d4ac5d43", |
|
174 | "4f26664364207fa8b1af9f8722647ab2d4ac5d43", | |
175 | "deff712f05a90d96edbd70facc47d944be5897e3", |
|
175 | "deff712f05a90d96edbd70facc47d944be5897e3", | |
176 | "32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf", |
|
176 | "32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf", | |
177 | "7e61ac704deecde634b51e59daa8110435dcb3da", |
|
177 | "7e61ac704deecde634b51e59daa8110435dcb3da", | |
178 | ] |
|
178 | ] | |
179 | @repository.changesets.each do |rev| |
|
179 | @repository.changesets.each do |rev| | |
180 | rev.destroy if del_revs.detect {|r| r == rev.scmid.to_s } |
|
180 | rev.destroy if del_revs.detect {|r| r == rev.scmid.to_s } | |
181 | end |
|
181 | end | |
182 | @project.reload |
|
182 | @project.reload | |
183 | assert_equal NUM_REV - 6, @repository.changesets.count |
|
183 | assert_equal NUM_REV - 6, @repository.changesets.count | |
184 |
|
184 | |||
185 | c = Changeset.new(:repository => @repository, |
|
185 | c = Changeset.new(:repository => @repository, | |
186 | :committed_on => Time.now, |
|
186 | :committed_on => Time.now, | |
187 | :revision => "abcd1234efgh", |
|
187 | :revision => "abcd1234efgh", | |
188 | :scmid => "abcd1234efgh", |
|
188 | :scmid => "abcd1234efgh", | |
189 | :comments => 'test') |
|
189 | :comments => 'test') | |
190 | assert c.save |
|
190 | assert c.save | |
191 | @project.reload |
|
191 | @project.reload | |
192 | assert_equal NUM_REV - 5, @repository.changesets.count |
|
192 | assert_equal NUM_REV - 5, @repository.changesets.count | |
193 |
|
193 | |||
194 | extra_info_heads << "1234abcd5678" |
|
194 | extra_info_heads << "1234abcd5678" | |
195 | h = {} |
|
195 | h = {} | |
196 | h["heads"] = extra_info_heads |
|
196 | h["heads"] = extra_info_heads | |
197 | @repository.merge_extra_info(h) |
|
197 | @repository.merge_extra_info(h) | |
198 | @repository.save |
|
198 | @repository.save | |
199 | @project.reload |
|
199 | @project.reload | |
200 | h1 = @repository.extra_info["heads"].dup |
|
200 | h1 = @repository.extra_info["heads"].dup | |
201 | assert h1.index("1234abcd5678") |
|
201 | assert h1.index("1234abcd5678") | |
202 | assert_equal 5, h1.size |
|
202 | assert_equal 5, h1.size | |
203 |
|
203 | |||
204 | @repository.fetch_changesets |
|
204 | @repository.fetch_changesets | |
205 | @project.reload |
|
205 | @project.reload | |
206 | assert_equal NUM_REV - 5, @repository.changesets.count |
|
206 | assert_equal NUM_REV - 5, @repository.changesets.count | |
207 | h2 = @repository.extra_info["heads"].dup |
|
207 | h2 = @repository.extra_info["heads"].dup | |
208 | assert_equal h1, h2 |
|
208 | assert_equal h1, h2 | |
209 | end |
|
209 | end | |
210 |
|
210 | |||
211 | def test_parents |
|
211 | def test_parents | |
212 | assert_equal 0, @repository.changesets.count |
|
212 | assert_equal 0, @repository.changesets.count | |
213 | @repository.fetch_changesets |
|
213 | @repository.fetch_changesets | |
214 | @project.reload |
|
214 | @project.reload | |
215 | assert_equal NUM_REV, @repository.changesets.count |
|
215 | assert_equal NUM_REV, @repository.changesets.count | |
216 | r1 = @repository.find_changeset_by_name("7234cb2750b63") |
|
216 | r1 = @repository.find_changeset_by_name("7234cb2750b63") | |
217 | assert_equal [], r1.parents |
|
217 | assert_equal [], r1.parents | |
218 | r2 = @repository.find_changeset_by_name("899a15dba03a3") |
|
218 | r2 = @repository.find_changeset_by_name("899a15dba03a3") | |
219 | assert_equal 1, r2.parents.length |
|
219 | assert_equal 1, r2.parents.length | |
220 | assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518", |
|
220 | assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518", | |
221 | r2.parents[0].identifier |
|
221 | r2.parents[0].identifier | |
222 | r3 = @repository.find_changeset_by_name("32ae898b720c2") |
|
222 | r3 = @repository.find_changeset_by_name("32ae898b720c2") | |
223 | assert_equal 2, r3.parents.length |
|
223 | assert_equal 2, r3.parents.length | |
224 | r4 = [r3.parents[0].identifier, r3.parents[1].identifier].sort |
|
224 | r4 = [r3.parents[0].identifier, r3.parents[1].identifier].sort | |
225 | assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8", r4[0] |
|
225 | assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8", r4[0] | |
226 | assert_equal "7e61ac704deecde634b51e59daa8110435dcb3da", r4[1] |
|
226 | assert_equal "7e61ac704deecde634b51e59daa8110435dcb3da", r4[1] | |
227 | end |
|
227 | end | |
228 |
|
228 | |||
229 | def test_db_consistent_ordering_init |
|
229 | def test_db_consistent_ordering_init | |
230 | assert_nil @repository.extra_info |
|
230 | assert_nil @repository.extra_info | |
231 | assert_equal 0, @repository.changesets.count |
|
231 | assert_equal 0, @repository.changesets.count | |
232 | @repository.fetch_changesets |
|
232 | @repository.fetch_changesets | |
233 | @project.reload |
|
233 | @project.reload | |
234 | assert_equal 1, @repository.extra_info["db_consistent"]["ordering"] |
|
234 | assert_equal 1, @repository.extra_info["db_consistent"]["ordering"] | |
235 | end |
|
235 | end | |
236 |
|
236 | |||
237 | def test_db_consistent_ordering_before_1_2 |
|
237 | def test_db_consistent_ordering_before_1_2 | |
238 | assert_nil @repository.extra_info |
|
238 | assert_nil @repository.extra_info | |
239 | assert_equal 0, @repository.changesets.count |
|
239 | assert_equal 0, @repository.changesets.count | |
240 | @repository.fetch_changesets |
|
240 | @repository.fetch_changesets | |
241 | @project.reload |
|
241 | @project.reload | |
242 | assert_equal NUM_REV, @repository.changesets.count |
|
242 | assert_equal NUM_REV, @repository.changesets.count | |
243 | assert_not_nil @repository.extra_info |
|
243 | assert_not_nil @repository.extra_info | |
244 | h = {} |
|
244 | h = {} | |
245 | h["heads"] = [] |
|
245 | h["heads"] = [] | |
246 | h["branches"] = {} |
|
246 | h["branches"] = {} | |
247 | h["db_consistent"] = {} |
|
247 | h["db_consistent"] = {} | |
248 | @repository.merge_extra_info(h) |
|
248 | @repository.merge_extra_info(h) | |
249 | @repository.save |
|
249 | @repository.save | |
250 | assert_equal NUM_REV, @repository.changesets.count |
|
250 | assert_equal NUM_REV, @repository.changesets.count | |
251 | @repository.fetch_changesets |
|
251 | @repository.fetch_changesets | |
252 | @project.reload |
|
252 | @project.reload | |
253 | assert_equal 0, @repository.extra_info["db_consistent"]["ordering"] |
|
253 | assert_equal 0, @repository.extra_info["db_consistent"]["ordering"] | |
254 |
|
254 | |||
255 | extra_info_heads = @repository.extra_info["heads"].dup |
|
255 | extra_info_heads = @repository.extra_info["heads"].dup | |
256 | extra_info_heads.delete_if { |x| x == "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c" } |
|
256 | extra_info_heads.delete_if { |x| x == "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c" } | |
257 | del_revs = [ |
|
257 | del_revs = [ | |
258 | "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c", |
|
258 | "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c", | |
259 | "ed5bb786bbda2dee66a2d50faf51429dbc043a7b", |
|
259 | "ed5bb786bbda2dee66a2d50faf51429dbc043a7b", | |
260 | "4f26664364207fa8b1af9f8722647ab2d4ac5d43", |
|
260 | "4f26664364207fa8b1af9f8722647ab2d4ac5d43", | |
261 | "deff712f05a90d96edbd70facc47d944be5897e3", |
|
261 | "deff712f05a90d96edbd70facc47d944be5897e3", | |
262 | "32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf", |
|
262 | "32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf", | |
263 | "7e61ac704deecde634b51e59daa8110435dcb3da", |
|
263 | "7e61ac704deecde634b51e59daa8110435dcb3da", | |
264 | ] |
|
264 | ] | |
265 | @repository.changesets.each do |rev| |
|
265 | @repository.changesets.each do |rev| | |
266 | rev.destroy if del_revs.detect {|r| r == rev.scmid.to_s } |
|
266 | rev.destroy if del_revs.detect {|r| r == rev.scmid.to_s } | |
267 | end |
|
267 | end | |
268 | @project.reload |
|
268 | @project.reload | |
269 | cs1 = @repository.changesets |
|
269 | cs1 = @repository.changesets | |
270 | assert_equal NUM_REV - 6, cs1.count |
|
270 | assert_equal NUM_REV - 6, cs1.count | |
271 | assert_equal 0, @repository.extra_info["db_consistent"]["ordering"] |
|
271 | assert_equal 0, @repository.extra_info["db_consistent"]["ordering"] | |
272 |
|
272 | |||
273 | extra_info_heads << "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8" |
|
273 | extra_info_heads << "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8" | |
274 | h = {} |
|
274 | h = {} | |
275 | h["heads"] = extra_info_heads |
|
275 | h["heads"] = extra_info_heads | |
276 | @repository.merge_extra_info(h) |
|
276 | @repository.merge_extra_info(h) | |
277 | @repository.save |
|
277 | @repository.save | |
278 | @project.reload |
|
278 | @project.reload | |
279 | assert @repository.extra_info["heads"].index("4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8") |
|
279 | assert @repository.extra_info["heads"].index("4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8") | |
280 | @repository.fetch_changesets |
|
280 | @repository.fetch_changesets | |
281 | @project.reload |
|
281 | @project.reload | |
282 | assert_equal NUM_REV, @repository.changesets.count |
|
282 | assert_equal NUM_REV, @repository.changesets.count | |
283 | assert_equal NUM_HEAD, @repository.extra_info["heads"].size |
|
283 | assert_equal NUM_HEAD, @repository.extra_info["heads"].size | |
284 |
|
284 | |||
285 | assert_equal 0, @repository.extra_info["db_consistent"]["ordering"] |
|
285 | assert_equal 0, @repository.extra_info["db_consistent"]["ordering"] | |
286 | end |
|
286 | end | |
287 |
|
287 | |||
288 | def test_heads_from_branches_hash |
|
288 | def test_heads_from_branches_hash | |
289 | assert_nil @repository.extra_info |
|
289 | assert_nil @repository.extra_info | |
290 | assert_equal 0, @repository.changesets.count |
|
290 | assert_equal 0, @repository.changesets.count | |
291 | assert_equal [], @repository.heads_from_branches_hash |
|
291 | assert_equal [], @repository.heads_from_branches_hash | |
292 | h = {} |
|
292 | h = {} | |
293 | h["branches"] = {} |
|
293 | h["branches"] = {} | |
294 | h["branches"]["test1"] = {} |
|
294 | h["branches"]["test1"] = {} | |
295 | h["branches"]["test1"]["last_scmid"] = "1234abcd" |
|
295 | h["branches"]["test1"]["last_scmid"] = "1234abcd" | |
296 | h["branches"]["test2"] = {} |
|
296 | h["branches"]["test2"] = {} | |
297 | h["branches"]["test2"]["last_scmid"] = "abcd1234" |
|
297 | h["branches"]["test2"]["last_scmid"] = "abcd1234" | |
298 | @repository.merge_extra_info(h) |
|
298 | @repository.merge_extra_info(h) | |
299 | @repository.save |
|
299 | @repository.save | |
300 | @project.reload |
|
300 | @project.reload | |
301 | assert_equal ["1234abcd", "abcd1234"], @repository.heads_from_branches_hash.sort |
|
301 | assert_equal ["1234abcd", "abcd1234"], @repository.heads_from_branches_hash.sort | |
302 | end |
|
302 | end | |
303 |
|
303 | |||
304 | def test_latest_changesets |
|
304 | def test_latest_changesets | |
305 | assert_equal 0, @repository.changesets.count |
|
305 | assert_equal 0, @repository.changesets.count | |
306 | @repository.fetch_changesets |
|
306 | @repository.fetch_changesets | |
307 | @project.reload |
|
307 | @project.reload | |
308 | assert_equal NUM_REV, @repository.changesets.count |
|
308 | assert_equal NUM_REV, @repository.changesets.count | |
309 | # with limit |
|
309 | # with limit | |
310 | changesets = @repository.latest_changesets('', 'master', 2) |
|
310 | changesets = @repository.latest_changesets('', 'master', 2) | |
311 | assert_equal 2, changesets.size |
|
311 | assert_equal 2, changesets.size | |
312 |
|
312 | |||
313 | # with path |
|
313 | # with path | |
314 | changesets = @repository.latest_changesets('images', 'master') |
|
314 | changesets = @repository.latest_changesets('images', 'master') | |
315 | assert_equal [ |
|
315 | assert_equal [ | |
316 | 'deff712f05a90d96edbd70facc47d944be5897e3', |
|
316 | 'deff712f05a90d96edbd70facc47d944be5897e3', | |
317 | '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', |
|
317 | '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', | |
318 | '7234cb2750b63f47bff735edc50a1c0a433c2518', |
|
318 | '7234cb2750b63f47bff735edc50a1c0a433c2518', | |
319 | ], changesets.collect(&:revision) |
|
319 | ], changesets.collect(&:revision) | |
320 |
|
320 | |||
321 | changesets = @repository.latest_changesets('README', nil) |
|
321 | changesets = @repository.latest_changesets('README', nil) | |
322 | assert_equal [ |
|
322 | assert_equal [ | |
323 | '32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf', |
|
323 | '32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf', | |
324 | '4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8', |
|
324 | '4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8', | |
325 | '713f4944648826f558cf548222f813dabe7cbb04', |
|
325 | '713f4944648826f558cf548222f813dabe7cbb04', | |
326 | '61b685fbe55ab05b5ac68402d5720c1a6ac973d1', |
|
326 | '61b685fbe55ab05b5ac68402d5720c1a6ac973d1', | |
327 | '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', |
|
327 | '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', | |
328 | '7234cb2750b63f47bff735edc50a1c0a433c2518', |
|
328 | '7234cb2750b63f47bff735edc50a1c0a433c2518', | |
329 | ], changesets.collect(&:revision) |
|
329 | ], changesets.collect(&:revision) | |
330 |
|
330 | |||
331 | # with path, revision and limit |
|
331 | # with path, revision and limit | |
332 | changesets = @repository.latest_changesets('images', '899a15dba') |
|
332 | changesets = @repository.latest_changesets('images', '899a15dba') | |
333 | assert_equal [ |
|
333 | assert_equal [ | |
334 | '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', |
|
334 | '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', | |
335 | '7234cb2750b63f47bff735edc50a1c0a433c2518', |
|
335 | '7234cb2750b63f47bff735edc50a1c0a433c2518', | |
336 | ], changesets.collect(&:revision) |
|
336 | ], changesets.collect(&:revision) | |
337 |
|
337 | |||
338 | changesets = @repository.latest_changesets('images', '899a15dba', 1) |
|
338 | changesets = @repository.latest_changesets('images', '899a15dba', 1) | |
339 | assert_equal [ |
|
339 | assert_equal [ | |
340 | '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', |
|
340 | '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', | |
341 | ], changesets.collect(&:revision) |
|
341 | ], changesets.collect(&:revision) | |
342 |
|
342 | |||
343 | changesets = @repository.latest_changesets('README', '899a15dba') |
|
343 | changesets = @repository.latest_changesets('README', '899a15dba') | |
344 | assert_equal [ |
|
344 | assert_equal [ | |
345 | '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', |
|
345 | '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', | |
346 | '7234cb2750b63f47bff735edc50a1c0a433c2518', |
|
346 | '7234cb2750b63f47bff735edc50a1c0a433c2518', | |
347 | ], changesets.collect(&:revision) |
|
347 | ], changesets.collect(&:revision) | |
348 |
|
348 | |||
349 | changesets = @repository.latest_changesets('README', '899a15dba', 1) |
|
349 | changesets = @repository.latest_changesets('README', '899a15dba', 1) | |
350 | assert_equal [ |
|
350 | assert_equal [ | |
351 | '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', |
|
351 | '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', | |
352 | ], changesets.collect(&:revision) |
|
352 | ], changesets.collect(&:revision) | |
353 |
|
353 | |||
354 | # with path, tag and limit |
|
354 | # with path, tag and limit | |
355 | changesets = @repository.latest_changesets('images', 'tag01.annotated') |
|
355 | changesets = @repository.latest_changesets('images', 'tag01.annotated') | |
356 | assert_equal [ |
|
356 | assert_equal [ | |
357 | '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', |
|
357 | '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', | |
358 | '7234cb2750b63f47bff735edc50a1c0a433c2518', |
|
358 | '7234cb2750b63f47bff735edc50a1c0a433c2518', | |
359 | ], changesets.collect(&:revision) |
|
359 | ], changesets.collect(&:revision) | |
360 |
|
360 | |||
361 | changesets = @repository.latest_changesets('images', 'tag01.annotated', 1) |
|
361 | changesets = @repository.latest_changesets('images', 'tag01.annotated', 1) | |
362 | assert_equal [ |
|
362 | assert_equal [ | |
363 | '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', |
|
363 | '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', | |
364 | ], changesets.collect(&:revision) |
|
364 | ], changesets.collect(&:revision) | |
365 |
|
365 | |||
366 | changesets = @repository.latest_changesets('README', 'tag01.annotated') |
|
366 | changesets = @repository.latest_changesets('README', 'tag01.annotated') | |
367 | assert_equal [ |
|
367 | assert_equal [ | |
368 | '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', |
|
368 | '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', | |
369 | '7234cb2750b63f47bff735edc50a1c0a433c2518', |
|
369 | '7234cb2750b63f47bff735edc50a1c0a433c2518', | |
370 | ], changesets.collect(&:revision) |
|
370 | ], changesets.collect(&:revision) | |
371 |
|
371 | |||
372 | changesets = @repository.latest_changesets('README', 'tag01.annotated', 1) |
|
372 | changesets = @repository.latest_changesets('README', 'tag01.annotated', 1) | |
373 | assert_equal [ |
|
373 | assert_equal [ | |
374 | '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', |
|
374 | '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', | |
375 | ], changesets.collect(&:revision) |
|
375 | ], changesets.collect(&:revision) | |
376 |
|
376 | |||
377 | # with path, branch and limit |
|
377 | # with path, branch and limit | |
378 | changesets = @repository.latest_changesets('images', 'test_branch') |
|
378 | changesets = @repository.latest_changesets('images', 'test_branch') | |
379 | assert_equal [ |
|
379 | assert_equal [ | |
380 | '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', |
|
380 | '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', | |
381 | '7234cb2750b63f47bff735edc50a1c0a433c2518', |
|
381 | '7234cb2750b63f47bff735edc50a1c0a433c2518', | |
382 | ], changesets.collect(&:revision) |
|
382 | ], changesets.collect(&:revision) | |
383 |
|
383 | |||
384 | changesets = @repository.latest_changesets('images', 'test_branch', 1) |
|
384 | changesets = @repository.latest_changesets('images', 'test_branch', 1) | |
385 | assert_equal [ |
|
385 | assert_equal [ | |
386 | '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', |
|
386 | '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', | |
387 | ], changesets.collect(&:revision) |
|
387 | ], changesets.collect(&:revision) | |
388 |
|
388 | |||
389 | changesets = @repository.latest_changesets('README', 'test_branch') |
|
389 | changesets = @repository.latest_changesets('README', 'test_branch') | |
390 | assert_equal [ |
|
390 | assert_equal [ | |
391 | '713f4944648826f558cf548222f813dabe7cbb04', |
|
391 | '713f4944648826f558cf548222f813dabe7cbb04', | |
392 | '61b685fbe55ab05b5ac68402d5720c1a6ac973d1', |
|
392 | '61b685fbe55ab05b5ac68402d5720c1a6ac973d1', | |
393 | '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', |
|
393 | '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', | |
394 | '7234cb2750b63f47bff735edc50a1c0a433c2518', |
|
394 | '7234cb2750b63f47bff735edc50a1c0a433c2518', | |
395 | ], changesets.collect(&:revision) |
|
395 | ], changesets.collect(&:revision) | |
396 |
|
396 | |||
397 | changesets = @repository.latest_changesets('README', 'test_branch', 2) |
|
397 | changesets = @repository.latest_changesets('README', 'test_branch', 2) | |
398 | assert_equal [ |
|
398 | assert_equal [ | |
399 | '713f4944648826f558cf548222f813dabe7cbb04', |
|
399 | '713f4944648826f558cf548222f813dabe7cbb04', | |
400 | '61b685fbe55ab05b5ac68402d5720c1a6ac973d1', |
|
400 | '61b685fbe55ab05b5ac68402d5720c1a6ac973d1', | |
401 | ], changesets.collect(&:revision) |
|
401 | ], changesets.collect(&:revision) | |
402 |
|
402 | |||
403 | if JRUBY_SKIP |
|
403 | if JRUBY_SKIP | |
404 | puts JRUBY_SKIP_STR |
|
404 | puts JRUBY_SKIP_STR | |
405 | else |
|
405 | else | |
406 | # latin-1 encoding path |
|
406 | # latin-1 encoding path | |
407 | changesets = @repository.latest_changesets( |
|
407 | changesets = @repository.latest_changesets( | |
408 | "latin-1-dir/test-#{@char_1}-2.txt", '64f1f3e89') |
|
408 | "latin-1-dir/test-#{@char_1}-2.txt", '64f1f3e89') | |
409 | assert_equal [ |
|
409 | assert_equal [ | |
410 | '64f1f3e89ad1cb57976ff0ad99a107012ba3481d', |
|
410 | '64f1f3e89ad1cb57976ff0ad99a107012ba3481d', | |
411 | '4fc55c43bf3d3dc2efb66145365ddc17639ce81e', |
|
411 | '4fc55c43bf3d3dc2efb66145365ddc17639ce81e', | |
412 | ], changesets.collect(&:revision) |
|
412 | ], changesets.collect(&:revision) | |
413 |
|
413 | |||
414 | changesets = @repository.latest_changesets( |
|
414 | changesets = @repository.latest_changesets( | |
415 | "latin-1-dir/test-#{@char_1}-2.txt", '64f1f3e89', 1) |
|
415 | "latin-1-dir/test-#{@char_1}-2.txt", '64f1f3e89', 1) | |
416 | assert_equal [ |
|
416 | assert_equal [ | |
417 | '64f1f3e89ad1cb57976ff0ad99a107012ba3481d', |
|
417 | '64f1f3e89ad1cb57976ff0ad99a107012ba3481d', | |
418 | ], changesets.collect(&:revision) |
|
418 | ], changesets.collect(&:revision) | |
419 | end |
|
419 | end | |
420 | end |
|
420 | end | |
421 |
|
421 | |||
422 | def test_latest_changesets_latin_1_dir |
|
422 | def test_latest_changesets_latin_1_dir | |
423 | if WINDOWS_PASS |
|
423 | if WINDOWS_PASS | |
424 | # |
|
424 | # | |
425 | elsif JRUBY_SKIP |
|
425 | elsif JRUBY_SKIP | |
426 | puts JRUBY_SKIP_STR |
|
426 | puts JRUBY_SKIP_STR | |
427 | else |
|
427 | else | |
428 | assert_equal 0, @repository.changesets.count |
|
428 | assert_equal 0, @repository.changesets.count | |
429 | @repository.fetch_changesets |
|
429 | @repository.fetch_changesets | |
430 | @project.reload |
|
430 | @project.reload | |
431 | assert_equal NUM_REV, @repository.changesets.count |
|
431 | assert_equal NUM_REV, @repository.changesets.count | |
432 | changesets = @repository.latest_changesets( |
|
432 | changesets = @repository.latest_changesets( | |
433 | "latin-1-dir/test-#{@char_1}-subdir", '1ca7f5ed') |
|
433 | "latin-1-dir/test-#{@char_1}-subdir", '1ca7f5ed') | |
434 | assert_equal [ |
|
434 | assert_equal [ | |
435 | '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', |
|
435 | '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', | |
436 | ], changesets.collect(&:revision) |
|
436 | ], changesets.collect(&:revision) | |
437 | end |
|
437 | end | |
438 | end |
|
438 | end | |
439 |
|
439 | |||
440 | def test_find_changeset_by_name |
|
440 | def test_find_changeset_by_name | |
441 | assert_equal 0, @repository.changesets.count |
|
441 | assert_equal 0, @repository.changesets.count | |
442 | @repository.fetch_changesets |
|
442 | @repository.fetch_changesets | |
443 | @project.reload |
|
443 | @project.reload | |
444 | assert_equal NUM_REV, @repository.changesets.count |
|
444 | assert_equal NUM_REV, @repository.changesets.count | |
445 | ['7234cb2750b63f47bff735edc50a1c0a433c2518', '7234cb2750b'].each do |r| |
|
445 | ['7234cb2750b63f47bff735edc50a1c0a433c2518', '7234cb2750b'].each do |r| | |
446 | assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518', |
|
446 | assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518', | |
447 | @repository.find_changeset_by_name(r).revision |
|
447 | @repository.find_changeset_by_name(r).revision | |
448 | end |
|
448 | end | |
449 | end |
|
449 | end | |
450 |
|
450 | |||
451 | def test_find_changeset_by_empty_name |
|
451 | def test_find_changeset_by_empty_name | |
452 | assert_equal 0, @repository.changesets.count |
|
452 | assert_equal 0, @repository.changesets.count | |
453 | @repository.fetch_changesets |
|
453 | @repository.fetch_changesets | |
454 | @project.reload |
|
454 | @project.reload | |
455 | assert_equal NUM_REV, @repository.changesets.count |
|
455 | assert_equal NUM_REV, @repository.changesets.count | |
456 | ['', ' ', nil].each do |r| |
|
456 | ['', ' ', nil].each do |r| | |
457 | assert_nil @repository.find_changeset_by_name(r) |
|
457 | assert_nil @repository.find_changeset_by_name(r) | |
458 | end |
|
458 | end | |
459 | end |
|
459 | end | |
460 |
|
460 | |||
461 | def test_identifier |
|
461 | def test_identifier | |
462 | assert_equal 0, @repository.changesets.count |
|
462 | assert_equal 0, @repository.changesets.count | |
463 | @repository.fetch_changesets |
|
463 | @repository.fetch_changesets | |
464 | @project.reload |
|
464 | @project.reload | |
465 | assert_equal NUM_REV, @repository.changesets.count |
|
465 | assert_equal NUM_REV, @repository.changesets.count | |
466 | c = @repository.changesets.find_by_revision( |
|
466 | c = @repository.changesets.find_by_revision( | |
467 | '7234cb2750b63f47bff735edc50a1c0a433c2518') |
|
467 | '7234cb2750b63f47bff735edc50a1c0a433c2518') | |
468 | assert_equal c.scmid, c.identifier |
|
468 | assert_equal c.scmid, c.identifier | |
469 | end |
|
469 | end | |
470 |
|
470 | |||
471 | def test_format_identifier |
|
471 | def test_format_identifier | |
472 | assert_equal 0, @repository.changesets.count |
|
472 | assert_equal 0, @repository.changesets.count | |
473 | @repository.fetch_changesets |
|
473 | @repository.fetch_changesets | |
474 | @project.reload |
|
474 | @project.reload | |
475 | assert_equal NUM_REV, @repository.changesets.count |
|
475 | assert_equal NUM_REV, @repository.changesets.count | |
476 | c = @repository.changesets.find_by_revision( |
|
476 | c = @repository.changesets.find_by_revision( | |
477 | '7234cb2750b63f47bff735edc50a1c0a433c2518') |
|
477 | '7234cb2750b63f47bff735edc50a1c0a433c2518') | |
478 | assert_equal '7234cb27', c.format_identifier |
|
478 | assert_equal '7234cb27', c.format_identifier | |
479 | end |
|
479 | end | |
480 |
|
480 | |||
481 | def test_activities |
|
481 | def test_activities | |
482 | c = Changeset.new(:repository => @repository, |
|
482 | c = Changeset.new(:repository => @repository, | |
483 | :committed_on => Time.now, |
|
483 | :committed_on => Time.now, | |
484 | :revision => 'abc7234cb2750b63f47bff735edc50a1c0a433c2', |
|
484 | :revision => 'abc7234cb2750b63f47bff735edc50a1c0a433c2', | |
485 | :scmid => 'abc7234cb2750b63f47bff735edc50a1c0a433c2', |
|
485 | :scmid => 'abc7234cb2750b63f47bff735edc50a1c0a433c2', | |
486 | :comments => 'test') |
|
486 | :comments => 'test') | |
487 | assert c.event_title.include?('abc7234c:') |
|
487 | assert c.event_title.include?('abc7234c:') | |
488 | assert_equal 'abc7234cb2750b63f47bff735edc50a1c0a433c2', c.event_url[:rev] |
|
488 | assert_equal 'abc7234cb2750b63f47bff735edc50a1c0a433c2', c.event_url[:rev] | |
489 | end |
|
489 | end | |
490 |
|
490 | |||
491 | def test_log_utf8 |
|
491 | def test_log_utf8 | |
492 | assert_equal 0, @repository.changesets.count |
|
492 | assert_equal 0, @repository.changesets.count | |
493 | @repository.fetch_changesets |
|
493 | @repository.fetch_changesets | |
494 | @project.reload |
|
494 | @project.reload | |
495 | assert_equal NUM_REV, @repository.changesets.count |
|
495 | assert_equal NUM_REV, @repository.changesets.count | |
496 | str_felix_hex = FELIX_HEX.dup |
|
496 | str_felix_hex = FELIX_HEX.dup | |
497 | if str_felix_hex.respond_to?(:force_encoding) |
|
497 | if str_felix_hex.respond_to?(:force_encoding) | |
498 | str_felix_hex.force_encoding('UTF-8') |
|
498 | str_felix_hex.force_encoding('UTF-8') | |
499 | end |
|
499 | end | |
500 | c = @repository.changesets.find_by_revision( |
|
500 | c = @repository.changesets.find_by_revision( | |
501 | 'ed5bb786bbda2dee66a2d50faf51429dbc043a7b') |
|
501 | 'ed5bb786bbda2dee66a2d50faf51429dbc043a7b') | |
502 | assert_equal "#{str_felix_hex} <felix@fachschaften.org>", c.committer |
|
502 | assert_equal "#{str_felix_hex} <felix@fachschaften.org>", c.committer | |
503 | end |
|
503 | end | |
504 |
|
504 | |||
505 | def test_previous |
|
505 | def test_previous | |
506 | assert_equal 0, @repository.changesets.count |
|
506 | assert_equal 0, @repository.changesets.count | |
507 | @repository.fetch_changesets |
|
507 | @repository.fetch_changesets | |
508 | @project.reload |
|
508 | @project.reload | |
509 | assert_equal NUM_REV, @repository.changesets.count |
|
509 | assert_equal NUM_REV, @repository.changesets.count | |
510 | %w|1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127 1ca7f5ed|.each do |r1| |
|
510 | %w|1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127 1ca7f5ed|.each do |r1| | |
511 | changeset = @repository.find_changeset_by_name(r1) |
|
511 | changeset = @repository.find_changeset_by_name(r1) | |
512 | %w|64f1f3e89ad1cb57976ff0ad99a107012ba3481d 64f1f3e89ad1|.each do |r2| |
|
512 | %w|64f1f3e89ad1cb57976ff0ad99a107012ba3481d 64f1f3e89ad1|.each do |r2| | |
513 | assert_equal @repository.find_changeset_by_name(r2), changeset.previous |
|
513 | assert_equal @repository.find_changeset_by_name(r2), changeset.previous | |
514 | end |
|
514 | end | |
515 | end |
|
515 | end | |
516 | end |
|
516 | end | |
517 |
|
517 | |||
518 | def test_previous_nil |
|
518 | def test_previous_nil | |
519 | assert_equal 0, @repository.changesets.count |
|
519 | assert_equal 0, @repository.changesets.count | |
520 | @repository.fetch_changesets |
|
520 | @repository.fetch_changesets | |
521 | @project.reload |
|
521 | @project.reload | |
522 | assert_equal NUM_REV, @repository.changesets.count |
|
522 | assert_equal NUM_REV, @repository.changesets.count | |
523 | %w|7234cb2750b63f47bff735edc50a1c0a433c2518 7234cb275|.each do |r1| |
|
523 | %w|7234cb2750b63f47bff735edc50a1c0a433c2518 7234cb275|.each do |r1| | |
524 | changeset = @repository.find_changeset_by_name(r1) |
|
524 | changeset = @repository.find_changeset_by_name(r1) | |
525 | assert_nil changeset.previous |
|
525 | assert_nil changeset.previous | |
526 | end |
|
526 | end | |
527 | end |
|
527 | end | |
528 |
|
528 | |||
529 | def test_next |
|
529 | def test_next | |
530 | assert_equal 0, @repository.changesets.count |
|
530 | assert_equal 0, @repository.changesets.count | |
531 | @repository.fetch_changesets |
|
531 | @repository.fetch_changesets | |
532 | @project.reload |
|
532 | @project.reload | |
533 | assert_equal NUM_REV, @repository.changesets.count |
|
533 | assert_equal NUM_REV, @repository.changesets.count | |
534 | %w|64f1f3e89ad1cb57976ff0ad99a107012ba3481d 64f1f3e89ad1|.each do |r2| |
|
534 | %w|64f1f3e89ad1cb57976ff0ad99a107012ba3481d 64f1f3e89ad1|.each do |r2| | |
535 | changeset = @repository.find_changeset_by_name(r2) |
|
535 | changeset = @repository.find_changeset_by_name(r2) | |
536 | %w|1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127 1ca7f5ed|.each do |r1| |
|
536 | %w|1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127 1ca7f5ed|.each do |r1| | |
537 | assert_equal @repository.find_changeset_by_name(r1), changeset.next |
|
537 | assert_equal @repository.find_changeset_by_name(r1), changeset.next | |
538 | end |
|
538 | end | |
539 | end |
|
539 | end | |
540 | end |
|
540 | end | |
541 |
|
541 | |||
542 | def test_next_nil |
|
542 | def test_next_nil | |
543 | assert_equal 0, @repository.changesets.count |
|
543 | assert_equal 0, @repository.changesets.count | |
544 | @repository.fetch_changesets |
|
544 | @repository.fetch_changesets | |
545 | @project.reload |
|
545 | @project.reload | |
546 | assert_equal NUM_REV, @repository.changesets.count |
|
546 | assert_equal NUM_REV, @repository.changesets.count | |
547 | %w|2a682156a3b6e77a8bf9cd4590e8db757f3c6c78 2a682156a3b6e77a|.each do |r1| |
|
547 | %w|2a682156a3b6e77a8bf9cd4590e8db757f3c6c78 2a682156a3b6e77a|.each do |r1| | |
548 | changeset = @repository.find_changeset_by_name(r1) |
|
548 | changeset = @repository.find_changeset_by_name(r1) | |
549 | assert_nil changeset.next |
|
549 | assert_nil changeset.next | |
550 | end |
|
550 | end | |
551 | end |
|
551 | end | |
552 | else |
|
552 | else | |
553 | puts "Git test repository NOT FOUND. Skipping unit tests !!!" |
|
553 | puts "Git test repository NOT FOUND. Skipping unit tests !!!" | |
554 | def test_fake; assert true end |
|
554 | def test_fake; assert true end | |
555 | end |
|
555 | end | |
556 | end |
|
556 | end |
@@ -1,372 +1,372 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2012 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 File.expand_path('../../test_helper', __FILE__) |
|
18 | require File.expand_path('../../test_helper', __FILE__) | |
19 |
|
19 | |||
20 | class RepositoryMercurialTest < ActiveSupport::TestCase |
|
20 | class RepositoryMercurialTest < ActiveSupport::TestCase | |
21 | fixtures :projects |
|
21 | fixtures :projects | |
22 |
|
22 | |||
23 | include Redmine::I18n |
|
23 | include Redmine::I18n | |
24 |
|
24 | |||
25 | REPOSITORY_PATH = Rails.root.join('tmp/test/mercurial_repository').to_s |
|
25 | REPOSITORY_PATH = Rails.root.join('tmp/test/mercurial_repository').to_s | |
26 | NUM_REV = 32 |
|
26 | NUM_REV = 32 | |
27 | CHAR_1_HEX = "\xc3\x9c" |
|
27 | CHAR_1_HEX = "\xc3\x9c" | |
28 |
|
28 | |||
29 | def setup |
|
29 | def setup | |
30 | @project = Project.find(3) |
|
30 | @project = Project.find(3) | |
31 | @repository = Repository::Mercurial.create( |
|
31 | @repository = Repository::Mercurial.create( | |
32 | :project => @project, |
|
32 | :project => @project, | |
33 | :url => REPOSITORY_PATH, |
|
33 | :url => REPOSITORY_PATH, | |
34 | :path_encoding => 'ISO-8859-1' |
|
34 | :path_encoding => 'ISO-8859-1' | |
35 | ) |
|
35 | ) | |
36 | assert @repository |
|
36 | assert @repository | |
37 | @char_1 = CHAR_1_HEX.dup |
|
37 | @char_1 = CHAR_1_HEX.dup | |
38 | @tag_char_1 = "tag-#{CHAR_1_HEX}-00" |
|
38 | @tag_char_1 = "tag-#{CHAR_1_HEX}-00" | |
39 | @branch_char_0 = "branch-#{CHAR_1_HEX}-00" |
|
39 | @branch_char_0 = "branch-#{CHAR_1_HEX}-00" | |
40 | @branch_char_1 = "branch-#{CHAR_1_HEX}-01" |
|
40 | @branch_char_1 = "branch-#{CHAR_1_HEX}-01" | |
41 | if @char_1.respond_to?(:force_encoding) |
|
41 | if @char_1.respond_to?(:force_encoding) | |
42 | @char_1.force_encoding('UTF-8') |
|
42 | @char_1.force_encoding('UTF-8') | |
43 | @tag_char_1.force_encoding('UTF-8') |
|
43 | @tag_char_1.force_encoding('UTF-8') | |
44 | @branch_char_0.force_encoding('UTF-8') |
|
44 | @branch_char_0.force_encoding('UTF-8') | |
45 | @branch_char_1.force_encoding('UTF-8') |
|
45 | @branch_char_1.force_encoding('UTF-8') | |
46 | end |
|
46 | end | |
47 | end |
|
47 | end | |
48 |
|
48 | |||
49 |
|
49 | |||
50 | def test_blank_path_to_repository_error_message |
|
50 | def test_blank_path_to_repository_error_message | |
51 | set_language_if_valid 'en' |
|
51 | set_language_if_valid 'en' | |
52 | repo = Repository::Mercurial.new( |
|
52 | repo = Repository::Mercurial.new( | |
53 | :project => @project, |
|
53 | :project => @project, | |
54 | :identifier => 'test' |
|
54 | :identifier => 'test' | |
55 | ) |
|
55 | ) | |
56 | assert !repo.save |
|
56 | assert !repo.save | |
57 | assert_include "Path to repository can't be blank", |
|
57 | assert_include "Path to repository can't be blank", | |
58 | repo.errors.full_messages |
|
58 | repo.errors.full_messages | |
59 | end |
|
59 | end | |
60 |
|
60 | |||
61 | def test_blank_path_to_repository_error_message_fr |
|
61 | def test_blank_path_to_repository_error_message_fr | |
62 | set_language_if_valid 'fr' |
|
62 | set_language_if_valid 'fr' | |
63 | str = "Chemin du d\xc3\xa9p\xc3\xb4t doit \xc3\xaatre renseign\xc3\xa9(e)" |
|
63 | str = "Chemin du d\xc3\xa9p\xc3\xb4t doit \xc3\xaatre renseign\xc3\xa9(e)" | |
64 | str.force_encoding('UTF-8') if str.respond_to?(:force_encoding) |
|
64 | str.force_encoding('UTF-8') if str.respond_to?(:force_encoding) | |
65 | repo = Repository::Mercurial.new( |
|
65 | repo = Repository::Mercurial.new( | |
66 | :project => @project, |
|
66 | :project => @project, | |
67 | :url => "", |
|
67 | :url => "", | |
68 | :identifier => 'test', |
|
68 | :identifier => 'test', | |
69 | :path_encoding => '' |
|
69 | :path_encoding => '' | |
70 | ) |
|
70 | ) | |
71 | assert !repo.save |
|
71 | assert !repo.save | |
72 | assert_include str, repo.errors.full_messages |
|
72 | assert_include str, repo.errors.full_messages | |
73 | end |
|
73 | end | |
74 |
|
74 | |||
75 | if File.directory?(REPOSITORY_PATH) |
|
75 | if File.directory?(REPOSITORY_PATH) | |
76 | def test_scm_available |
|
76 | def test_scm_available | |
77 | klass = Repository::Mercurial |
|
77 | klass = Repository::Mercurial | |
78 | assert_equal "Mercurial", klass.scm_name |
|
78 | assert_equal "Mercurial", klass.scm_name | |
79 | assert klass.scm_adapter_class |
|
79 | assert klass.scm_adapter_class | |
80 | assert_not_equal "", klass.scm_command |
|
80 | assert_not_equal "", klass.scm_command | |
81 | assert_equal true, klass.scm_available |
|
81 | assert_equal true, klass.scm_available | |
82 | end |
|
82 | end | |
83 |
|
83 | |||
84 | def test_fetch_changesets_from_scratch |
|
84 | def test_fetch_changesets_from_scratch | |
85 | assert_equal 0, @repository.changesets.count |
|
85 | assert_equal 0, @repository.changesets.count | |
86 | @repository.fetch_changesets |
|
86 | @repository.fetch_changesets | |
87 | @project.reload |
|
87 | @project.reload | |
88 | assert_equal NUM_REV, @repository.changesets.count |
|
88 | assert_equal NUM_REV, @repository.changesets.count | |
89 | assert_equal 46, @repository.changes.count |
|
89 | assert_equal 46, @repository.filechanges.count | |
90 | assert_equal "Initial import.\nThe repository contains 3 files.", |
|
90 | assert_equal "Initial import.\nThe repository contains 3 files.", | |
91 | @repository.changesets.find_by_revision('0').comments |
|
91 | @repository.changesets.find_by_revision('0').comments | |
92 | end |
|
92 | end | |
93 |
|
93 | |||
94 | def test_fetch_changesets_incremental |
|
94 | def test_fetch_changesets_incremental | |
95 | assert_equal 0, @repository.changesets.count |
|
95 | assert_equal 0, @repository.changesets.count | |
96 | @repository.fetch_changesets |
|
96 | @repository.fetch_changesets | |
97 | @project.reload |
|
97 | @project.reload | |
98 | assert_equal NUM_REV, @repository.changesets.count |
|
98 | assert_equal NUM_REV, @repository.changesets.count | |
99 | # Remove changesets with revision > 2 |
|
99 | # Remove changesets with revision > 2 | |
100 | @repository.changesets.find(:all).each {|c| c.destroy if c.revision.to_i > 2} |
|
100 | @repository.changesets.find(:all).each {|c| c.destroy if c.revision.to_i > 2} | |
101 | @project.reload |
|
101 | @project.reload | |
102 | assert_equal 3, @repository.changesets.count |
|
102 | assert_equal 3, @repository.changesets.count | |
103 |
|
103 | |||
104 | @repository.fetch_changesets |
|
104 | @repository.fetch_changesets | |
105 | @project.reload |
|
105 | @project.reload | |
106 | assert_equal NUM_REV, @repository.changesets.count |
|
106 | assert_equal NUM_REV, @repository.changesets.count | |
107 | end |
|
107 | end | |
108 |
|
108 | |||
109 | def test_isodatesec |
|
109 | def test_isodatesec | |
110 | # Template keyword 'isodatesec' supported in Mercurial 1.0 and higher |
|
110 | # Template keyword 'isodatesec' supported in Mercurial 1.0 and higher | |
111 | if @repository.scm.class.client_version_above?([1, 0]) |
|
111 | if @repository.scm.class.client_version_above?([1, 0]) | |
112 | assert_equal 0, @repository.changesets.count |
|
112 | assert_equal 0, @repository.changesets.count | |
113 | @repository.fetch_changesets |
|
113 | @repository.fetch_changesets | |
114 | @project.reload |
|
114 | @project.reload | |
115 | assert_equal NUM_REV, @repository.changesets.count |
|
115 | assert_equal NUM_REV, @repository.changesets.count | |
116 | rev0_committed_on = Time.gm(2007, 12, 14, 9, 22, 52) |
|
116 | rev0_committed_on = Time.gm(2007, 12, 14, 9, 22, 52) | |
117 | assert_equal @repository.changesets.find_by_revision('0').committed_on, rev0_committed_on |
|
117 | assert_equal @repository.changesets.find_by_revision('0').committed_on, rev0_committed_on | |
118 | end |
|
118 | end | |
119 | end |
|
119 | end | |
120 |
|
120 | |||
121 | def test_changeset_order_by_revision |
|
121 | def test_changeset_order_by_revision | |
122 | assert_equal 0, @repository.changesets.count |
|
122 | assert_equal 0, @repository.changesets.count | |
123 | @repository.fetch_changesets |
|
123 | @repository.fetch_changesets | |
124 | @project.reload |
|
124 | @project.reload | |
125 | assert_equal NUM_REV, @repository.changesets.count |
|
125 | assert_equal NUM_REV, @repository.changesets.count | |
126 |
|
126 | |||
127 | c0 = @repository.latest_changeset |
|
127 | c0 = @repository.latest_changeset | |
128 | c1 = @repository.changesets.find_by_revision('0') |
|
128 | c1 = @repository.changesets.find_by_revision('0') | |
129 | # sorted by revision (id), not by date |
|
129 | # sorted by revision (id), not by date | |
130 | assert c0.revision.to_i > c1.revision.to_i |
|
130 | assert c0.revision.to_i > c1.revision.to_i | |
131 | assert c0.committed_on < c1.committed_on |
|
131 | assert c0.committed_on < c1.committed_on | |
132 | end |
|
132 | end | |
133 |
|
133 | |||
134 | def test_latest_changesets |
|
134 | def test_latest_changesets | |
135 | assert_equal 0, @repository.changesets.count |
|
135 | assert_equal 0, @repository.changesets.count | |
136 | @repository.fetch_changesets |
|
136 | @repository.fetch_changesets | |
137 | @project.reload |
|
137 | @project.reload | |
138 | assert_equal NUM_REV, @repository.changesets.count |
|
138 | assert_equal NUM_REV, @repository.changesets.count | |
139 |
|
139 | |||
140 | # with_limit |
|
140 | # with_limit | |
141 | changesets = @repository.latest_changesets('', nil, 2) |
|
141 | changesets = @repository.latest_changesets('', nil, 2) | |
142 | assert_equal %w|31 30|, changesets.collect(&:revision) |
|
142 | assert_equal %w|31 30|, changesets.collect(&:revision) | |
143 |
|
143 | |||
144 | # with_filepath |
|
144 | # with_filepath | |
145 | changesets = @repository.latest_changesets( |
|
145 | changesets = @repository.latest_changesets( | |
146 | '/sql_escape/percent%dir/percent%file1.txt', nil) |
|
146 | '/sql_escape/percent%dir/percent%file1.txt', nil) | |
147 | assert_equal %w|30 11 10 9|, changesets.collect(&:revision) |
|
147 | assert_equal %w|30 11 10 9|, changesets.collect(&:revision) | |
148 |
|
148 | |||
149 | changesets = @repository.latest_changesets( |
|
149 | changesets = @repository.latest_changesets( | |
150 | '/sql_escape/underscore_dir/understrike_file.txt', nil) |
|
150 | '/sql_escape/underscore_dir/understrike_file.txt', nil) | |
151 | assert_equal %w|30 12 9|, changesets.collect(&:revision) |
|
151 | assert_equal %w|30 12 9|, changesets.collect(&:revision) | |
152 |
|
152 | |||
153 | changesets = @repository.latest_changesets('README', nil) |
|
153 | changesets = @repository.latest_changesets('README', nil) | |
154 | assert_equal %w|31 30 28 17 8 6 1 0|, changesets.collect(&:revision) |
|
154 | assert_equal %w|31 30 28 17 8 6 1 0|, changesets.collect(&:revision) | |
155 |
|
155 | |||
156 | changesets = @repository.latest_changesets('README','8') |
|
156 | changesets = @repository.latest_changesets('README','8') | |
157 | assert_equal %w|8 6 1 0|, changesets.collect(&:revision) |
|
157 | assert_equal %w|8 6 1 0|, changesets.collect(&:revision) | |
158 |
|
158 | |||
159 | changesets = @repository.latest_changesets('README','8', 2) |
|
159 | changesets = @repository.latest_changesets('README','8', 2) | |
160 | assert_equal %w|8 6|, changesets.collect(&:revision) |
|
160 | assert_equal %w|8 6|, changesets.collect(&:revision) | |
161 |
|
161 | |||
162 | # with_dirpath |
|
162 | # with_dirpath | |
163 | changesets = @repository.latest_changesets('images', nil) |
|
163 | changesets = @repository.latest_changesets('images', nil) | |
164 | assert_equal %w|1 0|, changesets.collect(&:revision) |
|
164 | assert_equal %w|1 0|, changesets.collect(&:revision) | |
165 |
|
165 | |||
166 | path = 'sql_escape/percent%dir' |
|
166 | path = 'sql_escape/percent%dir' | |
167 | changesets = @repository.latest_changesets(path, nil) |
|
167 | changesets = @repository.latest_changesets(path, nil) | |
168 | assert_equal %w|30 13 11 10 9|, changesets.collect(&:revision) |
|
168 | assert_equal %w|30 13 11 10 9|, changesets.collect(&:revision) | |
169 |
|
169 | |||
170 | changesets = @repository.latest_changesets(path, '11') |
|
170 | changesets = @repository.latest_changesets(path, '11') | |
171 | assert_equal %w|11 10 9|, changesets.collect(&:revision) |
|
171 | assert_equal %w|11 10 9|, changesets.collect(&:revision) | |
172 |
|
172 | |||
173 | changesets = @repository.latest_changesets(path, '11', 2) |
|
173 | changesets = @repository.latest_changesets(path, '11', 2) | |
174 | assert_equal %w|11 10|, changesets.collect(&:revision) |
|
174 | assert_equal %w|11 10|, changesets.collect(&:revision) | |
175 |
|
175 | |||
176 | path = 'sql_escape/underscore_dir' |
|
176 | path = 'sql_escape/underscore_dir' | |
177 | changesets = @repository.latest_changesets(path, nil) |
|
177 | changesets = @repository.latest_changesets(path, nil) | |
178 | assert_equal %w|30 13 12 9|, changesets.collect(&:revision) |
|
178 | assert_equal %w|30 13 12 9|, changesets.collect(&:revision) | |
179 |
|
179 | |||
180 | changesets = @repository.latest_changesets(path, '12') |
|
180 | changesets = @repository.latest_changesets(path, '12') | |
181 | assert_equal %w|12 9|, changesets.collect(&:revision) |
|
181 | assert_equal %w|12 9|, changesets.collect(&:revision) | |
182 |
|
182 | |||
183 | changesets = @repository.latest_changesets(path, '12', 1) |
|
183 | changesets = @repository.latest_changesets(path, '12', 1) | |
184 | assert_equal %w|12|, changesets.collect(&:revision) |
|
184 | assert_equal %w|12|, changesets.collect(&:revision) | |
185 |
|
185 | |||
186 | # tag |
|
186 | # tag | |
187 | changesets = @repository.latest_changesets('', 'tag_test.00') |
|
187 | changesets = @repository.latest_changesets('', 'tag_test.00') | |
188 | assert_equal %w|5 4 3 2 1 0|, changesets.collect(&:revision) |
|
188 | assert_equal %w|5 4 3 2 1 0|, changesets.collect(&:revision) | |
189 |
|
189 | |||
190 | changesets = @repository.latest_changesets('', 'tag_test.00', 2) |
|
190 | changesets = @repository.latest_changesets('', 'tag_test.00', 2) | |
191 | assert_equal %w|5 4|, changesets.collect(&:revision) |
|
191 | assert_equal %w|5 4|, changesets.collect(&:revision) | |
192 |
|
192 | |||
193 | changesets = @repository.latest_changesets('sources', 'tag_test.00') |
|
193 | changesets = @repository.latest_changesets('sources', 'tag_test.00') | |
194 | assert_equal %w|4 3 2 1 0|, changesets.collect(&:revision) |
|
194 | assert_equal %w|4 3 2 1 0|, changesets.collect(&:revision) | |
195 |
|
195 | |||
196 | changesets = @repository.latest_changesets('sources', 'tag_test.00', 2) |
|
196 | changesets = @repository.latest_changesets('sources', 'tag_test.00', 2) | |
197 | assert_equal %w|4 3|, changesets.collect(&:revision) |
|
197 | assert_equal %w|4 3|, changesets.collect(&:revision) | |
198 |
|
198 | |||
199 | # named branch |
|
199 | # named branch | |
200 | if @repository.scm.class.client_version_above?([1, 6]) |
|
200 | if @repository.scm.class.client_version_above?([1, 6]) | |
201 | changesets = @repository.latest_changesets('', @branch_char_1) |
|
201 | changesets = @repository.latest_changesets('', @branch_char_1) | |
202 | assert_equal %w|27 26|, changesets.collect(&:revision) |
|
202 | assert_equal %w|27 26|, changesets.collect(&:revision) | |
203 | end |
|
203 | end | |
204 |
|
204 | |||
205 | changesets = @repository.latest_changesets("latin-1-dir/test-#{@char_1}-subdir", @branch_char_1) |
|
205 | changesets = @repository.latest_changesets("latin-1-dir/test-#{@char_1}-subdir", @branch_char_1) | |
206 | assert_equal %w|27|, changesets.collect(&:revision) |
|
206 | assert_equal %w|27|, changesets.collect(&:revision) | |
207 | end |
|
207 | end | |
208 |
|
208 | |||
209 | def test_copied_files |
|
209 | def test_copied_files | |
210 | assert_equal 0, @repository.changesets.count |
|
210 | assert_equal 0, @repository.changesets.count | |
211 | @repository.fetch_changesets |
|
211 | @repository.fetch_changesets | |
212 | @project.reload |
|
212 | @project.reload | |
213 | assert_equal NUM_REV, @repository.changesets.count |
|
213 | assert_equal NUM_REV, @repository.changesets.count | |
214 |
|
214 | |||
215 | cs1 = @repository.changesets.find_by_revision('13') |
|
215 | cs1 = @repository.changesets.find_by_revision('13') | |
216 | assert_not_nil cs1 |
|
216 | assert_not_nil cs1 | |
217 | c1 = cs1.changes.sort_by(&:path) |
|
217 | c1 = cs1.filechanges.sort_by(&:path) | |
218 | assert_equal 2, c1.size |
|
218 | assert_equal 2, c1.size | |
219 |
|
219 | |||
220 | assert_equal 'A', c1[0].action |
|
220 | assert_equal 'A', c1[0].action | |
221 | assert_equal '/sql_escape/percent%dir/percentfile1.txt', c1[0].path |
|
221 | assert_equal '/sql_escape/percent%dir/percentfile1.txt', c1[0].path | |
222 | assert_equal '/sql_escape/percent%dir/percent%file1.txt', c1[0].from_path |
|
222 | assert_equal '/sql_escape/percent%dir/percent%file1.txt', c1[0].from_path | |
223 | assert_equal '3a330eb32958', c1[0].from_revision |
|
223 | assert_equal '3a330eb32958', c1[0].from_revision | |
224 |
|
224 | |||
225 | assert_equal 'A', c1[1].action |
|
225 | assert_equal 'A', c1[1].action | |
226 | assert_equal '/sql_escape/underscore_dir/understrike-file.txt', c1[1].path |
|
226 | assert_equal '/sql_escape/underscore_dir/understrike-file.txt', c1[1].path | |
227 | assert_equal '/sql_escape/underscore_dir/understrike_file.txt', c1[1].from_path |
|
227 | assert_equal '/sql_escape/underscore_dir/understrike_file.txt', c1[1].from_path | |
228 |
|
228 | |||
229 | cs2 = @repository.changesets.find_by_revision('15') |
|
229 | cs2 = @repository.changesets.find_by_revision('15') | |
230 | c2 = cs2.changes |
|
230 | c2 = cs2.filechanges | |
231 | assert_equal 1, c2.size |
|
231 | assert_equal 1, c2.size | |
232 |
|
232 | |||
233 | assert_equal 'A', c2[0].action |
|
233 | assert_equal 'A', c2[0].action | |
234 | assert_equal '/README (1)[2]&,%.-3_4', c2[0].path |
|
234 | assert_equal '/README (1)[2]&,%.-3_4', c2[0].path | |
235 | assert_equal '/README', c2[0].from_path |
|
235 | assert_equal '/README', c2[0].from_path | |
236 | assert_equal '933ca60293d7', c2[0].from_revision |
|
236 | assert_equal '933ca60293d7', c2[0].from_revision | |
237 |
|
237 | |||
238 | cs3 = @repository.changesets.find_by_revision('19') |
|
238 | cs3 = @repository.changesets.find_by_revision('19') | |
239 | c3 = cs3.changes |
|
239 | c3 = cs3.filechanges | |
240 | assert_equal 1, c3.size |
|
240 | assert_equal 1, c3.size | |
241 | assert_equal 'A', c3[0].action |
|
241 | assert_equal 'A', c3[0].action | |
242 | assert_equal "/latin-1-dir/test-#{@char_1}-1.txt", c3[0].path |
|
242 | assert_equal "/latin-1-dir/test-#{@char_1}-1.txt", c3[0].path | |
243 | assert_equal "/latin-1-dir/test-#{@char_1}.txt", c3[0].from_path |
|
243 | assert_equal "/latin-1-dir/test-#{@char_1}.txt", c3[0].from_path | |
244 | assert_equal '5d9891a1b425', c3[0].from_revision |
|
244 | assert_equal '5d9891a1b425', c3[0].from_revision | |
245 | end |
|
245 | end | |
246 |
|
246 | |||
247 | def test_find_changeset_by_name |
|
247 | def test_find_changeset_by_name | |
248 | assert_equal 0, @repository.changesets.count |
|
248 | assert_equal 0, @repository.changesets.count | |
249 | @repository.fetch_changesets |
|
249 | @repository.fetch_changesets | |
250 | @project.reload |
|
250 | @project.reload | |
251 | assert_equal NUM_REV, @repository.changesets.count |
|
251 | assert_equal NUM_REV, @repository.changesets.count | |
252 | %w|2 400bb8672109 400|.each do |r| |
|
252 | %w|2 400bb8672109 400|.each do |r| | |
253 | assert_equal '2', @repository.find_changeset_by_name(r).revision |
|
253 | assert_equal '2', @repository.find_changeset_by_name(r).revision | |
254 | end |
|
254 | end | |
255 | end |
|
255 | end | |
256 |
|
256 | |||
257 | def test_find_changeset_by_invalid_name |
|
257 | def test_find_changeset_by_invalid_name | |
258 | assert_equal 0, @repository.changesets.count |
|
258 | assert_equal 0, @repository.changesets.count | |
259 | @repository.fetch_changesets |
|
259 | @repository.fetch_changesets | |
260 | @project.reload |
|
260 | @project.reload | |
261 | assert_equal NUM_REV, @repository.changesets.count |
|
261 | assert_equal NUM_REV, @repository.changesets.count | |
262 | assert_nil @repository.find_changeset_by_name('100000') |
|
262 | assert_nil @repository.find_changeset_by_name('100000') | |
263 | end |
|
263 | end | |
264 |
|
264 | |||
265 | def test_identifier |
|
265 | def test_identifier | |
266 | assert_equal 0, @repository.changesets.count |
|
266 | assert_equal 0, @repository.changesets.count | |
267 | @repository.fetch_changesets |
|
267 | @repository.fetch_changesets | |
268 | @project.reload |
|
268 | @project.reload | |
269 | assert_equal NUM_REV, @repository.changesets.count |
|
269 | assert_equal NUM_REV, @repository.changesets.count | |
270 | c = @repository.changesets.find_by_revision('2') |
|
270 | c = @repository.changesets.find_by_revision('2') | |
271 | assert_equal c.scmid, c.identifier |
|
271 | assert_equal c.scmid, c.identifier | |
272 | end |
|
272 | end | |
273 |
|
273 | |||
274 | def test_format_identifier |
|
274 | def test_format_identifier | |
275 | assert_equal 0, @repository.changesets.count |
|
275 | assert_equal 0, @repository.changesets.count | |
276 | @repository.fetch_changesets |
|
276 | @repository.fetch_changesets | |
277 | @project.reload |
|
277 | @project.reload | |
278 | assert_equal NUM_REV, @repository.changesets.count |
|
278 | assert_equal NUM_REV, @repository.changesets.count | |
279 | c = @repository.changesets.find_by_revision('2') |
|
279 | c = @repository.changesets.find_by_revision('2') | |
280 | assert_equal '2:400bb8672109', c.format_identifier |
|
280 | assert_equal '2:400bb8672109', c.format_identifier | |
281 | end |
|
281 | end | |
282 |
|
282 | |||
283 | def test_find_changeset_by_empty_name |
|
283 | def test_find_changeset_by_empty_name | |
284 | assert_equal 0, @repository.changesets.count |
|
284 | assert_equal 0, @repository.changesets.count | |
285 | @repository.fetch_changesets |
|
285 | @repository.fetch_changesets | |
286 | @project.reload |
|
286 | @project.reload | |
287 | assert_equal NUM_REV, @repository.changesets.count |
|
287 | assert_equal NUM_REV, @repository.changesets.count | |
288 | ['', ' ', nil].each do |r| |
|
288 | ['', ' ', nil].each do |r| | |
289 | assert_nil @repository.find_changeset_by_name(r) |
|
289 | assert_nil @repository.find_changeset_by_name(r) | |
290 | end |
|
290 | end | |
291 | end |
|
291 | end | |
292 |
|
292 | |||
293 | def test_parents |
|
293 | def test_parents | |
294 | assert_equal 0, @repository.changesets.count |
|
294 | assert_equal 0, @repository.changesets.count | |
295 | @repository.fetch_changesets |
|
295 | @repository.fetch_changesets | |
296 | @project.reload |
|
296 | @project.reload | |
297 | assert_equal NUM_REV, @repository.changesets.count |
|
297 | assert_equal NUM_REV, @repository.changesets.count | |
298 | r1 = @repository.changesets.find_by_revision('0') |
|
298 | r1 = @repository.changesets.find_by_revision('0') | |
299 | assert_equal [], r1.parents |
|
299 | assert_equal [], r1.parents | |
300 | r2 = @repository.changesets.find_by_revision('1') |
|
300 | r2 = @repository.changesets.find_by_revision('1') | |
301 | assert_equal 1, r2.parents.length |
|
301 | assert_equal 1, r2.parents.length | |
302 | assert_equal "0885933ad4f6", |
|
302 | assert_equal "0885933ad4f6", | |
303 | r2.parents[0].identifier |
|
303 | r2.parents[0].identifier | |
304 | r3 = @repository.changesets.find_by_revision('30') |
|
304 | r3 = @repository.changesets.find_by_revision('30') | |
305 | assert_equal 2, r3.parents.length |
|
305 | assert_equal 2, r3.parents.length | |
306 | r4 = [r3.parents[0].identifier, r3.parents[1].identifier].sort |
|
306 | r4 = [r3.parents[0].identifier, r3.parents[1].identifier].sort | |
307 | assert_equal "3a330eb32958", r4[0] |
|
307 | assert_equal "3a330eb32958", r4[0] | |
308 | assert_equal "a94b0528f24f", r4[1] |
|
308 | assert_equal "a94b0528f24f", r4[1] | |
309 | end |
|
309 | end | |
310 |
|
310 | |||
311 | def test_activities |
|
311 | def test_activities | |
312 | c = Changeset.new(:repository => @repository, |
|
312 | c = Changeset.new(:repository => @repository, | |
313 | :committed_on => Time.now, |
|
313 | :committed_on => Time.now, | |
314 | :revision => '123', |
|
314 | :revision => '123', | |
315 | :scmid => 'abc400bb8672', |
|
315 | :scmid => 'abc400bb8672', | |
316 | :comments => 'test') |
|
316 | :comments => 'test') | |
317 | assert c.event_title.include?('123:abc400bb8672:') |
|
317 | assert c.event_title.include?('123:abc400bb8672:') | |
318 | assert_equal 'abc400bb8672', c.event_url[:rev] |
|
318 | assert_equal 'abc400bb8672', c.event_url[:rev] | |
319 | end |
|
319 | end | |
320 |
|
320 | |||
321 | def test_previous |
|
321 | def test_previous | |
322 | assert_equal 0, @repository.changesets.count |
|
322 | assert_equal 0, @repository.changesets.count | |
323 | @repository.fetch_changesets |
|
323 | @repository.fetch_changesets | |
324 | @project.reload |
|
324 | @project.reload | |
325 | assert_equal NUM_REV, @repository.changesets.count |
|
325 | assert_equal NUM_REV, @repository.changesets.count | |
326 | %w|28 3ae45e2d177d 3ae45|.each do |r1| |
|
326 | %w|28 3ae45e2d177d 3ae45|.each do |r1| | |
327 | changeset = @repository.find_changeset_by_name(r1) |
|
327 | changeset = @repository.find_changeset_by_name(r1) | |
328 | %w|27 7bbf4c738e71 7bbf|.each do |r2| |
|
328 | %w|27 7bbf4c738e71 7bbf|.each do |r2| | |
329 | assert_equal @repository.find_changeset_by_name(r2), changeset.previous |
|
329 | assert_equal @repository.find_changeset_by_name(r2), changeset.previous | |
330 | end |
|
330 | end | |
331 | end |
|
331 | end | |
332 | end |
|
332 | end | |
333 |
|
333 | |||
334 | def test_previous_nil |
|
334 | def test_previous_nil | |
335 | assert_equal 0, @repository.changesets.count |
|
335 | assert_equal 0, @repository.changesets.count | |
336 | @repository.fetch_changesets |
|
336 | @repository.fetch_changesets | |
337 | @project.reload |
|
337 | @project.reload | |
338 | assert_equal NUM_REV, @repository.changesets.count |
|
338 | assert_equal NUM_REV, @repository.changesets.count | |
339 | %w|0 0885933ad4f6 0885|.each do |r1| |
|
339 | %w|0 0885933ad4f6 0885|.each do |r1| | |
340 | changeset = @repository.find_changeset_by_name(r1) |
|
340 | changeset = @repository.find_changeset_by_name(r1) | |
341 | assert_nil changeset.previous |
|
341 | assert_nil changeset.previous | |
342 | end |
|
342 | end | |
343 | end |
|
343 | end | |
344 |
|
344 | |||
345 | def test_next |
|
345 | def test_next | |
346 | assert_equal 0, @repository.changesets.count |
|
346 | assert_equal 0, @repository.changesets.count | |
347 | @repository.fetch_changesets |
|
347 | @repository.fetch_changesets | |
348 | @project.reload |
|
348 | @project.reload | |
349 | assert_equal NUM_REV, @repository.changesets.count |
|
349 | assert_equal NUM_REV, @repository.changesets.count | |
350 | %w|27 7bbf4c738e71 7bbf|.each do |r2| |
|
350 | %w|27 7bbf4c738e71 7bbf|.each do |r2| | |
351 | changeset = @repository.find_changeset_by_name(r2) |
|
351 | changeset = @repository.find_changeset_by_name(r2) | |
352 | %w|28 3ae45e2d177d 3ae45|.each do |r1| |
|
352 | %w|28 3ae45e2d177d 3ae45|.each do |r1| | |
353 | assert_equal @repository.find_changeset_by_name(r1), changeset.next |
|
353 | assert_equal @repository.find_changeset_by_name(r1), changeset.next | |
354 | end |
|
354 | end | |
355 | end |
|
355 | end | |
356 | end |
|
356 | end | |
357 |
|
357 | |||
358 | def test_next_nil |
|
358 | def test_next_nil | |
359 | assert_equal 0, @repository.changesets.count |
|
359 | assert_equal 0, @repository.changesets.count | |
360 | @repository.fetch_changesets |
|
360 | @repository.fetch_changesets | |
361 | @project.reload |
|
361 | @project.reload | |
362 | assert_equal NUM_REV, @repository.changesets.count |
|
362 | assert_equal NUM_REV, @repository.changesets.count | |
363 | %w|31 31eeee7395c8 31eee|.each do |r1| |
|
363 | %w|31 31eeee7395c8 31eee|.each do |r1| | |
364 | changeset = @repository.find_changeset_by_name(r1) |
|
364 | changeset = @repository.find_changeset_by_name(r1) | |
365 | assert_nil changeset.next |
|
365 | assert_nil changeset.next | |
366 | end |
|
366 | end | |
367 | end |
|
367 | end | |
368 | else |
|
368 | else | |
369 | puts "Mercurial test repository NOT FOUND. Skipping unit tests !!!" |
|
369 | puts "Mercurial test repository NOT FOUND. Skipping unit tests !!!" | |
370 | def test_fake; assert true end |
|
370 | def test_fake; assert true end | |
371 | end |
|
371 | end | |
372 | end |
|
372 | end |
@@ -1,221 +1,221 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2012 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 File.expand_path('../../test_helper', __FILE__) |
|
18 | require File.expand_path('../../test_helper', __FILE__) | |
19 |
|
19 | |||
20 | class RepositorySubversionTest < ActiveSupport::TestCase |
|
20 | class RepositorySubversionTest < ActiveSupport::TestCase | |
21 | fixtures :projects, :repositories, :enabled_modules, :users, :roles |
|
21 | fixtures :projects, :repositories, :enabled_modules, :users, :roles | |
22 |
|
22 | |||
23 | NUM_REV = 11 |
|
23 | NUM_REV = 11 | |
24 |
|
24 | |||
25 | def setup |
|
25 | def setup | |
26 | @project = Project.find(3) |
|
26 | @project = Project.find(3) | |
27 | @repository = Repository::Subversion.create(:project => @project, |
|
27 | @repository = Repository::Subversion.create(:project => @project, | |
28 | :url => self.class.subversion_repository_url) |
|
28 | :url => self.class.subversion_repository_url) | |
29 | assert @repository |
|
29 | assert @repository | |
30 | end |
|
30 | end | |
31 |
|
31 | |||
32 | if repository_configured?('subversion') |
|
32 | if repository_configured?('subversion') | |
33 | def test_fetch_changesets_from_scratch |
|
33 | def test_fetch_changesets_from_scratch | |
34 | assert_equal 0, @repository.changesets.count |
|
34 | assert_equal 0, @repository.changesets.count | |
35 | @repository.fetch_changesets |
|
35 | @repository.fetch_changesets | |
36 | @project.reload |
|
36 | @project.reload | |
37 |
|
37 | |||
38 | assert_equal NUM_REV, @repository.changesets.count |
|
38 | assert_equal NUM_REV, @repository.changesets.count | |
39 | assert_equal 20, @repository.changes.count |
|
39 | assert_equal 20, @repository.filechanges.count | |
40 | assert_equal 'Initial import.', @repository.changesets.find_by_revision('1').comments |
|
40 | assert_equal 'Initial import.', @repository.changesets.find_by_revision('1').comments | |
41 | end |
|
41 | end | |
42 |
|
42 | |||
43 | def test_fetch_changesets_incremental |
|
43 | def test_fetch_changesets_incremental | |
44 | assert_equal 0, @repository.changesets.count |
|
44 | assert_equal 0, @repository.changesets.count | |
45 | @repository.fetch_changesets |
|
45 | @repository.fetch_changesets | |
46 | @project.reload |
|
46 | @project.reload | |
47 | assert_equal NUM_REV, @repository.changesets.count |
|
47 | assert_equal NUM_REV, @repository.changesets.count | |
48 |
|
48 | |||
49 | # Remove changesets with revision > 5 |
|
49 | # Remove changesets with revision > 5 | |
50 | @repository.changesets.find(:all).each {|c| c.destroy if c.revision.to_i > 5} |
|
50 | @repository.changesets.find(:all).each {|c| c.destroy if c.revision.to_i > 5} | |
51 | @project.reload |
|
51 | @project.reload | |
52 | assert_equal 5, @repository.changesets.count |
|
52 | assert_equal 5, @repository.changesets.count | |
53 |
|
53 | |||
54 | @repository.fetch_changesets |
|
54 | @repository.fetch_changesets | |
55 | @project.reload |
|
55 | @project.reload | |
56 | assert_equal NUM_REV, @repository.changesets.count |
|
56 | assert_equal NUM_REV, @repository.changesets.count | |
57 | end |
|
57 | end | |
58 |
|
58 | |||
59 | def test_latest_changesets |
|
59 | def test_latest_changesets | |
60 | assert_equal 0, @repository.changesets.count |
|
60 | assert_equal 0, @repository.changesets.count | |
61 | @repository.fetch_changesets |
|
61 | @repository.fetch_changesets | |
62 | @project.reload |
|
62 | @project.reload | |
63 | assert_equal NUM_REV, @repository.changesets.count |
|
63 | assert_equal NUM_REV, @repository.changesets.count | |
64 |
|
64 | |||
65 | # with limit |
|
65 | # with limit | |
66 | changesets = @repository.latest_changesets('', nil, 2) |
|
66 | changesets = @repository.latest_changesets('', nil, 2) | |
67 | assert_equal 2, changesets.size |
|
67 | assert_equal 2, changesets.size | |
68 | assert_equal @repository.latest_changesets('', nil).slice(0,2), changesets |
|
68 | assert_equal @repository.latest_changesets('', nil).slice(0,2), changesets | |
69 |
|
69 | |||
70 | # with path |
|
70 | # with path | |
71 | changesets = @repository.latest_changesets('subversion_test/folder', nil) |
|
71 | changesets = @repository.latest_changesets('subversion_test/folder', nil) | |
72 | assert_equal ["10", "9", "7", "6", "5", "2"], changesets.collect(&:revision) |
|
72 | assert_equal ["10", "9", "7", "6", "5", "2"], changesets.collect(&:revision) | |
73 |
|
73 | |||
74 | # with path and revision |
|
74 | # with path and revision | |
75 | changesets = @repository.latest_changesets('subversion_test/folder', 8) |
|
75 | changesets = @repository.latest_changesets('subversion_test/folder', 8) | |
76 | assert_equal ["7", "6", "5", "2"], changesets.collect(&:revision) |
|
76 | assert_equal ["7", "6", "5", "2"], changesets.collect(&:revision) | |
77 | end |
|
77 | end | |
78 |
|
78 | |||
79 | def test_directory_listing_with_square_brackets_in_path |
|
79 | def test_directory_listing_with_square_brackets_in_path | |
80 | assert_equal 0, @repository.changesets.count |
|
80 | assert_equal 0, @repository.changesets.count | |
81 | @repository.fetch_changesets |
|
81 | @repository.fetch_changesets | |
82 | @project.reload |
|
82 | @project.reload | |
83 | assert_equal NUM_REV, @repository.changesets.count |
|
83 | assert_equal NUM_REV, @repository.changesets.count | |
84 |
|
84 | |||
85 | entries = @repository.entries('subversion_test/[folder_with_brackets]') |
|
85 | entries = @repository.entries('subversion_test/[folder_with_brackets]') | |
86 | assert_not_nil entries, 'Expect to find entries in folder_with_brackets' |
|
86 | assert_not_nil entries, 'Expect to find entries in folder_with_brackets' | |
87 | assert_equal 1, entries.size, 'Expect one entry in folder_with_brackets' |
|
87 | assert_equal 1, entries.size, 'Expect one entry in folder_with_brackets' | |
88 | assert_equal 'README.txt', entries.first.name |
|
88 | assert_equal 'README.txt', entries.first.name | |
89 | end |
|
89 | end | |
90 |
|
90 | |||
91 | def test_directory_listing_with_square_brackets_in_base |
|
91 | def test_directory_listing_with_square_brackets_in_base | |
92 | @project = Project.find(3) |
|
92 | @project = Project.find(3) | |
93 | @repository = Repository::Subversion.create( |
|
93 | @repository = Repository::Subversion.create( | |
94 | :project => @project, |
|
94 | :project => @project, | |
95 | :url => "file:///#{self.class.repository_path('subversion')}/subversion_test/[folder_with_brackets]") |
|
95 | :url => "file:///#{self.class.repository_path('subversion')}/subversion_test/[folder_with_brackets]") | |
96 |
|
96 | |||
97 | assert_equal 0, @repository.changesets.count |
|
97 | assert_equal 0, @repository.changesets.count | |
98 | @repository.fetch_changesets |
|
98 | @repository.fetch_changesets | |
99 | @project.reload |
|
99 | @project.reload | |
100 |
|
100 | |||
101 | assert_equal 1, @repository.changesets.count, 'Expected to see 1 revision' |
|
101 | assert_equal 1, @repository.changesets.count, 'Expected to see 1 revision' | |
102 | assert_equal 2, @repository.changes.count, 'Expected to see 2 changes, dir add and file add' |
|
102 | assert_equal 2, @repository.filechanges.count, 'Expected to see 2 changes, dir add and file add' | |
103 |
|
103 | |||
104 | entries = @repository.entries('') |
|
104 | entries = @repository.entries('') | |
105 | assert_not_nil entries, 'Expect to find entries' |
|
105 | assert_not_nil entries, 'Expect to find entries' | |
106 | assert_equal 1, entries.size, 'Expect a single entry' |
|
106 | assert_equal 1, entries.size, 'Expect a single entry' | |
107 | assert_equal 'README.txt', entries.first.name |
|
107 | assert_equal 'README.txt', entries.first.name | |
108 | end |
|
108 | end | |
109 |
|
109 | |||
110 | def test_identifier |
|
110 | def test_identifier | |
111 | assert_equal 0, @repository.changesets.count |
|
111 | assert_equal 0, @repository.changesets.count | |
112 | @repository.fetch_changesets |
|
112 | @repository.fetch_changesets | |
113 | @project.reload |
|
113 | @project.reload | |
114 | assert_equal NUM_REV, @repository.changesets.count |
|
114 | assert_equal NUM_REV, @repository.changesets.count | |
115 | c = @repository.changesets.find_by_revision('1') |
|
115 | c = @repository.changesets.find_by_revision('1') | |
116 | assert_equal c.revision, c.identifier |
|
116 | assert_equal c.revision, c.identifier | |
117 | end |
|
117 | end | |
118 |
|
118 | |||
119 | def test_find_changeset_by_empty_name |
|
119 | def test_find_changeset_by_empty_name | |
120 | assert_equal 0, @repository.changesets.count |
|
120 | assert_equal 0, @repository.changesets.count | |
121 | @repository.fetch_changesets |
|
121 | @repository.fetch_changesets | |
122 | @project.reload |
|
122 | @project.reload | |
123 | assert_equal NUM_REV, @repository.changesets.count |
|
123 | assert_equal NUM_REV, @repository.changesets.count | |
124 | ['', ' ', nil].each do |r| |
|
124 | ['', ' ', nil].each do |r| | |
125 | assert_nil @repository.find_changeset_by_name(r) |
|
125 | assert_nil @repository.find_changeset_by_name(r) | |
126 | end |
|
126 | end | |
127 | end |
|
127 | end | |
128 |
|
128 | |||
129 | def test_identifier_nine_digit |
|
129 | def test_identifier_nine_digit | |
130 | c = Changeset.new(:repository => @repository, :committed_on => Time.now, |
|
130 | c = Changeset.new(:repository => @repository, :committed_on => Time.now, | |
131 | :revision => '123456789', :comments => 'test') |
|
131 | :revision => '123456789', :comments => 'test') | |
132 | assert_equal c.identifier, c.revision |
|
132 | assert_equal c.identifier, c.revision | |
133 | end |
|
133 | end | |
134 |
|
134 | |||
135 | def test_format_identifier |
|
135 | def test_format_identifier | |
136 | assert_equal 0, @repository.changesets.count |
|
136 | assert_equal 0, @repository.changesets.count | |
137 | @repository.fetch_changesets |
|
137 | @repository.fetch_changesets | |
138 | @project.reload |
|
138 | @project.reload | |
139 | assert_equal NUM_REV, @repository.changesets.count |
|
139 | assert_equal NUM_REV, @repository.changesets.count | |
140 | c = @repository.changesets.find_by_revision('1') |
|
140 | c = @repository.changesets.find_by_revision('1') | |
141 | assert_equal c.format_identifier, c.revision |
|
141 | assert_equal c.format_identifier, c.revision | |
142 | end |
|
142 | end | |
143 |
|
143 | |||
144 | def test_format_identifier_nine_digit |
|
144 | def test_format_identifier_nine_digit | |
145 | c = Changeset.new(:repository => @repository, :committed_on => Time.now, |
|
145 | c = Changeset.new(:repository => @repository, :committed_on => Time.now, | |
146 | :revision => '123456789', :comments => 'test') |
|
146 | :revision => '123456789', :comments => 'test') | |
147 | assert_equal c.format_identifier, c.revision |
|
147 | assert_equal c.format_identifier, c.revision | |
148 | end |
|
148 | end | |
149 |
|
149 | |||
150 | def test_activities |
|
150 | def test_activities | |
151 | c = Changeset.new(:repository => @repository, :committed_on => Time.now, |
|
151 | c = Changeset.new(:repository => @repository, :committed_on => Time.now, | |
152 | :revision => '1', :comments => 'test') |
|
152 | :revision => '1', :comments => 'test') | |
153 | assert c.event_title.include?('1:') |
|
153 | assert c.event_title.include?('1:') | |
154 | assert_equal '1', c.event_url[:rev] |
|
154 | assert_equal '1', c.event_url[:rev] | |
155 | end |
|
155 | end | |
156 |
|
156 | |||
157 | def test_activities_nine_digit |
|
157 | def test_activities_nine_digit | |
158 | c = Changeset.new(:repository => @repository, :committed_on => Time.now, |
|
158 | c = Changeset.new(:repository => @repository, :committed_on => Time.now, | |
159 | :revision => '123456789', :comments => 'test') |
|
159 | :revision => '123456789', :comments => 'test') | |
160 | assert c.event_title.include?('123456789:') |
|
160 | assert c.event_title.include?('123456789:') | |
161 | assert_equal '123456789', c.event_url[:rev] |
|
161 | assert_equal '123456789', c.event_url[:rev] | |
162 | end |
|
162 | end | |
163 |
|
163 | |||
164 | def test_log_encoding_ignore_setting |
|
164 | def test_log_encoding_ignore_setting | |
165 | with_settings :commit_logs_encoding => 'windows-1252' do |
|
165 | with_settings :commit_logs_encoding => 'windows-1252' do | |
166 | s1 = "\xC2\x80" |
|
166 | s1 = "\xC2\x80" | |
167 | s2 = "\xc3\x82\xc2\x80" |
|
167 | s2 = "\xc3\x82\xc2\x80" | |
168 | if s1.respond_to?(:force_encoding) |
|
168 | if s1.respond_to?(:force_encoding) | |
169 | s1.force_encoding('ISO-8859-1') |
|
169 | s1.force_encoding('ISO-8859-1') | |
170 | s2.force_encoding('UTF-8') |
|
170 | s2.force_encoding('UTF-8') | |
171 | assert_equal s1.encode('UTF-8'), s2 |
|
171 | assert_equal s1.encode('UTF-8'), s2 | |
172 | end |
|
172 | end | |
173 | c = Changeset.new(:repository => @repository, |
|
173 | c = Changeset.new(:repository => @repository, | |
174 | :comments => s2, |
|
174 | :comments => s2, | |
175 | :revision => '123', |
|
175 | :revision => '123', | |
176 | :committed_on => Time.now) |
|
176 | :committed_on => Time.now) | |
177 | assert c.save |
|
177 | assert c.save | |
178 | assert_equal s2, c.comments |
|
178 | assert_equal s2, c.comments | |
179 | end |
|
179 | end | |
180 | end |
|
180 | end | |
181 |
|
181 | |||
182 | def test_previous |
|
182 | def test_previous | |
183 | assert_equal 0, @repository.changesets.count |
|
183 | assert_equal 0, @repository.changesets.count | |
184 | @repository.fetch_changesets |
|
184 | @repository.fetch_changesets | |
185 | @project.reload |
|
185 | @project.reload | |
186 | assert_equal NUM_REV, @repository.changesets.count |
|
186 | assert_equal NUM_REV, @repository.changesets.count | |
187 | changeset = @repository.find_changeset_by_name('3') |
|
187 | changeset = @repository.find_changeset_by_name('3') | |
188 | assert_equal @repository.find_changeset_by_name('2'), changeset.previous |
|
188 | assert_equal @repository.find_changeset_by_name('2'), changeset.previous | |
189 | end |
|
189 | end | |
190 |
|
190 | |||
191 | def test_previous_nil |
|
191 | def test_previous_nil | |
192 | assert_equal 0, @repository.changesets.count |
|
192 | assert_equal 0, @repository.changesets.count | |
193 | @repository.fetch_changesets |
|
193 | @repository.fetch_changesets | |
194 | @project.reload |
|
194 | @project.reload | |
195 | assert_equal NUM_REV, @repository.changesets.count |
|
195 | assert_equal NUM_REV, @repository.changesets.count | |
196 | changeset = @repository.find_changeset_by_name('1') |
|
196 | changeset = @repository.find_changeset_by_name('1') | |
197 | assert_nil changeset.previous |
|
197 | assert_nil changeset.previous | |
198 | end |
|
198 | end | |
199 |
|
199 | |||
200 | def test_next |
|
200 | def test_next | |
201 | assert_equal 0, @repository.changesets.count |
|
201 | assert_equal 0, @repository.changesets.count | |
202 | @repository.fetch_changesets |
|
202 | @repository.fetch_changesets | |
203 | @project.reload |
|
203 | @project.reload | |
204 | assert_equal NUM_REV, @repository.changesets.count |
|
204 | assert_equal NUM_REV, @repository.changesets.count | |
205 | changeset = @repository.find_changeset_by_name('2') |
|
205 | changeset = @repository.find_changeset_by_name('2') | |
206 | assert_equal @repository.find_changeset_by_name('3'), changeset.next |
|
206 | assert_equal @repository.find_changeset_by_name('3'), changeset.next | |
207 | end |
|
207 | end | |
208 |
|
208 | |||
209 | def test_next_nil |
|
209 | def test_next_nil | |
210 | assert_equal 0, @repository.changesets.count |
|
210 | assert_equal 0, @repository.changesets.count | |
211 | @repository.fetch_changesets |
|
211 | @repository.fetch_changesets | |
212 | @project.reload |
|
212 | @project.reload | |
213 | assert_equal NUM_REV, @repository.changesets.count |
|
213 | assert_equal NUM_REV, @repository.changesets.count | |
214 | changeset = @repository.find_changeset_by_name('11') |
|
214 | changeset = @repository.find_changeset_by_name('11') | |
215 | assert_nil changeset.next |
|
215 | assert_nil changeset.next | |
216 | end |
|
216 | end | |
217 | else |
|
217 | else | |
218 | puts "Subversion test repository NOT FOUND. Skipping unit tests !!!" |
|
218 | puts "Subversion test repository NOT FOUND. Skipping unit tests !!!" | |
219 | def test_fake; assert true end |
|
219 | def test_fake; assert true end | |
220 | end |
|
220 | end | |
221 | end |
|
221 | end |
@@ -1,329 +1,331 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2012 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 File.expand_path('../../test_helper', __FILE__) |
|
18 | require File.expand_path('../../test_helper', __FILE__) | |
19 |
|
19 | |||
20 | class RepositoryTest < ActiveSupport::TestCase |
|
20 | class RepositoryTest < ActiveSupport::TestCase | |
21 | fixtures :projects, |
|
21 | fixtures :projects, | |
22 | :trackers, |
|
22 | :trackers, | |
23 | :projects_trackers, |
|
23 | :projects_trackers, | |
24 | :enabled_modules, |
|
24 | :enabled_modules, | |
25 | :repositories, |
|
25 | :repositories, | |
26 | :issues, |
|
26 | :issues, | |
27 | :issue_statuses, |
|
27 | :issue_statuses, | |
28 | :issue_categories, |
|
28 | :issue_categories, | |
29 | :changesets, |
|
29 | :changesets, | |
30 | :changes, |
|
30 | :changes, | |
31 | :users, |
|
31 | :users, | |
32 | :members, |
|
32 | :members, | |
33 | :member_roles, |
|
33 | :member_roles, | |
34 | :roles, |
|
34 | :roles, | |
35 | :enumerations |
|
35 | :enumerations | |
36 |
|
36 | |||
37 | include Redmine::I18n |
|
37 | include Redmine::I18n | |
38 |
|
38 | |||
39 | def setup |
|
39 | def setup | |
40 | @repository = Project.find(1).repository |
|
40 | @repository = Project.find(1).repository | |
41 | end |
|
41 | end | |
42 |
|
42 | |||
43 | def test_blank_log_encoding_error_message |
|
43 | def test_blank_log_encoding_error_message | |
44 | set_language_if_valid 'en' |
|
44 | set_language_if_valid 'en' | |
45 | repo = Repository::Bazaar.new( |
|
45 | repo = Repository::Bazaar.new( | |
46 | :project => Project.find(3), |
|
46 | :project => Project.find(3), | |
47 | :url => "/test", |
|
47 | :url => "/test", | |
48 | :log_encoding => '' |
|
48 | :log_encoding => '' | |
49 | ) |
|
49 | ) | |
50 | assert !repo.save |
|
50 | assert !repo.save | |
51 | assert_include "Commit messages encoding can't be blank", |
|
51 | assert_include "Commit messages encoding can't be blank", | |
52 | repo.errors.full_messages |
|
52 | repo.errors.full_messages | |
53 | end |
|
53 | end | |
54 |
|
54 | |||
55 | def test_blank_log_encoding_error_message_fr |
|
55 | def test_blank_log_encoding_error_message_fr | |
56 | set_language_if_valid 'fr' |
|
56 | set_language_if_valid 'fr' | |
57 | str = "Encodage des messages de commit doit \xc3\xaatre renseign\xc3\xa9(e)" |
|
57 | str = "Encodage des messages de commit doit \xc3\xaatre renseign\xc3\xa9(e)" | |
58 | str.force_encoding('UTF-8') if str.respond_to?(:force_encoding) |
|
58 | str.force_encoding('UTF-8') if str.respond_to?(:force_encoding) | |
59 | repo = Repository::Bazaar.new( |
|
59 | repo = Repository::Bazaar.new( | |
60 | :project => Project.find(3), |
|
60 | :project => Project.find(3), | |
61 | :url => "/test" |
|
61 | :url => "/test" | |
62 | ) |
|
62 | ) | |
63 | assert !repo.save |
|
63 | assert !repo.save | |
64 | assert_include str, repo.errors.full_messages |
|
64 | assert_include str, repo.errors.full_messages | |
65 | end |
|
65 | end | |
66 |
|
66 | |||
67 | def test_create |
|
67 | def test_create | |
68 | repository = Repository::Subversion.new(:project => Project.find(3)) |
|
68 | repository = Repository::Subversion.new(:project => Project.find(3)) | |
69 | assert !repository.save |
|
69 | assert !repository.save | |
70 |
|
70 | |||
71 | repository.url = "svn://localhost" |
|
71 | repository.url = "svn://localhost" | |
72 | assert repository.save |
|
72 | assert repository.save | |
73 | repository.reload |
|
73 | repository.reload | |
74 |
|
74 | |||
75 | project = Project.find(3) |
|
75 | project = Project.find(3) | |
76 | assert_equal repository, project.repository |
|
76 | assert_equal repository, project.repository | |
77 | end |
|
77 | end | |
78 |
|
78 | |||
79 | def test_first_repository_should_be_set_as_default |
|
79 | def test_first_repository_should_be_set_as_default | |
80 | repository1 = Repository::Subversion.new( |
|
80 | repository1 = Repository::Subversion.new( | |
81 | :project => Project.find(3), |
|
81 | :project => Project.find(3), | |
82 | :identifier => 'svn1', |
|
82 | :identifier => 'svn1', | |
83 | :url => 'file:///svn1' |
|
83 | :url => 'file:///svn1' | |
84 | ) |
|
84 | ) | |
85 | assert repository1.save |
|
85 | assert repository1.save | |
86 | assert repository1.is_default? |
|
86 | assert repository1.is_default? | |
87 |
|
87 | |||
88 | repository2 = Repository::Subversion.new( |
|
88 | repository2 = Repository::Subversion.new( | |
89 | :project => Project.find(3), |
|
89 | :project => Project.find(3), | |
90 | :identifier => 'svn2', |
|
90 | :identifier => 'svn2', | |
91 | :url => 'file:///svn2' |
|
91 | :url => 'file:///svn2' | |
92 | ) |
|
92 | ) | |
93 | assert repository2.save |
|
93 | assert repository2.save | |
94 | assert !repository2.is_default? |
|
94 | assert !repository2.is_default? | |
95 |
|
95 | |||
96 | assert_equal repository1, Project.find(3).repository |
|
96 | assert_equal repository1, Project.find(3).repository | |
97 | assert_equal [repository1, repository2], Project.find(3).repositories.sort |
|
97 | assert_equal [repository1, repository2], Project.find(3).repositories.sort | |
98 | end |
|
98 | end | |
99 |
|
99 | |||
100 | def test_destroy |
|
100 | def test_destroy | |
101 | changesets = Changeset.where("repository_id = 10").all.count |
|
101 | repository = Repository.find(10) | |
102 | changes = Changeset.joins([:changes]).where("repository_id = 10").all.count |
|
102 | changesets = repository.changesets.count | |
|
103 | changes = repository.filechanges.count | |||
|
104 | ||||
103 | assert_difference 'Changeset.count', -changesets do |
|
105 | assert_difference 'Changeset.count', -changesets do | |
104 | assert_difference 'Change.count', -changes do |
|
106 | assert_difference 'Change.count', -changes do | |
105 | Repository.find(10).destroy |
|
107 | Repository.find(10).destroy | |
106 | end |
|
108 | end | |
107 | end |
|
109 | end | |
108 | end |
|
110 | end | |
109 |
|
111 | |||
110 | def test_destroy_should_delete_parents_associations |
|
112 | def test_destroy_should_delete_parents_associations | |
111 | changeset = Changeset.find(102) |
|
113 | changeset = Changeset.find(102) | |
112 | changeset.parents = Changeset.find_all_by_id([100, 101]) |
|
114 | changeset.parents = Changeset.find_all_by_id([100, 101]) | |
113 |
|
115 | |||
114 | assert_difference 'Changeset.connection.select_all("select * from changeset_parents").size', -2 do |
|
116 | assert_difference 'Changeset.connection.select_all("select * from changeset_parents").size', -2 do | |
115 | Repository.find(10).destroy |
|
117 | Repository.find(10).destroy | |
116 | end |
|
118 | end | |
117 | end |
|
119 | end | |
118 |
|
120 | |||
119 | def test_destroy_should_delete_issues_associations |
|
121 | def test_destroy_should_delete_issues_associations | |
120 | changeset = Changeset.find(102) |
|
122 | changeset = Changeset.find(102) | |
121 | changeset.issues = Issue.find_all_by_id([1, 2]) |
|
123 | changeset.issues = Issue.find_all_by_id([1, 2]) | |
122 |
|
124 | |||
123 | assert_difference 'Changeset.connection.select_all("select * from changesets_issues").size', -2 do |
|
125 | assert_difference 'Changeset.connection.select_all("select * from changesets_issues").size', -2 do | |
124 | Repository.find(10).destroy |
|
126 | Repository.find(10).destroy | |
125 | end |
|
127 | end | |
126 | end |
|
128 | end | |
127 |
|
129 | |||
128 | def test_should_not_create_with_disabled_scm |
|
130 | def test_should_not_create_with_disabled_scm | |
129 | # disable Subversion |
|
131 | # disable Subversion | |
130 | with_settings :enabled_scm => ['Darcs', 'Git'] do |
|
132 | with_settings :enabled_scm => ['Darcs', 'Git'] do | |
131 | repository = Repository::Subversion.new( |
|
133 | repository = Repository::Subversion.new( | |
132 | :project => Project.find(3), :url => "svn://localhost") |
|
134 | :project => Project.find(3), :url => "svn://localhost") | |
133 | assert !repository.save |
|
135 | assert !repository.save | |
134 | assert_include I18n.translate('activerecord.errors.messages.invalid'), |
|
136 | assert_include I18n.translate('activerecord.errors.messages.invalid'), | |
135 | repository.errors[:type] |
|
137 | repository.errors[:type] | |
136 | end |
|
138 | end | |
137 | end |
|
139 | end | |
138 |
|
140 | |||
139 | def test_scan_changesets_for_issue_ids |
|
141 | def test_scan_changesets_for_issue_ids | |
140 | Setting.default_language = 'en' |
|
142 | Setting.default_language = 'en' | |
141 | Setting.notified_events = ['issue_added','issue_updated'] |
|
143 | Setting.notified_events = ['issue_added','issue_updated'] | |
142 |
|
144 | |||
143 | # choosing a status to apply to fix issues |
|
145 | # choosing a status to apply to fix issues | |
144 | Setting.commit_fix_status_id = IssueStatus.find( |
|
146 | Setting.commit_fix_status_id = IssueStatus.find( | |
145 | :first, |
|
147 | :first, | |
146 | :conditions => ["is_closed = ?", true]).id |
|
148 | :conditions => ["is_closed = ?", true]).id | |
147 | Setting.commit_fix_done_ratio = "90" |
|
149 | Setting.commit_fix_done_ratio = "90" | |
148 | Setting.commit_ref_keywords = 'refs , references, IssueID' |
|
150 | Setting.commit_ref_keywords = 'refs , references, IssueID' | |
149 | Setting.commit_fix_keywords = 'fixes , closes' |
|
151 | Setting.commit_fix_keywords = 'fixes , closes' | |
150 | Setting.default_language = 'en' |
|
152 | Setting.default_language = 'en' | |
151 | ActionMailer::Base.deliveries.clear |
|
153 | ActionMailer::Base.deliveries.clear | |
152 |
|
154 | |||
153 | # make sure issue 1 is not already closed |
|
155 | # make sure issue 1 is not already closed | |
154 | fixed_issue = Issue.find(1) |
|
156 | fixed_issue = Issue.find(1) | |
155 | assert !fixed_issue.status.is_closed? |
|
157 | assert !fixed_issue.status.is_closed? | |
156 | old_status = fixed_issue.status |
|
158 | old_status = fixed_issue.status | |
157 |
|
159 | |||
158 | Repository.scan_changesets_for_issue_ids |
|
160 | Repository.scan_changesets_for_issue_ids | |
159 | assert_equal [101, 102], Issue.find(3).changeset_ids |
|
161 | assert_equal [101, 102], Issue.find(3).changeset_ids | |
160 |
|
162 | |||
161 | # fixed issues |
|
163 | # fixed issues | |
162 | fixed_issue.reload |
|
164 | fixed_issue.reload | |
163 | assert fixed_issue.status.is_closed? |
|
165 | assert fixed_issue.status.is_closed? | |
164 | assert_equal 90, fixed_issue.done_ratio |
|
166 | assert_equal 90, fixed_issue.done_ratio | |
165 | assert_equal [101], fixed_issue.changeset_ids |
|
167 | assert_equal [101], fixed_issue.changeset_ids | |
166 |
|
168 | |||
167 | # issue change |
|
169 | # issue change | |
168 | journal = fixed_issue.journals.find(:first, :order => 'created_on desc') |
|
170 | journal = fixed_issue.journals.find(:first, :order => 'created_on desc') | |
169 | assert_equal User.find_by_login('dlopper'), journal.user |
|
171 | assert_equal User.find_by_login('dlopper'), journal.user | |
170 | assert_equal 'Applied in changeset r2.', journal.notes |
|
172 | assert_equal 'Applied in changeset r2.', journal.notes | |
171 |
|
173 | |||
172 | # 2 email notifications |
|
174 | # 2 email notifications | |
173 | assert_equal 2, ActionMailer::Base.deliveries.size |
|
175 | assert_equal 2, ActionMailer::Base.deliveries.size | |
174 | mail = ActionMailer::Base.deliveries.first |
|
176 | mail = ActionMailer::Base.deliveries.first | |
175 | assert_not_nil mail |
|
177 | assert_not_nil mail | |
176 | assert mail.subject.starts_with?( |
|
178 | assert mail.subject.starts_with?( | |
177 | "[#{fixed_issue.project.name} - #{fixed_issue.tracker.name} ##{fixed_issue.id}]") |
|
179 | "[#{fixed_issue.project.name} - #{fixed_issue.tracker.name} ##{fixed_issue.id}]") | |
178 | assert_mail_body_match( |
|
180 | assert_mail_body_match( | |
179 | "Status changed from #{old_status} to #{fixed_issue.status}", mail) |
|
181 | "Status changed from #{old_status} to #{fixed_issue.status}", mail) | |
180 |
|
182 | |||
181 | # ignoring commits referencing an issue of another project |
|
183 | # ignoring commits referencing an issue of another project | |
182 | assert_equal [], Issue.find(4).changesets |
|
184 | assert_equal [], Issue.find(4).changesets | |
183 | end |
|
185 | end | |
184 |
|
186 | |||
185 | def test_for_changeset_comments_strip |
|
187 | def test_for_changeset_comments_strip | |
186 | repository = Repository::Mercurial.create( |
|
188 | repository = Repository::Mercurial.create( | |
187 | :project => Project.find( 4 ), |
|
189 | :project => Project.find( 4 ), | |
188 | :url => '/foo/bar/baz' ) |
|
190 | :url => '/foo/bar/baz' ) | |
189 | comment = <<-COMMENT |
|
191 | comment = <<-COMMENT | |
190 | This is a loooooooooooooooooooooooooooong comment |
|
192 | This is a loooooooooooooooooooooooooooong comment | |
191 |
|
193 | |||
192 |
|
194 | |||
193 | COMMENT |
|
195 | COMMENT | |
194 | changeset = Changeset.new( |
|
196 | changeset = Changeset.new( | |
195 | :comments => comment, :commit_date => Time.now, |
|
197 | :comments => comment, :commit_date => Time.now, | |
196 | :revision => 0, :scmid => 'f39b7922fb3c', |
|
198 | :revision => 0, :scmid => 'f39b7922fb3c', | |
197 | :committer => 'foo <foo@example.com>', |
|
199 | :committer => 'foo <foo@example.com>', | |
198 | :committed_on => Time.now, :repository => repository ) |
|
200 | :committed_on => Time.now, :repository => repository ) | |
199 | assert( changeset.save ) |
|
201 | assert( changeset.save ) | |
200 | assert_not_equal( comment, changeset.comments ) |
|
202 | assert_not_equal( comment, changeset.comments ) | |
201 | assert_equal( 'This is a loooooooooooooooooooooooooooong comment', |
|
203 | assert_equal( 'This is a loooooooooooooooooooooooooooong comment', | |
202 | changeset.comments ) |
|
204 | changeset.comments ) | |
203 | end |
|
205 | end | |
204 |
|
206 | |||
205 | def test_for_urls_strip_cvs |
|
207 | def test_for_urls_strip_cvs | |
206 | repository = Repository::Cvs.create( |
|
208 | repository = Repository::Cvs.create( | |
207 | :project => Project.find(4), |
|
209 | :project => Project.find(4), | |
208 | :url => ' :pserver:login:password@host:/path/to/the/repository', |
|
210 | :url => ' :pserver:login:password@host:/path/to/the/repository', | |
209 | :root_url => 'foo ', |
|
211 | :root_url => 'foo ', | |
210 | :log_encoding => 'UTF-8') |
|
212 | :log_encoding => 'UTF-8') | |
211 | assert repository.save |
|
213 | assert repository.save | |
212 | repository.reload |
|
214 | repository.reload | |
213 | assert_equal ':pserver:login:password@host:/path/to/the/repository', |
|
215 | assert_equal ':pserver:login:password@host:/path/to/the/repository', | |
214 | repository.url |
|
216 | repository.url | |
215 | assert_equal 'foo', repository.root_url |
|
217 | assert_equal 'foo', repository.root_url | |
216 | end |
|
218 | end | |
217 |
|
219 | |||
218 | def test_for_urls_strip_subversion |
|
220 | def test_for_urls_strip_subversion | |
219 | repository = Repository::Subversion.create( |
|
221 | repository = Repository::Subversion.create( | |
220 | :project => Project.find(4), |
|
222 | :project => Project.find(4), | |
221 | :url => ' file:///dummy ') |
|
223 | :url => ' file:///dummy ') | |
222 | assert repository.save |
|
224 | assert repository.save | |
223 | repository.reload |
|
225 | repository.reload | |
224 | assert_equal 'file:///dummy', repository.url |
|
226 | assert_equal 'file:///dummy', repository.url | |
225 | end |
|
227 | end | |
226 |
|
228 | |||
227 | def test_for_urls_strip_git |
|
229 | def test_for_urls_strip_git | |
228 | repository = Repository::Git.create( |
|
230 | repository = Repository::Git.create( | |
229 | :project => Project.find(4), |
|
231 | :project => Project.find(4), | |
230 | :url => ' c:\dummy ') |
|
232 | :url => ' c:\dummy ') | |
231 | assert repository.save |
|
233 | assert repository.save | |
232 | repository.reload |
|
234 | repository.reload | |
233 | assert_equal 'c:\dummy', repository.url |
|
235 | assert_equal 'c:\dummy', repository.url | |
234 | end |
|
236 | end | |
235 |
|
237 | |||
236 | def test_manual_user_mapping |
|
238 | def test_manual_user_mapping | |
237 | assert_no_difference "Changeset.count(:conditions => 'user_id <> 2')" do |
|
239 | assert_no_difference "Changeset.count(:conditions => 'user_id <> 2')" do | |
238 | c = Changeset.create!( |
|
240 | c = Changeset.create!( | |
239 | :repository => @repository, |
|
241 | :repository => @repository, | |
240 | :committer => 'foo', |
|
242 | :committer => 'foo', | |
241 | :committed_on => Time.now, |
|
243 | :committed_on => Time.now, | |
242 | :revision => 100, |
|
244 | :revision => 100, | |
243 | :comments => 'Committed by foo.' |
|
245 | :comments => 'Committed by foo.' | |
244 | ) |
|
246 | ) | |
245 | assert_nil c.user |
|
247 | assert_nil c.user | |
246 | @repository.committer_ids = {'foo' => '2'} |
|
248 | @repository.committer_ids = {'foo' => '2'} | |
247 | assert_equal User.find(2), c.reload.user |
|
249 | assert_equal User.find(2), c.reload.user | |
248 | # committer is now mapped |
|
250 | # committer is now mapped | |
249 | c = Changeset.create!( |
|
251 | c = Changeset.create!( | |
250 | :repository => @repository, |
|
252 | :repository => @repository, | |
251 | :committer => 'foo', |
|
253 | :committer => 'foo', | |
252 | :committed_on => Time.now, |
|
254 | :committed_on => Time.now, | |
253 | :revision => 101, |
|
255 | :revision => 101, | |
254 | :comments => 'Another commit by foo.' |
|
256 | :comments => 'Another commit by foo.' | |
255 | ) |
|
257 | ) | |
256 | assert_equal User.find(2), c.user |
|
258 | assert_equal User.find(2), c.user | |
257 | end |
|
259 | end | |
258 | end |
|
260 | end | |
259 |
|
261 | |||
260 | def test_auto_user_mapping_by_username |
|
262 | def test_auto_user_mapping_by_username | |
261 | c = Changeset.create!( |
|
263 | c = Changeset.create!( | |
262 | :repository => @repository, |
|
264 | :repository => @repository, | |
263 | :committer => 'jsmith', |
|
265 | :committer => 'jsmith', | |
264 | :committed_on => Time.now, |
|
266 | :committed_on => Time.now, | |
265 | :revision => 100, |
|
267 | :revision => 100, | |
266 | :comments => 'Committed by john.' |
|
268 | :comments => 'Committed by john.' | |
267 | ) |
|
269 | ) | |
268 | assert_equal User.find(2), c.user |
|
270 | assert_equal User.find(2), c.user | |
269 | end |
|
271 | end | |
270 |
|
272 | |||
271 | def test_auto_user_mapping_by_email |
|
273 | def test_auto_user_mapping_by_email | |
272 | c = Changeset.create!( |
|
274 | c = Changeset.create!( | |
273 | :repository => @repository, |
|
275 | :repository => @repository, | |
274 | :committer => 'john <jsmith@somenet.foo>', |
|
276 | :committer => 'john <jsmith@somenet.foo>', | |
275 | :committed_on => Time.now, |
|
277 | :committed_on => Time.now, | |
276 | :revision => 100, |
|
278 | :revision => 100, | |
277 | :comments => 'Committed by john.' |
|
279 | :comments => 'Committed by john.' | |
278 | ) |
|
280 | ) | |
279 | assert_equal User.find(2), c.user |
|
281 | assert_equal User.find(2), c.user | |
280 | end |
|
282 | end | |
281 |
|
283 | |||
282 | def test_filesystem_avaialbe |
|
284 | def test_filesystem_avaialbe | |
283 | klass = Repository::Filesystem |
|
285 | klass = Repository::Filesystem | |
284 | assert klass.scm_adapter_class |
|
286 | assert klass.scm_adapter_class | |
285 | assert_equal true, klass.scm_available |
|
287 | assert_equal true, klass.scm_available | |
286 | end |
|
288 | end | |
287 |
|
289 | |||
288 | def test_merge_extra_info |
|
290 | def test_merge_extra_info | |
289 | repo = Repository::Subversion.new(:project => Project.find(3)) |
|
291 | repo = Repository::Subversion.new(:project => Project.find(3)) | |
290 | assert !repo.save |
|
292 | assert !repo.save | |
291 | repo.url = "svn://localhost" |
|
293 | repo.url = "svn://localhost" | |
292 | assert repo.save |
|
294 | assert repo.save | |
293 | repo.reload |
|
295 | repo.reload | |
294 | project = Project.find(3) |
|
296 | project = Project.find(3) | |
295 | assert_equal repo, project.repository |
|
297 | assert_equal repo, project.repository | |
296 | assert_nil repo.extra_info |
|
298 | assert_nil repo.extra_info | |
297 | h1 = {"test_1" => {"test_11" => "test_value_11"}} |
|
299 | h1 = {"test_1" => {"test_11" => "test_value_11"}} | |
298 | repo.merge_extra_info(h1) |
|
300 | repo.merge_extra_info(h1) | |
299 | assert_equal h1, repo.extra_info |
|
301 | assert_equal h1, repo.extra_info | |
300 | h2 = {"test_2" => { |
|
302 | h2 = {"test_2" => { | |
301 | "test_21" => "test_value_21", |
|
303 | "test_21" => "test_value_21", | |
302 | "test_22" => "test_value_22", |
|
304 | "test_22" => "test_value_22", | |
303 | }} |
|
305 | }} | |
304 | repo.merge_extra_info(h2) |
|
306 | repo.merge_extra_info(h2) | |
305 | assert_equal (h = {"test_11" => "test_value_11"}), |
|
307 | assert_equal (h = {"test_11" => "test_value_11"}), | |
306 | repo.extra_info["test_1"] |
|
308 | repo.extra_info["test_1"] | |
307 | assert_equal "test_value_21", |
|
309 | assert_equal "test_value_21", | |
308 | repo.extra_info["test_2"]["test_21"] |
|
310 | repo.extra_info["test_2"]["test_21"] | |
309 | h3 = {"test_2" => { |
|
311 | h3 = {"test_2" => { | |
310 | "test_23" => "test_value_23", |
|
312 | "test_23" => "test_value_23", | |
311 | "test_24" => "test_value_24", |
|
313 | "test_24" => "test_value_24", | |
312 | }} |
|
314 | }} | |
313 | repo.merge_extra_info(h3) |
|
315 | repo.merge_extra_info(h3) | |
314 | assert_equal (h = {"test_11" => "test_value_11"}), |
|
316 | assert_equal (h = {"test_11" => "test_value_11"}), | |
315 | repo.extra_info["test_1"] |
|
317 | repo.extra_info["test_1"] | |
316 | assert_nil repo.extra_info["test_2"]["test_21"] |
|
318 | assert_nil repo.extra_info["test_2"]["test_21"] | |
317 | assert_equal "test_value_23", |
|
319 | assert_equal "test_value_23", | |
318 | repo.extra_info["test_2"]["test_23"] |
|
320 | repo.extra_info["test_2"]["test_23"] | |
319 | end |
|
321 | end | |
320 |
|
322 | |||
321 | def test_sort_should_not_raise_an_error_with_nil_identifiers |
|
323 | def test_sort_should_not_raise_an_error_with_nil_identifiers | |
322 | r1 = Repository.new |
|
324 | r1 = Repository.new | |
323 | r2 = Repository.new |
|
325 | r2 = Repository.new | |
324 |
|
326 | |||
325 | assert_nothing_raised do |
|
327 | assert_nothing_raised do | |
326 | [r1, r2].sort |
|
328 | [r1, r2].sort | |
327 | end |
|
329 | end | |
328 | end |
|
330 | end | |
329 | end |
|
331 | end |
General Comments 0
You need to be logged in to leave comments.
Login now