4. Git branching strategies for CAE tool development
CAE automation codebases are not ordinary application repositories. A single repository often contains solver wrappers that shell out to Nastran, Abaqus, or Fluent; meshing automation that drives ANSYS Meshing, Gmsh, or HyperMesh through their scripting APIs; post-processing utilities; and the CI pipelines that validate all of them. These components have different release cadences and different failure modes. A meshing script that silently produces a skewed element distribution is far more dangerous than a wrapper that crashes immediately, because the defect only surfaces after a multi-hour solve. A branching strategy for this kind of repository must therefore make it easy to isolate risky changes, freeze validated solver interfaces, and patch a production pipeline without dragging in half-finished meshing work.
This chapter covers a three-tier branching model — main, release/*, and hotfix/*, with short-lived feature/* branches feeding into main — and shows how it maps onto the concrete artifacts of CAE tool development.
Why a Flat Branch Model Breaks Down
Many CAE teams start with a single long-lived develop branch plus feature branches. This works until the first production incident. Suppose a wrapper for Abaqus 2023 is validated and deployed to the nightly DOE pipeline. Two weeks later, a developer merges a feature that upgrades the wrapper to Abaqus 2024 syntax. The nightly pipeline now fails on every job, but the failure is buried in a solver log that nobody reads until morning. With a flat model there is no clean way to ship the Abaqus 2023 fix without also shipping the 2024 upgrade.
The three-tier model solves this by separating integration from release state. main is the integration branch where features land. release/* branches are frozen snapshots that production pipelines consume. hotfix/* branches are cut from a release branch, patched, and merged back into both the release branch and main.
Feature Branches: One Solver Interface per Branch
The most important rule for feature branches in a CAE repository is one solver interface per branch. A branch named feature/nastran-wrapper-v2024 should touch the Nastran wrapper, its unit tests, and nothing else. If the same change also requires a meshing tweak, that goes on a separate branch. This keeps the diff reviewable by a solver expert who may not understand the meshing code.
A practical feature branch workflow for a solver wrapper looks like this:
git checkout main
git pull --ff-only
git checkout -b feature/nastran-wrapper-v2024
# Edit wrapper: src/wrappers/nastran.py
# Add regression fixture: tests/fixtures/nastran_v2024_plate.bdf
# Update expected output: tests/expected/nastran_v2024_plate.json
git add src/wrappers/nastran.py tests/fixtures/nastran_v2024_plate.bdf
git commit -m "nastran: support SOL 101 card changes in v2024"
git push -u origin feature/nastran-wrapper-v2024
The regression fixture is the critical artifact. For a solver wrapper, a unit test that mocks the solver proves nothing. The fixture should be a small, fast-running deck — a 10-element plate, a 100-cell CFD case — that exercises the exact card or keyword that changed. The expected output is a JSON or CSV file containing the quantities the wrapper extracts: max displacement, first natural frequency, drag coefficient. When the CI job runs, it compares the wrapper's parsed output against this file with a tolerance appropriate to the quantity.
Release Branches: Freezing a Validated Pipeline
A release branch is cut from main when a set of solver wrappers and meshing scripts has passed the full validation suite. The branch is named after the pipeline version, not the date: release/2024.1, release/2024.2. Once cut, only bug fixes and documentation changes are allowed on it. New features go to main and wait for the next release.
The production CI pipeline should be configured to build from release branches only. In GitLab CI this is a single rule:
deploy:production:
stage: deploy
script:
- ./ci/package_pipeline.sh
- ./ci/push_to_artifact_registry.sh
rules:
- if: '$CI_COMMIT_BRANCH =~ /^release\/.*$/'
when: manual
- when: never
This prevents an accidental merge to main from reaching production. It also means that when a solver vendor releases a new version mid-cycle, the upgrade lands on main and is validated there, while production continues to run the frozen release.
Hotfix Branches: Patching a Live Pipeline
A hotfix is needed when a production pipeline produces wrong results or fails outright. The branch is cut from the affected release branch, not from main:
git checkout release/2024.1
git checkout -b hotfix/mesh-tolerance-fix
# Fix: src/meshing/tolerance.py — correct absolute-to-relative
# tolerance conversion for thin-walled geometries
git commit -am "meshing: fix relative tolerance for aspect ratio > 50"
git push -u origin hotfix/mesh-tolerance-fix
After the fix passes CI on the release branch, it is tagged (v2024.1.1) and merged back into main so the fix is not lost in the next release. The merge-back is the step teams most often forget, and it is the one that causes the same bug to reappear three months later.
main with a merge commit.
Mapping Branches to CAE Artifacts
| Branch type | Typical CAE content | Validation gate | Lifetime |
|---|---|---|---|
feature/* |
One solver wrapper, one meshing script, or one post-processor | Unit tests + fixture regression on a small deck | Days to two weeks |
main |
Integrated wrappers, meshing, CI config | Full regression suite on representative models | Permanent |
release/* |
Frozen wrapper set + pinned solver versions | End-to-end DOE smoke test | One release cycle |
hotfix/* |
Single defect fix on a release branch | Targeted regression + smoke test | Hours to two days |
Practical Failure Symptoms and Triage
When the branching model is applied incorrectly, the symptoms are recognizable. A production pipeline that suddenly fails after a routine main merge usually means the production job was pointed at main instead of a release branch. A hotfix that "disappears" after the next release means the merge-back to main was skipped. A feature branch that has been open for three weeks and now conflicts with every other branch usually means it was scoped too broadly — split it by solver interface.
The discipline that makes this model work is not the Git commands; it is the rule that a release branch is a contract. Once release/2024.1 is cut, the solver versions, meshing parameters, and wrapper interfaces it contains are fixed. Any change to that contract requires a new release branch, not a commit to the existing one.