How to Prevent Commiting to master/develop branch by Accidents using Pre-push Hooks in Git?


Have you ever been wanting to push your heads against the walls when you accidentally push commits to the master/develop branchs (even they are local branches)? You can however `git reset HEAD files` to unstage the commits, however, this is not a pleasant thing to do.

Luckily, the git provides many hooks and pre-push is the one that allows you to do some checks before the git actually pushes it. The pre-push is actually a bash script that runs before the `git push` command. If the script returns 1 then nothing happens. It the script returns 0, it will continue the push operation.

The pre-push is located at each reprository under .git/hooks directory, which is hidden. The following is the sample pre-push (named pre-push.sample)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#!/bin/sh
 
# An example hook script to verify what is about to be pushed.  Called by "git
# push" after it has checked the remote status, but before anything has been
# pushed.  If this script exits with a non-zero status nothing will be pushed.
#
# This hook is called with the following parameters:
#
# $1 -- Name of the remote to which the push is being done
# $2 -- URL to which the push is being done
#
# If pushing without using a named remote those arguments will be equal.
#
# Information about the commits which are being pushed is supplied as lines to
# the standard input in the form:
#
#   <local ref> </local><local sha1> <remote ref> </remote><remote sha1>
#
# This sample shows how to prevent push of commits where the log message starts
# with "WIP" (work in progress).
 
remote="$1"
url="$2"
 
z40=0000000000000000000000000000000000000000
 
while read local_ref local_sha remote_ref remote_sha
do
    if [ "$local_sha" = $z40 ]
    then
        # Handle delete
        :
    else
        if [ "$remote_sha" = $z40 ]
        then
            # New branch, examine all commits
            range="$local_sha"
        else
            # Update to existing branch, examine new commits
            range="$remote_sha..$local_sha"
        fi
 
        # Check for WIP commit
        commit=`git rev-list -n 1 --grep '^WIP' "$range"`
        if [ -n "$commit" ]
        then
            echo >&2 "Found WIP commit in $local_ref, not pushing"
            exit 1
        fi
    fi
done
 
exit 0
#!/bin/sh

# An example hook script to verify what is about to be pushed.  Called by "git
# push" after it has checked the remote status, but before anything has been
# pushed.  If this script exits with a non-zero status nothing will be pushed.
#
# This hook is called with the following parameters:
#
# $1 -- Name of the remote to which the push is being done
# $2 -- URL to which the push is being done
#
# If pushing without using a named remote those arguments will be equal.
#
# Information about the commits which are being pushed is supplied as lines to
# the standard input in the form:
#
#   <local ref> </local><local sha1> <remote ref> </remote><remote sha1>
#
# This sample shows how to prevent push of commits where the log message starts
# with "WIP" (work in progress).

remote="$1"
url="$2"

z40=0000000000000000000000000000000000000000

while read local_ref local_sha remote_ref remote_sha
do
	if [ "$local_sha" = $z40 ]
	then
		# Handle delete
		:
	else
		if [ "$remote_sha" = $z40 ]
		then
			# New branch, examine all commits
			range="$local_sha"
		else
			# Update to existing branch, examine new commits
			range="$remote_sha..$local_sha"
		fi

		# Check for WIP commit
		commit=`git rev-list -n 1 --grep '^WIP' "$range"`
		if [ -n "$commit" ]
		then
			echo >&2 "Found WIP commit in $local_ref, not pushing"
			exit 1
		fi
	fi
done

exit 0

We can check if the current branch is in one of the master branches e.g. master, origin/develop, if yes, then exit with code 1. The following pre-push script has to be chmod with executable permission. All it does is to check if current branch is forbidden, if yes, return 1 otherwise indicates 0 to continue.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/bash
# run `chmod +x .git/hooks/pre-push` (We make the hook executable by using the chmod utility.)
# To bypass this hook and force the push, use 'git push --no-verify'
 
master_branches=('origin/develop' 'master' 'develop')
current_branch=$(git symbolic-ref HEAD | sed -e 's,.*/\(.*\),\1,')
 
for branch in "${master_branches[@]}"
do
    if [ $branch = $current_branch ]; then
        echo "You're about to push $branch and you don't want that"
        exit 1
    fi
done
exit 0
#!/bin/bash
# run `chmod +x .git/hooks/pre-push` (We make the hook executable by using the chmod utility.)
# To bypass this hook and force the push, use 'git push --no-verify'

master_branches=('origin/develop' 'master' 'develop')
current_branch=$(git symbolic-ref HEAD | sed -e 's,.*/\(.*\),\1,')

for branch in "${master_branches[@]}"
do
    if [ $branch = $current_branch ]; then
        echo "You're about to push $branch and you don't want that"
        exit 1
    fi
done
exit 0

Here is how it looks like when you try to push to master:

1
2
3
4
5
$ git branch
* master
$ git push origin master 
You're about to push master and you don't want that
error: failed to push some refs to 'https://github.com/DoctorLai/ACM.git'
$ git branch
* master
$ git push origin master 
You're about to push master and you don't want that
error: failed to push some refs to 'https://github.com/DoctorLai/ACM.git'

As the pre-push has to be on each reprository, you can copy the file at your root direction, and use the following bash command to find and copy the file to all .git/hooks directory.

1
find . -type d -name "hooks" | grep ".git/hooks" | xargs echo cp pre-push
find . -type d -name "hooks" | grep ".git/hooks" | xargs echo cp pre-push

On windows, I guess the pre-push is still written in BASH. However, if the above not working on Windows, you might try the following Windows BATCH script – or just for education purposes.

@echo off
REM pre-push.cmd
REM To bypass this hook and force the push, use 'git push --no-verify'

setlocal enabledelayedexpansion
set branches=origin^/develop master develop

for /f "tokens=2" %%i in ('git branch') do (set current=%%i)
for %%j in (%branches%) do (
    if /i "%%j"=="%current%" (
        echo You're about to push to %%j, you don't want that
        exit /b 1
    )
)
endlocal
exit /b 0

Git has other hooks, for example: The Git Pre-Commit Hook to Avoid Pushing Only Unit Tests In NodeJs

–EOF (The Ultimate Computing & Technology Blog) —

GD Star Rating
loading...
770 words
Last Post: How to Construct Binary Search Tree from Preorder Traversal? (C++ and Java)
Next Post: The Algorithm to Find Anagram Mappings between Two Arrays

The Permanent URL is: How to Prevent Commiting to master/develop branch by Accidents using Pre-push Hooks in Git?

Leave a Reply