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)