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

Merged
merged 16 commits into from
Aug 18, 2025
Merged
Show file tree
Hide file tree
Changes from 14 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
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ jobs:
- os: ubuntu-22.04-arm
arch: 'default'
version: 'nightly'
- os: windows-latest
arch: 'default'
version: '1.7.3'
steps:
- uses: actions/checkout@v4
- uses: julia-actions/setup-julia@v2
Expand Down
6 changes: 4 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
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"
julia = "1.6"
julia = "~1.6.6, 1.7.3"

[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
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
14 changes: 14 additions & 0 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 Down Expand Up @@ -67,6 +68,19 @@ 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()
# Read path from git_cmd.env as it can be modified above
idx = findfirst(startswith("PATH="), git_cmd.env)
path = if isnothing(idx)
""
else
# dropping the `PATH=` part
git_cmd.env[idx][6:end]
end
path = vcat(dirname(Git_LFS_jll.git_lfs_path), path)
git_cmd = addenv(git_cmd, "PATH" => join(path, pathsep))::Cmd
end
Copy link
Member

Choose a reason for hiding this comment

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

Why not modifying this above where you still have access to the path variable (which is an array of strings), instead of fiddling with startswith/findfirst and such?

Copy link
Collaborator

@mortenpi mortenpi Aug 12, 2025

Choose a reason for hiding this comment

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

I think this is my "fault". Basically, 317931e#r2213866417

I don't know what the implications are for defaulting back to ENV["PATH"] on non-Windows, but not on Windows. So this is definitely correct in both cases, but it's not particularly clear why we need this complexity.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we did it above I think we would have to add code into the if statements that do different things based on the current platform. So we would duplicate the same code (adding git-lfs to the path) in multiple places which I think is less clear than doing it in one place at the end.

Copy link
Member

Choose a reason for hiding this comment

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

diff --git a/src/git_function.jl b/src/git_function.jl
index b4fd1e6..f406643 100644
--- a/src/git_function.jl
+++ b/src/git_function.jl
@@ -22,6 +22,9 @@ 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)
+    path = split(get(ENV, "PATH", ""), pathsep)
+    libpath = split(get(ENV, LIBPATH_env, ""), pathsep)
+
     git_cmd = @static if Sys.iswindows()
         Git_jll.git(; adjust_PATH, adjust_LIBPATH)::Cmd
     else
@@ -54,9 +57,6 @@ function git(; adjust_PATH::Bool = true, adjust_LIBPATH::Bool = true)
 
     # 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 = vcat(dirname(OpenSSH_jll.ssh_path), path)
         libpath = vcat(OpenSSH_jll.LIBPATH_list, libpath)
         path = vcat(dirname(Git_jll.git_path), path)
@@ -64,23 +64,16 @@ 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))
     end
 
     # Add git-lfs
     if Git_LFS_jll.is_available()
-        # Read path from git_cmd.env as it can be modified above
-        idx = findfirst(startswith("PATH="), git_cmd.env)
-        path = if isnothing(idx)
-            ""
-        else
-            # dropping the `PATH=` part
-            git_cmd.env[idx][6:end]
-        end
         path = vcat(dirname(Git_LFS_jll.git_lfs_path), path)
-        git_cmd = addenv(git_cmd, "PATH" => join(path, pathsep))::Cmd
+        unique!(filter!(!isempty, path))
     end
+
+    git_cmd = addenv(git_cmd, "PATH" => join(path, pathsep), LIBPATH_env => join(libpath, pathsep))
+
     return git_cmd
 end
 

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the suggestion, it's implemented and CI passes

Copy link
Member

Choose a reason for hiding this comment

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

Nice, I didn't even test it locally 🙂

return git_cmd
end

Expand Down
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