##// END OF EJS Templates
Clear changesets and changes with raw sql when deleting a repository (#1627)....
Jean-Philippe Lang -
r1651:fc07ba2a99fd
parent child
Show More
@@ -1,130 +1,139
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 Repository < ActiveRecord::Base
18 class Repository < ActiveRecord::Base
19 belongs_to :project
19 belongs_to :project
20 has_many :changesets, :dependent => :destroy, :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC"
20 has_many :changesets, :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC"
21 has_many :changes, :through => :changesets
21 has_many :changes, :through => :changesets
22
22
23 # Raw SQL to delete changesets and changes in the database
24 # has_many :changesets, :dependent => :destroy is too slow for big repositories
25 before_destroy :clear_changesets
26
23 # Checks if the SCM is enabled when creating a repository
27 # Checks if the SCM is enabled when creating a repository
24 validate_on_create { |r| r.errors.add(:type, :activerecord_error_invalid) unless Setting.enabled_scm.include?(r.class.name.demodulize) }
28 validate_on_create { |r| r.errors.add(:type, :activerecord_error_invalid) unless Setting.enabled_scm.include?(r.class.name.demodulize) }
25
29
26 # Removes leading and trailing whitespace
30 # Removes leading and trailing whitespace
27 def url=(arg)
31 def url=(arg)
28 write_attribute(:url, arg ? arg.to_s.strip : nil)
32 write_attribute(:url, arg ? arg.to_s.strip : nil)
29 end
33 end
30
34
31 # Removes leading and trailing whitespace
35 # Removes leading and trailing whitespace
32 def root_url=(arg)
36 def root_url=(arg)
33 write_attribute(:root_url, arg ? arg.to_s.strip : nil)
37 write_attribute(:root_url, arg ? arg.to_s.strip : nil)
34 end
38 end
35
39
36 def scm
40 def scm
37 @scm ||= self.scm_adapter.new url, root_url, login, password
41 @scm ||= self.scm_adapter.new url, root_url, login, password
38 update_attribute(:root_url, @scm.root_url) if root_url.blank?
42 update_attribute(:root_url, @scm.root_url) if root_url.blank?
39 @scm
43 @scm
40 end
44 end
41
45
42 def scm_name
46 def scm_name
43 self.class.scm_name
47 self.class.scm_name
44 end
48 end
45
49
46 def supports_cat?
50 def supports_cat?
47 scm.supports_cat?
51 scm.supports_cat?
48 end
52 end
49
53
50 def supports_annotate?
54 def supports_annotate?
51 scm.supports_annotate?
55 scm.supports_annotate?
52 end
56 end
53
57
54 def entry(path=nil, identifier=nil)
58 def entry(path=nil, identifier=nil)
55 scm.entry(path, identifier)
59 scm.entry(path, identifier)
56 end
60 end
57
61
58 def entries(path=nil, identifier=nil)
62 def entries(path=nil, identifier=nil)
59 scm.entries(path, identifier)
63 scm.entries(path, identifier)
60 end
64 end
61
65
62 def properties(path, identifier=nil)
66 def properties(path, identifier=nil)
63 scm.properties(path, identifier)
67 scm.properties(path, identifier)
64 end
68 end
65
69
66 def cat(path, identifier=nil)
70 def cat(path, identifier=nil)
67 scm.cat(path, identifier)
71 scm.cat(path, identifier)
68 end
72 end
69
73
70 def diff(path, rev, rev_to)
74 def diff(path, rev, rev_to)
71 scm.diff(path, rev, rev_to)
75 scm.diff(path, rev, rev_to)
72 end
76 end
73
77
74 # Default behaviour: we search in cached changesets
78 # Default behaviour: we search in cached changesets
75 def changesets_for_path(path)
79 def changesets_for_path(path)
76 path = "/#{path}" unless path.starts_with?('/')
80 path = "/#{path}" unless path.starts_with?('/')
77 Change.find(:all, :include => :changeset,
81 Change.find(:all, :include => :changeset,
78 :conditions => ["repository_id = ? AND path = ?", id, path],
82 :conditions => ["repository_id = ? AND path = ?", id, path],
79 :order => "committed_on DESC, #{Changeset.table_name}.id DESC").collect(&:changeset)
83 :order => "committed_on DESC, #{Changeset.table_name}.id DESC").collect(&:changeset)
80 end
84 end
81
85
82 # Returns a path relative to the url of the repository
86 # Returns a path relative to the url of the repository
83 def relative_path(path)
87 def relative_path(path)
84 path
88 path
85 end
89 end
86
90
87 def latest_changeset
91 def latest_changeset
88 @latest_changeset ||= changesets.find(:first)
92 @latest_changeset ||= changesets.find(:first)
89 end
93 end
90
94
91 def scan_changesets_for_issue_ids
95 def scan_changesets_for_issue_ids
92 self.changesets.each(&:scan_comment_for_issue_ids)
96 self.changesets.each(&:scan_comment_for_issue_ids)
93 end
97 end
94
98
95 # fetch new changesets for all repositories
99 # fetch new changesets for all repositories
96 # can be called periodically by an external script
100 # can be called periodically by an external script
97 # eg. ruby script/runner "Repository.fetch_changesets"
101 # eg. ruby script/runner "Repository.fetch_changesets"
98 def self.fetch_changesets
102 def self.fetch_changesets
99 find(:all).each(&:fetch_changesets)
103 find(:all).each(&:fetch_changesets)
100 end
104 end
101
105
102 # scan changeset comments to find related and fixed issues for all repositories
106 # scan changeset comments to find related and fixed issues for all repositories
103 def self.scan_changesets_for_issue_ids
107 def self.scan_changesets_for_issue_ids
104 find(:all).each(&:scan_changesets_for_issue_ids)
108 find(:all).each(&:scan_changesets_for_issue_ids)
105 end
109 end
106
110
107 def self.scm_name
111 def self.scm_name
108 'Abstract'
112 'Abstract'
109 end
113 end
110
114
111 def self.available_scm
115 def self.available_scm
112 subclasses.collect {|klass| [klass.scm_name, klass.name]}
116 subclasses.collect {|klass| [klass.scm_name, klass.name]}
113 end
117 end
114
118
115 def self.factory(klass_name, *args)
119 def self.factory(klass_name, *args)
116 klass = "Repository::#{klass_name}".constantize
120 klass = "Repository::#{klass_name}".constantize
117 klass.new(*args)
121 klass.new(*args)
118 rescue
122 rescue
119 nil
123 nil
120 end
124 end
121
125
122 private
126 private
123
127
124 def before_save
128 def before_save
125 # Strips url and root_url
129 # Strips url and root_url
126 url.strip!
130 url.strip!
127 root_url.strip!
131 root_url.strip!
128 true
132 true
129 end
133 end
134
135 def clear_changesets
136 connection.delete("DELETE FROM changes WHERE changes.changeset_id IN (SELECT changesets.id FROM changesets WHERE changesets.repository_id = #{id})")
137 connection.delete("DELETE FROM changesets WHERE changesets.repository_id = #{id}")
138 end
130 end
139 end
@@ -1,120 +1,130
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,
21 fixtures :projects,
22 :trackers,
22 :trackers,
23 :projects_trackers,
23 :projects_trackers,
24 :repositories,
24 :repositories,
25 :issues,
25 :issues,
26 :issue_statuses,
26 :issue_statuses,
27 :changesets,
27 :changesets,
28 :changes,
28 :changes,
29 :users,
29 :users,
30 :enumerations
30 :enumerations
31
31
32 def setup
32 def setup
33 @repository = Project.find(1).repository
33 @repository = Project.find(1).repository
34 end
34 end
35
35
36 def test_create
36 def test_create
37 repository = Repository::Subversion.new(:project => Project.find(3))
37 repository = Repository::Subversion.new(:project => Project.find(3))
38 assert !repository.save
38 assert !repository.save
39
39
40 repository.url = "svn://localhost"
40 repository.url = "svn://localhost"
41 assert repository.save
41 assert repository.save
42 repository.reload
42 repository.reload
43
43
44 project = Project.find(3)
44 project = Project.find(3)
45 assert_equal repository, project.repository
45 assert_equal repository, project.repository
46 end
46 end
47
47
48 def test_destroy
49 changesets = Changeset.count(:all, :conditions => "repository_id = 10")
50 changes = Change.count(:all, :conditions => "repository_id = 10", :include => :changeset)
51 assert_difference 'Changeset.count', -changesets do
52 assert_difference 'Change.count', -changes do
53 Repository.find(10).destroy
54 end
55 end
56 end
57
48 def test_should_not_create_with_disabled_scm
58 def test_should_not_create_with_disabled_scm
49 # disable Subversion
59 # disable Subversion
50 Setting.enabled_scm = ['Darcs', 'Git']
60 Setting.enabled_scm = ['Darcs', 'Git']
51 repository = Repository::Subversion.new(:project => Project.find(3), :url => "svn://localhost")
61 repository = Repository::Subversion.new(:project => Project.find(3), :url => "svn://localhost")
52 assert !repository.save
62 assert !repository.save
53 assert_equal :activerecord_error_invalid, repository.errors.on(:type)
63 assert_equal :activerecord_error_invalid, repository.errors.on(:type)
54 # re-enable Subversion for following tests
64 # re-enable Subversion for following tests
55 Setting.delete_all
65 Setting.delete_all
56 end
66 end
57
67
58 def test_scan_changesets_for_issue_ids
68 def test_scan_changesets_for_issue_ids
59 # choosing a status to apply to fix issues
69 # choosing a status to apply to fix issues
60 Setting.commit_fix_status_id = IssueStatus.find(:first, :conditions => ["is_closed = ?", true]).id
70 Setting.commit_fix_status_id = IssueStatus.find(:first, :conditions => ["is_closed = ?", true]).id
61 Setting.commit_fix_done_ratio = "90"
71 Setting.commit_fix_done_ratio = "90"
62 Setting.commit_ref_keywords = 'refs , references, IssueID'
72 Setting.commit_ref_keywords = 'refs , references, IssueID'
63 Setting.commit_fix_keywords = 'fixes , closes'
73 Setting.commit_fix_keywords = 'fixes , closes'
64 Setting.default_language = 'en'
74 Setting.default_language = 'en'
65 ActionMailer::Base.deliveries.clear
75 ActionMailer::Base.deliveries.clear
66
76
67 # make sure issue 1 is not already closed
77 # make sure issue 1 is not already closed
68 fixed_issue = Issue.find(1)
78 fixed_issue = Issue.find(1)
69 assert !fixed_issue.status.is_closed?
79 assert !fixed_issue.status.is_closed?
70 old_status = fixed_issue.status
80 old_status = fixed_issue.status
71
81
72 Repository.scan_changesets_for_issue_ids
82 Repository.scan_changesets_for_issue_ids
73 assert_equal [101, 102], Issue.find(3).changeset_ids
83 assert_equal [101, 102], Issue.find(3).changeset_ids
74
84
75 # fixed issues
85 # fixed issues
76 fixed_issue.reload
86 fixed_issue.reload
77 assert fixed_issue.status.is_closed?
87 assert fixed_issue.status.is_closed?
78 assert_equal 90, fixed_issue.done_ratio
88 assert_equal 90, fixed_issue.done_ratio
79 assert_equal [101], fixed_issue.changeset_ids
89 assert_equal [101], fixed_issue.changeset_ids
80
90
81 # issue change
91 # issue change
82 journal = fixed_issue.journals.find(:first, :order => 'created_on desc')
92 journal = fixed_issue.journals.find(:first, :order => 'created_on desc')
83 assert_equal User.find_by_login('dlopper'), journal.user
93 assert_equal User.find_by_login('dlopper'), journal.user
84 assert_equal 'Applied in changeset r2.', journal.notes
94 assert_equal 'Applied in changeset r2.', journal.notes
85
95
86 # 2 email notifications
96 # 2 email notifications
87 assert_equal 2, ActionMailer::Base.deliveries.size
97 assert_equal 2, ActionMailer::Base.deliveries.size
88 mail = ActionMailer::Base.deliveries.first
98 mail = ActionMailer::Base.deliveries.first
89 assert_kind_of TMail::Mail, mail
99 assert_kind_of TMail::Mail, mail
90 assert mail.subject.starts_with?("[#{fixed_issue.project.name} - #{fixed_issue.tracker.name} ##{fixed_issue.id}]")
100 assert mail.subject.starts_with?("[#{fixed_issue.project.name} - #{fixed_issue.tracker.name} ##{fixed_issue.id}]")
91 assert mail.body.include?("Status changed from #{old_status} to #{fixed_issue.status}")
101 assert mail.body.include?("Status changed from #{old_status} to #{fixed_issue.status}")
92
102
93 # ignoring commits referencing an issue of another project
103 # ignoring commits referencing an issue of another project
94 assert_equal [], Issue.find(4).changesets
104 assert_equal [], Issue.find(4).changesets
95 end
105 end
96
106
97 def test_for_changeset_comments_strip
107 def test_for_changeset_comments_strip
98 repository = Repository::Mercurial.create( :project => Project.find( 4 ), :url => '/foo/bar/baz' )
108 repository = Repository::Mercurial.create( :project => Project.find( 4 ), :url => '/foo/bar/baz' )
99 comment = <<-COMMENT
109 comment = <<-COMMENT
100 This is a loooooooooooooooooooooooooooong comment
110 This is a loooooooooooooooooooooooooooong comment
101
111
102
112
103 COMMENT
113 COMMENT
104 changeset = Changeset.new(
114 changeset = Changeset.new(
105 :comments => comment, :commit_date => Time.now, :revision => 0, :scmid => 'f39b7922fb3c',
115 :comments => comment, :commit_date => Time.now, :revision => 0, :scmid => 'f39b7922fb3c',
106 :committer => 'foo <foo@example.com>', :committed_on => Time.now, :repository => repository )
116 :committer => 'foo <foo@example.com>', :committed_on => Time.now, :repository => repository )
107 assert( changeset.save )
117 assert( changeset.save )
108 assert_not_equal( comment, changeset.comments )
118 assert_not_equal( comment, changeset.comments )
109 assert_equal( 'This is a loooooooooooooooooooooooooooong comment', changeset.comments )
119 assert_equal( 'This is a loooooooooooooooooooooooooooong comment', changeset.comments )
110 end
120 end
111
121
112 def test_for_urls_strip
122 def test_for_urls_strip
113 repository = Repository::Cvs.create(:project => Project.find(4), :url => ' :pserver:login:password@host:/path/to/the/repository',
123 repository = Repository::Cvs.create(:project => Project.find(4), :url => ' :pserver:login:password@host:/path/to/the/repository',
114 :root_url => 'foo ')
124 :root_url => 'foo ')
115 assert repository.save
125 assert repository.save
116 repository.reload
126 repository.reload
117 assert_equal ':pserver:login:password@host:/path/to/the/repository', repository.url
127 assert_equal ':pserver:login:password@host:/path/to/the/repository', repository.url
118 assert_equal 'foo', repository.root_url
128 assert_equal 'foo', repository.root_url
119 end
129 end
120 end
130 end
General Comments 0
You need to be logged in to leave comments. Login now