Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
24 changes: 4 additions & 20 deletions lua/octo/commands.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1894,6 +1894,9 @@ function M.create_pr(is_draft)

-- get repo info
local info = utils.get_repo_info(repo)
if info == nil or info == vim.NIL then
return
end

-- repo candidates = self + parent (in case of fork)
local repo_candidates_entries = { "Select target repo", "1. " .. repo }
Expand All @@ -1907,26 +1910,7 @@ function M.create_pr(is_draft)
local cmd = "git rev-parse --abbrev-ref HEAD"
local local_branch = string.gsub(vim.fn.system(cmd), "%s+", "")

-- get remote branches
if
info == nil
or info.refs == nil
or info.refs.nodes == nil
or info == vim.NIL
or info.refs == vim.NIL
or info.refs.nodes == vim.NIL
then
utils.error "Cannot grab remote branches"
return
end
local remote_branches = info.refs.nodes

local remote_branch_exists = false
for _, remote_branch in ipairs(remote_branches) do
if local_branch == remote_branch.name then
remote_branch_exists = true
end
end
local remote_branch_exists = utils.remote_branch_exists(repo, local_branch)
local remote_branch = local_branch
if not remote_branch_exists then
local choice =
Expand Down
20 changes: 14 additions & 6 deletions lua/octo/gh/queries.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1264,7 +1264,6 @@ query($login: String!, $endCursor: String) {
---@field projectsUrl string
---@field homepageUrl string
---@field primaryLanguage { name: string, color: string }
---@field refs { nodes: { name: string }[] }
---@field languages { nodes: { name: string, color: string }[] }
---@field viewerHasStarred boolean
---@field viewerSubscription octo.SubscriptionState
Expand Down Expand Up @@ -1292,11 +1291,6 @@ query($owner: String!, $name: String!) {
name
color
}
refs(last:100, refPrefix: "refs/heads/") {
nodes {
name
}
}
languages(first:100) {
nodes {
name
Expand All @@ -1307,6 +1301,20 @@ query($owner: String!, $name: String!) {
}
]] .. fragments.repository

-- https://docs.github.com/en/graphql/reference/objects#repository
---@class octo.Ref
---@field name string

M.ref = [[
query($owner: String!, $name: String!, $qualifiedName: String!) {
repository(owner: $owner, name: $name) {
ref(qualifiedName: $qualifiedName) {
name
}
}
}
]]

M.gists = [[
query($privacy: GistPrivacy = ALL, $endCursor: String) {
viewer {
Expand Down
20 changes: 20 additions & 0 deletions lua/octo/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -990,6 +990,26 @@ function M.get_repo_info(repo)
return info
end

---Checks whether a branch exists on the remote repo.
---Looks the single ref up directly, so it stays correct in repos with more
---branches than one page of `refs` can return.
---@param repo string
---@param branch string
---@return boolean
function M.remote_branch_exists(repo, branch)
local owner, name = M.split_repo(repo)
if M.is_blank(owner) or M.is_blank(name) or M.is_blank(branch) then
return false
end
local output = gh.api.graphql {
query = queries.ref,
fields = { owner = owner, name = name, qualifiedName = "refs/heads/" .. branch },
jq = ".data.repository.ref.name",
opts = { mode = "sync" },
}
return not M.is_blank(output) and vim.trim(output) == branch
end

---Gets repo's templates
---@param repo string
function M.get_repo_templates(repo)
Expand Down
69 changes: 69 additions & 0 deletions lua/tests/plenary/remote_branch_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
---@diagnostic disable
local config = require "octo.config"
local fragments = require "octo.gh.fragments"
local queries = require "octo.gh.queries"

config.setup {}
fragments.setup()
queries.setup()

local eq = assert.are.same

describe("remote branch lookup:", function()
local gh = require "octo.gh"
local utils = require "octo.utils"

local original_graphql
local captured

local function stub_graphql(output)
gh.api.graphql = function(opts)
captured = opts
return output
end
end

before_each(function()
captured = nil
original_graphql = gh.api.graphql
end)

after_each(function()
gh.api.graphql = original_graphql
end)

it("reports a branch that resolves on the remote", function()
stub_graphql "feature-branch\n"
assert.is_true(utils.remote_branch_exists("owner/repo", "feature-branch"))
end)

it("reports a branch that does not resolve", function()
stub_graphql ""
assert.is_false(utils.remote_branch_exists("owner/repo", "missing-branch"))
end)

it("reports a branch when the remote answers with a different ref", function()
stub_graphql "other-branch"
assert.is_false(utils.remote_branch_exists("owner/repo", "feature-branch"))
end)

it("asks for the single qualified ref rather than a page of branches", function()
stub_graphql "feature-branch"
utils.remote_branch_exists("owner/repo", "feature-branch")
eq(queries.ref, captured.query)
eq("owner", captured.fields.owner)
eq("repo", captured.fields.name)
eq("refs/heads/feature-branch", captured.fields.qualifiedName)
end)

it("skips the request when the repo or branch is blank", function()
stub_graphql "feature-branch"
assert.is_false(utils.remote_branch_exists("owner/repo", ""))
assert.is_false(utils.remote_branch_exists("owner", "feature-branch"))
assert.is_nil(captured)
end)

it("no longer pages branches in the repository query", function()
assert.is_nil(queries.repository:find("refs(last:", 1, true))
end)
end)
Loading