Skip to content

Commit

Permalink
fix: support finding pr branch via git config branch keys
Browse files Browse the repository at this point in the history
Fixes pwntester#799

When the gh cli tool checks out a branch created from a fork of the
origin it places remote tracking info directly in git config at a key
derived from branch.{branch_name} prefix.

Octo did not consider this when checking if the repository is checked
out to the branch pending merge into the origin.

Update the `in_pr_branch` function to also check if the current PR has
a git configuration which provides details of the upstream fork branch.

See the following `man git-config` entries for more details:
```
       branch.<name>.remote
           When on branch <name>, it tells git fetch and git push which remote to fetch from/push to. The remote to push to may
           be overridden with remote.pushDefault (for all branches). The remote to push to, for the current branch, may be
           further overridden by branch.<name>.pushRemote. If no remote is configured, or if you are not on any branch and there
           is more than one remote defined in the repository, it defaults to origin for fetching and remote.pushDefault for
           pushing. Additionally, . (a period) is the current local repository (a dot-repository), see branch.<name>.merge's
           final note below.

       branch.<name>.pushRemote
           When on branch <name>, it overrides branch.<name>.remote for pushing. It also overrides remote.pushDefault for
           pushing from branch <name>. When you pull from one place (e.g. your upstream) and push to another place (e.g. your
           own publishing repository), you would want to set remote.pushDefault to specify the remote to push to for all
           branches, and use this option to override it for a specific branch.

       branch.<name>.merge
           Defines, together with branch.<name>.remote, the upstream branch for the given branch. It tells git fetch/git
           pull/git rebase which branch to merge and can also affect git push (see push.default). When in branch <name>, it
           tells git fetch the default refspec to be marked for merging in FETCH_HEAD. The value is handled like the remote part
           of a refspec, and must match a ref which is fetched from the remote given by "branch.<name>.remote". The merge
           information is used by git pull (which at first calls git fetch) to lookup the default branch for merging. Without
           this option, git pull defaults to merge the first refspec fetched. Specify multiple values to get an octopus merge.
           If you wish to setup git pull so that it merges into <name> from another branch in the local repository, you can
           point branch.<name>.merge to the desired branch, and use the relative path setting . (a period) for
           branch.<name>.remote.

```

Signed-off-by: ldelossa <[email protected]>
  • Loading branch information
ldelossa committed Jan 19, 2025
1 parent 916932d commit 1b1eaac
Showing 1 changed file with 63 additions and 2 deletions.
65 changes: 63 additions & 2 deletions lua/octo/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -449,9 +449,14 @@ end
--- Determines if we are locally are in a branch matching the pr head ref
--- @param pr PullRequest
--- @return boolean
function M.in_pr_branch(pr)
function M.in_pr_branch_locally_tracked(pr)
local cmd = "git rev-parse --abbrev-ref --symbolic-full-name @{u}"
local local_branch_with_local_remote = vim.split(string.gsub(vim.fn.system(cmd), "%s+", ""), "/")
local cmd_out = vim.fn.system(cmd)
if vim.v.shell_error ~= 0 then
return false
end

local local_branch_with_local_remote = vim.split(string.gsub(cmd_out, "%s+", ""), "/")
local local_remote = local_branch_with_local_remote[1]
local local_branch = table.concat(local_branch_with_local_remote, "/", 2)

Expand All @@ -465,6 +470,62 @@ function M.in_pr_branch(pr)
return false
end

-- Determines if we are locally in a branch matting the pr head ref when
-- the remote and branch information is stored in the branch's git config values
-- The gh CLI tool stores remote info directly in {branch.{branch}.x} configuration
-- fields and does not create a remote
function M.in_pr_branch_config_tracked(pr)
local branch_cmd = "git rev-parse --abbrev-ref HEAD"
local branch = vim.fn.system(branch_cmd)
if vim.v.shell_error ~= 0 then
return false
end

if #branch == 0 then
return false
end

-- trim white space off branch
branch = string.gsub(branch, "%s+", "")

local merge_config_cmd = string.format('git config --get-regexp "^branch\\.%s\\.merge"', branch)

local merge_config = vim.fn.system(merge_config_cmd)
if vim.v.shell_error ~= 0 then
return false
end

if #merge_config == 0 then
return false
end

-- split merge_config to key, value with space delimeter
local merge_config_kv = vim.split(merge_config, "%s+")
-- use > 2 since there maybe some garbage white space at the end of the map.
if #merge_config_kv < 2 then
return false
end

local upstream_branch_ref = merge_config_kv[2]

-- remove the prefix /refs/heads/ from upstream_branch_ref resulting in
-- branch's name.
local upstream_branch_name = string.gsub(upstream_branch_ref, "^refs/heads/", "")

if upstream_branch_name:lower() == pr.head_ref_name then
return true
end

return false
end

--- Determines if we are locally are in a branch matching the pr head ref
--- @param pr PullRequest
--- @return boolean
function M.in_pr_branch(pr)
return M.in_pr_branch_locally_tracked(pr) or M.in_pr_branch_config_tracked(pr)
end

function M.checkout_pr(pr_number)
gh.run {
args = { "pr", "checkout", pr_number },
Expand Down

0 comments on commit 1b1eaac

Please sign in to comment.