petervanderdoes / gitflow-avh

AVH Edition of the git extensions to provide high-level repository operations for Vincent Driessen's branching model
http://nvie.com/posts/a-successful-git-branching-model/
Other
5.42k stars 527 forks source link

[Question] git flow possible incorrect versioning? #444

Open CrispyDrone opened 4 years ago

CrispyDrone commented 4 years ago

In GitFlow, are you allowed to merge features into develop once a release branch has been created?

As far as I can tell this seems to result in incorrect versioning of certain commits, namely:

Is this actually an issue? If not, please explain why. If yes, how do you deal with it?

At work, we've been experiencing a real issue related to this.

For the sake of the example, imagine 2 repositories, both using git flow.
Repository A consumes an artifact published from repository B (nuget package).
Repository B is currently at version 1.0.0 in master, and version 1.1.0-alpha+3 in develop.

Now the following sequence of actions occurs:

  1. Repository B creates a release branch from 1.1.0-alpha+3. Meanwhile development continues, and two additional features are merged with develop. Repository B now has version 1.1.0-beta+0 in the release branch, while develop is already at 1.1.0-alpha+5.
  2. Repository A takes a dependency on 1.1.0-alpha+5 version of repository B that was created after the release branch in repository A was started.
  3. Repository B's release branch is finished, and a stable package version 1.1.0 is created.
  4. Repository A's dependency on 1.1.0-alpha+5 is upgraded to the stable equivalent 1.1.0.
  5. BOOM: 1.1.0 does not contain the 2 additional features 1.1.0-alpha+4 and 1.1.0-alpha+5.

I guess there are multiple ways around this problem:

  1. Only consume stable packages. However, in our current environment this is not realistic as we have major release cycles.
  2. Work with alpha/beta versioning for both the file versions and git tags. By doing this, any new features merged into develop after the release branch has been started will be working towards the "next next" version. In the example that would result in 1.2.0-alpha+1 and 1.2.0-alpha+2 instead of 1.1.0-alpha+4 and 1.1.0-alpha+5.
talios commented 4 years ago

This sounds like a generic problem of using git describe for your version numbers, which to me feels quite dangerous as it only counts that N-commits-since-tag were made, not WHAT commits, or WHO committed on what repository/clone.

Not sure about how things are normally done in .NET / nunet land, but within the Java/Maven ecosystem I’m familiar with - we work on the basis that in-development artifacts are merely snapshots in time, and local version numbers end with -SNAPSHOT.

So for the scenario you have, the develop branch would be building 1.1.0-alpha-3-SNAPSHOT, if this were deployed to a repository for testing, the -SNAPSHOT is replaced with a timestamp.

However, when you create your release branch, you’d update the version to 1.1.0-beta-1-SNAPSHOT, which during the release process would update that version further to 1.1.0-beta-1 - and tag the repository with that specific commit, deploy that to nuget etc.

…then finally update the version to be 1.1.1-alpha-1-SNAPSHOT which gets merged back into develop (by way of master) for any subsequent development builds.

Are you deploying these git describe based ‘releases’ to a repository on every commit/push?

I don’t think this is so much a gitflow issue, but more a CI/CD management flow issue.

One potential solution could be:

Altho that IMHO, still leads to position that beta+1 doesn’t refer to a unique/addressable point in time.

From: CrispyDrone notifications@github.com notifications@github.com Reply: petervanderdoes/gitflow-avh reply@reply.github.com reply@reply.github.com Date: 15 October 2020 at 2:03:14 PM To: petervanderdoes/gitflow-avh gitflow-avh@noreply.github.com gitflow-avh@noreply.github.com Cc: Subscribed subscribed@noreply.github.com subscribed@noreply.github.com Subject: [petervanderdoes/gitflow-avh] [Question] git flow possible incorrect versioning? (#444)

Now the following sequence of actions occurs: Repository B creates a release branch from 1.1.0-alpha+3. Meanwhile development continues, and two additional features are merged with develop. Repository B now has version 1.1.0-beta+0 in the release branch, while develop is already at 1.1.0-alpha+5. Repository A takes a dependency on 1.1.0-alpha+5 version of repository B that was created after the release branch in repository A was started. Repository B's release branch is finished, and a stable package version 1.1.0 is created. Repository A's dependency on 1.1.0-alpha+5 is upgraded to the stable equivalent 1.1.0. BOOM: 1.1.0 does not contain the 2 additional features 1.1.0-alpha+4 and 1.1.0-alpha+5. I guess there are multiple ways around this problem: Only consume stable packages. However, in our current environment this is not realistic as we have major release cycles. Work with alpha/beta versioning for both the file versions and git tags. By doing this, any new features merged into develop after the release branch has been started will be working towards the "next next" version. In the example that would result in 1.2.0-alpha+1 and 1.2.0-alpha+2 instead of 1.1.0-alpha+4 and 1.1.0-alpha+5.

CrispyDrone commented 4 years ago

Thank you for your reply @talios. Sorry for my late response.

Could you explain a bit more what you mean with:

However, when you create your release branch, you’d update the version to 1.1.0-beta-1-SNAPSHOT, which during the release process would update that version further to 1.1.0-beta-1 - and tag the repository with that specific commit, deploy that to nuget etc.

Is 1.1.0-beta-1-SNAPSHOT reflected in the version number that's typically stored in a file like package.json or AssemblyInfo.cs? Next, the CI system simply uses this version and optionally attaches some build metadata to it?

So when I'm about to finish my release branch, is it tagged with 1.1.0-beta-1 or 1.1.0? If the former, how does master now get the 1.1.0 tag? Is this possible with the gitflow extensions of this repository?

…then finally update the version to be 1.1.1-alpha-1-SNAPSHOT which gets merged back into develop (by way of master) for any subsequent development builds.

This update to 1.1.1-alpha-1-SNAPSHOT is done in the release branch? You merge back master with develop? In the original git flow model it is described that you merge release back into develop, not master into develop? Also, I presume you meant 1.2.0-alpha-1-SNAPSHOT instead of 1.1.1-alpha-1-SNAPSHOT?

Now to further clarify my situation. We don't explicitly version anything in our local repositories. git describe and the files that contain version numbers are examples that I used to highlight the potential issue that I think exist in how git flow operates or how certain tools make assumptions about versioning.

My current understanding of git describe is that you should be able to rely on it to give you the previous version of your repository from the perspective of a random commit. But, I just realized that there's actually no reason for that commit to then automatically have to be included in the "next release". This depends entirely on the branching model. For example in Github Flow, it could be that a feature branch doesn't get merged for a very long time, and until you would do a rebase it would naturally report a much older version number, and at any point in time when you check this, there's no guarantee it's going to be included in the "next release".

However with git flow there is no such concept. Any new development on the develop branch must get included in the next release. So if my previous assumption is correct, then in theory git checkout develop && git describe --abbrev HEAD should always give you the version number that belongs to the previous release, and any commits that report this version number must belong to the next release.

To come back to the tooling, the setup we currently have is entirely automated through the git flow extension and a CI task known as GitVersion. Our "release manager" is actually responsible for everything except for creating features and hotfixes (don't ask me why, we are trying to improve the situation desperately).

So us developers, we don't care about versions numbers at all in our local repos. We simply create feature branches and merge them with develop.

GitVersion automatically attaches a version number to every build our CI server does. The information after + is actually build metadata which for develop only includes the build number, it's not the number of commits past a git tag as reported by git describe, apologies if I confused you. If someone builds their feature branch, it will also include the feature name.

We do not translate every build of repository B into a release of its nuget package. This could be done, but we don't have this automatic trigger configured.

Instead developers release a version when they need it. So for example, a developer is working on a new "feature" that requires modifying repository A and B at the same time. They now need to consume their new development in repository A so they simply publish a nuget package from whatever artifact has their development—in this case that was 1.1.0-alpha+5. This developer will merge their work with the develop branch in repository A. Some time later when 1.1.0 is published for repository B, there is the danger that anybody decides to upgrade the non-stable package of repository B to its stable equivalent. But this will blow up due to the lack of the 2 required features (introduced in 1.1.0-alpha+4 and 1.1.0-alpha+5).

For me personally, the thing that makes the most sense is that as soon as you create your release branch, any contributions on develop already have to be for the "next next" release. In the original git flow article that doesn't seem to be described, and the git flow extensions here don't seem to promote that either.

What I really want is 2 things:

It would be of real help if we there was a guide somewhere that achieves this and describes it it in terms of:

ChrisJStone commented 4 years ago

For me personally, the thing that makes the most sense is that as soon as you create your release branch, any contributions on develop already have to be for the "next next" release. In the original git flow article that doesn't seem to be described, and the git flow extensions here don't seem to promote that either.

The nature of how branches work in git this is all ready the case. As long as you do not merge any changes from the release branch into develop prior to release you could consider develop to be in a code freeze state in regards to the status of release.

From A successful Git branching model

"Release branches support preparation of a new production release. They allow for last-minute dotting of i’s and crossing t’s. Furthermore, they allow for minor bug fixes and preparing meta-data for a release (version number, build dates, etc.). By doing all of this work on a release branch, the develop branch is cleared to receive features for the next big release."

In regards to

every version number (local, or not) should refer to a unique, indentifyable binary and the source code that produced it

For your release branches this is all ready taken care of for your source code. As part of git flow release finish the master branch is tagged with your release tag. Once again from A successful Git branching model

"When the state of the release branch is ready to become a real release, some actions need to be carried out. First, the release branch is merged into master (since every commit on master is a new release by definition, remember). Next, that commit on master must be tagged for easy future reference to this historical version. "

version files (package.json, AssemblyInfo.cs,...): when to update them and how

You update them as often as you find necessary. You could use a text editor such as vim or write a script file to parse an environment variable.

version tags: when to add git tags, with what versions

Same as above. Create tags as often as you find necessary. Once again A successful Git branching model does a fantastic job of explaining some of the different branches used and the actual git commands needed to create branches and tags.

build numbers on CI servers

That's usually covered in the docs for your CI server.

In the end it is up to you and your development team to design the workflow that you are going to use. IE when to create branches, when to tag, when to merge how to merge etc...

CrispyDrone commented 4 years ago

Thanks for your response @ChrisJStone

I think that first quote is taken a bit out of context, or maybe I wasn't clear enough in my wording. New developments on the develop branch after a release branch has been created are indeed for the "next next" release. That is great. The problem for me is that nor the git flow extensions of this repository, or the original document are clear for me in regards to how versioning is supposed to work in this scenario.

As mentioned in the section "creating a release branch" of the original article:

After creating a new branch and switching to it, we bump the version number. Here, bump-version.sh is a fictional shell script that changes some files in the working copy to reflect the new version. (This can of course be a manual change—the point being that some files change.) Then, the bumped version number is committed.

This means the version number in develop remains unaffected. What happens to it is not described.

As soon as you merge the release branch back into develop, these files that were updated by the bump-version.sh will get updated. From the perspective of the develop branch, the version in these files is completely unreliable now because any new developments that occurred before the release branch was merged back will still have the previous version even though they didn't get released with other commits that shared that same version. This is part of the problem I've been describing (the same problem exist for "git describe" in my opinion).

So based on this, and from the original description of git flow, I presume that the intention is that develop doesn't have any "explicit versioning", which is supported by:

It is exactly at the start of a release branch that the upcoming release gets assigned a version number—not any earlier. Up until that moment, the develop branch reflected changes for the “next release”, but it is unclear whether that “next release” will eventually become 0.3 or 1.0, until the release branch is started. That decision is made on the start of the release branch and is carried out by the project’s rules on version number bumping.

However, if develop doesn't have a well-defined version or a version at all, that makes it very impractical in my current situation as we have large release cycles and often need to be able to take dependencies on software packages that are not release candidates or stable (refer to my previous posts for an example). That of course means that any package produced from develop would need a well-defined version.

As for the possible solutions:

The reason for opening this issue of course was to be able to understand how other people are dealing with this issue, and if it's not an issue in their workflows, how is that so?

Also, maybe I'm wrong in this sentiment, but I feel that a branching model and versioning should be pretty much plug-and-play. So I don't really agree stating that these are completely up to me/my team. I would say that the existence of these git-flow extensions, and tools such as GitVersion are a testament to that belief.

So since git-flow can automatically create tags based on the release branch name when finishing a release, maybe it should do something similar when starting a release? Concretely, I was looking more into GitVersion and they actually seem to recommend something of the nature of tagging the release branch with an "alpha" tag (however, I'm not sure if they actually mean immediately tagging it as soon as you create your release branch. I hope so, and will try to get some clarifications from them):

Note that at least one version tag is required on the branch. The recommended initial tag is {releaseVersion}.0-alpha1. So for a branch named release-1.2 the recommended tag would be 1.2.0-alpha1

ChrisJStone commented 4 years ago

Your welcome @CrispyDrone, I may have.

However, if develop doesn't have a well-defined version or a version at all, that makes it very impractical in my current situation as we have large release cycles and often need to be able to take dependencies on software packages that are not release candidates or stable

If you have a well defined versioning policy in place that is not a problem. I'm going to assume that you have a understanding of semantic versioning. With X.Y.Z being Major.Minor.Patch now you have also have a number of addition qualifiers such as ALPHA, BETA, RC, DEV, SNAPSHOT etc..... along with a build number, timestamp or what ever additional metadata your team decides is necessary to identify a unique version.

In my case I prefer to gpg-sign various commits to my project repository. So I typically do the following when I setup a new git repository.

I will perform a git init followed by git commit --allow-empty --gpg-sign. This way I can create the master branch and I know every commit made to master is signed by me. After this is when I run git flow init. I have not bothered to do so but you can modify the git-flow scripts so that it will perform a --gpg-sign.

Once all of my initial setup is completed I will create an initial release. I usually will use 0.0.1 for this release. It really has no functionality but it's a starting point for development.

At this point I would have 2 commits on my master branch. With the second commit having the TAG of 0.0.1 and X number of commits on my DEV branch.

Now at the start of my changelog I would have a version of #### 0.0.1-DEV1 <--- I use this to track my versions As commits are made to my dev branch I will increment my version with the number of commits made since the last merge from master. When I make a release from dev I will create a SNAPSHOT-# tag for example 0.0.1-SNAPSHOT-1

So I can have a repository version of 0.0.1-DEV4 with a release version of SNAPSHOT-1 and later on 0.0.1-DEV20 and SNAPSHOT-2

When I create a release such 0.1.0 I will update the version in dev to 0.1.0-DEV1 <-- since this is the start of a new development cycle

and the version in my release branch to 0.1.0-RC1 with RC being incremented as final bug fixes etc are made. Once a final release is made I commit the release branch to master and develop along with creating tag 0.1.0

My hotfix branches would be done similarly only I would start with 0.1.1 as my initial hotfix branch.

This particular fork of git-flow supports a number of hooks that could help you with what you need to accomplish. However you need to write the actual scripts. Now there are a couple of repos that @petervanderdoes has here that provide some example scripts. One of which he used while he was writing this fork of git-flow so it is setup for this repo.

I would say that the existence of these git-flow extensions, and tools such as GitVersion are a testament to that belief.

The whole point of git-flow and GitVersion is to help you the developer be more productive in your job. Instead of having to remember and type out multiple git commands to create and finish a release for your project all you have to type is git flow release start and the name of your branch and git flow release finish to finish the branch. It is still up to you to decide what to name that branch and the tag that is created.

I feel that a branching model and versioning should be pretty much plug-and-play.

If that were the case then why are there so many different VCS packages or CI servers. I've thought the same thing. In the end sadly it falls on us developers. With the number of different CI servers, VCS packages even development methodologies it's really not possible. The best thing that we can do is have a though understanding of how the software works and either find an existing tool that does what were looking for, find something close and write glue code to make it work within our development life cycle or write the tool ourselves.

There are software engineering standards that you can follow. However even in the standards it clearly stats that it is up to the end user of those standards to customize them to fit the individual needs.

Do you or the company you work for have a document that clearly states how your development and release life-cycles will work? What tools will you use, how will the release be versioned?

With out knowing exactly what the problem is we don't know what tool to use.