If using a development process that consists of multiple stages of maturity, development mainly progresses in one branch only. I often use a process that consists of the three branches ‘develop’, ‘staging’ and ‘master’, so in my case the development mainline is ‘develop’. You might want to check out my post on how to enforce or safeguard such a development process.

If the development process also advocates the use of feature branches, the ‘develop’ branch often is in a state that is mature enough to be merged into the upper branch.

To aid in this process I developed a little script to merge the current branch into the upper branch. The script removes the need to issue a bunch of Git commands and performs the following tasks:

  • check for unclean working copies prior merging
  • checkout target branch and pull from origin
  • merge from lower branch
  • push to origin
  • repeat the last three steps for the branch higher than the target branch

The script asks for confirmation before every action and assumes relatively save defaults (it’s e.g. not the default to directly merge up into master/production).

The script works for a process with three stages (‘develop’, ‘staging’, ‘master’) as well as for one with two stages (‘develop’, ‘master’) – the stages must be named as described, however.

#!/bin/bash
 
# This script performs a merge of the current branch in the upper branch
# (without fast forwarding).
#
# Author: Tobias König <tn@movb.de>
 
set -eu
 
#
# check branching
#
 
staging_exists=$(git rev-parse --verify staging 1>/dev/null 2>&1; echo $?) # 0 = true; 128 = false
 
#
# determine current and upper branch
#
 
current_branch=$(git symbolic-ref HEAD --short)
case $current_branch in
  develop) if [ "$staging_exists" = "0" ]; then
               upper_branch=staging
               upperer_branch=master
           else
               upper_branch=master
               upperer_branch=""
           fi ;;
  staging) upper_branch=master; upperer_branch="" ;;
  master)  echo You are on master, there is no upper branch to merge to.; exit 1 ;;
  *)       echo Current branch is unknown, I am dazed and confused.; exit 2 ;;
esac
 
#
# handle dirty working copies
#
 
dirty=$(git status --porcelain)
if [ -n "$dirty" ]; then
    echo Your working copy is dirty:
    echo "$dirty"
    echo
 
    # ask to continue if not really dirty, i.e. if only untracked files exist
    if ! echo "$dirty" | grep -qv '^?? '; then
        read -p "Still continue? (y/N) " continue_if_dirty
        if [ "$continue_if_dirty" != "y" ]; then
            exit 3
        fi
    # otherwise just exit because it's too dirty to continue
    else
        exit 4;
    fi
fi
 
#
# perform merge in upper branch
#
 
read -p "Merging from $current_branch to $upper_branch. Continue? (y/N) " perform_merge
if [ "$perform_merge" = "y" ]; then
    git checkout $upper_branch
    git pull
    git merge --no-ff $current_branch
    git checkout $current_branch
else
    exit
fi
 
#
# push upper branch
#
 
read -p "Push $upper_branch to origin? (y/N) " push_upper
if [ "$push_upper" = "y" ]; then
    git push origin $upper_branch
     
    if [ -z "$upperer_branch" ]; then
        echo
        echo All done.
    fi
else
    exit
fi
 
 
if [ -n "$upperer_branch" ]; then
 
    #
    # perform merge in upperer branch
    #
    read -p "Merging from $upper_branch to $upperer_branch. Continue? (y/N) " perform_merge
    if [ "$perform_merge" = "y" ]; then
        git checkout $upperer_branch
        git pull
        git merge --no-ff $upper_branch
        git checkout $current_branch
    else
        exit
    fi
 
    #
    # push upperer branch
    #
 
    read -p "Push $upperer_branch to origin? (y/N) " push_upperer
    if [ "$push_upperer" = "y" ]; then
        git push origin $upperer_branch
 
        echo
        echo All done.
    fi
fi

The script is executed by just calling merge-up when in a Git working copy that has either the branch ‘develop’ or ‘staging’ checked out.

tobi@spieli:~/dev/myproject$ merge-up