Skip to content

Commit b92130c

Browse files
authored
Make Git::URL.clone_to handle cloning to bare and mirror repos (#577)
Signed-off-by: James Couball <jcouball@yahoo.com>
1 parent 13471d7 commit b92130c

File tree

4 files changed

+223
-148
lines changed

4 files changed

+223
-148
lines changed

lib/git/url.rb

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def self.parse(url)
5252
end
5353
end
5454

55-
# The name `git clone` would use for the repository directory for the given URL
55+
# The directory `git clone` would use for the repository directory for the given URL
5656
#
5757
# @example
5858
# Git::URL.clone_to('https://github.com/ruby-git/ruby-git.git') #=> 'ruby-git'
@@ -61,12 +61,17 @@ def self.parse(url)
6161
#
6262
# @return [String] the name of the repository directory
6363
#
64-
def self.clone_to(url)
64+
def self.clone_to(url, bare: false, mirror: false)
6565
uri = parse(url)
6666
path_parts = uri.path.split('/')
6767
path_parts.pop if path_parts.last == '.git'
68-
69-
path_parts.last.sub(/\.git$/, '')
68+
directory = path_parts.last
69+
if bare || mirror
70+
directory += '.git' unless directory.end_with?('.git')
71+
elsif directory.end_with?('.git')
72+
directory = directory[0..-5]
73+
end
74+
directory
7075
end
7176
end
7277

tests/units/test_url.rb

Lines changed: 0 additions & 144 deletions
This file was deleted.

tests/units/test_url_clone_to.rb

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# frozen_string_literal: true
2+
3+
require 'test/unit'
4+
require File.join(File.dirname(__dir__), 'test_helper')
5+
6+
# Tests Git::URL.clone_to
7+
#
8+
class TestURLCloneTo < Test::Unit::TestCase
9+
def test_clone_to_full_repo
10+
GIT_URLS.each do |url_data|
11+
url = url_data[:url]
12+
expected_path = url_data[:expected_path]
13+
actual_path = Git::URL.clone_to(url)
14+
assert_equal(
15+
expected_path, actual_path,
16+
"Failed to determine the clone path for URL '#{url}' correctly"
17+
)
18+
end
19+
end
20+
21+
def test_clone_to_bare_repo
22+
GIT_URLS.each do |url_data|
23+
url = url_data[:url]
24+
expected_path = url_data[:expected_bare_path]
25+
actual_path = Git::URL.clone_to(url, bare: true)
26+
assert_equal(
27+
expected_path, actual_path,
28+
"Failed to determine the clone path for URL '#{url}' correctly"
29+
)
30+
end
31+
end
32+
33+
def test_clone_to_mirror_repo
34+
GIT_URLS.each do |url_data|
35+
url = url_data[:url]
36+
# The expected_path is the same for bare and mirror repos
37+
expected_path = url_data[:expected_bare_path]
38+
actual_path = Git::URL.clone_to(url, mirror: true)
39+
assert_equal(
40+
expected_path, actual_path,
41+
"Failed to determine the clone path for URL '#{url}' correctly"
42+
)
43+
end
44+
end
45+
46+
GIT_URLS = [
47+
{
48+
url: 'https://github.com/org/repo',
49+
expected_path: 'repo',
50+
expected_bare_path: 'repo.git'
51+
},
52+
{
53+
url: 'https://github.com/org/repo.git',
54+
expected_path: 'repo',
55+
expected_bare_path: 'repo.git'
56+
},
57+
{
58+
url: 'https://git.mydomain.com/org/repo/.git',
59+
expected_path: 'repo',
60+
expected_bare_path: 'repo.git'
61+
}
62+
].freeze
63+
64+
# Git::URL.clone_to makes some assumptions about how the `git` command names
65+
# the directory to clone to. This test ensures that the assumptions are
66+
# correct.
67+
#
68+
def test_git_clone_naming_assumptions
69+
in_temp_dir do |_path|
70+
setup_test_repositories
71+
72+
GIT_CLONE_COMMANDS.each do |command_data|
73+
command = command_data[:command]
74+
expected_path = command_data[:expected_path]
75+
76+
output = `#{command} 2>&1`
77+
78+
assert_match(/Cloning into (?:bare repository )?'#{expected_path}'/, output)
79+
FileUtils.rm_rf(expected_path)
80+
end
81+
end
82+
end
83+
84+
GIT_CLONE_COMMANDS = [
85+
# Clone to full repository
86+
{ command: 'git clone server/my_project', expected_path: 'my_project' },
87+
{ command: 'git clone server/my_project/.git', expected_path: 'my_project' },
88+
{ command: 'git clone server/my_project.git', expected_path: 'my_project' },
89+
90+
# Clone to bare repository
91+
{ command: 'git clone --bare server/my_project', expected_path: 'my_project.git' },
92+
{ command: 'git clone --bare server/my_project/.git', expected_path: 'my_project.git' },
93+
{ command: 'git clone --bare server/my_project.git', expected_path: 'my_project.git' },
94+
95+
# Clone to mirror repository
96+
{ command: 'git clone --mirror server/my_project', expected_path: 'my_project.git' },
97+
{ command: 'git clone --mirror server/my_project/.git', expected_path: 'my_project.git' },
98+
{ command: 'git clone --mirror server/my_project.git', expected_path: 'my_project.git' }
99+
].freeze
100+
101+
def setup_test_repositories
102+
# Create a repository to clone from
103+
Dir.mkdir 'server'
104+
remote = Git.init('server/my_project')
105+
Dir.chdir('server/my_project') do
106+
new_file('README.md', '# My New Project')
107+
remote.add
108+
remote.commit('Initial version')
109+
end
110+
111+
# Create a bare repository to clone from
112+
Git.clone('server/my_project', 'server/my_project.git', bare: true)
113+
end
114+
end

tests/units/test_url_parse.rb

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# frozen_string_literal: true
2+
3+
require 'test/unit'
4+
5+
# Tests Git::URL.parse
6+
#
7+
class TestURLParse < Test::Unit::TestCase
8+
def test_parse_with_invalid_url
9+
url = 'user@host.xz:/path/to/repo.git/'
10+
assert_raise(Addressable::URI::InvalidURIError) do
11+
Git::URL.parse(url)
12+
end
13+
end
14+
15+
def test_parse
16+
GIT_URLS.each do |url_data|
17+
url = url_data[:url]
18+
expected_uri = url_data[:expected_uri]
19+
actual_uri = Git::URL.parse(url).to_hash.delete_if { |_key, value| value.nil? }
20+
assert_equal(expected_uri, actual_uri, "Failed to parse URL '#{url}' correctly")
21+
end
22+
end
23+
24+
# For any URL, #to_s should return the url passed to Git::URL.parse(url)
25+
def test_to_s
26+
GIT_URLS.each do |url_data|
27+
url = url_data[:url]
28+
to_s = Git::URL.parse(url).to_s
29+
assert_equal(url, to_s, "Parsed URI#to_s does not return the original URL '#{url}' correctly")
30+
end
31+
end
32+
33+
GIT_URLS = [
34+
{
35+
url: 'ssh://host.xz/path/to/repo.git/',
36+
expected_uri: { scheme: 'ssh', host: 'host.xz', path: '/path/to/repo.git/' }
37+
},
38+
{
39+
url: 'ssh://host.xz:4443/path/to/repo.git/',
40+
expected_uri: { scheme: 'ssh', host: 'host.xz', port: 4443, path: '/path/to/repo.git/' }
41+
},
42+
{
43+
url: 'ssh:///path/to/repo.git/',
44+
expected_uri: { scheme: 'ssh', host: '', path: '/path/to/repo.git/' }
45+
},
46+
{
47+
url: 'user@host.xz:path/to/repo.git/',
48+
expected_uri: { scheme: 'git-alt', user: 'user', host: 'host.xz', path: '/path/to/repo.git/' }
49+
},
50+
{
51+
url: 'host.xz:path/to/repo.git/',
52+
expected_uri: { scheme: 'git-alt', host: 'host.xz', path: '/path/to/repo.git/' }
53+
},
54+
{
55+
url: 'git://host.xz:4443/path/to/repo.git/',
56+
expected_uri: { scheme: 'git', host: 'host.xz', port: 4443, path: '/path/to/repo.git/' }
57+
},
58+
{
59+
url: 'git://user@host.xz:4443/path/to/repo.git/',
60+
expected_uri: { scheme: 'git', user: 'user', host: 'host.xz', port: 4443, path: '/path/to/repo.git/' }
61+
},
62+
{
63+
url: 'https://host.xz/path/to/repo.git/',
64+
expected_uri: { scheme: 'https', host: 'host.xz', path: '/path/to/repo.git/' }
65+
},
66+
{
67+
url: 'https://host.xz:4443/path/to/repo.git/',
68+
expected_uri: { scheme: 'https', host: 'host.xz', port: 4443, path: '/path/to/repo.git/' }
69+
},
70+
{
71+
url: 'ftps://host.xz:4443/path/to/repo.git/',
72+
expected_uri: { scheme: 'ftps', host: 'host.xz', port: 4443, path: '/path/to/repo.git/' }
73+
},
74+
{
75+
url: 'ftps://host.xz:4443/path/to/repo.git/',
76+
expected_uri: { scheme: 'ftps', host: 'host.xz', port: 4443, path: '/path/to/repo.git/' }
77+
},
78+
{
79+
url: 'file:./relative-path/to/repo.git/',
80+
expected_uri: { scheme: 'file', path: './relative-path/to/repo.git/' }
81+
},
82+
{
83+
url: 'file:///path/to/repo.git/',
84+
expected_uri: { scheme: 'file', host: '', path: '/path/to/repo.git/' }
85+
},
86+
{
87+
url: 'file:///path/to/repo.git',
88+
expected_uri: { scheme: 'file', host: '', path: '/path/to/repo.git' }
89+
},
90+
{
91+
url: 'file://host.xz/path/to/repo.git',
92+
expected_uri: { scheme: 'file', host: 'host.xz', path: '/path/to/repo.git' }
93+
},
94+
{ url: '/path/to/repo.git/', expected_uri: { path: '/path/to/repo.git/' } },
95+
{ url: '/path/to/bare-repo/.git', expected_uri: { path: '/path/to/bare-repo/.git' } },
96+
{ url: 'relative-path/to/repo.git/', expected_uri: { path: 'relative-path/to/repo.git/' } },
97+
{ url: './relative-path/to/repo.git/', expected_uri: { path: './relative-path/to/repo.git/' } },
98+
{ url: '../ruby-git/.git', expected_uri: { path: '../ruby-git/.git' } }
99+
].freeze
100+
end

0 commit comments

Comments
 (0)