mirror of
https://github.com/zoriya/jj.nvim.git
synced 2026-08-15 23:53:18 +00:00
feat(fetch_pr): Introdue the fetch_pr command that allows to pick open
Pull requests from github and import them locally
This commit is contained in:
@@ -127,6 +127,9 @@ local split_module = require("jj.cmd.split")
|
||||
--- @class jj.cmd.open_pr_opts
|
||||
--- @field list_bookmarks? boolean Whether to select from all bookmarks instead of current revision
|
||||
|
||||
--- @class jj.cmd.fetch_pr_opts
|
||||
--- @field limit? number Limit the number of PRs to select from
|
||||
|
||||
--- @type jj.cmd.opts
|
||||
M.config = {
|
||||
describe = {
|
||||
@@ -1008,6 +1011,91 @@ function M.tag_push()
|
||||
end
|
||||
end
|
||||
|
||||
--- Opens a picker to localy fetch a PR from a github repository
|
||||
--- @param opts? jj.cmd.fetch_pr_opts Options for fetching PRs
|
||||
function M.fetch_pr(opts)
|
||||
if not utils.ensure_jj() then
|
||||
return
|
||||
end
|
||||
|
||||
if not utils.has_executable("git") then
|
||||
return
|
||||
end
|
||||
|
||||
if not utils.is_colocated() then
|
||||
utils.notify("Current repository is not colocated. Cannot fetch PR.", vim.log.levels.ERROR)
|
||||
return
|
||||
end
|
||||
|
||||
-- Create a new opts table with the default limit if not provided
|
||||
opts = vim.tbl_deep_extend("force", {
|
||||
limit = 100,
|
||||
}, opts or {})
|
||||
|
||||
-- Get the prs from github
|
||||
local prs = utils.list_github_prs(opts)
|
||||
if not prs or #prs == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
local needs_refresh = terminal.is_log_buffer_open()
|
||||
|
||||
vim.ui.select(prs, {
|
||||
prompt = "Select PR to fetch: ",
|
||||
format_item = function(pr)
|
||||
return string.format("#%s %s %s", pr.number, pr.title, pr.author)
|
||||
end,
|
||||
}, function(choice)
|
||||
if choice then
|
||||
utils.notify("Pulling PR #" .. choice.number .. "...", vim.log.levels.INFO)
|
||||
local pr = choice.number
|
||||
local count = 1
|
||||
local max_retries = 30
|
||||
|
||||
-- the function that actually tries to fetch recursively
|
||||
local function try_fetch()
|
||||
if count > max_retries then
|
||||
utils.notify(
|
||||
string.format("Failed to fetch PR #%s. Tried %d times.", pr, max_retries),
|
||||
vim.log.levels.ERROR
|
||||
)
|
||||
return
|
||||
end
|
||||
|
||||
local ref = string.format("pull/%s/head:pr-%s-%d", pr, pr, count)
|
||||
local cmd = string.format("git fetch origin %s", ref)
|
||||
|
||||
runner.execute_command_async(
|
||||
cmd,
|
||||
function()
|
||||
-- If we successfully pulled the PR, notify the user and refresh the log if it's open
|
||||
runner.execute_command_async("jj git import", function()
|
||||
utils.notify(
|
||||
string.format("PR #%s fetched as pr-%s-%d.", pr, pr, count),
|
||||
vim.log.levels.INFO
|
||||
)
|
||||
if needs_refresh then
|
||||
M.log({})
|
||||
end
|
||||
end, "Failed to import git refs")
|
||||
end,
|
||||
"",
|
||||
nil,
|
||||
true,
|
||||
function()
|
||||
-- If we errored increment the counter by one and try and fetch it again
|
||||
count = count + 1
|
||||
try_fetch()
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
-- Try and pull it once
|
||||
try_fetch()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
--- @param args string|string[] jj command arguments
|
||||
function M.j(args)
|
||||
if not utils.ensure_jj() then
|
||||
@@ -1164,6 +1252,9 @@ function M.j(args)
|
||||
end
|
||||
end
|
||||
end,
|
||||
fetch_pr = function()
|
||||
M.fetch_pr()
|
||||
end,
|
||||
}
|
||||
|
||||
if handlers[subcommand] then
|
||||
@@ -1213,6 +1304,7 @@ function M.register_command()
|
||||
"annotate_line",
|
||||
"commit",
|
||||
"tag",
|
||||
"fetch_pr",
|
||||
}
|
||||
local matches = {}
|
||||
for _, cmd in ipairs(subcommands) do
|
||||
|
||||
@@ -51,7 +51,8 @@ end
|
||||
--- @param error_prefix string|nil Optional error message prefix
|
||||
--- @param input string|nil Optional input to pass to stdin
|
||||
--- @param silent boolean|nil Optional to silent the notification
|
||||
function M.execute_command_async(cmd, on_success, error_prefix, input, silent)
|
||||
--- @param on_error function|nil Callback on error, receives ouptut as the parameter
|
||||
function M.execute_command_async(cmd, on_success, error_prefix, input, silent, on_error)
|
||||
local output_lines = {}
|
||||
|
||||
local job_id = vim.fn.jobstart({ "sh", "-c", cmd }, {
|
||||
@@ -85,6 +86,9 @@ function M.execute_command_async(cmd, on_success, error_prefix, input, silent)
|
||||
if not silent then
|
||||
vim.notify(error_message, vim.log.levels.ERROR, { title = "JJ" })
|
||||
end
|
||||
if on_error then
|
||||
on_error(output)
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
@@ -695,4 +695,51 @@ function M.reload_changed_file_buffers()
|
||||
vim.cmd.checktime()
|
||||
end
|
||||
|
||||
--- Using the gh cli lists all open prs
|
||||
--- @param opts {limit: integer|nil}
|
||||
--- @return {number: integer, title: string, author: string}[]|nil
|
||||
function M.list_github_prs(opts)
|
||||
if not M.has_executable("gh") then
|
||||
M.notify("Missing `gh` executable to list prs", vim.log.levels.ERROR)
|
||||
return
|
||||
end
|
||||
|
||||
-- Set the default limit
|
||||
local limit = 100
|
||||
|
||||
if opts and opts.limit then
|
||||
limit = opts.limit
|
||||
end
|
||||
|
||||
-- Start with a hardcoded limit of 100
|
||||
local cmd =
|
||||
[[gh pr list -L %s --json number,title,author --jq '.[] | "#\(.number);;;\(.title);;;(@\(.author.login))"']]
|
||||
cmd = string.format(cmd, limit)
|
||||
|
||||
-- Run the command to get the pr's
|
||||
local output, success = runner.execute_command_sync(cmd, nil, "Failed to get prs")
|
||||
if not success or not output then
|
||||
return
|
||||
end
|
||||
|
||||
local open_prs = {}
|
||||
-- Split each line
|
||||
local lines = vim.split(output, "\n", { trimempty = true })
|
||||
for _, line in ipairs(lines) do
|
||||
local parts = vim.split(line, ";;;", { trimempty = true })
|
||||
if #parts == 3 then
|
||||
local number = tonumber(parts[1]:match("#(%d+)"))
|
||||
local title = parts[2]
|
||||
local author = parts[3]
|
||||
if number and title and author then
|
||||
table.insert(open_prs, { number = number, title = title, author = author })
|
||||
end
|
||||
else
|
||||
M.notify("Unexpected PR list format: " .. line, vim.log.levels.WARN)
|
||||
end
|
||||
end
|
||||
|
||||
return open_prs
|
||||
end
|
||||
|
||||
return M
|
||||
|
||||
Reference in New Issue
Block a user