You should not be applying version strings to each commit. The git repo already has a git hash for that. Where the SemVer string comes into play, is on the packaging/publishing side of the production line. That's where you do your analysis of what has been included in the new release and whether they constitute breaking (major), non-breaking feature (minor) or bug fixes/cosmetic changes, and then generate a SemVer compliant change.
Tagging the commits is pointless and risky. What if post-build testing/analysis indicates that the specified change type (major, minor, patch) is incorrect? Change the tag in the repo? I suppose you could just rinse and repeat that processes until you get it right? Seems wasteful and potentially error prone, particularly when there are multiple actively developed clones of the repository.
Tags are transient by design. A tag can point to a specific commit hash one day, and another one the next. You risk exposing multiple mappings of the same tag to different commit hash values. In a large team, or any public repository, any number of clones could have different content with exactly the same label applied to them. This is a recipe for disaster, even if everyone is very very careful, and fully cognizant of the implications (risks). People make mistakes, particularly in agile environments, and managing repository tags correctly, is non-trivial.
The SemVer spec is clear, there shall be only one publicly available version <=> API/Package mapping. So the many common processes that do attempt to map semantic version strings with commit hashes, by using tags in their source repositories, aren't technically out of spec form the public's view of a products version history, provided the repository is kept private. The problem with this scheme is that the tooling used for internal testing, can't correctly use the same publish/release tooling internally, that it uses for external publish/release purposes, due to the internal violations of semantic versioning rules. It also comes with the risk of leakage into the public domain of version candidates that never made the cut.
The build should apply/record the commit hash to its outputs. Humans, assisted by process and automation should then determine the version label to slap on the product/package. This may involve analysis of semantic commit comments in the repository, the results of unit, integration and acceptance tests, internal customer advocacy, etc. When a determination is made to label a build output, an indelible record should be made that:
- Includes the new version label.
- Includes the commit hash the build operated on.
- Includes the feeds and associated package keys (name, version, Id, or whatever) and package hashes, that the build output was published to.
- The names and versions of all tooling used to produce the output.
- The content hash of the build output (though this is not always useful).
- Linkage to any records of process (discussions, process documentation, etc).
A reference to this record should be embedded in the package(s). External publication of some or all of this data is optional. The public will have access to the package and version, they can find the commit hash in a manifest, change log or other package/tooling appropriate data file. Post publication tagging of the git repo is okay for developer convenience, provided there is a well defined and secured process that takes its inputs from the publication process, and these tags should never be forwarded to the build system.
Note that it is okay to use non-human in the loop automation to generate prerelease semantic versions to packages/content for CI systems that feed up to the humans in the loop process(es) that determine which of those prerelease packages will be issued as non-prerelease publications.