Skip to content

Commit 88019cf

Browse files
committed
Make Git::URL.clone_to handle cloning to bare and mirror repos
Signed-off-by: James Couball <jcouball@yahoo.com>
1 parent 13471d7 commit 88019cf

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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy