DEV Community

Rizwan Saleem
Rizwan Saleem

Posted on

A Practical Git-Workflow for Multi-Repository Projects

A Practical Git-Workflow for Multi-Repository Projects

A Practical Git-Workflow for Multi-Repository Projects

Managing a project that spans multiple repositories can feel like herding cats. This guide presents a concrete, end-to-end Git workflow tailored for multi-repo codebases, shared components, and coordinated releases. It emphasizes clarity, traceability, and resilient collaboration across teams.

Why a multi-repo workflow matters

  • Separate concerns: core services, shared libraries, and tooling live in distinct repos, reducing blast radii.
  • Clear ownership: teams own repositories, branches, and CI configurations.
  • Coordinated releases: orchestrated version bumps and changelogs across repos.

This workflow focuses on: branching strategy, cross-repo dependencies, integration testing, release coordination, and automation to keep momentum without chaos.

Prerequisites

  • Git installed (>=2.30 recommended)
  • Access to all relevant repositories (read/write as appropriate)
  • A centralized CI/CD system that can run across repos (e.g., GitHub Actions, GitLab CI, or Jenkins)
  • A semantic versioning policy (e.g., MAJOR.MINOR.PATCH) ### 1) Choose a unifying branching model

Adopt a consistent three-tier model that spans all repos:

  • main (or master): production-ready state harvested from all repos
  • develop (or next): integration of features that are ready for QA
  • feature/*: short-lived branches for individual features or experiments

Rules:

  • Feature branches are created from develop and merged back into develop after code review and local testing.
  • Release branches (optional) can be used to stabilize a specific production release; they’re branched from develop and merged into main after release commits.
  • Hotfix branches are created from main when critical issues appear in production; merged back into both main and develop.

Tips:

  • Enforce protected branches for main and develop with required reviews, status checks, and signed commits.
  • Maintain a cross-repo PR discipline: when a feature touches multiple repos, coordinate via cross-repo issues or meta-PRs. ### 2) Establish a cross-repo dependency protocol

When one repo relies on another (e.g., core library A used by service B), define a dependency protocol:

  • Version pins: services pin to specific library versions (semantic tags like v1.2.3) instead of always pulling latest.
  • Lockfiles where applicable: use a lockfile system for dependencies that supports multi-repo constraints (e.g., a monorepo-style manifest that’s updated per release).
  • Compatibility matrix: maintain a small matrix showing compatible combinations of versions across repos.

Implementation example:

  • Each repo maintains a manifest.json or package.json-like file, listing compatible versions of its dependencies.
  • A separate “integration” script computes a compatible set of versions before merges to develop. ### 3) Standardize a cross-repo integration test plan

A robust integration test ensures that changes in one repo don’t break others.

Model:

  • Repo-centric tests: unit and integration tests run in each repo (CI).
  • Cross-repo tests: a dedicated integration workflow runs on a combined test environment, pulling specific version pins from the manifests.
  • Staging environment: assemble a temporary environment replicating production with all inter-repo dependencies pinned.

CI workflow sketch (pseudo for GitHub Actions):

  • workflow: ci-unit

    • trigger: push/pull_request
    • jobs:
    • test-unit-in-repo: run unit tests for that repo
  • workflow: ci-integration

    • trigger: on develop or release branches
    • jobs:
    • fetch-deps: read manifests, resolve versions
    • bootstrap: install and link across repos
    • run-integration-tests: start services in a test harness and validate end-to-end paths

Automation tip:

  • Use a "golden" test dataset and seed to ensure deterministic results across environments. ### 4) Create a shared release process across repos

A coordinated release ensures consistency and traceability.

Steps:
1) Decide release scope: which repos are included and which versions will be bumped.
2) Bump versions: update version numbers in each repo’s manifest and changelog.
3) Create release PRs: one per repo, or a single meta-release PR that references sub-PRs.
4) Run integration tests: ensure end-to-end correctness with patched versions.
5) Deploy to staging and perform smoke tests.
6) Tag and publish: create version tags and publish artifacts (e.g., Docker images, packages).
7) Notify stakeholders: summarize changes and impacts.

Automation tips:

  • Use a release bot that can create PRs, bump versions, and add release notes from conventional commits. ### 5) Implement a robust commit message convention

Adopt a conventional-commit-like standard tailored for multi-repo work:

  • feat(repo): description of new feature in repo
  • fix(repo): bug fix in repo
  • feat(core-lib): feature in shared library
  • chore(docs): documentation updates
  • test(repo): tests added or updated

Example:

  • feat(service-a): add new health check endpoint
  • fix(core-lib): fix serialization bug for message payloads

Guidelines:

  • Keep messages concise (50-72 characters for the subject line).
  • Include a brief motivation in the body if needed.
  • Reference related issues or PRs across repos.

    6) Use a cross-repo issue tracker

  • Create a top-level initiative or epic issue in a project management tool.

  • Link related repo issues to the top-level epic.

  • Use a standardized label schema to indicate cross-repo work (e.g., cross-repo, service-a, core-lib, integration).

Workflow example:

  • A new feature requires changes in service-a, service-b, and core-lib.
  • Open issues in each repo and connect them to the cross-repo epic.
  • When all linked issues are closed, proceed with the joint release process. ### 7) Debugging cross-repo failures

Strategies:

  • Reproduce locally with pinned versions: clone all repos, check out the specific versions, and run the integration suite in a controlled environment.
  • Use feature flags: enable cross-repo features behind flags to isolate failures.
  • Add observability: correlate logs and metrics across services (trace IDs, standardized log formats).

Practical tip:

  • Maintain a test harness repository that can spin up the required services with Docker Compose or Kubernetes manifests for reproducibility. ### 8) Example workflow: a feature touching multiple repos

Let’s walk through a concrete scenario.

  • You’re adding a shared “audit-log” feature used by two services and a core library.
  • Repos involved: core-lib, service-a, service-b, and a shared infra repo.

Step sequence:
1) Create feature branches:

  • core-lib: feature/audit-log-impl
  • service-a: feature/audit-integration
  • service-b: feature/audit-integration
  • infra: feature/audit-configs

2) Implement in parallel:

  • core-lib implements new audit-log API.
  • service-a and service-b adapt to consume the API.
  • infra updates deployment manifests to enable the feature flag.

3) CI runs per repo:

  • unit tests pass locally.
  • integration tests for cross-repo flow pass on a dedicated integration runner.

4) Resolve cross-repo compatibility:

  • update manifests to pin new audit-log versions.
  • adjust service configs to work with the new API.

5) Open a cross-repo PR or a meta-release PR:

  • Link all PRs and issues.
  • Run the cross-repo integration tests in CI.

6) Merge after approvals:

  • Merge feature branches into develop for all repos.
  • Run final integration suite in a fresh environment.

7) Release:

  • Bump versions, update changelogs, and tag releases.
  • Deploy to staging, perform smoke tests, then promote to production. ### 9) Practical command examples

Note: adjust remote/repo names to your setup.

  • Create a feature branch in multiple repos:

    • git fetch all
    • git checkout -b feature/audit-log-impl
    • git push -u origin feature/audit-log-impl
  • Bump version in a manifest (example.json):

    • jq '.version = "1.2.0"' example.json > tmp.json && mv tmp.json example.json
    • git add example.json
    • git commit -m "chore(core-lib): bump to v1.2.0 for audit-log feature"
  • Create a cross-repo integration test job (GitHub Actions style, pseudo):

    • name: Run cross-repo integration
    • run: | ./scripts/resolve-versions.sh ./scripts/bootstrap-all.sh ./scripts/run-cross-repo-tests.sh
  • Open a meta-release PR (conceptual):

    • Use your tool to create PRs in each repo, all referencing a central epic number. ### 10) Metrics and governance

Track these to gauge health over time:

  • Release cadence: time from develop → main merge
  • Cross-repo integration test pass rate
  • Mean time to fix cross-repo failures
  • Dependency compatibility incidents
  • Number of blocked days due to version conflicts

Governance practices:

  • Quarterly review of the cross-repo dependency policy
  • Rotate owners for the integration test harness to avoid tribal knowledge
  • Maintain a public changelog for major cross-repo features

    11) Quick-start checklist

  • Decide on main/develop/feature naming conventions and enforce via branch protection

  • Define a cross-repo dependency protocol and maintain manifests

  • Set up per-repo CI and a centralized cross-repo integration test workflow

  • Establish a release process with coordinated version bumps

  • Create a cross-repo issue tracker and link related work

  • Implement feature flags to minimize risk when integrating across repos
    If you’d like, I can tailor this workflow to your exact tech stack (GitHub, GitLab, Bitbucket; languages; CI tools) and sketch concrete YAML files for your setup. Do you want an example pipeline configured for GitHub Actions with a sample manifest format and a short, real-world repo mapping?

-

Rizwan Saleem | https://rizwansaleem.co

Top comments (0)