Skip to content
This repository was archived by the owner on Aug 5, 2019. It is now read-only.

How does SimpleGitVersion work?

Olivier Spinelli edited this page Dec 3, 2015 · 18 revisions

##Git Basics CSemVer (http://csemver.org) can be applied to Git repository: this is the goal of the SimpleGitVersion project. To understand it, try to forget the Git branches and consider the basics of Git:

  • A commit point has a ‘content’: its ‘File tree’ is our ‘base of code’.
  • A commit can have tags: in our case, when a commit is tagged with ‘v4.5.0-rc.2’ this means that the ‘File tree’ of this commit contains and defines everything we need to build this exact version of our product/artefact.
  • Tags are unique across the repository: there can be only one commit tagged with ‘v4.5.0-rc.2’.
  • More than one commits can contain identical File trees and this is easy to detect: these File trees share the same SHA1 that we call ContentSHA.
  • A commit point is either:
    • An orphan (the initial commit in ‘master’ commit for instance)
    • Based on one commit: it carries the differences between itself and its Parent.
    • Based on 2 (or more) commits: it merges its 2 (or more) Parents’ File tree into one File tree.
  • There can not be cycles in the graph. This is a classic DAG.

This is our playground for Git, no more no less. You may wonder where the branches are in this picture. We don’t use branches. Of course, branches help, they are important to organize the repository (and the work), but we consider them to live at a higher level of organization.

We are claiming that proper versioning can, and should, be achieved by considering only the commits and the topology of the repository.

##The algorithm SimpleGitVersion can compute two sets of versions for any commit C in the repository. The first step is to compute the Base Versions (Bv) of C:

  • Considering P(C), all the parent commits of C.
  • Computes the Base Versions Bv of C based on:
    • Tc = The greatest Version Tag that appears in P(C).
    • Mc = The greatest Version Tag or Content Tag that appears in P(C).
    • 0v = The special no-version Tag (its successors are the Very First Possible Versions).
    • Base Versions are: Bv = {Tc, Mc}, {Tc}, {Mc} or {0v} (when there is no Tc nor Mc).

Based on these (at most) two versions for C, the PossibleVersions and PossibleVersionsStrict are computed.

The PossibleVersionsMode option enables to choose the actual set that will be used by SimpleGitVersion.

PossibleVersions (the AllSuccessors mode)

This set is the union of the direct successors of the commit point's Base Versions from which any version greater or equal to any existing version in the repository is excluded.

This set of versions allow minor versions (with new features) to evolve independently of an existing major subsequent version. This is typically the one to use in branches for version that must offer "Long Term Support".

PossibleVersionsStrict (the Restricted mode)

This set is a subset of the PossibleVersion: any version that has an existing successor in the repository and is not a patch is excluded.

In a repository with v4.2.0 and v5.0.0, commits based on the v4.2.0 can not be v4.3.0 or v4.3.0-alpha. However, v4.2.1-alpha (and other PreRelease versions) or v4.2.1 can be released.

The algorithm itself (C#)

Instead of describing the algorithm, it is simpler to directly expose the code:

void ComputePossibleVersions()
{
  var allVersions = _tagCollector.ExistingVersions.Versions;

  // Special case: there is no existing versions (other than this 
  // that is skipped if it exists) but there is a startingVersionForCSemVer,
  // every commit may be the first one. 
  if( _tagCollector.StartingVersionForCSemVer != null 
      && (allVersions.Count == 0 || (allVersions.Count == 1 && ThisTag != null)) )
  {
    _possibleVersions = new[] { _tagCollector.StartingVersionForCSemVer };
    _possibleVersionsStrict = _possibleVersions;
  }
  else
  {
    var versions = allVersions.Where( c => c != _thisCommit );

    List<ReleaseTagVersion> possible = new List<ReleaseTagVersion>();
    List<ReleaseTagVersion> possibleStrict = new List<ReleaseTagVersion>();
    foreach( ReleaseTagVersion b in GetBaseVersions() )
    {
      // The base version b can be null here: a null version tag correctly 
      // generates the very first possible versions (and the comparison 
      // operators handle null).
      var nextReleased = versions.FirstOrDefault( c => c.ThisTag > b );
      var successors = ReleaseTagVersion.GetDirectSuccessors( false, b );
      foreach( ReleaseTagVersion v in successors
                   .Where( v => v > _tagCollector.StartingVersionForCSemVer 
                                && (nextReleased == null || v < nextReleased.ThisTag) ) )
      {
        if( !possible.Contains( v ) )
        {
          possible.Add( v );
          if( nextReleased == null || v.IsPatch )
          {
              possibleStrict.Add( v );
          }
        }
      }
    }
    _possibleVersions = possible;
    _possibleVersionsStrict = possibleStrict;
  }
}
Clone this wiki locally