The pre-push hooks are triggered whenever you use the push command and the script execution happens before the push; so, we can prevent a push if we find a reason to reject the push. One reason could be you have a commit that has the nopush
text in the commit message.
To use the Git pre-push, we need to have a remote repository for which we will be cloning jgit
again as follows:
$ git clone https://git.eclipse.org/r/jgit/jgit chapter7.1 Cloning into 'chapter7.1'... remote: Counting objects: 2429, done remote: Finding sources: 100% (534/534) remote: Total 45639 (delta 145), reused 45578 (delta 145) Receiving objects: 100% (45639/45639), 10.44 MiB | 2.07 MiB/s, done. Resolving deltas: 100% (24528/24528), done. Checking connectivity... done. Checking out files: 100% (1576/1576), done.
We want to be able to push to a remote, but unfortunately, Git will try to authenticate through HTTPS for the jgit
repository before the hooks are executed. Because of this, we will create a local clone from the chapter7.1
directory. This will make our remote a local folder.
$ git clone --branch master ./chapter7.1/ chapter7.2 Cloning into ' chapter7.2'... done. Checking out files: 100% (1576/1576), done. $ cd chapter7.2
We are cloning the chapter7.1
directory in a folder named chapter7.2
, and checking the master branch when the clone has finished. Now, we can go back to the chapter7.1
directory and continue with the exercise.
What we now want to do is to create a commit with a commit message that has nopush
as part of it. By adding this word to the commit message, the code in the hook will automatically stop the push. We will be doing this on top of a branch. So, to start with, you should check out a prepushHook
branch that tracks the origin/master
branch and then creates a commit. We will try to push it for the remote when we have the pre-push commit in place, as follows:
prepushHook
that tracks origin/master
:$ git checkout -b prepushHook --track origin/master Branch prepushHook set up to track remote branch master from origin. Switched to a new branch 'prepushHook'
$ git reset --hard 2e0d178 HEAD is now at 2e0d178 Add recursive variant of Config.getNames() methods
sed
and then add pom.xml
and commit it:$ sed -i -r 's/2.9.1/3.0.0/g' pom.xml $ git add pom.xml $ git commit -m "Please nopush" [prepushHook 69d571e] Please nopush 1 file changed, 1 insertion(+), 1 deletion(-)
git log -1
:$ git log -1 commit 1269d14fe0c32971ea33c95126a69ba6c0d52bbf Author: Rasmus Voss <[email protected]> Date: Thu Mar 6 23:07:54 2014 +0100 Please nopush
cp .git/hooks/pre-push.sample .git/hooks/pre-push
#!/bin/bash echo "You are not allowed to push" exit 1
HEAD
to the master
branch in the remote:$ git push origin HEAD:refs/heads/master You are not allowed to push error: failed to push some refs to '../chapter7.1/'
nopush
in any commit message. We can use git log --grep
to search for commits with the keyword nopush
in the commit message, as shown in the following command:$ git log --grep "nopush" commit 51201284a618c2def690c9358a07c1c27bba22d5 Author: Rasmus Voss <[email protected]> Date: Thu Mar 6 23:07:54 2014 +0100 Please nopush
nopush
. Now, we will make a simple check for this in the hook and edit the pre-push hook so that it has the following text:#!/bin/bash COMMITS=$(git log --grep "nopush") if [ "$COMMITS" ]; then echo "You have commit(s) with nopush message" echo "aborting push" exit 1 fi
HEAD
to the master branch on the remote origin
:$ git push origin HEAD:refs/heads/master You have commit(s) with nopush message aborting push error: failed to push some refs to 'c:/Users/Rasmus/repos/./chapter7.1/'
As expected, we are not allowed to push as we have the nopush
message in the commit.
Having a hook that can prevent you from pushing commits that you don't want to push is very handy. You can specify any keywords you want. Words such as reword
, temp
, nopush
, temporary
, or hack
can all be things you want to stop, but sometimes you want to get them through anyway.
What you can do is have a small checker that will check for specific words and then list the commits and ask if you want to push anyway.
If you change the script to the following snippet, the hook will try to find commits with the keyword nopush
and list them. If you wish to push them anyway, then you can find an answer to the question and Git will push anyway.
#!/bin/bash COMMITS=$(git log --grep "nopush" --format=format:%H) if [ $COMMITS ]; then exitmaybe=1 fi if [ $exitmaybe -eq 1 ]; then while true do 'clear' for commit in "$COMMITS" do echo "$commit has no push in the message" done echo "Are you sure you want to push the commit(s) " read REPLY <&1 case $REPLY in [Yy]* ) break;; [Nn]* ) exit 1;; * ) echo "Please answer yes or no.";; esac done fi
Try it with the git push
command again as shown in the following snippet:
$ git push origin HEAD:refs/heads/master Commit 70fea355bac0c65fd51f4874d75e65b4a29ad763 has nopush in message Are you sure you want to push the commit(s)
Type n
and press enter. Then, expect the push to be aborted with the following message:
error: failed to push some refs to 'c:/Users/Rasmus/repos/./chapter7.1/'
As predicted, it will not push. However, on the other hand, if you press y, Git will push to the remote. Try it now using the following command:
$ git push origin HEAD:refs/heads/master 054c5f78fdc82141e9d73e6b6955c38ff79c8b2e has no push in the message Are you sure you want to push the commit(s) y To c:/Users/Rasmus/repos/./chapter7.1/ ! [rejected] HEAD -> master (non-fast-forward) error: failed to push some refs to 'c:/Users/Rasmus/repos/./chapter7.1/' hint: Updates were rejected because a pushed branch tip is behind its remote hint: counterpart. Check out this branch and integrate the remote changes hint: (e.g. 'git pull ...') before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details.
As predicted, the push will be tried, but as you can see from the output, we are rejected by the remote. This is because we diverged, and the push was not working at the tip of the master branch.
So, with this hook, you can make your life a little easier by having the hook prevent you from accidentally pushing something you are not interested in getting pushed. This example also considers commits that have been released; so, if you select a different keyword, then other commits, not only the locally created ones, will be taken into consideration by the script.