Skip to content
Download PDF

Multi-Repo Workspace ​

Most real projects are not one repository. A single logical change — "add billing" — lands in the API, the web app, and the infra repo, and nothing in the usual toolchain tells you those three commits are the same piece of work.

forge workspace makes that visible: this repo plus every sibling declared under repo.related, each with its own resolved git provider and active changes.

sh
forge workspace                       # the whole workspace
forge workspace --change add-billing  # which repos contain this change
forge workspace --json                # machine output (absolute paths)
Workspace: 4 repo(s)

  forge       GitLab    self                          2 change(s)
  spec-kit    GitLab    ../spec-kit                   0 change(s)
  gentle-ai   GitHub    ../gentle-ai                  0 change(s)
  web         GitHub    https://github.com/acme/web   (remote)

Changes spanning multiple repos:
  add-billing   forge, spec-kit

Note the two providers in one workspace — a polyrepo project that straddles GitLab and GitHub is normal, and forge resolves each sibling independently.

Configuration ​

yaml
# .forge/config.yaml
repo:
  related:
    - name: api
      path: ../api                       # local checkout, read fully
    - name: web
      url: https://github.com/acme/web   # remote-only
      # provider: github                 # optional; else detected

Paths are absolute or relative to this repo's root. A remote-only sibling gets its provider resolved from the URL but reports no changes — forge does not clone anything to answer this question.

Spanning changes ​

SharedChanges — the "spanning" section — lists change names present in more than one repo. That's the signal worth acting on: it tells you a change is not done when its API side is merged but its web side isn't, and it's what coordinated cross-repo delivery builds on.

--change <name> inverts the view:

Change "add-billing" spans 2 repo(s): forge, api
Change "nothere" is not present in any workspace repo

Coordinated cross-repo delivery ​

--workspace on forge deliver and forge issues create runs the command in every workspace repo that contains the change — each through its own provider:

sh
forge deliver --change add-billing --workspace --dry-run
Workspace delivery for change "add-billing" across 2 repo(s): main, api

── main (GitLab) ──
Opening merge request on GitLab (…) via glab
  [dry-run] glab mr create --title "Add billing" --description "…"

── api (GitHub) ──
Opening pull request on GitHub (…) via gh
  [dry-run] gh pr create --title "Add billing" --body "…"

delivered 2/2 repo(s)

One command, one logical change, a merge request on GitLab and a pull request on GitHub — each body built from that repo's own proposal and tasks.

Per-repo isolation ​

One repo failing does not stop the others. Its error is reported under its own section, the remaining repos still run, and the command exits non-zero:

delivered 1/2 repo(s); 1 failed
Error: 1 of 2 repo(s) failed

That shape is deliberate: a partial delivery is a real state you need to see — knowing which repo refused (and why) is more useful than aborting the batch at the first failure. Each repo keeps its own readiness gate and --require-ci check.

Local checkouts only

Delivery runs glab/gh inside each repository, so a remote-only sibling (declared by url: with no local path:) cannot be delivered to — it is skipped with a note, never counted as a failure. Clone it and point path: at it to include it.

Degradation ​

A workspace never fails because one sibling is broken. A missing path, an unreadable changes directory, or an entry with neither path nor url is included with a note explaining why — carrying the resolved absolute path so the note is actionable — and every other repo still resolves.

See also ​