First of all, your current approach seems perfectly valid.
Working from several systems (I understand here several PCs in different places, or eventually different OS compiling the same sources) doesn't change the fact that you still work on the same piece of software. So this should not not be the major influencer of your branching strategy.
However, it could be interesting to create branches for new major features, especially if significant refactoring is required. Branching could then allow you to still maintain the current version while working on the new one, and in case of serious troubles switch back to the working version.
If you work solo on a single system, you could avoid such branching by keeping the changes in the local repository before pushing them to your central repository. But branching would facilitate things especially if implementation of the new features take longer time.
But in your case, when working on several systems, you can't rely on the local repository. So the branching would definitively bring you the benefit to have "work in progress" shared between several systems.
Be careful however not to make your branching strategy too complex, especially if you're working solo on your project.
Read the version from metadata generated at build time
One thing you can do is to read the version from your Git tags at build time. Generate the version from your git tags after commiting, as a part of the release process. After determining the version at the beginning of the build process, store it as metadata in a text or key/value pair file, in a location with other build artifacts or metadata that won't get stored in your repo (it should be generated as a part of the build, after a commit has been made, to the local working directory only).
The version is therefore metadata that is a build artifact regenerated every build rather than stored in the repo. It is still available to your code because the file is generated at build time, before final linking or storage of external resources into your DLL or executable. Therefore, the final executable has access to this version information at runtime.
- The advantages of this approach:
- You have a single source of truth - the git tags
- You are still able to access the version in your code
- You do not need to store the version directly in the source code, so there are not merge conflicts
- The disadvantages are:
- You still need to resolve the version conflicts - just it happens during the tagging process rather than as a merge conflict
- During development, you must ensure that development builds have tags in the repo or a means of generating them in the metadata, and that metadata must be regenerated for each build (or in-between official builds for development, if necessary).
To accomplish this, you will likely need to create a script to generate the version file or a value/pair entry file (JSON, etc.). As a part of your build process, run the script, then run the rest of the build.
After a build, you will have the metadata file available, so if the source is run in a script environment, it can access the version, but it will always be the old version from the last build.
For example, in the base Python Packaging module, setuptools
, there is an extension available called PBR that uses this method. It autogenerates the version info from git tags, including automatically incrementing based on a rule for every commit that doesn't have a tag. It then writes the version info into an xxx.egg-info/PKG-INFO
file that is a standard metadata file for Python packages. There are a functions to pull the version and other metadata info if needed in the live scripts. These files are typically included in .gitignore
, so they must be regenerated as a part of a package release or running tools that generate an executable.
Best Answer
Your code doesn't have a version. It's your compiled program which has a version.
Create the version number when you do the build and have a build step which tags the git repo at the point at which it pulled the source, with that version number