Heetola
Heetola

Reputation: 6191

Git pre-push to prevent merge

i made this bash script for my git hooks in order to avoid merging by mistake.

At work we use git-extension and it prefills the branch name, if you forget to prefix the branch name by refs/for or refs/drafts then your commits are merged on the distant repo bypassing the code review.

This can only happen to people with merge right. but sometimes someone with merge right will make that mistake.

#!/bin/sh

if [[ `grep 'refs/for'` ]]; then 
  echo "push to refs/for/ authorized"
elif [[ `grep 'refs/drafts/'` ]]; then
  echo "push to refs/drafts/ authorized"
else
  echo "PUSH DENIED, MERGE DETECTED, please merge using gerrit 'submit' button only"
  exit 1
fi

if I push to refs/for/v1.1.x it get push to refs/for/ authorized

however if I push to refs/drafts/v1.1.x I get PUSH DENIED, MERGE DETECTED, please merge using gerrit 'submit' button only

How is this possible, the condition syntax is exactly the same.

Thanks.

Upvotes: 0

Views: 104

Answers (1)

torek
torek

Reputation: 490168

As Anthony Sottile commented, your first grep command reads all the input, checking for refs/for. Your second grep reads the remaining input; since your first one read all of the input, none remains. The second one therefore never finds anything.

You can deal with this in many different ways. One is to copy stdin to a temporary file that you can read repeatedly. Another is to structure your hook so that you read the input only once, checking each line for each condition. This second method is obviously more efficient, though this "obviousness" is a bit of an illusion, depending on details I'm not going to get into here.

Anyway, the way I would write this is:

#! /bin/sh

summary_status=0     # assume all OK initially
while read lref lhash rref rhash; do
    case $rref in
    refs/for/*|refs/draft/*) echo "push to $rref ok";;
    *) echo "push to $rref not ok"; summary_status=1;;
    esac
done
exit $summary_status

This variant does not require bash features (hence the /bin/sh rather than /bin/bash in the #! line); if you change it to one that does, be sure to change the #! line. It reads the input exactly once. It also verifies that you did not run, e.g.:

git push remote master:refs/for/master develop

which your existing script would allow (since one is OK and one is not).

Upvotes: 1

Related Questions