gh stack link vs submit: Understanding the Two Ways to Manage Stacked PRs

The gh stack link command creates or updates stacked PRs directly on GitHub without using local stack metadata, while gh stack submit synchronizes your local stack file (.git/gh-stack) with GitHub by pushing branches and managing PRs interactively.

The github/gh-stack extension provides two distinct approaches for managing stacked pull requests. While both commands interact with GitHub's Stacks API, they serve opposite workflows—link operates purely against remote GitHub state, whereas submit orchestrates local Git operations, interactive editing, and remote synchronization. Understanding these differences ensures you choose the right tool for your branching strategy.

Core Architectural Differences

Remote-Only vs Local-First State Management

The fundamental distinction lies in where each command derives its source of truth. According to the command description in cmd/link.go (lines 29-34), gh stack link ignores the local stack file entirely. It discovers pull requests from branch names, PR numbers, or URLs provided as CLI arguments, then calls the Stacks API to create or modify a remote stack directly.

In contrast, gh stack submit relies on the local stack file (.git/gh-stack) as its primary data source. As implemented in cmd/submit.go (lines 83-88), the command loads the stack using stack.Load(gitDir) and manipulates that local representation throughout the execution before syncing to GitHub.

Source of Truth Operations

When you run gh stack link, GitHub serves as the source of truth. The command uses upsertStack (lines 42-48 in cmd/link.go) to create or update the remote stack metadata without reading or writing local state.

Conversely, gh stack submit treats your local file system as authoritative. It pushes branches, potentially collects PR drafts through an interactive editor, and finally calls syncStack (line 95 in cmd/submit.go) to reflect the local state onto GitHub.

Workflow Comparison

The link workflow is non-interactive and argument-driven:

  1. Provide a list of branches, PR numbers, or URLs as CLI arguments
  2. The command resolves each argument to a PR (or creates one)
  3. It uses detectAddMode (lines 51-55 in cmd/link.go) to determine whether to create a new remote stack or append to an existing one
  4. It performs a push-only step (pushBranchArgs at line 23) before looking up PRs
  5. Finally, it calls upsertStack to persist the stack on GitHub

This workflow uses listStacksSafe (lines 17-22) to safely query existing stacks before modification.

How gh stack submit Works

The submit workflow is local-state-driven and optionally interactive:

  1. Detect and load the current local stack (lines 97-104 in cmd/submit.go)
  2. Push all branches to the remote (lines 21-27)
  3. Create new PRs or update existing ones using ensurePR and createPR functions
  4. If the terminal is interactive and --auto is not set, launch the TUI (submitview) at lines 102-119 to collect PR drafts via collectPRDrafts (line 61)
  5. Handle pending modify states and forked merged stacks
  6. Call syncStack (lines 44-48) to update GitHub based on the local definition

Implementation Details

The runLink function (line 96) serves as the entry point. Key implementation characteristics include:

  • Decision logic: Uses detectAddMode (lines 51-55) to decide whether to create a new stack or add to an existing one
  • Safe querying: Employs listStacksSafe (lines 17-22) to avoid errors when checking existing stacks
  • Remote-only operations: Performs upsertStack calls without local file manipulation

The Submit Implementation (cmd/submit.go)

The submit command implementation reveals its local-first architecture:

  • State loading: Calls sf, err := stack.Load(gitDir) (lines 83-88) to read the local stack file
  • Interactive capabilities: Conditionally launches an interactive editor through collectPRDrafts (line 61) when drafting PR descriptions
  • Synchronization: Uses syncStack to reconcile local state with GitHub, handling complex scenarios like merged stack forks

When to Use Each Command

  • You manage branches with external tools (e.g., jj, git-town) and only need to group existing PRs on GitHub
  • You do not want a .git/gh-stack file in your repository
  • You need to link existing PRs or branches non-interactively via CLI arguments

Use gh stack submit When:

  • You want the full GH-Stack experience with local stack tracking
  • You need interactive drafting of PR titles and descriptions
  • You require automatic base-branch fixing and the ability to run subsequent stack commands (gh stack view, gh stack modify)
  • You want to push branches and sync state in a single operation

Command Examples

Link existing branches or PRs into a stack without local metadata:


# Create a new stack from three branches (no local .git/gh-stack created)

gh stack link auth-layer api-routes ui-components

# Add PRs to an existing stack #7

gh stack link 7 48 ui-polish

Submit local stack state to GitHub with optional interactivity:


# Open interactive TUI to draft PRs

gh stack submit

# Skip editor and auto-generate PR titles

gh stack submit --auto

# Mark all PRs as ready for review

gh stack submit --open

Summary

  • gh stack link is a remote-only command that operates directly against GitHub's Stacks API using upsertStack, ignoring local .git/gh-stack files
  • gh stack submit is a local-first command that loads state via stack.Load, pushes branches, optionally launches an interactive TUI, and syncs via syncStack
  • Link uses detectAddMode (lines 51-55) to decide between creating or appending to stacks, while submit uses local file detection (lines 97-104)
  • Link accepts CLI arguments only; submit can run interactively (lines 102-119) when not using --auto
  • Choose link for external workflow integration; choose submit for full stack lifecycle management

Frequently Asked Questions

Yes, but the command will ignore it. According to the implementation in cmd/link.go (lines 29-34), gh stack link operates purely against GitHub's API using listStacksSafe and upsertStack, leaving your local stack file untouched. This makes it safe to use alongside other stack-tracking tools.

Does gh stack submit work without a local stack file?

No. As implemented in cmd/submit.go (lines 83-88, 97-104), the command attempts to detect and load the local stack using stack.Load(gitDir). If no stack exists in .git/gh-stack, the command cannot proceed with its local-first workflow of pushing branches and syncing state.

Which command should I use with external git workflow tools like jj or git-town?

Use gh stack link. Since it ignores the local .git/gh-stack file and operates directly on GitHub via the Stacks API (using upsertStack at lines 42-48), it will not conflict with external systems that manage their own stack metadata. You can link your externally-managed branches into a GitHub stack without creating conflicting local state.

The submit command includes specific logic at lines 102-119 in cmd/submit.go to launch a TUI (submitview) when the terminal is interactive and the --auto flag is not set, allowing you to draft PR titles and descriptions via collectPRDrafts (line 61). In contrast, gh stack link (starting at runLink, line 96) is designed to accept all arguments via CLI and perform operations non-interactively, making it suitable for scripting and automation.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →