Skip to content

Commit 89f4b01

Browse files
committed
show a progress bar when cloning git repos
1 parent b20636d commit 89f4b01

File tree

5 files changed

+138
-9
lines changed

5 files changed

+138
-9
lines changed

src/GitTools.jl

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
module GitTools
2+
3+
import LibGit2
4+
using Printf
5+
6+
Base.@kwdef mutable struct MiniProgressBar
7+
max::Float64 = 1
8+
header::String = ""
9+
color::Symbol = :white
10+
width::Int = 40
11+
current::Float64 = 0.0
12+
prev::Float64 = 0.0
13+
has_shown::Bool = false
14+
end
15+
16+
function showprogress(io::IO, p::MiniProgressBar)
17+
perc = p.current / p.max * 100
18+
prev_perc = p.prev / p.max * 100
19+
if p.has_shown && !(perc - prev_perc > 0.1)
20+
return
21+
end
22+
p.prev = p.current
23+
p.has_shown = true
24+
n_filled = ceil(Int, p.width * perc / 100)
25+
n_left = p.width - n_filled
26+
print(io, " ")
27+
printstyled(io, p.header, color=p.color, bold=true)
28+
print(io, " [")
29+
print(io, "="^n_filled, ">")
30+
print(io, " "^n_left, "] ", )
31+
@printf io "%2.1f %%" perc
32+
print(io, "\r")
33+
end
34+
35+
function transfer_progress(progress::Ptr{LibGit2.GitTransferProgress}, p::Any)
36+
progress = unsafe_load(progress)
37+
if progress.total_deltas != 0
38+
p.header = "Resolving Deltas:"
39+
p.max = progress.total_deltas
40+
p.current = progress.indexed_deltas
41+
else
42+
p.max = progress.total_objects
43+
p.current = progress.received_objects
44+
end
45+
showprogress(stdout, p)
46+
return Cint(0)
47+
end
48+
49+
function clone(url, source_path; isbare::Bool=false, header = nothing, branch = nothing)
50+
isdir(source_path) && error("$source_path already exists")
51+
printstyled(stdout, "Cloning "; color = :green, bold=true)
52+
if header == nothing
53+
println(stdout, "from git repo: ", url, ".")
54+
else
55+
println(stdout, header)
56+
end
57+
p = MiniProgressBar(header = "Fetching:", color = Base.info_color())
58+
print(stdout, "\e[?25l")
59+
try
60+
GC.@preserve p branch begin
61+
callbacks = LibGit2.RemoteCallbacks(transfer_progress=cfunction(transfer_progress, Cint, Tuple{Ptr{LibGit2.GitTransferProgress}, Any}),
62+
payload = pointer_from_objref(p))
63+
fetch_opts = LibGit2.FetchOptions(callbacks = callbacks)
64+
if branch == nothing
65+
clone_opts = LibGit2.CloneOptions(fetch_opts=fetch_opts, bare=isbare)
66+
else
67+
clone_opts = LibGit2.CloneOptions(fetch_opts=fetch_opts, bare=isbare, checkout_branch=Cstring(pointer(branch)))
68+
end
69+
return LibGit2.clone(url, source_path, clone_opts)
70+
end
71+
catch e
72+
rm(source_path; force=true, recursive=true)
73+
rethrow(e)
74+
finally
75+
print(stdout, "\e[?25h")
76+
println(stdout)
77+
end
78+
end
79+
80+
end # module

src/Operations.jl

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import LibGit2
66

77
import REPL
88
using REPL.TerminalMenus
9-
using ..Types, ..GraphType, ..Resolve, ..Pkg2, ..BinaryProvider
9+
using ..Types, ..GraphType, ..Resolve, ..Pkg2, ..BinaryProvider, ..GitTools
1010
import ..depots, ..devdir, ..Types.uuid_julia
1111

1212
function find_installed(name::String, uuid::UUID, sha1::SHA1)
@@ -374,8 +374,7 @@ function install_git(
374374
ispath(clones_dir) || mkpath(clones_dir)
375375
repo_path = joinpath(clones_dir, string(uuid))
376376
repo = ispath(repo_path) ? LibGit2.GitRepo(repo_path) : begin
377-
@info("Cloning [$uuid] $name from $(urls[1])")
378-
LibGit2.clone(urls[1], repo_path, isbare=true)
377+
GitTools.clone(urls[1], repo_path; isbare=true, header = "[$uuid] $name from $(urls[1])")
379378
end
380379
git_hash = LibGit2.GitHash(hash.bytes)
381380
for i = 2:length(urls)
@@ -942,7 +941,7 @@ function develop(ctx::Context, pkgs_branches::Vector; path = devdir())
942941
for (_, repo) in repos
943942
@info "Cloning $(pkg.name) from $(repo) to $path"
944943
try
945-
LibGit2.clone(repo, pkgpath; branch = branch == nothing ? "" : branch)
944+
GitTools.clone(repo, pkgpath; branch = branch == nothing ? "" : branch)
946945
successfully_cloned = true
947946
break
948947
catch err

src/Pkg3.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ end
3030
# load snapshotted dependencies
3131
include("../ext/TOML/src/TOML.jl")
3232

33+
include("GitTools.jl")
3334
include("PlatformEngines.jl")
3435
include("Types.jl")
3536
include("Display.jl")

src/Types.jl

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ using REPL.TerminalMenus
99

1010
using ..TOML
1111
import ..Pkg3
12-
import Pkg3: depots, logdir
12+
import Pkg3: GitTools, depots, logdir
1313

1414
import Base: SHA1, AbstractEnv
1515
using SHA
@@ -612,8 +612,7 @@ function handle_repos!(env::EnvCache, pkgs::AbstractVector{PackageSpec}; upgrade
612612
ispath(clones_dir) || mkpath(clones_dir)
613613
repo_path = joinpath(clones_dir, string(hash(pkg.repo.url)))
614614
repo = ispath(repo_path) ? LibGit2.GitRepo(repo_path) : begin
615-
@info "Cloning package from $(pkg.repo.url)"
616-
LibGit2.clone(pkg.repo.url, repo_path, isbare=true)
615+
GitTools.clone(pkg.repo.url, repo_path, isbare=true)
617616
end
618617
if upgrade
619618
LibGit2.fetch(repo, remoteurl=pkg.repo.url, refspecs=refspecs)
@@ -855,9 +854,8 @@ function registries()::Vector{String}
855854
mkpath(user_regs)
856855
@info "Cloning default registries into $user_regs"
857856
for (reg, url) in DEFAULT_REGISTRIES
858-
@info " [+] $reg = $(repr(url))"
859857
path = joinpath(user_regs, reg)
860-
LibGit2.clone(url, path)
858+
GitTools.clone(url, path; header = "$reg: $(repr(url))")
861859
end
862860
end
863861
return [r for d in depots() for r in registries(d)]

src/version_parser.jl

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
const ver_reg = r"^([~^]?)?([0-9]+?)(?:\.([0-9]+?))?(?:\.([0-9]+?))?$"
2+
3+
function semver_interval(s::String)
4+
if !contains(s, ver_reg)
5+
error("invalid version specifier")
6+
end
7+
m = match(ver_reg, s)
8+
@assert length(m.captures) == 4
9+
n_significant = count(x -> x !== nothing, m.captures) - 1
10+
typ, _major, _minor, _patch = m.captures
11+
major = parse(Int, _major)
12+
minor = (n_significant < 2) ? 0 : parse(Int, _minor)
13+
patch = (n_significant < 3) ? 0 : parse(Int, _patch)
14+
if n_significant == 3 && major == 0 && minor == 0 && patch == 0
15+
error("invalid version 0.0.0")
16+
end
17+
vertyp = (typ == "" || typ == "^") ? :caret : :tilde
18+
v0 = VersionNumber(major, minor, patch)
19+
if vertyp == :caret
20+
if major != 0
21+
return v0, VersionNumber(v0.major+1, 0, 0)
22+
elseif minor != 0
23+
return v0, VersionNumber(v0.major, v0.minor+1, 0)
24+
else
25+
if n_significant == 1
26+
return v0, VersionNumber(1, 0, 0)
27+
elseif n_significant == 2
28+
return v0, VersionNumber(0, 1, 0)
29+
else
30+
return v0, VersionNumber(0, 0, v0.patch+1)
31+
end
32+
end
33+
else
34+
if n_significant == 3 || n_significant == 2
35+
return v0, VersionNumber(v0.major, v0.minor+1, 0)
36+
else
37+
return v0, VersionNumber(v0.major+1, 0, 0)
38+
end
39+
end
40+
end
41+
42+
@test semver_interval("^1.2.3") == (VersionNumber(1,2,3), VersionNumber(2,0,0))
43+
@test semver_interval("^1.2") == (VersionNumber(1,2,0), VersionNumber(2,0,0))
44+
@test semver_interval("1") == (VersionNumber(1,0,0), VersionNumber(2,0,0))
45+
@test semver_interval("^0.2.3") == (VersionNumber(0,2,3), VersionNumber(0,3,0))
46+
@test semver_interval("0.0.3") == (VersionNumber(0,0,3), VersionNumber(0,0,4))
47+
@test semver_interval("^0.0") == (VersionNumber(0,0,0), VersionNumber(0,1,0))
48+
@test semver_interval("0") == (VersionNumber(0,0,0), VersionNumber(1,0,0))
49+
@test semver_interval("~1.2.3") == (VersionNumber(1,2,3), VersionNumber(1,3,0))
50+
@test semver_interval("~1.2") == (VersionNumber(1,2,0), VersionNumber(1,3,0))
51+
@test semver_interval("~1") == (VersionNumber(1,0,0), VersionNumber(2,0,0))

0 commit comments

Comments
 (0)