Skip to content

Git LFS support via package extension #65

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 12 commits into from
4 changes: 3 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
name = "Git"
uuid = "d7ba0133-e1db-5d97-8f8c-041e4b3a1eb2"
version = "1.4.0"
authors = ["Dilum Aluthge", "contributors"]
version = "1.5.0"

[deps]
Git_LFS_jll = "020c3dae-16b3-5ae5-87b3-4cb189e250b2"
Git_jll = "f8c6e375-362e-5223-8a59-34ff63f689eb"
JLLWrappers = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210"
OpenSSH_jll = "9bd350c2-7e96-507f-8002-3f2e150b4e1b"

[compat]
Git_LFS_jll = "3.7"
Git_jll = "2.44"
JLLWrappers = "1.1"
OpenSSH_jll = "9, 10"
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ not need to have Git installed on your computer, and neither do the users of
your packages!

Git.jl provides a Git binary via
[Git_jll.jl](https://github.com/JuliaBinaryWrappers/Git_jll.jl).
[Git_jll.jl](https://github.com/JuliaBinaryWrappers/Git_jll.jl)
and a Git LFS binary for large file support via
[Git_LFS_jll.jl](https://github.com/JuliaBinaryWrappers/Git_LFS_jll.jl).
The latest version of Git.jl requires at least Julia 1.6.

Git.jl is intended to work on any platform that supports Julia,
Expand Down
44 changes: 33 additions & 11 deletions src/git_function.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using OpenSSH_jll: OpenSSH_jll
using Git_LFS_jll: Git_LFS_jll
using JLLWrappers: pathsep, LIBPATH_env

"""
Expand All @@ -21,9 +22,12 @@ julia> run(git(["clone", "https://github.com/JuliaRegistries/General"]))
to bypass the parsing of the command string.
"""
function git(; adjust_PATH::Bool = true, adjust_LIBPATH::Bool = true)
git_cmd = @static if Sys.iswindows()
Git_jll.git(; adjust_PATH, adjust_LIBPATH)::Cmd
else
git_cmd = Git_jll.git(; adjust_PATH, adjust_LIBPATH)::Cmd
env_mapping = Dict{String,String}(
"PATH" => _get_cmd_env(git_cmd, "PATH"),
LIBPATH_env => _get_cmd_env(git_cmd, LIBPATH_env),
)
@static if !Sys.iswindows()
root = Git_jll.artifact_dir

libexec = joinpath(root, "libexec")
Expand All @@ -35,7 +39,6 @@ function git(; adjust_PATH::Bool = true, adjust_LIBPATH::Bool = true)

ssl_cert = joinpath(dirname(Sys.BINDIR), "share", "julia", "cert.pem")

env_mapping = Dict{String,String}()
env_mapping["GIT_EXEC_PATH"] = libexec_git_core
env_mapping["GIT_SSL_CAINFO"] = ssl_cert
env_mapping["GIT_TEMPLATE_DIR"] = share_git_core_templates
Expand All @@ -46,15 +49,12 @@ function git(; adjust_PATH::Bool = true, adjust_LIBPATH::Bool = true)
# more details.
env_mapping["JLL_DYLD_FALLBACK_LIBRARY_PATH"] = Git_jll.LIBPATH[]
end

original_cmd = Git_jll.git(; adjust_PATH, adjust_LIBPATH)::Cmd
addenv(original_cmd, env_mapping...)::Cmd
end

# Use OpenSSH from the JLL: <https://github.com/JuliaVersionControl/Git.jl/issues/51>.
if !Sys.iswindows() && OpenSSH_jll.is_available()
path = split(get(ENV, "PATH", ""), pathsep)
libpath = split(get(ENV, LIBPATH_env, ""), pathsep)
path = split(get(env_mapping, "PATH", ""), pathsep)
libpath = split(get(env_mapping, LIBPATH_env, ""), pathsep)

path = vcat(dirname(OpenSSH_jll.ssh_path), path)
libpath = vcat(OpenSSH_jll.LIBPATH_list, libpath)
Expand All @@ -64,14 +64,36 @@ function git(; adjust_PATH::Bool = true, adjust_LIBPATH::Bool = true)
unique!(filter!(!isempty, path))
unique!(filter!(!isempty, libpath))

git_cmd = addenv(git_cmd, "PATH" => join(path, pathsep), LIBPATH_env => join(libpath, pathsep))
env_mapping["PATH"] = join(path, pathsep)
env_mapping[LIBPATH_env] = join(libpath, pathsep)
end

# Add git-lfs
if Git_LFS_jll.is_available()
env_mapping["PATH"] = string(
dirname(Git_LFS_jll.git_lfs_path),
pathsep,
get(env_mapping, "PATH", "")
)
end

return git_cmd
return addenv(git_cmd, env_mapping...)::Cmd
end

function git(args::AbstractVector{<:AbstractString}; kwargs...)
cmd = git(; kwargs...)
append!(cmd.exec, args)
return cmd
end

# The .env field of a Cmd object is an array of strings in the format
# `$(key)=$(value)` for each environment variable.
function _get_cmd_env(cmd::Cmd, key::AbstractString)
idx = findfirst(startswith("$(key)="), cmd.env)
if isnothing(idx)
return ""
else
# dropping the `$(key)=` part
return cmd.env[idx][(ncodeunits(key)+2):end]
end
end
14 changes: 14 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,20 @@ end
@test isdir("Git.jl")
@test isfile(joinpath("Git.jl", "Project.toml"))
end

# git-lfs tests
withtempdir() do tmp_dir
rname = "repo-with-large-file-storage"
@test !isdir(rname)
@test !isfile(joinpath(rname, "LargeFile.zip"))
run(`$(git()) clone --quiet https://github.com/Apress/repo-with-large-file-storage`)
run(pipeline(`$(git()) -C $rname lfs install --local`; stdout=devnull))
run(pipeline(`$(git()) -C $rname lfs pull`; stdout=devnull))
@test isdir(rname)
@test isfile(joinpath(rname, "LargeFile.zip"))
# Test filesize to make sure we got real file and not small LFS pointer file
@test filesize(joinpath(rname, "LargeFile.zip")) > 10^6
end
end

@testset "Safety" begin
Expand Down
Loading