##// END OF EJS Templates
Commit message parser:...
Jean-Philippe Lang -
r848:b30b6717a271
parent child
Show More
@@ -1,86 +1,85
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class Changeset < ActiveRecord::Base
18 class Changeset < ActiveRecord::Base
19 belongs_to :repository
19 belongs_to :repository
20 has_many :changes, :dependent => :delete_all
20 has_many :changes, :dependent => :delete_all
21 has_and_belongs_to_many :issues
21 has_and_belongs_to_many :issues
22
22
23 acts_as_event :title => Proc.new {|o| "#{l(:label_revision)} #{o.revision}" + (o.comments.blank? ? '' : (': ' + o.comments))},
23 acts_as_event :title => Proc.new {|o| "#{l(:label_revision)} #{o.revision}" + (o.comments.blank? ? '' : (': ' + o.comments))},
24 :description => :comments,
24 :description => :comments,
25 :datetime => :committed_on,
25 :datetime => :committed_on,
26 :author => :committer,
26 :author => :committer,
27 :url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.repository.project_id, :rev => o.revision}}
27 :url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.repository.project_id, :rev => o.revision}}
28
28
29 acts_as_searchable :columns => 'comments',
29 acts_as_searchable :columns => 'comments',
30 :include => :repository,
30 :include => :repository,
31 :project_key => "#{Repository.table_name}.project_id",
31 :project_key => "#{Repository.table_name}.project_id",
32 :date_column => 'committed_on'
32 :date_column => 'committed_on'
33
33
34 validates_presence_of :repository_id, :revision, :committed_on, :commit_date
34 validates_presence_of :repository_id, :revision, :committed_on, :commit_date
35 validates_numericality_of :revision, :only_integer => true
35 validates_numericality_of :revision, :only_integer => true
36 validates_uniqueness_of :revision, :scope => :repository_id
36 validates_uniqueness_of :revision, :scope => :repository_id
37 validates_uniqueness_of :scmid, :scope => :repository_id, :allow_nil => true
37 validates_uniqueness_of :scmid, :scope => :repository_id, :allow_nil => true
38
38
39 def comments=(comment)
39 def comments=(comment)
40 write_attribute(:comments, comment.strip)
40 write_attribute(:comments, comment.strip)
41 end
41 end
42
42
43 def committed_on=(date)
43 def committed_on=(date)
44 self.commit_date = date
44 self.commit_date = date
45 super
45 super
46 end
46 end
47
47
48 def after_create
48 def after_create
49 scan_comment_for_issue_ids
49 scan_comment_for_issue_ids
50 end
50 end
51
51
52 def scan_comment_for_issue_ids
52 def scan_comment_for_issue_ids
53 return if comments.blank?
53 return if comments.blank?
54 # keywords used to reference issues
54 # keywords used to reference issues
55 ref_keywords = Setting.commit_ref_keywords.downcase.split(",")
55 ref_keywords = Setting.commit_ref_keywords.downcase.split(",").collect(&:strip)
56 # keywords used to fix issues
56 # keywords used to fix issues
57 fix_keywords = Setting.commit_fix_keywords.downcase.split(",")
57 fix_keywords = Setting.commit_fix_keywords.downcase.split(",").collect(&:strip)
58 # status and optional done ratio applied
58 # status and optional done ratio applied
59 fix_status = IssueStatus.find_by_id(Setting.commit_fix_status_id)
59 fix_status = IssueStatus.find_by_id(Setting.commit_fix_status_id)
60 done_ratio = Setting.commit_fix_done_ratio.blank? ? nil : Setting.commit_fix_done_ratio.to_i
60 done_ratio = Setting.commit_fix_done_ratio.blank? ? nil : Setting.commit_fix_done_ratio.to_i
61
61
62 kw_regexp = (ref_keywords + fix_keywords).collect{|kw| Regexp.escape(kw.strip)}.join("|")
62 kw_regexp = (ref_keywords + fix_keywords).collect{|kw| Regexp.escape(kw)}.join("|")
63 return if kw_regexp.blank?
63 return if kw_regexp.blank?
64
64
65 # remove any associated issues
65 referenced_issues = []
66 self.issues.clear
67
68 comments.scan(Regexp.new("(#{kw_regexp})[\s:]+(([\s,;&]*#?\\d+)+)", Regexp::IGNORECASE)).each do |match|
66 comments.scan(Regexp.new("(#{kw_regexp})[\s:]+(([\s,;&]*#?\\d+)+)", Regexp::IGNORECASE)).each do |match|
69 action = match[0]
67 action = match[0]
70 target_issue_ids = match[1].scan(/\d+/)
68 target_issue_ids = match[1].scan(/\d+/)
71 target_issues = repository.project.issues.find_all_by_id(target_issue_ids)
69 target_issues = repository.project.issues.find_all_by_id(target_issue_ids)
72 if fix_status && fix_keywords.include?(action.downcase)
70 if fix_status && fix_keywords.include?(action.downcase)
73 # update status of issues
71 # update status of issues
74 logger.debug "Issues fixed by changeset #{self.revision}: #{issue_ids.join(', ')}." if logger && logger.debug?
72 logger.debug "Issues fixed by changeset #{self.revision}: #{issue_ids.join(', ')}." if logger && logger.debug?
75 target_issues.each do |issue|
73 target_issues.each do |issue|
76 # don't change the status is the issue is already closed
74 # don't change the status is the issue is already closed
77 next if issue.status.is_closed?
75 next if issue.status.is_closed?
78 issue.status = fix_status
76 issue.status = fix_status
79 issue.done_ratio = done_ratio if done_ratio
77 issue.done_ratio = done_ratio if done_ratio
80 issue.save
78 issue.save
81 end
79 end
82 end
80 end
83 self.issues << target_issues
81 referenced_issues += target_issues
84 end
82 end
83 self.issues = referenced_issues.uniq
85 end
84 end
86 end
85 end
@@ -1,38 +1,38
1 ---
1 ---
2 changesets_001:
2 changesets_001:
3 commit_date: 2007-04-11
3 commit_date: 2007-04-11
4 committed_on: 2007-04-11 15:14:44 +02:00
4 committed_on: 2007-04-11 15:14:44 +02:00
5 revision: 1
5 revision: 1
6 id: 100
6 id: 100
7 comments: My very first commit
7 comments: My very first commit
8 repository_id: 10
8 repository_id: 10
9 committer: dlopper
9 committer: dlopper
10 changesets_002:
10 changesets_002:
11 commit_date: 2007-04-12
11 commit_date: 2007-04-12
12 committed_on: 2007-04-12 15:14:44 +02:00
12 committed_on: 2007-04-12 15:14:44 +02:00
13 revision: 2
13 revision: 2
14 id: 101
14 id: 101
15 comments: 'This commit fixes #1, #2 and references #3'
15 comments: 'This commit fixes #1, #2 and references #1 & #3'
16 repository_id: 10
16 repository_id: 10
17 committer: dlopper
17 committer: dlopper
18 changesets_003:
18 changesets_003:
19 commit_date: 2007-04-12
19 commit_date: 2007-04-12
20 committed_on: 2007-04-12 15:14:44 +02:00
20 committed_on: 2007-04-12 15:14:44 +02:00
21 revision: 3
21 revision: 3
22 id: 102
22 id: 102
23 comments: |-
23 comments: |-
24 A commit with wrong issue ids
24 A commit with wrong issue ids
25 IssueID 666 3
25 IssueID 666 3
26 repository_id: 10
26 repository_id: 10
27 committer: dlopper
27 committer: dlopper
28 changesets_004:
28 changesets_004:
29 commit_date: 2007-04-12
29 commit_date: 2007-04-12
30 committed_on: 2007-04-12 15:14:44 +02:00
30 committed_on: 2007-04-12 15:14:44 +02:00
31 revision: 4
31 revision: 4
32 id: 103
32 id: 103
33 comments: |-
33 comments: |-
34 A commit with an issue id of an other project
34 A commit with an issue id of an other project
35 IssueID 4 2
35 IssueID 4 2
36 repository_id: 10
36 repository_id: 10
37 committer: dlopper
37 committer: dlopper
38 No newline at end of file
38
@@ -1,74 +1,76
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.dirname(__FILE__) + '/../test_helper'
18 require File.dirname(__FILE__) + '/../test_helper'
19
19
20 class RepositoryTest < Test::Unit::TestCase
20 class RepositoryTest < Test::Unit::TestCase
21 fixtures :projects, :repositories, :issues, :issue_statuses, :changesets, :changes
21 fixtures :projects, :repositories, :issues, :issue_statuses, :changesets, :changes
22
22
23 def setup
23 def setup
24 @repository = Project.find(1).repository
24 @repository = Project.find(1).repository
25 end
25 end
26
26
27 def test_create
27 def test_create
28 repository = Repository::Subversion.new(:project => Project.find(2))
28 repository = Repository::Subversion.new(:project => Project.find(2))
29 assert !repository.save
29 assert !repository.save
30
30
31 repository.url = "svn://localhost"
31 repository.url = "svn://localhost"
32 assert repository.save
32 assert repository.save
33 repository.reload
33 repository.reload
34
34
35 project = Project.find(2)
35 project = Project.find(2)
36 assert_equal repository, project.repository
36 assert_equal repository, project.repository
37 end
37 end
38
38
39 def test_scan_changesets_for_issue_ids
39 def test_scan_changesets_for_issue_ids
40 # choosing a status to apply to fix issues
40 # choosing a status to apply to fix issues
41 Setting.commit_fix_status_id = IssueStatus.find(:first, :conditions => ["is_closed = ?", true]).id
41 Setting.commit_fix_status_id = IssueStatus.find(:first, :conditions => ["is_closed = ?", true]).id
42 Setting.commit_fix_done_ratio = "90"
42 Setting.commit_fix_done_ratio = "90"
43 Setting.commit_ref_keywords = 'refs , references, IssueID'
44 Setting.commit_fix_keywords = 'fixes , closes'
43
45
44 # make sure issue 1 is not already closed
46 # make sure issue 1 is not already closed
45 assert !Issue.find(1).status.is_closed?
47 assert !Issue.find(1).status.is_closed?
46
48
47 Repository.scan_changesets_for_issue_ids
49 Repository.scan_changesets_for_issue_ids
48 assert_equal [101, 102], Issue.find(3).changeset_ids
50 assert_equal [101, 102], Issue.find(3).changeset_ids
49
51
50 # fixed issues
52 # fixed issues
51 fixed_issue = Issue.find(1)
53 fixed_issue = Issue.find(1)
52 assert fixed_issue.status.is_closed?
54 assert fixed_issue.status.is_closed?
53 assert_equal 90, fixed_issue.done_ratio
55 assert_equal 90, fixed_issue.done_ratio
54 assert_equal [101], fixed_issue.changeset_ids
56 assert_equal [101], fixed_issue.changeset_ids
55
57
56 # ignoring commits referencing an issue of another project
58 # ignoring commits referencing an issue of another project
57 assert_equal [], Issue.find(4).changesets
59 assert_equal [], Issue.find(4).changesets
58 end
60 end
59
61
60 def test_for_changeset_comments_strip
62 def test_for_changeset_comments_strip
61 repository = Repository::Mercurial.create( :project => Project.find( 4 ), :url => '/foo/bar/baz' )
63 repository = Repository::Mercurial.create( :project => Project.find( 4 ), :url => '/foo/bar/baz' )
62 comment = <<-COMMENT
64 comment = <<-COMMENT
63 This is a loooooooooooooooooooooooooooong comment
65 This is a loooooooooooooooooooooooooooong comment
64
66
65
67
66 COMMENT
68 COMMENT
67 changeset = Changeset.new(
69 changeset = Changeset.new(
68 :comments => comment, :commit_date => Time.now, :revision => 0, :scmid => 'f39b7922fb3c',
70 :comments => comment, :commit_date => Time.now, :revision => 0, :scmid => 'f39b7922fb3c',
69 :committer => 'foo <foo@example.com>', :committed_on => Time.now, :repository_id => repository )
71 :committer => 'foo <foo@example.com>', :committed_on => Time.now, :repository_id => repository )
70 assert( changeset.save )
72 assert( changeset.save )
71 assert_not_equal( comment, changeset.comments )
73 assert_not_equal( comment, changeset.comments )
72 assert_equal( 'This is a loooooooooooooooooooooooooooong comment', changeset.comments )
74 assert_equal( 'This is a loooooooooooooooooooooooooooong comment', changeset.comments )
73 end
75 end
74 end
76 end
General Comments 0
You need to be logged in to leave comments. Login now