17

I'm starting to play around with git hooks, and I'd like to create one to prevent a developer from creating a new branch when on a specific branch. The current process in our company is meant to look like this:

git checkout master
git fetch
git reset --hard origin/master
git checkout -b [branch name]
do awesome things.

However, occasionally when moving quickly, some developers end up starting this new branch from a staging repo. Which causes grief.

So, I'd like to create a hook to interrupt when a developer starts to create a new branch, check what branch they're on, and either exit 1 if the branch is not master (or just generally stop the action if the branch name is staging), or allow it otherwise.

Edit:

As I search more on this, I realize I want a pre-checkout hook, which doesn't appear to exist. Unless someone has a better idea, I'll proceed to print a very large warning in a post-checkout hook if the above scenario comes to pass.

hookedonwinter
  • 12,436
  • 19
  • 61
  • 74

3 Answers3

4

For the client side you can create a post-checkout hook that uses the git branch --merged to see branches merged in the current branch. If the branch you want to prevent from branching is merged in the current branch then you throw the error.

Code in bach would look like this:

#!/bin/sh

getBranchName()
{
    echo $(git rev-parse --abbrev-ref HEAD)
}

getMergedBranches()
{
    echo $(git branch --merged)
}

if [ "$(getBranchName)" != "dev" ]; then
    if [[ $(getMergedBranches) == *"dev"* ]]; then
        echo "Don't create branches from the dev branch!"
        exit 1
    fi
fi
jmurgic
  • 61
  • 4
3

For the client-side post-checkout hook I would first check the base branch name and then compare the commit hashes. I also added a safety check in the beginning (you probably don't want to delete dev or master) in case the staging branch has just been created.

#!/bin/sh

new_branch=$(git branch --show-current)

# These branches already exist and should always be checked out (and especially not be deleted)
if [[ $new_branch =~ ^dev|release|master$ ]]
then
    exit 0
fi

base_branch=$(git rev-parse --abbrev-ref @{-1})
base_head=$(git rev-parse @{-1})
new_head=$(git rev-parse HEAD)

# First check the name of the base branch
# Then compare the hashes of the last commits
if [ $base_branch = "staging" ] && [ $base_head = $new_head ]
then
    echo ""
    echo "HOLD YOUR HORSES!"
    echo "-----------------"       
    echo "Remember not to create branches from the staging branch."
    echo "Switching to dev and deleting the new branch..."
    echo "-----------------"
    git checkout --ignore-other-worktrees dev && git branch -D $new_branch
    exit 1
fi

On the server side you could use the pre-receive hook but you'll probably have to revert to git branch --merged like in the above answer: https://stackoverflow.com/a/35649638/9266796.

Johan Maes
  • 1,161
  • 13
  • 13
0

I would catch this on the blessed repo side. If a dev started a branch on something other than a commit that is off of latest master, reject the push of the branch. They can correct by rebasing off of master and pushing again. Ensure your error message is descriptive enough so they know how to correct their situation.

Further, update your master without checking it out with:

git fetch
git push . origin/master:master

Then create a branch off of the latest master:

git checkout -b somecoolfeature master

You don't have to even have your local master up to date:

git fetch
git checkout -b somecoolfeature origin/master
do awesome stuff :)
user229044
  • 232,980
  • 40
  • 330
  • 338
Adam Dymitruk
  • 124,556
  • 26
  • 146
  • 141