gg_multi

gg_multi is the multi-repository workspace engine of the Gg Multi Suite. It manages a master workspace of registered repositories and organisations, lets you create ticket workspaces that scope a subset of those repos to a single feature or bugfix, and orchestrates cross-repo actions (commit, push, review, publish, …) in dependency order.

gg_multi is normally driven via the gg CLI (which auto-detects workspace vs. single-package mode), but it also ships its own executable for direct use and in CI/CD pipelines.

A complete German hands-on walkthrough is available in handbook.md — recommended reading for new users.

What gg_multi gives you

  • A persistent master workspace under .master/ containing every registered repo and organisation.
  • Per-ticket workspaces under tickets/<id>/ that hold scoped clones of the repos you need for one feature.
  • Automatic dependency resolution: every cross-repo command runs in dependency order so downstream packages see consistent upstream state.
  • Path localisation: while you work on a ticket, intra-workspace pubspec.yaml references point to local paths; on review they are re-localised to Git refs.
  • A single review pipeline (do review) that brings every repo of a ticket into a state ready for merge or publish.

Installation

git clone https://github.com/ggsuite/gg_multi.git
cd gg_multi
./install         # or install.bat on Windows

This installs the gg_multi executable globally. In most day-to-day work you will use the gg CLI instead (dart pub global activate gg), which routes its shared can/did/do commands to gg_multi whenever you are inside a workspace.

Command Hierarchy

gg_multi
├── ls    repos | organizations | deps <target> | tickets
├── can   commit | push | publish | review
├── did   commit | push
└── do    commit | push | publish | review | cancel-review
          add | add-deps | rm | create ticket
          init | code | claude
          execute | install-git-hooks | install-gitattributes

All cross-repo commands run inside a ticket directory (tickets/<id>/) and iterate over the ticket's repos in dependency order.

gg_multi ls

Command Purpose
gg_multi ls repos list every repo in the master workspace, sorted by name
gg_multi ls organizations list every GitHub organisation represented in the master workspace
gg_multi ls deps <target> list dependencies / dev_dependencies of <target>
gg_multi ls tickets list every ticket workspace under tickets/

gg_multi do — workspace setup

Command Purpose
gg_multi do init initialise the master workspace in the current directory
`gg_multi do add --force]`
gg_multi do rm <target> remove a repo from the master workspace or delete a ticket workspace
gg_multi do create ticket <id> [-m <description>] create tickets/<id>/ with a .ticket file
gg_multi do add-deps <target> add every dependencies / dev_dependencies of <target> to the master workspace
gg_multi do code open the current ticket in VS Code
gg_multi do claude aggregate each repo's CLAUDE.md into one ticket-level CLAUDE.md
gg_multi do install-git-hooks install gg's git hooks in every ticket repo
gg_multi do install-gitattributes install a shared .gitattributes in every ticket repo
gg_multi do execute <cmd> run a shell command in every ticket repo

gg_multi do add is context-aware:

  • run from the workspace root: the repo is cloned into .master/,
  • run from inside a ticket (tickets/<id>/): the repo is also copied into the ticket and its local dependencies are pulled in.

gg_multi can — preflight checks

Command Purpose
gg_multi can commit run gg can commit in every ticket repo (analyze + format + tests)
gg_multi can push check that every ticket repo is push-ready
gg_multi can publish check that every publishable repo is publish-ready
gg_multi can review check that every repo is localized and has no uncommitted changes

Each can command aborts on the first failure so you find out early when a repo is in a bad state.

gg_multi do — execute across ticket repos

Command Purpose
gg_multi do commit -m <message> commit every ticket repo with the same message
gg_multi do push [--force] push every ticket repo
gg_multi do review unlocalise → localise as Git refs → pub upgrade → commit → push, for every repo
gg_multi do cancel-review revert a review and return to local working mode
gg_multi do publish publish every publishable package of the ticket

gg_multi did — reporting

Command Purpose
gg_multi did commit report which repos have new commits since the last reference
gg_multi did push report which repos have new pushed commits

Folder layout

my_project/
├── .master/                # every registered repo (managed by gg_multi)
│   ├── gg/
│   ├── gg_multi/
│   └── …
└── tickets/
    └── PROJ-123/           # one ticket workspace
        ├── .ticket         # JSON with id + description
        ├── app_core/       # ticket-scoped clone
        └── ui_core/

WorkspaceUtils.detectTicketPath() walks up the directory tree from wherever you invoke gg_multi to find the matching workspace, so the commands work from any sub-directory inside it.

Step-by-step: working on a ticket end-to-end

0. One-time project setup

mkdir my_project
cd my_project
gg_multi do init
gg_multi do add https://github.com/my-org    # pull in every repo of an org

1. Create a ticket workspace

gg_multi do create ticket PROJ-123 -m 'Simplify login flow'
cd tickets/PROJ-123

2. Add the repos you need

gg_multi do add app_core ui_core

Local dependencies are pulled in automatically and pubspec.yaml references are localised to relative paths inside the ticket.

3. Open the ticket (optional)

gg_multi do code

4. Develop and iterate locally

Work in each repo as you normally would. Inside a single repo you can run gg one check for the full single-repo check pipeline.

5. Commit across all ticket repos

gg_multi can commit
gg_multi do commit -m 'Simplify login flow'

can commit runs the per-repo check pipeline in dependency order and aborts on the first failure; do commit then commits each repo with the same message.

6. Push

gg_multi can push
gg_multi do push

7. Review

gg_multi do review

For every ticket repo this runs:

  1. Unlocalise references (back to original form via gg_localize_refs), status → unlocalized.
  2. Re-localise as Git references, status → git-localized.
  3. dart pub upgrade (if pubspec.yaml exists).
  4. gg do commit with a default review message.
  5. gg do push.

Need to keep working after starting a review?

gg_multi do cancel-review

8. Publish (when approved)

gg_multi can publish
gg_multi do publish

Publish is meant to be triggered manually by a human after review approval.

Non-interactive publish via --config

gg_multi do publish is interactive by default — it asks for a merge message and a version increment (patch / minor / major) for every publishable repo. In scripted runs, CI pipelines or release tooling this is awkward. Pass --config <path> to load the merge messages and increments from a JSON file instead:

gg_multi do publish --config .gg-publish.json

gg_multi looks for the file at <configArg> first (relative to the current directory, or absolute), then under the ticket directory. Missing fields cause a hard FormatException — no silent fall-back to an interactive prompt.

.gg-publish.json schema
{
  // Top-level defaults applied to every repo of the ticket.
  "version_increment": "patch",            // "patch" | "minor" | "major"
  "merge_message": "Default merge message",

  // Optional: per-repo overrides. Keyed by the repo's directory name
  // inside the ticket. Each field is independent — anything missing
  // falls back to the top-level value.
  "repos": {
    "<repoName>": {
      "version_increment": "minor",
      "merge_message": "Custom message for this repo"
    },
    "<otherRepo>": {
      "merge_message": "Only override the message, keep the default increment"
    }
  }
}

Resolution order per repo:

  1. repos.<repoName>.version_increment / merge_message, then
  2. top-level version_increment / merge_message.

If neither layer supplies a value for a repo that is about to publish, the run aborts with an error naming the missing field.

Example
// tickets/PROJ-123/.gg-publish.json
{
  "version_increment": "patch",
  "merge_message": "PROJ-123: simplify login flow",
  "repos": {
    "app_core": {
      "version_increment": "minor",
      "merge_message": "PROJ-123: new public login API"
    }
  }
}

Then:

cd tickets/PROJ-123
gg_multi do publish --config .gg-publish.json

app_core gets a minor bump with the custom message; every other ticket repo gets a patch bump with the top-level message.

The same flag exists on the single-repo gg one do publish (it reads the same schema but only uses the top-level fields). See the gg_one README for details.

Running tests

dart test

A coverage report can be generated with:

dart run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info

gg_multi is held at 100 % test coverage.

Getting help

gg_multi -h
gg_multi do -h
gg_multi do add -h
gg_multi can -h
gg_multi ls -h

Further reading

  • handbook.md — full hands-on walkthrough (German).
  • The sibling gg package — unified CLI that auto-routes shared commands between gg_one (single repo) and gg_multi (workspace).

License

gg_multi is licensed under the terms specified in the LICENSE file.

Libraries

gg_multi