Version Bump for git using Fish Shell

Intro

One feature that I really like from npm is the version command which handles bumping either patch, minor, or major version number following the Semantic Versioning standards. This sets the version attribute in your package.json file and creates a git tag using the new version number.

For example you can run the npm version minor command from the root of an npm project and it will change the version from 1.2.12 to 1.3.0 write this out to your package.json file and create a git tag for the new version.

Since the vast majority of my projects are php projects and not npm, I don't get a chance to use this feature nearly as much as I would like, so I found myself wishing there was a way to do this directly from git.

Details

I have been a big fan of the fish shell since first learning about it on syntax fm's "The Command Line for Web Developers" episode, mostly for the autosuggest and autocomplete features, but more recently I've discovered the powerful scripting features. So I decided to see if I could implement this functionality through the command line via a fish script.

So basically what I wanted to do was to write a script that would read the current git tag, break it down into major, minor, and patch variables, increment the correct element, reassamble it into major.minor.patch format and create a new git tag. Once this was working I realized that if the major or minor version was incremented I needed to reset the following elements to zero. After that was worked out, I thought that I could save myself another step if it went ahead and pushed the new tag to my remote repo.

At first I had it working with a custom function name, so from the root of my repo I could type git_version minor and the new tag was calculated, created, and pushed. However, I don't personally like typing underscores, if I don't have to. So I wondered if I could "override" the git command and look for my custom command and then call git with the command line arguments, if it didn't see my custom version command. That way it would feel like a more natural extension to git. That is when I came up with this solution:

# ~/.config/fish/functions/git.fish
function git

    if count $argv > /dev/null
        if [ $argv[1] = 'version' ]
            git_version $argv
        else
            command git $argv
            return $status
        end
    else
        command git
        return $status
    end

end

This worked beautifuly. If git version minor is ran from the terminal, it calls my custom git_version function and passes the arguments to it, if anything other than version is passed as the first argument, it hands it off to git to handle. Of course, this can be altered to add many more custom commands to git, I'm looking forward to adding more functionality in the future.

And here is the custom function that is called if the version argument is passed to git. I'm sure there is room for improvements, but the few dozen times I've used it so far it behaved as expected. It expects an argument of either 'major', 'minor', or 'patch' to determine which element to bump, with an optional build argument of alpha or beta which will append a -alpha or -beta to the tag. There is also a --force flag which will create and push the tag even if there are uncomitted changes on the branch. The other flag is a --dry flag, if you just want to see the outcome of the command without creating a tag. Lastly, just running git version will display the current version.

# ~/.config/fish/functions/git_version.fish
function git_version
    set dry false
    set build ''
    set type ''
    set force false
    getopts $argv | while read -l key value
        switch $key
            case _
                switch $value
                    case major minor patch
                        set type $value
                    case alpha beta
                        set build $value
                end
            case d dry
                set dry true
            case f force
                set force true
            case v version
                _git_print_version
                return 0
            case \*
                # Handle unknown flags here
                echo unknown flag $key

        end
    end

    set v (git describe --abbrev=0 --tags 2>/dev/null)

    if [ $status != 0 ]
        set v '0.0.0'
    end

    # strip any thing from the hyphen after to remove -alpha or -beta
    set v (string split "-" -- $v)[1]
    set vmajor (string split "." -- $v)[1]
    set vminor (string split "." -- $v)[2]
    set vpatch (string split "." -- $v)[3]

    if [ $type = 'patch' ]
        set vpatch (math $vpatch + 1)
    else if [ $type = 'minor' ]
        set vminor (math $vminor + 1)
        set vpatch 0
    else if [ $type = 'major' ]
        set vmajor (math $vmajor + 1)
        set vpatch 0
        set vminor 0
    else
        echo $v

        return 0
    end

    if [ $build ]
        if [ $build = 'alpha' ]
            set build alpha
        else if [ $build = 'beta' ]
            set build beta
        else
            echo "The 'build' can only be blank, alpha, or beta"
            return 1
        end
        set tag $vmajor.$vminor.$vpatch-$build
    else
        set tag $vmajor.$vminor.$vpatch
    end

    if $dry
        echo $tag
    else
        if git_is_dirty
            if $force
                echo 'Forcing new version'
            else
                echo 'git is dirty - must use --force'
                return 1
            end
        end
        git tag $tag
        git push origin master --tags
    end
end

function _git_print_version
    echo git verison bump 1.2.0
end

Conclusion

Granted, this only works for a specific work flow, and the keystrokes it saves are negligible, but it was a neat learning experience and now I am ready to tackle some more fish scripting to make my life easier.

If you are interested in using this, it can be installed via fisher:

fisher add eidsonator/fish-git-version

The source code is available on github at fish-git-version and pull requests are welcome, if you see any room for improvement.

Please leave a comment if you can see any cons to me overriding git like this, or if you have any custom fish scripts you'd like to share.

If you liked this post, you can follow me @ToddEidson on Twitter for the best way (for now) to be notified of future blog posts.

Date Published: 10 February, 2019

Tags: semver fish git

About Todd

Todd Eidson is a full stack application developer and life-long learner. He is a pragmatic programmer, a believer in clean coding, and an evangelist for extensible and reusable code. His hobbies include collecting old jazz records and going to live music performances.

North Central Ohio, US
toddeidson[dot]info

Obligatory Disclaimer

All opinions are my own, probably wrong, and subject to change without notice.

© 2017 Todd Eidson. All content is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.