A workflow I was introduced to and have worked with a lot in my teams is,
1. Everyone uses feature branches
2. Everyone cleans up their branch using interactive rebase on top of main before review
3. All merges have been freshly rebase on main
I think it works very well and keeps the history clean while preserving "each commit does one thing".
I worked on a team that did this, + required feature branches to get squashed to a single commit before merge. On that commit, we'd require a very brief bullet list of changes, sometimes a POC, and a link to the PR. This made git blame a lot more helpful when debugging issues. I am a big fan of this approach.
I wish my team would at least use the rebase merge strategy by default to avoid branch graphs looking like the London tube network. Not in 10 years did I have an issue with that (okay, except accidentally rebasing deliberate no-ff merges).
The one time I suggested that, someone immediately came up to me trying to convince me that rebases are the most dangerous thing ever.