8
\$\begingroup\$

I found nvie's git branching model simple enough to grasp and suitable for my projects - but the frontend supplied at GitHub was far too complex for me to understand. Hence, I wrote this script to start and finish tasks, automatically running the related commands to speed it all up a bit (without it becoming too abstract/complex).

I've read a handful of tutorials on writing shell scripts but found it difficult to find one that emphasized on portability/POSIX compliancy. I'd like the script to run just fine in sh, dash, bash and zsh. Any feedback is appreciated - for one thing, I'm a bit unsure about where to use $variable and ${variable}...

Intended usage

When I want to create a new branch for a task I run this:

gittask.sh new feature name_of_feature

When I'm done:

gittask.sh done

Finishing a task has it automatically deduce by the branch name prefix (feature/release/hotfix) what to do next.

  • Features are merged back into the development branch.
  • Releases are continuously merged back into the development branch (bugfixes) and, once the release branch is considered stable enough (decided upon by the user), merged into master (with a tag), ending the release branch.
  • Hotfixes are merged back into both the master (with a tag) and the development branch.

Branch model

I use two persistent/remote branches, master and development, as well as three temporary/local branches, feature, release and hotfix.

Master branch

  • Persistent
  • Remote branch
  • Each merge into master equals a new tag

Development branch

  • Persistent
  • Remote branch

Feature branches

  • Temporary
  • Branch off from development
  • Merge back into development
  • Naming: "subject" or "issue#" if on issue tracker ("feature/" is prepended)

Release branches

  • Temporary
  • Branch off from development
  • Merge back into development (continuously) and master (when done)
  • Naming: "major.minor.z", e.g. "1.2.0" (z is defined by hotfix, "release/" is prepended)
  • Signed tag needed when merging with master

Hotfix branches

  • Temporary
  • Branch off from master
  • Merge back into development and master
  • Naming: "x.y.fix", e.g. "1.2.1" (x, y is defined by release, "hotfix/" is prepended)
  • Signed tag needed when merging with master

Script

#!/bin/sh
# gittask.sh: taskbased git branching utility
# This script requires that git has been installed and properly configured,
# that the remote "master" and "development" branches exist (locally too) 
# and that a network connection to the "origin" repository is established.
set -o errexit
usage()
{
 echo
 echo "Usage:"
 echo " gittask.sh new feature name_of_feature"
 echo " - Creates a new branch off from 'development' named"
 echo " 'feature/name_of_feature'."
 echo " gittask.sh new release name_of_release"
 echo " - Creates a new branch off from 'development' named"
 echo " 'release/name_of_release'."
 echo " gittask.sh new hotfix name_of_hotfix"
 echo " - Creates a new branch off from 'master' named"
 echo " 'hotfix/name_of_hotfix'."
 echo " gittask.sh done"
 echo " - Merges current branch into master and/or development"
 echo " depending on if it's a feature, release or hotfix."
}
delete_branch()
{
 # Infinite loop, only way out (except for Ctrl+C) is to answer yes or no.
 while true; do
 echo "Delete $current branch? "
 read yn
 case $yn in
 [Yy]* ) 
 git branch -d ${current}
 break
 ;;
 [Nn]* )
 echo "Leaving $current branch as it is."
 break
 ;;
 * )
 echo "Error: Please answer (y)es or (n)o."
 ;;
 esac
 done
}
define_tag()
{
 # Don't proceed until both variables have been set.
 while [ -z ${version_number} ] && [ -z ${version_note} ]; do
 echo "Enter version number (major.minor.fix): "
 read version_number
 echo "Enter version number note: "
 read version_note
 done
}
# Confirm that user is in a git repository, abort otherwise.
git status >/dev/null 2>&1 || { echo "Error: You're not in a git repository."; exit 1; }
# If "new", confirm that the required arguments were provided.
if [ "1ドル" == "new" ] && [ -n "2ドル" ] && [ -n "3ドル" ]; then
 
 # Validate 3,ドル only allow a-z (lower case), 0-9 and _ (underscore) in branch names.
 [ "${3//[0-9a-z_]/}" = "" ] || { echo "Error: Branch names may only consist of a-z, 0-9 and underscore."; exit 1; }
 case 2ドル in
 feature )
 git checkout development
 git checkout -b "feature/3ドル"
 exit 0
 ;;
 release )
 git checkout development
 git checkout -b "release/3ドル"
 exit 0
 ;;
 hotfix )
 git checkout master
 git checkout -b "hotfix/3ドル"
 exit 0
 ;;
 * )
 echo "Error: You didn't specify feature, release or hotfix."
 exit 1
 ;;
 esac
# If "done", proceed to determine current branch and by that what to do next.
elif [ "1ドル" == "done" ]; then
 current=`git branch | awk '/\*/{print 2ドル}'`
 case ${current} in
 feature* )
 echo "Merging into development branch..."
 git checkout development
 git merge ${current}
 git push origin development
 delete_branch
 exit 0
 ;;
 release* )
 echo "Merging into development branch..."
 git checkout development
 git merge ${current}
 git push origin development
 # Infinite loop, only way out (except for Ctrl+C) is to answer yes or no.
 while true; do
 echo "Merge into master (make a release)? "
 read yn
 case $yn in
 [Yy]* )
 echo "Merging into master branch..."
 git checkout master
 git merge ${current}
 define_tag
 git tag -s ${version_number} -m ${version_note}
 git push --tags origin master
 delete_branch
 break
 ;;
 [Nn]* )
 echo "Leaving branch master as it is."
 break
 ;;
 * )
 echo "Error: Please answer (y)es or (n)o."
 ;;
 esac
 done
 exit 0
 ;;
 hotfix* )
 git checkout master
 git merge ${current}
 define_tag
 git tag -s ${version_number} -m ${version_note}
 git push --tags origin master
 git checkout development
 git merge ${current}
 git push origin development
 delete_branch
 exit 0
 ;;
 * )
 echo "Error: You're not on a feature, release or hotfix branch."
 exit 1
 ;;
 esac
else
 echo "Error: You didn't provide the needed arguments."
 usage
 exit 1
fi
asked Sep 17, 2012 at 16:16
\$\endgroup\$
1
  • 3
    \$\begingroup\$ I've revised this post and script several times, only to discover a new shortcoming each time it has been submitted. I don't expect myself to write a perfect piece of text or code at the first attempt - but it's odd how some things only become apparent once spoken out aloud. \$\endgroup\$ Commented Sep 18, 2012 at 9:54

2 Answers 2

1
\$\begingroup\$

The following is one of my favorite "rosetta stone" like references:

Unix Shells

answered Sep 20, 2012 at 17:46
\$\endgroup\$
1
\$\begingroup\$

It seems fine for me. Just three minor things:

  1. I'd consider calling the define_tag before the git merge or any other command which changes the current state of the repository. If a user press Ctrl+C they will find the repository in a different state than the beginning of the script.

  2. In the define_tag I'd print the existing tags (or the last five or ten tags). It could help users to choose the next one.

  3. There are same requirements in the comments:

    # This script requires that git has been installed and properly configured,
    # that the remote "master" and "development" branches exist (locally too) 
    # and that a network connection to the "origin" repository is established.
    

    You could check that git is installed properly and the required branches are exist at the beginning of the script.

answered Mar 14, 2013 at 20:36
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.