Web-development – How to handle version number in code, across branches

gitversioningweb-development

I'm working on a PHP app, using three main branches: 'master' (current stable release), 'bugfix' (for patches) and 'next' (for next minor version).

We store the current version number in the code – it's used for a few things including checking for updates to the app, and also simple cache-busting for CSS/JS files (like styles.css?v=1.2.3) which helps when updating.

My problem is regarding merge conflicts. Say I have version 1.2.3, and 'next' is branched off there to start work on version 1.3. I change the version in that branch to 1.3.0 or 1.3.0-dev. Then I need to fix a bug in the 1.2 line, so the 'dev' branch is patched and version 1.2.4 is released.

But now when I merge the changes into the 'next' branch I get merge conflicts, because the same line was edited in both branches. Are there any strategies to avoid this? Or should I just resolve the merge conflict and move on?

I've seen a few answers across SE that talk about using git tags (which we do use for releases) but I don't think that really helps my situation since we need the version number in the code.

Best Answer

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.