I found [nvie's git branching model](http://nvie.com/posts/a-successful-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}... <br /> <br /> 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. <br /> <br /> 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 <br /> <br /> 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