diff options
author | Douwe Maan <douwe@gitlab.com> | 2017-10-07 10:56:24 +0000 |
---|---|---|
committer | Douwe Maan <douwe@gitlab.com> | 2017-10-07 10:56:24 +0000 |
commit | 3cb798357fbfb8b3230b60130fc1c6b328ae84b7 (patch) | |
tree | ed86ce7199537e12a3ddf9ab71a2832b3da28621 /app | |
parent | 8eec69ef41ed66c2ef3c5db73f97eb39a9458f72 (diff) | |
parent | fc51bde939a24347e21b765b8c0e4238e2ffc67d (diff) | |
download | gitlab-ce-3cb798357fbfb8b3230b60130fc1c6b328ae84b7.tar.gz |
Merge branch 'bvl-fork-network-schema' into 'master'
Create merge requests across a fork network
Closes #20097
See merge request gitlab-org/gitlab-ce!14422
Diffstat (limited to 'app')
-rw-r--r-- | app/controllers/projects/merge_requests/creations_controller.rb | 7 | ||||
-rw-r--r-- | app/finders/merge_request_target_project_finder.rb | 18 | ||||
-rw-r--r-- | app/helpers/merge_requests_helper.rb | 3 | ||||
-rw-r--r-- | app/models/fork_network.rb | 15 | ||||
-rw-r--r-- | app/models/fork_network_member.rb | 7 | ||||
-rw-r--r-- | app/models/merge_request.rb | 2 | ||||
-rw-r--r-- | app/models/namespace.rb | 4 | ||||
-rw-r--r-- | app/models/project.rb | 29 | ||||
-rw-r--r-- | app/models/user.rb | 10 | ||||
-rw-r--r-- | app/services/projects/destroy_service.rb | 7 | ||||
-rw-r--r-- | app/services/projects/fork_service.rb | 20 | ||||
-rw-r--r-- | app/services/projects/unlink_fork_service.rb | 1 | ||||
-rw-r--r-- | app/views/projects/_home_panel.html.haml | 14 |
13 files changed, 117 insertions, 20 deletions
diff --git a/app/controllers/projects/merge_requests/creations_controller.rb b/app/controllers/projects/merge_requests/creations_controller.rb index 1096afbb798..99dc3dda9e7 100644 --- a/app/controllers/projects/merge_requests/creations_controller.rb +++ b/app/controllers/projects/merge_requests/creations_controller.rb @@ -120,10 +120,13 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap end def selected_target_project - if @project.id.to_s == params[:target_project_id] || @project.forked_project_link.nil? + if @project.id.to_s == params[:target_project_id] || !@project.forked? @project + elsif params[:target_project_id].present? + MergeRequestTargetProjectFinder.new(current_user: current_user, source_project: @project) + .execute.find(params[:target_project_id]) else - @project.forked_project_link.forked_from_project + @project.forked_from_project end end end diff --git a/app/finders/merge_request_target_project_finder.rb b/app/finders/merge_request_target_project_finder.rb new file mode 100644 index 00000000000..189eb3847eb --- /dev/null +++ b/app/finders/merge_request_target_project_finder.rb @@ -0,0 +1,18 @@ +class MergeRequestTargetProjectFinder + attr_reader :current_user, :source_project + + def initialize(current_user: nil, source_project:) + @current_user = current_user + @source_project = source_project + end + + def execute + if @source_project.fork_network + @source_project.fork_network.projects + .public_or_visible_to_user(current_user) + .with_feature_available_for_user(:merge_requests, current_user) + else + Project.where(id: source_project) + end + end +end diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index c31023f2d9a..5b2c58d193d 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -73,7 +73,8 @@ module MergeRequestsHelper end def target_projects(project) - [project, project.default_merge_request_target].uniq + MergeRequestTargetProjectFinder.new(current_user: current_user, source_project: project) + .execute end def merge_request_button_visibility(merge_request, closed) diff --git a/app/models/fork_network.rb b/app/models/fork_network.rb new file mode 100644 index 00000000000..218e37a5312 --- /dev/null +++ b/app/models/fork_network.rb @@ -0,0 +1,15 @@ +class ForkNetwork < ActiveRecord::Base + belongs_to :root_project, class_name: 'Project' + has_many :fork_network_members + has_many :projects, through: :fork_network_members + + after_create :add_root_as_member, if: :root_project + + def add_root_as_member + projects << root_project + end + + def find_forks_in(other_projects) + projects.where(id: other_projects) + end +end diff --git a/app/models/fork_network_member.rb b/app/models/fork_network_member.rb new file mode 100644 index 00000000000..6a9b52a1ef8 --- /dev/null +++ b/app/models/fork_network_member.rb @@ -0,0 +1,7 @@ +class ForkNetworkMember < ActiveRecord::Base + belongs_to :fork_network + belongs_to :project + belongs_to :forked_from_project, class_name: 'Project' + + validates :fork_network, :project, presence: true +end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 086226618e6..9b312f7db6c 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -403,7 +403,7 @@ class MergeRequest < ActiveRecord::Base return false unless for_fork? return true unless source_project - !source_project.forked_from?(target_project) + !source_project.in_fork_network_of?(target_project) end def reopenable? diff --git a/app/models/namespace.rb b/app/models/namespace.rb index e279d8dd8c5..4672881e220 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -139,7 +139,9 @@ class Namespace < ActiveRecord::Base end def find_fork_of(project) - projects.joins(:forked_project_link).find_by('forked_project_links.forked_from_project_id = ?', project.id) + return nil unless project.fork_network + + project.fork_network.find_forks_in(projects).first end def lfs_enabled? diff --git a/app/models/project.rb b/app/models/project.rb index 608b545f99f..ad1c339ae78 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -118,11 +118,20 @@ class Project < ActiveRecord::Base has_one :mock_monitoring_service has_one :microsoft_teams_service + # TODO: replace these relations with the fork network versions has_one :forked_project_link, foreign_key: "forked_to_project_id" has_one :forked_from_project, through: :forked_project_link has_many :forked_project_links, foreign_key: "forked_from_project_id" has_many :forks, through: :forked_project_links, source: :forked_to_project + # TODO: replace these relations with the fork network versions + + has_one :root_of_fork_network, + foreign_key: 'root_project_id', + inverse_of: :root_project, + class_name: 'ForkNetwork' + has_one :fork_network_member + has_one :fork_network, through: :fork_network_member # Merge Requests for target project should be removed with it has_many :merge_requests, foreign_key: 'target_project_id' @@ -1000,6 +1009,11 @@ class Project < ActiveRecord::Base end def forked? + return true if fork_network && fork_network.root_project != self + + # TODO: Use only the above conditional using the `fork_network` + # This is the old conditional that looks at the `forked_project_link`, we + # fall back to this while we're migrating the new models !(forked_project_link.nil? || forked_project_link.forked_from_project.nil?) end @@ -1119,8 +1133,19 @@ class Project < ActiveRecord::Base end end - def forked_from?(project) - forked? && project == forked_from_project + def forked_from?(other_project) + forked? && forked_from_project == other_project + end + + def in_fork_network_of?(other_project) + # TODO: Remove this in a next release when all fork_networks are populated + # This makes sure all MergeRequests remain valid while the projects don't + # have a fork_network yet. + return true if forked_from?(other_project) + + return false if fork_network.nil? || other_project.fork_network.nil? + + fork_network == other_project.fork_network end def origin_merge_requests diff --git a/app/models/user.rb b/app/models/user.rb index 959738ba608..c3f115ca074 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -697,15 +697,7 @@ class User < ActiveRecord::Base end def fork_of(project) - links = ForkedProjectLink.where( - forked_from_project_id: project, - forked_to_project_id: personal_projects.unscope(:order) - ) - if links.any? - links.first.forked_to_project - else - nil - end + namespace.find_fork_of(project) end def ldap_user? diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb index 54eb75ab9bf..19d75ff2efa 100644 --- a/app/services/projects/destroy_service.rb +++ b/app/services/projects/destroy_service.rb @@ -22,6 +22,13 @@ module Projects Projects::UnlinkForkService.new(project, current_user).execute + # The project is not necessarily a fork, so update the fork network originating + # from this project + if fork_network = project.root_of_fork_network + fork_network.update(root_project: nil, + deleted_root_project_name: project.full_name) + end + attempt_destroy_transaction(project) system_hook_service.execute_hooks_for(project, :destroy) diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb index ad67e68a86a..eb5cce5ab98 100644 --- a/app/services/projects/fork_service.rb +++ b/app/services/projects/fork_service.rb @@ -23,11 +23,31 @@ module Projects refresh_forks_count + link_fork_network(new_project) + new_project end private + def fork_network + if @project.fork_network + @project.fork_network + elsif forked_from_project = @project.forked_from_project + # TODO: remove this case when all background migrations have completed + # this only happens when a project had a `forked_project_link` that was + # not migrated to the `fork_network` relation + forked_from_project.fork_network || forked_from_project.create_root_of_fork_network + else + @project.create_root_of_fork_network + end + end + + def link_fork_network(new_project) + fork_network.fork_network_members.create(project: new_project, + forked_from_project: @project) + end + def refresh_forks_count Projects::ForksCountService.new(@project).refresh_cache end diff --git a/app/services/projects/unlink_fork_service.rb b/app/services/projects/unlink_fork_service.rb index f30b40423c8..abe414d0c05 100644 --- a/app/services/projects/unlink_fork_service.rb +++ b/app/services/projects/unlink_fork_service.rb @@ -16,6 +16,7 @@ module Projects refresh_forks_count(@project.forked_from_project) @project.forked_project_link.destroy + @project.fork_network_member.destroy end def refresh_forks_count(project) diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index 873b3045ea9..619b632918e 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -1,4 +1,6 @@ - empty_repo = @project.empty_repo? +- fork_network = @project.fork_network +- forked_from_project = @project.forked_from_project || fork_network&.root_project .project-home-panel.text-center{ class: ("empty-project" if empty_repo) } .limit-container-width{ class: container_class } .avatar-container.s70.project-avatar @@ -12,11 +14,15 @@ - if @project.description.present? = markdown_field(@project, :description) - - if forked_from_project = @project.forked_from_project + - if @project.forked? %p - #{ s_('ForkedFromProjectPath|Forked from') } - = link_to project_path(forked_from_project) do - = forked_from_project.namespace.try(:name) + - if forked_from_project + #{ s_('ForkedFromProjectPath|Forked from') } + = link_to project_path(forked_from_project) do + = forked_from_project.full_name + - else + - deleted_message = s_('ForkedFromProjectPath|Forked from %{project_name} (deleted)') + = deleted_message % { project_name: fork_network.deleted_root_project_name } .project-repo-buttons .count-buttons |