Miguel David |||

My git flow

I have yet to meet an engineer who does not like to optimise. We like solving puzzles, work with constraints and make science apply to real life. But optimisation is high up there in terms of what we like to do.

If you work in IT these days, not just programming, but as a systems administrator, a project manager, even a designer, you’ve likely got in touch with git. git is a distributed source control management tool. In other words, it smartly stores your code (or other artifacts) in a smart way somewhere while at the same time allowing others to work on the same code you are.

I work with git every day, so when my Harvest colleague Lee Jones, told me about his workflow for git, I was naturally interested. Less mouse clicks and more automation may only shave a couple of seconds a day, but in the long run they add up. After he shared it, I modified it slightly for my own needs and am sharing it with you below.

Workflow:

  1. branch-me [name of new branch] - this creates a new branch with my initials prefixed and replaces spaces with hyphens
  2. I make changes to the code base and commit my work
  3. merge-me - this pushes the current branch and opens the GitHub PR page. It attempts to set a human-friendly title (e.g. removes my initials, removes hyphens, capitalizes each word)
  4. I merge the PR in GitHub on the previously opened page
  5. clean-me - this updates local master branch (git pull), checks out the master branch, and deletes the feature branch
  6. Done!

The script is below and I am sourcing it in my ~/.bash_profile file so that it’s available in every session.

#!/bin/bash
## Originally Lee Jones's workflow, adapted to Miguel David on 12/07/2019

export GIT_BRANCH_PREFIX="md-" # HERE: change prefix as needed

# Creates a branch prefixed with a standard prefix and changes any spaces to dashes
function branch-me() {
  if [ "$1" == "" ]
  then
    echo "ERROR: no branch name provided"
    false
  else
    local branch_name=$(echo -n $GIT_BRANCH_PREFIX[email protected] | sed -e "s/ /-/g")
    git checkout -b "${branch_name}"
  fi
}

# Pushes the current branch and opens the PR page. Attempts to set the title
# from the branch name.
function merge-me() {
  local branch_name="$(_git-current-branch)"
  local repo_name="$(_github_repo_name)"
  local branch_name_formated_as_title=$(ruby -r uri -e "puts URI.escape(\"$branch_name\").gsub(/^$GIT_BRANCH_PREFIX/, '').gsub('-', ' ').capitalize")
  local commit_subjects="$(git log --pretty=%s master...${branch_name})"
  local pr_url="https://github.com/${repo_name}/compare/${branch_name}?expand=1"

  if [ "${branch_name}" == "master" ]
  then
    echo "ERROR: you cannot push to master for PR. Switch to a different branch first."
    kill -INT $$
  fi

  # if there are multiple commits, set title by using the branch name
  if [[ "$(echo "${commit_subjects}" | wc -l)" -gt "1" ]]
  then
    pr_url="${pr_url}&title=${branch_name_formated_as_title}"
  fi

  echo "Opening PR for ${repo_name} from the ${branch_name} branch..."
  echo "Pushing ${branch_name} branch to GitHub..."
  git push origin "${branch_name}"
  echo "Opening PR in browser..."
  # Sometimes the compare view does not trigger a new PR - waiting seems to help
  sleep 2
  open "${pr_url}"
}

# switch to master, pull from origin and merge, delete previous branch
function clean-me() {
  local original_branch_name=`_git-current-branch`
  local target_branch_name=${1:-master}
  if [ "$original_branch_name" = "${target_branch_name}" ]
  then
    echo "Already on ${target_branch_name} branch, updating..."
    git fetch origin
    git merge origin/$target_branch_name
  else
    git fetch origin
    git checkout $target_branch_name
    git merge origin/$target_branch_name
    # If the original branch was rebased before merging, `git branch -d` won't
    # work. We can assume it's safe to force deletion if there are effectively
    # no differences between the two branches.
    if [ "$(git diff $target_branch_name..$original_branch_name --shortstat)" == "" ]; then
      git branch -D "${original_branch_name}"
      # Otherwise, try to delete the branch normally.
    else
      if (git branch -d "${original_branch_name}"); then
        echo "Successfully deleted branch '$original_branch_name'!"
      else
        read -p "Do you want to force deletion of branch '$original_branch_name' (y/n)? " -n 1 -r
        echo
        if [[ $REPLY =~ ^[Yy]$ ]]; then
          git branch -D "${original_branch_name}"
        else
          echo "You said '$REPLY'. Exiting!"
          return 0
        fi
      fi
    fi
  fi
  echo "Cleaned up. NEEEXT!"
}

function _git-current-branch() {
  echo "$(git branch | grep -E '^\*' | awk '{ print $2 }')"
}

function _github_repo_name() {
  local remote_origin_url="$(git config --get remote.origin.url)"
  if $(echo "${remote_origin_url}" | grep -q -E "[email protected]:.*\.git"); then
    echo "$(echo $remote_origin_url | sed -e 's/[email protected]://' | sed -e 's/.git//')"
  else
    echo "ERROR: could not determine GitHub repo name from '${remote_origin_url}'."
    kill -INT $$
  fi
}

I hope this helps!

Up next Using multiple versions of kubectl on macOS When you work with multiple clients, or maybe with one company that has clusters in different versions of Kubernetes, you need to have a Today I learned about view Today I learned that has a alias, which opens in read-only mode. I will no more (pun intended)! I hope this helps!
Latest posts Today I learned about view My git flow Using multiple versions of kubectl on macOS Merging two MySQL (or MySQL compatible) databases in AWS using DMS Inspired Recommendations for Portugal God is dead (and we are suffering from it) Advice to my unborn child: be a plumber and an artist On light and shadow You can usually do more/better than you think you can Upkeep Comparing myself to others Fear, Procrastination. Procrastination, Fear Liberalism and a new system Chores Finding my calm place Turning 38 Privilege Humbling Habits Frustration The miracle of the blank page On being late Love is the base of it all A letter to my dead grandfather Self imposed stress Rent the world Start at 6:30 How to get multiple domains pointing to GitHub Pages using Cloudflare My MySQL Cookbook How to make Jekyll multilingual