This has come up on a few projects for me. The best solution I've had so far is to generate a version number like this:
x.y.<number of commits>.r<git-hash>
Typically, it's generated by our build system using a combination of some static file or tag to get the major revision numbers, "git rev-list HEAD | wc -l" (which was faster than using git log), and "git rev-parse HEAD". The reasoning was follows:
- We needed the ability to have high-level versioning happen explicitly (i.e. x.y)
- When parallel development was happening, we needed to NEVER generate the same version number.
- We wanted to easily track down where a version came from.
- When parallel lines were merged, we wanted the new version to resolve higher than either of the branches.
Number 2 is invisible to most people, but is really important, and really difficult with distributed source control. SVN helps with this by giving you a single revision number. It turns out that a commit count is as close as you can get, while magically solving #4 as well. In the presence of branches, this is still not unique, in which case we add the hash, which neatly solves #3 as well.
Most of this was to accommodate deploying via Python's pip. This guaranteed that "pip install" would maybe be a bit odd during parallel development (i.e. packages from people on different branches would intermingle, but in a deterministic fashion), but that after merges, everything sorted out. Barring the presence of an exposed rebase or amend, this worked quite nicely for the above requirements.
In case you're wondering, we chose to put the r in front of the hash due to some weirdness with how Python packaging handles letters in version numbers (i.e. a-e are less than 0, which would make "1.3.10.a1234" < "1.3.10" < "18.104.22.1684").