Loading...

Automate Your Git Commits On Feature Branches

Productivity
November 13, 2019
4 minutes to read
Share this post:

At some point it hits everyone. Your precious work of several hours is vanished because of a hardware failure. For that the industry came up with several solutions like the idea of version control systems. But recently I was dumb enough to not do any commit for several hours - just to leave the history clean. Big mistake… But did you think about automating that? How about committing every couple of minutes automatically? In this post I’ll explain this - on the first sight - weird workflow.

So, the idea is, to have a script that commits every couple of minutes as long as you’re working on a project. As soon as you are finished with your feature branch you use interactive rebasing to get rid of all the temporary commits.

The following one-liner is the command we want to automate:

git add -A && git commit -m "WIP" && git push -u origin

Basically you add all files to the index, commit with a default message and push the new commit to origin with one go.

The next step is to write a script which executes this command regular when there are changes in the given repository. The simplest script that does that looks like so:

#!/usr/bin/env bash
while :
do
  sleep 300
  git add -A && git commit -m "WIP" && git push -u origin
done

But obviously you should optimize the script a bit. You should get some better feedback what the script is currently doing and you should add a safety net that the script aborts as soon as you switch the branch. So a nicer solution is the following:

#!/usr/bin/env bash

function getCurrentBranch() {
    git rev-parse --abbrev-ref HEAD
}

COMMIT_INTERVAL=300 # This is the interval in seconds between our commits
CURRENT_BRANCH=$(getCurrentBranch) # We store the branch on which we were when we executed the script

echo "Starting feature branch workflow on $CURRENT_BRANCH will commit every $COMMIT_INTERVAL seconds"
while :
do
  sleep $COMMIT_INTERVAL
  # When we switch the branch we detect that before we continue with the commiting and pushing.
  if [ $CURRENT_BRANCH != "$(getCurrentBranch)" ]; then
    echo "You checked out another branch then you started with. For safety reasons I'll terminate"
    exit 0
  fi
  git add -A && git commit -m "WIP" && git push -u origin
done

When you’re finished with the feature branch you probably want to have a nicer history without all the WIP commits. For that you can use interactive rebasing. This git function allows you to rewrite the history of a branch. To start the process you type: git rebase -i master

The command will open your configured editor with a file like the following:

pick 1778493 WIP
pick ce1ee68 WIP
pick 282cdee WIP

In order to combine all the commits you exchange the all pick keywords but the first with squash or s:

pick 1778493 WIP
s ce1ee68 WIP
s 282cdee WIP

When we save and exit the file we get to edit the commit message for the squashed commits.

And that’s it. We can now run the above script as soon as we start to work on a feature branch and we’re sure that the changes will get pushed regular on remote.

Update 2019-11-14

After playing a bit more with the workflow I did a couple of improvements to the script which result in a nicer output.

#!/usr/bin/env bash
set -euo pipefail # Bash strict mode - see http://redsymbol.net/articles/unofficial-bash-strict-mode/

function getCurrentBranch() {
    git rev-parse --abbrev-ref HEAD
}

function currentTime() {
  echo "[$(date +"%T")]"
}

COMMIT_INTERVAL=5 # This is the interval in seconds between our commits
CURRENT_BRANCH=$(getCurrentBranch) # We store the branch on which we were when we executed the script

printf "%s Starting feature branch workflow on $CURRENT_BRANCH will commit every $COMMIT_INTERVAL seconds" "$(currentTime)"
while :
do
  sleep $COMMIT_INTERVAL
  # When we switch the branch we detect that before we continue with the commiting and pushing.
  if [ $CURRENT_BRANCH != "$(getCurrentBranch)" ]; then
    printf "%s You checked out another branch then you started with. For safety reasons I'll terminate" "$(currentTime)"
    exit 0
  fi
  if [ ! -z "$(git status --porcelain)" ]; then
    printf "\n\n%s About to commit: \n" "$(currentTime)"
    git status -s
    git add -A &>/dev/null && git commit -m "WIP" &>/dev/null && git push -u origin &>/dev/null
    printf "%s Success" "$(currentTime)"
  fi
done

These improvements result in an output like that:

You might also notice that I changed the commit interval to 5 seconds because I noticed that I prefer to persist changes as soon as possible. I actually adapted my workflows to work with many commits. For example I avoid triggering CI builds when “WIP” is part of the commit message . Furthermore I use the git-squash alias to squash all commits when I finished working.

In some cases you’d like to mark specific states of your history. For that I create an empty commit with git commit --allow-empty -m "Some message".

Have you heard of Marcus' Backend Newsletter?

New ideas. Every week!
Top