I've commented about this before.
The answer is development branches are forbidden but releases still use a kind of branching approach.
When you make a release you use commit A from main, then development continues, commit B adds a feature, and maybe commit C fixes a serious bug.
You don't want to make a new release at C because it includes new non tested features, instead you cherry-pick fixes to your release, test the new release candidates, and release that when ready.
Development still happens in main however.
Another big tool to minimise these problems is to separate the concept of feature release from the concept of binary release. You don't have to make a true deployment to release new features or roll them back, just use a toggle switch.