Patch #9359 » invert_project_repository_relationship_2-1.3-stable-9308.patch
test/unit/user_test.rb (working copy) | ||
---|---|---|
314 | 314 |
def test_destroy_should_nullify_changesets |
315 | 315 |
changeset = Changeset.create!( |
316 | 316 |
:repository => Repository::Subversion.create!( |
317 |
:project_id => 1, |
|
318 | 317 |
:url => 'file:///var/svn' |
319 | 318 |
), |
320 | 319 |
:revision => '12', |
test/unit/project_test.rb (working copy) | ||
---|---|---|
59 | 59 |
should_have_many :boards |
60 | 60 |
should_have_many :changesets, :through => :repository |
61 | 61 | |
62 |
should_have_one :repository |
|
62 |
should_belong_to :repository |
|
63 | ||
63 | 64 |
should_have_one :wiki |
64 | 65 | |
65 | 66 |
should_have_and_belong_to_many :trackers |
test/functional/sys_controller_test.rb (working copy) | ||
---|---|---|
54 | 54 |
assert_response :created |
55 | 55 |
assert_equal 'application/xml', @response.content_type |
56 | 56 | |
57 |
r = Project.find(4).repository |
|
58 |
assert r.is_a?(Repository::Subversion) |
|
57 |
assert r = Project.find(4).repository
|
|
58 |
assert r.is_a?(Repository::Subversion), r.inspect
|
|
59 | 59 |
assert_equal 'file:///create/project/repository/subproject2', r.url |
60 | 60 |
|
61 | 61 |
assert_tag 'repository-subversion', |
... | ... | |
67 | 67 |
assert_no_tag 'extra_info' |
68 | 68 |
end |
69 | 69 | |
70 |
def test_should_reuse_existing_repository |
|
71 |
assert_nil Project.find(4).repository |
|
72 |
assert_nil Project.find(5).repository |
|
73 | ||
74 |
assert_difference "Repository.count", +1 do |
|
75 |
post :create_project_repository, :id => 4, |
|
76 |
:vendor => 'Subversion', |
|
77 |
:repository => { :url => 'file:///create/project/repository/subproject2'} |
|
78 | ||
79 |
end |
|
80 |
assert_response :created |
|
81 |
assert r = Project.find(4).repository |
|
82 |
assert r.is_a?(Repository::Subversion), r.inspect |
|
83 |
assert_equal 'file:///create/project/repository/subproject2', r.url |
|
84 | ||
85 |
assert_no_difference "Repository.count" do |
|
86 |
post :create_project_repository, :id => 5, |
|
87 |
:vendor => 'Subversion', |
|
88 |
:repository => { :url => 'file:///create/project/repository/subproject2'} |
|
89 |
end |
|
90 |
assert_response :created |
|
91 |
assert_equal r, Project.find(5).repository |
|
92 |
end |
|
93 | ||
70 | 94 |
def test_fetch_changesets |
71 | 95 |
Repository::Subversion.any_instance.expects(:fetch_changesets).returns(true) |
72 | 96 |
get :fetch_changesets |
test/fixtures/repositories.yml (working copy) | ||
---|---|---|
1 | 1 |
--- |
2 | 2 |
repositories_001: |
3 |
project_id: 1 |
|
4 | 3 |
url: file:///<%= Rails.root %>/tmp/test/subversion_repository |
5 | 4 |
id: 10 |
6 | 5 |
root_url: file:///<%= Rails.root %>/tmp/test/subversion_repository |
... | ... | |
8 | 7 |
login: "" |
9 | 8 |
type: Subversion |
10 | 9 |
repositories_002: |
11 |
project_id: 2 |
|
12 | 10 |
url: svn://localhost/test |
13 | 11 |
id: 11 |
14 | 12 |
root_url: svn://localhost |
test/fixtures/projects.yml (working copy) | ||
---|---|---|
11 | 11 |
parent_id: |
12 | 12 |
lft: 1 |
13 | 13 |
rgt: 10 |
14 |
repository_id: 10 |
|
14 | 15 |
projects_002: |
15 | 16 |
created_on: 2006-07-19 19:14:19 +02:00 |
16 | 17 |
name: OnlineStore |
... | ... | |
23 | 24 |
parent_id: |
24 | 25 |
lft: 11 |
25 | 26 |
rgt: 12 |
27 |
repository_id: 11 |
|
26 | 28 |
projects_003: |
27 | 29 |
created_on: 2006-07-19 19:15:21 +02:00 |
28 | 30 |
name: eCookbook Subproject 1 |
app/helpers/application_helper.rb (working copy) | ||
---|---|---|
721 | 721 |
:class => 'news' |
722 | 722 |
end |
723 | 723 |
when 'commit' |
724 |
if project && project.repository && (changeset = Changeset.visible.find(:first, :conditions => ["repository_id = ? AND scmid LIKE ?", project.repository.id, "#{name}%"])) |
|
724 |
if project && project.repository && (changeset = Changeset.visible.find(:first, :conditions => ["#{Changeset.table_name}.repository_id = ? AND scmid LIKE ?", project.repository.id, "#{name}%"]))
|
|
725 | 725 |
link = link_to h("#{project_prefix}#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.identifier}, |
726 | 726 |
:class => 'changeset', |
727 | 727 |
:title => truncate_single_line(h(changeset.comments), :length => 100) |
app/models/repository.rb (working copy) | ||
---|---|---|
20 | 20 |
class Repository < ActiveRecord::Base |
21 | 21 |
include Redmine::Ciphering |
22 | 22 | |
23 |
belongs_to :project |
|
23 |
has_many :projects |
|
24 |
has_one :project, :order => 'lft ASC' |
|
25 | ||
24 | 26 |
has_many :changesets, :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC" |
25 | 27 |
has_many :changes, :through => :changesets |
26 | 28 | |
... | ... | |
249 | 251 |
# Can be called periodically by an external script |
250 | 252 |
# eg. ruby script/runner "Repository.fetch_changesets" |
251 | 253 |
def self.fetch_changesets |
252 |
Project.active.has_module(:repository).find(:all, :include => :repository).each do |project| |
|
253 |
if project.repository |
|
254 |
begin |
|
255 |
project.repository.fetch_changesets |
|
256 |
rescue Redmine::Scm::Adapters::CommandFailed => e |
|
257 |
logger.error "scm: error during fetching changesets: #{e.message}" |
|
258 |
end |
|
254 |
Project.active.has_module(:repository).find(:all, :include => :repository). |
|
255 |
map(&:repository). |
|
256 |
uniq. |
|
257 |
compact.each do |repository| |
|
258 |
begin |
|
259 |
repository.fetch_changesets |
|
260 |
rescue Redmine::Scm::Adapters::CommandFailed => e |
|
261 |
logger.error "scm: error during fetching changesets: #{e.message}" |
|
259 | 262 |
end |
260 | 263 |
end |
261 | 264 |
end |
... | ... | |
280 | 283 |
nil |
281 | 284 |
end |
282 | 285 | |
286 |
def self.find_existing_or_new(klass_name, attributes = {}) |
|
287 |
if repo = find_existing(klass_name, attributes) |
|
288 |
repo |
|
289 |
else |
|
290 |
factory klass_name, attributes |
|
291 |
end |
|
292 |
end |
|
293 | ||
294 |
def self.find_existing(klass_name, attributes = {}) |
|
295 |
factory(klass_name).class.find_by_url(attributes[:url]) rescue nil |
|
296 |
end |
|
297 | ||
283 | 298 |
def self.scm_adapter_class |
284 | 299 |
nil |
285 | 300 |
end |
app/models/changeset.rb (working copy) | ||
---|---|---|
34 | 34 |
acts_as_event :title => Proc.new {|o| "#{l(:label_revision)} #{o.format_identifier}" + (o.short_comments.blank? ? '' : (': ' + o.short_comments))}, |
35 | 35 |
:description => :long_comments, |
36 | 36 |
:datetime => :committed_on, |
37 |
:url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.repository.project, :rev => o.identifier}}
|
|
37 |
:url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.project, :rev => o.identifier}} |
|
38 | 38 | |
39 | 39 |
acts_as_searchable :columns => 'comments', |
40 | 40 |
:include => {:repository => :project}, |
41 |
:project_key => "#{Repository.table_name}.project_id",
|
|
41 |
:project_key => "#{Project.table_name}.id", # this will only include a matching commit in results if the user has access to the first project of the repository. hard to fix...
|
|
42 | 42 |
:date_column => 'committed_on' |
43 | 43 | |
44 | 44 |
acts_as_activity_provider :timestamp => "#{table_name}.committed_on", |
app/models/project.rb (working copy) | ||
---|---|---|
46 | 46 |
has_many :news, :dependent => :destroy, :include => :author |
47 | 47 |
has_many :issue_categories, :dependent => :delete_all, :order => "#{IssueCategory.table_name}.name" |
48 | 48 |
has_many :boards, :dependent => :destroy, :order => "position ASC" |
49 |
has_one :repository, :dependent => :destroy
|
|
49 |
belongs_to :repository
|
|
50 | 50 |
has_many :changesets, :through => :repository |
51 | 51 |
has_one :wiki, :dependent => :destroy |
52 | 52 |
# Custom field for the project issues |
... | ... | |
80 | 80 |
validates_exclusion_of :identifier, :in => %w( new ) |
81 | 81 | |
82 | 82 |
before_destroy :delete_all_members |
83 |
after_destroy :destroy_repository_if_orphaned |
|
83 | 84 | |
84 | 85 |
named_scope :has_module, lambda { |mod| { :conditions => ["#{Project.table_name}.id IN (SELECT em.project_id FROM #{EnabledModule.table_name} em WHERE em.name=?)", mod.to_s] } } |
85 | 86 |
named_scope :active, { :conditions => "#{Project.table_name}.status = #{STATUS_ACTIVE}"} |
... | ... | |
425 | 426 |
connection.delete("DELETE FROM #{mr} WHERE #{mr}.member_id IN (SELECT #{me}.id FROM #{me} WHERE #{me}.project_id = #{id})") |
426 | 427 |
Member.delete_all(['project_id = ?', id]) |
427 | 428 |
end |
429 |
|
|
430 |
# destroys the repository after deletion of the project if it isn't attached to any other projects. |
|
431 |
def destroy_repository_if_orphaned |
|
432 |
if (repo = Repository.find_by_id(repository_id)) && repo.projects.blank? |
|
433 |
repo.destroy |
|
434 |
end |
|
435 |
end |
|
428 | 436 | |
429 | 437 |
# Users/groups issues can be assigned to |
430 | 438 |
def assignable_users |
app/controllers/sys_controller.rb (working copy) | ||
---|---|---|
30 | 30 |
render :nothing => true, :status => 409 |
31 | 31 |
else |
32 | 32 |
logger.info "Repository for #{project.name} was reported to be created by #{request.remote_ip}." |
33 |
project.repository = Repository.factory(params[:vendor], params[:repository]) |
|
34 |
if project.repository && project.repository.save |
|
35 |
render :xml => project.repository.to_xml(:only => [:id, :url]), :status => 201 |
|
33 |
repository = Repository.find_existing_or_new params[:vendor], params[:repository] |
|
34 |
repository.projects << project if repository && !repository.projects.include?(project) |
|
35 |
if repository.save |
|
36 |
render :xml => repository, :status => 201 |
|
36 | 37 |
else |
37 | 38 |
render :nothing => true, :status => 422 |
38 | 39 |
end |
app/controllers/repositories_controller.rb (working copy) | ||
---|---|---|
37 | 37 |
def edit |
38 | 38 |
@repository = @project.repository |
39 | 39 |
if !@repository && !params[:repository_scm].blank? |
40 |
@repository = Repository.factory(params[:repository_scm])
|
|
41 |
@repository.project = @project if @repository
|
|
40 |
@repository = Repository.find_existing_or_new params[:repository_scm], params[:repository]
|
|
41 |
@repository.projects << @project if @repository && !@repository.projects.include?(@project)
|
|
42 | 42 |
end |
43 | 43 |
if request.post? && @repository |
44 | 44 |
p1 = params[:repository] |
db/migrate/20110928215351_invert_repository_project_relationship.rb (revision 0) | ||
---|---|---|
1 |
class InvertRepositoryProjectRelationship < ActiveRecord::Migration |
|
2 |
class OldRepository < ActiveRecord::Base |
|
3 |
set_table_name 'repositories' |
|
4 |
set_inheritance_column 'foo' |
|
5 |
belongs_to :project, :class_name => 'OldProject', :foreign_key => :project_id |
|
6 |
has_many :changesets, :dependent => :destroy, :foreign_key => :repository_id |
|
7 |
end |
|
8 |
class OldProject < ActiveRecord::Base |
|
9 |
set_table_name 'projects' |
|
10 |
has_one :repository, :class_name => 'OldRepository', :foreign_key => :project_id |
|
11 |
end |
|
12 |
class NewRepository < ActiveRecord::Base |
|
13 |
set_table_name 'repositories' |
|
14 |
set_inheritance_column 'foo' |
|
15 |
has_many :projects, :class_name => 'NewProject', :foreign_key => :repository_id |
|
16 |
end |
|
17 |
class NewProject < ActiveRecord::Base |
|
18 |
set_table_name 'projects' |
|
19 |
belongs_to :repository, :class_name => 'NewRepository', :foreign_key => :repository_id |
|
20 |
end |
|
21 | ||
22 | ||
23 |
def self.up |
|
24 |
add_column :projects, :repository_id, :integer |
|
25 | ||
26 |
# make sure multiple projects referencing the same physical repository also use the same repository record |
|
27 |
OldProject.find_each do |p| |
|
28 |
if old_repo = p.repository |
|
29 |
puts "migrating #{p.identifier}" |
|
30 |
if repo = OldRepository.find( :first, |
|
31 |
:conditions => { :url => p.repository.url, |
|
32 |
:type => p.repository['type'] }, |
|
33 |
:order => 'id ASC' ) |
|
34 |
puts "setting repo to #{repo.id} (is: #{p.repository_id})" |
|
35 |
execute "update projects set repository_id = #{repo.id} where id = #{p.id}" |
|
36 |
unless old_repo.id == repo.id |
|
37 |
# remove the now orphaned repository |
|
38 |
old_repo.destroy |
|
39 |
end |
|
40 |
else |
|
41 |
puts "repository not found for project #{p.inspcet}" |
|
42 |
end |
|
43 |
end |
|
44 |
end |
|
45 | ||
46 |
remove_column :repositories, :project_id |
|
47 |
end |
|
48 | ||
49 |
def self.down |
|
50 |
add_column :repositories, :project_id, :integer |
|
51 | ||
52 |
NewRepository.find_each do |repo| |
|
53 |
if repo.projects.size > 1 |
|
54 |
repo.projects.each do |p| |
|
55 |
r = repo.class.new(repo.attributes) |
|
56 |
r.project_id = p.id |
|
57 |
r.save! |
|
58 |
end |
|
59 |
repo.destroy |
|
60 |
elsif p = repo.projects.first |
|
61 |
repo.update_attribute :project_id, p.id |
|
62 |
end |
|
63 |
end |
|
64 |
change_column :repositories, :project_id, :integer, :null => false |
|
65 |
remove_column :projects, :repository_id |
|
66 |
end |
|
67 |
end |