Git Hooks for WIP Commits

code git

Use git hooks to remind yourself about stashed changes or commits that are works in progress.

Closeup of a fishing lure with two hooks Photo by Trophy Technology on Unsplash

Problem: In git, you type git checkout master (or some branch) and get the following message:

error: Your local changes to the following files would be overwritten by checkout:
src/app/superhero/capeColors.ts
src/app/villain/evilLair.ts
src/app/quest/adventures.json
src/assets/images/unicorn.png

You could git stash your changes, switch your branch, and then later on switch back and git stash pop.

However, what if you do not know when you will switch back? It could be in a few minutes, or perhaps next year. You might forget that you made those changes and then accidentally redo work that you have already done.

On the other hand, the changes aren't really commit-worthy. You don't want them to be part of your commit history. A full git commit -m "Add QuantumSwarm optimizer" is not really what you want.

You could add some kind of prefix that means work in progress like WIP: to your commit, e.g. git commit -m "WIP: Don't push this!" but, again, you do not really know when you will be back to this branch. If you begin work again and accidentally commit after your WIP: commit, you will have to mess about with your commit history when and if you notice.

It would be better if you could somehow get a reminder that there is a work in progress on that branch.

Solution: Go ahead and add the WIP: prefix to your commit message but then use a git hook to remind yourself that there is a WIP: commit when you checkout the branch. If you want, you can actually prevent more commits to that branch until the WIP: is removed.

Git hooks are custom scripts that can be triggered by various Git events, allowing you to automate tasks and enforce custom rules or workflows. The two we will need are the pre-commit hook and the post-checkout hook.

post-checkout hook #

post-checkout runs after a branch has been checked out. We can use this to echo a reminder when you switch back to the branch with the WIP commit. With this code, you will get a warning message in your console reminding you that this branch is a work in progress whenever you checkout a branch with the last commit prefixed by WIP:

$ git checkout amazing-feature-branch
Switched to branch 'amazing-feature-branch'
WARNING: There are WIP commits that need attention:
ed38de4 - WIP: almost amazing work in progress

Create a file named post-checkout in the .git/hooks directory of your repository and paste the following code:

#!/bin/sh

WIP_COMMIT=$(git log --grep="^WIP:" --format="%h - %s")

if [ ! -z "$WIP_COMMIT" ]; then
echo "WARNING: There are WIP commits that need attention:"
echo "$WIP_COMMIT"
fi

Or, if you prefer colors!

#!/bin/sh

# ANSI escape code for bright yellow text
BRIGHT_YELLOW="\033[93m"
# ANSI escape code for bright orange text
BRIGHT_ORANGE="\033[38;5;214m"
# ANSI escape code to reset text color
RESET="\033[0m"

WIP_COMMIT=$(git log --grep="^WIP:" --format="%h - %s")

if [ ! -z "$WIP_COMMIT" ]; then
echo "${BRIGHT_ORANGE}WARNING:${RESET} There are WIP commits that need attention:"
echo "${BRIGHT_YELLOW}$WIP_COMMIT${RESET}"
fi

Make the file executable with chmod +x .git/hooks/post-checkout.

pre-commit hook #

The pre-commit hook will run after you type git commit but before committing. Use this to ensure that you do not add another commit over your WIP: commit. If your last commit has the WIP: prefix, print an error and then prevent the new commit:

$ git commit -m "Also amazing"

Error: The last commit message has the 'WIP:' prefix. Please remove or amend the commit before proceeding.

Create a file named pre-commit in the .git/hooks directory of your repository and paste the following code:

#!/bin/sh

# Check if the last commit message starts with `WIP:`
last_commit_message=$(git log -1 --pretty=%B)
if echo "$last_commit_message" | grep -q "^WIP:"; then
echo "Error: The last commit message has the 'WIP:' prefix. Please remove or amend the commit before proceeding."
exit 1
fi

Make the file executable with chmod +x .git/hooks/pre-commit.

This hook will prevent you from committing if the last commit message starts with WIP:. When you get this message, you can do:

git reset --soft HEAD~1

This will un-commit your WIP: changes and let you continue your work.

.bashrc check #

The post-checkout hook only runs when you checkout a branch and not when you open your terminal with that branch already checked out. If you close your IDE and then open it again, you will not get a warning. If you want to be sure that you get the WIP: warning then, too, git does not have a hook for that.

If you're using a Linux variant, you can add a check to your shell script. When you open your terminal, the check will run and print the WIP: warning. Add the following function to the end of your .bashrc (or whatever shell configuration file your shell uses):

function check_wip_commits() {
local wip_commits=$(git log --grep="^WIP:" --format="%h - %s" 2>/dev/null)

if [ ! -z "$wip_commits" ]; then
local bright_orange="\033[38;5;214m"
local reset="\033[0m"

echo "${bright_orange}WARNING:${reset} There are WIP commits that need attention:"
echo "${wip_commits}"
fi
}

check_wip_commits

Then you can restart your terminal or type source ~/.bashrc

post-checkout for stash #

There is also the option of reminding yourself that you have uncommitted changes in your stash. With this code, you will get a warning like this:

$ git checkout amazing-feature-branch
Reminder: There's a stash for the current branch (amazing-feature-branch):
stash@{0}: WIP on amazing-feature-branch: 7a17a51 Add minions
Use '
git stash apply' or 'git stash drop' as needed.

To add this reminder, open ./git/hooks/post-checkout and add the following to the end. If you already have the #!/bin/sh header from earlier, do not add that. Skip adding the color variables if you already have those, also.

#!/bin/sh

BRIGHT_YELLOW="\033[93m"
# ANSI escape code for bright orange text
BRIGHT_ORANGE="\033[38;5;214m"
# ANSI escape code to reset text color
RESET="\033[0m"

current_branch=$(git rev-parse --abbrev-ref HEAD)
stash_info=$(git stash list | grep "on ${current_branch}:")

if [ -n "$stash_info" ]; then
echo "${BRIGHT_ORANGE}Reminder:${RESET} : There's a stash for the current branch ($current_branch)"
echo "$stash_info"
echo "${BRIGHT_YELLOW}Use 'git stash apply' or 'git stash drop' as needed.${RESET}"
fi

Remember to make the file executable with chmod +x .git/hooks/post-checkout, if you did not yet already.

Now, when you switch to any branch with changes in stash, you will get a reminder.

More information #

The git documentation on hooks is pretty good: https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks

Comments:

Leave a comment

There was an error. It's me, not you. You could try again later? Or try another method? Email? Smoke signals? Tell me what went wrong!

please enter a substantive message with no link or website address
please enter your name
please enter an email

Comment successfully sent, and it awaits moderation.