@@ 13,6 13,12 @@ local M = {}
--- @field history_url (fun(RepoUrl.UrlBuilderParams): string)?
--- @field raw_url (fun(RepoUrl.UrlBuilderParams): string)?
--- @field tree_url (fun(RepoUrl.UrlBuilderParams): string)?
+--
+--- @class RepoUrl.GitDir
+--- @field path string
+--- @field commit string
+--- @field branch? string The current checked out branch if any
+--- @field remote? string The tracked remote of the current branch if any
local fallback_gocache_dir = nil
@@ 317,18 323,77 @@ local function list_git_remotes(git_dir)
return remotes
end
----@param git_dir string
----@param preferred_remotes string[]
----@return {commit: string, remote_url: string, hostname: string}
-local function git_info(git_dir, preferred_remotes)
- local commit_command = { 'git', '-C', git_dir, 'rev-parse', 'HEAD' }
- local commit, commit_success = git_run(commit_command)
- if not commit_success then
- error('unable to get git commit: ' .. commit)
+-- find_remote attempts to find the remote for the git repository at the given commit
+-- It does this heuristically in the following order, until one succeeds:
+-- 1. Uses the tracked remote of the branch
+-- 2. Uses the preferred_remotes config
+-- 3. Fallbacks to any remote
+--- @param git_dir RepoUrl.GitDir
+local function find_remote(git_dir)
+ -- Try to get the tracked remote first
+ if git_dir.remote then
+ return git_dir.remote
+ end
+
+ local remotes = list_git_remotes(git_dir.path)
+ if not remotes then
+ error 'no remote found'
+ end
+ -- Check preferred
+ for _, it in ipairs(config.preferred_remotes) do
+ if remotes[it] then
+ return it
+ end
+ end
+
+ -- Fallback to any remote
+ for it, _ in ipairs(remotes) do
+ return it
+ end
+end
+
+--- @param path string
+--- @return RepoUrl.GitDir
+local function current_git_dir(path)
+ local ok = false
+ local git_dir = {
+ path = path,
+ }
+
+ local cmd = { 'git', '-C', path, 'rev-parse', 'HEAD' }
+ git_dir.commit, ok = git_run(cmd)
+ if not ok then
+ error('unable to get git commit: ' .. git_dir.commit)
+ end
+
+ local branch
+ cmd = { 'git', '-C', path, 'rev-parse', '--abbrev-ref', 'HEAD' }
+ branch, ok = git_run(cmd)
+ if not ok then
+ error('unable to get git branch: ' .. branch)
end
+ if branch and branch ~= 'HEAD' then
+ git_dir.branch = branch
+
+ local remote
+ cmd = { 'git', '-C', path, 'config', '--get', 'branch.' .. branch .. '.remote' }
+ remote, ok = git_run(cmd)
+ if not ok then
+ error('unable to get git remote: ' .. remote)
+ end
+ if remote and remote ~= '' then
+ git_dir.remote = remote
+ end
+ end
+ return git_dir
+end
+
+---@param path string
+---@return {commit: string, remote_url: string, hostname: string}
+local function git_info(path)
local function get_remote_url(remote_name)
- local remote_command = { 'git', '-C', git_dir, 'remote', 'get-url', remote_name }
+ local remote_command = { 'git', '-C', path, 'remote', 'get-url', remote_name }
local remote_url, remote_success = git_run(remote_command)
if not remote_success then
error('unable to get git remote url: ' .. remote_url)
@@ 355,32 420,22 @@ local function git_info(git_dir, preferred_remotes)
return remote_to_url(remote_url)
end
- local remote_url = nil
- local remotes = list_git_remotes(git_dir)
- -- Check preferred
- for _, remote in ipairs(preferred_remotes) do
- if remotes[remote] then
- remote_url = get_remote_url(remote)
- break
- end
- end
- -- Fallback to the rest
- if remote_url == nil then
- for _, remote in ipairs(remotes) do
- if remotes[remote] then
- remote_url = get_remote_url(remote)
- break
- end
- end
+ local git_dir = current_git_dir(path)
+
+ local remote = find_remote(git_dir)
+ if not remote then
+ error 'unable to find remote'
end
+
+ local remote_url = get_remote_url(remote)
if remote_url == nil then
- error 'unable to find remote'
+ error 'unable to find remote url'
end
local hostname = remote_url:match '://([^/]+)/'
return {
- commit = commit,
+ commit = git_dir.commit,
remote_url = remote_url,
hostname = hostname,
}
@@ 426,7 481,7 @@ local get_repo_info = function()
error 'not a git repository'
end
- local git = git_info(project_dir, config.preferred_remotes)
+ local git = git_info(project_dir)
local rel_filepath = string.sub(filename, #project_dir + 2)
local info = {
base_url = git.remote_url,