Skip to content

Gitflow and Gitversion wrapped in Powershell

2017-04-18

Silvrback blog image sb_float_right brings to teams. I think it adds just enough rigor with declared releases and features with the agility to being able to move fast for hotfixes and keep production humming along. Getting of continuous delivery pipelines against them is fairly straightforward.

The simplicity of it does come at the price a number of git commands to get your process moving but I think that after you've first become comfortable with the git command line and going though some of the gymnastics of using the processes directly some helper scripts become useful.

I started out with a Powershell module. First setting up the consumer with some variables to keep some churn down. $m = "master" and $d="develop". Since the high level unit of currency will be with features, releases and hotfixes I've setup functions for those concepts as well.

function Start-Feature { 
    [cmdletbinding()]
    param
    ( [Parameter(Mandatory=$true)]$name
    )
    process {
        Write-Host "Starting new feature $name" -ForegroundColor Green
        $name = $name -replace "feature/", ""
        git checkout -q -b "feature/$name" $d
    }
}

Silvrback blog image

The key here is that it's a branch cut from develop and with a useful name after the features/ prefix, such as feature/add-contact-form. This branch will land all the commits related to this feature and frees up other developers to also do independent features work derived develop as well.

As features are finished they are merged back into develop. Since there are couple of different flows around merging I found it useful to wrap that up as well for folks where are not super experienced with some of the subtle differences between rebase and merging.

function Update-BranchFrom {
    [cmdletbinding()]
    param(
        [Parameter(Position = 0)]$branch,
        [Parameter(ParameterSetName = "rebase", Position = 1)][switch]$rebase,
        [Parameter(ParameterSetName = "merge", Position = 1)][switch]$merge,
        [Parameter(ParameterSetName = "merge", Position = 2)][switch]$noff
    )
    process {
        if ($rebase) {
            Write-Host "git rebase $branch" -ForegroundColor Green
            git rebase $branch
        }
        else {
            if ($noff) {
                Write-Host "git merge --no-ff $branch" -ForegroundColor Green
                git merge --no-ff $branch
            }
            else {
                Write-Host "git merge $branch" -ForegroundColor Green
                git merge $branch
            }
        }
    }
}

I also have a function for rebase and not one for any specific merge flows is because I will rebase my feature branches from develop as develop lands commits or when really when any derived branches need to be updated from the source. Not everyone does it this way but I prefer my teams do use this approach as I personally keeps the software story cleaner.

function Resume-Rebase {
        git add -A
        git rebase --continue
}

Silvrback blog image

My release related functions take on another dependency (the first one being git itself) by requiring GitVersion in your path. GitVersion is a super useful tool for calculating current version numbers based on the state of your repo. It outputs Semantic Versioning or SemVer which is a solid version strategy in my view.

function Start-Release {
    [cmdletbinding()]
    param
    ( [Parameter(Mandatory=$false)][switch]$majorVersion
    )
    process { 
        Set-Branch $m
        $version = (gitversion | convertFrom-json)

        if($majorVersion) {
            $major = [int]$version.Major + 1
            $minor = [int]$version.Minor
        } else {
            $major = [int]$version.Major
            $minor = [int]$version.Minor + 1
        }
        $patch = [int]$version.Patch
        $name = "release/$major`.$minor`.$patch"

        Write-Host "Starting new $name" -ForegroundColor Green
        git checkout -q -b $name $d

        return $name;
    }
}

Silvrback blog image

This function needs to get the current version information from master so that is can increment the minor version number and use that for the next release. For example, v1.1.8 would have the next minor release of v1.2.0 or major release of v2.0.0.

Completing a release is as simple as merging the release branch down to master. This function then assigns a tag the current commit and heads back to develop and will rebase the latest state of master into develop and removes the release branch afterwards. This isn't as robust as it could be but if your source branches were correctly rebased and current it should "Just Work"

function Complete-Release {
    [cmdletbinding()]
    param( [Parameter(Mandatory=$true)]$releaseBranch
    ) 
    process { 
        Set-Branch $m

        $name = ($releaseBranch -split "/")[1]

        Update-BranchFrom $releaseBranch -merge -noff
        New-Tag $name

        Set-Branch $d
        Update-BranchFrom $m -rebase
        Remove-Branch $releaseBranch
    }
}

Silvrback blog image

Here is the New-Tag function

function New-Tag  {
    [cmdletbinding()]
    param ( [Parameter(Mandatory=$true)]$tag
    )
    process {
        Write-Host "git tag -a v$tag -m version v$tag" -ForegroundColor Green
        git tag -a "v$tag" -m "version v$tag" --force
    }
}

A Hot Fix release is almost identical except they are derived from master

function Start-HotFix {
    process {
        Set-Branch $m
        $version = (gitversion | convertFrom-json)
        $major = [int]$version.Major
        $minor = [int]$version.Minor
        $patch = [int]$version.Patch + 1
        $name = "hotfix/$major`.$minor`.$patch"

        Write-Host "Starting new $name" -ForegroundColor Green
        git checkout -q -b $name $m

        return "hotfix/$major`.$minor`.$patch"
    }
}
Silvrback blog image sb_float_center

function Complete-HotFix {
    [cmdletbinding()]
    param
    ( [Parameter(Mandatory=$true)]$hotfixBranch
    )
    process { 
        Set-Branch $m
        Update-BranchFrom $hotfixBranch -merge -noff
        Remove-Branch $hotfixBranch
        $tag = ($hotfixBranch -split "/")[1]
        New-Tag $tag
    }
}
Silvrback blog image sb_float_center

I have the module up on GitHub (https://github.com/motowilliams/poshflow) where PRs and suggestions are welcome.