How the Fork Routing Mechanism Works for GitHub Pull Requests in no‑mistakes
The fork routing mechanism in no‑mistakes stores an optional fork_url in the local database and passes it to the GitHub CLI via the --head flag, allowing pull requests to originate from a contributor’s fork while targeting the upstream repository.
The no‑mistakes open‑source tool automates SCM workflows by abstracting provider‑specific commands into a unified pipeline. Understanding how it handles fork routing for GitHub pull requests is essential for contributors who work across multiple remotes and need to submit changes from personal forks rather than the primary repository.
Storing the Fork Configuration
When you initialize a repository, no‑mistakes persists an optional fork_url in its local SQLite database. This value determines whether subsequent pull requests route through a fork or originate from the current checkout.
In internal/db/repo.go, the schema stores the fork reference alongside the upstream URL:
INSERT INTO repos (id, working_path, upstream_url, fork_url, default_branch, created_at) …
You populate this column using the CLI:
no-mistakes init --fork-url https://github.com/your-username/no-mistakes-fork.git
The configuration layer in internal/config/config.go exposes this value via the Repo struct as sctx.Repo.ForkURL, making it available throughout the pipeline execution context.
Building the SCM Host with Fork Support
The decision to route through a fork occurs in internal/pipeline/steps/host.go within the buildHost function. This step constructs a provider‑specific scm.Host implementation based on the detected SCM provider.
For GitHub, the logic resolves two repository slugs:
case scm.ProviderGitHub:
repo := github.RepoSlug(sctx.Repo.UpstreamURL)
if repo == "" && sctx.Run.PRURL != nil {
repo = github.RepoSlug(*sctx.Run.PRURL)
}
forkRepo := ""
if sctx.Repo.ForkURL != "" {
forkRepo = github.RepoSlug(sctx.Repo.ForkURL) // ← fork routing
}
return github.NewWithFork(cmdFactory,
func() bool { return stepCLIAvailable(sctx, provider) },
repo, forkRepo), ""
repo– The slug of the upstream repository (e.g.,owner/main-repo).forkRepo– The slug extracted fromForkURLwhen present.
The constructor github.NewWithFork (defined in internal/scm/github/github.go) initializes the host with both slugs. When forkRepo is non‑empty, the host configures the underlying gh CLI to append the --head <fork> argument to all PR creation commands. If fork_url is empty, the pipeline falls back to github.New, which omits the --head flag and creates PRs directly from the local checkout.
Executing the Pull Request Step
The PR step itself (internal/pipeline/steps/pr.go) remains agnostic to fork routing. It delegates all provider‑specific logic to the host built by the previous step:
created, err := host.CreatePR(ctx, branch,
sctx.Repo.DefaultBranch, scm.PRContent(content))
When a fork is configured, host.CreatePR translates this call into a gh pr create command that explicitly targets the upstream repository while specifying the fork as the head:
gh pr create --repo owner/no-mistakes \
--head your-username/no-mistakes-fork \
--base main \
--title "feat: add feature" \
--body "Description of changes"
This matches the standard GitHub workflow where the PR base is the upstream default branch and the compare branch resides in the contributor’s fork.
Provider Limitations and Guardrails
The fork routing mechanism is currently implemented only for GitHub. The buildHost function contains explicit guardrails for other providers in internal/pipeline/steps/host.go:
case scm.ProviderGitLab:
if sctx.Repo.ForkURL != "" {
return nil, "fork PR routing for GitLab is not implemented"
}
...
case scm.ProviderBitbucket:
if sctx.Repo.ForkURL != "" {
return nil, "fork PR routing for Bitbucket is not implemented"
}
Attempts to configure a fork_url with GitLab or Bitbucket result in an immediate error with a clear log message, preventing half‑wired configurations.
Practical Configuration Examples
Initializing a Repository with Fork Support
To enable fork routing, provide the fork URL during initialization:
no-mistakes init --fork-url https://github.com/jdoe/no-mistakes-fork.git
Verify the configuration programmatically:
repo, _ := db.GetRepoByPath("./")
fmt.Println(repo.ForkURL) // → https://github.com/jdoe/no-mistakes-fork.git
Running a Pipeline That Creates a Fork‑Based PR
Execute the standard run command:
no-mistakes run
When the pipeline reaches the PR step, buildHost detects the configured ForkURL and invokes github.NewWithFork. The effective GitHub CLI command constructed by the host includes both the upstream target and the fork head, creating a cross‑repository pull request automatically.
Disabling Fork Routing for Direct PRs
To create a PR directly from the current repository without using a fork, clear the stored URL:
repo.ForkURL = ""
db.UpdateRepoForkURL(repo.ID, "")
Subsequent runs will invoke github.New instead of github.NewWithFork, generating gh pr create commands without the --head flag.
Summary
- no‑mistakes stores the optional
fork_urlin therepostable viainternal/db/repo.goand exposes it throughsctx.Repo.ForkURL. - The
buildHostfunction ininternal/pipeline/steps/host.godetects a non‑empty fork URL and instantiates the GitHub host usinggithub.NewWithFork. - When configured, the host passes the
--head <fork>flag to theghCLI, routing the pull request from the fork into the upstream repository. - The PR step in
internal/pipeline/steps/pr.godelegates to the host, requiring no special logic for fork handling. - Fork routing is GitHub‑only; GitLab and Bitbucket explicitly error if a
fork_urlis configured.
Frequently Asked Questions
How do I configure no‑mistakes to use my fork for pull requests?
Run the initialization command with the --fork-url flag: no-mistakes init --fork-url https://github.com/yourusername/repo-fork.git. This persists the URL in the local database, and subsequent PR steps automatically route through your fork using the --head argument with the GitHub CLI.
What happens if I don’t specify a fork URL?
If the fork_url column is empty, buildHost calls github.New instead of github.NewWithFork. The resulting gh pr create command omits the --head flag, creating the pull request from the current branch in the upstream repository rather than from a fork.
Does fork routing work with GitLab or Bitbucket repositories?
No. As implemented in internal/pipeline/steps/host.go, the tool returns an explicit error message ("fork PR routing for GitLab is not implemented" or the Bitbucket equivalent) if a fork_url is configured for non‑GitHub providers. This prevents incomplete workflow implementations.
Which source files control the fork routing logic?
The primary files are internal/pipeline/steps/host.go (host construction and fork detection), internal/scm/github/github.go (the NewWithFork constructor and CLI flag generation), and internal/db/repo.go (persistence of the fork_url column). The PR execution logic resides in internal/pipeline/steps/pr.go.
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 →