##// END OF EJS Templates
Merged r3394, r3466, r3467, r3468 and r3471 from trunk....
Jean-Philippe Lang -
r3387:17f60af49005
parent child
Show More
@@ -0,0 +1,9
1 class AddIndexOnChangesetsScmid < ActiveRecord::Migration
2 def self.up
3 add_index :changesets, [:repository_id, :scmid], :name => :changesets_repos_scmid
4 end
5
6 def self.down
7 remove_index :changesets, :name => :changesets_repos_scmid
8 end
9 end
@@ -0,0 +1,1
1 Texte encodοΏ½ en ISO-8859-1. No newline at end of file
@@ -1,181 +1,186
1 1 # Redmine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
2 # Copyright (C) 2006-2010 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require 'iconv'
19 19
20 20 class Changeset < ActiveRecord::Base
21 21 belongs_to :repository
22 22 belongs_to :user
23 23 has_many :changes, :dependent => :delete_all
24 24 has_and_belongs_to_many :issues
25 25
26 26 acts_as_event :title => Proc.new {|o| "#{l(:label_revision)} #{o.revision}" + (o.short_comments.blank? ? '' : (': ' + o.short_comments))},
27 27 :description => :long_comments,
28 28 :datetime => :committed_on,
29 29 :url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.repository.project, :rev => o.revision}}
30 30
31 31 acts_as_searchable :columns => 'comments',
32 32 :include => {:repository => :project},
33 33 :project_key => "#{Repository.table_name}.project_id",
34 34 :date_column => 'committed_on'
35 35
36 36 acts_as_activity_provider :timestamp => "#{table_name}.committed_on",
37 37 :author_key => :user_id,
38 38 :find_options => {:include => [:user, {:repository => :project}]}
39 39
40 40 validates_presence_of :repository_id, :revision, :committed_on, :commit_date
41 41 validates_uniqueness_of :revision, :scope => :repository_id
42 42 validates_uniqueness_of :scmid, :scope => :repository_id, :allow_nil => true
43 43
44 44 named_scope :visible, lambda {|*args| { :include => {:repository => :project},
45 45 :conditions => Project.allowed_to_condition(args.first || User.current, :view_changesets) } }
46 46
47 47 def revision=(r)
48 48 write_attribute :revision, (r.nil? ? nil : r.to_s)
49 49 end
50 50
51 51 def comments=(comment)
52 52 write_attribute(:comments, Changeset.normalize_comments(comment))
53 53 end
54 54
55 55 def committed_on=(date)
56 56 self.commit_date = date
57 57 super
58 58 end
59 59
60 def committer=(arg)
61 write_attribute(:committer, self.class.to_utf8(arg.to_s))
62 end
63
60 64 def project
61 65 repository.project
62 66 end
63 67
64 68 def author
65 69 user || committer.to_s.split('<').first
66 70 end
67 71
68 72 def before_create
69 73 self.user = repository.find_committer_user(committer)
70 74 end
71 75
72 76 def after_create
73 77 scan_comment_for_issue_ids
74 78 end
75 79 require 'pp'
76 80
77 81 def scan_comment_for_issue_ids
78 82 return if comments.blank?
79 83 # keywords used to reference issues
80 84 ref_keywords = Setting.commit_ref_keywords.downcase.split(",").collect(&:strip)
81 85 # keywords used to fix issues
82 86 fix_keywords = Setting.commit_fix_keywords.downcase.split(",").collect(&:strip)
83 87 # status and optional done ratio applied
84 88 fix_status = IssueStatus.find_by_id(Setting.commit_fix_status_id)
85 89 done_ratio = Setting.commit_fix_done_ratio.blank? ? nil : Setting.commit_fix_done_ratio.to_i
86 90
87 91 kw_regexp = (ref_keywords + fix_keywords).collect{|kw| Regexp.escape(kw)}.join("|")
88 92 return if kw_regexp.blank?
89 93
90 94 referenced_issues = []
91 95
92 96 if ref_keywords.delete('*')
93 97 # find any issue ID in the comments
94 98 target_issue_ids = []
95 99 comments.scan(%r{([\s\(\[,-]|^)#(\d+)(?=[[:punct:]]|\s|<|$)}).each { |m| target_issue_ids << m[1] }
96 100 referenced_issues += find_referenced_issues_by_id(target_issue_ids)
97 101 end
98 102
99 103 comments.scan(Regexp.new("(#{kw_regexp})[\s:]+(([\s,;&]*#?\\d+)+)", Regexp::IGNORECASE)).each do |match|
100 104 action = match[0]
101 105 target_issue_ids = match[1].scan(/\d+/)
102 106 target_issues = find_referenced_issues_by_id(target_issue_ids)
103 107 if fix_status && fix_keywords.include?(action.downcase)
104 108 # update status of issues
105 109 logger.debug "Issues fixed by changeset #{self.revision}: #{issue_ids.join(', ')}." if logger && logger.debug?
106 110 target_issues.each do |issue|
107 111 # the issue may have been updated by the closure of another one (eg. duplicate)
108 112 issue.reload
109 113 # don't change the status is the issue is closed
110 114 next if issue.status.is_closed?
111 115 csettext = "r#{self.revision}"
112 116 if self.scmid && (! (csettext =~ /^r[0-9]+$/))
113 117 csettext = "commit:\"#{self.scmid}\""
114 118 end
115 119 journal = issue.init_journal(user || User.anonymous, ll(Setting.default_language, :text_status_changed_by_changeset, csettext))
116 120 issue.status = fix_status
117 121 issue.done_ratio = done_ratio if done_ratio
118 122 Redmine::Hook.call_hook(:model_changeset_scan_commit_for_issue_ids_pre_issue_update,
119 123 { :changeset => self, :issue => issue })
120 124 issue.save
121 125 end
122 126 end
123 127 referenced_issues += target_issues
124 128 end
125 129
126 130 self.issues = referenced_issues.uniq
127 131 end
128 132
129 133 def short_comments
130 134 @short_comments || split_comments.first
131 135 end
132 136
133 137 def long_comments
134 138 @long_comments || split_comments.last
135 139 end
136 140
137 141 # Returns the previous changeset
138 142 def previous
139 143 @previous ||= Changeset.find(:first, :conditions => ['id < ? AND repository_id = ?', self.id, self.repository_id], :order => 'id DESC')
140 144 end
141 145
142 146 # Returns the next changeset
143 147 def next
144 148 @next ||= Changeset.find(:first, :conditions => ['id > ? AND repository_id = ?', self.id, self.repository_id], :order => 'id ASC')
145 149 end
146 150
147 151 # Strips and reencodes a commit log before insertion into the database
148 152 def self.normalize_comments(str)
149 153 to_utf8(str.to_s.strip)
150 154 end
151 155
152 156 private
153 157
154 158 # Finds issues that can be referenced by the commit message
155 159 # i.e. issues that belong to the repository project, a subproject or a parent project
156 160 def find_referenced_issues_by_id(ids)
157 161 Issue.find_all_by_id(ids, :include => :project).select {|issue|
158 162 project == issue.project || project.is_ancestor_of?(issue.project) || project.is_descendant_of?(issue.project)
159 163 }
160 164 end
161 165
162 166 def split_comments
163 167 comments =~ /\A(.+?)\r?\n(.*)$/m
164 168 @short_comments = $1 || comments
165 169 @long_comments = $2.to_s.strip
166 170 return @short_comments, @long_comments
167 171 end
168 172
169 173 def self.to_utf8(str)
170 174 return str if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str) # for us-ascii
171 175 encoding = Setting.commit_logs_encoding.to_s.strip
172 176 unless encoding.blank? || encoding == 'UTF-8'
173 177 begin
174 return Iconv.conv('UTF-8', encoding, str)
178 str = Iconv.conv('UTF-8', encoding, str)
175 179 rescue Iconv::Failure
176 180 # do nothing here
177 181 end
178 182 end
179 str
183 # removes invalid UTF8 sequences
184 Iconv.conv('UTF-8//IGNORE', 'UTF-8', str + ' ')[0..-3]
180 185 end
181 186 end
@@ -1,78 +1,81
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 # Copyright (C) 2007 Patrick Aljord patcito@Ε‹mail.com
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require 'redmine/scm/adapters/git_adapter'
19 19
20 20 class Repository::Git < Repository
21 21 attr_protected :root_url
22 22 validates_presence_of :url
23 23
24 24 def scm_adapter
25 25 Redmine::Scm::Adapters::GitAdapter
26 26 end
27 27
28 28 def self.scm_name
29 29 'Git'
30 30 end
31 31
32 32 def branches
33 33 scm.branches
34 34 end
35 35
36 36 def tags
37 37 scm.tags
38 38 end
39 39
40 40 # With SCM's that have a sequential commit numbering, redmine is able to be
41 41 # clever and only fetch changesets going forward from the most recent one
42 42 # it knows about. However, with git, you never know if people have merged
43 # commits into the middle of the repository history, so we always have to
44 # parse the entire log.
43 # commits into the middle of the repository history, so we should parse
44 # the entire log. Since it's way too slow for large repositories, we only
45 # parse 1 week before the last known commit.
46 # The repository can still be fully reloaded by calling #clear_changesets
47 # before fetching changesets (eg. for offline resync)
45 48 def fetch_changesets
46 # Save ourselves an expensive operation if we're already up to date
47 return if scm.num_revisions == changesets.count
49 c = changesets.find(:first, :order => 'committed_on DESC')
50 since = (c ? c.committed_on - 7.days : nil)
48 51
49 revisions = scm.revisions('', nil, nil, :all => true)
52 revisions = scm.revisions('', nil, nil, :all => true, :since => since)
50 53 return if revisions.nil? || revisions.empty?
51 54
52 # Find revisions that redmine knows about already
53 existing_revisions = changesets.find(:all).map!{|c| c.scmid}
55 recent_changesets = changesets.find(:all, :conditions => ['committed_on >= ?', since])
54 56
55 57 # Clean out revisions that are no longer in git
56 Changeset.delete_all(["scmid NOT IN (?) AND repository_id = (?)", revisions.map{|r| r.scmid}, self.id])
58 recent_changesets.each {|c| c.destroy unless revisions.detect {|r| r.scmid.to_s == c.scmid.to_s }}
57 59
58 60 # Subtract revisions that redmine already knows about
59 revisions.reject!{|r| existing_revisions.include?(r.scmid)}
61 recent_revisions = recent_changesets.map{|c| c.scmid}
62 revisions.reject!{|r| recent_revisions.include?(r.scmid)}
60 63
61 64 # Save the remaining ones to the database
62 65 revisions.each{|r| r.save(self)} unless revisions.nil?
63 66 end
64 67
65 68 def latest_changesets(path,rev,limit=10)
66 69 revisions = scm.revisions(path, nil, rev, :limit => limit, :all => false)
67 70 return [] if revisions.nil? || revisions.empty?
68 71
69 72 changesets.find(
70 73 :all,
71 74 :conditions => [
72 75 "scmid IN (?)",
73 76 revisions.map!{|c| c.scmid}
74 77 ],
75 78 :order => 'committed_on DESC'
76 79 )
77 80 end
78 81 end
@@ -1,263 +1,260
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require 'redmine/scm/adapters/abstract_adapter'
19 19
20 20 module Redmine
21 21 module Scm
22 22 module Adapters
23 23 class GitAdapter < AbstractAdapter
24 24 # Git executable name
25 25 GIT_BIN = "git"
26 26
27 27 def info
28 28 begin
29 29 Info.new(:root_url => url, :lastrev => lastrev('',nil))
30 30 rescue
31 31 nil
32 32 end
33 33 end
34 34
35 35 def branches
36 branches = []
36 return @branches if @branches
37 @branches = []
37 38 cmd = "#{GIT_BIN} --git-dir #{target('')} branch"
38 39 shellout(cmd) do |io|
39 40 io.each_line do |line|
40 branches << line.match('\s*\*?\s*(.*)$')[1]
41 @branches << line.match('\s*\*?\s*(.*)$')[1]
41 42 end
42 43 end
43 branches.sort!
44 @branches.sort!
44 45 end
45 46
46 47 def tags
47 tags = []
48 return @tags if @tags
48 49 cmd = "#{GIT_BIN} --git-dir #{target('')} tag"
49 50 shellout(cmd) do |io|
50 io.readlines.sort!.map{|t| t.strip}
51 @tags = io.readlines.sort!.map{|t| t.strip}
51 52 end
52 53 end
53 54
54 55 def default_branch
55 56 branches.include?('master') ? 'master' : branches.first
56 57 end
57 58
58 59 def entries(path=nil, identifier=nil)
59 60 path ||= ''
60 61 entries = Entries.new
61 62 cmd = "#{GIT_BIN} --git-dir #{target('')} ls-tree -l "
62 63 cmd << shell_quote("HEAD:" + path) if identifier.nil?
63 64 cmd << shell_quote(identifier + ":" + path) if identifier
64 65 shellout(cmd) do |io|
65 66 io.each_line do |line|
66 67 e = line.chomp.to_s
67 68 if e =~ /^\d+\s+(\w+)\s+([0-9a-f]{40})\s+([0-9-]+)\s+(.+)$/
68 69 type = $1
69 70 sha = $2
70 71 size = $3
71 72 name = $4
72 73 full_path = path.empty? ? name : "#{path}/#{name}"
73 74 entries << Entry.new({:name => name,
74 75 :path => full_path,
75 76 :kind => (type == "tree") ? 'dir' : 'file',
76 77 :size => (type == "tree") ? nil : size,
77 78 :lastrev => lastrev(full_path,identifier)
78 79 }) unless entries.detect{|entry| entry.name == name}
79 80 end
80 81 end
81 82 end
82 83 return nil if $? && $?.exitstatus != 0
83 84 entries.sort_by_name
84 85 end
85 86
86 87 def lastrev(path,rev)
87 88 return nil if path.nil?
88 89 cmd = "#{GIT_BIN} --git-dir #{target('')} log --pretty=fuller --no-merges -n 1 "
89 90 cmd << " #{shell_quote rev} " if rev
90 91 cmd << "-- #{path} " unless path.empty?
91 92 shellout(cmd) do |io|
92 93 begin
93 94 id = io.gets.split[1]
94 95 author = io.gets.match('Author:\s+(.*)$')[1]
95 96 2.times { io.gets }
96 97 time = io.gets.match('CommitDate:\s+(.*)$')[1]
97 98
98 99 Revision.new({
99 100 :identifier => id,
100 101 :scmid => id,
101 102 :author => author,
102 103 :time => time,
103 104 :message => nil,
104 105 :paths => nil
105 106 })
106 107 rescue NoMethodError => e
107 108 logger.error("The revision '#{path}' has a wrong format")
108 109 return nil
109 110 end
110 111 end
111 112 end
112 113
113 def num_revisions
114 cmd = "#{GIT_BIN} --git-dir #{target('')} log --all --pretty=format:'' | wc -l"
115 shellout(cmd) {|io| io.gets.chomp.to_i + 1}
116 end
117
118 114 def revisions(path, identifier_from, identifier_to, options={})
119 115 revisions = Revisions.new
120 116
121 cmd = "#{GIT_BIN} --git-dir #{target('')} log --find-copies-harder --raw --date=iso --pretty=fuller"
117 cmd = "#{GIT_BIN} --git-dir #{target('')} log --raw --date=iso --pretty=fuller"
122 118 cmd << " --reverse" if options[:reverse]
123 119 cmd << " --all" if options[:all]
124 120 cmd << " -n #{options[:limit]} " if options[:limit]
125 121 cmd << " #{shell_quote(identifier_from + '..')} " if identifier_from
126 122 cmd << " #{shell_quote identifier_to} " if identifier_to
123 cmd << " --since=#{shell_quote(options[:since].strftime("%Y-%m-%d %H:%M:%S"))}" if options[:since]
127 124 cmd << " -- #{path}" if path && !path.empty?
128 125
129 126 shellout(cmd) do |io|
130 127 files=[]
131 128 changeset = {}
132 129 parsing_descr = 0 #0: not parsing desc or files, 1: parsing desc, 2: parsing files
133 130 revno = 1
134 131
135 132 io.each_line do |line|
136 133 if line =~ /^commit ([0-9a-f]{40})$/
137 134 key = "commit"
138 135 value = $1
139 136 if (parsing_descr == 1 || parsing_descr == 2)
140 137 parsing_descr = 0
141 138 revision = Revision.new({
142 139 :identifier => changeset[:commit],
143 140 :scmid => changeset[:commit],
144 141 :author => changeset[:author],
145 142 :time => Time.parse(changeset[:date]),
146 143 :message => changeset[:description],
147 144 :paths => files
148 145 })
149 146 if block_given?
150 147 yield revision
151 148 else
152 149 revisions << revision
153 150 end
154 151 changeset = {}
155 152 files = []
156 153 revno = revno + 1
157 154 end
158 155 changeset[:commit] = $1
159 156 elsif (parsing_descr == 0) && line =~ /^(\w+):\s*(.*)$/
160 157 key = $1
161 158 value = $2
162 159 if key == "Author"
163 160 changeset[:author] = value
164 161 elsif key == "CommitDate"
165 162 changeset[:date] = value
166 163 end
167 164 elsif (parsing_descr == 0) && line.chomp.to_s == ""
168 165 parsing_descr = 1
169 166 changeset[:description] = ""
170 167 elsif (parsing_descr == 1 || parsing_descr == 2) \
171 168 && line =~ /^:\d+\s+\d+\s+[0-9a-f.]+\s+[0-9a-f.]+\s+(\w)\s+(.+)$/
172 169 parsing_descr = 2
173 170 fileaction = $1
174 171 filepath = $2
175 172 files << {:action => fileaction, :path => filepath}
176 173 elsif (parsing_descr == 1 || parsing_descr == 2) \
177 174 && line =~ /^:\d+\s+\d+\s+[0-9a-f.]+\s+[0-9a-f.]+\s+(\w)\d+\s+(\S+)\s+(.+)$/
178 175 parsing_descr = 2
179 176 fileaction = $1
180 177 filepath = $3
181 178 files << {:action => fileaction, :path => filepath}
182 179 elsif (parsing_descr == 1) && line.chomp.to_s == ""
183 180 parsing_descr = 2
184 181 elsif (parsing_descr == 1)
185 182 changeset[:description] << line[4..-1]
186 183 end
187 184 end
188 185
189 186 if changeset[:commit]
190 187 revision = Revision.new({
191 188 :identifier => changeset[:commit],
192 189 :scmid => changeset[:commit],
193 190 :author => changeset[:author],
194 191 :time => Time.parse(changeset[:date]),
195 192 :message => changeset[:description],
196 193 :paths => files
197 194 })
198 195
199 196 if block_given?
200 197 yield revision
201 198 else
202 199 revisions << revision
203 200 end
204 201 end
205 202 end
206 203
207 204 return nil if $? && $?.exitstatus != 0
208 205 revisions
209 206 end
210 207
211 208 def diff(path, identifier_from, identifier_to=nil)
212 209 path ||= ''
213 210
214 211 if identifier_to
215 212 cmd = "#{GIT_BIN} --git-dir #{target('')} diff #{shell_quote identifier_to} #{shell_quote identifier_from}"
216 213 else
217 214 cmd = "#{GIT_BIN} --git-dir #{target('')} show #{shell_quote identifier_from}"
218 215 end
219 216
220 217 cmd << " -- #{shell_quote path}" unless path.empty?
221 218 diff = []
222 219 shellout(cmd) do |io|
223 220 io.each_line do |line|
224 221 diff << line
225 222 end
226 223 end
227 224 return nil if $? && $?.exitstatus != 0
228 225 diff
229 226 end
230 227
231 228 def annotate(path, identifier=nil)
232 229 identifier = 'HEAD' if identifier.blank?
233 230 cmd = "#{GIT_BIN} --git-dir #{target('')} blame -l #{shell_quote identifier} -- #{shell_quote path}"
234 231 blame = Annotate.new
235 232 content = nil
236 233 shellout(cmd) { |io| io.binmode; content = io.read }
237 234 return nil if $? && $?.exitstatus != 0
238 235 # git annotates binary files
239 236 return nil if content.is_binary_data?
240 237 content.split("\n").each do |line|
241 238 next unless line =~ /([0-9a-f]{39,40})\s\((\w*)[^\)]*\)(.*)/
242 239 blame.add_line($3.rstrip, Revision.new(:identifier => $1, :author => $2.strip))
243 240 end
244 241 blame
245 242 end
246 243
247 244 def cat(path, identifier=nil)
248 245 if identifier.nil?
249 246 identifier = 'HEAD'
250 247 end
251 248 cmd = "#{GIT_BIN} --git-dir #{target('')} show #{shell_quote(identifier + ':' + path)}"
252 249 cat = nil
253 250 shellout(cmd) do |io|
254 251 io.binmode
255 252 cat = io.read
256 253 end
257 254 return nil if $? && $?.exitstatus != 0
258 255 cat
259 256 end
260 257 end
261 258 end
262 259 end
263 260 end
@@ -1,120 +1,136
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
1 # encoding: utf-8
2 #
3 # Redmine - project management software
4 # Copyright (C) 2006-2010 Jean-Philippe Lang
3 5 #
4 6 # This program is free software; you can redistribute it and/or
5 7 # modify it under the terms of the GNU General Public License
6 8 # as published by the Free Software Foundation; either version 2
7 9 # of the License, or (at your option) any later version.
8 10 #
9 11 # This program is distributed in the hope that it will be useful,
10 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 14 # GNU General Public License for more details.
13 15 #
14 16 # You should have received a copy of the GNU General Public License
15 17 # along with this program; if not, write to the Free Software
16 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 19
18 20 require File.dirname(__FILE__) + '/../test_helper'
19 21
20 22 class ChangesetTest < ActiveSupport::TestCase
21 23 fixtures :projects, :repositories, :issues, :issue_statuses, :changesets, :changes, :issue_categories, :enumerations, :custom_fields, :custom_values, :users, :members, :member_roles, :trackers
22 24
23 25 def setup
24 26 end
25 27
26 28 def test_ref_keywords_any
27 29 ActionMailer::Base.deliveries.clear
28 30 Setting.commit_fix_status_id = IssueStatus.find(:first, :conditions => ["is_closed = ?", true]).id
29 31 Setting.commit_fix_done_ratio = '90'
30 32 Setting.commit_ref_keywords = '*'
31 33 Setting.commit_fix_keywords = 'fixes , closes'
32 34
33 35 c = Changeset.new(:repository => Project.find(1).repository,
34 36 :committed_on => Time.now,
35 37 :comments => 'New commit (#2). Fixes #1')
36 38 c.scan_comment_for_issue_ids
37 39
38 40 assert_equal [1, 2], c.issue_ids.sort
39 41 fixed = Issue.find(1)
40 42 assert fixed.closed?
41 43 assert_equal 90, fixed.done_ratio
42 44 assert_equal 1, ActionMailer::Base.deliveries.size
43 45 end
44 46
45 47 def test_ref_keywords_any_line_start
46 48 Setting.commit_ref_keywords = '*'
47 49
48 50 c = Changeset.new(:repository => Project.find(1).repository,
49 51 :committed_on => Time.now,
50 52 :comments => '#1 is the reason of this commit')
51 53 c.scan_comment_for_issue_ids
52 54
53 55 assert_equal [1], c.issue_ids.sort
54 56 end
55 57
56 58 def test_ref_keywords_allow_brackets_around_a_issue_number
57 59 Setting.commit_ref_keywords = '*'
58 60
59 61 c = Changeset.new(:repository => Project.find(1).repository,
60 62 :committed_on => Time.now,
61 63 :comments => '[#1] Worked on this issue')
62 64 c.scan_comment_for_issue_ids
63 65
64 66 assert_equal [1], c.issue_ids.sort
65 67 end
66 68
67 69 def test_ref_keywords_allow_brackets_around_multiple_issue_numbers
68 70 Setting.commit_ref_keywords = '*'
69 71
70 72 c = Changeset.new(:repository => Project.find(1).repository,
71 73 :committed_on => Time.now,
72 74 :comments => '[#1 #2, #3] Worked on these')
73 75 c.scan_comment_for_issue_ids
74 76
75 77 assert_equal [1,2,3], c.issue_ids.sort
76 78 end
77 79
78 80 def test_commit_referencing_a_subproject_issue
79 81 c = Changeset.new(:repository => Project.find(1).repository,
80 82 :committed_on => Time.now,
81 83 :comments => 'refs #5, a subproject issue')
82 84 c.scan_comment_for_issue_ids
83 85
84 86 assert_equal [5], c.issue_ids.sort
85 87 assert c.issues.first.project != c.project
86 88 end
87 89
88 90 def test_commit_referencing_a_parent_project_issue
89 91 # repository of child project
90 92 r = Repository::Subversion.create!(:project => Project.find(3), :url => 'svn://localhost/test')
91 93
92 94 c = Changeset.new(:repository => r,
93 95 :committed_on => Time.now,
94 96 :comments => 'refs #2, an issue of a parent project')
95 97 c.scan_comment_for_issue_ids
96 98
97 99 assert_equal [2], c.issue_ids.sort
98 100 assert c.issues.first.project != c.project
99 101 end
100 102
101 103 def test_previous
102 104 changeset = Changeset.find_by_revision('3')
103 105 assert_equal Changeset.find_by_revision('2'), changeset.previous
104 106 end
105 107
106 108 def test_previous_nil
107 109 changeset = Changeset.find_by_revision('1')
108 110 assert_nil changeset.previous
109 111 end
110 112
111 113 def test_next
112 114 changeset = Changeset.find_by_revision('2')
113 115 assert_equal Changeset.find_by_revision('3'), changeset.next
114 116 end
115 117
116 118 def test_next_nil
117 119 changeset = Changeset.find_by_revision('10')
118 120 assert_nil changeset.next
119 121 end
122
123 def test_comments_should_be_converted_to_utf8
124 with_settings :commit_logs_encoding => 'ISO-8859-1' do
125 c = Changeset.new
126 c.comments = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt")
127 assert_equal "Texte encodΓ© en ISO-8859-1.", c.comments
128 end
129 end
130
131 def test_invalid_utf8_sequences_in_comments_should_be_stripped
132 c = Changeset.new
133 c.comments = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt")
134 assert_equal "Texte encod en ISO-8859-1.", c.comments
135 end
120 136 end
@@ -1,69 +1,69
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.dirname(__FILE__) + '/../test_helper'
19 19
20 20 class RepositoryGitTest < ActiveSupport::TestCase
21 21 fixtures :projects
22 22
23 23 # No '..' in the repository path
24 24 REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/git_repository'
25 25 REPOSITORY_PATH.gsub!(/\//, "\\") if Redmine::Platform.mswin?
26 26
27 27 def setup
28 28 @project = Project.find(1)
29 29 assert @repository = Repository::Git.create(:project => @project, :url => REPOSITORY_PATH)
30 30 end
31 31
32 32 if File.directory?(REPOSITORY_PATH)
33 33 def test_fetch_changesets_from_scratch
34 34 @repository.fetch_changesets
35 35 @repository.reload
36 36
37 37 assert_equal 12, @repository.changesets.count
38 assert_equal 20, @repository.changes.count
38 assert_equal 21, @repository.changes.count
39 39
40 40 commit = @repository.changesets.find(:first, :order => 'committed_on ASC')
41 41 assert_equal "Initial import.\nThe repository contains 3 files.", commit.comments
42 42 assert_equal "jsmith <jsmith@foo.bar>", commit.committer
43 43 assert_equal User.find_by_login('jsmith'), commit.user
44 44 # TODO: add a commit with commit time <> author time to the test repository
45 45 assert_equal "2007-12-14 09:22:52".to_time, commit.committed_on
46 46 assert_equal "2007-12-14".to_date, commit.commit_date
47 47 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518", commit.revision
48 48 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518", commit.scmid
49 49 assert_equal 3, commit.changes.count
50 50 change = commit.changes.sort_by(&:path).first
51 51 assert_equal "README", change.path
52 52 assert_equal "A", change.action
53 53 end
54 54
55 55 def test_fetch_changesets_incremental
56 56 @repository.fetch_changesets
57 57 # Remove the 3 latest changesets
58 58 @repository.changesets.find(:all, :order => 'committed_on DESC', :limit => 3).each(&:destroy)
59 59 @repository.reload
60 60 assert_equal 9, @repository.changesets.count
61 61
62 62 @repository.fetch_changesets
63 63 assert_equal 12, @repository.changesets.count
64 64 end
65 65 else
66 66 puts "Git test repository NOT FOUND. Skipping unit tests !!!"
67 67 def test_fake; assert true end
68 68 end
69 69 end
General Comments 0
You need to be logged in to leave comments. Login now