Support git partial clones with sparse checkouts

This commit is contained in:
Harry Marr 2022-05-02 19:53:44 -04:00
parent 2bec760529
commit 7df90eb7e1
No known key found for this signature in database
GPG Key ID: EC359447F66859CC
2 changed files with 38 additions and 2 deletions

View File

@ -15,7 +15,7 @@ class URL < Delegator
:verified, :using, :verified, :using,
:tag, :branch, :revisions, :revision, :tag, :branch, :revisions, :revision,
:trust_cert, :cookies, :referer, :header, :user_agent, :trust_cert, :cookies, :referer, :header, :user_agent,
:data :data, :only_paths
extend Forwardable extend Forwardable
def_delegators :uri, :path, :scheme, :to_s def_delegators :uri, :path, :scheme, :to_s
@ -36,6 +36,7 @@ class URL < Delegator
header: T.nilable(String), header: T.nilable(String),
user_agent: T.nilable(T.any(Symbol, String)), user_agent: T.nilable(T.any(Symbol, String)),
data: T.nilable(T::Hash[String, String]), data: T.nilable(T::Hash[String, String]),
only_paths: T.nilable(T::Array[String]),
).void ).void
} }
def initialize( def initialize(
@ -51,7 +52,8 @@ class URL < Delegator
referer: nil, referer: nil,
header: nil, header: nil,
user_agent: nil, user_agent: nil,
data: nil data: nil,
only_paths: nil
) )
@uri = URI(uri) @uri = URI(uri)
@ -69,6 +71,7 @@ class URL < Delegator
specs[:header] = @header = header specs[:header] = @header = header
specs[:user_agent] = @user_agent = user_agent || :default specs[:user_agent] = @user_agent = user_agent || :default
specs[:data] = @data = data specs[:data] = @data = data
specs[:only_paths] = @only_paths = only_paths
@specs = specs.compact @specs = specs.compact
end end
@ -156,6 +159,7 @@ class URL < Delegator
header: T.nilable(String), header: T.nilable(String),
user_agent: T.nilable(T.any(Symbol, String)), user_agent: T.nilable(T.any(Symbol, String)),
data: T.nilable(T::Hash[String, String]), data: T.nilable(T::Hash[String, String]),
only_paths: T.nilable(T::Array[String]),
caller_location: Thread::Backtrace::Location, caller_location: Thread::Backtrace::Location,
dsl: T.nilable(Cask::DSL), dsl: T.nilable(Cask::DSL),
block: T.nilable(T.proc.params(arg0: T.all(String, BlockDSL::PageWithURL)).returns(T.untyped)), block: T.nilable(T.proc.params(arg0: T.all(String, BlockDSL::PageWithURL)).returns(T.untyped)),
@ -175,6 +179,7 @@ class URL < Delegator
header: nil, header: nil,
user_agent: nil, user_agent: nil,
data: nil, data: nil,
only_paths: nil,
caller_location: T.must(caller_locations).fetch(0), caller_location: T.must(caller_locations).fetch(0),
dsl: nil, dsl: nil,
&block &block
@ -202,6 +207,7 @@ class URL < Delegator
header: header, header: header,
user_agent: user_agent, user_agent: user_agent,
data: data, data: data,
only_paths: only_paths,
) )
end end
) )

View File

@ -814,6 +814,7 @@ class GitDownloadStrategy < VCSDownloadStrategy
super super
@ref_type ||= :branch @ref_type ||= :branch
@ref ||= "master" @ref ||= "master"
@only_paths = meta[:only_paths]
end end
# @see AbstractDownloadStrategy#source_modified_time # @see AbstractDownloadStrategy#source_modified_time
@ -880,6 +881,14 @@ class GitDownloadStrategy < VCSDownloadStrategy
(cached_location/".gitmodules").exist? (cached_location/".gitmodules").exist?
end end
def partial_clone_sparse_checkout?
return false if @only_paths.nil?
# There is some support for partial clones prior to 2.20, but we avoid using it
# due to performance issues
Version.create(Utils::Git.version) >= Version.create("2.20.0")
end
sig { returns(T::Array[String]) } sig { returns(T::Array[String]) }
def clone_args def clone_args
args = %w[clone] args = %w[clone]
@ -889,6 +898,8 @@ class GitDownloadStrategy < VCSDownloadStrategy
args << "--branch" << @ref args << "--branch" << @ref
end end
args << "--no-checkout" << "--filter=blob:none" if partial_clone_sparse_checkout?
args << "-c" << "advice.detachedHead=false" # silences detached head warning args << "-c" << "advice.detachedHead=false" # silences detached head warning
args << @url << cached_location args << @url << cached_location
end end
@ -922,6 +933,13 @@ class GitDownloadStrategy < VCSDownloadStrategy
command! "git", command! "git",
args: ["config", "advice.detachedHead", "false"], args: ["config", "advice.detachedHead", "false"],
chdir: cached_location chdir: cached_location
if partial_clone_sparse_checkout?
command! "git",
args: ["config", "origin.partialclonefilter", "blob:none"],
chdir: cached_location
configure_sparse_checkout
end
end end
sig { params(timeout: T.nilable(Time)).void } sig { params(timeout: T.nilable(Time)).void }
@ -950,6 +968,9 @@ class GitDownloadStrategy < VCSDownloadStrategy
args: ["config", "homebrew.cacheversion", cache_version], args: ["config", "homebrew.cacheversion", cache_version],
chdir: cached_location, chdir: cached_location,
timeout: timeout&.remaining timeout: timeout&.remaining
configure_sparse_checkout if partial_clone_sparse_checkout?
checkout(timeout: timeout) checkout(timeout: timeout)
update_submodules(timeout: timeout) if submodules? update_submodules(timeout: timeout) if submodules?
end end
@ -1020,6 +1041,15 @@ class GitDownloadStrategy < VCSDownloadStrategy
dot_git.atomic_write("gitdir: #{relative_git_dir}\n") dot_git.atomic_write("gitdir: #{relative_git_dir}\n")
end end
end end
def configure_sparse_checkout
command! "git",
args: ["config", "core.sparseCheckout", "true"],
chdir: cached_location
sparse_checkout_paths = @only_paths.join("\n") + "\n"
(git_dir/"info"/"sparse-checkout").atomic_write(sparse_checkout_paths)
end
end end
# Strategy for downloading a Git repository from GitHub. # Strategy for downloading a Git repository from GitHub.