How gh-stack Fetches and Checks Out Remote Stacks from GitHub: A Complete Technical Guide
gh-stack resolves remote stacks by querying GitHub's Stacks API to discover pull request relationships, reconciles them against local state, imports the required Git refs, and checks out the target branch using standard Git operations.
The gh-stack extension for the GitHub CLI enables developers to manage dependent pull requests as cohesive units. When you need to work with a stack that exists only on GitHub, understanding how the tool fetches and checks out remote stacks is essential for effective branch management. This article examines the exact implementation details found in the github/gh-stack repository.
Entry Point and Target Resolution
The checkout process begins in cmd/checkout.go with the runCheckout function (lines 77-85), which parses the user’s target—whether a stack number, PR number/URL, or branch name—and initiates resolution.
For numeric inputs such as gh stack checkout 7, the resolveNumericTarget helper (lines 55-64) handles the lookup logic:
- Local stack lookup: It first attempts to find a local stack via
checkoutStackByNumber(lines 65-73). - Remote fallback: If no local match exists, it delegates to
checkoutRemoteStack(lines 80-85) to perform the remote GitHub lookup.
Discovering Remote Stacks via GitHub API
The checkoutRemoteStack function performs the actual GitHub API calls to locate the stack structure. It begins by calling client.FindStackForPR(prNumber) in internal/github/github.go (lines 1035-1045), which issues a GET request to the Stacks REST endpoint:
/repos/{owner}/{repo}/stacks?pull_request={n}
This endpoint returns the stack composition, including all associated pull request numbers.
Fetching Pull Request Metadata
Once the stack structure is identified, fetchStackPRDetails (cmd/checkout.go lines 78-88) iterates over the PR numbers and retrieves detailed metadata for each one. The client.FindPRByNumber method executes a GraphQL query (internal/github/github.go lines 40-52) to obtain:
- Head and base refs
- Merge state status
- Repository relationships
With this data, the function determines the trunk (the base of the first PR in the stack) and the target branch (the head of the specific PR the user requested) (cmd/checkout.go lines 42-52).
Reconciling Local and Remote State
The core synchronization logic resides in reconcileAndImportRemoteStack (cmd/checkout.go lines 105-112). This function performs several validation steps:
- Checks if all PRs in the stack are already merged, exiting early if true.
- Searches for an existing local stack that tracks any of the remote PRs using
findLocalStackForRemotePRs(lines 95-103). - If a matching local stack exists with identical composition to the remote stack, it updates the local identifiers and saves the stack file (lines 33-40).
- When no local match exists, it proceeds to import the remote stack via
importRemoteStack(lines 56-66).
Importing Remote Git References
The importRemoteStack function handles the physical transfer of Git objects from the remote repository to your local environment. According to the source code in cmd/checkout.go, this process executes the following sequence (lines 60-63, 70-92):
- Fetch remote refs: Calls
git.Fetchto pull the required references from the remote repository. - Ensure trunk exists: Verifies the trunk branch exists locally.
- Create local branches: Invokes
ensureLocalBranchFromRemoteto create local tracking branches for each PR head. - Build stack structure: Constructs a
stack.Stackstruct that mirrors the remote composition exactly. - Persist state: Saves the stack configuration to
.git/gh-stackviastack.Save(lines 104-106).
Checking Out the Target Branch
After the stack object—whether existing local or newly imported—is obtained, runCheckout completes the operation by switching the working directory to the desired branch. It calls git.CheckoutBranch (cmd/checkout.go lines 43-46) and prints a success message confirming the checkout.
Code Examples
# Checkout a stack by its stack number (remote or local)
$ gh stack checkout 7
// Simplified flow inside runCheckout
func runCheckout(cfg *config.Config, opts *checkoutOptions) error {
// … load local stack file …
s, branch, err := resolveNumericTarget(cfg, sf, gitDir, number, opts.target)
// … handle errors …
git.CheckoutBranch(branch) // switch to the target branch
}
// Remote stack lookup and import (high-level)
func checkoutRemoteStack(cfg *config.Config, sf *stack.StackFile, gitDir string, prNumber int) (*stack.Stack, string, error) {
client, _ := cfg.GitHubClient()
remoteStack, _ := client.FindStackForPR(prNumber) // API call
prs, _ := fetchStackPRDetails(client, remoteStack.PRNumbers())
// Determine trunk & targetBranch, then reconcile/import
return reconcileAndImportRemoteStack(cfg, client, sf, gitDir, remoteStack, prs, trunk, targetBranch)
}
Summary
- The entry point for remote stack checkout is
runCheckoutincmd/checkout.go. - Remote discovery uses
FindStackForPRto query GitHub's Stacks REST endpoint (GET /repos/{owner}/{repo}/stacks). - GraphQL queries retrieve detailed PR metadata including refs and merge state via
FindPRByNumber. - State reconciliation happens in
reconcileAndImportRemoteStack, which checks for existing local stacks before importing. - Git operations include
git.FetchandensureLocalBranchFromRemoteto create local branches from remote refs. - The final checkout uses
git.CheckoutBranchto switch to the target branch.
Frequently Asked Questions
What GitHub API endpoints does gh-stack use to find remote stacks?
gh-stack uses the Stacks REST endpoint at GET /repos/{owner}/{repo}/stacks?pull_request={n} to discover which stack contains a specific pull request, as implemented in FindStackForPR within internal/github/github.go (lines 1035-1045). For detailed metadata about each pull request in the stack, it uses GraphQL queries via FindPRByNumber (lines 40-52).
How does gh-stack handle authentication for private repositories?
gh-stack leverages the existing GitHub CLI authentication context. The cfg.GitHubClient() call within checkoutRemoteStack returns an authenticated client that uses the same credentials and tokens configured for the standard gh CLI, ensuring seamless access to private repositories without additional configuration.
What happens if a remote stack has already been partially merged?
The reconcileAndImportRemoteStack function checks if all PRs are already merged at the beginning of its execution and exits early if true. For partially merged stacks, it still imports the remaining unmerged branches and updates the local stack file to reflect the current state, allowing you to work with the active portions of the stack.
Can gh-stack checkout remote stacks without internet connectivity?
No, checking out a remote stack requires internet connectivity. The checkoutRemoteStack function must contact GitHub's API via FindStackForPR and fetch Git refs using git.Fetch. However, once a stack has been imported locally, you can work with it offline using standard Git operations.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →