Question

I have a git repository with a long history. Releases were tagged along the master branch over time. If a user wants to go back and check out an old tagged release, they do not have the advantage of using an up-to-date build system or updated references to external data sources, licensing, etc.

The ideal end-goal is for the user to check out a tag, and have that point to a commit hash that links to an updated version of the old release with up-to-date build environments, pointers to external data, etc.

This is a widely used, multi-user repository.

I see three options, looking for recommendations:

  1. create release/ branches that get updated but never merged back into master (low risk). Tag the head commit of that branch, and maintain as needed, deprecating maintenance for older versions.
  2. git rebase to rewrite history and bring updated older tagged releases back into master/develop, but out of temporal order (high risk)
  3. Do nothing, and provide an external solution to apply a patch when the user checks out an old tag.
Was it helpful?

Solution

In my previous company we had the same problem, we had to "update" old releases e.g. to allow them to run on modern OS oe support a new version of the old hardware.

Our solution was your #1, create a branch for each release; we called them "maintenance" branches. Our git looked something like:

  • maintenance/product_v1.0 (tags: v1.1, v1.2, v1.3...)
  • maintenance/product_v2.0 (tags: v2.1, v2.1.1, v2.2...)
  • master (the future v3.0)

When a new OS version began to be used in out products, for example, I would check out the release branch and made the necessary modifications to allow the code to compile and run on both the new OS and the old ones. If a formal installation was to be done, I would also do an official release and a tag. There is no need to keep tagging the head of the maintenance branch, you can just checkout the branch (you can think of the branch name exactly as a special tag always pointing to the HEAD of that branch).

I would definitely NOT rebase, that would be confusing, having two independent histories coexist in the same branch - that's why separate branches are meant for :-) And in any case rebasing on branches used by many people should be avoided because it generates little annoyances to everybody (e.g. pushes suddenly rejected and generation of unnecessary merge commits).

OTHER TIPS

A tag denotes a specific point in development history. It should not change, at all. Ideally, build should be reproducible. This means checking out a tag and building it should - bit-for-bit - produce the exact same artifact, every time. Changing the versions of the build tools involved will alter that. Thats mostly not even desired.

But there is an alternative: branches.

You can have a stable branch containing the current, stable production ready release of your software, a development branch that you are working on, and several branches for major versions, e.g:

  • master
  • development
  • release-1.0.x
  • release-1.1.x
  • release-2.0.x

If you ever need to change the tooling with which version 1.0 is built, you check out the release-1.0.x branch. Lets say its HEAD is also currently tagged v1.0.3.

If you update the build tooling and make sure it works, e.g. it build on a new OS version, then you tag that version 1.0.4 and release it as such, with a changelog denoting that these are compatibility changes for that new OS version. If you just change tooling versions, you'd put "maintenance changes" or something alike in the changelog.

"Updating" a tag seems ill-advised, as tags represent specific points in the development of the software, and thus shouldn't just change or jump around.

With the aforementioned setup it is also easy to backport changes, e.g. security fixes, to older versions of your software.

I'd suggest making a separate versioned maintenance branch, and create a new versioned tag whenever you re-release an old version with new build environment. That way also, if a bug is caused by the changed build environment, there's clarity on which versions are affected as they'd be on different patch version.

If your build system is fairly simple, you can bring the old maintenance branch up to date by simply doing git checkout -- dirname and/or git cherry-pick. If you have more sophisticated build system, you probably would want to create a separate repository for your build system and add that other repo as a git submodule or as a formal dependency of your project.

Licensed under: CC-BY-SA with attribution
scroll top