Skip to content

Commit 65716ef

Browse files
authored
Merge pull request #75 from square/tsutton/feat-sparse
Add --sparse-paths for sparse checkout feature
2 parents 51742a2 + aaff7b3 commit 65716ef

File tree

4 files changed

+69
-12
lines changed

4 files changed

+69
-12
lines changed

.rubocop.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,9 @@ Metrics/MethodLength:
2424
Metrics/BlockLength:
2525
Exclude:
2626
- 'spec/**/*'
27+
28+
Metrics/CyclomaticComplexity:
29+
Max: 15
30+
31+
Metrics/PerceivedComplexity:
32+
Max: 15

README.md

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ Why fastclone?
1414
Doing lots of repeated checkouts on a specific machine?
1515

1616
| Repository | 1st Fastclone | 2nd Fastclone | git clone | cp -R |
17-
| -----------|---------------|---------------|-----------|-------|
18-
| angular.js | 8s | 3s | 6s | 0.5s |
19-
| bootstrap | 26s | 3s | 11s | 0.2s |
20-
| gradle | 25s | 9s | 19s | 6.2s |
21-
| linux | 4m 53s | 1m 6s | 3m 51s | 29s |
22-
| react.js | 18s | 3s | 8s | 0.5s |
23-
| tensorflow | 19s | 4s | 8s | 1.5s |
17+
| ---------- | ------------- | ------------- | --------- | ----- |
18+
| angular.js | 8s | 3s | 6s | 0.5s |
19+
| bootstrap | 26s | 3s | 11s | 0.2s |
20+
| gradle | 25s | 9s | 19s | 6.2s |
21+
| linux | 4m 53s | 1m 6s | 3m 51s | 29s |
22+
| react.js | 18s | 3s | 8s | 0.5s |
23+
| tensorflow | 19s | 4s | 8s | 1.5s |
2424

2525
Above times captured using `time` without verbose mode.
2626

@@ -51,6 +51,8 @@ Usage
5151
--lock-timeout N Timeout in seconds to acquire a lock on any reference repo.
5252
Default is 0 which waits indefinitely.
5353
--pre-clone-hook command An optional command that should be invoked before cloning mirror repo
54+
--sparse-paths PATHS Comma-separated list of paths for sparse checkout.
55+
Enables sparse checkout mode using git sparse-checkout.
5456

5557
Change the default `REFERENCE_REPO_DIR` environment variable if necessary.
5658

@@ -66,6 +68,11 @@ The hook is invoked with given arguments:
6668
1. path to the repo mirror location
6769
1. attempt number, 0-indexed
6870

71+
Sparse checkout support
72+
-----------------------
73+
74+
In passing `--sparse-paths`, git-fastclone will instead perform a sparse checkout, where the passed list of paths will be set up as patterns. This can be useful if you're interested only in a subset of paths in the repository.
75+
6976
How to test?
7077
------------
7178
Manual testing:

lib/git-fastclone.rb

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ class Runner
8585

8686
attr_accessor :reference_dir, :prefetch_submodules, :reference_updated, :reference_mutex,
8787
:options, :abs_clone_path, :using_local_repo, :verbose, :print_git_errors, :color,
88-
:flock_timeout_secs
88+
:flock_timeout_secs, :sparse_paths
8989

9090
def initialize
9191
# Prefetch reference repos for submodules we've seen before
@@ -115,6 +115,8 @@ def initialize
115115
self.color = false
116116

117117
self.flock_timeout_secs = 0
118+
119+
self.sparse_paths = nil
118120
end
119121

120122
def run
@@ -172,6 +174,12 @@ def parse_options
172174
'No-op when a file is missing') do |script_file|
173175
options[:pre_clone_hook] = script_file
174176
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)
182+
end
175183
end.parse!
176184
end
177185

@@ -199,6 +207,16 @@ def parse_inputs
199207
raise msg
200208
end
201209

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+
202220
self.reference_dir = ENV['REFERENCE_REPO_DIR'] || DEFAULT_REFERENCE_REPO_DIR
203221
FileUtils.mkdir_p(reference_dir)
204222

@@ -234,13 +252,23 @@ def clone(url, rev, src_dir, config)
234252
clear_clone_dest_if_needed(attempt_number, clone_dest)
235253

236254
clone_commands = ['git', 'clone', verbose ? '--verbose' : '--quiet']
237-
clone_commands << '--reference' << mirror.to_s << url.to_s << clone_dest
255+
# For sparse checkouts, clone directly from the local mirror and skip the actual checkout process
256+
# For normal clones, use --reference and clone from the remote URL
257+
if sparse_paths
258+
clone_commands.push('--no-checkout')
259+
clone_commands << mirror.to_s << clone_dest
260+
else
261+
clone_commands << '--reference' << mirror.to_s << url.to_s << clone_dest
262+
end
238263
clone_commands << '--config' << config.to_s unless config.nil?
239264
fail_on_error(*clone_commands, quiet: !verbose, print_on_failure: print_git_errors)
265+
266+
# Configure sparse checkout if enabled
267+
perform_sparse_checkout(clone_dest, rev) if sparse_paths
240268
end
241269

242-
# Only checkout if we're changing branches to a non-default branch
243-
if rev
270+
# Only checkout if we're changing branches to a non-default branch (for non-sparse clones)
271+
if !sparse_paths && rev
244272
fail_on_error('git', 'checkout', '--quiet', rev.to_s, quiet: !verbose,
245273
print_on_failure: print_git_errors,
246274
chdir: File.join(abs_clone_path, src_dir))
@@ -258,6 +286,22 @@ def clone(url, rev, src_dir, config)
258286
end
259287
end
260288

289+
def perform_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+
261305
def update_submodules(pwd, url)
262306
return unless File.exist?(File.join(abs_clone_path, pwd, '.gitmodules'))
263307

lib/git-fastclone/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22

33
# Version string for git-fastclone
44
module GitFastCloneVersion
5-
VERSION = '1.5.1'
5+
VERSION = '1.6.0'
66
end

0 commit comments

Comments
 (0)