How Fork Routing for GitHub PRs Works in no-mistakes with Separate Origin/Fork Remotes
no-mistakes implements fork routing by distinguishing between origin (upstream) and fork remotes, ensuring pushes target the contributor's fork while pull requests are created against the upstream repository using the --repo and --head <fork-owner>:<branch> flags.
Contributing to open-source projects typically requires maintaining separate remotes for the upstream repository and your personal fork. The no-mistakes tool automates fork routing for GitHub PRs by intelligently routing git pushes to your fork while ensuring pull request operations target the canonical upstream repository. This architecture prevents permission errors and ensures CI workflows run on the correct repository.
Understanding the Fork Routing Architecture
Repository Metadata and Push URL Resolution
In internal/db/repo.go (lines 19-27), the Repo.PushURL() method determines where to push branch updates. It checks the ForkURL field first, falling back to UpstreamURL when no fork is configured. This ensures that when you run push commands, updates are sent to your personal fork rather than the upstream repository.
GitHub Host Handling and CLI Argument Construction
The github.Host struct in internal/scm/github/github.go (lines 47-55) manages GitHub CLI (gh) argument generation. When initialized via NewWithFork, it stores the fork owner separately from the upstream slug. The headRef method composes references as <owner>:<branch>, while repoArgs injects --repo <upstream-slug> to ensure all operations target the canonical repository.
How PR Creation and Lookup Handle Forks
Creating PRs Against Upstream with Fork Branches
The CreatePR method in github.go (lines 18-22) constructs the CLI command to open pull requests against the upstream repository while using the fork as the source. When a fork is present, the command renders:
gh pr create --repo owner/project \
--head fork-owner:feature-branch \
--base main
This pattern ensures the PR is opened against the upstream repository but references the fork's branch as the head.
Finding Existing PRs with Owner Validation
The FindPR method (lines 58-68) queries existing pull requests using gh pr list --head <branch> while passing --repo <upstream>. When a fork owner is configured, it requests additional JSON fields including headRepositoryOwner and validates matches using the matchesHead function. This prevents false positives when multiple forks use identical branch names.
The Complete Fork Routing Workflow
The fork routing implementation follows this execution flow:
-
Initialization: Run
no-mistakes init --fork-url <fork-url>to record the fork URL in the database (Repo.ForkURL). -
Push Operations: Any push uses
Repo.PushURL(), automatically routing to the fork remote when present. -
PR Creation: The system builds a
github.Hostinstance withNewWithFork, rendering the head reference as<fork-owner>:<branch>while maintaining--repo <upstream-slug>. -
PR Discovery:
FindPRfilters results by both branch name and fork owner, using theheadRepositoryOwnerfield to validate correct matches.
Implementation Examples
Pushing to the Correct Remote
When executing push operations, the pipeline uses Repo.PushURL() to determine the target:
// internal/pipeline/steps/push.go
pushURL := sctx.Repo.PushURL() // Returns fork URL if set, otherwise upstream
gitCmd := exec.CommandContext(ctx, "git", "push", pushURL,
fmt.Sprintf("%s:%s", localBranch, remoteBranch))
Creating a PR with Fork Context
The PR creation step initializes the host with fork awareness:
// internal/pipeline/steps/pr.go
func createPR(ctx context.Context, sctx *StepContext, branch, base string, title, body string) error {
// Host built with fork owner when fork URL exists
host := github.NewWithFork(cmdFactory, cliAvailable, hostName,
upstreamSlug, forkSlug)
content := scm.PRContent{Title: title, Body: body}
// Head renders as <fork-owner>:<branch>
_, err := host.CreatePR(ctx, branch, base, content)
return err
}
Finding Existing PRs
To locate existing pull requests with fork validation:
func findExistingPR(ctx context.Context, host *github.Host, branch, base string) (*scm.PR, error) {
// Adds extra JSON fields when fork owner is set
pr, err := host.FindPR(ctx, branch, base)
if err != nil {
return nil, err
}
return pr, nil
}
Key Source Files
The fork routing logic spans these critical files:
internal/db/repo.go: ImplementsPushURL()to select between fork and upstream remotes.internal/scm/github/github.go: ContainsNewWithFork,CreatePR, andFindPRmethods handling--repoand--headflags.AGENTS.md: Documents the fork routing policy requiring--repofor upstream and--head <fork_owner>:<branch>syntax.internal/pipeline/steps/push.go: DemonstratesPushURL()usage for routing pushes to the correct remote.
Summary
- no-mistakes distinguishes between upstream (
origin) and contributor fork remotes through theRepo.PushURL()method ininternal/db/repo.go. - Push operations automatically target the fork when configured, preventing unauthorized upstream writes.
- PR creation uses
--repo <upstream>combined with--head <fork-owner>:<branch>to open requests against the canonical repository while referencing fork branches. - The
FindPRmethod validates PR matches againstheadRepositoryOwnerto avoid collisions when multiple forks share branch names. - This architecture supports the standard GitHub workflow where contributors push to forks but integrate via the upstream repository.
Frequently Asked Questions
How does no-mistakes determine whether to push to origin or fork?
The tool checks Repo.ForkURL in internal/db/repo.go. If a fork URL is configured via no-mistakes init --fork-url, PushURL() returns that URL; otherwise, it falls back to the upstream URL. This ensures contributors never accidentally push directly to the canonical repository when they intended to update their fork.
Why does the PR creation command use --head with a prefix?
When a fork is configured, github.Host renders the head reference as <fork-owner>:<branch> (e.g., contributor:feature-branch). This syntax tells GitHub to look for the branch in the specified fork rather than the upstream repository, while the --repo flag ensures the PR itself is opened against the upstream project.
How does no-mistakes prevent matching PRs from the wrong fork?
The FindPR method in internal/scm/github/github.go requests the headRepositoryOwner field when listing PRs and validates it against the stored fork owner using the matchesHead function. This filters out PRs from other forks that might use identical branch names, ensuring the daemon only identifies the correct pull request.
Where is the fork routing policy documented?
The high-level behavior is specified in AGENTS.md under the Fork Routing section, which mandates that PR commands must always reference the upstream repository via --repo and use the fork owner only in the head reference. The implementation details live in internal/db/repo.go for URL resolution and internal/scm/github/github.go for CLI argument construction.
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 →