Skip to content
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