# How the Fork Routing Mechanism Works for GitHub Pull Requests in no‑mistakes

> Understand the fork routing mechanism for GitHub pull requests. Learn how no-mistakes directs PRs from forks to your upstream repo for seamless collaboration.

- Repository: [Kun Chen/no-mistakes](https://github.com/kunchenguid/no-mistakes)
- Tags: internals
- Published: 2026-07-16

---

**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`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/db/repo.go), the schema stores the fork reference alongside the upstream URL:

```go
INSERT INTO repos (id, working_path, upstream_url, fork_url, default_branch, created_at) …

```

You populate this column using the CLI:

```bash
no-mistakes init --fork-url https://github.com/your-username/no-mistakes-fork.git

```

The configuration layer in [`internal/config/config.go`](https://github.com/kunchenguid/no-mistakes/blob/main/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`](https://github.com/kunchenguid/no-mistakes/blob/main/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:

```go
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 from `ForkURL` when present.

The constructor `github.NewWithFork` (defined in [`internal/scm/github/github.go`](https://github.com/kunchenguid/no-mistakes/blob/main/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`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/pr.go)) remains agnostic to fork routing. It delegates all provider‑specific logic to the host built by the previous step:

```go
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:

```bash
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`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/host.go):

```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:

```bash
no-mistakes init --fork-url https://github.com/jdoe/no-mistakes-fork.git

```

Verify the configuration programmatically:

```go
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:

```bash
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:

```go
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_url` in the `repos` table via [`internal/db/repo.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/db/repo.go) and exposes it through `sctx.Repo.ForkURL`.
- The `buildHost` function in [`internal/pipeline/steps/host.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/host.go) detects a non‑empty fork URL and instantiates the GitHub host using `github.NewWithFork`.
- When configured, the host passes the `--head <fork>` flag to the `gh` CLI, routing the pull request from the fork into the upstream repository.
- The PR step in [`internal/pipeline/steps/pr.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/pr.go) delegates to the host, requiring no special logic for fork handling.
- Fork routing is **GitHub‑only**; GitLab and Bitbucket explicitly error if a `fork_url` is 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`](https://github.com/kunchenguid/no-mistakes/blob/main/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`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/host.go) (host construction and fork detection), [`internal/scm/github/github.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/scm/github/github.go) (the `NewWithFork` constructor and CLI flag generation), and [`internal/db/repo.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/db/repo.go) (persistence of the `fork_url` column). The PR execution logic resides in [`internal/pipeline/steps/pr.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/pr.go).