Skip to content

Commit 7e81de5

Browse files
author
Douwe Maan
committed
Merge branch '116-fix-fork-project-for-hashed-storage' into 'master'
Add a 'fork-repository' command that works with hashed storage Closes #116 See merge request gitlab-org/gitlab-shell!174
2 parents 76e7554 + 2e44e31 commit 7e81de5

File tree

4 files changed

+103
-1
lines changed

4 files changed

+103
-1
lines changed

CHANGELOG

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
v5.10.0
2+
- Add a 'fork-repository' command that works with hashed storage (!174)
3+
14
v5.9.4
25
- Add relative git object dir envvars to check access request
36

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
5.9.4
1+
5.10.0

lib/gitlab_projects.rb

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ def exec
6969
import_project
7070
when 'fork-project';
7171
fork_project
72+
when 'fork-repository';
73+
fork_repository
7274
when 'fetch-remote';
7375
fetch_remote
7476
when 'push-branches';
@@ -360,6 +362,35 @@ def mv_storage
360362
end
361363
end
362364

365+
def fork_repository
366+
from_path = full_path
367+
368+
new_repos_path = ARGV.shift
369+
new_full_path = ARGV.shift
370+
371+
unless new_repos_path && new_full_path
372+
$logger.error "fork-repository failed: no destination repository path provided."
373+
return false
374+
end
375+
376+
to_path = File.join(new_repos_path, new_full_path)
377+
378+
# The repository cannot already exist
379+
if File.exists?(to_path)
380+
$logger.error "fork-repository failed: destination repository <#{to_path}> already exists."
381+
return false
382+
end
383+
384+
# Ensure the namepsace / hashed storage directory exists
385+
FileUtils.mkdir_p(File.dirname(to_path), mode: 0770)
386+
387+
$logger.info "Forking repository from <#{from_path}> to <#{to_path}>."
388+
cmd = %W(git clone --bare -- #{from_path} #{to_path})
389+
system(*cmd) && self.class.create_hooks(to_path)
390+
end
391+
392+
# DEPRECATED in favour of fork_repository, which takes a source and destination
393+
# repository path and so can work with hashed storage. Remove in v6.0.0
363394
def fork_project
364395
destination_repos_path = ARGV.shift
365396

spec/gitlab_projects_spec.rb

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,74 @@ def stub_tempfile(name, filename, opts = {})
491491
end
492492
end
493493

494+
describe :fork_repository do
495+
let(:source_repos_path) { tmp_repos_path }
496+
let(:dest_repos_path) { tmp_repos_path }
497+
let(:source_repo_name) { File.join('source-namespace', repo_name) }
498+
let(:dest_repo_name) { File.join('@hashed', 'aa', 'bb', 'xyz.git') }
499+
let(:dest_repo) { File.join(dest_repos_path, dest_repo_name) }
500+
let(:dest_namespace) { File.dirname(dest_repo) }
501+
let(:gl_repo_fork) { build_gitlab_projects('fork-repository', source_repos_path, source_repo_name, dest_repos_path, dest_repo_name) }
502+
let(:gl_projects_import) { build_gitlab_projects('import-project', source_repos_path, source_repo_name, 'https://gitlab.com/gitlab-org/gitlab-test.git') }
503+
504+
before do
505+
FileUtils.mkdir_p(dest_repos_path)
506+
gl_projects_import.exec
507+
end
508+
509+
after do
510+
FileUtils.rm_rf(dest_repos_path)
511+
end
512+
513+
it "should not fork without a source repository path" do
514+
missing_arg = build_gitlab_projects('fork-repository', tmp_repos_path, source_repo_name)
515+
expect($logger).to receive(:error).with("fork-repository failed: no destination repository path provided.")
516+
expect(missing_arg.exec).to be_false
517+
end
518+
519+
it "should not fork without a destination repository path" do
520+
missing_arg = build_gitlab_projects('fork-repository', tmp_repos_path, source_repo_name, tmp_repos_path)
521+
$logger.should_receive(:error).with("fork-repository failed: no destination repository path provided.")
522+
expect(missing_arg.exec).to be_false
523+
end
524+
525+
it "should fork the repository" do
526+
expect(gl_repo_fork.exec).to be_true
527+
expect(File.exists?(dest_repo)).to be_true
528+
expect(File.exists?(File.join(dest_repo, 'hooks', 'pre-receive'))).to be_true
529+
expect(File.exists?(File.join(dest_repo, 'hooks', 'post-receive'))).to be_true
530+
end
531+
532+
it "should not fork if a project of the same name already exists" do
533+
# create a fake project at the intended destination
534+
FileUtils.mkdir_p(dest_repo)
535+
536+
# trying to fork again should fail as the repo already exists
537+
message = "fork-repository failed: destination repository <#{dest_repo}> already exists."
538+
expect($logger).to receive(:error).with(message)
539+
expect(gl_repo_fork.exec).to be_false
540+
end
541+
542+
it "should log a fork-project event" do
543+
message = "Forking repository from <#{File.join(tmp_repos_path, source_repo_name)}> to <#{dest_repo}>."
544+
expect($logger).to receive(:info).with(message)
545+
546+
expect(gl_repo_fork.exec).to be_true
547+
end
548+
549+
context 'different storages' do
550+
let(:dest_repos_path) { File.join(ROOT_PATH, 'tmp', 'alternative') }
551+
552+
it "should fork the repo" do
553+
expect(gl_repo_fork.exec).to be_true
554+
expect(File.exists?(dest_repo)).to be_true
555+
expect(File.exists?(File.join(dest_repo, 'hooks', 'pre-receive'))).to be_true
556+
expect(File.exists?(File.join(dest_repo, 'hooks', 'post-receive'))).to be_true
557+
end
558+
end
559+
end
560+
561+
494562
describe :fork_project do
495563
let(:source_repo_name) { File.join('source-namespace', repo_name) }
496564
let(:dest_repo) { File.join(tmp_repos_path, 'forked-to-namespace', repo_name) }

0 commit comments

Comments
 (0)