GitLab forking sometimes will not work due to wide fork_network search.
I got this problem on my self-hosted Gitlab on 14.6.7 and I just checked the latest source code and it seems not be fixed on the main branch so I raise this ticket. I did not try to reproduce it on the latest branch but I can reproduce this on the self-hosted GitLab server.
=== symptoms ===
Suppose a user has a few projects on his namespace.
- project 2-1.
- project 2-2.
- random projects. ...
Now this user wants to fork project 1-3. project 1-3 is a child of project root.
project root has the child project like project1-1, project 1-2, project 1-3.
project 1-1 has a child project as project 2-1.
project 1-2 has a child project as project 2-2.
Now when the user was trying to fork from project 1-3, GitLab will try to find the fork_network of project 1-3, meaning that it will find project 1-1, project 1-2, project 2-1, project 2-2, project root, and so on.
In these projects, it will also compare if any project exists under the user which means project 2-1 and project 2-2. If it is, then the code thinks it has found a fork of this project. However, this should not be true because project 1-3 has never been forked before.
=== revelent code ===
app/models/manespace.rb
def find_fork_of(project)
return unless project.fork_network
if Gitlab::SafeRequestStore.active?
forks_in_namespace = Gitlab::SafeRequestStore.fetch("namespaces:#{id}:forked_projects") do
Hash.new do |found_forks, project|
found_forks[project] = project.fork_network.find_forks_in(projects).first
end
end
forks_in_namespace[project]
else
project.fork_network.find_forks_in(projects).first
end
endIt says when it is trying to find the fork of the project, it will search this project's fork_network instead of the project and its child only.
== impact ==
It should be pretty small as there is a workaround for us to do.
app/views/projects/buttons/_fork.html.haml
- if current_user.already_forked?(@project) && current_user.forkable_namespaces.size < 2
= link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: s_('ProjectOverview|Go to your fork'), class: 'gl-button btn btn-default btn-sm has-tooltip fork-btn' do
= sprite_icon('fork', css_class: 'icon')
%span= s_('ProjectOverview|Fork')
- elseThe file will also check if the size of a user's namespace is smaller than 2 so I think I can just let the user to be able to join another group to avoid the check.