Mainly my workflow is:
1. Commit often with moderately useful commit titles/texts.
2. Rebase -i prior to pushing to remote. During rebase, squash/fixup together relevant commits and write succinct commit titles with useful commit texts. Then push.
3. Rebase -i again at the end of the PR to guarantee everything is coherent and that each commit that makes it in the merge at least builds and passes existing tests (i.e. that functionality before the branch isn't broken in the branch).
4. Merge into master. Merge commit should include the PR info (like PR text contents) in the merge text if at all possible.
5. Forget. Maybe move to a new git host.
The years later when I identify a bug I can just:
1. Put together a reproducible example.
2. Git bisect --first-parent (only top level commits/merges) to identify the merge where the bug was introduced.
3. Git bisect the merge itself to identify the exact commit that introduced it.
4. From there I can determine the context of hows and whys behind the bug being introduced, identify a possible solution, and evaluate any process changes that could have prevented this bug from being introduced elsewhere in the project.