How do I maintain the version number of my bash script which is git source controlled?

I’ve just finished the first working version of a more complex bash script, and I’m wrapping my head around on how to maintain the scripts version.

Why do I need this? Following GNU Coding Standards For Commandline Interfaces I’ve added a version option which among a license and copyright header shows the current version.

  • Delete all .SVN folders in paths with embedded blanks
  • How do I configure git to inherit my specific $PATH environment variable?
  • Git / detached HEAD, get work back?
  • Git Bash giving sh.exe: ruby: command not found
  • git post-commit hook - script on committed files
  • How to git clone dotfiles into my home directory with one command?
  • Still, I don’t know how to keep the version ‘up-to-date’.

    My idea so far is to use git tags for major | minor | patch releases, and somehow replace a variable contained in the script.

    So, if I have a tag named 1.1.0, then

    $ myscript --version
    

    Should output something like:

    myscript 1.1.0
    

    The script contains a shell variable for this:

    version=1.1.0
    

    Still, I don’t now how to keep the version in sync with the latest tag?

    EDIT

    Sry for this slightly confusing question…

  • Git-flow reopening a finished feature
  • Git diff without comments
  • Sourcetree: Find out if a branch is merged
  • Replace working copy of git repo with actual contents of repo in .git?
  • git-bisect but for N repos
  • git checkout remote branch shows extraneous files?
  • 4 Solutions collect form web for “How do I maintain the version number of my bash script which is git source controlled?”

    As far as I can tell, what you want is impossible. In order to have the version number be committed into the version control software, you’d have to edit the version number, commit, then tag; not the other way around. You can however have the process a little more streamlined.

    We can do this by writing a post-commit hook that’ll read your script’s version number and if it has changed from last time, write out a new tag. In order to do this, cd into .git/hooks from your project directory, create a file called post-commit (or move post-commit.sample) and make it executable. Then edit it so it looks something like this:

    #!/bin/bash
    
    NEWEST_TAG=$(git describe --abbrev=0 --tags)
    
    SCRIPT_VERSION=$(grep "^version=" myscript | awk -F= '{print $2}')
    
    if [ x$NEWEST_TAG != x$SCRIPT_VERSION ]; then
        git tag -a $SCRIPT_VERSION -m "version $SCRIPT_VERSION"
    fi
    

    Next time you bump your script’s version number and commit your changes, you’ll see a new tag added to your latest commit.

    The hook-based answers previously given tightly couple the synchronization of the version string with a git commit; however it sounds like you want it to work the other way around, i.e. the version is extracted from manually created git tag metadata. It turns out that in fact this is pretty much the approach the git source code itself uses, so let’s learn how you could adopt something similar by examining its approach:

    • It has a GIT-VERSION-GEN shell script which attempts to intelligently determine the current version. Essentially it tries to extract the version string via git describe, but falls back to a hardcoded default if that doesn’t work. The version is written to GIT-VERSION-FILE at the top of the source tree.
    • The Makefile invokes this script and includes the generated file:

      GIT-VERSION-FILE: FORCE
              @$(SHELL_PATH) ./GIT-VERSION-GEN
      -include GIT-VERSION-FILE
      

      Now the version is accessible to the rest of the Makefile via $(GIT_VERSION).

    • Various Make rules then use this to perform substitutions on any files which need the version string hardcoded, such as various Perl scripts:

      $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl GIT-VERSION-FILE
              $(QUIET_GEN)$(RM) $@ $@+ && \
              INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C perl -s --no-print-directory instlibdir` && \
              sed -e '1{' \
                  [... snipped other substitutions ...]
                  -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
                  $@.perl >$@+ && \
              chmod +x $@+ && \
              mv $@+ $@
      

      For example if you look near the beginning of git-svn.perl, you’ll see:

      $VERSION = '@@GIT_VERSION@@';
      

    In my source tree, this rule has compiled to a file git-svn which contains the line:

        $VERSION = '1.7.11.rc0.55.gb2478aa';
    

    so if I check the version of my locally compiled git-svn, I see:

        $ git svn --version
        git-svn version 1.7.11.rc0.55.gb2478aa (svn 1.6.17)
    

    whereas if I run the rpm-based installation, I see:

        $ /usr/bin/git svn --version
        git-svn version 1.7.6.5 (svn 1.6.17)
    

    This last output demonstrates that the approach works even when the source code is compiled from a released tarball which does not contain any version control metadata in the .git/ subdirectory. This means that the version string nicely distinguishes between stable releases and development snapshots.

    Although git-svn is a Perl script, clearly the same substitution approach would work fine for your shell-script.

    Instead of creating the tag yourself, use a post-commit hook. The idea is to check if HEAD contains a line modifying the version assignment, and if so, create a new tag. This is just a rough example; it may be buggy, and it is definitely not as efficient as it could be.

    #!/bin/bash
    
    before=$( git log HEAD | awk -F= '/-version=/ {print $1}' )
    after=$( git log HEAD | awk -F= '/+version=/ {print $1}' )
    
    if [[ $before != $after ]]; then
        git tag myscript-$after
    fi
    

    Still, I don’t know how to keep the version ‘up-to-date’.

    I do like the post-hook recommendations. However, an alternative approach, you could also use a build system if you already have one available. Do you have an automatic build system? Jenkins or Bamboo? Our build system creates a new tag for each successful build passing all unit tests. Granted this is a script, so you may only have to run unit tests, if you have them. You can add task in your build job to increment or match the version with the tag of the successful build or latest commit which runs and passes any tests.

    Git Baby is a git and github fan, let's start git clone.