From f1a82bc07b7749740cea7f7ed30712993a65a291 Mon Sep 17 00:00:00 2001 From: Romeo Valentin Date: Sat, 25 May 2024 01:21:34 -0700 Subject: [PATCH 1/5] Allow for remote names other than `origin`. We introduce `getremotename` that tries to check the `remote.pushDefault` option to determine the remote name, and falls back to `origin` if it's not set. --- src/utilities/utilities.jl | 52 ++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/src/utilities/utilities.jl b/src/utilities/utilities.jl index 1abbdb337e..5894a50d34 100644 --- a/src/utilities/utilities.jl +++ b/src/utilities/utilities.jl @@ -421,7 +421,31 @@ const julia_remote = Remotes.GitHub("JuliaLang", "julia") """ Stores the memoized results of [`getremote`](@ref). """ -const GIT_REMOTE_CACHE = Dict{String,Union{Remotes.Remote,Nothing}}() +const GIT_REMOTENAME_CACHE = Dict{String,Union{String,Nothing}}() +const GIT_REMOTEURL_CACHE = Dict{String,Union{Remotes.Remote,Nothing}}() + +""" +$(TYPEDSIGNATURES) + +Determines the GitHub remote of a directory by checking `remote.pushDefault` of the repository. +Defaults to `origin`. + +The results for a given directory are memoized in [`GIT_REMOTENAME_CACHE`](@ref), since calling +`git` is expensive and it is often called on the same directory over and over again. +""" +function getremotename(dir::AbstractString) + isdir(dir) || return nothing + return get!(GIT_REMOTENAME_CACHE, dir) do + try + remotename = readchomp(setenv(`$(git()) config --get remote.pushDefault`; dir=dir)) + @assert !isempty(remotename) + remotename + catch e + @warn "git config --get remote.pushDefault failed. Assuming remote `origin`." exception=(e, catch_backtrace()) + "origin" + end + end +end function parse_remote_url(remote::AbstractString) # TODO: we only match for GitHub repositories automatically. Could we engineer a @@ -435,24 +459,25 @@ end """ $(TYPEDSIGNATURES) -Determines the GitHub remote of a directory by checking `remote.origin.url` of the -repository. Returns a [`Remotes.GitHub`](@ref), or `nothing` is something has gone wrong -(e.g. it's run on a directory not in a Git repo, or `origin.url` points to a non-GitHub -remote). +Determines the GitHub remote of a directory by checking `remote.\$(remotename).url` of the +repository. `remotename` is determined by [`getremotename`](@ref). Returns a [`Remotes.GitHub`](@ref), +or `nothing` is something has gone wrong (e.g. it's run on a directory not in a Git repo, or the url +points to a non-GitHub remote). -The results for a given directory are memoized in [`GIT_REMOTE_CACHE`](@ref), since calling +The results for a given directory are memoized in [`GIT_REMOTEURL_CACHE`](@ref), since calling `git` is expensive and it is often called on the same directory over and over again. """ function getremote(dir::AbstractString) isdir(dir) || return nothing - return get!(GIT_REMOTE_CACHE, dir) do - remote = try - readchomp(setenv(`$(git()) config --get remote.origin.url`; dir=dir)) + return get!(GIT_REMOTEURL_CACHE, dir) do + remotename = getremotename(dir) + remoteurl = try + readchomp(setenv(`$(git()) config --get remote.$(remotename).url`; dir=dir)) catch e - @debug "git config --get remote.origin.url failed" exception=(e, catch_backtrace()) + @debug "git config --get remote.$(remotename).url failed" exception=(e, catch_backtrace()) "" end - return parse_remote_url(remote) + return parse_remote_url(remoteurl) end end @@ -616,7 +641,7 @@ it out automatically. `root` is the the directory where `git` gets run. `varname` is just informational and used to construct the warning messages. """ -function git_remote_head_branch(varname, root; remotename = "origin", fallback = "master") +function git_remote_head_branch(varname, root; fallback = "master") gitcmd = git(nothrow = true) if gitcmd === nothing @warn """ @@ -628,8 +653,9 @@ function git_remote_head_branch(varname, root; remotename = "origin", fallback = end # We need to do addenv() here to merge the new variables with the environment set by # Git_jll and the git() function. + remotename = getremotename(root) cmd = addenv( - setenv(`$gitcmd remote show $(remotename)`, dir=root), + setenv(`$gitcmd remote show $remotename`, dir=root), "GIT_TERMINAL_PROMPT" => "0", "GIT_SSH_COMMAND" => get(ENV, "GIT_SSH_COMMAND", "ssh -o \"BatchMode yes\""), ) From ac069f6594446e15926f9012ba933fd1b48da39f Mon Sep 17 00:00:00 2001 From: Romeo Valentin Date: Tue, 28 May 2024 20:06:31 -0700 Subject: [PATCH 2/5] Remove "Nothing" options for remotename cache. --- src/utilities/utilities.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utilities/utilities.jl b/src/utilities/utilities.jl index 5894a50d34..b61a3935a1 100644 --- a/src/utilities/utilities.jl +++ b/src/utilities/utilities.jl @@ -421,7 +421,7 @@ const julia_remote = Remotes.GitHub("JuliaLang", "julia") """ Stores the memoized results of [`getremote`](@ref). """ -const GIT_REMOTENAME_CACHE = Dict{String,Union{String,Nothing}}() +const GIT_REMOTENAME_CACHE = Dict{String, String}() const GIT_REMOTEURL_CACHE = Dict{String,Union{Remotes.Remote,Nothing}}() """ From c704cfe0b30b7e4da2348ce5b120d84b72db4aa4 Mon Sep 17 00:00:00 2001 From: Romeo Valentin Date: Tue, 28 May 2024 20:06:46 -0700 Subject: [PATCH 3/5] Automatically pick remote `origin` if it exists. --- src/utilities/utilities.jl | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/utilities/utilities.jl b/src/utilities/utilities.jl index b61a3935a1..ef2fe09118 100644 --- a/src/utilities/utilities.jl +++ b/src/utilities/utilities.jl @@ -427,8 +427,9 @@ const GIT_REMOTEURL_CACHE = Dict{String,Union{Remotes.Remote,Nothing}}() """ $(TYPEDSIGNATURES) -Determines the GitHub remote of a directory by checking `remote.pushDefault` of the repository. -Defaults to `origin`. +Determines the GitHub remote of a directory, returninf 'origin' if that remote exists, +and otherwise returning the result of `git config --get remote.pushDefault`. +If neither exist, throw an error. The results for a given directory are memoized in [`GIT_REMOTENAME_CACHE`](@ref), since calling `git` is expensive and it is often called on the same directory over and over again. @@ -437,12 +438,25 @@ function getremotename(dir::AbstractString) isdir(dir) || return nothing return get!(GIT_REMOTENAME_CACHE, dir) do try - remotename = readchomp(setenv(`$(git()) config --get remote.pushDefault`; dir=dir)) - @assert !isempty(remotename) - remotename + originurl = readchomp(setenv(`$(git()) config --get remote.origin.url`; dir=dir)) + @assert !isempty(originurl) + return "origin" + catch; end + + try + pushdefault = readchomp(setenv(`$(git()) config --get remote.pushDefault`; dir=dir)) + @assert !isempty(pushdefault) + @warn "Remote 'origin' not found. Using `git config --get remote.pushDefault` instead, which is set to '$(pushdefault)'." + return pushdefault + catch ; end + + + # If neither was found, throw an error. + try + @assert false catch e - @warn "git config --get remote.pushDefault failed. Assuming remote `origin`." exception=(e, catch_backtrace()) - "origin" + @warn "Neither remote 'origin' nor 'remotes.pushDefault' found" exception=(e, catch_backtrace()) + "" end end end From d7425a0c79039de8e70d032dd4a2617226661481 Mon Sep 17 00:00:00 2001 From: Romeo Valentin Date: Tue, 28 May 2024 20:06:58 -0700 Subject: [PATCH 4/5] Add note in docs. --- docs/src/lib/remote-links.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/lib/remote-links.md b/docs/src/lib/remote-links.md index 0b6bdd1812..d2fe506f05 100644 --- a/docs/src/lib/remote-links.md +++ b/docs/src/lib/remote-links.md @@ -69,7 +69,7 @@ This means that usually it is not necessary to specify either explicitly in the The rules are as follows: * If `repo` _is not_ specified, it is essentially [determined like any other remote link](@ref remotes-for-files), by trying to figure out the repository that contains the `root` path argument of [`makedocs`](@ref) (defaulting to the directory of the `make.jl` script; usually the `docs/` directory). - The [`Remote`](@ref Remotes.Remote) object will one of the `remotes`, which in turn may have been determined automatically via the `origin` URL of the containing Git repository. + The [`Remote`](@ref Remotes.Remote) object will be one of the `remotes`, which in turn may have been determined automatically via the `origin` URL of the containing Git repository, or the 'remotes.pushDefault' remote's URL if `origin` does not exist. * If `repo` _is_ specified, but the `remotes` for the repository root is not, `repo` will function as a `remotes` entry for the repository root. This is so that it would not be necessary to specify the same argument twice (i.e. once for general repository links, once for file links). From 7e3f8c3f4d81267ab6a305fcc91ac38ae00ece60 Mon Sep 17 00:00:00 2001 From: Romeo Valentin Date: Tue, 28 May 2024 20:43:25 -0700 Subject: [PATCH 5/5] Add tests for `getremotename` and `getremote` --- test/repolinks.jl | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/test/repolinks.jl b/test/repolinks.jl index e52594d250..89ae4ba65a 100644 --- a/test/repolinks.jl +++ b/test/repolinks.jl @@ -5,7 +5,7 @@ module RepoLinkTests using Test using Random: randstring -using Documenter: Documenter, Remotes, git, edit_url, source_url, MarkdownAST, walk_navpages, expand +using Documenter: Documenter, Remotes, git, edit_url, source_url, MarkdownAST, walk_navpages, expand, getremote, getremotename, GIT_REMOTENAME_CACHE using Documenter.HTMLWriter: render_article, HTMLContext, HTML using Markdown include("TestUtilities.jl"); using Main.TestUtilities @@ -67,6 +67,13 @@ extrepo_commit = init_git_repo( # A repository outside of the main repository (without remote) extrepo_noremote = joinpath(tmproot, "extrepo_noremote") extrepo_noremote_commit = init_git_repo(create_defaultfiles, extrepo_noremote; remote=nothing) +# A repository with a repo that's not called 'origin' +nonoriginrepo = joinpath(tmproot, "nonoriginrepo") +nonoriginrepo_commit = init_git_repo( + create_defaultfiles, nonoriginrepo; + remote=("nonorigin", "git@github.com:TestOrg/NonoriginRepo.jl.git") +) + # Just a directory outside of the main repository extdirectory = joinpath(tmproot, "extdirectory") mkpath(extdirectory) @@ -218,6 +225,18 @@ end @test doc.user.remote == Remotes.GitHub("AlternateOrg", "AlternateRepo.jl") end +@testset "Get remotename and remoteurl" begin + @test getremotename(mainrepo) == "origin" + @test getremote(mainrepo) == Remotes.GitHub("TestOrg", "TestRepo.jl") + @test_warn "Neither remote" getremotename(nonoriginrepo) + @test getremotename(nonoriginrepo) == "" + run(setenv(`$(git()) config --add remote.pushDefault nonorigin`; dir=nonoriginrepo)) + pop!(GIT_REMOTENAME_CACHE, nonoriginrepo) + @test Documenter.getremotename(nonoriginrepo) == "nonorigin" + @test Documenter.getremote(nonoriginrepo) == Documenter.Remotes.GitHub("TestOrg","NonoriginRepo.jl") +end + + # Setting both the `repo` and also overriding the same path in `remotes` should error @test_throws Exception Documenter.Document( root = joinpath(mainrepo, "docs"),