git-fastclone 1.5.0 → 1.6.0.pre1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/git-fastclone/version.rb +1 -1
- data/lib/git-fastclone.rb +65 -14
- data/spec/git_fastclone_runner_spec.rb +19 -2
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 691025585b07db11da08c4cba7a6a6f8adbc6fa869c4d05cb4fb4abe9f29231c
|
|
4
|
+
data.tar.gz: 68d8a58de28932d66eb99f37630bb3bb7a9ee5e23738ed6c9c50550cc72f8a74
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7594ba7271f38731ec3522e7af1f067d606ae3dc91860ffb9d0cff6a34dee62d1dee62a8c5a77d206f9fa9ef782ea44893a9542c1ef5ef5163176269a979c8cf
|
|
7
|
+
data.tar.gz: 4b0b325e9ca5c756a9efce9e28f6edc7d8a9084616e60de8e33feca4a890a9319946914eece980b192aa4dcc54da362fb93954071820b4008410c6a0abfee62c
|
data/lib/git-fastclone.rb
CHANGED
|
@@ -85,7 +85,7 @@ module GitFastClone
|
|
|
85
85
|
|
|
86
86
|
attr_accessor :reference_dir, :prefetch_submodules, :reference_updated, :reference_mutex,
|
|
87
87
|
:options, :abs_clone_path, :using_local_repo, :verbose, :print_git_errors, :color,
|
|
88
|
-
:flock_timeout_secs
|
|
88
|
+
:flock_timeout_secs, :sparse_paths
|
|
89
89
|
|
|
90
90
|
def initialize
|
|
91
91
|
# Prefetch reference repos for submodules we've seen before
|
|
@@ -115,6 +115,8 @@ module GitFastClone
|
|
|
115
115
|
self.color = false
|
|
116
116
|
|
|
117
117
|
self.flock_timeout_secs = 0
|
|
118
|
+
|
|
119
|
+
self.sparse_paths = nil
|
|
118
120
|
end
|
|
119
121
|
|
|
120
122
|
def run
|
|
@@ -162,14 +164,21 @@ module GitFastClone
|
|
|
162
164
|
options[:config] = config
|
|
163
165
|
end
|
|
164
166
|
|
|
165
|
-
opts.on('--lock-timeout N', 'Timeout in seconds to acquire a lock on any reference repo.
|
|
166
|
-
Default is 0 which waits indefinitely.') do |timeout_secs|
|
|
167
|
+
opts.on('--lock-timeout N', 'Timeout in seconds to acquire a lock on any reference repo.',
|
|
168
|
+
'Default is 0 which waits indefinitely.') do |timeout_secs|
|
|
167
169
|
self.flock_timeout_secs = timeout_secs.to_i
|
|
168
170
|
end
|
|
169
171
|
|
|
170
|
-
opts.on('--pre-clone-hook
|
|
171
|
-
'An optional
|
|
172
|
-
|
|
172
|
+
opts.on('--pre-clone-hook script_file',
|
|
173
|
+
'An optional file that should be invoked before cloning mirror repo',
|
|
174
|
+
'No-op when a file is missing') do |script_file|
|
|
175
|
+
options[:pre_clone_hook] = script_file
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
opts.on('--sparse-paths PATHS',
|
|
179
|
+
'Comma-separated list of paths for sparse checkout.',
|
|
180
|
+
'Enables sparse checkout mode using git sparse-checkout.') do |paths|
|
|
181
|
+
self.sparse_paths = paths.split(',').map(&:strip)
|
|
173
182
|
end
|
|
174
183
|
end.parse!
|
|
175
184
|
end
|
|
@@ -198,6 +207,16 @@ module GitFastClone
|
|
|
198
207
|
raise msg
|
|
199
208
|
end
|
|
200
209
|
|
|
210
|
+
# Validate that --branch is specified when using --sparse-paths
|
|
211
|
+
if sparse_paths && !options[:branch]
|
|
212
|
+
msg = "Error: --branch is required when using --sparse-paths\n" \
|
|
213
|
+
"Sparse checkouts need an explicit branch/revision to checkout.\n" \
|
|
214
|
+
'Usage: git-fastclone --sparse-paths <paths> --branch <branch> <url>'
|
|
215
|
+
raise msg.red if color
|
|
216
|
+
|
|
217
|
+
raise msg
|
|
218
|
+
end
|
|
219
|
+
|
|
201
220
|
self.reference_dir = ENV['REFERENCE_REPO_DIR'] || DEFAULT_REFERENCE_REPO_DIR
|
|
202
221
|
FileUtils.mkdir_p(reference_dir)
|
|
203
222
|
|
|
@@ -233,13 +252,23 @@ module GitFastClone
|
|
|
233
252
|
clear_clone_dest_if_needed(attempt_number, clone_dest)
|
|
234
253
|
|
|
235
254
|
clone_commands = ['git', 'clone', verbose ? '--verbose' : '--quiet']
|
|
236
|
-
clone_commands
|
|
255
|
+
clone_commands.push('--no-checkout', '--shared') if sparse_paths
|
|
256
|
+
# For sparse checkouts with --shared, clone directly from the local mirror
|
|
257
|
+
# For normal clones, use --reference and clone from the remote URL
|
|
258
|
+
if sparse_paths
|
|
259
|
+
clone_commands << mirror.to_s << clone_dest
|
|
260
|
+
else
|
|
261
|
+
clone_commands << '--reference' << mirror.to_s << url.to_s << clone_dest
|
|
262
|
+
end
|
|
237
263
|
clone_commands << '--config' << config.to_s unless config.nil?
|
|
238
264
|
fail_on_error(*clone_commands, quiet: !verbose, print_on_failure: print_git_errors)
|
|
265
|
+
|
|
266
|
+
# Configure sparse checkout if enabled
|
|
267
|
+
configure_sparse_checkout(clone_dest, rev) if sparse_paths
|
|
239
268
|
end
|
|
240
269
|
|
|
241
|
-
# Only checkout if we're changing branches to a non-default branch
|
|
242
|
-
if rev
|
|
270
|
+
# Only checkout if we're changing branches to a non-default branch (for non-sparse clones)
|
|
271
|
+
if !sparse_paths && rev
|
|
243
272
|
fail_on_error('git', 'checkout', '--quiet', rev.to_s, quiet: !verbose,
|
|
244
273
|
print_on_failure: print_git_errors,
|
|
245
274
|
chdir: File.join(abs_clone_path, src_dir))
|
|
@@ -257,6 +286,22 @@ module GitFastClone
|
|
|
257
286
|
end
|
|
258
287
|
end
|
|
259
288
|
|
|
289
|
+
def configure_sparse_checkout(clone_dest, rev)
|
|
290
|
+
puts 'Configuring sparse checkout...' if verbose
|
|
291
|
+
|
|
292
|
+
# Initialize sparse checkout with cone mode
|
|
293
|
+
fail_on_error('git', 'sparse-checkout', 'init', '--cone',
|
|
294
|
+
quiet: !verbose, print_on_failure: print_git_errors, chdir: clone_dest)
|
|
295
|
+
|
|
296
|
+
# Set the sparse paths
|
|
297
|
+
fail_on_error('git', 'sparse-checkout', 'set', *sparse_paths,
|
|
298
|
+
quiet: !verbose, print_on_failure: print_git_errors, chdir: clone_dest)
|
|
299
|
+
|
|
300
|
+
# Checkout the specified branch/revision
|
|
301
|
+
fail_on_error('git', 'checkout', '--quiet', rev.to_s,
|
|
302
|
+
quiet: !verbose, print_on_failure: print_git_errors, chdir: clone_dest)
|
|
303
|
+
end
|
|
304
|
+
|
|
260
305
|
def update_submodules(pwd, url)
|
|
261
306
|
return unless File.exist?(File.join(abs_clone_path, pwd, '.gitmodules'))
|
|
262
307
|
|
|
@@ -292,13 +337,13 @@ module GitFastClone
|
|
|
292
337
|
end
|
|
293
338
|
end
|
|
294
339
|
|
|
295
|
-
def with_reference_repo_lock(url, &
|
|
340
|
+
def with_reference_repo_lock(url, &)
|
|
296
341
|
# Sane POSIX implementations remove exclusive flocks when a process is terminated or killed
|
|
297
342
|
# We block here indefinitely. Waiting for other git-fastclone processes to release the lock.
|
|
298
343
|
# With the default timeout of 0 we will wait forever, this can be overridden on the command line.
|
|
299
344
|
lockfile = reference_repo_lock_file(url, reference_dir, using_local_repo)
|
|
300
345
|
Timeout.timeout(flock_timeout_secs) { lockfile.flock(File::LOCK_EX) }
|
|
301
|
-
with_reference_repo_thread_lock(url, &
|
|
346
|
+
with_reference_repo_thread_lock(url, &)
|
|
302
347
|
ensure
|
|
303
348
|
# Not strictly necessary to do this unlock as an ensure. If ever exception is caught outside this
|
|
304
349
|
# primitive, ensure protection may come in handy.
|
|
@@ -306,12 +351,12 @@ module GitFastClone
|
|
|
306
351
|
lockfile.close
|
|
307
352
|
end
|
|
308
353
|
|
|
309
|
-
def with_reference_repo_thread_lock(url, &
|
|
354
|
+
def with_reference_repo_thread_lock(url, &)
|
|
310
355
|
# We also need thread level locking because pre-fetch means multiple threads can
|
|
311
356
|
# attempt to update the same repository from a single git-fastclone process
|
|
312
357
|
# file locks in posix are tracked per process, not per userland thread.
|
|
313
358
|
# This gives us the equivalent of pthread_mutex around these accesses.
|
|
314
|
-
reference_mutex[reference_repo_name(url)].synchronize(&
|
|
359
|
+
reference_mutex[reference_repo_name(url)].synchronize(&)
|
|
315
360
|
end
|
|
316
361
|
|
|
317
362
|
def update_submodule_reference(url, submodule_url_list)
|
|
@@ -452,7 +497,13 @@ module GitFastClone
|
|
|
452
497
|
private def trigger_pre_clone_hook_if_needed(url, mirror, attempt_number)
|
|
453
498
|
return if Dir.exist?(mirror) || !options.include?(:pre_clone_hook)
|
|
454
499
|
|
|
455
|
-
|
|
500
|
+
hook_command = options[:pre_clone_hook]
|
|
501
|
+
unless File.exist?(File.expand_path(hook_command))
|
|
502
|
+
puts 'pre_clone_hook script is missing' if verbose
|
|
503
|
+
return
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
popen2e_wrapper(hook_command, url.to_s, mirror.to_s, attempt_number.to_s, quiet: !verbose)
|
|
456
507
|
end
|
|
457
508
|
end
|
|
458
509
|
end
|
|
@@ -45,7 +45,6 @@ describe GitFastClone::Runner do
|
|
|
45
45
|
it 'should initialize properly' do
|
|
46
46
|
stub_const('GitFastClone::DEFAULT_REFERENCE_REPO_DIR', 'new_dir')
|
|
47
47
|
|
|
48
|
-
expect(Hash).to respond_to(:new).with(2).arguments
|
|
49
48
|
expect(GitFastClone::DEFAULT_REFERENCE_REPO_DIR).to eq('new_dir')
|
|
50
49
|
expect(subject.prefetch_submodules).to eq(true)
|
|
51
50
|
expect(subject.reference_mutex).to eq({})
|
|
@@ -146,11 +145,13 @@ describe GitFastClone::Runner do
|
|
|
146
145
|
end
|
|
147
146
|
end
|
|
148
147
|
|
|
149
|
-
context 'with pre-clone-hook
|
|
148
|
+
context 'with pre-clone-hook' do
|
|
150
149
|
let(:pre_clone_hook) { '/some/command' }
|
|
151
150
|
before(:each) do
|
|
152
151
|
subject.options[:pre_clone_hook] = pre_clone_hook
|
|
153
152
|
subject.reference_dir = placeholder_arg
|
|
153
|
+
allow(File).to receive(:exist?).and_call_original
|
|
154
|
+
allow(File).to receive(:exist?).with(pre_clone_hook).and_return(true)
|
|
154
155
|
allow(subject).to receive(:with_git_mirror).and_call_original
|
|
155
156
|
allow(subject).to receive(:with_reference_repo_lock) do |_url, &block|
|
|
156
157
|
block.call
|
|
@@ -192,6 +193,22 @@ describe GitFastClone::Runner do
|
|
|
192
193
|
|
|
193
194
|
subject.clone(placeholder_arg, nil, '.', 'config')
|
|
194
195
|
end
|
|
196
|
+
|
|
197
|
+
context 'non-existing script' do
|
|
198
|
+
before(:each) do
|
|
199
|
+
allow(File).to receive(:exist?).with(pre_clone_hook).and_return(false)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
it 'does not invoke hook command' do
|
|
203
|
+
allow(subject).to receive(:fail_on_error)
|
|
204
|
+
expect(subject).not_to receive(:popen2e_wrapper).with(
|
|
205
|
+
pre_clone_hook, 'PH', 'PH/PH', '0',
|
|
206
|
+
{ quiet: true }
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
subject.clone(placeholder_arg, nil, '.', 'config')
|
|
210
|
+
end
|
|
211
|
+
end
|
|
195
212
|
end
|
|
196
213
|
end
|
|
197
214
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: git-fastclone
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.6.0.pre1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Michael Tauraso
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date:
|
|
12
|
+
date: 2025-11-10 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: colorize
|
|
@@ -59,14 +59,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
59
59
|
requirements:
|
|
60
60
|
- - ">="
|
|
61
61
|
- !ruby/object:Gem::Version
|
|
62
|
-
version: '2
|
|
62
|
+
version: '3.2'
|
|
63
63
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
64
64
|
requirements:
|
|
65
65
|
- - ">="
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
67
|
version: '0'
|
|
68
68
|
requirements: []
|
|
69
|
-
rubygems_version: 3.
|
|
69
|
+
rubygems_version: 3.5.22
|
|
70
70
|
signing_key:
|
|
71
71
|
specification_version: 4
|
|
72
72
|
summary: git-clone --recursive on steroids!
|