Skip to content

Support git-lfs (large file storage) #64

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

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
8 changes: 7 additions & 1 deletion 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 Down Expand Up @@ -52,8 +53,8 @@ function git(; adjust_PATH::Bool = true, adjust_LIBPATH::Bool = true)
end

# Use OpenSSH from the JLL: <https://github.com/JuliaVersionControl/Git.jl/issues/51>.
path = split(get(ENV, "PATH", ""), pathsep)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this might be incorrect? On Windows, this means that git will now use the system ENV, rather than the one set by the JLL, right? So it's missing the relevant JLL paths? Which might also be the reason for the CI failure?

I was just implementing my own version of this PR (as I hadn't notice this one 😅), and I ended up parsing git_cmd.env to ensure that I push to the correct path always. It's a bit noisy and ugly, but it was looking like this:

function _push_git_lfs_to_path!(git_cmd::Cmd)
    GitLFSExt = Base.get_extension(@__MODULE__, :GitLFSExt)
    if isnothing(GitLFSExt)
        throw(GitLFSNotLoadedError())
    end

    idx = findfirst(startswith("PATH="), git_cmd.env)
    path = if isnothing(idx)
        ""
    else
        # dropping the `PATH=` part
        git_cmd.env[idx][6:end]
    end
    path = split(get(ENV, LIBPATH_env, ""), path)
    pushfirst!(path, GitLFSExt.git_lfs_path())

    return addenv(git_cmd, "PATH" => join(path, pathsep))
end

A simpler approach might be to pull something like

path = vcat(dirname(Git_jll.git_path), split(get(ENV, "PATH", ""), pathsep))
libpath = vcat(Git_jll.LIBPATH_list, split(get(ENV, LIBPATH_env, ""), pathsep))

out of the if and conditionally only add the OpenSSH paths in the if? But I wasn't 100% sure that's correct either, so I opted for the more convoluted approach where I know I am only minimally changing the PATH variable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yeah, I pulled path = split(get(ENV, "PATH", ""), pathsep) out of a block that only runs if the system is not windows so it would make sense I could have missed something like this. The part that confused me was why it passed on the Windows runs with more recent Julia versions and failed on the Julia 1.6 run.

I'll try to give this another try tonight or tomorrow. If you see an easy solution, you're welcome to submit a PR against my branch here if you'd like.

Copy link
Contributor Author

@cgarling cgarling Jul 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @mortenpi, thanks for the suggestion! I think I have something working based on your solution that I'm about to push to see if it fixes the CI errors, but I'm not sure what your line

path = split(get(ENV, LIBPATH_env, ""), path)

is supposed to do and it was causing errors so I removed it. LMK of any other changes you would suggest.

Edit: new commit did not fix CI errors -.-

if !Sys.iswindows() && OpenSSH_jll.is_available()
path = split(get(ENV, "PATH", ""), pathsep)
libpath = split(get(ENV, LIBPATH_env, ""), pathsep)

path = vcat(dirname(OpenSSH_jll.ssh_path), path)
Expand All @@ -67,6 +68,11 @@ function git(; adjust_PATH::Bool = true, adjust_LIBPATH::Bool = true)
git_cmd = addenv(git_cmd, "PATH" => join(path, pathsep), LIBPATH_env => join(libpath, pathsep))
end

# Add git-lfs
if Git_LFS_jll.is_available()
path = vcat(dirname(Git_LFS_jll.git_lfs_path), path)
git_cmd = addenv(git_cmd, "PATH" => join(path, pathsep))
end
return git_cmd
end

Expand Down
18 changes: 16 additions & 2 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 Expand Up @@ -76,12 +90,12 @@ end
@testset "OpenSSH integration" begin
is_ci = parse(Bool, strip(get(ENV, "CI", "false")))
is_gha = parse(Bool, strip(get(ENV, "GITHUB_ACTIONS", "false")))
if is_ci && is_gha
ssh_privkey = get(ENV, "CI_READONLY_DEPLOYKEY_FOR_CI_TESTSUITE_PRIVATEKEY", nothing)
if is_ci && is_gha && !isnothing(ssh_privkey)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is logically the same as b98ca06, not a great surprise this doesn't work either. My suggestion was

ssh_privkey = get(ENV, "CI_READONLY_DEPLOYKEY_FOR_CI_TESTSUITE_PRIVATEKEY", "")
if !isempty(ssh_privkey

Also, the other checks are useless at this point.

@info "This is GitHub Actions CI, so running the OpenSSH test..."
mktempdir() do sshprivkeydir
privkey_filepath = joinpath(sshprivkeydir, "my_private_key")
open(privkey_filepath, "w") do io
ssh_privkey = ENV["CI_READONLY_DEPLOYKEY_FOR_CI_TESTSUITE_PRIVATEKEY"]
println(io, ssh_privkey)
end # open
# We need to chmod our private key to 600, or SSH will ignore it.
Expand Down
Loading